diff --git a/README.md b/README.md index 85d7678..99ca18c 100644 --- a/README.md +++ b/README.md @@ -83,18 +83,20 @@ Note that it is a good idea to `rush install` after each `git pull` as dependenc ## Source Code Edit Workflow -1. Make source code changes -2. Ensure unit tests pass when run locally: `npm test -s` +1. Make source code changes on a new Git branch +2. Ensure unit tests pass when run locally: `rush test` 3. Locally commit changes: `git commit` (or use the Visual Studio Code user interface) 4. Repeat steps 1-3 until ready to push changes 5. Add changelog entry (which could potentially cover several commits): `rush change` 6. Follow prompts to enter a change description or press ENTER if the change does not warrant a changelog entry. If multiple packages have changed, multiple sets of prompts will be presented. If the changes are only to non-published packages (like **testbed**), then `rush change` will indicate that a changelog entry is not needed. 7. Completing the `rush change` prompts will cause new changelog entry JSON files to be created. -8. To keep the Git history clean, amend the prior commit using the **Commit Staged (Amend)** menu item in Visual Studio Code or use the command line: `git commit --amend --no-edit` -9. Push changes +8. Check for API signature changes: `rush extract-api`. This will update the signature files, located in `common/api`. +9. Review any diffs to the API signature files in the `common/api` directory to ensure they are compatible with the intended release of the package +10. Commit the changelog JSON files and any API signature updates. +11. Publish changes on the branch and open a pull request. -If using the command line, steps 5 through 9 above can be completed in one step by running `rushchange.bat` from the imodeljs-core root directory. -> Note: The CI build will break if changes are pushed without running `rush change`. The fix will be to run `rush change` (as above) and push those changes as a separate commit. +If using the command line, steps 5 through 7 above can be completed in one step by running `rushchange.bat` from the imodeljs-core root directory. +> Note: The CI build will break if changes are pushed without running `rush change` and `rush extract-api` (if the API was changed). The fix will be to complete steps 5 through 10. Here is a sample [changelog](https://github.com/Microsoft/web-build-tools/blob/master/apps/rush/CHANGELOG.md) to demonstrate the level of detail expected. diff --git a/common/api/README.md b/common/api/README.md new file mode 100644 index 0000000..ea3e5ca --- /dev/null +++ b/common/api/README.md @@ -0,0 +1,5 @@ +# API Signature Files + +The `common/api` folder contains API Signature files that are generated via the `rush extract-api` command. +The API Signature files have a `.api.ts` file extension and should not be edited manually. +Current API Signature files can be compared against past versions to detect API changes. diff --git a/common/api/bentleyjs-core.api.ts b/common/api/bentleyjs-core.api.ts new file mode 100644 index 0000000..13de934 --- /dev/null +++ b/common/api/bentleyjs-core.api.ts @@ -0,0 +1,1067 @@ +// @beta +class ActivityLoggingContext { + constructor(activityId: string, versionId?: string); + // (undocumented) + protected static _current: ActivityLoggingContext; + readonly activityId: string; + static readonly current: ActivityLoggingContext; + enter(): this; + readonly versionId: string; +} + +// @beta +export function assert(condition: boolean, msg?: string): void; + +// @beta +enum AuthStatus { + // (undocumented) + AUTHSTATUS_BASE = 131072, + // (undocumented) + Error = 131072, + // (undocumented) + Success = 0 +} + +// @public +export function base64StringToUint8Array(base64: string): Uint8Array; + +// @public +class BeDuration { + static fromMilliseconds(milliseconds: number): BeDuration; + static fromSeconds(seconds: number): BeDuration; + readonly isTowardsFuture: boolean; + readonly isTowardsPast: boolean; + readonly isZero: boolean; + readonly milliseconds: number; + minus(other: BeDuration): BeDuration; + plus(other: BeDuration): BeDuration; + // (undocumented) + readonly seconds: number; + static wait(ms: number): Promise; +} + +// @public +class BeEvent { + addListener(listener: T, scope?: any): () => void; + addOnce(listener: T, scope?: any): () => void; + clear(): void; + has(listener: T, scope?: any): boolean; + readonly numberOfListeners: number; + raiseEvent(...args: any[]): void; + removeListener(listener: T, scope?: any): boolean; +} + +// @beta +class BeEventList { + get(name: string): BeEvent; + remove(name: string): void; +} + +// @public +class BentleyError extends Error { + constructor(errorNumber: number | IModelStatus | DbResult | BentleyStatus | BriefcaseStatus | RepositoryStatus | ChangeSetStatus | HttpStatus | WSStatus | IModelHubStatus, message?: string, log?: LogFunction, category?: string, getMetaData?: GetMetaDataFunction); + protected _initName(): string; + // (undocumented) + errorNumber: number; + // (undocumented) + getMetaData(): any; + // (undocumented) + readonly hasMetaData: boolean; +} + +// @public +enum BentleyStatus { + // (undocumented) + ERROR = 32768, + // (undocumented) + SUCCESS = 0 +} + +// @public +class BeTimePoint { + after(other: BeTimePoint): boolean; + before(other: BeTimePoint): boolean; + static beforeNow(val: BeDuration): BeTimePoint; + static fromNow(val: BeDuration): BeTimePoint; + readonly isInFuture: boolean; + readonly isInPast: boolean; + readonly milliseconds: number; + minus(duration: BeDuration): BeTimePoint; + static now(): BeTimePoint; + plus(duration: BeDuration): BeTimePoint; +} + +// @beta +class BeUiEvent extends BeEvent<(args: TEventArgs) => void> { + emit(args: TEventArgs): void; +} + +// @beta +enum BriefcaseStatus { + // (undocumented) + CannotAcquire = 131072, + // (undocumented) + CannotApplyChanges = 131078, + // (undocumented) + CannotCopy = 131075, + // (undocumented) + CannotDelete = 131076, + // (undocumented) + CannotDownload = 131073, + // (undocumented) + CannotUpload = 131074, + // (undocumented) + VersionNotFound = 131077 +} + +// @public +enum ChangeSetApplyOption { + Merge = 1, + None = 0, + Reinstate = 3, + Reverse = 2 +} + +// @beta +enum ChangeSetStatus { + ApplyError = 90113, + CannotMergeIntoMaster = 90136, + CannotMergeIntoReadonly = 90135, + CannotMergeIntoReversed = 90137, + // (undocumented) + CHANGESET_ERROR_BASE = 90112, + ChangeTrackingNotEnabled = 90114, + CorruptedChangeStream = 90115, + CouldNotOpenDgnDb = 90131, + FileNotFound = 90116, + FileWriteError = 90117, + HasLocalChanges = 90118, + HasUncommittedChanges = 90119, + InDynamicTransaction = 90122, + InvalidId = 90120, + InvalidVersion = 90121, + IsCreatingChangeSet = 90123, + IsNotCreatingChangeSet = 90124, + MergePropagationError = 90125, + MergeSchemaChangesOnOpen = 90132, + NothingToMerge = 90126, + NoTransactions = 90127, + ParentMismatch = 90128, + ProcessSchemaChangesOnOpen = 90134, + ReverseOrReinstateSchemaChangesOnOpen = 90133, + SQLiteError = 90129, + // (undocumented) + Success = 0, + WrongDgnDb = 90130 +} + +// @public (undocumented) +export function compareBooleans(a: boolean, b: boolean): number; + +// @public (undocumented) +export function compareNumbers(a: number, b: number): number; + +// @public (undocumented) +export function comparePossiblyUndefined(compareDefined: (lhs: T, rhs: T) => number, lhs?: T, rhs?: T): number; + +// @public (undocumented) +export function compareStrings(a: string, b: string): number; + +// @public (undocumented) +export function compareStringsOrUndefined(lhs?: string, rhs?: string): number; + +// @public +export function compareWithTolerance(a: number, b: number, tolerance?: number): number; + +// @public +enum DbOpcode { + Delete = 9, + Insert = 18, + Update = 23 +} + +// @public +enum DbResult { + BE_SQLITE_ABORT = 4, + // (undocumented) + BE_SQLITE_ABORT_ROLLBACK = 516, + BE_SQLITE_AUTH = 23, + BE_SQLITE_BUSY = 5, + // (undocumented) + BE_SQLITE_BUSY_RECOVERY = 261, + BE_SQLITE_CANTOPEN = 14, + // (undocumented) + BE_SQLITE_CANTOPEN_FULLPATH = 782, + // (undocumented) + BE_SQLITE_CANTOPEN_ISDIR = 526, + // (undocumented) + BE_SQLITE_CANTOPEN_NOTEMPDIR = 270, + BE_SQLITE_CONSTRAINT_BASE = 19, + // (undocumented) + BE_SQLITE_CONSTRAINT_CHECK = 275, + // (undocumented) + BE_SQLITE_CONSTRAINT_COMMITHOOK = 531, + // (undocumented) + BE_SQLITE_CONSTRAINT_FOREIGNKEY = 787, + // (undocumented) + BE_SQLITE_CONSTRAINT_FUNCTION = 1043, + // (undocumented) + BE_SQLITE_CONSTRAINT_NOTNULL = 1299, + // (undocumented) + BE_SQLITE_CONSTRAINT_PRIMARYKEY = 1555, + // (undocumented) + BE_SQLITE_CONSTRAINT_TRIGGER = 1811, + // (undocumented) + BE_SQLITE_CONSTRAINT_UNIQUE = 2067, + // (undocumented) + BE_SQLITE_CONSTRAINT_VTAB = 2323, + BE_SQLITE_CORRUPT = 11, + // (undocumented) + BE_SQLITE_CORRUPT_VTAB = 267, + BE_SQLITE_DONE = 101, + BE_SQLITE_EMPTY = 16, + BE_SQLITE_ERROR = 1, + BE_SQLITE_ERROR_AlreadyOpen = 33554442, + BE_SQLITE_ERROR_BadDbProfile = 100663306, + BE_SQLITE_ERROR_ChangeTrackError = 218103818, + BE_SQLITE_ERROR_CouldNotAcquireLocksOrCodes = 352321546, + BE_SQLITE_ERROR_FileExists = 16777226, + BE_SQLITE_ERROR_FileNotFound = 67108874, + BE_SQLITE_ERROR_InvalidChangeSetVersion = 234881034, + BE_SQLITE_ERROR_InvalidProfileVersion = 117440522, + BE_SQLITE_ERROR_NoPropertyTable = 50331658, + BE_SQLITE_ERROR_NoTxnActive = 83886090, + BE_SQLITE_ERROR_ProfileTooNew = 201326602, + BE_SQLITE_ERROR_ProfileTooNewForReadWrite = 184549386, + BE_SQLITE_ERROR_ProfileTooOld = 167772170, + BE_SQLITE_ERROR_ProfileTooOldForReadWrite = 150994954, + BE_SQLITE_ERROR_ProfileUpgradeFailed = 134217738, + BE_SQLITE_ERROR_SchemaImportFailed = 335544330, + BE_SQLITE_ERROR_SchemaLockFailed = 301989898, + BE_SQLITE_ERROR_SchemaTooNew = 268435466, + BE_SQLITE_ERROR_SchemaTooOld = 285212682, + BE_SQLITE_ERROR_SchemaUpgradeFailed = 318767114, + BE_SQLITE_ERROR_SchemaUpgradeRequired = 251658250, + BE_SQLITE_FORMAT = 24, + BE_SQLITE_FULL = 13, + BE_SQLITE_INTERNAL = 2, + BE_SQLITE_INTERRUPT = 9, + BE_SQLITE_IOERR = 10, + // (undocumented) + BE_SQLITE_IOERR_ACCESS = 3338, + // (undocumented) + BE_SQLITE_IOERR_BLOCKED = 2826, + // (undocumented) + BE_SQLITE_IOERR_CHECKRESERVEDLOCK = 3594, + // (undocumented) + BE_SQLITE_IOERR_CLOSE = 4106, + // (undocumented) + BE_SQLITE_IOERR_DELETE = 2570, + // (undocumented) + BE_SQLITE_IOERR_DELETE_NOENT = 5898, + // (undocumented) + BE_SQLITE_IOERR_DIR_CLOSE = 4362, + // (undocumented) + BE_SQLITE_IOERR_DIR_FSYNC = 1290, + // (undocumented) + BE_SQLITE_IOERR_FSTAT = 1802, + // (undocumented) + BE_SQLITE_IOERR_FSYNC = 1034, + // (undocumented) + BE_SQLITE_IOERR_LOCK = 3850, + // (undocumented) + BE_SQLITE_IOERR_NOMEM = 3082, + // (undocumented) + BE_SQLITE_IOERR_RDLOCK = 2314, + // (undocumented) + BE_SQLITE_IOERR_READ = 266, + // (undocumented) + BE_SQLITE_IOERR_SEEK = 5642, + // (undocumented) + BE_SQLITE_IOERR_SHMLOCK = 5130, + // (undocumented) + BE_SQLITE_IOERR_SHMMAP = 5386, + // (undocumented) + BE_SQLITE_IOERR_SHMOPEN = 4618, + // (undocumented) + BE_SQLITE_IOERR_SHMSIZE = 4874, + // (undocumented) + BE_SQLITE_IOERR_SHORT_READ = 522, + // (undocumented) + BE_SQLITE_IOERR_TRUNCATE = 1546, + // (undocumented) + BE_SQLITE_IOERR_UNLOCK = 2058, + // (undocumented) + BE_SQLITE_IOERR_WRITE = 778, + BE_SQLITE_LOCKED = 6, + // (undocumented) + BE_SQLITE_LOCKED_SHAREDCACHE = 262, + BE_SQLITE_MISMATCH = 20, + BE_SQLITE_MISUSE = 21, + BE_SQLITE_NOLFS = 22, + BE_SQLITE_NOMEM = 7, + BE_SQLITE_NOTADB = 26, + BE_SQLITE_NOTFOUND = 12, + // (undocumented) + BE_SQLITE_OK = 0, + BE_SQLITE_PERM = 3, + BE_SQLITE_PROTOCOL = 15, + BE_SQLITE_RANGE = 25, + BE_SQLITE_READONLY = 8, + // (undocumented) + BE_SQLITE_READONLY_CANTLOCK = 520, + // (undocumented) + BE_SQLITE_READONLY_RECOVERY = 264, + // (undocumented) + BE_SQLITE_READONLY_ROLLBACK = 776, + BE_SQLITE_ROW = 100, + BE_SQLITE_SCHEMA = 17, + BE_SQLITE_TOOBIG = 18 +} + +// @public +class Dictionary implements Iterable> { + // WARNING: The name "__@iterator" contains unsupported characters; API names should use only letters, numbers, and underscores + [Symbol.iterator](): Iterator>; + constructor(compareKeys: OrderedComparator, cloneKey?: CloneFunction, cloneValue?: CloneFunction); + // (undocumented) + protected readonly _cloneKey: CloneFunction; + // (undocumented) + protected readonly _cloneValue: CloneFunction; + // (undocumented) + protected readonly _compareKeys: OrderedComparator; + // (undocumented) + protected _keys: K[]; + // (undocumented) + protected _values: V[]; + clear(): void; + delete(key: K): boolean; + extractArrays: { + keys: K[]; + values: V[]; + } + extractPairs(): Array<{ + key: K; + value: V; + }>; + forEach(func: (key: K, value: V) => void): void; + get(key: K): V | undefined; + insert(key: K, value: V): boolean; + readonly length: number; + protected lowerBound: { + equal: boolean; + index: number; + } + set(key: K, value: V): void; +} + +// @public +interface DictionaryEntry { + key: K; + value: V; +} + +// @public +class DisposableList implements IDisposable { + constructor(disposables?: Array); + add(disposable: IDisposable | DisposeFunc): void; + dispose(): void; + remove(disposable: IDisposable): void; +} + +// @public +export function dispose(disposable?: IDisposable): undefined; + +// @public +export function disposeArray(list?: IDisposable[]): undefined; + +// @public +class Entry { + constructor(key: K, value: V); + // (undocumented) + key: K; + // (undocumented) + newer?: Entry; + // (undocumented) + older?: Entry; + // (undocumented) + value: V; +} + +// @alpha +class EnvMacroSubst { + static anyPropertyContainsEnvvars(obj: any, recurse: boolean): boolean; + static containsEnvvars(str: string): boolean; + static replace(str: string, defaultValues?: any): string; + static replaceInProperties(obj: any, recurse: boolean, defaultValues?: any): void; +} + +// @public +module Guid { + function createValue(): GuidString; + + function isGuid(value: string): boolean; + + function isV4Guid(value: string): boolean; + +} + +// @beta +enum HttpStatus { + ClientError = 94211, + Info = 94209, + Redirection = 94210, + ServerError = 94212, + Success = 0 +} + +// WARNING: Unsupported export: invalid +// @public +module Id64 { + function fromJSON(prop?: string): Id64String; + + function fromLocalAndBriefcaseIds(localId: number, briefcaseId: number): Id64String; + + function fromString(val: string): Id64String; + + function fromUint32Pair(lowBytes: number, highBytes: number): Id64String; + + function getBriefcaseId(id: Id64String): number; + + function getLocalId(id: Id64String): number; + + function getLowerUint32(id: Id64String): number; + + function getUint32Pair(id: Id64String): Uint32Pair; + + function getUpperUint32(id: Id64String): number; + + function isId64(id: string): boolean; + + function isInvalid(id: Id64String): boolean; + + function isTransient(id: Id64String): boolean; + + function isTransientId64(id: string): boolean; + + function isValid(id: Id64String): boolean; + + function isValidId64(id: string): boolean; + + // (undocumented) + function isValidUint32Pair(lowBytes: number, highBytes: number): boolean; + + function toIdSet(arg: Id64Arg): Id64Set; + + // @public + class Uint32Map { + // (undocumented) + protected readonly _map: Map>; + clear(): void; + get(low: number, high: number): T | undefined; + getById(id: Id64String): T | undefined; + readonly isEmpty: boolean; + set(low: number, high: number, value: T): void; + setById(id: Id64String, value: T): void; + readonly size: number; + } + + interface Uint32Pair { + lower: number; + upper: number; + } + + // @public + class Uint32Set { + // (undocumented) + protected readonly _map: Map>; + add(low: number, high: number): void; + addId(id: Id64String): void; + clear(): void; + has(low: number, high: number): boolean; + hasId(id: Id64String): boolean; + readonly isEmpty: boolean; + readonly size: number; + toId64Array(): Id64Array; + toId64Set(): Id64Set; + } + +} + +// @public +interface IDisposable { + dispose(): void; +} + +// @beta +enum IModelHubStatus { + // (undocumented) + AnotherUserPushing = 102409, + // (undocumented) + BriefcaseDoesNotBelongToUser = 102408, + // (undocumented) + BriefcaseDoesNotExist = 102407, + // (undocumented) + ChangeSetAlreadyExists = 102410, + // (undocumented) + ChangeSetAlreadyHasVersion = 102438, + // (undocumented) + ChangeSetDoesNotExist = 102411, + // (undocumented) + ChangeSetPointsToBadSeed = 102414, + // (undocumented) + CodeDoesNotExist = 102431, + // (undocumented) + CodeReservedByAnotherBriefcase = 102430, + // (undocumented) + CodesExist = 102421, + // (undocumented) + CodeStateInvalid = 102429, + // (undocumented) + ConflictsAggregate = 102441, + // (undocumented) + DatabaseOperationFailed = 102443, + // (undocumented) + DatabaseTemporarilyLocked = 102419, + // (undocumented) + EventSubscriptionAlreadyExists = 102434, + // (undocumented) + EventSubscriptionDoesNotExist = 102433, + // (undocumented) + EventTypeDoesNotExist = 102432, + // (undocumented) + FailedToGetProjectById = 102442, + // (undocumented) + FailedToGetProjectMembers = 102437, + // (undocumented) + FailedToGetProjectPermissions = 102436, + // (undocumented) + FileAlreadyExists = 102426, + // (undocumented) + FileDoesNotExist = 102425, + // (undocumented) + FileHandlerNotSet = 102661, + // (undocumented) + FileIsNotUploaded = 102412, + // (undocumented) + FileNotFound = 102662, + // (undocumented) + iModelAlreadyExists = 102423, + // (undocumented) + iModelDoesNotExist = 102424, + // (undocumented) + IMODELHUBERROR_BASE = 102400, + // (undocumented) + IMODELHUBERROR_REQUESTERRORBASE = 102656, + // (undocumented) + iModelIsLocked = 102420, + // (undocumented) + iModelIsNotInitialized = 102413, + // (undocumented) + InvalidArgumentError = 102658, + // (undocumented) + InvalidBriefcase = 102406, + // (undocumented) + InvalidPropertiesValues = 102403, + // (undocumented) + JobSchedulingFailed = 102440, + // (undocumented) + LockDoesNotExist = 102427, + // (undocumented) + LockOwnedByAnotherBriefcase = 102428, + // (undocumented) + LocksExist = 102422, + // (undocumented) + MaximumNumberOfBriefcasesPerUser = 102417, + // (undocumented) + MaximumNumberOfBriefcasesPerUserPerMinute = 102418, + // (undocumented) + MissingDownloadUrlError = 102659, + // (undocumented) + MissingRequiredProperties = 102402, + // (undocumented) + NotSupportedInBrowser = 102660, + // (undocumented) + OperationFailed = 102415, + // (undocumented) + ProjectIdIsNotSpecified = 102435, + // (undocumented) + PullIsRequired = 102416, + // (undocumented) + SeedFileInitializationFailed = 102444, + // (undocumented) + Success = 0, + // (undocumented) + UndefinedArgumentError = 102657, + // (undocumented) + Unknown = 102401, + // (undocumented) + UserDoesNotHaveAccess = 102405, + // (undocumented) + UserDoesNotHavePermission = 102404, + // (undocumented) + VersionAlreadyExists = 102439 +} + +// @public +enum IModelStatus { + // (undocumented) + AlreadyLoaded = 65537, + // (undocumented) + AlreadyOpen = 65538, + // (undocumented) + BadArg = 65539, + // (undocumented) + BadElement = 65540, + // (undocumented) + BadModel = 65541, + // (undocumented) + BadRequest = 65542, + // (undocumented) + BadSchema = 65543, + // (undocumented) + CannotUndo = 65544, + // (undocumented) + CodeNotReserved = 65545, + // (undocumented) + ConstraintNotUnique = 65601, + // (undocumented) + DeletionProhibited = 65546, + // (undocumented) + DuplicateCode = 65547, + // (undocumented) + DuplicateName = 65548, + // (undocumented) + ElementBlockedChange = 65549, + // (undocumented) + FileAlreadyExists = 65550, + // (undocumented) + FileNotFound = 65551, + // (undocumented) + FileNotLoaded = 65552, + // (undocumented) + ForeignKeyConstraint = 65553, + // (undocumented) + IdExists = 65554, + // (undocumented) + IMODEL_ERROR_BASE = 65536, + // (undocumented) + InDynamicTransaction = 65555, + // (undocumented) + InvalidCategory = 65556, + // (undocumented) + InvalidCode = 65557, + // (undocumented) + InvalidCodeSpec = 65558, + // (undocumented) + InvalidId = 65559, + // (undocumented) + InvalidName = 65560, + // (undocumented) + InvalidParent = 65561, + // (undocumented) + InvalidProfileVersion = 65562, + // (undocumented) + IsCreatingChangeSet = 65563, + // (undocumented) + LockNotHeld = 65564, + // (undocumented) + Mismatch2d3d = 65565, + // (undocumented) + MismatchGcs = 65566, + // (undocumented) + MissingDomain = 65567, + // (undocumented) + MissingHandler = 65568, + // (undocumented) + MissingId = 65569, + // (undocumented) + NoGeoLocation = 65602, + // (undocumented) + NoGeometry = 65570, + // (undocumented) + NoMultiTxnOperation = 65571, + // (undocumented) + NotDgnMarkupProject = 65572, + // (undocumented) + NotEnabled = 65573, + // (undocumented) + NotFound = 65574, + // (undocumented) + NothingToRedo = 65578, + // (undocumented) + NothingToUndo = 65579, + // (undocumented) + NotOpen = 65575, + // (undocumented) + NotOpenForWrite = 65576, + // (undocumented) + NotSameUnitBase = 65577, + // (undocumented) + ParentBlockedChange = 65580, + // (undocumented) + ReadError = 65581, + // (undocumented) + ReadOnly = 65582, + // (undocumented) + ReadOnlyDomain = 65583, + // (undocumented) + RepositoryManagerError = 65584, + // (undocumented) + SQLiteError = 65585, + // (undocumented) + Success = 0, + // (undocumented) + TransactionActive = 65586, + // (undocumented) + UnitsMissing = 65587, + // (undocumented) + UnknownFormat = 65588, + // (undocumented) + UpgradeFailed = 65589, + // (undocumented) + ValidationFailed = 65590, + // (undocumented) + VersionTooNew = 65591, + // (undocumented) + VersionTooOld = 65592, + // (undocumented) + ViewNotFound = 65593, + // (undocumented) + WriteError = 65594, + // (undocumented) + WrongClass = 65595, + // (undocumented) + WrongDomain = 65597, + // (undocumented) + WrongElement = 65598, + // (undocumented) + WrongHandler = 65599, + // (undocumented) + WrongIModel = 65596, + // (undocumented) + WrongModel = 65600 +} + +// @public +class IndexedValue { + constructor(value: T, index: number); + // (undocumented) + readonly index: number; + // (undocumented) + readonly value: T; +} + +// @public +class IndexMap { + constructor(compare: OrderedComparator, maximumSize?: number, clone?: CloneFunction); + // (undocumented) + protected _array: Array>; + // (undocumented) + protected readonly _clone: CloneFunction; + // (undocumented) + protected readonly _compareValues: OrderedComparator; + // (undocumented) + protected readonly _maximumSize: number; + clear(): void; + indexOf(value: T): number; + insert(value: T, onInsert?: (value: T) => any): number; + readonly isEmpty: boolean; + readonly isFull: boolean; + readonly length: number; + // (undocumented) + protected lowerBound: { + equal: boolean; + index: number; + } +} + +// @public +module JsonUtils { + function asArray(json: any): any; + + function asBool(json: any, defaultVal?: boolean): boolean; + + function asDouble(json: any, defaultVal?: number): number; + + function asInt(json: any, defaultVal?: number): number; + + function asObject(json: any): any; + + function asString(json: any, defaultVal?: string): string; + + function setOrRemoveBoolean(json: any, key: string, val: boolean, defaultVal: boolean): void; + + function setOrRemoveNumber(json: any, key: string, val: number, defaultVal: number): void; + + function toObject(val: any): any; + +} + +// @public +class Logger { + static addActivityId(mdata: any): void; + static configureLevels(cfg: LoggerLevelsConfig): void; + static getLevel(category: string): LogLevel | undefined; + static initialize(logError: LogFunction | undefined, logWarning?: LogFunction | undefined, logInfo?: LogFunction | undefined, logTrace?: LogFunction | undefined): void; + static initializeToConsole(): void; + static isEnabled(category: string, level: LogLevel): boolean; + static logError(category: string, message: string, metaData?: GetMetaDataFunction): void; + static logException(category: string, err: Error, log?: LogFunction, metaData?: GetMetaDataFunction): void; + static logExceptionCallstacks: boolean; + static logInfo(category: string, message: string, metaData?: GetMetaDataFunction): void; + static logTrace(category: string, message: string, metaData?: GetMetaDataFunction): void; + static logWarning(category: string, message: string, metaData?: GetMetaDataFunction): void; + static makeMetaData(getMetaData?: GetMetaDataFunction): any; + static parseLogLevel(str: string): LogLevel; + static setLevel(category: string, minLevel: LogLevel): void; + static setLevelDefault(minLevel: LogLevel): void; + static turnOffCategories(): void; + static turnOffLevelDefault(): void; + static validateProps(config: any): void; +} + +// @public +interface LoggerCategoryAndLevel { + // (undocumented) + category: string; + // (undocumented) + logLevel: string; +} + +// @public +interface LoggerLevelsConfig { + // (undocumented) + categoryLevels?: LoggerCategoryAndLevel[]; + // (undocumented) + defaultLevel?: string; +} + +// @public +enum LogLevel { + Error = 3, + Info = 1, + None = 4, + Trace = 0, + Warning = 2 +} + +// @public +export function lowerBound(value: T, list: U[], compare: OrderedComparator): { + index: number; + equal: boolean; +}; + +// @public +class LRUMap { + constructor(limit: number); + assign(entries: Iterable<[K, V]>): void; + clear(): void; + delete(key: K): V | undefined; + entries(): Iterator<[K, V] | undefined> | undefined; + find(key: K): V | undefined; + forEach(fun: (value: V, key: K, m: LRUMap) => void, thisObj?: any): void; + get(key: K): V | undefined; + has(key: K): boolean; + keys(): Iterator | undefined; + limit: number; + newest?: Entry; + oldest?: Entry; + set(key: K, value: V): LRUMap; + shift(): [K, V] | undefined; + size: number; + toJSON(): Array<{ + key: K; + value: V; + }>; + toString(): string; + values(): Iterator | undefined; +} + +// @public +enum OpenMode { + // (undocumented) + Readonly = 1, + // (undocumented) + ReadWrite = 2 +} + +// @public +class PerfLogger implements IDisposable { + constructor(routine: string); + // (undocumented) + dispose(): void; +} + +// @public +class PriorityQueue implements Iterable { + // WARNING: The name "__@iterator" contains unsupported characters; API names should use only letters, numbers, and underscores + [Symbol.iterator](): Iterator; + constructor(compare: OrderedComparator, clone?: CloneFunction); + // (undocumented) + protected _array: T[]; + // (undocumented) + protected readonly _clone: CloneFunction; + // (undocumented) + protected readonly _compare: OrderedComparator; + // (undocumented) + protected _heapify(index: number): void; + protected _peek(index: number): T | undefined; + protected _pop(index: number): T | undefined; + // (undocumented) + protected _swap(a: number, b: number): void; + clear(): void; + readonly front: T | undefined; + readonly isEmpty: boolean; + readonly length: number; + pop(): T | undefined; + push(value: T): T; + sort(): void; +} + +// @beta +enum RepositoryStatus { + CannotCreateChangeSet = 86023, + ChangeSetRequired = 86025, + CodeNotReserved = 86027, + CodeUnavailable = 86026, + CodeUsed = 86028, + InvalidRequest = 86024, + InvalidResponse = 86020, + LockAlreadyHeld = 86018, + LockNotHeld = 86029, + LockUsed = 86022, + PendingTransactions = 86021, + RepositoryIsLocked = 86030, + ServerUnavailable = 86017, + // (undocumented) + Success = 0, + SyncError = 86019 +} + +// @beta +enum RpcInterfaceStatus { + IncompatibleVersion = 135168, + // (undocumented) + RPC_INTERFACE_ERROR_BASE = 135168, + // (undocumented) + Success = 0 +} + +// @public +export function shallowClone(value: T): T; + +// @public +class SortedArray implements Iterable { + // WARNING: The name "__@iterator" contains unsupported characters; API names should use only letters, numbers, and underscores + [Symbol.iterator](): Iterator; + constructor(compare: OrderedComparator, allowDuplicates?: boolean, clone?: CloneFunction); + // (undocumented) + protected readonly _allowDuplicates: boolean; + // (undocumented) + protected _array: T[]; + // (undocumented) + protected readonly _clone: CloneFunction; + // (undocumented) + protected readonly _compare: OrderedComparator; + clear(): void; + contains(value: T): boolean; + extractArray(): T[]; + findEqual(value: T): T | undefined; + forEach(func: (value: T) => void): void; + get(index: number): T | undefined; + indexOf(value: T): number; + insert(value: T, onInsert?: (value: T) => any): number; + readonly isEmpty: boolean; + readonly length: number; + protected lowerBound: { + equal: boolean; + index: number; + } + remove(value: T): number; +} + +// @beta +interface StatusCodeWithMessage { + // (undocumented) + message: string; + // (undocumented) + status: ErrorCodeType; +} + +// @public +class StopWatch { + constructor(description?: string | undefined, startImmediately?: boolean); + readonly current: BeDuration; + readonly currentSeconds: number; + // (undocumented) + description?: string | undefined; + readonly elapsed: BeDuration; + readonly elapsedSeconds: number; + reset(): void; + start(): void; + stop(): BeDuration; +} + +// @public +class TransientIdSequence { + readonly next: Id64String; +} + +// @public +export function using(resources: T | T[], func: (...r: T[]) => TResult): TResult; + +// @public +export function utf8ToString(utf8: Uint8Array): string | undefined; + +// @beta +enum WSStatus { + // (undocumented) + ClassNotFound = 98311, + // (undocumented) + FileNotFound = 98314, + // (undocumented) + InstanceNotFound = 98313, + // (undocumented) + LoginFailed = 98306, + // (undocumented) + LoginRequired = 98319, + // (undocumented) + NoClientLicense = 98317, + // (undocumented) + NoServerLicense = 98316, + // (undocumented) + NotEnoughRights = 98308, + // (undocumented) + NotSupported = 98315, + // (undocumented) + PropertyNotFound = 98312, + // (undocumented) + RepositoryNotFound = 98309, + // (undocumented) + SchemaNotFound = 98310, + // (undocumented) + SslRequired = 98307, + // (undocumented) + Success = 0, + // (undocumented) + TooManyBadLoginAttempts = 98318, + // (undocumented) + Unknown = 98305, + // (undocumented) + WSERROR_BASE = 98304 +} + +// WARNING: Unsupported export: Listener +// WARNING: Unsupported export: GetMetaDataFunction +// WARNING: Unsupported export: OrderedComparator +// WARNING: Unsupported export: DisposeFunc +// WARNING: Unsupported export: Id64String +// WARNING: Unsupported export: GuidString +// WARNING: Unsupported export: Id64Set +// WARNING: Unsupported export: Id64Array +// WARNING: Unsupported export: Id64Arg +// WARNING: Unsupported export: LogFunction +// WARNING: Unsupported export: CloneFunction +// WARNING: Unsupported export: ComputePriorityFunction +// (No @packagedocumentation comment for this package) diff --git a/common/api/ecschema-metadata.api.ts b/common/api/ecschema-metadata.api.ts new file mode 100644 index 0000000..9b19178 --- /dev/null +++ b/common/api/ecschema-metadata.api.ts @@ -0,0 +1,2113 @@ +// @public (undocumented) +class ArrayProperty extends Property { + // (undocumented) + protected _maxOccurs?: number; + // (undocumented) + protected _minOccurs: number; + // (undocumented) + readonly maxOccurs: number | undefined; + // (undocumented) + readonly minOccurs: number; +} + +// @public +class BaseDiagnostic implements IDiagnostic { + constructor(ecDefinition: TYPE, messageArgs: ARGS); + readonly category: DiagnosticCategory; + readonly code: string; + readonly diagnosticType: DiagnosticType; + ecDefinition: TYPE; + messageArgs: ARGS; + readonly messageText: string; +} + +// @public +class ClassDiagnostic extends SchemaItemDiagnostic { + constructor(ecClass: AnyClass, messageArgs: ARGS); +} + +// @public +export function classModifierToString(modifier: ECClassModifier): string; + +// @public +class Constant extends SchemaItem { + constructor(schema: Schema, name: string); + // (undocumented) + protected _definition: string; + // (undocumented) + protected _denominator: number; + // (undocumented) + protected _numerator: number; + // (undocumented) + protected _phenomenon?: LazyLoadedPhenomenon; + // (undocumented) + readonly definition: string; + // (undocumented) + readonly denominator: number; + // WARNING: The type "ConstantProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserialize(constantProps: ConstantProps): Promise; + // WARNING: The type "ConstantProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserializeSync(constantProps: ConstantProps): void; + // (undocumented) + readonly numerator: number; + // (undocumented) + readonly phenomenon: LazyLoadedPhenomenon | undefined; + // WARNING: The type "SchemaItemType.Constant" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly schemaItemType: SchemaItemType.Constant; + // (undocumented) + toJson: { + [value: string]: any; + } +} + +// @public +export function containerTypeToString(type: CustomAttributeContainerType): string; + +// @public +export function createClassDiagnosticClass(code: string, messageText: string, category?: DiagnosticCategory): { + new (ecClass: AnyClass, messageArgs: ARGS): { + readonly code: string; + readonly category: DiagnosticCategory; + readonly messageText: string; + readonly diagnosticType: DiagnosticType; + ecDefinition: AnyClass; + messageArgs: ARGS; + }; + diagnosticType: DiagnosticType; +}; + +// @public +export function createCustomAttributeContainerDiagnosticClass(code: string, messageText: string, category?: DiagnosticCategory): { + new (container: CustomAttributeContainerProps, messageArgs: ARGS): { + readonly code: string; + readonly category: DiagnosticCategory; + readonly messageText: string; + readonly diagnosticType: DiagnosticType; + ecDefinition: CustomAttributeContainerProps; + messageArgs: ARGS; + }; +}; + +// @public +export function createPropertyDiagnosticClass(code: string, messageText: string, category?: DiagnosticCategory): { + new (property: AnyProperty, messageArgs: ARGS): { + readonly code: string; + readonly category: DiagnosticCategory; + readonly messageText: string; + readonly diagnosticType: DiagnosticType; + ecDefinition: AnyProperty; + messageArgs: ARGS; + }; +}; + +// @public +export function createRelationshipConstraintDiagnosticClass(code: string, messageText: string, category?: DiagnosticCategory): { + new (constraint: RelationshipConstraint, messageArgs: ARGS): { + readonly code: string; + readonly category: DiagnosticCategory; + readonly messageText: string; + readonly diagnosticType: DiagnosticType; + ecDefinition: RelationshipConstraint; + messageArgs: ARGS; + }; +}; + +// @public +export function createSchemaDiagnosticClass(code: string, messageText: string, category?: DiagnosticCategory): { + new (schema: Schema, messageArgs: ARGS): { + readonly code: string; + readonly category: DiagnosticCategory; + readonly messageText: string; + readonly diagnosticType: DiagnosticType; + ecDefinition: Schema; + messageArgs: ARGS; + }; + diagnosticType: DiagnosticType; +}; + +// @public +export function createSchemaItemDiagnosticClass(code: string, messageText: string, category?: DiagnosticCategory): { + new (ecDefinition: ITEM, messageArgs: ARGS): { + readonly code: string; + readonly category: DiagnosticCategory; + readonly messageText: string; + readonly diagnosticType: DiagnosticType; + ecDefinition: ITEM; + messageArgs: ARGS; + }; + diagnosticType: DiagnosticType; +}; + +// @public +class CustomAttributeClass extends ECClass { + constructor(schema: Schema, name: string, modifier?: ECClassModifier); + // (undocumented) + protected _containerType?: CustomAttributeContainerType; + // (undocumented) + readonly containerType: CustomAttributeContainerType; + // WARNING: The type "CustomAttributeClassProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserialize(customAttributeProps: CustomAttributeClassProps): Promise; + // WARNING: The type "CustomAttributeClassProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserializeSync(customAttributeProps: CustomAttributeClassProps): void; + // WARNING: The type "SchemaItemType.CustomAttributeClass" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly schemaItemType: SchemaItemType.CustomAttributeClass; + // (undocumented) + toJson: { + [value: string]: any; + } +} + +// WARNING: The type "CustomAttributeContainerProps" needs to be exported by the package (e.g. added to index.ts) +// @public +class CustomAttributeContainerDiagnostic extends BaseDiagnostic { + // WARNING: The type "CustomAttributeContainerProps" needs to be exported by the package (e.g. added to index.ts) + constructor(container: CustomAttributeContainerProps, messageArgs: ARGS); + // (undocumented) + readonly diagnosticType: DiagnosticType; +} + +// @public +enum CustomAttributeContainerType { + // (undocumented) + Any = 4095, + // (undocumented) + AnyClass = 30, + // (undocumented) + AnyProperty = 992, + // (undocumented) + AnyRelationshipConstraint = 3072, + // (undocumented) + CustomAttributeClass = 4, + // (undocumented) + EntityClass = 2, + // (undocumented) + NavigationProperty = 512, + // (undocumented) + PrimitiveArrayProperty = 128, + // (undocumented) + PrimitiveProperty = 32, + // (undocumented) + RelationshipClass = 16, + // (undocumented) + Schema = 1, + // (undocumented) + SourceRelationshipConstraint = 1024, + // (undocumented) + StructArrayProperty = 256, + // (undocumented) + StructClass = 8, + // (undocumented) + StructProperty = 64, + // (undocumented) + TargetRelationshipConstraint = 2048 +} + +// @public (undocumented) +enum DecimalPrecision { + // (undocumented) + Eight = 8, + // (undocumented) + Eleven = 11, + // (undocumented) + Five = 5, + // (undocumented) + Four = 4, + // (undocumented) + Nine = 9, + // (undocumented) + One = 1, + // (undocumented) + Seven = 7, + // (undocumented) + Six = 6, + // (undocumented) + Ten = 10, + // (undocumented) + Three = 3, + // (undocumented) + Twelve = 12, + // (undocumented) + Two = 2, + // (undocumented) + Zero = 0 +} + +// @public +class DelayedPromise implements Promise { + // WARNING: The name "__@toStringTag" contains unsupported characters; API names should use only letters, numbers, and underscores + // (undocumented) + readonly __@toStringTag: "Promise"; + constructor(startCallback: () => Promise); + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise; + start: () => Promise; + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise; +} + +// @public (undocumented) +interface DelayedPromiseWithPropsConstructor { + new (props: TProps, startCallback: () => Promise): Readonly & DelayedPromise; +} + +// @public +enum DiagnosticCategory { + // (undocumented) + Error = 1, + // (undocumented) + Message = 3, + // (undocumented) + Suggestion = 2, + // (undocumented) + Warning = 0 +} + +// @public (undocumented) +export function diagnosticCategoryToString(category: DiagnosticCategory): "Error" | "Warning" | "Message" | "Suggestion"; + +// @public +class DiagnosticReporterBase implements IDiagnosticReporter { + constructor(i18n?: I18N); + protected formatStringFromArgs(text: string, args: ArrayLike, baseIndex?: number): string; + i18N?: I18N; + report(diagnostic: AnyDiagnostic): void; + protected abstract reportDiagnostic(diagnostic: AnyDiagnostic, messageText: string): void; +} + +// @public +enum DiagnosticType { + // (undocumented) + CustomAttributeContainer = 4, + // (undocumented) + None = 0, + // (undocumented) + Property = 3, + // (undocumented) + RelationshipConstraint = 5, + // (undocumented) + Schema = 1, + // (undocumented) + SchemaItem = 2 +} + +// @public (undocumented) +export function diagnosticTypeToString(type: DiagnosticType): "Schema" | "None" | "CustomAttributeContainer" | "Property" | "RelationshipConstraint" | "SchemaItem"; + +// @public +class ECClass extends SchemaItem, implements CustomAttributeContainerProps { + constructor(schema: Schema, name: string, modifier?: ECClassModifier); + // (undocumented) + protected _baseClass?: LazyLoadedECClass; + // (undocumented) + protected _modifier: ECClassModifier; + // (undocumented) + protected _properties?: Property[]; + // WARNING: The type "CustomAttribute" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected addCustomAttribute(customAttribute: CustomAttribute): void; + protected addProperty(prop: T): T; + // (undocumented) + baseClass: LazyLoadedECClass | undefined; + // (undocumented) + protected buildPropertyCache(result: Property[], existingValues?: Map, resetBaseCaches?: boolean): Promise; + // (undocumented) + protected buildPropertyCacheSync(result: Property[], existingValues?: Map, resetBaseCaches?: boolean): void; + protected createPrimitiveArrayProperty(name: string, primitiveType: PrimitiveType): Promise; + protected createPrimitiveArrayPropertySync(name: string, primitiveType: PrimitiveType): PrimitiveArrayProperty; + protected createPrimitiveProperty(name: string, primitiveType: PrimitiveType): Promise; + protected createPrimitivePropertySync(name: string, primitiveType: PrimitiveType): PrimitiveProperty; + // (undocumented) + protected createStructArrayProperty(name: string, structType: string | StructClass): Promise; + // (undocumented) + protected createStructArrayPropertySync(name: string, structType: string | StructClass): StructArrayProperty; + // (undocumented) + protected createStructProperty(name: string, structType: string | StructClass): Promise; + // (undocumented) + protected createStructPropertySync(name: string, structType: string | StructClass): StructProperty; + // WARNING: The type "CustomAttributeSet" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly customAttributes: CustomAttributeSet | undefined; + // WARNING: The type "ClassProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserialize(classProps: ClassProps): Promise; + // WARNING: The type "ClassProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserializeSync(classProps: ClassProps): void; + getAllBaseClasses(): AsyncIterableIterator; + // (undocumented) + getAllBaseClassesSync(): Iterable; + // (undocumented) + getBaseClassSync(): ECClass | undefined; + getInheritedProperty(name: string): Promise; + getInheritedPropertySync(name: string): Property | undefined; + getProperties(resetCache?: boolean): Promise; + getPropertiesSync(resetCache?: boolean): Property[]; + getProperty(name: string, includeInherited?: boolean): Promise; + getPropertySync(name: string, includeInherited?: boolean): Property | undefined; + is(targetClass: string, schemaName: string): Promise; + isSync(targetClass: ECClass): boolean; + // (undocumented) + protected loadPrimitiveType(primitiveType: string | PrimitiveType | Enumeration | undefined, schema: Schema): Promise; + // (undocumented) + protected loadPrimitiveTypeSync(primitiveType: string | PrimitiveType | Enumeration | undefined, schema: Schema): PrimitiveType | Enumeration; + // (undocumented) + protected loadStructType(structType: string | StructClass | undefined, schema: Schema): Promise; + // (undocumented) + protected loadStructTypeSync(structType: string | StructClass | undefined, schema: Schema): StructClass; + // (undocumented) + protected static mergeProperties(target: Property[], existingValues: Map, propertiesToMerge: Property[], overwriteExisting: boolean): void; + // (undocumented) + readonly modifier: ECClassModifier; + // (undocumented) + readonly properties: Property[] | undefined; + // (undocumented) + toJson: { + [value: string]: any; + } + traverseBaseClasses(callback: (ecClass: ECClass, arg?: any) => boolean, arg?: any): Promise; + traverseBaseClassesSync(callback: (ecClass: ECClass, arg?: any) => boolean, arg?: any): boolean; +} + +// @public (undocumented) +enum ECClassModifier { + // (undocumented) + Abstract = 1, + // (undocumented) + None = 0, + // (undocumented) + Sealed = 2 +} + +// @public +class ECName { + constructor(name: string); + // (undocumented) + readonly name: string; + // (undocumented) + static validate(newName: string): boolean; +} + +// @public (undocumented) +class ECObjectsError extends BentleyError { + constructor(errorNumber: number, message?: string); + // (undocumented) + readonly errorNumber: number; + // (undocumented) + toDebugString(): string; +} + +// @public (undocumented) +enum ECObjectsStatus { + // (undocumented) + ClassNotFound = 35074, + // (undocumented) + DifferentSchemaContexts = 35076, + // (undocumented) + DuplicateItem = 35053, + // (undocumented) + DuplicateProperty = 35054, + // (undocumented) + DuplicateSchema = 35055, + // (undocumented) + ECOBJECTS_ERROR_BASE = 35052, + // (undocumented) + ImmutableSchema = 35056, + // (undocumented) + InvalidContainerType = 35057, + // (undocumented) + InvalidECJson = 35058, + // (undocumented) + InvalidECName = 35059, + // (undocumented) + InvalidECVersion = 35060, + // (undocumented) + InvalidEnumValue = 35061, + // (undocumented) + InvalidModifier = 35062, + // (undocumented) + InvalidMultiplicity = 35063, + // (undocumented) + InvalidPrimitiveType = 35064, + // (undocumented) + InvalidRelationshipEnd = 35068, + // (undocumented) + InvalidSchemaItemType = 35065, + // (undocumented) + InvalidSchemaString = 35073, + // (undocumented) + InvalidSchemaXML = 35072, + // (undocumented) + InvalidStrength = 35066, + // (undocumented) + InvalidStrengthDirection = 35067, + // (undocumented) + InvalidType = 35069, + // (undocumented) + MissingSchemaUrl = 35070, + // (undocumented) + SchemaContextUndefined = 35075, + // (undocumented) + Success = 0, + // (undocumented) + UnableToLocateSchema = 35071 +} + +// @public (undocumented) +class ECStringConstants { + // (undocumented) + static readonly CONTAINERTYPE_ANY: string; + // (undocumented) + static readonly CONTAINERTYPE_ANYCLASS: string; + // (undocumented) + static readonly CONTAINERTYPE_ANYPROPERTY: string; + // (undocumented) + static readonly CONTAINERTYPE_ANYRELATIONSHIPCONSTRAINT: string; + // (undocumented) + static readonly CONTAINERTYPE_CUSTOMATTRIBUTECLASS: string; + // (undocumented) + static readonly CONTAINERTYPE_ENTITYCLASS: string; + // (undocumented) + static readonly CONTAINERTYPE_NAVIGATIONPROPERTY: string; + // (undocumented) + static readonly CONTAINERTYPE_PRIMITIVEARRAYPROPERTY: string; + // (undocumented) + static readonly CONTAINERTYPE_PRIMITIVEPROPERTY: string; + // (undocumented) + static readonly CONTAINERTYPE_RELATIONSHIPCLASS: string; + // (undocumented) + static readonly CONTAINERTYPE_SCHEMA: string; + // (undocumented) + static readonly CONTAINERTYPE_SOURCERELATIONSHIPCONSTRAINT: string; + // (undocumented) + static readonly CONTAINERTYPE_STRUCTARRAYPROPERTY: string; + // (undocumented) + static readonly CONTAINERTYPE_STRUCTCLASS: string; + // (undocumented) + static readonly CONTAINERTYPE_STRUCTPROPERTY: string; + // (undocumented) + static readonly CONTAINERTYPE_TARGETRELATIONSHIPCONSTRAINT: string; + // (undocumented) + static readonly RELATIONSHIP_END_SOURCE: string; + // (undocumented) + static readonly RELATIONSHIP_END_TARGET: string; +} + +// @public (undocumented) +class ECVersion { + constructor(read?: number, write?: number, minor?: number); + compare(rhv: ECVersion): number; + static fromString(versionString: string): ECVersion; + // (undocumented) + readonly minor: number; + // (undocumented) + readonly read: number; + toString(padZeroes?: boolean): string; + // (undocumented) + readonly write: number; +} + +// @public +class EntityClass extends ECClass { + constructor(schema: Schema, name: string, modifier?: ECClassModifier); + // (undocumented) + protected _mixins?: LazyLoadedMixin[]; + // (undocumented) + protected addMixin(mixin: Mixin): void; + // (undocumented) + protected buildPropertyCache(result: Property[], existingValues?: Map, resetBaseCaches?: boolean): Promise; + // (undocumented) + protected buildPropertyCacheSync(result: Property[], existingValues?: Map, resetBaseCaches?: boolean): void; + // (undocumented) + protected createNavigationProperty(name: string, relationship: string | RelationshipClass, direction: string | StrengthDirection): Promise; + // (undocumented) + protected createNavigationPropertySync(name: string, relationship: string | RelationshipClass, direction: string | StrengthDirection): NavigationProperty; + // WARNING: The type "EntityClassProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserialize(entityClassProps: EntityClassProps): Promise; + // WARNING: The type "EntityClassProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserializeSync(entityClassProps: EntityClassProps): void; + getInheritedProperty(name: string): Promise; + getInheritedPropertySync(name: string): Property | undefined; + // (undocumented) + getMixinsSync(): Iterable; + // (undocumented) + readonly mixins: LazyLoadedMixin[]; + // WARNING: The type "SchemaItemType.EntityClass" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly schemaItemType: SchemaItemType.EntityClass; + // (undocumented) + toJson(standalone: boolean, includeSchemaVersion: boolean): any | void; +} + +// @public +class Enumeration extends SchemaItem { + // WARNING: The type "PrimitiveType.Integer" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "PrimitiveType.String" needs to be exported by the package (e.g. added to index.ts) + constructor(schema: Schema, name: string, primitiveType?: PrimitiveType.Integer | PrimitiveType.String); + // (undocumented) + protected _enumerators: AnyEnumerator[]; + // (undocumented) + protected _isStrict: boolean; + // WARNING: The type "PrimitiveType.Integer" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "PrimitiveType.String" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected _type?: PrimitiveType.Integer | PrimitiveType.String; + protected addEnumerator(enumerator: AnyEnumerator): void; + createEnumerator(name: string, value: string | number, label?: string, description?: string): AnyEnumerator; + // WARNING: The type "EnumerationProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserialize(enumerationProps: EnumerationProps): Promise; + // WARNING: The type "EnumerationProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserializeSync(enumerationProps: EnumerationProps): void; + // (undocumented) + readonly enumerators: Enumerator[]; + getEnumerator(value: string): Enumerator | undefined; + getEnumeratorByName(name: string): AnyEnumerator | undefined; + // (undocumented) + readonly isInt: boolean; + // (undocumented) + readonly isStrict: boolean; + // (undocumented) + readonly isString: boolean; + // WARNING: The type "SchemaItemType.Enumeration" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly schemaItemType: SchemaItemType.Enumeration; + // (undocumented) + toJson: { + [value: string]: any; + } + // WARNING: The type "PrimitiveType.Integer" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "PrimitiveType.String" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly type: PrimitiveType.Integer | PrimitiveType.String | undefined; +} + +// @public (undocumented) +class EnumerationArrayProperty extends EnumerationArrayProperty_base { + constructor(ecClass: ECClass, name: string, type: LazyLoadedEnumeration); +} + +// @public (undocumented) +class EnumerationProperty extends PrimitiveOrEnumPropertyBase { + constructor(ecClass: ECClass, name: string, type: LazyLoadedEnumeration); + // (undocumented) + protected _enumeration?: LazyLoadedEnumeration; + // WARNING: The type "EnumerationPropertyProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserialize(enumerationPropertyProps: EnumerationPropertyProps): Promise; + // WARNING: The type "EnumerationPropertyProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserializeSync(enumerationPropertyProps: EnumerationPropertyProps): void; + // (undocumented) + readonly enumeration: LazyLoadedEnumeration | undefined; + // (undocumented) + toJson(): any; +} + +// @public (undocumented) +interface Enumerator { + // (undocumented) + readonly description?: string; + // (undocumented) + readonly label?: string; + // (undocumented) + readonly name: string; + // (undocumented) + readonly value: T; +} + +// @public +class FileSchemaKey extends SchemaKey { + constructor(key: SchemaKey, fileName: string, schemaJson?: string); + // (undocumented) + fileName: string; + // (undocumented) + schemaText?: string; +} + +// @public (undocumented) +class Format extends SchemaItem { + constructor(schema: Schema, name: string); + // (undocumented) + protected _decimalSeparator: string; + // (undocumented) + protected _formatTraits: FormatTraits; + // (undocumented) + protected _includeZero: boolean; + // (undocumented) + protected _minWidth?: number; + // (undocumented) + protected _precision: number; + // (undocumented) + protected _roundFactor: number; + // (undocumented) + protected _scientificType?: ScientificType; + // (undocumented) + protected _showSignOption: ShowSignOption; + // (undocumented) + protected _spacer: string; + // (undocumented) + protected _stationOffsetSize?: number; + // (undocumented) + protected _stationSeparator: string; + // (undocumented) + protected _thousandSeparator: string; + // (undocumented) + protected _type: FormatType; + // (undocumented) + protected _units?: Array<[Unit | InvertedUnit, string | undefined]>; + // (undocumented) + protected _uomSeparator: string; + protected addUnit(unit: Unit | InvertedUnit, label?: string): void; + // (undocumented) + readonly decimalSeparator: string; + // WARNING: The type "FormatProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserialize(formatProps: FormatProps): Promise; + // WARNING: The type "FormatProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserializeSync(formatProps: FormatProps): void; + // (undocumented) + readonly formatTraits: FormatTraits; + // (undocumented) + hasFormatTrait(formatTrait: FormatTraits): boolean; + // (undocumented) + readonly includeZero: boolean | undefined; + // (undocumented) + readonly minWidth: number | undefined; + // (undocumented) + readonly precision: DecimalPrecision | FractionalPrecision; + // (undocumented) + readonly roundFactor: number; + // WARNING: The type "SchemaItemType.Format" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly schemaItemType: SchemaItemType.Format; + // (undocumented) + readonly scientificType: ScientificType | undefined; + // (undocumented) + protected setPrecision(precision: number): void; + // (undocumented) + readonly showSignOption: ShowSignOption; + // (undocumented) + readonly spacer: string | undefined; + // (undocumented) + readonly stationOffsetSize: number | undefined; + // (undocumented) + readonly stationSeparator: string; + // (undocumented) + readonly thousandSeparator: string; + // (undocumented) + toJson: { + [value: string]: any; + } + // (undocumented) + readonly type: FormatType; + // (undocumented) + readonly units: Array<[Unit | InvertedUnit, string | undefined]> | undefined; + // (undocumented) + readonly uomSeparator: string; +} + +// @public (undocumented) +enum FormatTraits { + // (undocumented) + ApplyRounding = 16, + // (undocumented) + ExponentOnlyNegative = 512, + // (undocumented) + FractionDash = 32, + // (undocumented) + KeepDecimalPoint = 8, + // (undocumented) + KeepSingleZero = 2, + // (undocumented) + PrependUnitLabel = 128, + // (undocumented) + ShowUnitLabel = 64, + // (undocumented) + TrailZeroes = 1, + // (undocumented) + Use1000Separator = 256, + // (undocumented) + ZeroEmpty = 4 +} + +// @public (undocumented) +export function formatTraitsToArray(currentFormatTrait: FormatTraits): string[]; + +// @public (undocumented) +enum FormatType { + // (undocumented) + Decimal = 0, + // (undocumented) + Fractional = 1, + // (undocumented) + Scientific = 2, + // (undocumented) + Station = 3 +} + +// @public (undocumented) +export function formatTypeToString(type: FormatType): string; + +// @public (undocumented) +enum FractionalPrecision { + // (undocumented) + Eight = 8, + // (undocumented) + Four = 4, + // (undocumented) + One = 1, + // (undocumented) + OneHundredTwentyEight = 128, + // (undocumented) + Sixteen = 16, + // (undocumented) + SixtyFour = 64, + // (undocumented) + ThirtyTwo = 32, + // (undocumented) + Two = 2, + // (undocumented) + TwoHundredFiftySix = 256 +} + +// @public (undocumented) +export function getItemNamesFromFormatString(formatString: string): Iterable; + +// @public +interface IDiagnostic { + category: DiagnosticCategory; + code: string; + diagnosticType: DiagnosticType; + ecDefinition: TYPE; + messageArgs: ARGS; + messageText: string; +} + +// @public +interface IDiagnosticReporter { + i18N?: I18N; + report(diagnostic: AnyDiagnostic): void; +} + +// @public +class InvertedUnit extends SchemaItem { + constructor(schema: Schema, name: string); + // (undocumented) + protected _invertsUnit?: LazyLoadedUnit; + // (undocumented) + protected _unitSystem?: LazyLoadedUnitSystem; + // WARNING: The type "InvertedUnitProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserialize(invertedUnitProps: InvertedUnitProps): Promise; + // WARNING: The type "InvertedUnitProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserializeSync(invertedUnitProps: InvertedUnitProps): void; + // (undocumented) + readonly invertsUnit: LazyLoadedUnit | undefined; + // WARNING: The type "SchemaItemType.InvertedUnit" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly schemaItemType: SchemaItemType.InvertedUnit; + // (undocumented) + toJson: { + [value: string]: any; + } + // (undocumented) + readonly unitSystem: LazyLoadedUnitSystem | undefined; +} + +// @public +interface IRuleSet { + classRules?: Array>; + constantRules?: Array>; + customAttributeClassRules?: Array>; + customAttributeContainerRules?: Array>; + customAttributeInstanceRules?: Array>; + entityClassRules?: Array>; + enumerationRules?: Array>; + formatRules?: Array>; + invertedUnitRules?: Array>; + kindOfQuantityRules?: Array>; + mixinRules?: Array>; + name: string; + phenomenonRules?: Array>; + propertyCategoryRules?: Array>; + propertyRules?: Array>; + relationshipConstraintRules?: Array>; + relationshipRules?: Array>; + schemaItemRules?: Array>; + schemaRules?: Array>; + structClassRules?: Array>; + unitRules?: Array>; + unitSystemRules?: Array>; +} + +// @public (undocumented) +interface ISchemaItemLocater { + // (undocumented) + getSchemaItem(schemaItemKey: SchemaItemKey): Promise; +} + +// @public +interface ISchemaLocater { + getSchema(schemaKey: SchemaKey, matchType: SchemaMatchType, context?: SchemaContext): Promise; + getSchemaSync(schemaKey: SchemaKey, matchType: SchemaMatchType, context?: SchemaContext): T | undefined; +} + +// @public +interface ISchemaPartVisitor { + visitClass?: (ecClass: AnyClass) => Promise; + visitClassSync?: (ecClass: AnyClass) => void; + visitConstant?: (constant: Constant) => Promise; + visitConstantSync?: (constant: Constant) => void; + visitCustomAttributeClass?: (customAttributeClass: CustomAttributeClass) => Promise; + visitCustomAttributeClassSync?: (customAttributeClass: CustomAttributeClass) => void; + visitCustomAttributeContainer?: (customAttributeContainer: CustomAttributeContainerProps) => Promise; + visitCustomAttributeContainerSync?: (customAttributeContainer: CustomAttributeContainerProps) => void; + visitEmptySchema?: (schema: Schema) => Promise; + visitEmptySchemaSync?: (schema: Schema) => void; + visitEntityClass?: (entityClass: EntityClass) => Promise; + visitEntityClassSync?: (entityClass: EntityClass) => void; + visitEnumeration?: (enumeration: Enumeration) => Promise; + visitEnumerationSync?: (enumeration: Enumeration) => void; + visitFormat?: (format: Format) => Promise; + visitFormatSync?: (format: Format) => void; + visitFullSchema?: (schema: Schema) => Promise; + visitFullSchemaSync?: (schema: Schema) => void; + visitInvertedUnit?: (invertedUnit: InvertedUnit) => Promise; + visitInvertedUnitSync?: (invertedUnit: InvertedUnit) => void; + visitKindOfQuantity?: (koq: KindOfQuantity) => Promise; + visitKindOfQuantitySync?: (koq: KindOfQuantity) => void; + visitMixin?: (mixin: Mixin) => Promise; + visitMixinSync?: (mixin: Mixin) => void; + visitPhenomenon?: (phenomena: Phenomenon) => Promise; + visitPhenomenonSync?: (phenomena: Phenomenon) => void; + visitProperty?: (property: AnyProperty) => Promise; + visitPropertyCategory?: (category: PropertyCategory) => Promise; + visitPropertyCategorySync?: (category: PropertyCategory) => void; + visitPropertySync?: (property: AnyProperty) => void; + visitRelationshipClass?: (relationshipClass: RelationshipClass) => Promise; + visitRelationshipClassSync?: (relationshipClass: RelationshipClass) => void; + visitRelationshipConstraint?: (relationshipConstraint: RelationshipConstraint) => Promise; + visitRelationshipConstraintSync?: (relationshipConstraint: RelationshipConstraint) => void; + visitSchemaItem?: (schemaItem: SchemaItem) => Promise; + visitSchemaItemSync?: (schemaItem: SchemaItem) => void; + visitStructClass?: (structClass: StructClass) => Promise; + visitStructClassSync?: (structClass: StructClass) => void; + visitUnit?: (unit: Unit) => Promise; + visitUnitSync?: (unit: Unit) => void; + visitUnitSystem?: (unitSystem: UnitSystem) => Promise; + visitUnitSystemSync?: (unitSystem: UnitSystem) => void; +} + +// @public +class KindOfQuantity extends SchemaItem { + constructor(schema: Schema, name: string); + // (undocumented) + protected _persistenceUnit?: LazyLoadedUnit | LazyLoadedInvertedUnit; + // (undocumented) + protected _presentationUnits: Array; + // (undocumented) + protected _relativeError: number; + // (undocumented) + protected addPresentationFormat(format: Format | OverrideFormat, isDefault?: boolean): void; + // (undocumented) + protected createFormatOverride(parent: Format, name: string, precision?: number, unitLabelOverrides?: Array<[Unit | InvertedUnit, string | undefined]>): OverrideFormat; + // (undocumented) + readonly defaultPresentationFormat: undefined | Format | OverrideFormat; + // WARNING: The type "KindOfQuantityProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserialize(kindOfQuantityProps: KindOfQuantityProps): Promise; + // WARNING: The type "KindOfQuantityProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserializeSync(kindOfQuantityProps: KindOfQuantityProps): void; + // (undocumented) + persistenceUnit: LazyLoadedUnit | LazyLoadedInvertedUnit | undefined; + // (undocumented) + readonly presentationUnits: Array | undefined; + // (undocumented) + readonly relativeError: number; + // WARNING: The type "SchemaItemType.KindOfQuantity" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly schemaItemType: SchemaItemType.KindOfQuantity; + // (undocumented) + toJson: { + [value: string]: any; + } +} + +// @public +class LoggingDiagnosticReporter extends DiagnosticReporterBase { + // (undocumented) + reportDiagnostic(diagnostic: AnyDiagnostic, messageText: string): void; +} + +// @public +class Mixin extends ECClass { + constructor(schema: Schema, name: string); + // (undocumented) + protected _appliesTo?: LazyLoadedEntityClass; + // (undocumented) + applicableTo(entityClass: EntityClass): Promise; + // (undocumented) + readonly appliesTo: LazyLoadedEntityClass | undefined; + // (undocumented) + protected createNavigationProperty(name: string, relationship: string | RelationshipClass, direction: string | StrengthDirection): Promise; + // (undocumented) + protected createNavigationPropertySync(name: string, relationship: string | RelationshipClass, direction: string | StrengthDirection): NavigationProperty; + // WARNING: The type "MixinProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserialize(mixinProps: MixinProps): Promise; + // WARNING: The type "MixinProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserializeSync(mixinProps: MixinProps): void; + // WARNING: The type "SchemaItemType.Mixin" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly schemaItemType: SchemaItemType.Mixin; + // (undocumented) + toJson: { + [value: string]: any; + } +} + +// @public (undocumented) +class NavigationProperty extends Property { + constructor(ecClass: ECClass, name: string, relationship: LazyLoadedRelationshipClass, direction?: StrengthDirection); + // (undocumented) + protected _direction: StrengthDirection; + // (undocumented) + protected _relationshipClass: LazyLoadedRelationshipClass; + // (undocumented) + readonly direction: StrengthDirection; + // (undocumented) + getRelationshipClassSync(): RelationshipClass | undefined; + // (undocumented) + readonly relationshipClass: LazyLoadedRelationshipClass; + // (undocumented) + toJson(): any; +} + +// @public (undocumented) +interface NoDelayedPromiseMethods { + // (undocumented) + [propName: string]: any; + // (undocumented) + catch?: never; + // (undocumented) + start?: never; + // (undocumented) + then?: never; +} + +// @public +class OverrideFormat { + constructor(parent: Format, name: string, precision?: DecimalPrecision | FractionalPrecision, unitAndLabels?: Array<[Unit | InvertedUnit, string | undefined]>); + // (undocumented) + readonly decimalSeparator: string; + // (undocumented) + readonly formatTraits: FormatTraits; + // (undocumented) + readonly fullName: string; + // (undocumented) + hasFormatTrait(formatTrait: FormatTraits): boolean; + // (undocumented) + readonly includeZero: boolean | undefined; + // (undocumented) + readonly minWidth: number | undefined; + readonly name: string; + readonly parent: Format; + // (undocumented) + readonly precision: DecimalPrecision | FractionalPrecision; + // (undocumented) + readonly roundFactor: number; + // (undocumented) + readonly scientificType: ScientificType | undefined; + // (undocumented) + readonly showSignOption: ShowSignOption; + // (undocumented) + readonly spacer: string | undefined; + // (undocumented) + readonly stationOffsetSize: number | undefined; + // (undocumented) + readonly stationSeparator: string; + // (undocumented) + readonly thousandSeparator: string; + // (undocumented) + readonly type: FormatType; + // (undocumented) + readonly units: [Unit | InvertedUnit, string | undefined][] | undefined; + // (undocumented) + readonly uomSeparator: string; +} + +// @public +export function parseClassModifier(modifier: string): ECClassModifier | undefined; + +// @public +export function parseCustomAttributeContainerType(type: string): CustomAttributeContainerType | undefined; + +// @public (undocumented) +export function parseDecimalPrecision(jsonObjPrecision: number): DecimalPrecision | undefined; + +// @public (undocumented) +export function parseFormatTrait(formatTraitsString: string): FormatTraits | undefined; + +// @public (undocumented) +export function parseFormatType(jsonObjType: string): FormatType | undefined; + +// @public (undocumented) +export function parseFractionalPrecision(jsonObjPrecision: number): FractionalPrecision | undefined; + +// @public (undocumented) +export function parsePrecision(precision: number, type: FormatType): DecimalPrecision | FractionalPrecision | undefined; + +// @public +export function parsePrimitiveType(type: string): PrimitiveType | undefined; + +// @public (undocumented) +export function parseRelationshipEnd(end: string): RelationshipEnd | undefined; + +// @public +export function parseSchemaItemType(type: string): SchemaItemType | undefined; + +// @public (undocumented) +export function parseScientificType(scientificType: string): ScientificType | undefined; + +// @public (undocumented) +export function parseShowSignOption(showSignOption: string): ShowSignOption | undefined; + +// @public +export function parseStrength(strength: string): StrengthType | undefined; + +// @public (undocumented) +export function parseStrengthDirection(direction: string): StrengthDirection | undefined; + +// @public (undocumented) +class Phenomenon extends SchemaItem { + constructor(schema: Schema, name: string); + // (undocumented) + protected _definition: string; + // (undocumented) + readonly definition: string; + // WARNING: The type "PhenomenonProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserialize(phenomenonProps: PhenomenonProps): Promise; + // WARNING: The type "PhenomenonProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserializeSync(phenomenonProps: PhenomenonProps): void; + // WARNING: The type "SchemaItemType.Phenomenon" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly schemaItemType: SchemaItemType.Phenomenon; + // (undocumented) + toJson: { + [value: string]: any; + } +} + +// @public (undocumented) +class PrimitiveArrayProperty extends PrimitiveArrayProperty_base { + constructor(ecClass: ECClass, name: string, primitiveType?: PrimitiveType); +} + +// @public (undocumented) +class PrimitiveOrEnumPropertyBase extends Property { + constructor(ecClass: ECClass, name: string, type: PropertyType); + // (undocumented) + protected _extendedTypeName?: string; + // (undocumented) + protected _maxLength?: number; + // (undocumented) + protected _maxValue?: number; + // (undocumented) + protected _minLength?: number; + // (undocumented) + protected _minValue?: number; + // WARNING: The type "PrimitiveOrEnumPropertyBaseProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserialize(propertyBaseProps: PrimitiveOrEnumPropertyBaseProps): Promise; + // WARNING: The type "PrimitiveOrEnumPropertyBaseProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserializeSync(propertyBaseProps: PrimitiveOrEnumPropertyBaseProps): void; + // (undocumented) + readonly extendedTypeName: string | undefined; + // (undocumented) + readonly maxLength: number | undefined; + // (undocumented) + readonly maxValue: number | undefined; + // (undocumented) + readonly minLength: number | undefined; + // (undocumented) + readonly minValue: number | undefined; + // (undocumented) + toJson(): any; +} + +// @public (undocumented) +class PrimitiveProperty extends PrimitiveOrEnumPropertyBase { + constructor(ecClass: ECClass, name: string, primitiveType?: PrimitiveType); + // WARNING: The type "PrimitivePropertyProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserialize(primitivePropertyProps: PrimitivePropertyProps): Promise; + // WARNING: The type "PrimitivePropertyProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserializeSync(primitivePropertyProps: PrimitivePropertyProps): void; + // (undocumented) + readonly primitiveType: PrimitiveType; + // (undocumented) + toJson(): any; +} + +// @public +enum PrimitiveType { + // (undocumented) + Binary = 257, + // (undocumented) + Boolean = 513, + // (undocumented) + DateTime = 769, + // (undocumented) + Double = 1025, + // (undocumented) + IGeometry = 2561, + // (undocumented) + Integer = 1281, + // (undocumented) + Long = 1537, + // (undocumented) + Point2d = 1793, + // (undocumented) + Point3d = 2049, + // (undocumented) + String = 2305, + // (undocumented) + Uninitialized = 0 +} + +// @public (undocumented) +export function primitiveTypeToString(type: PrimitiveType): string; + +// @public +class Property implements CustomAttributeContainerProps { + constructor(ecClass: ECClass, name: string, type: PropertyType); + // (undocumented) + protected _category?: LazyLoadedPropertyCategory; + // (undocumented) + protected _class: AnyClass; + // (undocumented) + protected _description?: string; + // (undocumented) + protected _isReadOnly?: boolean; + // (undocumented) + protected _kindOfQuantity?: LazyLoadedKindOfQuantity; + // (undocumented) + protected _label?: string; + // (undocumented) + protected _name: ECName; + // (undocumented) + protected _priority?: number; + // (undocumented) + protected _type: PropertyType; + // WARNING: The type "CustomAttribute" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected addCustomAttribute(customAttribute: CustomAttribute): void; + // (undocumented) + readonly category: LazyLoadedPropertyCategory | undefined; + // (undocumented) + readonly class: AnyClass; + // WARNING: The type "CustomAttributeSet" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly customAttributes: CustomAttributeSet | undefined; + // (undocumented) + readonly description: string | undefined; + // WARNING: The type "PropertyProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserialize(propertyProps: PropertyProps): Promise; + // WARNING: The type "PropertyProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserializeSync(propertyProps: PropertyProps): void; + readonly fullName: string; + // (undocumented) + getCategorySync(): PropertyCategory | undefined; + // (undocumented) + getKindOfQuantitySync(): KindOfQuantity | undefined; + // (undocumented) + isArray(): this is AnyArrayProperty; + // (undocumented) + isEnumeration(): this is AnyEnumerationProperty; + // (undocumented) + isNavigation(): this is NavigationProperty; + // (undocumented) + isPrimitive(): this is AnyPrimitiveProperty; + // (undocumented) + readonly isReadOnly: boolean; + // (undocumented) + isStruct(): this is AnyStructProperty; + // (undocumented) + readonly kindOfQuantity: LazyLoadedKindOfQuantity | undefined; + // (undocumented) + readonly label: string | undefined; + // (undocumented) + readonly name: string; + // (undocumented) + readonly priority: number; + readonly schema: Schema; + // (undocumented) + toJson(): any; +} + +// @public (undocumented) +class PropertyCategory extends SchemaItem { + constructor(schema: Schema, name: string); + // (undocumented) + protected _priority: number; + // WARNING: The type "PropertyCategoryProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserialize(propertyCategoryProps: PropertyCategoryProps): Promise; + // WARNING: The type "PropertyCategoryProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserializeSync(propertyCategoryProps: PropertyCategoryProps): void; + // (undocumented) + readonly priority: number; + // WARNING: The type "SchemaItemType.PropertyCategory" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly schemaItemType: SchemaItemType.PropertyCategory; + // (undocumented) + toJson: { + [value: string]: any; + } +} + +// @public +class PropertyDiagnostic extends BaseDiagnostic { + constructor(property: AnyProperty, messageArgs: ARGS); + // (undocumented) + readonly diagnosticType: DiagnosticType; +} + +// @public (undocumented) +enum PropertyType { + // (undocumented) + Binary = 257, + // (undocumented) + Binary_Array = 261, + // (undocumented) + Boolean = 513, + // (undocumented) + Boolean_Array = 517, + // (undocumented) + DateTime = 769, + // (undocumented) + DateTime_Array = 773, + // (undocumented) + Double = 1025, + // (undocumented) + Double_Array = 1029, + // (undocumented) + IGeometry = 2561, + // (undocumented) + IGeometry_Array = 2565, + // (undocumented) + Integer = 1281, + // (undocumented) + Integer_Array = 1285, + // (undocumented) + Integer_Enumeration = 1297, + // (undocumented) + Integer_Enumeration_Array = 1301, + // (undocumented) + Long = 1537, + // (undocumented) + Long_Array = 1541, + // (undocumented) + Navigation = 8, + // (undocumented) + Point2d = 1793, + // (undocumented) + Point2d_Array = 1797, + // (undocumented) + Point3d = 2049, + // (undocumented) + Point3d_Array = 2053, + // (undocumented) + String = 2305, + // (undocumented) + String_Array = 2309, + // (undocumented) + String_Enumeration = 2321, + // (undocumented) + String_Enumeration_Array = 2325, + // (undocumented) + Struct = 2, + // (undocumented) + Struct_Array = 6 +} + +// @public (undocumented) +export function propertyTypeToString(type: PropertyType): "PrimitiveProperty" | "StructProperty" | "StructArrayProperty" | "NavigationProperty" | "PrimitiveArrayProperty"; + +// @public (undocumented) +module PropertyTypeUtils { + // (undocumented) + function asArray(t: PropertyType): PropertyType; + + // (undocumented) + function fromPrimitiveType(t: PrimitiveType): PropertyType; + + // (undocumented) + function getPrimitiveType(t: PropertyType): PrimitiveType; + + // (undocumented) + function isArray(t: PropertyType): boolean; + + // (undocumented) + function isEnumeration(t: PropertyType): boolean; + + // (undocumented) + function isNavigation(t: PropertyType): boolean; + + // (undocumented) + function isPrimitive(t: PropertyType): boolean; + + // (undocumented) + function isStruct(t: PropertyType): boolean; + +} + +// @public +class RelationshipClass extends ECClass { + constructor(schema: Schema, name: string, modifier?: ECClassModifier); + // (undocumented) + protected _source: RelationshipConstraint; + // (undocumented) + protected _strength: StrengthType; + // (undocumented) + protected _strengthDirection: StrengthDirection; + // (undocumented) + protected _target: RelationshipConstraint; + // (undocumented) + protected createNavigationProperty(name: string, relationship: string | RelationshipClass, direction: string | StrengthDirection): Promise; + // (undocumented) + protected createNavigationPropertySync(name: string, relationship: string | RelationshipClass, direction: string | StrengthDirection): NavigationProperty; + // WARNING: The type "RelationshipClassProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserialize(relationshipClassProps: RelationshipClassProps): Promise; + // WARNING: The type "RelationshipClassProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserializeSync(relationshipClassProps: RelationshipClassProps): void; + // (undocumented) + readonly schema: Schema; + // WARNING: The type "SchemaItemType.RelationshipClass" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly schemaItemType: SchemaItemType.RelationshipClass; + // (undocumented) + readonly source: RelationshipConstraint; + // (undocumented) + readonly strength: StrengthType; + // (undocumented) + readonly strengthDirection: StrengthDirection; + // (undocumented) + readonly target: RelationshipConstraint; + // (undocumented) + toJson: { + [value: string]: any; + } +} + +// @public +class RelationshipConstraint implements CustomAttributeContainerProps { + constructor(relClass: RelationshipClass, relEnd: RelationshipEnd, roleLabel?: string, polymorphic?: boolean); + // (undocumented) + protected _abstractConstraint?: LazyLoadedRelationshipConstraintClass; + // (undocumented) + protected _constraintClasses?: LazyLoadedRelationshipConstraintClass[]; + // (undocumented) + protected _multiplicity?: RelationshipMultiplicity; + // (undocumented) + protected _polymorphic?: boolean; + // (undocumented) + protected _relationshipClass: RelationshipClass; + // (undocumented) + protected _relationshipEnd: RelationshipEnd; + // (undocumented) + protected _roleLabel?: string; + // (undocumented) + abstractConstraint: LazyLoadedRelationshipConstraintClass | undefined; + addClass(constraint: EntityClass | Mixin | RelationshipClass): void; + // WARNING: The type "CustomAttribute" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected addCustomAttribute(customAttribute: CustomAttribute): void; + static classCompatibleWithConstraint(constraintClass: ECClass, testClass: ECClass, isPolymorphic: boolean): Promise; + // (undocumented) + readonly constraintClasses: LazyLoadedRelationshipConstraintClass[] | undefined; + // WARNING: The type "CustomAttributeSet" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly customAttributes: CustomAttributeSet | undefined; + // WARNING: The type "RelationshipConstraintProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserialize(relationshipConstraintProps: RelationshipConstraintProps): Promise; + // WARNING: The type "RelationshipConstraintProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserializeSync(relationshipConstraintProps: RelationshipConstraintProps): void; + readonly fullName: "Source" | "Target"; + readonly isSource: boolean; + // (undocumented) + readonly multiplicity: RelationshipMultiplicity | undefined; + // (undocumented) + readonly polymorphic: boolean | undefined; + // (undocumented) + readonly relationshipClass: RelationshipClass; + // (undocumented) + readonly relationshipEnd: RelationshipEnd; + // (undocumented) + readonly roleLabel: string | undefined; + readonly schema: Schema; + supportsClass(ecClass: ECClass): Promise; + // (undocumented) + toJson: { + [value: string]: any; + } +} + +// @public +class RelationshipConstraintDiagnostic extends BaseDiagnostic { + constructor(constraint: RelationshipConstraint, messageArgs: ARGS); + // (undocumented) + readonly diagnosticType: DiagnosticType; +} + +// @public +enum RelationshipEnd { + // (undocumented) + Source = 0, + // (undocumented) + Target = 1 +} + +// @public (undocumented) +export function relationshipEndToString(end: RelationshipEnd): string; + +// @public (undocumented) +class RelationshipMultiplicity { + constructor(lowerLimit: number, upperLimit: number); + // (undocumented) + equals(rhs: RelationshipMultiplicity): boolean; + // (undocumented) + static fromString(str: string): RelationshipMultiplicity | undefined; + // (undocumented) + readonly lowerLimit: number; + // (undocumented) + static readonly oneMany: RelationshipMultiplicity; + // (undocumented) + static readonly oneOne: RelationshipMultiplicity; + // (undocumented) + toString(): string; + // (undocumented) + readonly upperLimit: number; + // (undocumented) + static readonly zeroMany: RelationshipMultiplicity; + // (undocumented) + static readonly zeroOne: RelationshipMultiplicity; +} + +// @public (undocumented) +class Schema implements CustomAttributeContainerProps { + constructor(context: SchemaContext, name: string, readVersion: number, writeVersion: number, minorVersion: number); + // (undocumented) + protected _alias?: string; + // (undocumented) + protected _description?: string; + // (undocumented) + protected _label?: string; + // (undocumented) + protected _schemaKey?: SchemaKey; + // WARNING: The type "CustomAttribute" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected addCustomAttribute(customAttribute: CustomAttribute): void; + // (undocumented) + protected addItem(item: T): void; + // (undocumented) + protected addReference(refSchema: Schema): Promise; + // (undocumented) + protected addReferenceSync(refSchema: Schema): void; + // (undocumented) + readonly alias: string | undefined; + readonly context: SchemaContext; + protected createConstant(name: string): Promise; + // (undocumented) + protected createConstantSync(name: string): Constant; + protected createCustomAttributeClass(name: string, modifier?: ECClassModifier): Promise; + // (undocumented) + protected createCustomAttributeClassSync(name: string, modifier?: ECClassModifier): CustomAttributeClass; + protected createEntityClass(name: string, modifier?: ECClassModifier): Promise; + // (undocumented) + protected createEntityClassSync(name: string, modifier?: ECClassModifier): EntityClass; + // WARNING: The type "PrimitiveType.Integer" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "PrimitiveType.String" needs to be exported by the package (e.g. added to index.ts) + protected createEnumeration(name: string, primitiveType?: PrimitiveType.Integer | PrimitiveType.String): Promise; + // WARNING: The type "PrimitiveType.Integer" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "PrimitiveType.String" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected createEnumerationSync(name: string, primitiveType?: PrimitiveType.Integer | PrimitiveType.String): Enumeration; + protected createFormat(name: string): Promise; + // (undocumented) + protected createFormatSync(name: string): Format; + protected createInvertedUnit(name: string): Promise; + // (undocumented) + protected createInvertedUnitSync(name: string): InvertedUnit; + protected createKindOfQuantity(name: string): Promise; + // (undocumented) + protected createKindOfQuantitySync(name: string): KindOfQuantity; + protected createMixinClass(name: string): Promise; + // (undocumented) + protected createMixinClassSync(name: string): Mixin; + protected createPhenomenon(name: string): Promise; + // (undocumented) + protected createPhenomenonSync(name: string): Phenomenon; + protected createPropertyCategory(name: string): Promise; + // (undocumented) + protected createPropertyCategorySync(name: string): PropertyCategory; + protected createRelationshipClass(name: string, modifier?: ECClassModifier): Promise; + // (undocumented) + protected createRelationshipClassSync(name: string, modifier?: ECClassModifier): RelationshipClass; + protected createStructClass(name: string, modifier?: ECClassModifier): Promise; + // (undocumented) + protected createStructClassSync(name: string, modifier?: ECClassModifier): StructClass; + protected createUnit(name: string): Promise; + // (undocumented) + protected createUnitSync(name: string): Unit; + protected createUnitSystem(name: string): Promise; + // (undocumented) + protected createUnitSystemSync(name: string): UnitSystem; + // WARNING: The type "CustomAttributeSet" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly customAttributes: CustomAttributeSet | undefined; + // (undocumented) + readonly description: string | undefined; + // WARNING: The type "SchemaProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserialize(schemaProps: SchemaProps): Promise; + // WARNING: The type "SchemaProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserializeSync(schemaProps: SchemaProps): void; + // (undocumented) + static fromJson(jsonObj: object | string, context: SchemaContext): Promise; + // (undocumented) + static fromJsonSync(jsonObj: object | string, context: SchemaContext): Schema; + readonly fullName: string; + // (undocumented) + getClasses(): ECClass[]; + getItem(name: string): Promise; + // (undocumented) + getItems(): T[]; + getItemSync(name: string): T | undefined; + // (undocumented) + getReference(refSchemaName: string): Promise; + // (undocumented) + getReferenceSync(refSchemaName: string): T | undefined; + getSchemaItemKey(fullName: string): SchemaItemKey; + // (undocumented) + readonly label: string | undefined; + lookupItem(key: Readonly | string): Promise; + lookupItemSync(key: Readonly | string): T | undefined; + // (undocumented) + readonly minorVersion: number; + // (undocumented) + readonly name: string; + // (undocumented) + readonly readVersion: number; + // (undocumented) + readonly references: Schema[]; + readonly schema: Schema; + // (undocumented) + readonly schemaKey: SchemaKey; + // (undocumented) + toJson: { + [value: string]: any; + } + // (undocumented) + readonly writeVersion: number; +} + +// @public (undocumented) +class SchemaCache implements ISchemaLocater { + constructor(); + addSchema(schema: T): Promise; + addSchemaSync(schema: T): void; + // (undocumented) + readonly count: number; + getSchema(schemaKey: SchemaKey, matchType?: SchemaMatchType): Promise; + // (undocumented) + getSchemaSync(schemaKey: SchemaKey, matchType?: SchemaMatchType): T | undefined; +} + +// @public +class SchemaContext implements ISchemaLocater, ISchemaItemLocater { + constructor(); + // (undocumented) + addLocater(locater: ISchemaLocater): void; + addSchema(schema: Schema): Promise; + addSchemaItem(schemaItem: SchemaItem): Promise; + addSchemaSync(schema: Schema): void; + // (undocumented) + getSchema(schemaKey: SchemaKey, matchType?: SchemaMatchType): Promise; + // (undocumented) + getSchemaItem(schemaItemKey: SchemaItemKey): Promise; + // (undocumented) + getSchemaItemSync(schemaItemKey: SchemaItemKey): T | undefined; + // (undocumented) + getSchemaSync(schemaKey: SchemaKey, matchType?: SchemaMatchType): T | undefined; +} + +// @public +class SchemaDiagnostic extends BaseDiagnostic { + constructor(schema: Schema, messageArgs: ARGS); + // (undocumented) + static diagnosticType: DiagnosticType; +} + +// @public +class SchemaFileLocater { + constructor(); + addSchemaSearchPath(schemaPath: string): void; + addSchemaSearchPaths(schemaPaths: string[]): void; + compareSchemaKeyByVersion(lhs: FileSchemaKey, rhs: FileSchemaKey): number; + // (undocumented) + fileExists(filePath: string): Promise; + protected findEligibleSchemaKeys(desiredKey: SchemaKey, matchType: SchemaMatchType, format: string): FileSchemaKey[]; + // (undocumented) + abstract getSchema(key: SchemaKey, matchType: SchemaMatchType, context: SchemaContext): Promise; + // (undocumented) + protected abstract getSchemaKey(data: string): SchemaKey; + // (undocumented) + readUtf8FileToString(filePath: string): Promise; + // (undocumented) + searchPaths: string[]; +} + +// @public +class SchemaGraphUtil { + static buildDependencyOrderedSchemaList(insertSchema: Schema, schemas?: Schema[]): Schema[]; +} + +// @public +class SchemaItem { + constructor(schema: Schema, name: string); + // (undocumented) + protected _description?: string; + // (undocumented) + protected _key: SchemaItemKey; + // (undocumented) + protected _label?: string; + // (undocumented) + readonly description: string | undefined; + // WARNING: The type "SchemaItemProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserialize(schemaItemProps: SchemaItemProps): Promise; + // WARNING: The type "SchemaItemProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserializeSync(schemaItemProps: SchemaItemProps): void; + static equalByKey(thisSchemaItem: SchemaItem, thatSchemaItemOrKey?: SchemaItem | SchemaItemKey): boolean; + // (undocumented) + readonly fullName: string; + // (undocumented) + readonly key: SchemaItemKey; + // (undocumented) + readonly label: string | undefined; + // (undocumented) + readonly name: string; + static parseFullName(fullName: string): [string, string]; + // (undocumented) + readonly schema: Schema; + // (undocumented) + readonly schemaItemType: SchemaItemType; + // (undocumented) + toJson: { + [value: string]: any; + } +} + +// @public +class SchemaItemDiagnostic extends BaseDiagnostic { + constructor(ecDefinition: TYPE, messageArgs: ARGS); + // (undocumented) + static diagnosticType: DiagnosticType; +} + +// @public +class SchemaItemKey { + constructor(name: string, schema: SchemaKey); + // (undocumented) + protected _schemaKey: SchemaKey; + // (undocumented) + readonly fullName: string; + matches(rhs: SchemaItemKey): boolean; + // (undocumented) + matchesFullName(rhs: string): boolean; + // (undocumented) + readonly name: string; + // (undocumented) + readonly schemaKey: SchemaKey; + // (undocumented) + readonly schemaName: string; +} + +// @public (undocumented) +enum SchemaItemType { + // (undocumented) + Constant = 10, + // (undocumented) + CustomAttributeClass = 3, + // (undocumented) + EntityClass = 0, + // (undocumented) + Enumeration = 5, + // (undocumented) + Format = 13, + // (undocumented) + InvertedUnit = 9, + // (undocumented) + KindOfQuantity = 6, + // (undocumented) + Mixin = 1, + // (undocumented) + Phenomenon = 11, + // (undocumented) + PropertyCategory = 7, + // (undocumented) + RelationshipClass = 4, + // (undocumented) + StructClass = 2, + // (undocumented) + Unit = 8, + // (undocumented) + UnitSystem = 12 +} + +// @public +export function schemaItemTypeToString(value: SchemaItemType): string; + +// @public +class SchemaJsonFileLocater extends SchemaFileLocater, implements ISchemaLocater { + getSchema(schemaKey: SchemaKey, matchType: SchemaMatchType, context: SchemaContext): Promise; + protected getSchemaKey(data: string): SchemaKey; + getSchemaSync(schemaKey: SchemaKey, matchType: SchemaMatchType, context: SchemaContext): T | undefined; +} + +// @public +class SchemaKey { + constructor(name: string, version: ECVersion); + // (undocumented) + protected _version: ECVersion; + // (undocumented) + compareByName(rhs: SchemaKey | string | undefined): boolean; + compareByVersion(rhs: SchemaKey): number; + // (undocumented) + matches(rhs: SchemaKey, matchType?: SchemaMatchType): boolean; + // (undocumented) + readonly minorVersion: number; + // (undocumented) + readonly name: string; + // (undocumented) + static parseString(fullName: string): SchemaKey; + // (undocumented) + readonly readVersion: number; + toString(padZeroes?: boolean): string; + // (undocumented) + readonly version: ECVersion; + // (undocumented) + readonly writeVersion: number; +} + +// @public (undocumented) +class SchemaMap extends Array { +} + +// @public +enum SchemaMatchType { + // (undocumented) + Exact = 1, + // (undocumented) + Identical = 0, + // (undocumented) + Latest = 3, + // (undocumented) + LatestReadCompatible = 4, + // (undocumented) + LatestWriteCompatible = 2 +} + +// @public +class SchemaPartVisitorDelegate { + constructor(visitor: ISchemaPartVisitor); + visitSchema(schema: Schema, fullSchema?: boolean): Promise; + visitSchemaPart(schemaPart: AnyECType): Promise; + visitSchemaPartSync(schemaPart: AnyECType): void; + visitSchemaSync(schema: Schema, fullSchema?: boolean): void; +} + +// @public +class SchemaValidationVisitor implements ISchemaPartVisitor { + // (undocumented) + applyClassRules(ecClass: AnyClass, ruleSet: IRuleSet): Promise; + // (undocumented) + applyConstantRules(constant: Constant, ruleSet: IRuleSet): Promise; + // WARNING: The type "CustomAttributeContainerProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + applyCustomAttributeContainerRules(container: CustomAttributeContainerProps, ruleSet: IRuleSet): Promise; + // WARNING: The type "CustomAttributeContainerProps" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "CustomAttribute" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + applyCustomAttributeInstanceRules(container: CustomAttributeContainerProps, customAttribute: CustomAttribute, ruleSet: IRuleSet): Promise; + // (undocumented) + applyCustomAttributeRules(customAttribute: CustomAttributeClass, ruleSet: IRuleSet): Promise; + // (undocumented) + applyEntityRules(entityClass: EntityClass, ruleSet: IRuleSet): Promise; + // (undocumented) + applyEnumerationRules(enumeration: Enumeration, ruleSet: IRuleSet): Promise; + // (undocumented) + applyFormatRules(format: Format, ruleSet: IRuleSet): Promise; + // (undocumented) + applyInvertedUnitRules(invertedUnit: InvertedUnit, ruleSet: IRuleSet): Promise; + // (undocumented) + applyKindOfQuantityRules(kindOfQuantity: KindOfQuantity, ruleSet: IRuleSet): Promise; + // (undocumented) + applyMixinRules(mixin: Mixin, ruleSet: IRuleSet): Promise; + // (undocumented) + applyPhenomenonRules(phenomenon: Phenomenon, ruleSet: IRuleSet): Promise; + // (undocumented) + applyPropertyCategoryRules(propertyCategory: PropertyCategory, ruleSet: IRuleSet): Promise; + // (undocumented) + applyPropertyRules(property: AnyProperty, ruleSet: IRuleSet): Promise; + // (undocumented) + applyRelationshipConstraintRules(constraint: RelationshipConstraint, ruleSet: IRuleSet): Promise; + // (undocumented) + applyRelationshipRules(relationship: RelationshipClass, ruleSet: IRuleSet): Promise; + // (undocumented) + applySchemaItemRules(schemaItem: SchemaItem, ruleSet: IRuleSet): Promise; + // (undocumented) + applySchemaRules(schema: Schema, ruleSet: IRuleSet): Promise; + // (undocumented) + applyStructRules(structClass: StructClass, ruleSet: IRuleSet): Promise; + // (undocumented) + applyUnitRules(unit: Unit, ruleSet: IRuleSet): Promise; + // (undocumented) + applyUnitSystemRules(unitSystem: UnitSystem, ruleSet: IRuleSet): Promise; + readonly diagnosticReporters: IDiagnosticReporter[]; + registerReporter(...reporters: IDiagnosticReporter[]): void; + registerRuleSet(ruleSet: IRuleSet): void; + // WARNING: The type "RuleSetArray" needs to be exported by the package (e.g. added to index.ts) + readonly ruleSets: RuleSetArray; + visitClass(ecClass: AnyClass): Promise; + visitConstant(constant: Constant): Promise; + visitCustomAttributeClass(customAttribute: CustomAttributeClass): Promise; + // WARNING: The type "CustomAttributeContainerProps" needs to be exported by the package (e.g. added to index.ts) + visitCustomAttributeContainer(container: CustomAttributeContainerProps): Promise; + visitEntityClass(entity: EntityClass): Promise; + visitEnumeration(enumeration: Enumeration): Promise; + visitFormat(format: Format): Promise; + visitFullSchema(schema: Schema): Promise; + visitInvertedUnit(invertedUnit: InvertedUnit): Promise; + visitKindOfQuantity(koq: KindOfQuantity): Promise; + visitMixin(mixin: Mixin): Promise; + visitPhenomenon(phenomenon: Phenomenon): Promise; + visitProperty(property: AnyProperty): Promise; + visitPropertyCategory(category: PropertyCategory): Promise; + visitRelationshipClass(relationship: RelationshipClass): Promise; + visitRelationshipConstraint(constraint: RelationshipConstraint): Promise; + visitSchemaItem(schemaItem: SchemaItem): Promise; + visitStructClass(struct: StructClass): Promise; + visitUnit(unit: Unit): Promise; + visitUnitSystem(unitSystem: UnitSystem): Promise; +} + +// @public +class SchemaWalker { + constructor(visitor: ISchemaPartVisitor); + traverseSchema(schema: T): Promise; +} + +// @public +class SchemaXmlFileLocater extends SchemaFileLocater, implements ISchemaLocater { + addSchemaReferences(schema: Schema, context?: SchemaContext): Promise; + addSchemaReferencesSync(schema: Schema, context?: SchemaContext): void; + getSchema(key: SchemaKey, matchType: SchemaMatchType, context: SchemaContext): Promise; + getSchemaKey(data: string): SchemaKey; + getSchemaReferenceKeys(schemaKey: FileSchemaKey): SchemaKey[]; + getSchemaSync(key: SchemaKey, matchType: SchemaMatchType, context: SchemaContext): T | undefined; + loadSchema(schemaPath: string, context: SchemaContext): Promise; +} + +// @public (undocumented) +enum ScientificType { + // (undocumented) + Normalized = 0, + // (undocumented) + ZeroNormalized = 1 +} + +// @public (undocumented) +export function scientificTypeToString(scientificType: ScientificType): string; + +// @public (undocumented) +enum ShowSignOption { + // (undocumented) + NegativeParentheses = 3, + // (undocumented) + NoSign = 0, + // (undocumented) + OnlyNegative = 1, + // (undocumented) + SignAlways = 2 +} + +// @public (undocumented) +export function showSignOptionToString(showSign: ShowSignOption): string; + +// @public (undocumented) +enum StrengthDirection { + // (undocumented) + Backward = 2, + // (undocumented) + Forward = 1 +} + +// @public (undocumented) +export function strengthDirectionToString(direction: StrengthDirection): string; + +// @public (undocumented) +export function strengthToString(strength: StrengthType): string; + +// @public (undocumented) +enum StrengthType { + // (undocumented) + Embedding = 2, + // (undocumented) + Holding = 1, + // (undocumented) + Referencing = 0 +} + +// @public (undocumented) +class StructArrayProperty extends StructArrayProperty_base { + constructor(ecClass: ECClass, name: string, type: StructClass); +} + +// @public +class StructClass extends ECClass { + constructor(schema: Schema, name: string, modifier?: ECClassModifier); + // WARNING: The type "SchemaItemType.StructClass" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly schemaItemType: SchemaItemType.StructClass; +} + +// @public (undocumented) +class StructProperty extends Property { + constructor(ecClass: ECClass, name: string, type: StructClass); + // (undocumented) + protected _structClass: StructClass; + // WARNING: The type "StructPropertyProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserialize(structPropertyProps: StructPropertyProps): Promise; + // WARNING: The type "StructPropertyProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserializeSync(structPropertyProps: StructPropertyProps): void; + // (undocumented) + readonly structClass: StructClass; + // (undocumented) + toJson(): any; +} + +// @public +class Unit extends SchemaItem { + constructor(schema: Schema, name: string); + // (undocumented) + protected _definition: string; + // (undocumented) + protected _denominator: number; + // (undocumented) + protected _numerator: number; + // (undocumented) + protected _offset: number; + // (undocumented) + protected _phenomenon?: LazyLoadedPhenomenon; + // (undocumented) + protected _unitSystem?: LazyLoadedUnitSystem; + // (undocumented) + readonly definition: string; + // (undocumented) + readonly denominator: number; + // WARNING: The type "UnitProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserialize(unitProps: UnitProps): Promise; + // WARNING: The type "UnitProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + deserializeSync(unitProps: UnitProps): void; + // (undocumented) + readonly numerator: number; + // (undocumented) + readonly offset: number; + // (undocumented) + readonly phenomenon: LazyLoadedPhenomenon | undefined; + // WARNING: The type "SchemaItemType.Unit" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly schemaItemType: SchemaItemType.Unit; + // (undocumented) + toJson: { + [value: string]: any; + } + // (undocumented) + readonly unitSystem: LazyLoadedUnitSystem | undefined; +} + +// @public (undocumented) +class UnitSystem extends SchemaItem { + constructor(schema: Schema, name: string); + // WARNING: The type "SchemaItemType.UnitSystem" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly schemaItemType: SchemaItemType.UnitSystem; +} + +// WARNING: Unsupported export: AnyEnumerator +// WARNING: Unsupported export: AnyArrayProperty +// WARNING: Unsupported export: AnyEnumerationProperty +// WARNING: Unsupported export: AnyPrimitiveProperty +// WARNING: Unsupported export: AnyProperty +// WARNING: Unsupported export: AnyStructProperty +// WARNING: Unsupported export: DiagnosticCodes +// WARNING: Unsupported export: Diagnostics +// WARNING: Unsupported export: ECRuleSet +// WARNING: Unsupported export: DelayedPromiseWithProps +// WARNING: Unsupported export: DelayedPromiseWithProps +// WARNING: Unsupported export: LazyLoadedSchema +// WARNING: Unsupported export: LazyLoadedSchemaItem +// WARNING: Unsupported export: LazyLoadedECClass +// WARNING: Unsupported export: LazyLoadedEntityClass +// WARNING: Unsupported export: LazyLoadedMixin +// WARNING: Unsupported export: LazyLoadedStructClass +// WARNING: Unsupported export: LazyLoadedCustomAttributeClass +// WARNING: Unsupported export: LazyLoadedRelationshipClass +// WARNING: Unsupported export: LazyLoadedEnumeration +// WARNING: Unsupported export: LazyLoadedKindOfQuantity +// WARNING: Unsupported export: LazyLoadedPropertyCategory +// WARNING: Unsupported export: LazyLoadedRelationshipConstraintClass +// WARNING: Unsupported export: LazyLoadedUnit +// WARNING: Unsupported export: LazyLoadedInvertedUnit +// WARNING: Unsupported export: LazyLoadedConstant +// WARNING: Unsupported export: LazyLoadedPhenomenon +// WARNING: Unsupported export: LazyLoadedUnitSystem +// WARNING: Unsupported export: LazyLoadedFormat +// WARNING: Unsupported export: AnyClass +// WARNING: Unsupported export: AnySchemaItem +// WARNING: Unsupported export: AnyECType +// WARNING: Unsupported export: formatStringRgx +// WARNING: Unsupported export: AnyDiagnostic +// WARNING: Unsupported export: IRule +// WARNING: Unsupported export: BaseRule +// (No @packagedocumentation comment for this package) diff --git a/common/api/electron-manager.api.ts b/common/api/electron-manager.api.ts new file mode 100644 index 0000000..e8049d9 --- /dev/null +++ b/common/api/electron-manager.api.ts @@ -0,0 +1,40 @@ +// @public +class IModelJsElectronManager extends StandardElectronManager { + constructor(webResourcesPath?: string); + // (undocumented) + protected readonly _defaultWindowOptions: { + autoHideMenuBar: boolean; + icon: string; + } + // (undocumented) + appIconPath: string; + // (undocumented) + frontendURL: string; + // (undocumented) + initialize(windowOptions?: BrowserWindowConstructorOptions): Promise; +} + +// @public +class StandardElectronManager { + // (undocumented) + protected readonly _defaultWindowOptions: BrowserWindowConstructorOptions; + readonly frontendURL: string; + initialize(windowOptions?: BrowserWindowConstructorOptions): Promise; + readonly mainWindow: BrowserWindow | undefined; +} + +// @public +class WebpackDevServerElectronManager extends StandardElectronManager { + constructor(frontendPort?: number); + // (undocumented) + protected readonly _defaultWindowOptions: { + autoHideMenuBar: boolean; + icon: string; + } + // (undocumented) + appIconPath: string; + // (undocumented) + frontendURL: string; +} + +// (No @packagedocumentation comment for this package) diff --git a/common/api/geometry-core.api.ts b/common/api/geometry-core.api.ts new file mode 100644 index 0000000..beb959a --- /dev/null +++ b/common/api/geometry-core.api.ts @@ -0,0 +1,5924 @@ +// @public +class AbstractNewtonIterator { + protected constructor(stepSizeTolerance?: number, successiveConvergenceTarget?: number, maxIterations?: number); + // (undocumented) + protected _maxIterations: number; + // (undocumented) + protected _numAccepted: number; + // (undocumented) + protected _stepSizeTolerance: number; + // (undocumented) + protected _successiveConvergenceTarget: number; + // (undocumented) + abstract applyCurrentStep(isFinalStep: boolean): boolean; + abstract computeStep(): boolean; + abstract currentStepSize(): number; + // (undocumented) + numIterations: number; + // (undocumented) + runIterations(): boolean; + // (undocumented) + testConvergence(delta: number): boolean; +} + +// WARNING: EQN_EPS has incomplete type information +// WARNING: s_safeDivideFactor has incomplete type information +// WARNING: s_quadricRelTol has incomplete type information +// WARNING: sTestWindow has incomplete type information +// @public +class AnalyticRoots { + // (undocumented) + static appendCubicRoots(c: Float64Array | number[], results: GrowableFloat64Array): void; + // (undocumented) + static appendImplicitLineUnitCircleIntersections(alpha: number, beta: number, gamma: number, cosValues: OptionalGrowableFloat64Array, sinValues: OptionalGrowableFloat64Array, radiansValues: OptionalGrowableFloat64Array, reltol?: number): number; + static appendLinearRoot(c0: number, c1: number, values: GrowableFloat64Array): void; + static appendQuadraticRoots(c: Float64Array | number[], values: GrowableFloat64Array): void; + // (undocumented) + static appendQuarticRoots(c: Float64Array | number[], results: GrowableFloat64Array): void; + // (undocumented) + static cbrt(x: number): number; + static isSmallRatio(x: number, y: number, abstol?: number, reltol?: number): boolean; + static isZero(x: number): boolean; + // (undocumented) + static mostDistantFromMean(data: GrowableFloat64Array | undefined): number; + static safeDivide(values: Float64Array, numerator: number, denominator: number, defaultValue: number | undefined, offset: number): boolean; +} + +// WARNING: piOver4Radians has incomplete type information +// WARNING: piOver2Radians has incomplete type information +// WARNING: piRadians has incomplete type information +// WARNING: pi2Radians has incomplete type information +// WARNING: piOver12Radians has incomplete type information +// @public +class Angle implements BeJSONFunctions { + static adjustDegrees0To360(degrees: number): number; + static adjustDegreesSigned180(degrees: number): number; + static adjustRadians0To2Pi(radians: number): number; + static adjustRadiansMinusPiPlusPi(radians: number): number; + static cleanupTrigValue(value: number, tolerance?: number): number; + // (undocumented) + clone(): Angle; + // (undocumented) + cos(): number; + static create360(): Angle; + static createAtan2(numerator: number, denominator: number): Angle; + static createDegrees(degrees: number): Angle; + static createDegreesAdjustPositive(degrees: number): Angle; + static createDegreesAdjustSigned180(degrees: number): Angle; + static createRadians(radians: number): Angle; + // (undocumented) + readonly degrees: number; + // (undocumented) + static readonly degreesPerRadian: number; + static degreesToRadians(degrees: number): number; + static dotProductsToHalfAngleTrigValues(dotUU: number, dotVV: number, dotUV: number, favorZero?: boolean): TrigValues; + // (undocumented) + freeze(): void; + static fromJSON(json?: AngleProps, defaultValRadians?: number): Angle; + isAlmostEqual(other: Angle): boolean; + isAlmostEqualAllowPeriodShift(other: Angle): boolean; + isAlmostEqualNoPeriodShift(other: Angle): boolean; + static isAlmostEqualRadiansAllowPeriodShift(radiansA: number, radiansB: number): boolean; + static isAlmostEqualRadiansNoPeriodShift(radiansA: number, radiansB: number): boolean; + // (undocumented) + readonly isAlmostZero: boolean; + // (undocumented) + readonly isExactZero: boolean; + readonly isFullCircle: boolean; + // (undocumented) + static isFullCircleRadians(radians: number): boolean; + readonly isHalfCircle: boolean; + static isHalfCircleRadians(radians: number): boolean; + static isPerpendicularDotSet(dotUU: number, dotVV: number, dotUV: number): boolean; + // (undocumented) + readonly radians: number; + static radiansBetweenVectorsXYZ(ux: number, uy: number, uz: number, vx: number, vy: number, vz: number): number; + // (undocumented) + static readonly radiansPerDegree: number; + static radiansToDegrees(radians: number): number; + setDegrees(degrees: number): void; + setFrom(other: Angle): void; + setFromJSON(json?: AngleProps, defaultValRadians?: number): void; + setRadians(radians: number): void; + // (undocumented) + sin(): number; + // (undocumented) + tan(): number; + toJSON(): AngleProps; + // (undocumented) + toJSONRadians(): AngleProps; + static trigValuesToHalfAngleTrigValues(rCos2A: number, rSin2A: number): TrigValues; + // (undocumented) + static zero(): Angle; +} + +// @public +class AngleSweep implements BeJSONFunctions { + angleToPositivePeriodicFraction(theta: Angle): number; + angleToSignedPeriodicFraction(theta: Angle): number; + angleToUnboundedFraction(theta: Angle): number; + capLatitudeInPlace(): void; + clone(): AngleSweep; + cloneMinusRadians(radians: number): AngleSweep; + static create360(startRadians?: number): AngleSweep; + static createFullLatitude(): AngleSweep; + static createStartEnd(startAngle: Angle, endAngle: Angle, result?: AngleSweep): AngleSweep; + static createStartEndDegrees(startDegrees?: number, endDegrees?: number, result?: AngleSweep): AngleSweep; + static createStartEndRadians(startRadians?: number, endRadians?: number, result?: AngleSweep): AngleSweep; + static createStartSweep(startAngle: Angle, sweepAngle: Angle, result?: AngleSweep): AngleSweep; + static createStartSweepDegrees(startDegrees?: number, sweepDegrees?: number, result?: AngleSweep): AngleSweep; + static createStartSweepRadians(startRadians?: number, sweepRadians?: number, result?: AngleSweep): AngleSweep; + readonly endAngle: Angle; + readonly endDegrees: number; + readonly endRadians: number; + fractionPeriod(): number; + fractionToAngle(fraction: number): Angle; + fractionToRadians(fraction: number): number; + static fromJSON(json?: AngleSweepProps): AngleSweep; + // (undocumented) + interpolate(fraction: number, other: AngleSweep): AngleSweep; + isAlmostEqual(other: AngleSweep): boolean; + isAlmostEqualAllowPeriodShift(other: AngleSweep): boolean; + isAlmostEqualNoPeriodShift(other: AngleSweep): boolean; + isAngleInSweep(angle: Angle): boolean; + readonly isCCW: boolean; + readonly isFullCircle: boolean; + readonly isFullLatitudeSweep: boolean; + isRadiansInSweep(radians: number): boolean; + radiansArraytoPositivePeriodicFractions(data: GrowableFloat64Array): void; + // (undocumented) + radiansToPositivePeriodicFraction(radians: number): number; + // (undocumented) + radiansToSignedPeriodicFraction(radians: number): number; + reverseInPlace(): void; + setFrom(other: AngleSweep): void; + setFromJSON(json?: any): void; + setStartEndDegrees(startDegrees?: number, endDegrees?: number): void; + setStartEndRadians(startRadians?: number, endRadians?: number): void; + readonly startAngle: Angle; + readonly startDegrees: number; + readonly startRadians: number; + readonly sweepDegrees: number; + readonly sweepRadians: number; + toJSON(): any; +} + +// @public +class AnnotatedLineString3d { + // (undocumented) + curveParam?: GrowableFloat64Array; + uvwParam?: GrowableXYZArray; + // (undocumented) + vectorV?: GrowableXYZArray; + // (undocumented) + vecturU?: GrowableXYZArray; +} + +// WARNING: quadratureGuassCount has incomplete type information +// WARNING: quadratureIntervalAngleDegrees has incomplete type information +// @public +class Arc3d extends CurvePrimitive, implements BeJSONFunctions { + // (undocumented) + allPerpendicularAngles(spacePoint: Point3d, _extend?: boolean, _endpoints?: boolean): number[]; + // (undocumented) + angleToPointAndDerivative(theta: Angle, result?: Ray3d): Ray3d; + announceClipIntervals(clipper: Clipper, announce?: AnnounceNumberNumberCurvePrimitive): boolean; + // (undocumented) + appendPlaneIntersectionPoints(plane: PlaneAltitudeEvaluator, result: CurveLocationDetail[]): number; + readonly center: Point3d; + circularRadius(): number | undefined; + // (undocumented) + clone(): Arc3d; + clonePartialCurve(fractionA: number, fractionB: number): CurvePrimitive | undefined; + // (undocumented) + cloneTransformed(transform: Transform): CurvePrimitive; + // (undocumented) + closestPoint(spacePoint: Point3d, extend: boolean, result?: CurveLocationDetail): CurveLocationDetail; + computeStrokeCountForOptions(options?: StrokeOptions): number; + // (undocumented) + static create(center: Point3d, vector0: Vector3d, vector90: Vector3d, sweep?: AngleSweep, result?: Arc3d): Arc3d; + static createCircularStartMiddleEnd(pointA: XYAndZ, pointB: XYAndZ, pointC: XYAndZ, result?: Arc3d): Arc3d | LineString3d | undefined; + // (undocumented) + static createRefs(center: Point3d, matrix: Matrix3d, sweep: AngleSweep, result?: Arc3d): Arc3d; + // (undocumented) + static createScaledXYColumns(center: Point3d, matrix: Matrix3d, radius0: number, radius90: number, sweep: AngleSweep, result?: Arc3d): Arc3d; + // (undocumented) + static createUnitCircle(): Arc3d; + // (undocumented) + static createXY(center: Point3d, radius: number, sweep?: AngleSweep): Arc3d; + // (undocumented) + static createXYEllipse(center: Point3d, radiusA: number, radiusB: number, sweep?: AngleSweep): Arc3d; + curveLength(): number; + curveLengthBetweenFractions(fraction0: number, fraction1: number): number; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + emitStrokableParts(handler: IStrokeHandler, options?: StrokeOptions): void; + emitStrokes(dest: LineString3d, options?: StrokeOptions): void; + // (undocumented) + endPoint(result?: Point3d): Point3d; + // (undocumented) + extendRange(range: Range3d, transform?: Transform): void; + // (undocumented) + fractionToPoint(fraction: number, result?: Point3d): Point3d; + fractionToPointAnd2Derivatives(fraction: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + // (undocumented) + fractionToPointAndDerivative(fraction: number, result?: Ray3d): Ray3d; + getFractionToDistanceScale(): number | undefined; + // (undocumented) + isAlmostEqual(otherGeometry: GeometryQuery): boolean; + // (undocumented) + readonly isCircular: boolean; + readonly isExtensibleFractionSpace: boolean; + // (undocumented) + isInPlane(plane: Plane3dByOriginAndUnitNormal): boolean; + // (undocumented) + isSameGeometryClass(other: GeometryQuery): boolean; + readonly matrix: Matrix3d; + maxVectorLength(): number; + moveSignedDistanceFromFraction(startFraction: number, signedDistance: number, allowExtension: false, result?: CurveLocationDetail): CurveLocationDetail; + quickEccentricity(): number; + quickLength(): number; + // (undocumented) + radiansToPointAndDerivative(radians: number, result?: Ray3d): Ray3d; + // (undocumented) + reverseInPlace(): void; + // (undocumented) + set(center: Point3d, matrix: Matrix3d, sweep: AngleSweep | undefined): void; + // (undocumented) + setFrom(other: Arc3d): void; + // (undocumented) + setFromJSON(json?: any): void; + // (undocumented) + setRefs(center: Point3d, matrix: Matrix3d, sweep: AngleSweep): void; + // (undocumented) + setVector0Vector90(vector0: Vector3d, vector90: Vector3d): void; + // (undocumented) + startPoint(result?: Point3d): Point3d; + // (undocumented) + sweep: AngleSweep; + toJSON(): any; + // (undocumented) + toScaledMatrix3d: { + axes: Matrix3d; + center: Point3d; + r0: number; + r90: number; + sweep: AngleSweep; + } + toTransformedPoint4d: { + center: Point4d; + sweep: AngleSweep; + vector0: Point4d; + vector90: Point4d; + } + toTransformedVectors: { + center: Point3d; + sweep: AngleSweep; + vector0: Vector3d; + vector90: Vector3d; + } + toVectors: { + center: Point3d; + sweep: AngleSweep; + vector0: Vector3d; + vector90: Vector3d; + } + // (undocumented) + tryTransformInPlace(transform: Transform): boolean; + readonly vector0: Vector3d; + readonly vector90: Vector3d; +} + +// @public +class AuxChannel { + constructor(data: AuxChannelData[], dataType: AuxChannelDataType, name?: string, inputName?: string); + // (undocumented) + clone(): AuxChannel; + data: AuxChannelData[]; + // (undocumented) + dataType: AuxChannelDataType; + readonly entriesPerValue: number; + inputName?: string; + // (undocumented) + isAlmostEqual(other: AuxChannel, tol?: number): boolean; + readonly isScalar: boolean; + name?: string; + readonly scalarRange: Range1d | undefined; + readonly valueCount: number; +} + +// @public +class AuxChannelData { + constructor(input: number, values: number[]); + // (undocumented) + clone(): AuxChannelData; + // (undocumented) + copyValues(other: AuxChannelData, thisIndex: number, otherIndex: number, blockSize: number): void; + input: number; + // (undocumented) + isAlmostEqual(other: AuxChannelData, tol?: number): boolean; + values: number[]; +} + +// @public +enum AuxChannelDataType { + Distance = 1, + Normal = 3, + Scalar = 0, + Vector = 2 +} + +// @public (undocumented) +enum AxisIndex { + // (undocumented) + X = 0, + // (undocumented) + Y = 1, + // (undocumented) + Z = 2 +} + +// @public +enum AxisOrder { + XYZ = 0, + XZY = 4, + YXZ = 5, + YZX = 1, + ZXY = 2, + ZYX = 6 +} + +// @public +enum AxisScaleSelect { + LongestRangeDirection = 1, + NonUniformRangeContainment = 2, + Unit = 0 +} + +// @public +class BagOfCurves extends CurveCollection { + constructor(); + // (undocumented) + protected _children: AnyCurve[]; + // (undocumented) + announceToCurveProcessor(processor: RecursiveCurveProcessor, indexInParent?: number): void; + // (undocumented) + readonly children: AnyCurve[]; + // (undocumented) + cloneEmptyPeer(): BagOfCurves; + // (undocumented) + cloneStroked(options?: StrokeOptions): BagOfCurves; + // (undocumented) + static create(...data: AnyCurve[]): BagOfCurves; + // (undocumented) + dgnBoundaryType(): number; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + // (undocumented) + getChild(i: number): AnyCurve | undefined; + // (undocumented) + isSameGeometryClass(other: GeometryQuery): boolean; + // (undocumented) + tryAddChild(child: AnyCurve): boolean; +} + +// @public (undocumented) +interface BeJSONFunctions { + setFromJSON(json: any): void; + // (undocumented) + toJSON(): any; +} + +// @public +class Bezier1dNd { + constructor(blockSize: number, polygon: Float64Array); + clonePolygon(result?: Float64Array): Float64Array; + static create(data: Point2d[] | Point3d[] | Point4d[]): Bezier1dNd | undefined; + evaluate(s: number, buffer?: Float64Array): Float64Array; + evaluateDerivative(s: number, buffer?: Float64Array): Float64Array; + fractionToParentFraction(fraction: number): number; + getPolygonPoint(i: number, buffer?: Float64Array): Float64Array | undefined; + interpolatePoleInPlace(poleIndexA: number, fraction: number, poleIndexB: number): void; + interval?: Segment1d; + isAlmostEqual(other: any): boolean; + loadSpanPoles(data: Float64Array, spanIndex: number): void; + loadSpanPolesWithWeight(data: Float64Array, dataDimension: number, spanIndex: number, weight: number): void; + readonly order: number; + readonly packedData: Float64Array; + reverseInPlace(): void; + static saturate1dInPlace(coffs: Float64Array, knots: KnotVector, spanIndex: number): boolean; + // (undocumented) + saturateInPlace(knots: KnotVector, spanIndex: number): boolean; + setInterval(a: number, b: number): void; + setPolygonPoint(i: number, buffer: Float64Array): void; + subdivideInPlaceKeepLeft(fraction: number): boolean; + subdivideInPlaceKeepRight(fraction: number): boolean; + subdivideToIntervalInPlace(fraction0: number, fraction1: number): boolean; + unpackToJsonArrays(): any[]; +} + +// @public +class BezierCoffs { + constructor(data: number | Float64Array | number[]); + addInPlace(a: number): void; + protected allocateToOrder(order: number): void; + abstract basisFunctions(u: number, result?: Float64Array): Float64Array; + // (undocumented) + abstract clone(): BezierCoffs; + coffs: Float64Array; + copyFrom(other: BezierCoffs): void; + createPeer(): BezierCoffs; + abstract evaluate(u: number): number; + filter01(roots: number[] | undefined, restrictTo01?: boolean): number[] | undefined; + static maxAbsDiff(dataA: BezierCoffs, dataB: BezierCoffs): number | undefined; + readonly order: number; + roots(targetValue: number, _restrictTo01: boolean): number[] | undefined; + scaleInPlace(scale: number): void; + subdivide(u: number, left: BezierCoffs, right: BezierCoffs): boolean; + abstract sumBasisFunctionDerivatives(u: number, polygon: Float64Array, n: number, result?: Float64Array): Float64Array; + abstract sumBasisFunctions(u: number, polygon: Float64Array, n: number, result?: Float64Array): Float64Array; + // (undocumented) + zero(): void; +} + +// @public +class BezierCurve3d extends BezierCurveBase { + // (undocumented) + clone(): BezierCurve3d; + // (undocumented) + clonePartialCurve(f0: number, f1: number): BezierCurve3d | undefined; + cloneTransformed(transform: Transform): BezierCurve3d; + computeStrokeCountForOptions(options?: StrokeOptions): number; + copyPointsAsLineString(): LineString3d; + static create(data: Point3d[] | Point2d[]): BezierCurve3d | undefined; + static createOrder(order: number): BezierCurve3d; + dispatchToGeometryHandler(handler: GeometryHandler): any; + // (undocumented) + extendRange(rangeToExtend: Range3d, transform?: Transform): void; + fractionToPoint(fraction: number, result?: Point3d): Point3d; + fractionToPointAnd2Derivatives(fraction: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + fractionToPointAndDerivative(fraction: number, result?: Ray3d): Ray3d; + getPolePoint3d(i: number, result?: Point3d): Point3d | undefined; + getPolePoint4d(i: number, result?: Point4d): Point4d | undefined; + // (undocumented) + isAlmostEqual(other: any): boolean; + // (undocumented) + isSameGeometryClass(other: any): boolean; + loadSpanPoles(data: Float64Array, spanIndex: number): void; + // (undocumented) + tryTransformInPlace(transform: Transform): boolean; +} + +// @public +class BezierCurve3dH extends BezierCurveBase { + // (undocumented) + clone(): BezierCurve3dH; + cloneTransformed(transform: Transform): BezierCurve3dH; + computeStrokeCountForOptions(options?: StrokeOptions): number; + static create(data: Point3d[] | Point4d[] | Point2d[]): BezierCurve3dH | undefined; + static createOrder(order: number): BezierCurve3dH; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + // (undocumented) + extendRange(rangeToExtend: Range3d, transform?: Transform): void; + fractionToPoint(fraction: number, result?: Point3d): Point3d; + fractionToPoint4d(fraction: number, result?: Point4d): Point4d; + fractionToPointAnd2Derivatives(fraction: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + fractionToPointAndDerivative(fraction: number, result?: Ray3d): Ray3d; + getPolePoint3d(i: number, result?: Point3d): Point3d | undefined; + getPolePoint4d(i: number, result?: Point4d): Point4d | undefined; + // (undocumented) + isAlmostEqual(other: any): boolean; + // (undocumented) + isSameGeometryClass(other: any): boolean; + // (undocumented) + isUnitWeight(tolerance?: number): boolean; + loadSpan3dPolesWithWeight(data: Float64Array, spanIndex: number, weight: number): void; + loadSpan4dPoles(data: Float64Array, spanIndex: number): void; + poleProductsXYZW(products: Float64Array, ax: number, ay: number, az: number, aw: number): void; + tryMultiplyMatrix4dInPlace(matrix: Matrix4d): void; + tryTransformInPlace(transform: Transform): boolean; + updateClosestPointByTruePerpendicular(spacePoint: Point3d, detail: CurveLocationDetail): boolean; +} + +// @public +class BezierCurveBase extends CurvePrimitive { + protected constructor(blockSize: number, data: Float64Array); + // (undocumented) + protected _polygon: Bezier1dNd; + // (undocumented) + protected _workBezier?: UnivariateBezier; + // (undocumented) + protected _workCoffsA?: Float64Array; + // (undocumented) + protected _workCoffsB?: Float64Array; + protected _workData0: Float64Array; + // (undocumented) + protected _workData1: Float64Array; + protected _workPoint0: Point3d; + // (undocumented) + protected _workPoint1: Point3d; + protected allocateAndZeroBezierWorkData(primaryBezierOrder: number, orderA: number, orderB: number): void; + abstract computeStrokeCountForOptions(options?: StrokeOptions): number; + copyPolesAsJsonArray(): any[]; + // (undocumented) + readonly degree: number; + emitStrokableParts(handler: IStrokeHandler, _options?: StrokeOptions): void; + emitStrokes(dest: LineString3d, options?: StrokeOptions): void; + // (undocumented) + endPoint(): Point3d; + abstract extendRange(rangeToExtend: Range3d, transform?: Transform): void; + // (undocumented) + fractionToParentFraction(fraction: number): number; + abstract getPolePoint3d(i: number, point?: Point3d): Point3d | undefined; + abstract getPolePoint4d(i: number, point?: Point4d): Point4d | undefined; + isInPlane(plane: Plane3dByOriginAndUnitNormal): boolean; + // (undocumented) + readonly numPoles: number; + // (undocumented) + readonly order: number; + // (undocumented) + polygonLength(): number; + // (undocumented) + quickLength(): number; + reverseInPlace(): void; + saturateInPlace(knotVector: KnotVector, spanIndex: number): boolean; + // (undocumented) + setInterval(a: number, b: number): void; + // (undocumented) + startPoint(): Point3d; +} + +// @public +class BezierPolynomialAlgebra { + static accumulate(dataA: Float64Array, orderA: number, resultB: Float64Array): void; + static accumulateProduct(product: Float64Array, dataA: Float64Array, dataB: Float64Array, scale?: number): void; + static accumulateProductWithDifferences(product: Float64Array, dataA: Float64Array, dataB: Float64Array, scale?: number): void; + static accumulateScaledShiftedComponentTimesComponentDelta(product: Float64Array, data: Float64Array, dataBlockSize: number, dataOrder: number, scale: number, indexA: number, constA: number, indexB: number): void; + static componentDifference(difference: Float64Array, data: Float64Array, dataBlockSize: number, dataOrder: number, index: number): void; + static scaledComponentSum(sum: Float64Array, data: Float64Array, dataBlockSize: number, dataOrder: number, indexA: number, constA: number, indexB: number, constB: number): void; + static univariateDifference(data: Float64Array, difference: Float64Array): void; +} + +// @public (undocumented) +class Box extends SolidPrimitive { + protected constructor(map: Transform, baseX: number, baseY: number, topX: number, topY: number, capped: boolean); + // (undocumented) + clone(): Box; + // (undocumented) + cloneTransformed(transform: Transform): Box | undefined; + // (undocumented) + constantVSection(zFraction: number): CurveCollection; + // (undocumented) + static createDgnBox(baseOrigin: Point3d, vectorX: Vector3d, vectorY: Vector3d, topOrigin: Point3d, baseX: number, baseY: number, topX: number, topY: number, capped: boolean): Box | undefined; + // (undocumented) + static createDgnBoxWithAxes(baseOrigin: Point3d, axes: Matrix3d, topOrigin: Point3d, baseX: number, baseY: number, topX: number, topY: number, capped: boolean): Box | undefined; + // (undocumented) + static createRange(range: Range3d, capped: boolean): Box | undefined; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + // (undocumented) + extendRange(range: Range3d, transform?: Transform): void; + // (undocumented) + getBaseOrigin(): Point3d; + // (undocumented) + getBaseX(): number; + // (undocumented) + getBaseY(): number; + getConstructiveFrame(): Transform | undefined; + // (undocumented) + getCorners(): Point3d[]; + // (undocumented) + getTopOrigin(): Point3d; + // (undocumented) + getTopX(): number; + // (undocumented) + getTopY(): number; + // (undocumented) + getVectorX(): Vector3d; + // (undocumented) + getVectorY(): Vector3d; + // (undocumented) + getVectorZ(): Vector3d; + // (undocumented) + isAlmostEqual(other: GeometryQuery): boolean; + readonly isClosedVolume: boolean; + // (undocumented) + isSameGeometryClass(other: any): boolean; + // (undocumented) + strokeConstantVSection(zFraction: number): LineString3d; + // (undocumented) + tryTransformInPlace(transform: Transform): boolean; +} + +// WARNING: primaryCapId has incomplete type information +// @public (undocumented) +class BoxTopology { + // (undocumented) + static readonly axisEdgeVertex: number[][][]; + // (undocumented) + static readonly cornerIndexCCW: number[][]; + // (undocumented) + static readonly faceDirections: number[][][]; + // (undocumented) + static readonly faceId: number[][]; + // (undocumented) + static readonly partnerFace: number[][]; + static readonly points: Point3d[]; +} + +// @public +class BSpline1dNd { + protected constructor(numPoles: number, poleLength: number, order: number, knots: KnotVector); + // (undocumented) + basisBuffer: Float64Array; + // (undocumented) + basisBuffer1: Float64Array; + // (undocumented) + basisBuffer2: Float64Array; + // (undocumented) + static create(numPoles: number, poleLength: number, order: number, knots: KnotVector): BSpline1dNd | undefined; + // (undocumented) + readonly degree: number; + // (undocumented) + evaluateBasisFunctionsInSpan(spanIndex: number, spanFraction: number, f: Float64Array, df?: Float64Array, ddf?: Float64Array): void; + // (undocumented) + evaluateBuffersAtKnot(u: number, numDerivative?: number): void; + // (undocumented) + evaluateBuffersInSpan(spanIndex: number, spanFraction: number): void; + // (undocumented) + evaluateBuffersInSpan1(spanIndex: number, spanFraction: number): void; + // (undocumented) + getPoint3dPole(i: number, result?: Point3d): Point3d | undefined; + // (undocumented) + knots: KnotVector; + // (undocumented) + readonly numPoles: number; + // (undocumented) + readonly numSpan: number; + // (undocumented) + readonly order: number; + // (undocumented) + packedData: Float64Array; + // (undocumented) + poleBuffer: Float64Array; + // (undocumented) + poleBuffer1: Float64Array; + // (undocumented) + poleBuffer2: Float64Array; + // (undocumented) + poleLength: number; + // (undocumented) + reverseInPlace(): void; + // (undocumented) + spanFractionToKnot(span: number, localFraction: number): number; + sumPoleBuffer1ForSpan(spanIndex: number): void; + sumPoleBuffer2ForSpan(spanIndex: number): void; + sumPoleBufferForSpan(spanIndex: number): void; + testCloseablePolygon(mode?: BSplineWrapMode): boolean; +} + +// @public +class BSpline2dNd extends GeometryQuery { + protected constructor(numPolesU: number, numPolesV: number, poleLength: number, knotsU: KnotVector, knotsV: KnotVector); + // (undocumented) + protected _basisBuffer1UV: Float64Array[]; + // (undocumented) + protected _basisBufferUV: Float64Array[]; + // (undocumented) + protected _poleBuffer: Float64Array; + // (undocumented) + protected _poleBuffer1UV: Float64Array[]; + // (undocumented) + coffs: Float64Array; + // (undocumented) + degreeUV(select: UVSelect): number; + // (undocumented) + evaluateBuffersAtKnot(u: number, v: number, numDerivative?: number): void; + extendRangeXYZ(rangeToExtend: Range3d, transform?: Transform): void; + extendRangeXYZH(rangeToExtend: Range3d, transform?: Transform): void; + abstract fractionToPointAndDerivatives(_fractionU: number, _fractionV: number, _result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors | undefined; + fractionToRigidFrame(fractionU: number, fractionV: number, result?: Transform): Transform | undefined; + // (undocumented) + getPoint3dPole(i: number, j: number, result?: Point3d): Point3d | undefined; + // (undocumented) + getPoint3dPoleXYZW(i: number, j: number, result?: Point3d): Point3d | undefined; + isClosable(select: UVSelect): boolean; + // (undocumented) + knots: KnotVector[]; + // (undocumented) + numberToUVSelect(value: number): UVSelect; + // (undocumented) + numPolesTotal(): number; + // (undocumented) + numPolesUV(select: UVSelect): number; + // (undocumented) + numSpanUV(select: UVSelect): number; + // (undocumented) + orderUV(select: UVSelect): number; + // (undocumented) + poleDimension: number; + // (undocumented) + poleStepUV(select: UVSelect): number; + reverseInPlace(select: UVSelect): void; + setWrappable(select: UVSelect, value: BSplineWrapMode): void; + // (undocumented) + spanFractionsToBasisFunctions(select: UVSelect, spanIndex: number, spanFraction: number, f: Float64Array, df?: Float64Array): void; + spanFractionToKnot(select: UVSelect, span: number, localFraction: number): number; + sumpoleBufferDerivativesForSpan(spanIndexU: number, spanIndexV: number): void; + sumPoleBufferForSpan(spanIndexU: number, spanIndexV: number): void; + // (undocumented) + static validOrderAndPoleCounts(orderU: number, numPolesU: number, orderV: number, numPolesV: number, numUV: number): boolean; +} + +// @public +class BSplineCurve3d extends BSplineCurve3dBase { + // (undocumented) + clone(): BSplineCurve3d; + // (undocumented) + cloneTransformed(transform: Transform): BSplineCurve3d; + // WARNING: The type "StrokeCountMap" needs to be exported by the package (e.g. added to index.ts) + computeAndAttachRecursiveStrokeCounts(options?: StrokeOptions, parentStrokeMap?: StrokeCountMap): void; + computeStrokeCountForOptions(options?: StrokeOptions): number; + copyKnots(includeExtraEndKnot: boolean): number[]; + copyPoints(): any[]; + copyPointsFloat64Array(): Float64Array; + static create(poleArray: Float64Array | Point3d[], knotArray: Float64Array | number[], order: number): BSplineCurve3d | undefined; + static createUniformKnots(poles: Point3d[] | Float64Array, order: number): BSplineCurve3d | undefined; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + // (undocumented) + emitStrokableParts(handler: IStrokeHandler, options?: StrokeOptions): void; + // (undocumented) + emitStrokes(dest: LineString3d, options?: StrokeOptions): void; + // (undocumented) + evaluatePointAndTangentInSpan(spanIndex: number, spanFraction: number): Ray3d; + evaluatePointInSpan(spanIndex: number, spanFraction: number): Point3d; + // (undocumented) + extendRange(rangeToExtend: Range3d, transform?: Transform): void; + // (undocumented) + fractionToPoint(fraction: number, result?: Point3d): Point3d; + fractionToPointAnd2Derivatives(fraction: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + // (undocumented) + fractionToPointAndDerivative(fraction: number, result?: Ray3d): Ray3d; + // (undocumented) + getPolePoint3d(poleIndex: number, result?: Point3d): Point3d | undefined; + // (undocumented) + getPolePoint4d(poleIndex: number, result?: Point4d): Point4d | undefined; + getSaturatedBezierSpan3d(spanIndex: number, result?: BezierCurveBase): BezierCurveBase | undefined; + getSaturatedBezierSpan3dH(spanIndex: number, result?: BezierCurveBase): BezierCurve3dH | undefined; + getSaturatedBezierSpan3dOr3dH(spanIndex: number, prefer3dH: boolean, result?: BezierCurveBase): BezierCurveBase | undefined; + // (undocumented) + isAlmostEqual(other: any): boolean; + readonly isClosable: BSplineWrapMode; + // (undocumented) + isInPlane(plane: Plane3dByOriginAndUnitNormal): boolean; + // (undocumented) + isSameGeometryClass(other: any): boolean; + knotToPoint(u: number, result?: Point3d): Point3d; + knotToPointAnd2Derivatives(u: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + knotToPointAndDerivative(u: number, result?: Ray3d): Ray3d; + // (undocumented) + quickLength(): number; + setWrappable(value: BSplineWrapMode): void; + // (undocumented) + spanFractionToKnot(span: number, localFraction: number): number; + // (undocumented) + tryTransformInPlace(transform: Transform): boolean; +} + +// @public +class BSplineCurve3dBase extends CurvePrimitive { + protected constructor(poleDimension: number, numPoles: number, order: number, knots: KnotVector); + // (undocumented) + protected _bcurve: BSpline1dNd; + appendPlaneIntersectionPoints(plane: PlaneAltitudeEvaluator, result: CurveLocationDetail[]): number; + closestPoint(spacePoint: Point3d, _extend: boolean): CurveLocationDetail | undefined; + collectBezierSpans(prefer3dH: boolean): BezierCurveBase[]; + copyKnots(includeExtraEndKnot: boolean): number[]; + // (undocumented) + readonly degree: number; + endPoint(): Point3d; + abstract evaluatePointAndTangentInSpan(spanIndex: number, spanFraction: number, result?: Ray3d): Ray3d; + abstract evaluatePointInSpan(spanIndex: number, spanFraction: number, result?: Point3d): Point3d; + // (undocumented) + fractionToPoint(fraction: number, result?: Point3d): Point3d; + fractionToPointAnd2Derivatives(fraction: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + fractionToPointAndDerivative(fraction: number, result?: Ray3d): Ray3d; + abstract getPolePoint3d(poleIndex: number, result?: Point3d): Point3d | undefined; + abstract getPolePoint4d(poleIndex: number, result?: Point4d): Point4d | undefined; + abstract getSaturatedBezierSpan3dOr3dH(spanIndex: number, prefer3dH: boolean, result?: BezierCurveBase): BezierCurveBase | undefined; + abstract knotToPoint(knot: number, result?: Point3d): Point3d; + abstract knotToPointAnd2Derivatives(knot: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + abstract knotToPointAndDerivative(knot: number, result?: Ray3d): Ray3d; + // (undocumented) + readonly numPoles: number; + // (undocumented) + readonly numSpan: number; + // (undocumented) + readonly order: number; + poleIndexToDataIndex(poleIndex: number): number | undefined; + reverseInPlace(): void; + setWrappable(value: BSplineWrapMode): void; + startPoint(): Point3d; +} + +// @public +class BSplineCurve3dH extends BSplineCurve3dBase { + // (undocumented) + clone(): BSplineCurve3dH; + // (undocumented) + cloneTransformed(transform: Transform): BSplineCurve3dH; + // WARNING: The type "StrokeCountMap" needs to be exported by the package (e.g. added to index.ts) + computeAndAttachRecursiveStrokeCounts(options?: StrokeOptions, parentStrokeMap?: StrokeCountMap): void; + computeStrokeCountForOptions(options?: StrokeOptions): number; + copyPoints(): any[]; + copyPointsFloat64Array(): Float64Array; + static create(controlPoints: Float64Array | Point4d[] | Point3d[], knotArray: Float64Array | number[], order: number): BSplineCurve3dH | undefined; + static createUniformKnots(controlPoints: Point3d[] | Point4d[] | Float64Array, order: number): BSplineCurve3dH | undefined; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + // (undocumented) + emitStrokableParts(handler: IStrokeHandler, options?: StrokeOptions): void; + // (undocumented) + emitStrokes(dest: LineString3d, options?: StrokeOptions): void; + evaluatePointAndTangentInSpan(spanIndex: number, spanFraction: number, result?: Ray3d): Ray3d; + evaluatePointInSpan(spanIndex: number, spanFraction: number, result?: Point3d): Point3d; + // (undocumented) + extendRange(rangeToExtend: Range3d, transform?: Transform): void; + // (undocumented) + getPolePoint3d(poleIndex: number, result?: Point3d): Point3d | undefined; + // (undocumented) + getPolePoint4d(poleIndex: number, result?: Point4d): Point4d | undefined; + getSaturatedBezierSpan3dH(spanIndex: number, result?: BezierCurveBase): BezierCurveBase | undefined; + getSaturatedBezierSpan3dOr3dH(spanIndex: number, _prefer3dH: boolean, result?: BezierCurveBase): BezierCurveBase | undefined; + // (undocumented) + isAlmostEqual(other: any): boolean; + readonly isClosable: boolean; + // (undocumented) + isInPlane(plane: Plane3dByOriginAndUnitNormal): boolean; + // (undocumented) + isSameGeometryClass(other: any): boolean; + knotToPoint(u: number, result?: Point3d): Point3d; + knotToPointAnd2Derivatives(u: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + knotToPointAndDerivative(u: number, result?: Ray3d): Ray3d; + // (undocumented) + quickLength(): number; + // (undocumented) + spanFractionToKnot(span: number, localFraction: number): number; + // (undocumented) + tryTransformInPlace(transform: Transform): boolean; +} + +// @public +class BSplineSurface3d extends BSpline2dNd, implements BSplineSurface3dQuery { + // (undocumented) + clone(): BSplineSurface3d; + cloneTransformed(transform: Transform): BSplineSurface3d; + copyKnots(select: UVSelect, includeExtraEndKnot: boolean): number[]; + copyPointsFloat64Array(): Float64Array; + static create(controlPointArray: Point3d[] | Float64Array, numPolesU: number, orderU: number, knotArrayU: number[] | Float64Array | undefined, numPolesV: number, orderV: number, knotArrayV: number[] | Float64Array | undefined): BSplineSurface3d | undefined; + static createGrid(points: number[][][], orderU: number, knotArrayU: number[] | Float64Array | undefined, orderV: number, knotArrayV: number[] | Float64Array | undefined): BSplineSurface3d | undefined; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + // (undocumented) + extendRange(rangeToExtend: Range3d, transform?: Transform): void; + fractionToPoint(fractionU: number, fractionV: number): Point3d; + fractionToPointAndDerivatives(fractionU: number, fractionV: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + getPointArray(flatArray?: boolean): any[]; + getPointGridJSON(): PackedPointGrid; + // (undocumented) + getPole(i: number, j: number, result?: Point3d): Point3d | undefined; + // (undocumented) + isAlmostEqual(other: any): boolean; + // (undocumented) + isInPlane(plane: Plane3dByOriginAndUnitNormal): boolean; + // (undocumented) + isSameGeometryClass(other: any): boolean; + knotToPoint(u: number, v: number): Point3d; + knotToPointAndDerivatives(u: number, v: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + // (undocumented) + tryTransformInPlace(transform: Transform): boolean; +} + +// @public +class BSplineSurface3dH extends BSpline2dNd, implements BSplineSurface3dQuery { + // (undocumented) + clone(): BSplineSurface3dH; + // (undocumented) + cloneTransformed(transform: Transform): BSplineSurface3dH; + copyKnots(select: UVSelect, includeExtraEndKnot: boolean): number[]; + copyPoints4d(): Point4d[]; + copyPointsAndWeights(points: Point3d[], weights: number[], formatter?: (x: number, y: number, z: number) => any): void; + static create(controlPointArray: Point3d[], weightArray: number[], numPolesU: number, orderU: number, knotArrayU: number[] | undefined, numPolesV: number, orderV: number, knotArrayV: number[] | undefined): BSplineSurface3dH | undefined; + static createGrid(xyzwGrid: number[][][], weightStyle: WeightStyle, orderU: number, knotArrayU: number[], orderV: number, knotArrayV: number[]): BSplineSurface3dH | undefined; + dispatchToGeometryHandler(handler: GeometryHandler): any; + extendRange(rangeToExtend: Range3d, transform?: Transform): void; + fractionToPoint(fractionU: number, fractionV: number, result?: Point3d): Point3d; + // (undocumented) + fractionToPoint4d(fractionU: number, fractionV: number): Point4d; + fractionToPointAndDerivatives(fractionU: number, fractionV: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + getPointGridJSON(): PackedPointGrid; + // (undocumented) + getPole(i: number, j: number, result?: Point3d): Point3d | undefined; + // (undocumented) + isAlmostEqual(other: any): boolean; + // (undocumented) + isInPlane(plane: Plane3dByOriginAndUnitNormal): boolean; + // (undocumented) + isSameGeometryClass(other: any): boolean; + knotToPoint(knotU: number, knotV: number, result?: Point3d): Point3d; + knotToPoint4d(u: number, v: number): Point4d; + knotToPointAndDerivatives(u: number, v: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + // (undocumented) + tryTransformInPlace(transform: Transform): boolean; +} + +// @public +interface BSplineSurface3dQuery { + // (undocumented) + clone(): BSplineSurface3dQuery; + // (undocumented) + cloneTransformed(transform: Transform): BSplineSurface3dQuery; + degreeUV(select: UVSelect): number; + // (undocumented) + extendRange(range: Range3d, transform?: Transform): void; + // (undocumented) + fractionToPoint(uFractioin: number, vFraction: number): Point3d; + // (undocumented) + fractionToRigidFrame(uFraction: number, vFraction: number): Transform | undefined; + getPointGridJSON(): PackedPointGrid; + // (undocumented) + isAlmostEqual(other: any): boolean; + // (undocumented) + isClosable(select: UVSelect): boolean; + // (undocumented) + isInPlane(plane: Plane3dByOriginAndUnitNormal): boolean; + // (undocumented) + isSameGeometryClass(other: any): boolean; + // (undocumented) + knotToPoint(uKnot: number, vKnot: number): Point3d; + numberToUVSelect(value: number): UVSelect; + // (undocumented) + numPolesTotal(): number; + numPolesUV(select: UVSelect): number; + numSpanUV(select: UVSelect): number; + orderUV(select: UVSelect): number; + poleStepUV(select: UVSelect): number; + // (undocumented) + reverseInPlace(select: UVSelect): void; + // (undocumented) + tryTransformInPlace(transform: Transform): boolean; +} + +// @public +enum BSplineWrapMode { + None = 0, + OpenByAddingControlPoints = 1, + OpenByRemovingKnots = 2 +} + +// @public +enum ClipMask { + // (undocumented) + All = 63, + // (undocumented) + None = 0, + // (undocumented) + XAndY = 15, + // (undocumented) + XHigh = 2, + // (undocumented) + XLow = 1, + // (undocumented) + YHigh = 8, + // (undocumented) + YLow = 4, + // (undocumented) + ZHigh = 32, + // (undocumented) + ZLow = 16 +} + +// @public +interface Clipper { + // (undocumented) + announceClippedArcIntervals(arc: Arc3d, announce?: AnnounceNumberNumberCurvePrimitive): boolean; + announceClippedSegmentIntervals(f0: number, f1: number, pointA: Point3d, pointB: Point3d, announce?: AnnounceNumberNumber): boolean; + // (undocumented) + isPointOnOrInside(point: Point3d, tolerance?: number): boolean; +} + +// @public +class ClipPlane implements Clipper { + // (undocumented) + announceClippedArcIntervals(arc: Arc3d, announce?: AnnounceNumberNumberCurvePrimitive): boolean; + announceClippedSegmentIntervals(f0: number, f1: number, pointA: Point3d, pointB: Point3d, announce?: (fraction0: number, fraction1: number) => void): boolean; + // (undocumented) + appendIntersectionRadians(arc: Arc3d, intersectionRadians: GrowableFloat64Array): void; + clone(): ClipPlane; + cloneNegated(): ClipPlane; + // (undocumented) + convexPolygonClipInPlace(xyz: Point3d[], work: Point3d[]): void; + // (undocumented) + convexPolygonSplitInsideOutside(xyz: Point3d[], xyzIn: Point3d[], xyzOut: Point3d[], altitudeRange: Range1d): void; + // (undocumented) + static createEdgeAndUpVector(point0: Point3d, point1: Point3d, upVector: Vector3d, tiltAngle: Angle, result?: ClipPlane): ClipPlane | undefined; + // (undocumented) + static createEdgeXY(point0: Point3d, point1: Point3d, result?: ClipPlane): ClipPlane | undefined; + static createNormalAndDistance(normal: Vector3d, distance: number, invisible?: boolean, interior?: boolean, result?: ClipPlane): ClipPlane | undefined; + static createNormalAndPoint(normal: Vector3d, point: Point3d, invisible?: boolean, interior?: boolean, result?: ClipPlane): ClipPlane | undefined; + static createNormalAndPointXYZXYZ(normalX: number, normalY: number, normalZ: number, originX: number, originY: number, originZ: number, invisible?: boolean, interior?: boolean): ClipPlane | undefined; + static createPlane(plane: Plane3dByOriginAndUnitNormal, invisible?: boolean, interior?: boolean, result?: ClipPlane): ClipPlane; + // (undocumented) + readonly distance: number; + // (undocumented) + dotProductPlaneNormalPoint(point: Point3d): number; + // (undocumented) + dotProductVector(vector: Vector3d): number; + // (undocumented) + evaluatePoint(point: Point3d): number; + // (undocumented) + static fractionTol: number; + // (undocumented) + static fromJSON(json: any, result?: ClipPlane): ClipPlane | undefined; + getBoundedSegmentSimpleIntersection(pointA: Point3d, pointB: Point3d): number | undefined; + // (undocumented) + getPlane3d(): Plane3dByOriginAndUnitNormal; + // (undocumented) + getPlane4d(): Point4d; + // (undocumented) + readonly interior: boolean; + // (undocumented) + readonly invisible: boolean; + // (undocumented) + readonly inwardNormalRef: Vector3d; + // (undocumented) + isAlmostEqual(other: ClipPlane): boolean; + // (undocumented) + isPointInside(point: Point3d, tolerance?: number): boolean; + // (undocumented) + isPointOn(point: Point3d, tolerance?: number): boolean; + // (undocumented) + isPointOnOrInside(point: Point3d, tolerance?: number): boolean; + // (undocumented) + multiplyPlaneByMatrix(matrix: Matrix4d): void; + negateInPlace(): void; + offsetDistance(offset: number): void; + // (undocumented) + polygonCrossings(xyz: Point3d[], crossings: Point3d[]): void; + // (undocumented) + setFlags(invisible: boolean, interior: boolean): void; + // (undocumented) + setInvisible(invisible: boolean): void; + // (undocumented) + setPlane4d(plane: Point4d): void; + toJSON(): any; + // (undocumented) + transformInPlace(transform: Transform): boolean; +} + +// @public +enum ClipPlaneContainment { + // (undocumented) + Ambiguous = 2, + // (undocumented) + StronglyInside = 1, + // (undocumented) + StronglyOutside = 3 +} + +// @public +class ClipPrimitive { + protected constructor(planeSet?: UnionOfConvexClipPlaneSets | undefined, isInvisible?: boolean); + // (undocumented) + protected _clipPlanes?: UnionOfConvexClipPlaneSets; + // (undocumented) + protected _invisible: boolean; + // (undocumented) + protected _maskPlanes?: UnionOfConvexClipPlaneSets; + static addOutsideEdgeSetToParams(x0: number, y0: number, x1: number, y1: number, pParams: PlaneSetParamsCache, isInvisible?: boolean): void; + static addShapeToParams(shape: Point3d[], pFlags: number[], pParams: PlaneSetParamsCache): void; + classifyPointContainment(points: Point3d[], ignoreMasks: boolean): ClipPlaneContainment; + // (undocumented) + containsZClip(): boolean; + // (undocumented) + abstract fetchClipPlanesRef(): UnionOfConvexClipPlaneSets | undefined; + // (undocumented) + abstract fetchMaskPlanesRef(): UnionOfConvexClipPlaneSets | undefined; + abstract getRange(returnMaskRange: boolean, transform: Transform, result?: Range3d): Range3d | undefined; + // (undocumented) + readonly invisible: boolean; + // (undocumented) + static isLimitEdge(limitValue: number, point0: Point3d, point1: Point3d): boolean; + // (undocumented) + abstract multiplyPlanesTimesMatrix(matrix: Matrix4d): boolean; + setInvisible(invisible: boolean): void; + // (undocumented) + abstract toJSON(): any; + transformInPlace(transform: Transform): boolean; +} + +// @public +class ClipShape extends ClipPrimitive { + protected constructor(polygon?: Point3d[], zLow?: number, zHigh?: number, transform?: Transform, isMask?: boolean, invisible?: boolean); + // (undocumented) + protected _bCurve: BSplineCurve3d | undefined; + // (undocumented) + protected _isMask: boolean; + // (undocumented) + protected _polygon: Point3d[]; + // (undocumented) + protected _transformFromClip: Transform | undefined; + // (undocumented) + protected _transformToClip: Transform | undefined; + // (undocumented) + protected _transformValid: boolean; + // (undocumented) + protected _zHigh: number | undefined; + // (undocumented) + protected _zHighValid: boolean; + // (undocumented) + protected _zLow: number | undefined; + // (undocumented) + protected _zLowValid: boolean; + arePlanesDefined(): boolean; + readonly bCurve: BSplineCurve3d | undefined; + clone(result?: ClipShape): ClipShape; + static createBlock(extremities: Range3d, clipMask: ClipMask, isMask?: boolean, invisible?: boolean, transform?: Transform, result?: ClipShape): ClipShape; + static createEmpty(isMask?: boolean, invisible?: boolean, transform?: Transform, result?: ClipShape): ClipShape; + static createFrom(other: ClipShape, result?: ClipShape): ClipShape; + static createShape(polygon?: Point3d[], zLow?: number, zHigh?: number, transform?: Transform, isMask?: boolean, invisible?: boolean, result?: ClipShape): ClipShape | undefined; + fetchClipPlanesRef(): UnionOfConvexClipPlaneSets; + fetchMaskPlanesRef(): UnionOfConvexClipPlaneSets | undefined; + // (undocumented) + static fromJSON(json: any, result?: ClipShape): ClipShape | undefined; + getRange(returnMaskRange?: boolean, transform?: Transform, result?: Range3d): Range3d | undefined; + initSecondaryProps(isMask: boolean, zLow?: number, zHigh?: number, transform?: Transform): void; + readonly invisible: boolean; + readonly isMask: boolean; + readonly isValidPolygon: boolean; + // (undocumented) + readonly isXYPolygon: boolean; + // (undocumented) + multiplyPlanesTimesMatrix(matrix: Matrix4d): boolean; + performTransformFromClip(point: Point3d): void; + performTransformToClip(point: Point3d): void; + pointInside(point: Point3d, onTolerance?: number): boolean; + readonly polygon: Point3d[]; + setPolygon(polygon: Point3d[]): void; + // (undocumented) + toJSON(): any; + readonly transformFromClip: Transform | undefined; + // (undocumented) + transformInPlace(transform: Transform): boolean; + readonly transformToClip: Transform | undefined; + readonly transformValid: boolean; + readonly zHigh: number | undefined; + readonly zHighValid: boolean; + readonly zLow: number | undefined; + readonly zLowValid: boolean; +} + +// @public +enum ClipStatus { + // (undocumented) + ClipRequired = 0, + // (undocumented) + TrivialAccept = 2, + // (undocumented) + TrivialReject = 1 +} + +// @public +class ClipUtilities { + static announceNNC(intervals: Range1d[], cp: CurvePrimitive, announce?: AnnounceNumberNumberCurvePrimitive): boolean; + static clipPolygonToClipShape(polygon: Point3d[], clipShape: ClipShape): Point3d[][]; + // (undocumented) + static collectClippedCurves(curve: CurvePrimitive, clipper: Clipper): CurvePrimitive[]; + static pointSetSingleClipStatus(points: GrowableXYZArray, planeSet: UnionOfConvexClipPlaneSets, tolerance: number): ClipStatus; + // (undocumented) + static selectIntervals01(curve: CurvePrimitive, unsortedFractions: GrowableFloat64Array, clipper: Clipper, announce?: AnnounceNumberNumberCurvePrimitive): boolean; +} + +// @public +class ClipVector { + appendClone(clip: ClipShape): void; + appendReference(clip: ClipShape): void; + appendShape(shape: Point3d[], zLow?: number, zHigh?: number, transform?: Transform, isMask?: boolean, invisible?: boolean): boolean; + // (undocumented) + boundingRange: Range3d; + classifyPointContainment(points: Point3d[], ignoreMasks?: boolean): ClipPlaneContainment; + classifyRangeContainment(range: Range3d, ignoreMasks: boolean): ClipPlaneContainment; + clear(): void; + readonly clips: ClipShape[]; + clone(result?: ClipVector): ClipVector; + static createClipShapeClones(clips: ClipShape[], result?: ClipVector): ClipVector; + static createClipShapeRefs(clips: ClipShape[], result?: ClipVector): ClipVector; + static createEmpty(result?: ClipVector): ClipVector; + static createFrom(donor: ClipVector, result?: ClipVector): ClipVector; + extractBoundaryLoops(loopPoints: Point3d[][], transform?: Transform): number[]; + static fromJSON(json: any, result?: ClipVector): ClipVector; + getRange(transform?: Transform, result?: Range3d): Range3d | undefined; + isAnyLineStringPointInside(points: Point3d[]): boolean; + isLineStringCompletelyContained(points: Point3d[]): boolean; + readonly isValid: boolean; + multiplyPlanesTimesMatrix(matrix: Matrix4d): boolean; + parseClipPlanes(): void; + pointInside(point: Point3d, onTolerance?: number): boolean; + setInvisible(invisible: boolean): void; + sumSizes(intervals: Segment1d[], begin: number, end: number): number; + toJSON(): any; + transformInPlace(transform: Transform): boolean; +} + +// WARNING: clusterTerminator has incomplete type information +// @public (undocumented) +class ClusterableArray extends GrowableBlockedArray { + constructor(numCoordinatePerPoint: number, numExtraDataPerPoint: number, initialBlockCapacity: number); + addBlock(data: number[]): void; + addDirect(x0: number, x1: number, x2?: number, x3?: number, x4?: number): void; + addPoint2d(xy: Point2d, a?: number, b?: number, c?: number): void; + addPoint3d(xyz: Point3d, a?: number, b?: number, c?: number): void; + // (undocumented) + static clusterGrowablePoint3dArray(source: GrowableXYZArray, tolerance?: number): PackedPointsWithIndex; + clusterIndicesLexical(clusterTolerance?: number): Uint32Array; + // (undocumented) + static clusterPoint3dArray(data: Point3d[], tolerance?: number): PackedPointsWithIndex; + countClusters(clusteredBlocks: Uint32Array): number; + // (undocumented) + createIndexBlockToClusterIndex(clusteredBlocks: Uint32Array): Uint32Array; + // (undocumented) + createIndexBlockToClusterStart(clusteredBlocks: Uint32Array): Uint32Array; + createIndexClusterToClusterStart(clusteredBlocks: Uint32Array): Uint32Array; + getData(blockIndex: number, i: number): number; + getExtraData(blockIndex: number, i: number): number; + // (undocumented) + getPoint2d(blockIndex: number, result?: Point2d): Point2d; + // (undocumented) + getPoint3d(blockIndex: number, result?: Point3d): Point3d; + // (undocumented) + static isClusterTerminator(x: number): boolean; + setExtraData(blockIndex: number, i: number, value: number): void; + setupPrimaryClusterSort(): void; + sortSubsetsBySingleKey(blockedIndices: Uint32Array, dataIndex: number): void; + // (undocumented) + static sortVectorComponent(index: number): number; + // (undocumented) + toJSON(): any[]; +} + +// @public +export function compareRange1dLexicalLowHigh(a: Range1d, b: Range1d): number; + +// @public (undocumented) +class Complex implements BeJSONFunctions { + constructor(x?: number, y?: number); + // (undocumented) + angle(): Angle; + // (undocumented) + clone(): Complex; + // (undocumented) + static create(x?: number, y?: number, result?: Complex): Complex; + // (undocumented) + distance(other: Complex): number; + // (undocumented) + divide(other: Complex, result?: Complex): Complex | undefined; + // (undocumented) + static fromJSON(json?: any): Complex; + // (undocumented) + isAlmostEqual(other: Complex): boolean; + // (undocumented) + magnitude(): number; + // (undocumented) + magnitudeSquared(): number; + // (undocumented) + minus(other: Complex, result?: Complex): Complex; + // (undocumented) + plus(other: Complex, result?: Complex): Complex; + // (undocumented) + set(x?: number, y?: number): void; + // (undocumented) + setFrom(other: Complex): void; + // (undocumented) + setFromJSON(json?: any): void; + // (undocumented) + sqrt(result?: Complex): Complex; + // (undocumented) + times(other: Complex, result?: Complex): Complex; + timesXY(x: number, y: number, result?: Complex): Complex; + toJSON(): any; + // (undocumented) + x: any; + // (undocumented) + y: any; +} + +// @public +class Cone extends SolidPrimitive, implements UVSurface, UVSurfaceIsoParametricDistance { + protected constructor(map: Transform, radiusA: number, radiusB: number, capped: boolean); + // (undocumented) + clone(): Cone; + // (undocumented) + cloneTransformed(transform: Transform): Cone | undefined; + // (undocumented) + constantVSection(vFraction: number): CurveCollection | undefined; + static createAxisPoints(centerA: Point3d, centerB: Point3d, radiusA: number, radiusB: number, capped: boolean): Cone | undefined; + static createBaseAndTarget(centerA: Point3d, centerB: Point3d, vectorX: Vector3d, vectorY: Vector3d, radiusA: number, radiusB: number, capped: boolean): Cone; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + // (undocumented) + extendRange(range: Range3d, transform?: Transform): void; + // (undocumented) + getCenterA(): Point3d; + // (undocumented) + getCenterB(): Point3d; + getConstructiveFrame(): Transform | undefined; + // (undocumented) + getMaxRadius(): number; + // (undocumented) + getRadiusA(): number; + // (undocumented) + getRadiusB(): number; + // (undocumented) + getVectorX(): Vector3d; + // (undocumented) + getVectorY(): Vector3d; + // (undocumented) + isAlmostEqual(other: GeometryQuery): boolean; + readonly isClosedVolume: boolean; + // (undocumented) + isSameGeometryClass(other: any): boolean; + maxIsoParametricDistance(): Vector2d; + strokeConstantVSection(v: number, fixedStrokeCount: number | undefined, options: StrokeOptions | undefined): LineString3d; + // (undocumented) + tryTransformInPlace(transform: Transform): boolean; + // (undocumented) + uvFractionToPoint(uFraction: number, vFraction: number, result?: Point3d): Point3d; + // (undocumented) + uvFractionToPointAndTangents(uFraction: number, vFraction: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + // (undocumented) + vFractionToRadius(v: number): number; +} + +// @public +class Constant { + // (undocumented) + static readonly circumferenceOfEarth: number; + // (undocumented) + static readonly diameterOfEarth: number; + // (undocumented) + static readonly oneCentimeter: number; + // (undocumented) + static readonly oneKilometer: number; + // (undocumented) + static readonly oneMeter: number; + // (undocumented) + static readonly oneMillimeter: number; +} + +// @public +class ConstructCurveBetweenCurves extends NullGeometryHandler { + handleArc3d(arc0: Arc3d): any; + handleLineSegment3d(segment0: LineSegment3d): any; + handleLineString3d(ls0: LineString3d): any; + static interpolateBetween(geometry0: GeometryQuery, fraction: number, geometry1: GeometryQuery): GeometryQuery | undefined; +} + +// WARNING: hugeVal has incomplete type information +// @public +class ConvexClipPlaneSet implements Clipper { + // (undocumented) + addPlaneToConvexSet(plane: ClipPlane | undefined): void; + // (undocumented) + addZClipPlanes(invisible: boolean, zLow?: number, zHigh?: number): void; + // (undocumented) + announceClippedArcIntervals(arc: Arc3d, announce?: AnnounceNumberNumberCurvePrimitive): boolean; + announceClippedSegmentIntervals(f0: number, f1: number, pointA: Point3d, pointB: Point3d, announce?: (fraction0: number, fraction1: number) => void): boolean; + classifyPointContainment(points: Point3d[], onIsOutside: boolean): ClipPlaneContainment; + // (undocumented) + clipPointsOnOrInside(points: Point3d[], inOrOn: Point3d[], out: Point3d[]): void; + clipUnboundedSegment(pointA: Point3d, pointB: Point3d, announce?: (fraction0: number, fraction1: number) => void): boolean; + // (undocumented) + clone(result?: ConvexClipPlaneSet): ConvexClipPlaneSet; + // (undocumented) + static createEmpty(result?: ConvexClipPlaneSet): ConvexClipPlaneSet; + // (undocumented) + static createPlanes(planes: ClipPlane[], result?: ConvexClipPlaneSet): ConvexClipPlaneSet; + static createRange3dPlanes(range: Range3d, lowX?: boolean, highX?: boolean, lowY?: boolean, highY?: boolean, lowZ?: boolean, highZ?: boolean): ConvexClipPlaneSet; + static createSweptPolyline(points: Point3d[], upVector: Vector3d, tiltAngle: Angle): ConvexClipPlaneSet | undefined; + // (undocumented) + static createXYBox(x0: number, y0: number, x1: number, y1: number, result?: ConvexClipPlaneSet): ConvexClipPlaneSet; + // (undocumented) + static createXYPolyLine(points: Point3d[], interior: boolean[], leftIsInside: boolean, result?: ConvexClipPlaneSet): ConvexClipPlaneSet; + static createXYPolyLineInsideLeft(points: Point3d[], result?: ConvexClipPlaneSet): ConvexClipPlaneSet; + // (undocumented) + static fromJSON(json: any, result?: ConvexClipPlaneSet): ConvexClipPlaneSet; + getRangeOfAlignedPlanes(transform?: Transform, result?: Range3d): Range3d | undefined; + // (undocumented) + isAlmostEqual(other: ConvexClipPlaneSet): boolean; + // (undocumented) + isPointInside(point: Point3d): boolean; + // (undocumented) + isPointOnOrInside(point: Point3d, tolerance: number): boolean; + // (undocumented) + isSphereInside(point: Point3d, radius: number): boolean; + // (undocumented) + multiplyPlanesByMatrix(matrix: Matrix4d): void; + negateAllPlanes(): void; + // (undocumented) + readonly planes: ClipPlane[]; + // (undocumented) + polygonClip(input: Point3d[], output: Point3d[], work: Point3d[]): void; + reloadSweptPolygon(points: Point3d[], sweepDirection: Vector3d, sideSelect: number): number; + // (undocumented) + setInvisible(invisible: boolean): void; + // (undocumented) + static testRayIntersections(tNear: Float64Array, origin: Point3d, direction: Vector3d, planes: ConvexClipPlaneSet): boolean; + // (undocumented) + toJSON(): any; + // (undocumented) + transformInPlace(transform: Transform): void; +} + +// @public (undocumented) +class ConvexPolygon2d { + constructor(points: Point2d[]); + clipRay(ray: Ray2d): Range1d; + static computeConvexHull(points: Point2d[]): Point2d[]; + containsPoint(point: Point2d): boolean; + static createHull(points: Point2d[]): ConvexPolygon2d; + static createHullIsValidCheck(points: Point2d[]): ConvexPolygon2d; + distanceOutside(xy: Point2d): number; + static isValidConvexHull(points: Point2d[]): boolean; + offsetInPlace(distance: number): boolean; + readonly points: Point2d[]; + rangeAlongRay(ray: Ray2d): Range1d; + rangePerpendicularToRay(ray: Ray2d): Range1d; +} + +// @public +class CoordinateXYZ extends GeometryQuery { + clone(): GeometryQuery | undefined; + cloneTransformed(transform: Transform): GeometryQuery | undefined; + // (undocumented) + static create(point: Point3d): CoordinateXYZ; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + // (undocumented) + extendRange(rangeToExtend: Range3d, transform?: Transform): void; + isAlmostEqual(other: GeometryQuery): boolean; + isSameGeometryClass(other: GeometryQuery): boolean; + // (undocumented) + readonly point: Point3d; + range(): Range3d; + tryTransformInPlace(transform: Transform): boolean; +} + +// @public +class CurveChain extends CurveCollection { + protected constructor(); + // (undocumented) + protected _curves: CurvePrimitive[]; + // (undocumented) + readonly children: CurvePrimitive[]; + // (undocumented) + cloneStroked(options?: StrokeOptions): AnyCurve; + abstract cyclicCurvePrimitive(index: number): CurvePrimitive | undefined; + // (undocumented) + extendRange(range: Range3d, transform?: Transform): void; + // (undocumented) + getChild(i: number): CurvePrimitive | undefined; + // (undocumented) + getPackedStrokes(options?: StrokeOptions): GrowableXYZArray | undefined; + reverseChildrenInPlace(): void; + // (undocumented) + tryAddChild(child: AnyCurve): boolean; +} + +// @public +class CurveChainWithDistanceIndex extends CurvePrimitive { + chainDistanceToChainFraction(distance: number): number; + // WARNING: The type "PathFragment" needs to be exported by the package (e.g. added to index.ts) + protected chainDistanceToFragment(distance: number, allowExtrapolation?: boolean): PathFragment | undefined; + // (undocumented) + clone(): CurvePrimitive | undefined; + cloneTransformed(transform: Transform): CurvePrimitive | undefined; + closestPoint(spacePoint: Point3d, _extend: boolean): CurveLocationDetail | undefined; + // WARNING: The type "StrokeCountMap" needs to be exported by the package (e.g. added to index.ts) + computeAndAttachRecursiveStrokeCounts(options?: StrokeOptions, parentStrokeMap?: StrokeCountMap): void; + computeStrokeCountForOptions(options?: StrokeOptions): number; + // (undocumented) + static createCapture(path: CurveChain, options?: StrokeOptions): CurveChainWithDistanceIndex; + // WARNING: The type "PathFragment" needs to be exported by the package (e.g. added to index.ts) + protected curveAndChildFractionToFragment(curve: CurvePrimitive, fraction: number): PathFragment | undefined; + // (undocumented) + curveLength(): number; + curveLengthBetweenFractions(fraction0: number, fraction1: number): number; + dispatchToGeometryHandler(handler: GeometryHandler): any; + emitStrokableParts(dest: IStrokeHandler, options?: StrokeOptions): void; + emitStrokes(dest: LineString3d, options?: StrokeOptions): void; + // (undocumented) + endPoint(result?: Point3d): Point3d; + extendRange(rangeToExtend: Range3d, transform?: Transform): void; + fractionToPoint(fraction: number, result?: Point3d): Point3d; + fractionToPointAnd2Derivatives(fraction: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors | undefined; + fractionToPointAndDerivative(fraction: number, result?: Ray3d): Ray3d; + // (undocumented) + fractionToPointAndUnitTangent(fraction: number, result?: Ray3d): Ray3d; + isAlmostEqual(other: GeometryQuery): boolean; + isInPlane(plane: Plane3dByOriginAndUnitNormal): boolean; + // (undocumented) + isSameGeometryClass(other: GeometryQuery): boolean; + moveSignedDistanceFromFraction(startFraction: number, signedDistance: number, allowExtension: boolean, result?: CurveLocationDetail): CurveLocationDetail; + // (undocumented) + quickLength(): number; + reverseInPlace(): void; + startPoint(result?: Point3d): Point3d; + tryTransformInPlace(transform: Transform): boolean; +} + +// @public +class CurveCollection extends GeometryQuery { + abstract announceToCurveProcessor(processor: RecursiveCurveProcessor): void; + checkForNonLinearPrimitives(): boolean; + // (undocumented) + clone(): CurveCollection | undefined; + abstract cloneEmptyPeer(): CurveCollection; + abstract cloneStroked(options?: StrokeOptions): AnyCurve; + // (undocumented) + cloneTransformed(transform: Transform): CurveCollection | undefined; + // (undocumented) + abstract dgnBoundaryType(): number; + extendRange(rangeToExtend: Range3d, transform?: Transform): void; + // (undocumented) + abstract getChild(i: number): AnyCurve | undefined; + readonly isAnyRegionType: boolean; + readonly isClosedPath: boolean; + // (undocumented) + isInner: boolean; + readonly isOpenPath: boolean; + maxGap(): number; + sumLengths(): number; + abstract tryAddChild(child: AnyCurve): boolean; + // (undocumented) + tryTransformInPlace(transform: Transform): boolean; +} + +// @public (undocumented) +class CurveCurve { + static intersectionProjectedXY(worldToLocal: Matrix4d, geometryA: GeometryQuery, extendA: boolean, geometryB: GeometryQuery, extendB: boolean): CurveLocationDetailArrayPair; + static intersectionXY(geometryA: GeometryQuery, extendA: boolean, geometryB: GeometryQuery, extendB: boolean): CurveLocationDetailArrayPair; +} + +// @public +enum CurveIntervalRole { + intervalEnd = 12, + intervalInterior = 11, + intervalStart = 10, + isolated = 0, + isolatedAtVertex = 1 +} + +// @public +class CurveLocationDetail { + constructor(); + a: number; + childDetail?: CurveLocationDetail; + clone(result?: CurveLocationDetail): CurveLocationDetail; + static create(curve: CurvePrimitive, result?: CurveLocationDetail): CurveLocationDetail; + static createConditionalMoveSignedDistance(allowExtension: boolean, curve: CurvePrimitive, startFraction: number, endFraction: number, requestedSignedDistance: number, result?: CurveLocationDetail): CurveLocationDetail; + static createCurveEvaluatedFraction(curve: CurvePrimitive, fraction: number, result?: CurveLocationDetail): CurveLocationDetail; + static createCurveFractionPoint(curve: CurvePrimitive, fraction: number, point: Point3d, result?: CurveLocationDetail): CurveLocationDetail; + static createCurveFractionPointDistance(curve: CurvePrimitive, fraction: number, point: Point3d, a: number, result?: CurveLocationDetail): CurveLocationDetail; + static createCurveFractionPointDistanceCurveSearchStatus(curve: CurvePrimitive, fraction: number, point: Point3d, distance: number, status: CurveSearchStatus, result?: CurveLocationDetail): CurveLocationDetail; + curve?: CurvePrimitive; + curveSearchStatus?: CurveSearchStatus; + fraction: number; + intervalRole?: CurveIntervalRole; + readonly isIsolated: boolean; + point: Point3d; + pointQ: Point3d; + setCurve(curve: CurvePrimitive): void; + setDistanceTo(point: Point3d): void; + setFP(fraction: number, point: Point3d, vector?: Vector3d, a?: number): void; + setFR(fraction: number, ray: Ray3d, a?: number): void; + setIntervalRole(value: CurveIntervalRole): void; + updateIfCloserCurveFractionPointDistance(curve: CurvePrimitive, fraction: number, point: Point3d, a: number): boolean; + vectorInCurveLocationDetail?: Vector3d; +} + +// @public +class CurveLocationDetailArrayPair { + constructor(); + // (undocumented) + dataA: CurveLocationDetail[]; + // (undocumented) + dataB: CurveLocationDetail[]; +} + +// @public +class CurveLocationDetailPair { + constructor(); + clone(result?: CurveLocationDetailPair): CurveLocationDetailPair; + static createDetailRef(detailA: CurveLocationDetail, detailB: CurveLocationDetail, result?: CurveLocationDetailPair): CurveLocationDetailPair; + // (undocumented) + detailA: CurveLocationDetail; + // (undocumented) + detailB: CurveLocationDetail; +} + +// @public +class CurvePrimitive extends GeometryQuery { + protected constructor(); + // WARNING: The type "StrokeCountMap" needs to be exported by the package (e.g. added to index.ts) + addMappedStrokesToLineString3D(map: StrokeCountMap, linestring: LineString3d): number; + announceClipIntervals(_clipper: Clipper, _announce?: AnnounceNumberNumberCurvePrimitive): boolean; + appendPlaneIntersectionPoints(plane: PlaneAltitudeEvaluator, result: CurveLocationDetail[]): number; + clonePartialCurve(_fractionA: number, _fractionB: number): CurvePrimitive | undefined; + closestPoint(spacePoint: Point3d, extend: boolean): CurveLocationDetail | undefined; + // WARNING: The type "StrokeCountMap" needs to be exported by the package (e.g. added to index.ts) + computeAndAttachRecursiveStrokeCounts(options?: StrokeOptions, parentMap?: StrokeCountMap): void; + abstract computeStrokeCountForOptions(options?: StrokeOptions): number; + curveLength(): number; + curveLengthBetweenFractions(fraction0: number, fraction1: number): number; + curveLengthWithFixedIntervalCountQuadrature(fraction0: number, fraction1: number, numInterval: number, numGauss?: number): number; + abstract emitStrokableParts(dest: IStrokeHandler, options?: StrokeOptions): void; + abstract emitStrokes(dest: LineString3d, options?: StrokeOptions): void; + // (undocumented) + endPoint(result?: Point3d): Point3d; + fractionToFrenetFrame(fraction: number, result?: Transform): Transform | undefined; + abstract fractionToPoint(fraction: number, result?: Point3d): Point3d; + abstract fractionToPointAnd2Derivatives(fraction: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors | undefined; + abstract fractionToPointAndDerivative(fraction: number, result?: Ray3d): Ray3d; + // (undocumented) + fractionToPointAndUnitTangent(fraction: number, result?: Ray3d): Ray3d; + getFractionToDistanceScale(): number | undefined; + // WARNING: The type "StrokeCountMap" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "StrokeCountMap" needs to be exported by the package (e.g. added to index.ts) + static installStrokeCountMap(curve: CurvePrimitive, curveMap: StrokeCountMap, parentMap?: StrokeCountMap): void; + readonly isExtensibleFractionSpace: boolean; + abstract isInPlane(plane: Plane3dByOriginAndUnitNormal): boolean; + moveSignedDistanceFromFraction(startFraction: number, signedDistance: number, allowExtension: boolean, result?: CurveLocationDetail): CurveLocationDetail; + protected moveSignedDistanceFromFractionGeneric(startFraction: number, signedDistance: number, allowExtension: boolean, result?: CurveLocationDetail): CurveLocationDetail; + abstract quickLength(): number; + abstract reverseInPlace(): void; + startPoint(result?: Point3d): Point3d; + // WARNING: The type "StrokeCountMap" needs to be exported by the package (e.g. added to index.ts) + strokeData?: StrokeCountMap; +} + +// @public +enum CurveSearchStatus { + error = 0, + stoppedAtBoundary = 2, + success = 1 +} + +// @public +class DeepCompare { + constructor(numberRelTol?: number); + // (undocumented) + compare(a: any, b: any, tolerance?: number): boolean; + // (undocumented) + compareNumber(_a: number, _b: number): boolean; + // (undocumented) + errorTracker: any[]; + // (undocumented) + numberRelTol: number; + // (undocumented) + propertyCounts: { + [key: string]: any; + } + // (undocumented) + typeCounts: { + arrays: number; + booleans: number; + functions: number; + numbers: number; + objects: number; + strings: number; + undefined: number; + } +} + +// @public (undocumented) +class Degree2PowerPolynomial { + constructor(c0?: number, c1?: number, c2?: number); + // (undocumented) + addConstant(a: number): void; + // (undocumented) + addSquaredLinearTerm(a: number, b: number, s?: number): void; + // (undocumented) + coffs: number[]; + evaluate(x: number): number; + evaluateDerivative(x: number): number; + // (undocumented) + static fromRootsAndC2(root0: number, root1: number, c2?: number): Degree2PowerPolynomial; + // (undocumented) + realRoots(): number[] | undefined; + static solveQuadratic(a: number, b: number, c: number): number[] | undefined; + // (undocumented) + tryGetVertexFactorization(): { + x0: number; + y0: number; + c: number; + } | undefined; +} + +// @public (undocumented) +class Degree3PowerPolynomial { + constructor(c0?: number, c1?: number, c2?: number, c3?: number); + // (undocumented) + addConstant(a: number): void; + // (undocumented) + addSquaredLinearTerm(a: number, b: number, s?: number): void; + // (undocumented) + coffs: number[]; + evaluate(x: number): number; + evaluateDerivative(x: number): number; + // (undocumented) + static fromRootsAndC3(root0: number, root1: number, root2: number, c3?: number): Degree3PowerPolynomial; +} + +// @public (undocumented) +class Degree4PowerPolynomial { + constructor(c0?: number, c1?: number, c2?: number, c3?: number, c4?: number); + // (undocumented) + addConstant(a: number): void; + // (undocumented) + coffs: number[]; + evaluate(x: number): number; + evaluateDerivative(x: number): number; + // (undocumented) + static fromRootsAndC4(root0: number, root1: number, root2: number, root3: number, c4?: number): Degree4PowerPolynomial; +} + +// @public +class FacetFaceData { + clone(result?: FacetFaceData): FacetFaceData; + convertParamToDistance(param: Point2d, result?: Point2d): Point2d; + convertParamToNormalized(param: Point2d, result?: Point2d): Point2d; + static createNull(): FacetFaceData; + // (undocumented) + readonly paramDistanceRange: Range2d; + // (undocumented) + readonly paramRange: Range2d; + scaleDistances(distanceScale: number): void; + setNull(): void; + setParamDistanceRangeFromNewFaceData(polyface: IndexedPolyface, facetStart: number, facetEnd: number): boolean; +} + +// @public +class FrameBuilder { + constructor(); + announce(data: any): void; + announcePoint(point: Point3d): number; + // (undocumented) + announceVector(vector: Vector3d): number; + // (undocumented) + applyDefaultUpVector(vector?: Vector3d): void; + // (undocumented) + clear(): void; + static createFrameToDistantPoints(points: Point3d[]): Transform | undefined; + static createLocalToWorldTransformInRange(range: Range3d, scaleSelect?: AxisScaleSelect, fractionX?: number, fractionY?: number, fractionZ?: number, defaultAxisLength?: number): Transform; + static createRightHandedFrame(defaultUpVector: Vector3d | undefined, ...params: any[]): Transform | undefined; + static createRightHandedLocalToWorld(...params: any[]): Transform | undefined; + getValidatedFrame(allowLeftHanded?: boolean): Transform | undefined; + // (undocumented) + readonly hasOrigin: boolean; + savedVectorCount(): number; +} + +// WARNING: smallMetricDistance has incomplete type information +// WARNING: smallMetricDistanceSquared has incomplete type information +// WARNING: smallAngleRadians has incomplete type information +// WARNING: smallAngleRadiansSquared has incomplete type information +// WARNING: largeFractionResult has incomplete type information +// @public (undocumented) +class Geometry { + static axisIndexToRightHandedAxisOrder(axisIndex: AxisIndex): AxisOrder; + static axisOrderToAxis(order: AxisOrder, index: number): number; + static clamp(value: number, min: number, max: number): number; + static clampToStartEnd(x: number, a: number, b: number): number; + static conditionalDivideFraction(numerator: number, denominator: number): number | undefined; + static correctSmallMetricDistance(distance: number, replacement?: number): number; + static crossProductMagnitude(ux: number, uy: number, uz: number, vx: number, vy: number, vz: number): number; + static crossProductXYXY(ux: number, uy: number, vx: number, vy: number): number; + static crossProductXYZXYZ(ux: number, uy: number, uz: number, vx: number, vy: number, vz: number, result?: Vector3d): Vector3d; + // (undocumented) + static curvatureMagnitude(ux: number, uy: number, uz: number, vx: number, vy: number, vz: number): number; + // (undocumented) + static cyclic3dAxis(axis: number): number; + static defined01(value: any): number; + static distanceXYXY(x0: number, y0: number, x1: number, y1: number): number; + static distanceXYZXYZ(x0: number, y0: number, z0: number, x1: number, y1: number, z1: number): number; + static dotProductXYZXYZ(ux: number, uy: number, uz: number, vx: number, vy: number, vz: number): number; + // (undocumented) + static readonly fullCircleRadiansMinusSmallAngle: number; + // (undocumented) + static hypotenuseSquaredXY(x: number, y: number): number; + // (undocumented) + static hypotenuseSquaredXYZ(x: number, y: number, z: number): number; + // (undocumented) + static hypotenuseSquaredXYZW(x: number, y: number, z: number, w: number): number; + // (undocumented) + static hypotenuseXY(x: number, y: number): number; + // (undocumented) + static hypotenuseXYZ(x: number, y: number, z: number): number; + // (undocumented) + static hypotenuseXYZW(x: number, y: number, z: number, w: number): number; + static interpolate(a: number, f: number, b: number): number; + static inverseInterpolate(x0: number, f0: number, x1: number, f1: number, targetF?: number, defaultResult?: number): number | undefined; + static inverseInterpolate01(f0: number, f1: number, targetF?: number): number | undefined; + // (undocumented) + static inverseMetricDistance(a: number): number | undefined; + // (undocumented) + static inverseMetricDistanceSquared(a: number): number | undefined; + // (undocumented) + static isAlmostEqualNumber(a: number, b: number): boolean; + static isArrayOfNumberArray(json: any, numNumberArray: number, minEntries?: number): boolean; + // (undocumented) + static isDistanceWithinTol(distance: number, tol: number): boolean; + static isIn01(x: number, apply01?: boolean): boolean; + static isIn01WithTolerance(x: number, tolerance: number): boolean; + static isNumberArray(json: any, minEntries?: number): boolean; + // (undocumented) + static isSameCoordinate(x: number, y: number, tol?: number): boolean; + // (undocumented) + static isSameCoordinateSquared(x: number, y: number): boolean; + // (undocumented) + static isSamePoint2d(dataA: Point2d, dataB: Point2d): boolean; + // (undocumented) + static isSamePoint3d(dataA: Point3d, dataB: Point3d): boolean; + // (undocumented) + static isSamePoint3dXY(dataA: Point3d, dataB: Point3d): boolean; + // (undocumented) + static isSameVector2d(dataA: Vector2d, dataB: Vector2d): boolean; + // (undocumented) + static isSameVector3d(dataA: Vector3d, dataB: Vector3d): boolean; + // (undocumented) + static isSameXYZ(dataA: XYZ, dataB: XYZ): boolean; + // (undocumented) + static isSmallAngleRadians(value: number): boolean; + // (undocumented) + static isSmallMetricDistance(distance: number): boolean; + // (undocumented) + static isSmallMetricDistanceSquared(distanceSquared: number): boolean; + // (undocumented) + static isSmallRelative(value: number): boolean; + static lexicalXYLessThan(a: XY | XYZ, b: XY | XYZ): 1 | 0 | -1; + // (undocumented) + static lexicalXYZLessThan(a: XYZ, b: XYZ): 1 | 0 | -1; + static lexicalYXLessThan(a: XY | XYZ, b: XY | XYZ): 1 | 0 | -1; + // (undocumented) + static maxAbsDiff(a: number, b0: number, b1: number): number; + // (undocumented) + static maxAbsXY(x: number, y: number): number; + // (undocumented) + static maxAbsXYZ(x: number, y: number, z: number): number; + // (undocumented) + static maxXY(a: number, b: number): number; + // (undocumented) + static maxXYZ(a: number, b: number, c: number): number; + static modulo(a: number, period: number): number; + // (undocumented) + static resolveNumber(value: number | undefined, defaultValue?: number): number; + static restrictToInterval(x: number, a: number, b: number): number; + static safeDivideFraction(numerator: number, denominator: number, defaultResult: number): number; + static solveTrigForm(constCoff: number, cosCoff: number, sinCoff: number): Vector2d[] | undefined; + // (undocumented) + static square(x: number): number; + static stepCount(stepSize: number, total: number, minCount?: number, maxCount?: number): number; + // (undocumented) + static tripleProduct(ux: number, uy: number, uz: number, vx: number, vy: number, vz: number, wx: number, wy: number, wz: number): number; + static tripleProductPoint4dXYW(columnA: Point4d, columnB: Point4d, columnC: Point4d): number; + static tripleProductXYW(columnA: XAndY, weightA: number, columnB: XAndY, weightB: number, columnC: XAndY, weightC: number): number; +} + +// @public (undocumented) +class GeometryHandler { + // (undocumented) + abstract handleArc3d(g: Arc3d): any; + // (undocumented) + handleBagOfCurves(g: BagOfCurves): any; + // (undocumented) + abstract handleBezierCurve3d(g: BezierCurve3d): any; + // (undocumented) + abstract handleBezierCurve3dH(g: BezierCurve3dH): any; + // (undocumented) + abstract handleBox(g: Box): any; + // (undocumented) + abstract handleBSplineCurve3d(g: BSplineCurve3d): any; + // (undocumented) + abstract handleBSplineCurve3dH(g: BSplineCurve3dH): any; + // (undocumented) + abstract handleBSplineSurface3d(g: BSplineSurface3d): any; + // (undocumented) + abstract handleBSplineSurface3dH(g: BSplineSurface3dH): any; + // (undocumented) + abstract handleCone(g: Cone): any; + // (undocumented) + abstract handleCoordinateXYZ(g: CoordinateXYZ): any; + // (undocumented) + handleCurveCollection(_g: CurveCollection): any; + // (undocumented) + abstract handleIndexedPolyface(g: IndexedPolyface): any; + // (undocumented) + abstract handleLinearSweep(g: LinearSweep): any; + // (undocumented) + abstract handleLineSegment3d(g: LineSegment3d): any; + // (undocumented) + abstract handleLineString3d(g: LineString3d): any; + // (undocumented) + handleLoop(g: Loop): any; + // (undocumented) + handleParityRegion(g: ParityRegion): any; + // (undocumented) + handlePath(g: Path): any; + // (undocumented) + abstract handlePointString3d(g: PointString3d): any; + // (undocumented) + abstract handleRotationalSweep(g: RotationalSweep): any; + // (undocumented) + abstract handleRuledSweep(g: RuledSweep): any; + // (undocumented) + abstract handleSphere(g: Sphere): any; + // (undocumented) + abstract handleTorusPipe(g: TorusPipe): any; + // (undocumented) + abstract handleTransitionSpiral(g: TransitionSpiral3d): any; + // (undocumented) + handleUnionRegion(g: UnionRegion): any; +} + +// @public +class GeometryQuery { + readonly children: GeometryQuery[] | undefined; + abstract clone(): GeometryQuery | undefined; + abstract cloneTransformed(transform: Transform): GeometryQuery | undefined; + // (undocumented) + abstract dispatchToGeometryHandler(handler: GeometryHandler): any; + abstract extendRange(rangeToExtend: Range3d, transform?: Transform): void; + isAlmostEqual(other: GeometryQuery): boolean; + abstract isSameGeometryClass(other: GeometryQuery): boolean; + range(transform?: Transform, result?: Range3d): Range3d; + abstract tryTransformInPlace(transform: Transform): boolean; + tryTranslateInPlace(dx: number, dy?: number, dz?: number): boolean; +} + +// @public +class GrowableBlockedArray { + constructor(blockSize: number, initialBlocks?: number); + // (undocumented) + protected _blockSize: number; + // (undocumented) + protected _data: Float64Array; + // (undocumented) + protected _inUse: number; + addBlock(newData: number[]): void; + blockCapacity(): number; + protected blockIndexToDoubleIndex(blockIndex: number): number; + checkedComponent(blockIndex: number, componentIndex: number): number | undefined; + clear(): void; + static compareLexicalBlock(data: Float64Array, blockSize: number, ia: number, ib: number): number; + component(blockIndex: number, componentIndex: number): number; + // (undocumented) + distanceBetweenBlocks(blockIndexA: number, blockIndexB: number): number; + // (undocumented) + distanceBetweenSubBlocks(blockIndexA: number, blockIndexB: number, iBegin: number, iEnd: number): number; + ensureBlockCapacity(blockCapacity: number): void; + getWithinBlock(blockIndex: number, indexWithinBlock: number): number; + protected newBlockIndex(): number; + readonly numBlocks: number; + readonly numPerBlock: number; + popBlock(): void; + sortIndicesLexical(compareBlocks?: BlockComparisonFunction): Uint32Array; +} + +// @public +class GrowableFloat64Array { + constructor(initialCapacity?: number); + // (undocumented) + at(index: number): number; + // (undocumented) + back(): number; + // (undocumented) + capacity(): number; + clear(): void; + clone(maintainExcessCapacity?: boolean): GrowableFloat64Array; + // (undocumented) + static compare(a: any, b: any): number; + compressAdjcentDuplicates(tolerance?: number): void; + static create(contents: Float64Array | number[]): GrowableFloat64Array; + ensureCapacity(newCapacity: number): void; + // (undocumented) + front(): number; + // (undocumented) + readonly length: number; + move(i: number, j: number): void; + pop(): void; + push(toPush: number): void; + pushBlockCopy(copyFromIndex: number, numToCopy: number): void; + // (undocumented) + reassign(index: number, value: number): void; + resize(newLength: number, padValue?: number): void; + restrictToInterval(a: number, b: number): void; + setAt(index: number, value: number): void; + sort(compareMethod?: (a: any, b: any) => number): void; + swap(i: number, j: number): void; +} + +// @public +class GrowableXYArray extends IndexedXYCollection { + constructor(numPoints?: number); + areaXY(): number; + atPoint2dIndex(pointIndex: number, result?: Point2d): Point2d | undefined; + atVector2dIndex(vectorIndex: number, result?: Vector2d): Vector2d | undefined; + // (undocumented) + back(result?: Point2d): Point2d | undefined; + clear(): void; + clone(): GrowableXYArray; + compareLexicalBlock(ia: number, ib: number): number; + component(pointIndex: number, componentIndex: number): number; + // (undocumented) + static create(data: XAndY[] | GrowableXYZArray): GrowableXYArray; + static createFromGrowableXYZArray(source: GrowableXYZArray, transform?: Transform, dest?: GrowableXYArray): GrowableXYArray; + crossProductIndexIndexIndex(originIndex: number, targetAIndex: number, targetBIndex: number): number | undefined; + crossProductXAndYIndexIndex(origin: XAndY, targetAIndex: number, targetBIndex: number): number | undefined; + distance(i: number, j: number): number | undefined; + distanceIndexToPoint(i: number, spacePoint: Point2d): number | undefined; + ensureCapacity(pointCapacity: number): void; + // (undocumented) + extendRange(rangeToExtend: Range2d, transform?: Transform): void; + float64Data(): Float64Array; + // (undocumented) + readonly float64Length: number; + // (undocumented) + front(result?: Point2d): Point2d | undefined; + getPoint2dArray(): Point2d[]; + getPoint2dAt(pointIndex: number, result?: Point2d): Point2d; + // (undocumented) + getPoint3dArray(z?: number): Point3d[]; + interpolate(i: number, fraction: number, j: number, result?: Point2d): Point2d | undefined; + // (undocumented) + static isAlmostEqual(dataA: GrowableXYArray | undefined, dataB: GrowableXYArray | undefined): boolean; + isIndexValid(index: number): boolean; + // (undocumented) + readonly length: number; + multiplyMatrix3dInPlace(matrix: Matrix3d): void; + multiplyTransformInPlace(transform: Transform): void; + pop(): void; + push(toPush: XAndY): void; + pushAll(points: XAndY[]): void; + pushAllXYAndZ(points: XYAndZ[] | GrowableXYZArray): void; + pushFromGrowableXYArray(source: GrowableXYArray, sourceIndex: number): boolean; + pushWrap(numWrap: number): void; + // (undocumented) + pushXY(x: number, y: number): void; + resize(pointCount: number): void; + scaleInPlace(factor: number): void; + setAt(pointIndex: number, value: XAndY): boolean; + setCoordinates(pointIndex: number, x: number, y: number): boolean; + sortIndicesLexical(): Uint32Array; + // (undocumented) + sumLengths(): number; + transferFromGrowableXYArray(destIndex: number, source: GrowableXYArray, sourceIndex: number): boolean; + tryTransformInverseInPlace(transform: Transform): boolean; + vectorIndexIndex(i: number, j: number, result?: Vector2d): Vector2d | undefined; + vectorXAndYIndex(origin: XAndY, j: number, result?: Vector2d): Vector2d | undefined; +} + +// @public +class GrowableXYZArray extends IndexedXYZCollection { + constructor(numPoints?: number); + accumulateCrossProductIndexIndexIndex(originIndex: number, targetAIndex: number, targetBIndex: number, result: Vector3d): void; + addSteppedPoints(other: GrowableXYZArray, pointIndex0: number, step: number, numAdd: number): void; + areaXY(): number; + atPoint2dIndex(pointIndex: number, result?: Point2d): Point2d | undefined; + atPoint3dIndex(pointIndex: number, result?: Point3d): Point3d | undefined; + atVector3dIndex(vectorIndex: number, result?: Vector3d): Vector3d | undefined; + // (undocumented) + back(result?: Point3d): Point3d | undefined; + clear(): void; + clone(result?: GrowableXYZArray): GrowableXYZArray; + compareLexicalBlock(ia: number, ib: number): number; + component(pointIndex: number, componentIndex: number): number; + // (undocumented) + static create(data: XYAndZ[]): GrowableXYZArray; + crossProductIndexIndexIndex(originIndex: number, targetAIndex: number, targetBIndex: number, result?: Vector3d): Vector3d | undefined; + crossProductXYAndZIndexIndex(origin: XYAndZ, targetAIndex: number, targetBIndex: number, result?: Vector3d): Vector3d | undefined; + distance(i: number, j: number): number | undefined; + static distanceBetweenPointsIn2Arrays(arrayA: GrowableXYZArray, i: number, arrayB: GrowableXYZArray, j: number): number | undefined; + distanceIndexToPoint(i: number, spacePoint: XYAndZ): number | undefined; + static distanceRangeBetweenCorrespondingPoints(arrayA: GrowableXYZArray, arrayB: GrowableXYZArray): Range1d; + ensureCapacity(pointCapacity: number): void; + // (undocumented) + extendRange(rangeToExtend: Range3d, transform?: Transform): void; + float64Data(): Float64Array; + // (undocumented) + readonly float64Length: number; + // (undocumented) + front(result?: Point3d): Point3d | undefined; + getPoint2dAt(pointIndex: number, result?: Point2d): Point2d; + // (undocumented) + getPoint3dArray(): Point3d[]; + getPoint3dAt(pointIndex: number, result?: Point3d): Point3d; + interpolate(i: number, fraction: number, j: number, result?: Point3d): Point3d | undefined; + // (undocumented) + static isAlmostEqual(dataA: GrowableXYZArray | undefined, dataB: GrowableXYZArray | undefined): boolean; + // (undocumented) + isCloseToPlane(plane: Plane3dByOriginAndUnitNormal, tolerance?: number): boolean; + isIndexValid(index: number): boolean; + // (undocumented) + readonly length: number; + multiplyAndRenormalizeMatrix3dInverseTransposeInPlace(matrix: Matrix3d): void; + multiplyMatrix3dInPlace(matrix: Matrix3d): void; + multiplyTransformInPlace(transform: Transform): void; + pop(): void; + push(toPush: XYAndZ): void; + pushAll(points: Point3d[]): void; + pushFromGrowableXYZArray(source: GrowableXYZArray, sourceIndex: number): boolean; + pushWrap(numWrap: number): void; + // (undocumented) + pushXYZ(x: number, y: number, z: number): void; + resize(pointCount: number): void; + scaleInPlace(factor: number): void; + setAt(pointIndex: number, value: XYAndZ): boolean; + setCoordinates(pointIndex: number, x: number, y: number, z: number): boolean; + sortIndicesLexical(): Uint32Array; + // (undocumented) + sumLengths(): number; + transferFromGrowableXYZArray(destIndex: number, source: GrowableXYZArray, sourceIndex: number): boolean; + tryTransformInverseInPlace(transform: Transform): boolean; + vectorIndexIndex(i: number, j: number, result?: Vector3d): Vector3d | undefined; + vectorXYAndZIndex(origin: XYAndZ, j: number, result?: Vector3d): Vector3d | undefined; +} + +// @public +class HalfEdge { + constructor(x?: number, y?: number, z?: number, i?: number); + clearMask(mask: HalfEdgeMask): void; + clearMaskAroundFace(mask: HalfEdgeMask): void; + clearMaskAroundVertex(mask: HalfEdgeMask): void; + collectAroundFace(f?: NodeFunction): any[]; + collectAroundVertex(f?: NodeFunction): any[]; + // (undocumented) + countEdgesAroundFace(): number; + // (undocumented) + countEdgesAroundVertex(): number; + // (undocumented) + countMaskAroundFace(mask: HalfEdgeMask, value?: boolean): number; + // (undocumented) + countMaskAroundVertex(mask: HalfEdgeMask, value?: boolean): number; + // (undocumented) + static createEdgeXYXY(id0: any, x0: number, y0: number, id1: any, x1: number, y1: number): HalfEdge; + static createHalfEdgePair(heArray: HalfEdge[] | undefined): HalfEdge; + static createHalfEdgePairWithCoordinates(xA: number | undefined, yA: number | undefined, zA: number | undefined, iA: number | undefined, xB: number | undefined, yB: number | undefined, zB: number | undefined, iB: number | undefined, heArray: HalfEdge[] | undefined): HalfEdge; + decomission(): void; + distanceXY(other: HalfEdge): number; + readonly edgeMate: HalfEdge; + readonly facePredecessor: HalfEdge; + readonly faceSuccessor: HalfEdge; + static filterIsMaskOff(node: HalfEdge, mask: HalfEdgeMask): boolean; + static filterIsMaskOn(node: HalfEdge, mask: HalfEdgeMask): boolean; + getMask(mask: HalfEdgeMask): number; + // (undocumented) + i: number; + // (undocumented) + readonly id: any; + isEqualXY(other: HalfEdge): boolean; + isMaskSet(mask: HalfEdgeMask): boolean; + // (undocumented) + maskBits: number; + // (undocumented) + nextZ: HalfEdge; + // (undocumented) + static nodeToId(node: HalfEdge): any; + // (undocumented) + static nodeToIdMaskXY: { + id: any; + mask: any; + xy: number[]; + } + // (undocumented) + static nodeToIdString(node: HalfEdge): any; + // (undocumented) + static nodeToIdXYString(node: HalfEdge): string; + // (undocumented) + static nodeToMaskString(node: HalfEdge): string; + // (undocumented) + static nodeToSelf(node: HalfEdge): any; + // (undocumented) + static nodeToXY(node: HalfEdge): number[]; + static pinch(nodeA: HalfEdge, nodeB: HalfEdge): void; + // (undocumented) + prevZ: HalfEdge; + setMask(mask: HalfEdgeMask): void; + // (undocumented) + setMaskAroundFace(mask: HalfEdgeMask): void; + // (undocumented) + setMaskAroundVertex(mask: HalfEdgeMask): void; + signedFaceArea(): number; + static splitEdge(base: undefined | HalfEdge, xA: number | undefined, yA: number | undefined, zA: number | undefined, iA: number | undefined, heArray: HalfEdge[] | undefined): HalfEdge; + // (undocumented) + steiner: boolean; + sumAroundFace(f: NodeToNumberFunction): number; + sumAroundVertex(f: NodeToNumberFunction): number; + // (undocumented) + testAndSetMask(mask: HalfEdgeMask): number; + // (undocumented) + static testNodeMaskNotExterior(node: HalfEdge): boolean; + // (undocumented) + vectorToFaceSuccessor(result?: Vector3d): Vector3d; + // (undocumented) + vectorToFaceSuccessorXY(result?: Vector2d): Vector2d; + // (undocumented) + readonly vertexPredecessor: HalfEdge; + // (undocumented) + readonly vertexSuccessor: HalfEdge; + // (undocumented) + x: number; + // (undocumented) + y: number; + // (undocumented) + z: number; + // (undocumented) + zOrder: number; +} + +// @public +class HalfEdgeGraph { + constructor(); + addEdgeXY(x0: number, y0: number, x1: number, y1: number): HalfEdge; + // (undocumented) + allHalfEdges: HalfEdge[]; + announceFaceLoops(announceFace: GraphNodeFunction): void; + announceVertexLoops(announceVertex: GraphNodeFunction): void; + clearMask(mask: HalfEdgeMask): void; + // (undocumented) + collectFaceLoops(): HalfEdge[]; + collectSegments(): LineSegment3d[]; + // (undocumented) + collectVertexLoops(): HalfEdge[]; + // (undocumented) + countFaceLoops(): number; + // (undocumented) + countFaceLoopsWithMaskFilter(filter: HalfEdgeAndMaskToBooleanFunction, mask: HalfEdgeMask): number; + // (undocumented) + countMask(mask: HalfEdgeMask): number; + // (undocumented) + countNodes(): number; + countVertexLoops(): number; + createEdgeXYZXYZ(xA?: number, yA?: number, zA?: number, iA?: number, xB?: number, yB?: number, zB?: number, iB?: number): HalfEdge; + decommission(): void; + reverseMask(mask: HalfEdgeMask): void; + setMask(mask: HalfEdgeMask): void; + splitEdge(base: undefined | HalfEdge, xA?: number, yA?: number, zA?: number, iA?: number): HalfEdge; +} + +// @public (undocumented) +enum HalfEdgeMask { + // (undocumented) + ALL_MASK = 4294967295, + // (undocumented) + BOUNDARY = 2, + // (undocumented) + BOUNDARY_VERTEX_MASK = 64, + // (undocumented) + CONSTU_MASK = 4, + // (undocumented) + CONSTV_MASK = 8, + // (undocumented) + DIRECTED_EDGE_MASK = 256, + // (undocumented) + EXTERIOR = 1, + // (undocumented) + HULL_MASK = 1024, + // (undocumented) + NULL_MASK = 0, + // (undocumented) + POLAR_LOOP_MASK = 4096, + // (undocumented) + PRIMARY_EDGE = 512, + // (undocumented) + PRIMARY_VERTEX_MASK = 128, + // (undocumented) + SECTION_EDGE_MASK = 2048, + // (undocumented) + TRIANGULATED_NODE_MASK = 16384, + // (undocumented) + USEAM_MASK = 16, + // (undocumented) + VISITED = 8192, + // (undocumented) + VSEAM_MASK = 32 +} + +// @public (undocumented) +module IModelJson { + interface ArcByVectorProps { + // (undocumented) + center: XYZProps; + // (undocumented) + sweepStartEnd: AngleSweepProps; + // (undocumented) + vectorX: XYZProps; + // (undocumented) + vectorY: XYZProps; + } + + interface AxesProps { + xyVectors?: [XYZProps, XYZProps]; + yawPitchRollAngles?: YawPitchRollProps; + zxVectors?: [XYZProps, XYZProps]; + } + + interface BcurveProps { + // (undocumented) + knot: [number]; + order: number; + point: [XYZProps]; + } + + interface BoxProps extends AxesProps { + baseX: number; + baseY: number; + capped?: boolean; + height?: number; + origin: XYZProps; + topOrigin?: XYZProps; + topX?: number; + topY?: number; + } + + interface BSplineSurfaceProps { + // (undocumented) + orderU: number; + // (undocumented) + orderV: number; + // (undocumented) + points: [[[number]]]; + // (undocumented) + uKnots: [number]; + // (undocumented) + vKnots: [number]; + } + + interface ConeProps extends AxesProps { + capped?: boolean; + // (undocumented) + end: XYZProps; + endRadius?: number; + // (undocumented) + start: XYZProps; + startRadius: number; + vectorX?: XYZProps; + // (undocumented) + vectorY?: XYZProps; + } + + interface CurveCollectionProps extends PlanarRegionProps { + bagofCurves?: [CurveCollectionProps]; + path?: [CurvePrimitiveProps]; + } + + // (undocumented) + interface CurvePrimitiveProps { + // (undocumented) + arc?: ArcByVectorProps | [XYZProps, XYZProps, XYZProps]; + // (undocumented) + bcurve?: BcurveProps; + // (undocumented) + lineSegment?: [XYZProps, XYZProps]; + // (undocumented) + lineString?: XYZProps[]; + // (undocumented) + transitionSpiral?: TransitionSpiralProps; + } + + interface CylinderProps { + capped?: boolean; + end: XYZProps; + // (undocumented) + radius: number; + start: XYZProps; + } + + // (undocumented) + interface GeometryProps extends CurvePrimitiveProps, SolidPrimitiveProps, CurveCollectionProps { + // (undocumented) + bsurf?: BSplineSurfaceProps; + // (undocumented) + indexedMesh?: IndexedMeshProps; + // (undocumented) + point?: XYZProps; + } + + interface IndexedMeshProps { + color?: [number]; + colorIndex?: [number]; + normal?: [XYZProps]; + normalIndex?: [number]; + param?: [XYProps]; + paramIndex?: [number]; + point: [XYZProps]; + pointIndex: [number]; + } + + interface LinearSweepProps { + capped?: boolean; + contour: CurveCollectionProps; + vector: XYZProps; + } + + interface PlanarRegionProps { + loop?: [CurvePrimitiveProps]; + parityRegion?: [{ + loop: [CurvePrimitiveProps]; + }]; + // (undocumented) + unionRegion?: [PlanarRegionProps]; + } + + // (undocumented) + interface PointProps { + // (undocumented) + point?: XYZProps; + } + + class Reader { + constructor(); + // (undocumented) + static parse(json?: any): any; + // (undocumented) + static parseArray(data?: any): any[] | undefined; + // (undocumented) + static parseBcurve(data?: any): BSplineCurve3d | BSplineCurve3dH | undefined; + // (undocumented) + static parseBox(json?: any): any; + // (undocumented) + static parseBsurf(data?: any): BSplineSurface3d | BSplineSurface3dH | undefined; + // WARNING: The type "ConeProps" needs to be exported by the package (e.g. added to index.ts) + static parseConeProps(json?: ConeProps): any; + // (undocumented) + static parseCoordinate(data?: any): CoordinateXYZ | undefined; + // (undocumented) + static parseCurveCollectionMembers(result: CurveCollection, data?: any): CurveCollection | undefined; + // WARNING: The type "CylinderProps" needs to be exported by the package (e.g. added to index.ts) + static parseCylinderProps(json?: CylinderProps): any; + // (undocumented) + static parseIndexedMesh(data?: any): any | undefined; + // (undocumented) + static parseLinearSweep(json?: any): any; + // (undocumented) + static parsePointArray(json?: any): Point3d[]; + // (undocumented) + static parsePolyfaceAuxData(data?: any): PolyfaceAuxData | undefined; + // (undocumented) + static parseRotationalSweep(json?: any): any; + // (undocumented) + static parseRuledSweep(json?: any): any; + // WARNING: The type "SphereProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + static parseSphere(json?: SphereProps): any; + // (undocumented) + static parseTorusPipe(json?: any): any; + // (undocumented) + static parseTransitionSpiral(data?: any): TransitionSpiral3d | undefined; + } + + interface RotationalSweepProps { + axis: XYZProps; + capped?: boolean; + center: XYZProps; + contour: CurveCollectionProps; + sweepAngle: AngleProps; + } + + interface RuledSweepProps { + capped?: boolean; + // (undocumented) + countour: [CurveCollectionProps]; + } + + interface SolidPrimitiveProps { + // (undocumented) + box?: BoxProps; + // (undocumented) + cone?: ConeProps; + // (undocumented) + cylinder?: CylinderProps; + // (undocumented) + linearSweep?: LinearSweepProps; + // (undocumented) + rotationalSweep?: RotationalSweepProps; + // (undocumented) + ruledSweep?: RuledSweepProps; + // (undocumented) + sphere?: SphereProps; + // (undocumented) + torusPipe?: TorusPipeProps; + } + + interface SphereProps extends AxesProps { + capped?: boolean; + center: XYZProps; + latitudeStartEnd?: AngleSweepProps; + radius?: number; + radiusX?: number; + radiusY?: number; + radiusZ?: number; + } + + interface TorusPipeProps extends AxesProps { + capped?: boolean; + center: XYZProps; + majorRadius: number; + minorRadius?: number; + sweepAngle?: AngleProps; + } + + interface TransitionSpiralProps extends AxesProps { + // (undocumented) + curveLength?: number; + // (undocumented) + endBearing?: AngleProps; + // (undocumented) + endRadius?: number; + // (undocumented) + fractionInterval?: number[]; + intervalFractions?: [number, number]; + origin: XYZProps; + startBearing?: AngleProps; + // (undocumented) + startRadius?: number; + type?: string; + } + + // (undocumented) + class Writer extends GeometryHandler { + // (undocumented) + emit(data: any): any; + // (undocumented) + emitArray(data: object[]): any; + // (undocumented) + handleArc3d(data: Arc3d): any; + // (undocumented) + handleBagOfCurves(data: BagOfCurves): any; + // (undocumented) + handleBezierCurve3d(curve: BezierCurve3d): any; + // (undocumented) + handleBezierCurve3dH(curve: BezierCurve3dH): any; + // (undocumented) + handleBox(box: Box): any; + // (undocumented) + handleBSplineCurve3d(curve: BSplineCurve3d): any; + // (undocumented) + handleBSplineCurve3dH(curve: BSplineCurve3dH): any; + // (undocumented) + handleBSplineSurface3d(surface: BSplineSurface3d): any; + // (undocumented) + handleBSplineSurface3dH(surface: BSplineSurface3dH): any; + // (undocumented) + handleCone(data: Cone): any; + // (undocumented) + handleCoordinateXYZ(data: CoordinateXYZ): any; + // (undocumented) + handleIndexedPolyface(pf: IndexedPolyface): any; + // (undocumented) + handleLinearSweep(data: LinearSweep): any; + // (undocumented) + handleLineSegment3d(data: LineSegment3d): any; + // (undocumented) + handleLineString3d(data: LineString3d): any; + // (undocumented) + handleLoop(data: Loop): any; + // (undocumented) + handleParityRegion(data: ParityRegion): any; + // (undocumented) + handlePath(data: Path): any; + // (undocumented) + handlePointString3d(data: PointString3d): any; + // (undocumented) + handleRotationalSweep(data: RotationalSweep): any; + // (undocumented) + handleRuledSweep(data: RuledSweep): any; + // (undocumented) + handleSphere(data: Sphere): any; + // (undocumented) + handleTorusPipe(data: TorusPipe): any; + // (undocumented) + handleTransitionSpiral(data: TransitionSpiral3d): any; + // (undocumented) + handleUnionRegion(data: UnionRegion): any; + static toIModelJson(data: any): any; + } + +} + +// @public (undocumented) +class IndexedPolyface extends Polyface { + protected constructor(data: PolyfaceData, facetStart?: number[], facetToFaceData?: number[]); + // (undocumented) + protected _facetStart: number[]; + // (undocumented) + protected _facetToFaceData: number[]; + // (undocumented) + addColor(color: number): number; + // (undocumented) + addColorIndex(index: number): void; + addIndexedPolyface(source: IndexedPolyface, reversed: boolean, transform: Transform | undefined): void; + // (undocumented) + addNormal(normal: Vector3d, priorIndexA?: number, priorIndexB?: number): number; + // (undocumented) + addNormalIndex(index: number): void; + // (undocumented) + addNormalXYZ(x: number, y: number, z: number): number; + // (undocumented) + addParam(param: Point2d): number; + // (undocumented) + addParamIndex(index: number): void; + // (undocumented) + addParamUV(u: number, v: number, priorIndexA?: number, priorIndexB?: number): number; + // (undocumented) + addParamXY(x: number, y: number): number; + addPoint(point: Point3d, priorIndex?: number): number; + // (undocumented) + addPointIndex(index: number, visible?: boolean): void; + addPointXYZ(x: number, y: number, z: number): number; + cleanupOpenFacet(): void; + // (undocumented) + clone(): IndexedPolyface; + // (undocumented) + cloneTransformed(transform: Transform): IndexedPolyface; + readonly colorCount: number; + // (undocumented) + static create(needNormals?: boolean, needParams?: boolean, needColors?: boolean): IndexedPolyface; + createVisitor(numWrap?: number): PolyfaceVisitor; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + // (undocumented) + extendRange(range: Range3d, transform?: Transform): void; + readonly faceCount: number; + readonly facetCount: number; + facetIndex0(index: number): number; + facetIndex1(index: number): number; + getFaceDataByFacetIndex(facetIndex: number): FacetFaceData; + isAlmostEqual(other: any): boolean; + // (undocumented) + isSameGeometryClass(other: any): boolean; + // (undocumented) + isValidFacetIndex(index: number): boolean; + readonly normalCount: number; + // (undocumented) + numEdgeInFacet(facetIndex: number): number; + readonly paramCount: number; + readonly pointCount: number; + // (undocumented) + range(transform?: Transform, result?: Range3d): Range3d; + // (undocumented) + reverseIndices(): void; + // (undocumented) + reverseNormals(): void; + setNewFaceData(endFacetIndex?: number): boolean; + terminateFacet(validateAllIndices?: boolean): any; + tryGetFaceData(i: number): FacetFaceData | undefined; + tryTransformInPlace(transform: Transform): boolean; + // (undocumented) + readonly zeroTerminatedIndexCount: number; +} + +// @public (undocumented) +class IndexedPolyfaceVisitor extends PolyfaceData, implements PolyfaceVisitor { + // (undocumented) + clientAuxIndex(i: number): number; + // (undocumented) + clientColorIndex(i: number): number; + // (undocumented) + clientNormalIndex(i: number): number; + // (undocumented) + clientParamIndex(i: number): number; + // (undocumented) + clientPointIndex(i: number): number; + // (undocumented) + static create(polyface: IndexedPolyface, numWrap: number): IndexedPolyfaceVisitor; + // (undocumented) + currentReadIndex(): number; + // (undocumented) + moveToNextFacet(): boolean; + // (undocumented) + moveToReadIndex(facetIndex: number): boolean; + // (undocumented) + readonly numEdgesThisFacet: number; + // (undocumented) + reset(): void; + tryGetDistanceParameter(index: number, result?: Point2d): Point2d | undefined; + tryGetNormalizedParameter(index: number, result?: Point2d): Point2d | undefined; +} + +// @public +class IndexedXYCollection { + // (undocumented) + abstract atPoint2dIndex(index: number, result?: Point2d): Point2d | undefined; + // (undocumented) + abstract atVector2dIndex(index: number, result?: Vector2d): Vector2d | undefined; + // (undocumented) + abstract crossProductIndexIndexIndex(origin: number, indexA: number, indexB: number): number | undefined; + // (undocumented) + abstract crossProductXAndYIndexIndex(origin: XAndY, indexA: number, indexB: number): number | undefined; + readonly length: number; + // (undocumented) + abstract vectorIndexIndex(indexA: number, indexB: number, result?: Vector2d): Vector2d | undefined; + // (undocumented) + abstract vectorXAndYIndex(origin: XAndY, indexB: number, result?: Vector2d): Vector2d | undefined; +} + +// @public +class IndexedXYZCollection { + // (undocumented) + abstract accumulateCrossProductIndexIndexIndex(origin: number, indexA: number, indexB: number, result: Vector3d): void; + // (undocumented) + abstract atPoint3dIndex(index: number, result?: Point3d): Point3d | undefined; + // (undocumented) + abstract atVector3dIndex(index: number, result?: Vector3d): Vector3d | undefined; + // (undocumented) + abstract crossProductIndexIndexIndex(origin: number, indexA: number, indexB: number, result?: Vector3d): Vector3d | undefined; + // (undocumented) + abstract crossProductXYAndZIndexIndex(origin: XYAndZ, indexA: number, indexB: number, result?: Vector3d): Vector3d | undefined; + readonly length: number; + // (undocumented) + abstract vectorIndexIndex(indexA: number, indexB: number, result?: Vector3d): Vector3d | undefined; + // (undocumented) + abstract vectorXYAndZIndex(origin: XYAndZ, indexB: number, result?: Vector3d): Vector3d | undefined; +} + +// @public +enum InverseMatrixState { + // (undocumented) + inverseStored = 1, + // (undocumented) + singular = 2, + // (undocumented) + unknown = 0 +} + +// @public (undocumented) +interface IsNullCheck { + // (undocumented) + isNull(): boolean; +} + +// @public +interface IStrokeHandler { + announceBezierCurve?(bezier: BezierCurveBase, numStrokes: number, parent: CurvePrimitive, spandex: number, fraction0: number, fraction1: number): void; + announceIntervalForUniformStepStrokes(cp: CurvePrimitive, numStrokes: number, fraction0: number, fraction1: number): void; + // (undocumented) + announcePointTangent(xyz: Point3d, fraction: number, tangent: Vector3d): void; + announceSegmentInterval(cp: CurvePrimitive, point0: Point3d, point1: Point3d, numStrokes: number, fraction0: number, fraction1: number): void; + // (undocumented) + endCurvePrimitive(cp: CurvePrimitive): void; + // (undocumented) + endParentCurvePrimitive(cp: CurvePrimitive): void; + // (undocumented) + startCurvePrimitive(cp: CurvePrimitive): void; + startParentCurvePrimitive(cp: CurvePrimitive): void; +} + +// WARNING: knotTolerance has incomplete type information +// @public +class KnotVector { + // (undocumented) + baseKnotFractionToKnot(knotIndex0: number, localFraction: number): number; + clone(): KnotVector; + copyKnots(includeExtraEndKnot: boolean): number[]; + static create(knotArray: number[] | Float64Array, degree: number, skipFirstAndLast?: boolean): KnotVector; + createBasisArray(): Float64Array; + static createUniformClamped(numPoles: number, degree: number, a0: number, a1: number): KnotVector; + static createUniformWrapped(numInterval: number, degree: number, a0: number, a1: number): KnotVector; + // (undocumented) + degree: number; + evaluateBasisFunctions(knotIndex0: number, u: number, f: Float64Array): void; + evaluateBasisFunctions1(knotIndex0: number, u: number, f: Float64Array, df: Float64Array, ddf?: Float64Array): void; + // (undocumented) + fractionToKnot(fraction: number): number; + grevilleKnot(spanIndex: number): number; + // (undocumented) + isAlmostEqual(other: KnotVector): boolean; + isIndexOfRealSpan(spanIndex: number): boolean; + // (undocumented) + readonly knotLength01: number; + // (undocumented) + knots: Float64Array; + // (undocumented) + knotToLeftKnotIndex(u: number): number; + // (undocumented) + readonly leftKnot: number; + // (undocumented) + readonly leftKnotIndex: number; + // (undocumented) + readonly numSpans: number; + // (undocumented) + reflectKnots(): void; + // (undocumented) + readonly rightKnot: number; + // (undocumented) + readonly rightKnotIndex: number; + // (undocumented) + setKnots(knots: number[] | Float64Array, skipFirstAndLast?: boolean): void; + // (undocumented) + spanFractionToFraction(spanIndex: number, localFraction: number): number; + // (undocumented) + spanFractionToKnot(spanIndex: number, localFraction: number): number; + spanIndexToLeftKnotIndex(spanIndex: number): number; + // (undocumented) + spanIndexToSpanLength(spanIndex: number): number; + // (undocumented) + testClosable(mode?: BSplineWrapMode): boolean; + wrappable: BSplineWrapMode; +} + +// @public +class LinearSweep extends SolidPrimitive { + // (undocumented) + clone(): LinearSweep; + // (undocumented) + cloneSweepVector(): Vector3d; + // (undocumented) + cloneTransformed(transform: Transform): LinearSweep; + // (undocumented) + constantVSection(vFraction: number): CurveCollection | undefined; + // (undocumented) + static create(contour: CurveCollection, direction: Vector3d, capped: boolean): LinearSweep | undefined; + static createZSweep(xyPoints: XAndY[], z: number, zSweep: number, capped: boolean): LinearSweep | undefined; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + // (undocumented) + extendRange(range: Range3d, transform?: Transform): void; + getConstructiveFrame(): Transform | undefined; + // (undocumented) + getCurvesRef(): CurveCollection; + // (undocumented) + getSweepContourRef(): SweepContour; + // (undocumented) + isAlmostEqual(other: GeometryQuery): boolean; + readonly isClosedVolume: boolean; + // (undocumented) + isSameGeometryClass(other: any): boolean; + // (undocumented) + tryTransformInPlace(transform: Transform): boolean; +} + +// @public +class LineSegment3d extends CurvePrimitive, implements BeJSONFunctions { + announceClipIntervals(clipper: Clipper, announce?: AnnounceNumberNumberCurvePrimitive): boolean; + // (undocumented) + appendPlaneIntersectionPoints(plane: PlaneAltitudeEvaluator, result: CurveLocationDetail[]): number; + clone(): LineSegment3d; + clonePartialCurve(fractionA: number, fractionB: number): CurvePrimitive | undefined; + cloneTransformed(transform: Transform): CurvePrimitive; + // (undocumented) + closestPoint(spacePoint: Point3d, extend: boolean, result?: CurveLocationDetail): CurveLocationDetail; + computeStrokeCountForOptions(options?: StrokeOptions): number; + static create(point0: Point3d, point1: Point3d, result?: LineSegment3d): LineSegment3d; + static createXYXY(x0: number, y0: number, x1: number, y1: number, z?: number, result?: LineSegment3d): LineSegment3d; + static createXYZXYZ(x0: number, y0: number, z0: number, x1: number, y1: number, z1: number, result?: LineSegment3d): LineSegment3d; + // (undocumented) + curveLength(): number; + // (undocumented) + curveLengthBetweenFractions(fraction0: number, fraction1: number): number; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + emitStrokableParts(handler: IStrokeHandler, options?: StrokeOptions): void; + emitStrokes(dest: LineString3d, options?: StrokeOptions): void; + // (undocumented) + endPoint(result?: Point3d): Point3d; + extendRange(range: Range3d, transform?: Transform): void; + // (undocumented) + fractionToPoint(fraction: number, result?: Point3d): Point3d; + fractionToPointAnd2Derivatives(fraction: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + // (undocumented) + fractionToPointAndDerivative(fraction: number, result?: Ray3d): Ray3d; + // (undocumented) + static fromJSON(json?: any): LineSegment3d; + getFractionToDistanceScale(): number | undefined; + // (undocumented) + isAlmostEqual(other: GeometryQuery): boolean; + readonly isExtensibleFractionSpace: boolean; + // (undocumented) + isInPlane(plane: Plane3dByOriginAndUnitNormal): boolean; + // (undocumented) + isSameGeometryClass(other: GeometryQuery): boolean; + // (undocumented) + readonly point0Ref: Point3d; + // (undocumented) + readonly point1Ref: Point3d; + // (undocumented) + quickLength(): number; + reverseInPlace(): void; + set(point0: Point3d, point1: Point3d): void; + setFrom(other: LineSegment3d): void; + setFromJSON(json?: any): void; + setRefs(point0: Point3d, point1: Point3d): void; + // (undocumented) + startPoint(result?: Point3d): Point3d; + toJSON(): any; + // (undocumented) + tryTransformInPlace(transform: Transform): boolean; +} + +// @public +class LineString3d extends CurvePrimitive, implements BeJSONFunctions { + addClosurePoint(): void; + addDerivative(vector: Vector3d): void; + addFraction(fraction: number): void; + // WARNING: The type "StrokeCountMap" needs to be exported by the package (e.g. added to index.ts) + addMappedStrokesToLineString3D(map: StrokeCountMap, destLinestring: LineString3d): number; + addPoint(point: Point3d): void; + // (undocumented) + addPoints(...points: any[]): void; + addPointXYZ(x: number, y: number, z?: number): void; + // (undocumented) + addSteppedPoints(source: GrowableXYZArray, pointIndex0: number, step: number, numAdd: number): void; + addSurfaceNormal(vector: Vector3d): void; + addUVParam(uvParam: XAndY): void; + addUVParamAsUV(u: number, v: number): void; + announceClipIntervals(clipper: Clipper, announce?: AnnounceNumberNumberCurvePrimitive): boolean; + appendFractionalStrokePoints(curve: CurvePrimitive, numStrokes: number, fraction0?: number, fraction1?: number, include01?: boolean): void; + appendFractionToPoint(curve: CurvePrimitive, fraction: number): void; + // (undocumented) + appendInterpolatedStrokePoints(numStrokes: number, point0: Point3d, point1: Point3d, include01: boolean): void; + appendPlaneIntersectionPoints(plane: PlaneAltitudeEvaluator, result: CurveLocationDetail[]): number; + appendStrokePoint(point: Point3d, fraction?: number): void; + clear(): void; + // (undocumented) + clone(): LineString3d; + clonePartialCurve(fractionA: number, fractionB: number): CurvePrimitive | undefined; + // (undocumented) + cloneTransformed(transform: Transform): CurvePrimitive; + // (undocumented) + closestPoint(spacePoint: Point3d, extend: boolean, result?: CurveLocationDetail): CurveLocationDetail; + // WARNING: The type "StrokeCountMap" needs to be exported by the package (e.g. added to index.ts) + computeAndAttachRecursiveStrokeCounts(options?: StrokeOptions, parentStrokeMap?: StrokeCountMap): void; + computeStrokeCountForOptions(options?: StrokeOptions): number; + computeUVFromXYZTransform(transform: Transform): void; + // (undocumented) + static create(...points: any[]): LineString3d; + static createFloat64Array(xyzData: Float64Array): LineString3d; + static createForStrokes(capacity: number | undefined, options: StrokeOptions | undefined): LineString3d; + // (undocumented) + static createPoints(points: Point3d[]): LineString3d; + // (undocumented) + static createRectangleXY(point0: Point3d, ax: number, ay: number, closed?: boolean): LineString3d; + static createRegularPolygonXY(center: Point3d, edgeCount: number, radius: number, radiusToVertices?: boolean): LineString3d; + // (undocumented) + static createXY(points: XAndY[], z: number, enforceClosure?: boolean): LineString3d; + // (undocumented) + curveLength(): number; + // (undocumented) + curveLengthBetweenFractions(fraction0: number, fraction1: number): number; + derivativeAt(i: number, result?: Vector3d): Vector3d | undefined; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + emitStrokableParts(handler: IStrokeHandler, options?: StrokeOptions): void; + emitStrokes(dest: LineString3d, options?: StrokeOptions): void; + // (undocumented) + endPoint(): Point3d; + ensureEmptyNormalIndices(): GrowableFloat64Array; + ensureEmptyPointIndices(): GrowableFloat64Array; + ensureEmptySurfaceNormals(): GrowableXYZArray; + ensureEmptyUVIndices(): GrowableFloat64Array; + ensureEmptyUVParams(): GrowableXYArray; + // (undocumented) + extendRange(rangeToExtend: Range3d, transform?: Transform): void; + readonly fractions: GrowableFloat64Array | undefined; + fractionToFrenetFrame(fraction: number, result?: Transform): Transform; + // (undocumented) + fractionToPoint(fraction: number, result?: Point3d): Point3d; + fractionToPointAnd2Derivatives(fraction: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + // (undocumented) + fractionToPointAndDerivative(fraction: number, result?: Ray3d): Ray3d; + // (undocumented) + static fromJSON(json?: any): LineString3d; + getIndexedSegment(index: number): LineSegment3d | undefined; + initializeDerivativeArray(retainArrayContentsIfAlreadyPresent?: boolean): void; + initializeFractionArray(retainArrayContentsIfAlreadyPresent?: boolean): void; + initializeUVParamsArray(retainArrayContentsIfAlreadyPresent?: boolean): void; + // (undocumented) + isAlmostEqual(other: GeometryQuery): boolean; + readonly isExtensibleFractionSpace: boolean; + // (undocumented) + isInPlane(plane: Plane3dByOriginAndUnitNormal): boolean; + // (undocumented) + readonly isPhysicallyClosed: boolean; + // (undocumented) + isSameGeometryClass(other: GeometryQuery): boolean; + moveSignedDistanceFromFraction(startFraction: number, signedDistance: number, allowExtension: false, result?: CurveLocationDetail): CurveLocationDetail; + // (undocumented) + readonly normalIndices: GrowableFloat64Array | undefined; + // (undocumented) + numPoints(): number; + // (undocumented) + readonly packedDerivatives: GrowableXYZArray | undefined; + readonly packedPoints: GrowableXYZArray; + // (undocumented) + readonly packedSurfaceNormals: GrowableXYZArray | undefined; + // (undocumented) + readonly packedUVParams: GrowableXYArray | undefined; + // (undocumented) + readonly paramIndices: GrowableFloat64Array | undefined; + pointAt(i: number, result?: Point3d): Point3d | undefined; + // (undocumented) + readonly pointIndices: GrowableFloat64Array | undefined; + readonly points: Point3d[]; + popPoint(): void; + // (undocumented) + quickLength(): number; + quickUnitNormal(result?: Vector3d): Vector3d | undefined; + // (undocumented) + reverseInPlace(): void; + segmentIndexAndLocalFractionToGlobalFraction(index: number, localFraction: number): number; + // (undocumented) + setFrom(other: LineString3d): void; + // (undocumented) + setFromJSON(json?: any): void; + // (undocumented) + startPoint(): Point3d; + surfaceNormalAt(i: number, result?: Vector3d): Vector3d | undefined; + toJSON(): any; + // (undocumented) + tryTransformInPlace(transform: Transform): boolean; + vectorBetween(i: number, j: number, result?: Vector3d): Vector3d | undefined; +} + +// @public +class Loop extends CurveChain { + constructor(); + // (undocumented) + announceToCurveProcessor(processor: RecursiveCurveProcessor, indexInParent?: number): void; + // (undocumented) + cloneEmptyPeer(): Loop; + // (undocumented) + cloneStroked(options?: StrokeOptions): AnyCurve; + static create(...curves: CurvePrimitive[]): Loop; + static createArray(curves: CurvePrimitive[]): Loop; + // (undocumented) + static createPolygon(points: Point3d[]): Loop; + // (undocumented) + cyclicCurvePrimitive(index: number): CurvePrimitive | undefined; + // (undocumented) + dgnBoundaryType(): number; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + // (undocumented) + isInner: boolean; + // (undocumented) + isSameGeometryClass(other: GeometryQuery): boolean; +} + +// @public +class Map4d implements BeJSONFunctions { + // (undocumented) + clone(): Map4d; + static createBoxMap(lowA: Point3d, highA: Point3d, lowB: Point3d, highB: Point3d, result?: Map4d): Map4d | undefined; + static createIdentity(): Map4d; + static createRefs(matrix0: Matrix4d, matrix1: Matrix4d): Map4d; + static createTransform(transform0: Transform, transform1?: Transform): Map4d | undefined; + static createVectorFrustum(origin: Point3d, uVector: Vector3d, vVector: Vector3d, wVector: Vector3d, fraction: number): Map4d | undefined; + static fromJSON(json?: any): Map4d; + // (undocumented) + isAlmostEqual(other: Map4d): boolean; + // (undocumented) + multiplyMapMap(other: Map4d): Map4d; + // (undocumented) + reverseInPlace(): void; + sandwich0This1(other: Map4d): Map4d; + sandwich1This0(other: Map4d): Map4d; + setFrom(other: Map4d): void; + setFromJSON(json: any): void; + setIdentity(): void; + // (undocumented) + toJSON(): any; + // (undocumented) + readonly transform0: Matrix4d; + // (undocumented) + readonly transform1: Matrix4d; +} + +// @public +class Matrix3d implements BeJSONFunctions { + constructor(coffs?: Float64Array); + addScaledInPlace(other: Matrix3d, scale: number): void; + applyGivensColumnOp(i: number, j: number, c: number, s: number): void; + at(row: number, column: number): number; + axisOrderCrossProductsInPlace(axisOrder: AxisOrder): void; + // (undocumented) + clone(result?: Matrix3d): Matrix3d; + // (undocumented) + coffs: Float64Array; + // (undocumented) + columnX(result?: Vector3d): Vector3d; + // (undocumented) + columnXDotColumnY(): number; + // (undocumented) + columnXMagnitude(): number; + // (undocumented) + columnXMagnitudeSquared(): number; + // (undocumented) + columnXYCrossProductMagnitude(): number; + // (undocumented) + columnY(result?: Vector3d): Vector3d; + // (undocumented) + columnYMagnitude(): number; + // (undocumented) + columnYMagnitudeSquared(): number; + // (undocumented) + columnZ(result?: Vector3d): Vector3d; + // (undocumented) + columnZCrossVector(vector: XYZ, result?: Vector3d): Vector3d; + // (undocumented) + columnZMagnitude(): number; + // (undocumented) + columnZMagnitudeSquared(): number; + computeCachedInverse(useCacheIfAvailable: boolean): boolean; + conditionNumber(): number; + static create90DegreeRotationAroundAxis(axisIndex: number): Matrix3d; + static createCapture(coffs: Float64Array, inverseCoffs?: Float64Array): Matrix3d; + static createColumns(vectorU: Vector3d, vectorV: Vector3d, vectorW: Vector3d, result?: Matrix3d): Matrix3d; + // (undocumented) + static createColumnsInAxisOrder(axisOrder: AxisOrder, columnA: Vector3d, columnB: Vector3d, columnC: Vector3d | undefined, result?: Matrix3d): Matrix3d; + static createColumnsXYW(vectorU: XAndY, uz: number, vectorV: XAndY, vz: number, vectorW: XAndY, wz: number, result?: Matrix3d): Matrix3d; + static createDirectionalScale(direction: Vector3d, scale: number, result?: Matrix3d): Matrix3d; + // (undocumented) + static createFromQuaternion(quat: Point4d): Matrix3d; + // (undocumented) + static createIdentity(result?: Matrix3d): Matrix3d; + static createPartialRotationVectorToVector(vectorA: Vector3d, fraction: number, vectorB: Vector3d, result?: Matrix3d): Matrix3d | undefined; + static createPerpendicularVectorFavorPlaneContainingZ(vector: Vector3d, result?: Vector3d): Vector3d; + static createPerpendicularVectorFavorXYPlane(vector: Vector3d, result?: Vector3d): Vector3d; + static createRigidFromColumns(vectorA: Vector3d, vectorB: Vector3d, axisOrder: AxisOrder, result?: Matrix3d): Matrix3d | undefined; + static createRigidFromMatrix3d(source: Matrix3d, axisOrder?: AxisOrder, result?: Matrix3d): Matrix3d | undefined; + static createRigidHeadsUp(vectorA: Vector3d, axisOrder?: AxisOrder, result?: Matrix3d): Matrix3d; + static createRigidViewAxesZTowardsEye(x: number, y: number, z: number, result?: Matrix3d): Matrix3d; + // (undocumented) + static createRotationAroundAxisIndex(axisIndex: AxisIndex, angle: Angle, result?: Matrix3d): Matrix3d; + // (undocumented) + static createRotationAroundVector(axis: Vector3d, angle: Angle, result?: Matrix3d): Matrix3d | undefined; + // (undocumented) + static createRotationVectorToVector(vectorA: Vector3d, vectorB: Vector3d, result?: Matrix3d): Matrix3d | undefined; + static createRows(vectorU: Vector3d, vectorV: Vector3d, vectorW: Vector3d, result?: Matrix3d): Matrix3d; + // (undocumented) + static createRowValues(axx: number, axy: number, axz: number, ayx: number, ayy: number, ayz: number, azx: number, azy: number, azz: number, result?: Matrix3d): Matrix3d; + static createScale(scaleFactorX: number, scaleFactorY: number, scaleFactorZ: number, result?: Matrix3d): Matrix3d; + static createShuffledColumns(vectorU: Vector3d, vectorV: Vector3d, vectorW: Vector3d, axisOrder: AxisOrder, result?: Matrix3d): Matrix3d; + static createStandardWorldToView(index: StandardViewIndex, invert?: boolean, result?: Matrix3d): Matrix3d; + static createUniformScale(scaleFactor: number): Matrix3d; + static createViewedAxes(rightVector: Vector3d, upVector: Vector3d, leftNoneRight?: number, topNoneBottom?: number): Matrix3d | undefined; + // (undocumented) + static createZero(): Matrix3d; + determinant(): number; + // (undocumented) + dotColumnX(vector: XYZ): number; + // (undocumented) + dotColumnY(vector: XYZ): number; + // (undocumented) + dotColumnZ(vector: XYZ): number; + // (undocumented) + dotRowX(vector: XYZ): number; + // (undocumented) + dotRowXXYZ(x: number, y: number, z: number): number; + // (undocumented) + dotRowY(vector: XYZ): number; + // (undocumented) + dotRowYXYZ(x: number, y: number, z: number): number; + // (undocumented) + dotRowZ(vector: XYZ): number; + // (undocumented) + dotRowZXYZ(x: number, y: number, z: number): number; + factorPerpendicularColumns(matrixC: Matrix3d, matrixU: Matrix3d): boolean; + factorRigidWithSignedScale(): { + rigidAxes: Matrix3d; + scale: number; + } | undefined; + fastSymmetricEigenvalues(leftEigenvectors: Matrix3d, lambda: Vector3d): boolean; + // (undocumented) + static flatIndexOf(row: number, column: number): number; + freeze(): void; + // (undocumented) + static fromJSON(json?: Matrix3dProps): Matrix3d; + getAxisAndAngleOfRotation: { + angle: Angle; + axis: Vector3d; + ok: boolean; + } + getColumn(columnIndex: number, result?: Vector3d): Vector3d; + getRow(columnIndex: number, result?: Vector3d): Vector3d; + static readonly identity: Matrix3d; + indexedColumnWithWeight(index: number, weight: number, result?: Point4d): Point4d; + inverse(result?: Matrix3d): Matrix3d | undefined; + // (undocumented) + inverseCoffs: Float64Array | undefined; + // (undocumented) + inverseState: InverseMatrixState; + isAlmostEqual(other: Matrix3d, tol?: number): boolean; + readonly isDiagonal: boolean; + isExactEqual(other: Matrix3d): boolean; + readonly isIdentity: boolean; + isRigid(allowMirror?: boolean): boolean; + readonly isSignedPermutation: boolean; + // (undocumented) + isSingular(): boolean; + readonly isUpperTriangular: boolean; + readonly isXY: boolean; + maxAbs(): number; + maxDiff(other: Matrix3d): number; + multiplyInverse(vector: Vector3d, result?: Vector3d): Vector3d | undefined; + multiplyInverseTranspose(vector: Vector3d, result?: Vector3d): Vector3d | undefined; + multiplyInverseXYZAsPoint3d(x: number, y: number, z: number, result?: Point3d): Point3d | undefined; + multiplyInverseXYZAsVector3d(x: number, y: number, z: number, result?: Vector3d): Vector3d | undefined; + multiplyMatrixMatrix(other: Matrix3d, result?: Matrix3d): Matrix3d; + multiplyMatrixMatrixTranspose(other: Matrix3d, result?: Matrix3d): Matrix3d; + multiplyMatrixTransform(other: Transform, result?: Transform): Transform; + multiplyMatrixTransposeMatrix(other: Matrix3d, result?: Matrix3d): Matrix3d; + // (undocumented) + multiplyTransposeVector(vector: Vector3d, result?: Vector3d): Vector3d; + multiplyTransposeVectorInPlace(xyzData: XYZ): void; + multiplyTransposeXYZ(x: number, y: number, z: number, result?: Vector3d): Vector3d; + multiplyVector(vector: Vector3d, result?: Vector3d): Vector3d; + multiplyVectorArrayInPlace(data: XYZ[]): void; + multiplyVectorInPlace(xyzData: XYZ): void; + multiplyXY(x: number, y: number, result?: Vector3d): Vector3d; + multiplyXYZ(x: number, y: number, z: number, result?: Vector3d): Vector3d; + multiplyXYZtoXYZ(xyz: XYZ, result: XYZ): XYZ; + normalizeColumnsInPlace(originalMagnitudes?: Vector3d): boolean; + normalizeRowsInPlace(originalMagnitudes?: Vector3d): boolean; + // (undocumented) + static numComputeCache: number; + // (undocumented) + static numUseCache: number; + // (undocumented) + originPlusMatrixTimesXY(origin: XYZ, x: number, y: number, result?: Point3d): Point3d; + rowX(result?: Vector3d): Vector3d; + // (undocumented) + rowXMagnitude(): number; + rowY(result?: Vector3d): Vector3d; + // (undocumented) + rowYMagnitude(): number; + rowZ(result?: Vector3d): Vector3d; + // (undocumented) + rowZMagnitude(): number; + sameDiagonalScale(): number | undefined; + scale(scale: number, result?: Matrix3d): Matrix3d; + scaleColumns(scaleX: number, scaleY: number, scaleZ: number, result?: Matrix3d): Matrix3d; + scaleColumnsInPlace(scaleX: number, scaleY: number, scaleZ: number): void; + scaleRows(scaleX: number, scaleY: number, scaleZ: number, result?: Matrix3d): Matrix3d; + setAt(row: number, column: number, value: number): void; + setColumn(columnIndex: number, value: Vector3d | undefined): void; + setColumns(vectorX: Vector3d | undefined, vectorY: Vector3d | undefined, vectorZ?: Vector3d | undefined): void; + setColumnsPoint4dXYZ(vectorU: Point4d, vectorV: Point4d, vectorW: Point4d): void; + // (undocumented) + setFrom(other: Matrix3d): void; + // (undocumented) + setFromJSON(json?: Matrix3dProps): void; + // (undocumented) + setIdentity(): void; + // (undocumented) + setRow(columnIndex: number, value: Vector3d): void; + setRowValues(axx: number, axy: number, axz: number, ayx: number, ayy: number, ayz: number, azx: number, azy: number, azz: number): void; + // (undocumented) + setZero(): void; + sumDiagonal(): number; + sumDiagonalSquares(): number; + sumSkewSquares(): number; + sumSquares(): number; + symmetricEigenvalues(leftEigenvectors: Matrix3d, lambda: Vector3d): boolean; + testPerpendicularUnitRowsAndColumns(): boolean; + toJSON(): Matrix3dProps; + // (undocumented) + toQuaternion(): Point4d; + transpose(result?: Matrix3d): Matrix3d; + transposeInPlace(): void; + // (undocumented) + static useCachedInverse: boolean; + // (undocumented) + static xyPlusMatrixTimesXY(origin: XAndY, matrix: Matrix3d, vector: XAndY, result?: Point2d): Point2d; + // (undocumented) + static xyzMinusMatrixTimesXYZ(origin: XYZ, matrix: Matrix3d, vector: XYZ, result?: Point3d): Point3d; + // (undocumented) + static xyzPlusMatrixTimesCoordinates(origin: XYZ, matrix: Matrix3d, x: number, y: number, z: number, result?: Point3d): Point3d; + static xyzPlusMatrixTimesCoordinatesToFloat64Array(origin: XYZ, matrix: Matrix3d, x: number, y: number, z: number, result?: Float64Array): Float64Array; + static xyzPlusMatrixTimesWeightedCoordinates(origin: XYZ, matrix: Matrix3d, x: number, y: number, z: number, w: number, result?: Point4d): Point4d; + static xyzPlusMatrixTimesWeightedCoordinatesToFloat64Array(origin: XYZ, matrix: Matrix3d, x: number, y: number, z: number, w: number, result?: Float64Array): Float64Array; + // (undocumented) + static xyzPlusMatrixTimesXYZ(origin: XYZ, matrix: Matrix3d, vector: XYAndZ, result?: Point3d): Point3d; +} + +// @public +class Matrix4d implements BeJSONFunctions { + addMomentsInPlace(x: number, y: number, z: number, w: number): void; + addScaledInPlace(other: Matrix4d, scale?: number): void; + readonly asTransform: Transform | undefined; + // (undocumented) + atIJ(rowIndex: number, columnIndex: number): number; + // (undocumented) + clone(): Matrix4d; + cloneTransposed(result?: Matrix4d): Matrix4d; + // (undocumented) + columnDotColumn(columnIndexThis: number, other: Matrix4d, columnIndexOther: number): number; + // (undocumented) + columnDotRow(columnIndexThis: number, other: Matrix4d, rowIndexOther: number): number; + // (undocumented) + columnW(): Point4d; + // (undocumented) + columnX(): Point4d; + // (undocumented) + columnY(): Point4d; + // (undocumented) + columnZ(): Point4d; + static createBoxToBox(lowA: Point3d, highA: Point3d, lowB: Point3d, highB: Point3d, result?: Matrix4d): Matrix4d | undefined; + static createIdentity(result?: Matrix4d): Matrix4d; + createInverse(): Matrix4d | undefined; + static createRowValues(cxx: number, cxy: number, cxz: number, cxw: number, cyx: number, cyy: number, cyz: number, cyw: number, czx: number, czy: number, czz: number, czw: number, cwx: number, cwy: number, cwz: number, cww: number, result?: Matrix4d): Matrix4d; + static createTransform(source: Transform, result?: Matrix4d): Matrix4d; + static createTranslationAndScaleXYZ(tx: number, ty: number, tz: number, scaleX: number, scaleY: number, scaleZ: number, result?: Matrix4d): Matrix4d; + static createTranslationXYZ(x: number, y: number, z: number, result?: Matrix4d): Matrix4d; + static createZero(result?: Matrix4d): Matrix4d; + diagonal(): Point4d; + // (undocumented) + static fromJSON(json?: Matrix4dProps): Matrix4d; + getSteppedPoint(i0: number, step: number, result?: Point4d): Point4d; + // (undocumented) + readonly hasPerspective: boolean; + // (undocumented) + isAlmostEqual(other: Matrix4d): boolean; + isIdentity(tol?: number): boolean; + matrixPart(): Matrix3d; + maxAbs(): number; + maxDiff(other: Matrix4d): number; + multiplyBlockedFloat64ArrayInPlace(data: Float64Array): void; + multiplyMatrixMatrix(other: Matrix4d, result?: Matrix4d): Matrix4d; + multiplyMatrixMatrixTranspose(other: Matrix4d, result?: Matrix4d): Matrix4d; + multiplyMatrixTransposeMatrix(other: Matrix4d, result?: Matrix4d): Matrix4d; + multiplyPoint3d(pt: XYAndZ, w: number, result?: Point4d): Point4d; + multiplyPoint3dArray(pts: XYAndZ[], results: Point4d[], w?: number): void; + multiplyPoint3dArrayQuietNormalize(points: Point3d[]): void; + multiplyPoint3dQuietNormalize(point: XYAndZ, result?: Point3d): Point3d; + multiplyPoint4d(point: Point4d, result?: Point4d): Point4d; + multiplyPoint4dArrayQuietRenormalize(pts: Point4d[], results: Point3d[]): void; + multiplyTransposePoint4d(point: Point4d, result?: Point4d): Point4d; + multiplyTransposeXYZW(x: number, y: number, z: number, w: number, result?: Point4d): Point4d; + multiplyXYZW(x: number, y: number, z: number, w: number, result?: Point4d): Point4d; + multiplyXYZWQuietRenormalize(x: number, y: number, z: number, w: number, result?: Point3d): Point3d; + // (undocumented) + rowArrays(f?: (value: number) => any): any; + // (undocumented) + rowDotColumn(rowIndex: number, other: Matrix4d, columnIndex: number): number; + // (undocumented) + rowDotRow(rowIndexThis: number, other: Matrix4d, rowIndexOther: number): number; + rowOperation(rowIndexA: number, rowIndexB: number, firstColumnIndex: number, scale: number): void; + // (undocumented) + rowW(): Point4d; + // (undocumented) + rowX(): Point4d; + // (undocumented) + rowY(): Point4d; + // (undocumented) + rowZ(): Point4d; + scaleRowsInPlace(ax: number, ay: number, az: number, aw: number): void; + // (undocumented) + setFrom(other: Matrix4d): void; + // (undocumented) + setFromJSON(json?: Matrix4dProps): void; + setIdentity(): void; + setOriginAndVectors(origin: XYZ, vectorX: Vector3d, vectorY: Vector3d, vectorZ: Vector3d): void; + setZero(): void; + toJSON(): Matrix4dProps; + weight(): number; +} + +// @public +class MomentData { + // (undocumented) + accumulatePointMomentsFromOrigin(points: Point3d[]): void; + // (undocumented) + clearSums(origin?: Point3d): void; + static inertiaProductsToPrincipalAxes(origin: XYZ, inertiaProducts: Matrix4d): MomentData | undefined; + localToWorldMap: Transform; + // (undocumented) + static momentTensorFromInertiaProducts(products: Matrix3d): Matrix3d; + // (undocumented) + origin: Point3d; + // (undocumented) + static pointsToPrincipalAxes(points: Point3d[]): MomentData; + radiusOfGyration: Vector3d; + // (undocumented) + shiftSumsToCentroid(): boolean; + // (undocumented) + static sortColumnsForIncreasingMoments(axes: Matrix3d, moments: Vector3d): void; + // (undocumented) + sums: Matrix4d; +} + +// @public (undocumented) +class Newton1dUnbounded extends AbstractNewtonIterator { + constructor(func: NewtonEvaluatorRtoRD); + // (undocumented) + applyCurrentStep(): boolean; + computeStep(): boolean; + // (undocumented) + currentStepSize(): number; + // (undocumented) + getX(): number; + // (undocumented) + setTarget(y: number): void; + // (undocumented) + setX(x: number): boolean; +} + +// @public +class Newton1dUnboundedApproximateDerivative extends AbstractNewtonIterator { + constructor(func: NewtonEvaluatorRtoR); + // (undocumented) + applyCurrentStep(): boolean; + computeStep(): boolean; + // (undocumented) + currentStepSize(): number; + // (undocumented) + derivativeH: number; + // (undocumented) + getX(): number; + // (undocumented) + setX(x: number): boolean; +} + +// @public +class Newton2dUnboundedWithDerivative extends AbstractNewtonIterator { + constructor(func: NewtonEvaluatorRRtoRRD); + // (undocumented) + applyCurrentStep(): boolean; + computeStep(): boolean; + // (undocumented) + currentStepSize(): number; + // (undocumented) + getU(): number; + // (undocumented) + getV(): number; + // (undocumented) + setUV(x: number, y: number): boolean; +} + +// @public +class NewtonEvaluatorRRtoRRD { + constructor(); + currentF: Plane3dByOriginAndVectors; + abstract evaluate(x: number, y: number): boolean; +} + +// @public +class NewtonEvaluatorRtoR { + // (undocumented) + currentF: number; + // (undocumented) + abstract evaluate(x: number): boolean; +} + +// @public +class NewtonEvaluatorRtoRD { + // (undocumented) + currentdFdX: number; + // (undocumented) + currentF: number; + // (undocumented) + abstract evaluate(x: number): boolean; +} + +// @public +class NullGeometryHandler extends GeometryHandler { + // (undocumented) + handleArc3d(_g: Arc3d): any; + // (undocumented) + handleBagOfCurves(_g: BagOfCurves): any; + // (undocumented) + handleBezierCurve3d(_g: BezierCurve3d): any; + // (undocumented) + handleBezierCurve3dH(_g: BezierCurve3dH): any; + // (undocumented) + handleBox(_g: Box): any; + // (undocumented) + handleBSplineCurve3d(_g: BSplineCurve3d): any; + // (undocumented) + handleBSplineCurve3dH(_g: BSplineCurve3dH): any; + // (undocumented) + handleBSplineSurface3d(_g: BSplineSurface3d): any; + // (undocumented) + handleBSplineSurface3dH(_g: BSplineSurface3dH): any; + // (undocumented) + handleCone(_g: Cone): any; + // (undocumented) + handleCoordinateXYZ(_g: CoordinateXYZ): any; + // (undocumented) + handleCurveCollection(_g: CurveCollection): any; + // (undocumented) + handleIndexedPolyface(_g: IndexedPolyface): any; + // (undocumented) + handleLinearSweep(_g: LinearSweep): any; + // (undocumented) + handleLineSegment3d(_g: LineSegment3d): any; + // (undocumented) + handleLineString3d(_g: LineString3d): any; + // (undocumented) + handleLoop(_g: Loop): any; + // (undocumented) + handleParityRegion(_g: ParityRegion): any; + // (undocumented) + handlePath(_g: Path): any; + // (undocumented) + handlePointString3d(_g: PointString3d): any; + // (undocumented) + handleRotationalSweep(_g: RotationalSweep): any; + // (undocumented) + handleRuledSweep(_g: RuledSweep): any; + // (undocumented) + handleSphere(_g: Sphere): any; + // (undocumented) + handleTorusPipe(_g: TorusPipe): any; + // (undocumented) + handleTransitionSpiral(_g: TransitionSpiral3d): any; + // (undocumented) + handleUnionRegion(_g: UnionRegion): any; +} + +// @public (undocumented) +class NumberArray { + static isAlmostEqual(dataA: number[] | Float64Array | undefined, dataB: number[] | Float64Array | undefined, tolerance: number): boolean; + // (undocumented) + static isCoordinateInArray(x: number, data: number[] | undefined): boolean; + static isExactEqual(dataA: any[] | Float64Array | undefined, dataB: any[] | Float64Array | undefined): boolean; + // (undocumented) + static maxAbsArray(values: number[]): number; + // (undocumented) + static maxAbsDiff(dataA: number[], dataB: number[]): number; + // (undocumented) + static maxAbsDiffFloat64(dataA: Float64Array, dataB: Float64Array): number; + // (undocumented) + static maxAbsTwo(a1: number, a2: number): number; + static preciseSum(data: number[]): number; + static sum(data: number[] | Float64Array): number; +} + +// @public +class Order2Bezier extends BezierCoffs { + constructor(f0?: number, f1?: number); + basisFunctions(u: number, result?: Float64Array): Float64Array; + clone(): Order2Bezier; + evaluate(u: number): number; + roots(targetValue: number, restrictTo01: boolean): number[] | undefined; + // (undocumented) + solve(rightHandSide: number): number | undefined; + static solveCoffs(a0: number, a1: number): number | undefined; + sumBasisFunctionDerivatives(_u: number, polygon: Float64Array, n: number, result?: Float64Array): Float64Array; + sumBasisFunctions(u: number, polygon: Float64Array, n: number, result?: Float64Array): Float64Array; +} + +// @public +class Order3Bezier extends BezierCoffs { + constructor(f0?: number, f1?: number, f2?: number); + addSquareLinear(f0: number, f1: number, a: number): void; + basisFunctions(u: number, result?: Float64Array): Float64Array; + // (undocumented) + clone(): Order3Bezier; + evaluate(u: number): number; + // (undocumented) + roots(targetValue: number, restrictTo01: boolean): number[] | undefined; + sumBasisFunctionDerivatives(u: number, polygon: Float64Array, n: number, result?: Float64Array): Float64Array; + sumBasisFunctions(u: number, polygon: Float64Array, n: number, result?: Float64Array): Float64Array; +} + +// @public +class Order4Bezier extends BezierCoffs { + constructor(f0?: number, f1?: number, f2?: number, f3?: number); + basisFunctions(u: number, result?: Float64Array): Float64Array; + // (undocumented) + clone(): Order4Bezier; + static createFromDegree3PowerPolynomial(source: Degree3PowerPolynomial): Order4Bezier; + // (undocumented) + static createProductOrder3Order2(factorA: Order3Bezier, factorB: Order2Bezier): Order4Bezier; + evaluate(u: number): number; + // (undocumented) + realRoots(e: number, restrictTo01: boolean, roots: GrowableFloat64Array): undefined; + sumBasisFunctionDerivatives(u: number, polygon: Float64Array, n: number, result?: Float64Array): Float64Array; + sumBasisFunctions(u: number, polygon: Float64Array, n: number, result?: Float64Array): Float64Array; +} + +// @public +class Order5Bezier extends BezierCoffs { + constructor(f0?: number, f1?: number, f2?: number, f3?: number, f4?: number); + // (undocumented) + addConstant(a: number): void; + // (undocumented) + addProduct(f: Order3Bezier, g: Order3Bezier, a: number): void; + basisFunctions(u: number, result?: Float64Array): Float64Array; + // (undocumented) + clone(): Order5Bezier; + static createFromDegree4PowerPolynomial(source: Degree4PowerPolynomial): Order5Bezier; + evaluate(u: number): number; + // (undocumented) + realRoots(e: number, restrictTo01: boolean, roots: GrowableFloat64Array): void; + sumBasisFunctionDerivatives(u: number, polygon: Float64Array, n: number, result?: Float64Array): Float64Array; + sumBasisFunctions(u: number, polygon: Float64Array, n: number, result?: Float64Array): Float64Array; +} + +// @public +class OrderedRotationAngles { + static createAngles(xRotation: Angle, yRotation: Angle, zRotation: Angle, order: AxisOrder, result?: OrderedRotationAngles): OrderedRotationAngles; + static createDegrees(xDegrees: number, yDegrees: number, zDegrees: number, order: AxisOrder, result?: OrderedRotationAngles): OrderedRotationAngles; + static createFromMatrix3d(matrix: Matrix3d, order: AxisOrder, result?: OrderedRotationAngles): OrderedRotationAngles; + static createRadians(xRadians: number, yRadians: number, zRadians: number, order: AxisOrder, result?: OrderedRotationAngles): OrderedRotationAngles; + // (undocumented) + readonly order: AxisOrder; + toMatrix3d(result?: Matrix3d): Matrix3d; + // (undocumented) + static treatVectorsAsColumns: boolean; + // (undocumented) + readonly xAngle: Angle; + // (undocumented) + readonly xDegrees: number; + // (undocumented) + readonly xRadians: number; + // (undocumented) + readonly yAngle: Angle; + // (undocumented) + readonly yDegrees: number; + // (undocumented) + readonly yRadians: number; + // (undocumented) + readonly zAngle: Angle; + // (undocumented) + readonly zDegrees: number; + // (undocumented) + readonly zRadians: number; +} + +// @public +interface PackedPointGrid { + numCartesianDimensions: number; + points: number[][][]; + weightStyle?: WeightStyle; +} + +// @public +class PackedPointsWithIndex { + constructor(numOldIndexEntry: number); + // (undocumented) + growablePackedPoints: GrowableXYZArray | undefined; + // (undocumented) + static invalidIndex: number; + // (undocumented) + oldToNew: Uint32Array; + // (undocumented) + packedPoints: Point3d[]; + updateIndices(indices: number[]): boolean; +} + +// @public +class ParityRegion extends CurveCollection { + constructor(); + // (undocumented) + protected _children: Loop[]; + // (undocumented) + announceToCurveProcessor(processor: RecursiveCurveProcessor, indexInParent?: number): void; + // (undocumented) + readonly children: Loop[]; + // (undocumented) + clone(): ParityRegion; + // (undocumented) + cloneEmptyPeer(): ParityRegion; + // (undocumented) + cloneStroked(options?: StrokeOptions): ParityRegion; + // (undocumented) + static create(...data: Loop[]): ParityRegion; + // (undocumented) + dgnBoundaryType(): number; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + // (undocumented) + getChild(i: number): Loop | undefined; + // (undocumented) + isSameGeometryClass(other: GeometryQuery): boolean; + // (undocumented) + tryAddChild(child: AnyCurve): boolean; +} + +// @public +class PascalCoefficients { + static getBezierBasisDerivatives(order: number, u: number, result?: Float64Array): Float64Array; + static getBezierBasisValues(order: number, u: number, result?: Float64Array): Float64Array; + static getRow(row: number): Float64Array; +} + +// @public +class Path extends CurveChain { + constructor(); + // (undocumented) + announceToCurveProcessor(processor: RecursiveCurveProcessor, indexInParent?: number): void; + // (undocumented) + cloneEmptyPeer(): Path; + // (undocumented) + cloneStroked(options?: StrokeOptions): AnyCurve; + static create(...curves: Array): Path; + static createArray(curves: CurvePrimitive[]): Path; + cyclicCurvePrimitive(index: number): CurvePrimitive | undefined; + // (undocumented) + dgnBoundaryType(): number; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + // (undocumented) + isSameGeometryClass(other: GeometryQuery): boolean; +} + +// @public +class Plane3dByOriginAndUnitNormal implements BeJSONFunctions { + // (undocumented) + altitude(spacePoint: Point3d): number; + // (undocumented) + altitudeToPoint(altitude: number, result?: Point3d): Point3d; + // (undocumented) + altitudeXYZ(x: number, y: number, z: number): number; + // (undocumented) + altitudeXYZW(x: number, y: number, z: number, w: number): number; + // (undocumented) + clone(result?: Plane3dByOriginAndUnitNormal): Plane3dByOriginAndUnitNormal; + cloneTransformed(transform: Transform): Plane3dByOriginAndUnitNormal | undefined; + // (undocumented) + static create(origin: Point3d, normal: Vector3d, result?: Plane3dByOriginAndUnitNormal): Plane3dByOriginAndUnitNormal | undefined; + static createPointPointVectorInPlane(pointA: Point3d, pointB: Point3d, vector: Vector3d): Plane3dByOriginAndUnitNormal | undefined; + static createXYPlane(origin?: Point3d): Plane3dByOriginAndUnitNormal; + static createYZPlane(origin?: Point3d): Plane3dByOriginAndUnitNormal; + static createZXPlane(origin?: Point3d): Plane3dByOriginAndUnitNormal; + // (undocumented) + static fromJSON(json?: any): Plane3dByOriginAndUnitNormal; + // (undocumented) + getNormalRef(): Vector3d; + // (undocumented) + getOriginRef(): Point3d; + // (undocumented) + isAlmostEqual(other: Plane3dByOriginAndUnitNormal): boolean; + isPointInPlane(spacePoint: Point3d): boolean; + // (undocumented) + projectPointToPlane(spacePoint: Point3d, result?: Point3d): Point3d; + set(origin: Point3d, normal: Vector3d): void; + setFrom(source: Plane3dByOriginAndUnitNormal): void; + // (undocumented) + setFromJSON(json?: any): void; + toJSON(): any; + // (undocumented) + velocity(spaceVector: Vector3d): number; + // (undocumented) + velocityXYZ(x: number, y: number, z: number): number; + // (undocumented) + weightedAltitude(spacePoint: Point4d): number; +} + +// @public +class Plane3dByOriginAndVectors implements BeJSONFunctions { + static createCapture(origin: Point3d, vectorU: Vector3d, vectorV: Vector3d, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + static createFromTransformColumnsXYAndLengths(transform: Transform, xLength: number | undefined, yLength: number | undefined, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + static createOriginAndTargets(origin: Point3d, targetU: Point3d, targetV: Point3d, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + // (undocumented) + static createOriginAndVectors(origin: Point3d, vectorU: Vector3d, vectorV: Vector3d, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + static createOriginAndVectorsArrays(origin: Float64Array, vectorU: Float64Array, vectorV: Float64Array, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + static createOriginAndVectorsWeightedArrays(originw: Float64Array, vectorUw: Float64Array, vectorVw: Float64Array, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + // (undocumented) + static createOriginAndVectorsXYZ(x0: number, y0: number, z0: number, ux: number, uy: number, uz: number, vx: number, vy: number, vz: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + static createXYPlane(result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + fractionToPoint(u: number, v: number, result?: Point3d): Point3d; + // (undocumented) + fractionToVector(u: number, v: number, result?: Vector3d): Vector3d; + // (undocumented) + static fromJSON(json?: any): Plane3dByOriginAndVectors; + // (undocumented) + isAlmostEqual(other: Plane3dByOriginAndVectors): boolean; + // (undocumented) + origin: Point3d; + // (undocumented) + setFromJSON(json?: any): void; + // (undocumented) + setOriginAndVectors(origin: Point3d, vectorU: Vector3d, vectorV: Vector3d): Plane3dByOriginAndVectors; + // (undocumented) + setOriginAndVectorsXYZ(x0: number, y0: number, z0: number, ux: number, uy: number, uz: number, vx: number, vy: number, vz: number): Plane3dByOriginAndVectors; + toJSON(): any; + // (undocumented) + vectorU: Vector3d; + // (undocumented) + vectorV: Vector3d; +} + +// @public +interface PlaneAltitudeEvaluator { + altitude(point: Point3d): number; + velocity(vector: Vector3d): number; + velocityXYZ(x: number, y: number, z: number): number; + weightedAltitude(point: Point4d): number; +} + +// @public +class PlaneByOriginAndVectors4d { + // (undocumented) + clone(result?: PlaneByOriginAndVectors4d): PlaneByOriginAndVectors4d; + // (undocumented) + static createOriginAndTargets3d(origin: Point3d, targetU: Point3d, targetV: Point3d, result?: PlaneByOriginAndVectors4d): PlaneByOriginAndVectors4d; + static createOriginAndVectors(origin: Point4d, vectorU: Point4d, vectorV: Point4d, result?: PlaneByOriginAndVectors4d): PlaneByOriginAndVectors4d; + static createOriginAndVectorsXYZW(x0: number, y0: number, z0: number, w0: number, ux: number, uy: number, uz: number, uw: number, vx: number, vy: number, vz: number, vw: number, result?: PlaneByOriginAndVectors4d): PlaneByOriginAndVectors4d; + // (undocumented) + static createXYPlane(result?: PlaneByOriginAndVectors4d): PlaneByOriginAndVectors4d; + // (undocumented) + fractionToPoint(u: number, v: number, result?: Point4d): Point4d; + // (undocumented) + isAlmostEqual(other: PlaneByOriginAndVectors4d): boolean; + // (undocumented) + origin: Point4d; + setFrom(other: PlaneByOriginAndVectors4d): void; + setOriginAndVectors(origin: Point4d, vectorU: Point4d, vectorV: Point4d): PlaneByOriginAndVectors4d; + setOriginAndVectorsXYZW(x0: number, y0: number, z0: number, w0: number, ux: number, uy: number, uz: number, uw: number, vx: number, vy: number, vz: number, vw: number): PlaneByOriginAndVectors4d; + // (undocumented) + vectorU: Point4d; + // (undocumented) + vectorV: Point4d; +} + +// @public +class PlaneSetParamsCache { + constructor(zLow: number, zHigh: number, localOrigin?: Point3d, isMask?: boolean, isInvisible?: boolean, focalLength?: number); + // (undocumented) + clipPlaneSet: UnionOfConvexClipPlaneSets; + // (undocumented) + focalLength: number; + // (undocumented) + invisible: boolean; + // (undocumented) + isMask: boolean; + // (undocumented) + limitValue: number; + // (undocumented) + localOrigin: Point3d; + // (undocumented) + zHigh: number; + // (undocumented) + zLow: number; +} + +// @public (undocumented) +class Point2d extends XY, implements BeJSONFunctions { + constructor(x?: number, y?: number); + // (undocumented) + addForwardLeft(tangentFraction: number, leftFraction: number, vector: Vector2d): Point2d; + // (undocumented) + clone(): Point2d; + static create(x?: number, y?: number, result?: Point2d): Point2d; + // (undocumented) + static createFrom(xy: XAndY | undefined, result?: Point2d): Point2d; + // (undocumented) + static createZero(result?: Point2d): Point2d; + crossProductToPoints(target1: XAndY, target2: XAndY): number; + // (undocumented) + dotVectorsToTargets(targetA: XAndY, targetB: XAndY): number; + // (undocumented) + forwardLeftInterpolate(tangentFraction: number, leftFraction: number, point: XAndY): Point2d; + // (undocumented) + fractionOfProjectionToLine(startPoint: Point2d, endPoint: Point2d, defaultFraction?: number): number; + // (undocumented) + static fromJSON(json?: XYProps): Point2d; + interpolate(fraction: number, other: XAndY, result?: Point2d): Point2d; + interpolateXY(fractionX: number, fractionY: number, other: XAndY, result?: Point2d): Point2d; + minus(vector: XAndY, result?: Point2d): Point2d; + plus(vector: XAndY, result?: Point2d): Point2d; + plus2Scaled(vectorA: XAndY, scalarA: number, vectorB: XAndY, scalarB: number, result?: Point2d): Point2d; + plus3Scaled(vectorA: XAndY, scalarA: number, vectorB: XAndY, scalarB: number, vectorC: XAndY, scalarC: number, result?: Point2d): Point2d; + plusScaled(vector: XAndY, scaleFactor: number, result?: Point2d): Point2d; + plusXY(dx?: number, dy?: number, result?: Point2d): Point2d; +} + +// @public (undocumented) +class Point2dArray { + // (undocumented) + static clonePoint2dArray(data: Point2d[]): Point2d[]; + // (undocumented) + static isAlmostEqual(dataA: undefined | Point2d[], dataB: undefined | Point2d[]): boolean; + static pointCountExcludingTrailingWraparound(data: XAndY[]): number; +} + +// @public +class Point2dArrayCarrier extends IndexedXYCollection { + constructor(data: Point2d[]); + // (undocumented) + atPoint2dIndex(index: number, result?: Point2d): Point2d | undefined; + // (undocumented) + atVector2dIndex(index: number, result?: Vector2d): Vector2d | undefined; + // (undocumented) + crossProductIndexIndexIndex(originIndex: number, indexA: number, indexB: number): number | undefined; + // (undocumented) + crossProductXAndYIndexIndex(origin: XAndY, indexA: number, indexB: number): number | undefined; + // (undocumented) + data: Point2d[]; + // (undocumented) + isValidIndex(index: number): boolean; + readonly length: number; + // (undocumented) + vectorIndexIndex(indexA: number, indexB: number, result?: Vector2d): Vector2d | undefined; + // (undocumented) + vectorXAndYIndex(origin: XAndY, indexB: number, result?: Vector2d): Vector2d | undefined; +} + +// @public +class Point3d extends XYZ { + constructor(x?: number, y?: number, z?: number); + clone(result?: Point3d): Point3d; + static create(x?: number, y?: number, z?: number, result?: Point3d): Point3d; + static createAdd2Scaled(pointA: XYAndZ, scaleA: number, pointB: XYAndZ, scaleB: number, result?: Point3d): Point3d; + static createAdd3Scaled(pointA: XYAndZ, scaleA: number, pointB: XYAndZ, scaleB: number, pointC: XYAndZ, scaleC: number, result?: Point3d): Point3d; + static createFrom(data: XYAndZ | XAndY | Float64Array, result?: Point3d): Point3d; + static createFromPacked(xyzData: Float64Array, pointIndex: number, result?: Point3d): Point3d | undefined; + static createFromPackedXYZW(xyzData: Float64Array, pointIndex: number, result?: Point3d): Point3d | undefined; + static createScale(source: XYAndZ, scale: number, result?: Point3d): Point3d; + static createZero(result?: Point3d): Point3d; + crossProductToPoints(pointA: Point3d, pointB: Point3d, result?: Vector3d): Vector3d; + crossProductToPointsXY(pointA: Point3d, pointB: Point3d): number; + dotVectorsToTargets(targetA: Point3d, targetB: Point3d): number; + fractionOfProjectionToLine(startPoint: Point3d, endPoint: Point3d, defaultFraction?: number): number; + // (undocumented) + static fromJSON(json?: XYZProps): Point3d; + interpolate(fraction: number, other: Point3d, result?: Point3d): Point3d; + interpolatePerpendicularXY(fraction: number, pointB: Point3d, fractionXYPerp: number, result?: Point3d): Point3d; + interpolatePointAndTangent(fraction: number, other: Point3d, tangentScale: number, result?: Ray3d): Ray3d; + interpolateXYZ(fractionX: number, fractionY: number, fractionZ: number, other: Point3d, result?: Point3d): Point3d; + minus(vector: XYAndZ, result?: Point3d): Point3d; + plus(vector: XYAndZ, result?: Point3d): Point3d; + plus2Scaled(vectorA: XYAndZ, scalarA: number, vectorB: XYZ, scalarB: number, result?: Point3d): Point3d; + plus3Scaled(vectorA: XYAndZ, scalarA: number, vectorB: XYAndZ, scalarB: number, vectorC: XYAndZ, scalarC: number, result?: Point3d): Point3d; + plusScaled(vector: XYAndZ, scaleFactor: number, result?: Point3d): Point3d; + plusXYZ(dx?: number, dy?: number, dz?: number, result?: Point3d): Point3d; + tripleProductToPoints(pointA: Point3d, pointB: Point3d, pointC: Point3d): number; +} + +// @public (undocumented) +class Point3dArray { + static centroid(points: IndexedXYZCollection, result?: Point3d): Point3d; + // (undocumented) + static clonePoint2dArray(data: XYAndZ[]): Point2d[]; + // (undocumented) + static clonePoint3dArray(data: XYAndZ[]): Point3d[]; + static closestPointIndex(data: XYAndZ[], spacePoint: XYAndZ): number; + static indexOfMostDistantPoint(points: Point3d[], spacePoint: XYZ, farVector: Vector3d): number | undefined; + static indexOfPointWithMaxCrossProductMagnitude(points: Point3d[], spacePoint: Point3d, vector: Vector3d, farVector: Vector3d): number | undefined; + // (undocumented) + static isAlmostEqual(dataA: Point3d[] | Float64Array | undefined, dataB: Point3d[] | Float64Array | undefined): boolean; + static isCloseToPlane(data: Point3d[] | Float64Array, plane: Plane3dByOriginAndUnitNormal, tolerance?: number): boolean; + // (undocumented) + static multiplyInPlace(transform: Transform, xyz: Float64Array): void; + // (undocumented) + static packToFloat64Array(data: Point3d[]): Float64Array; + static sumEdgeLengths(data: Point3d[] | Float64Array, addClosureEdge?: boolean): number; + static unpackNumbersToNestedArrays(data: Float64Array, numPerBlock: number): any[]; + static unpackNumbersToNestedArraysIJK(data: Float64Array, numPerBlock: number, numPerRow: number): any[]; + // (undocumented) + static unpackNumbersToPoint3dArray(data: Float64Array | number[]): Point3d[]; +} + +// @public +class Point3dArrayCarrier extends IndexedXYZCollection { + constructor(data: Point3d[]); + // (undocumented) + accumulateCrossProductIndexIndexIndex(originIndex: number, indexA: number, indexB: number, result: Vector3d): void; + // (undocumented) + atPoint3dIndex(index: number, result?: Point3d): Point3d | undefined; + // (undocumented) + atVector3dIndex(index: number, result?: Vector3d): Vector3d | undefined; + // (undocumented) + crossProductIndexIndexIndex(originIndex: number, indexA: number, indexB: number, result?: Vector3d): Vector3d | undefined; + // (undocumented) + crossProductXYAndZIndexIndex(origin: XYAndZ, indexA: number, indexB: number, result?: Vector3d): Vector3d | undefined; + // (undocumented) + data: Point3d[]; + // (undocumented) + isValidIndex(index: number): boolean; + readonly length: number; + // (undocumented) + vectorIndexIndex(indexA: number, indexB: number, result?: Vector3d): Vector3d | undefined; + // (undocumented) + vectorXYAndZIndex(origin: XYAndZ, indexB: number, result?: Vector3d): Vector3d | undefined; +} + +// @public +class Point4d implements BeJSONFunctions { + protected constructor(x?: number, y?: number, z?: number, w?: number); + altitude(point: Point3d): number; + // (undocumented) + clone(result?: Point4d): Point4d; + // (undocumented) + static create(x?: number, y?: number, z?: number, w?: number, result?: Point4d): Point4d; + static createAdd2Scaled(vectorA: Point4d, scalarA: number, vectorB: Point4d, scalarB: number, result?: Point4d): Point4d; + static createAdd3Scaled(vectorA: Point4d, scalarA: number, vectorB: Point4d, scalarB: number, vectorC: Point4d, scalarC: number, result?: Point4d): Point4d; + static createFromPackedXYZW(data: Float64Array, xIndex?: number, result?: Point4d): Point4d; + // (undocumented) + static createFromPointAndWeight(xyz: XYAndZ, w: number): Point4d; + static createPlanePointPointZ(pointA: Point4d, pointB: Point4d, result?: Point4d): Point4d; + static createRealDerivativePlane3dByOriginAndVectorsDefault000(x: number, y: number, z: number, w: number, dx: number, dy: number, dz: number, dw: number, ddx: number, ddy: number, ddz: number, ddw: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + static createRealDerivativeRay3dDefault000(x: number, y: number, z: number, w: number, dx: number, dy: number, dz: number, dw: number, result?: Ray3d): Ray3d; + static createRealPoint3dDefault000(x: number, y: number, z: number, w: number, result?: Point3d): Point3d; + // (undocumented) + static createZero(): Point4d; + // (undocumented) + crossWeightedMinus(other: Point4d, result?: Vector3d): Vector3d; + static determinantIndexed3X3(pointA: Point4d, pointB: Point4d, pointC: Point4d, i: number, j: number, k: number): number; + distanceSquaredXYZW(other: Point4d): number; + distanceXYZW(other: Point4d): number; + dotProduct(other: Point4d): number; + dotProductXYZW(x: number, y: number, z: number, w: number): number; + dotVectorsToTargets(targetA: Point4d, targetB: Point4d): number; + // (undocumented) + static fromJSON(json?: Point4dProps): Point4d; + interpolate(fraction: number, pointB: Point4d, result?: Point4d): Point4d; + // (undocumented) + static interpolateQuaternions(quaternion0: Point4d, fractionParameter: number, quaternion1: Point4d, result?: Point4d): Point4d; + // (undocumented) + isAlmostEqual(other: Point4d): boolean; + isAlmostEqualXYZW(x: number, y: number, z: number, w: number): boolean; + // (undocumented) + readonly isAlmostZero: boolean; + // (undocumented) + magnitudeSquaredXYZ(): number; + // (undocumented) + magnitudeXYZW(): number; + // (undocumented) + maxAbs(): number; + maxDiff(other: Point4d): number; + // (undocumented) + minus(other: Point4d, result?: Point4d): Point4d; + negate(result?: Point4d): Point4d; + // (undocumented) + normalizeQuaternion(): number; + normalizeWeight(result?: Point4d): Point4d | undefined; + normalizeXYZW(result?: Point4d): Point4d | undefined; + static perpendicularPoint4dPlane(pointA: Point4d, pointB: Point4d, pointC: Point4d): Point4d; + // (undocumented) + plus(other: Point4d, result?: Point4d): Point4d; + plus2Scaled(vectorA: Point4d, scalarA: number, vectorB: Point4d, scalarB: number, result?: Point4d): Point4d; + plus3Scaled(vectorA: Point4d, scalarA: number, vectorB: Point4d, scalarB: number, vectorC: Point4d, scalarC: number, result?: Point4d): Point4d; + plusScaled(vector: Point4d, scaleFactor: number, result?: Point4d): Point4d; + // (undocumented) + radiansToPoint4dXYZW(other: Point4d): number | undefined; + realDistanceXY(other: Point4d): number | undefined; + realPoint(result?: Point3d): Point3d | undefined; + realPointDefault000(result?: Point3d): Point3d; + // (undocumented) + safeDivideOrNull(denominator: number, result?: Point4d): Point4d | undefined; + scale(scale: number, result?: Point4d): Point4d; + set(x?: number, y?: number, z?: number, w?: number): Point4d; + // (undocumented) + setFrom(other: Point4d): Point4d; + // (undocumented) + setFromJSON(json?: Point4dProps): void; + toJSON(): Point4dProps; + // (undocumented) + toPlane3dByOriginAndUnitNormal(result?: Plane3dByOriginAndUnitNormal): Plane3dByOriginAndUnitNormal | undefined; + static unitW(): Point4d; + static unitX(): Point4d; + static unitY(): Point4d; + static unitZ(): Point4d; + velocity(vector: Vector3d): number; + velocityXYZ(x: number, y: number, z: number): number; + // (undocumented) + w: number; + weightedAltitude(point: Point4d): number; + // (undocumented) + x: number; + // (undocumented) + xyzw: Float64Array; + // (undocumented) + y: number; + // (undocumented) + z: number; +} + +// @public (undocumented) +class Point4dArray { + // (undocumented) + static isAlmostEqual(dataA: Point4d[] | Float64Array | undefined, dataB: Point4d[] | Float64Array | undefined): boolean; + static isCloseToPlane(data: Point4d[] | Float64Array, plane: Plane3dByOriginAndUnitNormal, tolerance?: number): boolean; + static multiplyInPlace(transform: Transform, xyzw: Float64Array): void; + static packPointsAndWeightsToFloat64Array(points: Point3d[], weights: number[], result?: Float64Array): Float64Array; + // (undocumented) + static packToFloat64Array(data: Point4d[], result?: Float64Array): Float64Array; + static unpackFloat64ArrayToPointsAndWeights(data: Float64Array, points: Point3d[], weights: number[], pointFormatter?: (x: number, y: number, z: number) => any): void; + static unpackToPoint4dArray(data: Float64Array): Point4d[]; +} + +// @public +class PointString3d extends GeometryQuery, implements BeJSONFunctions { + addPoint(point: Point3d): void; + addPoints(...points: any[]): void; + clear(): void; + // (undocumented) + clone(): PointString3d; + // (undocumented) + cloneTransformed(transform: Transform): PointString3d; + closestPoint: { + index: number; + xyz: Point3d; + } + static create(...points: any[]): PointString3d; + static createFloat64Array(xyzData: Float64Array): PointString3d; + static createPoints(points: Point3d[]): PointString3d; + dispatchToGeometryHandler(handler: GeometryHandler): any; + extendRange(rangeToExtend: Range3d, transform?: Transform): void; + // (undocumented) + static fromJSON(json?: any): PointString3d; + isAlmostEqual(other: GeometryQuery): boolean; + isInPlane(plane: Plane3dByOriginAndUnitNormal): boolean; + // (undocumented) + isSameGeometryClass(other: GeometryQuery): boolean; + numPoints(): number; + pointAt(i: number, result?: Point3d): Point3d | undefined; + readonly points: Point3d[]; + popPoint(): void; + reverseInPlace(): void; + // (undocumented) + setFrom(other: PointString3d): void; + // (undocumented) + setFromJSON(json?: any): void; + toJSON(): any; + tryTransformInPlace(transform: Transform): boolean; +} + +// @public +class Polyface extends GeometryQuery { + protected constructor(data: PolyfaceData); + static areIndicesValid(indices: number[] | undefined, indexPositionA: number, indexPositionB: number, data: any | undefined, dataLength: number): boolean; + abstract createVisitor(_numWrap: number): PolyfaceVisitor; + // (undocumented) + data: PolyfaceData; + // (undocumented) + twoSided: boolean; +} + +// @public +class PolyfaceAuxData { + constructor(channels: AuxChannel[], indices: number[]); + // (undocumented) + channels: AuxChannel[]; + // (undocumented) + clone(): PolyfaceAuxData; + // (undocumented) + createForVisitor(): PolyfaceAuxData; + // (undocumented) + indices: number[]; + // (undocumented) + isAlmostEqual(other: PolyfaceAuxData, tol?: number): boolean; +} + +// @public +class PolyfaceBuilder extends NullGeometryHandler { + addBetweenLineStringsWithRuleEdgeNormals(lineStringA: LineString3d, vA: number, lineStringB: LineString3d, vB: number, addClosure?: boolean): void; + addBetweenLineStringsWithStoredIndices(lineStringA: LineString3d, lineStringB: LineString3d): void; + addBetweenTransformedLineStrings(curves: AnyCurve, transformA: Transform, transformB: Transform, addClosure?: boolean): void; + // (undocumented) + addBox(box: Box): void; + // (undocumented) + addCone(cone: Cone): void; + addCoordinateFacets(pointArray: Point3d[][], paramArray?: Point2d[][], normalArray?: Vector3d[][], endFace?: boolean): void; + // (undocumented) + addGeometryQuery(g: GeometryQuery): void; + addGraph(graph: HalfEdgeGraph, needParams: boolean, acceptFaceFunction?: HalfEdgeToBooleanFunction): void; + addIndexedPolyface(source: IndexedPolyface, reversed: boolean, transform?: Transform): void; + // (undocumented) + addLinearSweep(surface: LinearSweep): void; + // (undocumented) + addLinearSweepLineStringsXYZOnly(contour: AnyCurve, vector: Vector3d): void; + addPolygon(points: Point3d[], numPointsToUse?: number): void; + addQuadFacet(points: Point3d[], params?: Point2d[], normals?: Vector3d[]): void; + // (undocumented) + addRotationalSweep(surface: RotationalSweep): void; + // (undocumented) + addRuledSweep(surface: RuledSweep): boolean; + // (undocumented) + addSphere(sphere: Sphere, strokeCount?: number): void; + // (undocumented) + addTorusPipe(surface: TorusPipe, phiStrokeCount?: number, thetaStrokeCount?: number): void; + addTransformedUnitBox(transform: Transform): void; + addTriangleFacet(points: Point3d[], params?: Point2d[], normals?: Vector3d[]): void; + addTriangleFan(conePoint: Point3d, ls: LineString3d, toggle: boolean): void; + addTrianglesInUncheckedConvexPolygon(ls: LineString3d, toggle: boolean): void; + addUVGridBody(surface: UVSurface, numU: number, numV: number, uMap?: Segment1d, vMap?: Segment1d): void; + applyStrokeCountsToCurvePrimitives(data: AnyCurve | GeometryQuery): void; + claimPolyface(compress?: boolean): IndexedPolyface; + // (undocumented) + static create(options?: StrokeOptions): PolyfaceBuilder; + endFace(): boolean; + findOrAddNormalnLineString(ls: LineString3d, index: number, transform?: Transform, priorIndexA?: number, priorIndexB?: number): number | undefined; + findOrAddParamInGrowableXYArray(data: GrowableXYArray, index: number): number | undefined; + findOrAddParamInLineString(ls: LineString3d, index: number, v: number, priorIndexA?: number, priorIndexB?: number): number | undefined; + findOrAddParamXY(x: number, y: number): number; + findOrAddPoint(xyz: Point3d): number; + findOrAddPointInLineString(ls: LineString3d, index: number, transform?: Transform, priorIndex?: number): number | undefined; + findOrAddPointXYZ(x: number, y: number, z: number): number; + // (undocumented) + static graphToPolyface(graph: HalfEdgeGraph, options?: StrokeOptions, acceptFaceFunction?: HalfEdgeToBooleanFunction): IndexedPolyface; + // (undocumented) + handleBox(g: Box): any; + // (undocumented) + handleCone(g: Cone): any; + // (undocumented) + handleLinearSweep(g: LinearSweep): any; + // (undocumented) + handleRotationalSweep(g: RotationalSweep): any; + // (undocumented) + handleRuledSweep(g: RuledSweep): any; + // (undocumented) + handleSphere(g: Sphere): any; + // (undocumented) + handleTorusPipe(g: TorusPipe): any; + // (undocumented) + readonly options: StrokeOptions; + // (undocumented) + toggleReversedFacetFlag(): void; +} + +// WARNING: planarityLocalRelTol has incomplete type information +// @public +class PolyfaceData { + constructor(needNormals?: boolean, needParams?: boolean, needColors?: boolean); + // (undocumented) + auxData: PolyfaceAuxData | undefined; + // (undocumented) + clone(): PolyfaceData; + // (undocumented) + color: number[] | undefined; + // (undocumented) + readonly colorCount: number; + // (undocumented) + colorIndex: number[] | undefined; + // (undocumented) + compress(): void; + copyNormalTo(i: number, dest: Vector3d): void; + copyParamTo(i: number, dest: Point2d): void; + copyPointTo(i: number, dest: Point3d): void; + // (undocumented) + edgeVisible: boolean[]; + face: FacetFaceData[]; + readonly faceCount: number; + gatherIndexedData(other: PolyfaceData, index0: number, index1: number, numWrap: number): void; + getColor(i: number): number; + getEdgeVisible(i: number): boolean; + getNormal(i: number): Vector3d | undefined; + getParam(i: number): Point2d; + getPoint(i: number): Point3d | undefined; + // (undocumented) + readonly indexCount: number; + // (undocumented) + isAlmostEqual(other: PolyfaceData): boolean; + isAlmostEqualParamIndexUV(index: number, u: number, v: number): boolean; + static isValidFacetStartIndexArray(facetStartIndex: number[]): boolean; + // (undocumented) + normal: GrowableXYZArray | undefined; + // (undocumented) + readonly normalCount: number; + // (undocumented) + normalIndex: number[] | undefined; + // (undocumented) + param: Point2d[] | undefined; + // (undocumented) + readonly paramCount: number; + // (undocumented) + paramIndex: number[] | undefined; + // (undocumented) + point: GrowableXYZArray; + // (undocumented) + readonly pointCount: number; + // (undocumented) + pointIndex: number[]; + // (undocumented) + range(result?: Range3d, transform?: Transform): Range3d; + // (undocumented) + readonly requireNormals: boolean; + // (undocumented) + resizeAllDataArrays(length: number): void; + reverseIndices(facetStartIndex?: number[]): void; + // (undocumented) + reverseNormals(): void; + // (undocumented) + trimAllIndexArrays(length: number): void; + // (undocumented) + tryTransformInPlace(transform: Transform): boolean; +} + +// @public +class PolyfaceQuery { + static computePrincipalAreaMoments(source: Polyface): MomentData | undefined; + static indexedPolyfaceToLoops(polyface: Polyface): BagOfCurves; + static isPolyfaceClosedByEdgePairing(source: Polyface): boolean; + // (undocumented) + static sumFacetAreas(source: Polyface | PolyfaceVisitor): number; + static sumFacetSecondAreaMomentProducts(source: Polyface | PolyfaceVisitor, origin: Point3d): Matrix4d; + static sumTetrahedralVolumes(source: Polyface | PolyfaceVisitor, origin?: Point3d): number; + static visitorToLoop(visitor: PolyfaceVisitor): Loop; +} + +// @public +interface PolyfaceVisitor extends PolyfaceData { + // (undocumented) + clientAuxIndex(i: number): number; + // (undocumented) + clientColorIndex(i: number): number; + // (undocumented) + clientNormalIndex(i: number): number; + // (undocumented) + clientParamIndex(i: number): number; + // (undocumented) + clientPointIndex(i: number): number; + // (undocumented) + currentReadIndex(): number; + // (undocumented) + moveToNextFacet(): boolean; + // (undocumented) + moveToReadIndex(index: number): boolean; + // (undocumented) + reset(): void; +} + +// @public +class PolygonOps { + static addSecondMomentAreaProducts(points: IndexedXYZCollection, origin: Point3d, moments: Matrix4d): void; + static area(points: Point3d[]): number; + // (undocumented) + static areaNormal(points: Point3d[], result?: Vector3d): Vector3d; + static areaNormalGo(points: IndexedXYZCollection, result?: Vector3d): Vector3d | undefined; + static areaXY(points: Point3d[]): number; + // (undocumented) + static centroidAndAreaXY(points: Point2d[], centroid: Point2d): number | undefined; + // (undocumented) + static centroidAreaNormal(points: Point3d[]): Ray3d | undefined; + static parity(pPoint: Point2d, pPointArray: Point2d[] | Point3d[], tol?: number): number; + static parityVectorTest(pPoint: Point2d, theta: number, pPointArray: Point2d[] | Point3d[], tol: number): number | undefined; + static parityXTest(pPoint: Point2d, pPointArray: Point2d[] | Point3d[], tol: number): number | undefined; + static parityYTest(pPoint: Point2d, pPointArray: Point2d[] | Point3d[], tol: number): number | undefined; + static sumTriangleAreas(points: Point3d[]): number; + static sumTriangleAreasXY(points: Point3d[]): number; + static testXYPolygonTurningDirections(pPointArray: Point2d[] | Point3d[]): number; + // (undocumented) + static unitNormal(points: IndexedXYZCollection, result: Vector3d): boolean; +} + +// @public (undocumented) +class PowerPolynomial { + // (undocumented) + static accumulate(coffP: Float64Array, coffQ: Float64Array, scaleQ: number): number; + // (undocumented) + static degreeKnownEvaluate(coff: Float64Array, degree: number, x: number): number; + // (undocumented) + static evaluate(coff: Float64Array, x: number): number; + // (undocumented) + static zero(coff: Float64Array): void; +} + +// @public +class Quadrature { + // (undocumented) + static readonly gaussW1Interval01: Float64Array; + // (undocumented) + static readonly gaussW2Interval01: Float64Array; + // (undocumented) + static readonly gaussW3Interval01: Float64Array; + // (undocumented) + static readonly gaussW4Interval01: Float64Array; + // (undocumented) + static readonly gaussW5Interval01: Float64Array; + // (undocumented) + static readonly gaussX1Interval01: Float64Array; + // (undocumented) + static readonly gaussX2Interval01: Float64Array; + // (undocumented) + static readonly gaussX3Interval01: Float64Array; + // (undocumented) + static readonly gaussX4Interval01: Float64Array; + // (undocumented) + static readonly gaussX5Interval01: Float64Array; + static mapWeights(xA: number, h: number, xRef: Float64Array, wRef: Float64Array, xMapped: Float64Array, wMapped: Float64Array): number; + // (undocumented) + static setupGauss1(xA: number, xB: number, xMapped: Float64Array, wMapped: Float64Array): number; + // (undocumented) + static setupGauss2(xA: number, xB: number, xMapped: Float64Array, wMapped: Float64Array): number; + // (undocumented) + static setupGauss3(xA: number, xB: number, xMapped: Float64Array, wMapped: Float64Array): number; + // (undocumented) + static setupGauss4(xA: number, xB: number, xMapped: Float64Array, wMapped: Float64Array): number; + static setupGauss5(xA: number, xB: number, xMapped: Float64Array, wMapped: Float64Array): number; + static sum1(xx: Float64Array, ww: Float64Array, n: number, f: (x: number) => number): number; +} + +// @public (undocumented) +export function quotientDerivative2(ddg: number, dh: number, ddh: number, f: number, df: number, divh: number): number; + +// @public (undocumented) +class Range1d extends RangeBase { + clone(result?: this): this; + containsRange(other: Range1d): boolean; + containsX(x: number): boolean; + static createArray(values: Float64Array | number[], result?: T): T; + static createFrom(other: T, result?: T): T; + static createNull(result?: T): T; + static createX(x: number, result?: T): T; + static createXX(xA: number, xB: number, result?: T): T; + static createXXOrCorrectToNull(xA: number, xB: number, result?: T): T; + distanceToRange(other: Range1d): number; + distanceToX(x: number): number; + expandInPlace(delta: number): void; + extendArray(values: Float64Array | number[]): void; + extendArraySubset(values: Float64Array | number[], beginIndex: number, numValue: number): void; + extendRange(other: Range1d): void; + extendX(x: number): void; + fractionToPoint(fraction: number): number; + // (undocumented) + static fromJSON(json?: Range1dProps): T; + // (undocumented) + high: number; + intersect(other: Range1d, result?: Range1d): Range1d; + intersectsRange(other: Range1d): boolean; + isAlmostEqual(other: Range1d): boolean; + readonly isAlmostZeroLength: boolean; + readonly isNull: boolean; + readonly isSinglePoint: boolean; + length(): number; + // (undocumented) + low: number; + maxAbs(): number; + scaleAboutCenterInPlace(scaleFactor: number): void; + setFrom(other: Range1d): void; + setFromJSON(json: Range1dProps): void; + // (undocumented) + setNull(): void; + setX(x: number): void; + toJSON(): Range1dProps; + union(other: Range1d, result?: Range1d): Range1d; +} + +// @public +class Range1dArray { + static countContainingRanges(data: Range1d[], value: number): number; + static differenceSorted(dataA: Range1d[], dataB: Range1d[]): Range1d[]; + static getBreaks(data: Range1d[], result?: GrowableFloat64Array, sort?: boolean, compress?: boolean): GrowableFloat64Array; + // (undocumented) + static intersectSorted(dataA: Range1d[], dataB: Range1d[]): Range1d[]; + static isSorted(data: Range1d[], strict?: boolean): boolean; + // (undocumented) + static paritySorted(dataA: Range1d[], dataB: Range1d[]): Range1d[]; + // (undocumented) + static simplifySortParity(data: Range1d[], removeZeroLengthRanges?: boolean): void; + static simplifySortUnion(data: Range1d[], removeZeroLengthRanges?: boolean): void; + static sort(data: Range1d[]): void; + static sumLengths(data: Range1d[]): number; + static testParity(data: Range1d[], value: number): boolean; + static testUnion(data: Range1d[], value: number): boolean; + // (undocumented) + static unionSorted(dataA: Range1d[], dataB: Range1d[]): Range1d[]; +} + +// @public (undocumented) +class Range2d extends RangeBase, implements LowAndHighXY { + constructor(lowx?: number, lowy?: number, highx?: number, highy?: number); + // (undocumented) + readonly bottom: number; + // (undocumented) + readonly center: Point2d; + clone(result?: this): this; + containsPoint(point: XAndY): boolean; + containsRange(other: LowAndHighXY): boolean; + containsXY(x: number, y: number): boolean; + static createArray(points: Point2d[], result?: T): T; + // (undocumented) + static createFrom(other: LowAndHighXY, result?: T): T; + static createNull(result?: T): T; + static createXY(x: number, y: number, result?: T): T; + static createXYXY(xA: number, yA: number, xB: number, yB: number, result?: T): T; + static createXYXYOrCorrectToNull(xA: number, yA: number, xB: number, yB: number, result?: T): T; + diagonal(result?: Vector2d): Vector2d; + diagonalFractionToPoint(fraction: number, result?: Point2d): Point2d; + distanceToPoint(point: XAndY): number; + distanceToRange(other: LowAndHighXY): number; + expandInPlace(delta: number): void; + extendPoint(point: XAndY): void; + extendRange(other: LowAndHighXY): void; + extendTransformedXY(transform: Transform, x: number, y: number): void; + extendXY(x: number, y: number): void; + fractionToPoint(fractionX: number, fractionY: number, result?: Point2d): Point2d; + // (undocumented) + freeze(): void; + static fromArrayBuffer(buffer: ArrayBuffer): T; + static fromFloat64Array(f64: Float64Array): T; + // (undocumented) + static fromJSON(json?: Range2dProps): T; + // (undocumented) + readonly height: number; + // (undocumented) + high: Point2d; + intersect(other: LowAndHighXY, result?: Range2d): Range2d; + intersectsRange(other: LowAndHighXY): boolean; + // (undocumented) + isAlmostEqual(other: Range2d): boolean; + readonly isAlmostZeroX: boolean; + readonly isAlmostZeroY: boolean; + readonly isNull: boolean; + readonly isSinglePoint: boolean; + // (undocumented) + readonly left: number; + // (undocumented) + low: Point2d; + maxAbs(): number; + // (undocumented) + readonly right: number; + scaleAboutCenterInPlace(scaleFactor: number): void; + // (undocumented) + setFrom(other: LowAndHighXY): void; + setFromJSON(json: Range2dProps): void; + // (undocumented) + setNull(): void; + setXY(x: number, y: number): void; + // (undocumented) + static toFloat64Array(val: LowAndHighXY): Float64Array; + // (undocumented) + toJSON(): Range2dProps; + // (undocumented) + readonly top: number; + union(other: LowAndHighXY, result?: Range2d): Range2d; + // (undocumented) + readonly width: number; + xLength(): number; + yLength(): number; +} + +// @public (undocumented) +class Range3d extends RangeBase, implements LowAndHighXYZ, BeJSONFunctions { + constructor(lowx?: number, lowy?: number, lowz?: number, highx?: number, highy?: number, highz?: number); + // (undocumented) + readonly back: number; + // (undocumented) + readonly bottom: number; + readonly center: Point3d; + // (undocumented) + clone(result?: this): this; + containsPoint(point: Point3d): boolean; + containsPointXY(point: Point3d): boolean; + containsRange(other: Range3d): boolean; + containsXYZ(x: number, y: number, z: number): boolean; + corners(): Point3d[]; + static create(...point: Point3d[]): Range3d; + static createArray(points: Point3d[], result?: T): T; + // (undocumented) + static createFrom(other: Range3d, result?: T): T; + static createInverseTransformedArray(transform: Transform, points: Point3d[]): T; + static createNull(result?: T): T; + static createRange2d(range: Range2d, z?: number, result?: T): T; + static createTransformed(transform: Transform, ...point: Point3d[]): T; + static createTransformedArray(transform: Transform, points: Point3d[]): T; + static createXYZ(x: number, y: number, z: number, result?: T): T; + static createXYZXYZ(xA: number, yA: number, zA: number, xB: number, yB: number, zB: number, result?: T): T; + static createXYZXYZOrCorrectToNull(xA: number, yA: number, zA: number, xB: number, yB: number, zB: number, result?: T): T; + // (undocumented) + readonly depth: number; + diagonal(result?: Vector3d): Vector3d; + diagonalFractionToPoint(fraction: number, result?: Point3d): Point3d; + distanceToPoint(point: XYAndZ): number; + distanceToRange(other: Range3d): number; + ensureMinLengths(min?: number): void; + expandInPlace(delta: number): void; + extend(...point: Point3d[]): void; + extendArray(points: Point3d[] | GrowableXYZArray, transform?: Transform): void; + extendInverseTransformedArray(points: Point3d[] | GrowableXYZArray, transform: Transform): void; + extendInverseTransformedXYZ(transform: Transform, x: number, y: number, z: number): boolean; + extendPoint(point: Point3d): void; + extendRange(other: LowAndHighXYZ): void; + extendTransformedPoint(transform: Transform, point: Point3d): void; + extendTransformedXYZ(transform: Transform, x: number, y: number, z: number): void; + extendTransformedXYZW(transform: Transform, x: number, y: number, z: number, w: number): void; + extendTransformTransformedXYZ(transformA: Transform, transformB: Transform, x: number, y: number, z: number): void; + extendXYZ(x: number, y: number, z: number): void; + extendXYZW(x: number, y: number, z: number, w: number): void; + fractionToPoint(fractionX: number, fractionY: number, fractionZ: number, result?: Point3d): Point3d; + // (undocumented) + freeze(): void; + static fromArrayBuffer(buffer: ArrayBuffer): T; + static fromFloat64Array(f64: Float64Array): T; + // (undocumented) + static fromJSON(json?: Range3dProps): T; + // (undocumented) + readonly front: number; + getLocalToWorldTransform(result?: Transform): Transform; + getNpcToWorldRangeTransform(result?: Transform): Transform; + // (undocumented) + readonly height: number; + // (undocumented) + high: Point3d; + intersect(other: Range3d, result?: Range3d): Range3d; + intersectsRange(other: Range3d): boolean; + intersectsRangeXY(other: Range3d): boolean; + isAlmostEqual(other: Range3d): boolean; + readonly isAlmostZeroX: boolean; + readonly isAlmostZeroY: boolean; + readonly isAlmostZeroZ: boolean; + readonly isNull: boolean; + readonly isSinglePoint: boolean; + // (undocumented) + readonly left: number; + localToWorld(xyz: XYAndZ, result?: Point3d): Point3d | undefined; + localToWorldArrayInPlace(points: Point3d[]): boolean; + localXYZToWorld(fractionX: number, fractionY: number, fractionZ: number, result?: Point3d): Point3d | undefined; + // (undocumented) + low: Point3d; + maxAbs(): number; + maxLength(): number; + // (undocumented) + readonly right: number; + scaleAboutCenterInPlace(scaleFactor: number): void; + setFrom(other: Range3d): void; + // (undocumented) + setFromJSON(json?: Range3dProps): void; + setNull(): void; + setXYZ(x: number, y: number, z: number): void; + // (undocumented) + static toFloat64Array(val: LowAndHighXYZ): Float64Array; + toJSON(): Range3dProps; + // (undocumented) + readonly top: number; + union(other: Range3d, result?: Range3d): Range3d; + // (undocumented) + readonly width: number; + worldToLocal(point: Point3d, result?: Point3d): Point3d | undefined; + worldToLocalArrayInPlace(point: Point3d[]): boolean; + xLength(): number; + yLength(): number; + zLength(): number; +} + +// @public (undocumented) +class RangeBase { + // (undocumented) + protected static readonly _EXTREME_NEGATIVE: number; + // (undocumented) + protected static readonly _EXTREME_POSITIVE: number; + // (undocumented) + static coordinateToRangeAbsoluteDistance(x: number, low: number, high: number): number; + // (undocumented) + static isExtremePoint2d(xy: Point2d): boolean; + // (undocumented) + static isExtremePoint3d(xyz: Point3d): boolean; + // (undocumented) + static isExtremeValue(x: number): boolean; + protected static npcScaleFactor(low: number, high: number): number; + static rangeToRangeAbsoluteDistance(lowA: number, highA: number, lowB: number, highB: number): number; +} + +// @public (undocumented) +class Ray2d { + // (undocumented) + ccwPerpendicularRay(): Ray2d; + // (undocumented) + static createOriginAndDirection(origin: Point2d, direction: Vector2d): Ray2d; + // (undocumented) + static createOriginAndDirectionCapture(origin: Point2d, direction: Vector2d): Ray2d; + // (undocumented) + static createOriginAndTarget(origin: Point2d, target: Point2d): Ray2d; + // (undocumented) + cwPerpendicularRay(): Ray2d; + // (undocumented) + readonly direction: Vector2d; + fractionToPoint(f: number): Point2d; + intersectUnboundedLine(linePointA: Point2d, linePointB: Point2d, fraction: number[], dhds: number[]): boolean; + // (undocumented) + normalizeDirectionInPlace(): boolean; + // (undocumented) + readonly origin: Point2d; + parallelRay(leftFraction: number): Ray2d; + perpendicularProjectionFraction(point: Point2d): number; + projectionFraction(point: Point2d): number; +} + +// @public +class Ray3d implements BeJSONFunctions { + // (undocumented) + a?: number; + clone(result?: Ray3d): Ray3d; + cloneTransformed(transform: Transform): Ray3d; + // (undocumented) + static create(origin: Point3d, direction: Vector3d, result?: Ray3d): Ray3d; + static createCapture(origin: Point3d, direction: Vector3d): Ray3d; + static createPointVectorNumber(origin: Point3d, direction: Vector3d, a: number, result?: Ray3d): Ray3d; + static createStartEnd(origin: Point3d, target: Point3d, result?: Ray3d): Ray3d; + static createWeightedDerivative(weightedPoint: Float64Array, weightedDerivative: Float64Array, result?: Ray3d): Ray3d | undefined; + // (undocumented) + static createXAxis(): Ray3d; + static createXYZUVW(originX: number, originY: number, originZ: number, directionX: number, directionY: number, directionZ: number, result?: Ray3d): Ray3d; + // (undocumented) + static createYAxis(): Ray3d; + // (undocumented) + static createZAxis(): Ray3d; + // (undocumented) + static createZero(result?: Ray3d): Ray3d; + // (undocumented) + direction: Vector3d; + distance(spacePoint: Point3d): number; + // (undocumented) + dotProductToPoint(spacePoint: Point3d): number; + fractionToPoint(fraction: number): Point3d; + // (undocumented) + static fromJSON(json?: any): Ray3d; + // (undocumented) + getDirectionRef(): Vector3d; + // (undocumented) + getOriginRef(): Point3d; + intersectionWithPlane(plane: Plane3dByOriginAndUnitNormal, result?: Point3d): number | undefined; + // (undocumented) + isAlmostEqual(other: Ray3d): boolean; + // (undocumented) + origin: Point3d; + perpendicularPartOfVectorToTarget(targetPoint: XYAndZ, result?: Vector3d): Vector3d; + // (undocumented) + pointToFraction(spacePoint: Point3d): number; + // (undocumented) + projectPointToRay(spacePoint: Point3d): Point3d; + set(origin: Point3d, direction: Vector3d): void; + setFrom(source: Ray3d): void; + setFromJSON(json?: any): void; + toJSON(): any; + toRigidZFrame(): Transform | undefined; + transformInPlace(transform: Transform): void; + // (undocumented) + tryNormalizeInPlaceWithAreaWeight(a: number): boolean; + trySetDirectionMagnitudeInPlace(magnitude?: number): boolean; +} + +// @public +class RecurseToCurvesGeometryHandler extends GeometryHandler { + // (undocumented) + handleArc3d(_g: Arc3d): any; + // (undocumented) + handleBagOfCurves(g: BagOfCurves): any; + // (undocumented) + handleBezierCurve3d(_g: BezierCurve3d): any; + // (undocumented) + handleBezierCurve3dH(_g: BezierCurve3dH): any; + // (undocumented) + handleBox(_g: Box): any; + // (undocumented) + handleBSplineCurve3d(_g: BSplineCurve3d): any; + // (undocumented) + handleBSplineCurve3dH(_g: BSplineCurve3dH): any; + // (undocumented) + handleBSplineSurface3d(_g: BSplineSurface3d): any; + // (undocumented) + handleBSplineSurface3dH(_g: BSplineSurface3dH): any; + // (undocumented) + handleChildren(g: GeometryQuery): any; + // (undocumented) + handleCone(_g: Cone): any; + // (undocumented) + handleCoordinateXYZ(_g: CoordinateXYZ): any; + // (undocumented) + handleCurveCollection(_g: CurveCollection): any; + // (undocumented) + handleIndexedPolyface(_g: IndexedPolyface): any; + // (undocumented) + handleLinearSweep(_g: LinearSweep): any; + // (undocumented) + handleLineSegment3d(_g: LineSegment3d): any; + // (undocumented) + handleLineString3d(_g: LineString3d): any; + // (undocumented) + handleLoop(g: Loop): any; + // (undocumented) + handleParityRegion(g: ParityRegion): any; + // (undocumented) + handlePath(g: Path): any; + // (undocumented) + handlePointString3d(_g: PointString3d): any; + // (undocumented) + handleRotationalSweep(_g: RotationalSweep): any; + // (undocumented) + handleRuledSweep(_g: RuledSweep): any; + // (undocumented) + handleSphere(_g: Sphere): any; + // (undocumented) + handleTorusPipe(_g: TorusPipe): any; + // (undocumented) + handleTransitionSpiral(_g: TransitionSpiral3d): any; + // (undocumented) + handleUnionRegion(g: UnionRegion): any; +} + +// @public +class RecursiveCurveProcessor { + protected constructor(); + // (undocumented) + announceBagOfCurves(data: BagOfCurves, _indexInParent?: number): void; + announceCurvePrimitive(_data: CurvePrimitive, _indexInParent?: number): void; + announceLoop(data: Loop, _indexInParent?: number): void; + announceParityRegion(data: ParityRegion, _indexInParent?: number): void; + announcePath(data: Path, _indexInParent?: number): void; + announceUnexpected(_data: AnyCurve, _indexInParent: number): void; + announceUnionRegion(data: UnionRegion, _indexInParent?: number): void; +} + +// @public +class RecursiveCurveProcessorWithStack extends RecursiveCurveProcessor { + protected constructor(); + // (undocumented) + protected _stack: CurveCollection[]; + // (undocumented) + announceBagOfCurves(data: BagOfCurves, _indexInParent?: number): void; + announceCurvePrimitive(_data: CurvePrimitive, _indexInParent?: number): void; + announceLoop(data: Loop, indexInParent?: number): void; + announceParityRegion(data: ParityRegion, _indexInParent?: number): void; + announcePath(data: Path, indexInParent?: number): void; + announceUnexpected(_data: AnyCurve, _indexInParent: number): void; + announceUnionRegion(data: UnionRegion, indexInParent?: number): void; + // (undocumented) + enter(data: CurveCollection): void; + // (undocumented) + leave(): CurveCollection | undefined; +} + +// @public (undocumented) +class RotationalSweep extends SolidPrimitive { + // (undocumented) + clone(): RotationalSweep; + // (undocumented) + cloneAxisRay(): Ray3d; + // (undocumented) + cloneTransformed(transform: Transform): RotationalSweep; + // (undocumented) + constantVSection(vFraction: number): CurveCollection | undefined; + // (undocumented) + static create(contour: CurveCollection, axis: Ray3d, sweepAngle: Angle, capped: boolean): RotationalSweep | undefined; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + // (undocumented) + extendRange(range: Range3d, transform?: Transform): void; + getConstructiveFrame(): Transform | undefined; + // (undocumented) + getCurves(): CurveCollection; + // (undocumented) + getFractionalRotationTransform(vFraction: number, result?: Transform): Transform; + // (undocumented) + getSweep(): Angle; + // (undocumented) + getSweepContourRef(): SweepContour; + // (undocumented) + isAlmostEqual(other: GeometryQuery): boolean; + readonly isClosedVolume: boolean; + // (undocumented) + isSameGeometryClass(other: any): boolean; + // (undocumented) + tryTransformInPlace(transform: Transform): boolean; +} + +// @public (undocumented) +class RuledSweep extends SolidPrimitive { + // (undocumented) + clone(): RuledSweep; + // (undocumented) + cloneContours(): CurveCollection[]; + // (undocumented) + cloneSweepContours(): SweepContour[]; + // (undocumented) + cloneTransformed(transform: Transform): RuledSweep; + // (undocumented) + constantVSection(vFraction: number): CurveCollection | undefined; + // (undocumented) + static create(contours: CurveCollection[], capped: boolean): RuledSweep | undefined; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + // (undocumented) + extendRange(rangeToExtend: Range3d, transform?: Transform): void; + getConstructiveFrame(): Transform | undefined; + // (undocumented) + isAlmostEqual(other: GeometryQuery): boolean; + readonly isClosedVolume: boolean; + // (undocumented) + isSameGeometryClass(other: any): boolean; + static mutatePartners(collectionA: CurveCollection, collectionB: CurveCollection, primitiveMutator: CurvePrimitiveMutator): CurveCollection | undefined; + // (undocumented) + sweepContoursRef(): SweepContour[]; + // (undocumented) + tryTransformInPlace(transform: Transform): boolean; +} + +// @public +class Segment1d { + clone(): Segment1d; + static create(x0?: number, x1?: number, result?: Segment1d): Segment1d; + fractionToPoint(fraction: number): number; + isAlmostEqual(other: Segment1d): boolean; + readonly isExact01: boolean; + // (undocumented) + readonly isIn01: boolean; + reverseInPlace(): void; + set(x0: number, x1: number): void; + setFrom(other: Segment1d): void; + shift(dx: number): void; + // (undocumented) + x0: number; + // (undocumented) + x1: number; +} + +// @public (undocumented) +class SmallSystem { + // (undocumented) + static linearSystem2d(ux: number, vx: number, // first row of matrix + uy: number, vy: number, // second row of matrix + cx: number, cy: number, // right side + result: Vector2d): boolean; + static linearSystem3d(axx: number, axy: number, axz: number, // first row of matrix + ayx: number, ayy: number, ayz: number, // second row of matrix + azx: number, azy: number, azz: number, // second row of matrix + cx: number, cy: number, cz: number, // right side + result?: Vector3d): Vector3d | undefined; + static lineSegment2dXYTransverseIntersectionUnbounded(a0: Point2d, a1: Point2d, b0: Point2d, b1: Point2d, result: Vector2d): boolean; + static lineSegment3dClosestApproachUnbounded(a0: Point3d, a1: Point3d, b0: Point3d, b1: Point3d, result: Vector2d): boolean; + static lineSegment3dHXYClosestPointUnbounded(hA0: Point4d, hA1: Point4d, spacePoint: Point4d): number | undefined; + static lineSegment3dHXYTransverseIntersectionUnbounded(hA0: Point4d, hA1: Point4d, hB0: Point4d, hB1: Point4d, result?: Vector2d): Vector2d | undefined; + static lineSegment3dXYClosestPointUnbounded(pointA0: Point3d, pointA1: Point3d, spacePoint: Point3d): number | undefined; + static lineSegment3dXYTransverseIntersectionUnbounded(a0: Point3d, a1: Point3d, b0: Point3d, b1: Point3d, result: Vector2d): boolean; +} + +// @public +class SolidPrimitive extends GeometryQuery { + protected constructor(capped: boolean); + // (undocumented) + protected _capped: boolean; + capped: boolean; + abstract constantVSection(_vFraction: number): CurveCollection | undefined; + abstract getConstructiveFrame(): Transform | undefined; + readonly isClosedVolume: boolean; +} + +// @public +class Sphere extends SolidPrimitive, implements UVSurface { + // (undocumented) + clone(): Sphere; + cloneCenter(): Point3d; + cloneLatitudeSweep(): AngleSweep; + // (undocumented) + cloneLocalToWorld(): Transform; + // (undocumented) + cloneTransformed(transform: Transform): Sphere | undefined; + cloneVectorX(): Vector3d; + cloneVectorY(): Vector3d; + cloneVectorZ(): Vector3d; + // (undocumented) + constantVSection(vFraction: number): CurveCollection | undefined; + // (undocumented) + static createCenterRadius(center: Point3d, radius: number, latitudeSweep?: AngleSweep): Sphere; + static createDgnSphere(center: Point3d, vectorX: Vector3d, vectorZ: Vector3d, radiusXY: number, radiusZ: number, latitudeSweep: AngleSweep, capped: boolean): Sphere | undefined; + static createEllipsoid(localToWorld: Transform, latitudeSweep: AngleSweep, capped: boolean): Sphere | undefined; + static createFromAxesAndScales(center: Point3d, axes: undefined | Matrix3d, radiusX: number, radiusY: number, radiusZ: number, latitudeSweep: AngleSweep | undefined, capped: boolean): Sphere | undefined; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + // (undocumented) + extendRange(range: Range3d, transform?: Transform): void; + getConstructiveFrame(): Transform | undefined; + // (undocumented) + isAlmostEqual(other: GeometryQuery): boolean; + readonly isClosedVolume: boolean; + // (undocumented) + isSameGeometryClass(other: any): boolean; + readonly latitudeSweepFraction: number; + maxIsoParametricDistance(): Vector2d; + strokeConstantVSection(v: number, fixedStrokeCount: number | undefined, options?: StrokeOptions): LineString3d; + // (undocumented) + trueSphereRadius(): number | undefined; + // (undocumented) + tryTransformInPlace(transform: Transform): boolean; + uFractionToRadians(u: number): number; + uvFractionToPoint(uFraction: number, vFraction: number, result?: Point3d): Point3d; + uvFractionToPointAndTangents(uFraction: number, vFraction: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + vFractionToRadians(v: number): number; +} + +// @public +class SphereImplicit { + constructor(r: number); + // (undocumented) + evaluateDerivativesThetaPhi(theta: number, phi: number, dxdTheta: Vector3d, dxdPhi: Vector3d): void; + // (undocumented) + evaluateImplicitFunction(x: number, y: number, z: number): number; + // (undocumented) + evaluateImplicitFunctionXYZW(wx: number, wy: number, wz: number, w: number): number; + // (undocumented) + evaluateThetaPhi(thetaRadians: number, phiRadians: number): Point3d; + // (undocumented) + radius: number; + // (undocumented) + xyzToThetaPhiR: { + phi: number; + r: number; + theta: number; + valid: boolean; + } +} + +// @public (undocumented) +enum StandardViewIndex { + // (undocumented) + Back = 6, + // (undocumented) + Bottom = 2, + // (undocumented) + Front = 5, + // (undocumented) + Iso = 7, + // (undocumented) + Left = 3, + // (undocumented) + Right = 4, + // (undocumented) + RightIso = 8, + // (undocumented) + Top = 1 +} + +// @public +class StrokeOptions { + angleTol?: Angle; + // (undocumented) + applyAngleTol(minCount: number, sweepRadians: number, defaultStepRadians: number): number; + // (undocumented) + applyChordTol(minCount: number, radius: number, sweepRadians: number): number; + // (undocumented) + applyMaxEdgeLength(minCount: number, totalLength: number): number; + // (undocumented) + applyMinStrokesPerPrimitive(minCount: number): number; + // (undocumented) + applyTolerancesToArc(radius: number, sweepRadians?: number): number; + chordTol?: number; + // (undocumented) + static createForCurves(): StrokeOptions; + // (undocumented) + static createForFacets(): StrokeOptions; + // (undocumented) + defaultCircleStrokes: number; + // (undocumented) + readonly hasMaxEdgeLength: boolean; + maxEdgeLength?: number; + minStrokesPerPrimitive?: number; + // (undocumented) + needColors?: boolean; + needConvexFacets?: boolean; + // (undocumented) + needNormals: boolean; + // (undocumented) + needParams: boolean; + shouldTriangulate: boolean; +} + +// @public +class SweepContour { + // (undocumented) + axis: Ray3d | undefined; + buildFacets(_builder: PolyfaceBuilder, options: StrokeOptions | undefined): void; + // (undocumented) + clone(): SweepContour; + // (undocumented) + cloneTransformed(transform: Transform): SweepContour | undefined; + // (undocumented) + static createForLinearSweep(contour: CurveCollection, defaultNormal?: Vector3d): SweepContour | undefined; + // (undocumented) + static createForRotation(contour: CurveCollection, axis: Ray3d): SweepContour | undefined; + // (undocumented) + curves: CurveCollection; + emitFacets(builder: PolyfaceBuilder, reverse: boolean, transform?: Transform): void; + // (undocumented) + getCurves(): CurveCollection; + // (undocumented) + isAlmostEqual(other: any): boolean; + // (undocumented) + localToWorld: Transform; + purgeFacets(): void; + // (undocumented) + tryTransformInPlace(transform: Transform): boolean; +} + +// @public (undocumented) +class TorusImplicit { + constructor(majorRadiusR: number, minorRadiusr: number); + // (undocumented) + boxSize(): number; + // (undocumented) + evaluateDerivativesThetaPhi(theta: number, phi: number, dxdTheta: Vector3d, dxdPhi: Vector3d): void; + // (undocumented) + evaluateImplicitFunctionPoint(xyz: Point3d): number; + // (undocumented) + evaluateImplicitFunctionXYZ(x: number, y: number, z: number): number; + // (undocumented) + evaluateImplicitFunctionXYZW(x: number, y: number, z: number, w: number): number; + // (undocumented) + evaluateThetaPhi(theta: number, phi: number): Point3d; + // (undocumented) + evaluateThetaPhiDistance(theta: number, phi: number, distance: number): Point3d; + // (undocumented) + implicitFunctionScale(): number; + // (undocumented) + majorRadius: number; + // (undocumented) + minorRadius: number; + xyzToThetaPhiDistance: { + distance: number; + phi: number; + rho: number; + safePhi: boolean; + theta: number; + } +} + +// @public +class TorusPipe extends SolidPrimitive, implements UVSurface, UVSurfaceIsoParametricDistance { + protected constructor(map: Transform, radiusA: number, radiusB: number, sweep: Angle, capped: boolean); + // (undocumented) + clone(): TorusPipe; + // (undocumented) + cloneCenter(): Point3d; + // (undocumented) + cloneTransformed(transform: Transform): TorusPipe | undefined; + // (undocumented) + cloneVectorX(): Vector3d; + // (undocumented) + cloneVectorY(): Vector3d; + // (undocumented) + constantUSection(uFraction: number): CurveCollection | undefined; + // (undocumented) + constantVSection(v: number): CurveCollection | undefined; + static createDgnTorusPipe(center: Point3d, vectorX: Vector3d, vectorY: Vector3d, majorRadius: number, minorRadius: number, sweep: Angle, capped: boolean): TorusPipe | undefined; + // (undocumented) + static createInFrame(frame: Transform, majorRadius: number, minorRadius: number, sweep: Angle, capped: boolean): TorusPipe | undefined; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + // (undocumented) + extendRange(range: Range3d, transform?: Transform): void; + getConstructiveFrame(): Transform | undefined; + // (undocumented) + getIsReversed(): boolean; + // (undocumented) + getMajorRadius(): number; + // (undocumented) + getMinorRadius(): number; + // (undocumented) + getSweepAngle(): Angle; + // (undocumented) + getThetaFraction(): number; + // (undocumented) + isAlmostEqual(other: GeometryQuery): boolean; + readonly isClosedVolume: boolean; + // (undocumented) + isSameGeometryClass(other: any): boolean; + maxIsoParametricDistance(): Vector2d; + // (undocumented) + tryTransformInPlace(transform: Transform): boolean; + uvFractionToPoint(u: number, v: number, result?: Point3d): Point3d; + uvFractionToPointAndTangents(u: number, v: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + vFractionToRadians(v: number): number; +} + +// @public +class Transform implements BeJSONFunctions { + clone(result?: Transform): Transform; + // (undocumented) + cloneRigid(axisOrder?: AxisOrder): Transform | undefined; + static createFixedPointAndMatrix(fixedPoint: Point3d, matrix: Matrix3d, result?: Transform): Transform; + static createIdentity(result?: Transform): Transform; + static createOriginAndMatrix(origin: XYZ | undefined, matrix: Matrix3d | undefined, result?: Transform): Transform; + static createOriginAndMatrixColumns(origin: XYZ, vectorX: Vector3d, vectorY: Vector3d, vectorZ: Vector3d, result?: Transform): Transform; + static createRefs(origin: XYZ, matrix: Matrix3d, result?: Transform): Transform; + static createRowValues(qxx: number, qxy: number, qxz: number, ax: number, qyx: number, qyy: number, qyz: number, ay: number, qzx: number, qzy: number, qzz: number, az: number, result?: Transform): Transform; + static createScaleAboutPoint(fixedPoint: Point3d, scale: number, result?: Transform): Transform; + static createTranslation(translation: XYZ, result?: Transform): Transform; + static createTranslationXYZ(x?: number, y?: number, z?: number, result?: Transform): Transform; + static createZero(result?: Transform): Transform; + // (undocumented) + freeze(): void; + // (undocumented) + static fromJSON(json?: TransformProps): Transform; + getOrigin(): Point3d; + getTranslation(): Vector3d; + static readonly identity: Transform; + static initFromRange(min: Point3d, max: Point3d, npcToGlobal?: Transform, globalToNpc?: Transform): void; + // (undocumented) + inverse(): Transform | undefined; + isAlmostEqual(other: Transform): boolean; + readonly isIdentity: boolean; + // (undocumented) + static matchArrayLengths(source: any[], dest: any[], constructionFunction: () => any): number; + readonly matrix: Matrix3d; + multiplyComponentXYZ(componentIndex: number, x: number, y: number, z: number): number; + multiplyComponentXYZW(componentIndex: number, x: number, y: number, z: number, w: number): number; + // (undocumented) + multiplyInversePoint3d(point: XYAndZ, result?: Point3d): Point3d | undefined; + multiplyInversePoint3dArray(source: Point3d[], result?: Point3d[]): Point3d[] | undefined; + multiplyInversePoint3dArrayInPlace(source: Point3d[]): boolean; + multiplyPoint2d(source: XAndY, result?: Point2d): Point2d; + multiplyPoint2dArray(source: Point2d[], result?: Point2d[]): Point2d[]; + multiplyPoint3d(point: XYAndZ, result?: Point3d): Point3d; + multiplyPoint3dArray(source: Point3d[], result?: Point3d[]): Point3d[]; + multiplyPoint3dArrayInPlace(points: Point3d[]): void; + multiplyRange(range: Range3d, result?: Range3d): Range3d; + multiplyTransformMatrix3d(other: Matrix3d, result?: Transform): Transform; + multiplyTransformTransform(other: Transform, result?: Transform): Transform; + multiplyTransposeXYZW(x: number, y: number, z: number, w: number, result?: Point4d): Point4d; + multiplyVector(vector: Vector3d, result?: Vector3d): Vector3d; + multiplyVectorXYZ(x: number, y: number, z: number, result?: Vector3d): Vector3d; + multiplyXYZ(x: number, y: number, z: number, result?: Point3d): Point3d; + multiplyXYZToFloat64Array(x: number, y: number, z: number, result?: Float64Array): Float64Array; + multiplyXYZW(x: number, y: number, z: number, w: number, result?: Point4d): Point4d; + multiplyXYZWToFloat64Array(x: number, y: number, z: number, w: number, result?: Float64Array): Float64Array; + readonly origin: XYZ; + // (undocumented) + setFrom(other: Transform): void; + // (undocumented) + setFromJSON(json?: TransformProps): void; + setIdentity(): void; + setMultiplyTransformTransform(transformA: Transform, transformB: Transform): void; + setOriginAndMatrixColumns(origin: XYZ, vectorX: Vector3d, vectorY: Vector3d, vectorZ: Vector3d): void; + // (undocumented) + toJSON(): TransformProps; +} + +// @public +class TransitionConditionalProperties { + constructor(radius0: number | undefined, radius1: number | undefined, bearing0: Angle | undefined, bearing1: Angle | undefined, arcLength: number | undefined); + // (undocumented) + bearing0: Angle | undefined; + // (undocumented) + bearing1: Angle | undefined; + clone(): TransitionConditionalProperties; + // (undocumented) + curveLength: number | undefined; + isAlmostEqual(other: TransitionConditionalProperties): boolean; + numDefinedProperties(): number; + // (undocumented) + radius0: number | undefined; + // (undocumented) + radius1: number | undefined; + tryResolveAnySingleUnknown(): boolean; +} + +// WARNING: defaultSpiralType has incomplete type information +// @public (undocumented) +class TransitionSpiral3d extends CurvePrimitive { + constructor(spiralType: string | undefined, radius01: Segment1d, bearing01: AngleSweep, activeFractionInterval: Segment1d, localToWorld: Transform, arcLength: number, properties: TransitionConditionalProperties | undefined); + // (undocumented) + activeFractionInterval: Segment1d; + // (undocumented) + static averageCurvature(radiusLimits: Segment1d): number; + static averageCurvatureR0R1(r0: number, r1: number): number; + // (undocumented) + bearing01: AngleSweep; + // (undocumented) + clone(): TransitionSpiral3d; + // (undocumented) + cloneTransformed(transform: Transform): TransitionSpiral3d; + computeStrokeCountForOptions(options?: StrokeOptions): number; + static create(spiralType: string | undefined, radius0: number | undefined, radius1: number | undefined, bearing0: Angle | undefined, bearing1: Angle | undefined, arcLength: number | undefined, fractionInterval: undefined | Segment1d, localToWorld: Transform): TransitionSpiral3d | undefined; + static createRadiusRadiusBearingBearing(radius01: Segment1d, bearing01: AngleSweep, activeFractionInterval: Segment1d, localToWorld: Transform): TransitionSpiral3d; + // (undocumented) + static curvatureToRadius(curvature: number): number; + curveLength(): number; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + // (undocumented) + emitStrokableParts(dest: IStrokeHandler, options?: StrokeOptions): void; + // (undocumented) + emitStrokes(dest: LineString3d, options?: StrokeOptions): void; + // (undocumented) + endPoint(): Point3d; + // (undocumented) + extendRange(rangeToExtend: Range3d, transform?: Transform): void; + fractionToBearingRadians(fraction: number): number; + fractionToCurvature(fraction: number): number; + fractionToFrenetFrame(fraction: number, result?: Transform): Transform; + // (undocumented) + fractionToPoint(fraction: number, result?: Point3d): Point3d; + fractionToPointAnd2Derivatives(fraction: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors | undefined; + // (undocumented) + fractionToPointAndDerivative(fraction: number, result?: Ray3d): Ray3d; + getSpiralType(): string; + // (undocumented) + static initWorkSpace(): void; + // (undocumented) + isAlmostEqual(other: GeometryQuery): boolean; + // (undocumented) + isInPlane(plane: Plane3dByOriginAndUnitNormal): boolean; + // (undocumented) + isSameGeometryClass(other: any): boolean; + // (undocumented) + localToWorld: Transform; + readonly originalProperties: TransitionConditionalProperties | undefined; + quickLength(): number; + // (undocumented) + radius01: Segment1d; + // (undocumented) + static radius0LengthSweepRadiansToRadius1(radius0: number, arcLength: number, sweepRadians: number): number; + // (undocumented) + static radius1LengthSweepRadiansToRadius0(radius1: number, arcLength: number, sweepRadians: number): number; + // (undocumented) + static radiusRadiusLengthToSweepRadians(radius0: number, radius1: number, arcLength: number): number; + // (undocumented) + static radiusRadiusSweepRadiansToArcLength(radius0: number, radius1: number, sweepRadians: number): number; + // (undocumented) + static radiusToCurvature(radius: number): number; + // (undocumented) + refreshComputedProperties(): void; + // (undocumented) + reverseInPlace(): void; + // (undocumented) + setFrom(other: TransitionSpiral3d): TransitionSpiral3d; + // (undocumented) + startPoint(): Point3d; + // (undocumented) + tryTransformInPlace(transform: Transform): boolean; +} + +// @public (undocumented) +class Triangulator { + static cleanupTriangulation(graph: HalfEdgeGraph): void; + static createFaceLoopFromIndexedXYZCollection(data: GrowableXYZArray, returnPositiveAreaLoop: boolean, markExterior: boolean): HalfEdge | undefined; + static earcutOuterAndInnerLoops(loops: XAndY[][]): HalfEdgeGraph; + static earcutSingleLoop(data: XAndY[]): HalfEdgeGraph; + // (undocumented) + static triangulateStrokedLoops(strokedLoops: GrowableXYZArray[]): HalfEdgeGraph | undefined; +} + +// @public (undocumented) +class TriDiagonalSystem { + constructor(n: number); + // (undocumented) + addToB(row: number, bb: number): void; + // (undocumented) + addToRow(row: number, left: number, diag: number, right: number): void; + // (undocumented) + copy(): TriDiagonalSystem; + // (undocumented) + defactor(): boolean; + // (undocumented) + factor(): boolean; + // (undocumented) + factorAndBackSubstitute(): boolean; + // (undocumented) + factorAndBackSubstitutePointArrays(vectorB: Point3d[], vectorX: Point3d[]): boolean; + // (undocumented) + flatten(): any; + // (undocumented) + flattenWithPoints(xyzB: Point3d[]): any; + // (undocumented) + getB(row: number): number; + // (undocumented) + getX(row: number): number; + // (undocumented) + multiplyAX(): boolean; + // (undocumented) + multiplyAXPoints(pointX: Point3d[], pointB: Point3d[]): boolean; + // (undocumented) + order(): number; + // (undocumented) + reset(): void; + // (undocumented) + setB(row: number, bb: number): void; + // (undocumented) + setRow(row: number, left: number, diag: number, right: number): void; + // (undocumented) + setX(row: number, xx: number): void; +} + +// WARNING: coeffientRelTol has incomplete type information +// @public (undocumented) +class TrigPolynomial { + // (undocumented) + static readonly C: Float64Array; + // (undocumented) + static readonly CC: Float64Array; + // (undocumented) + static readonly CCminusSS: Float64Array; + // (undocumented) + static readonly CW: Float64Array; + // (undocumented) + static readonly S: Float64Array; + // (undocumented) + static readonly SC: Float64Array; + // (undocumented) + static readonly SmallAngle: number; + // (undocumented) + static solveAngles(coff: Float64Array, nominalDegree: number, referenceCoefficient: number, radians: number[]): boolean; + // (undocumented) + static solveUnitCircleEllipseIntersection(cx: number, cy: number, ux: number, uy: number, vx: number, vy: number, ellipseRadians: number[], circleRadians: number[]): boolean; + // (undocumented) + static solveUnitCircleHomogeneousEllipseIntersection(cx: number, cy: number, cw: number, ux: number, uy: number, uw: number, vx: number, vy: number, vw: number, ellipseRadians: number[], circleRadians: number[]): boolean; + // (undocumented) + static solveUnitCircleImplicitQuadricIntersection(axx: number, axy: number, ayy: number, ax: number, ay: number, a1: number, radians: number[]): boolean; + // (undocumented) + static readonly SS: Float64Array; + // (undocumented) + static readonly SW: Float64Array; + // (undocumented) + static readonly W: Float64Array; + // (undocumented) + static readonly WW: Float64Array; +} + +// @public (undocumented) +interface TrigValues { + // (undocumented) + c: number; + // (undocumented) + radians: number; + // (undocumented) + s: number; +} + +// @public +class UnionOfConvexClipPlaneSets implements Clipper { + // (undocumented) + addConvexSet(toAdd: ConvexClipPlaneSet): void; + // (undocumented) + addOutsideZClipSets(invisible: boolean, zLow?: number, zHigh?: number): void; + // (undocumented) + announceClippedArcIntervals(arc: Arc3d, announce?: AnnounceNumberNumberCurvePrimitive): boolean; + announceClippedSegmentIntervals(f0: number, f1: number, pointA: Point3d, pointB: Point3d, announce?: (fraction0: number, fraction1: number) => void): boolean; + appendIntervalsFromSegment(segment: LineSegment3d, intervals: Segment1d[]): void; + classifyPointContainment(points: Point3d[], onIsOutside: boolean): number; + // (undocumented) + clone(result?: UnionOfConvexClipPlaneSets): UnionOfConvexClipPlaneSets; + // (undocumented) + readonly convexSets: ConvexClipPlaneSet[]; + // (undocumented) + static createConvexSets(convexSets: ConvexClipPlaneSet[], result?: UnionOfConvexClipPlaneSets): UnionOfConvexClipPlaneSets; + // (undocumented) + static createEmpty(result?: UnionOfConvexClipPlaneSets): UnionOfConvexClipPlaneSets; + // (undocumented) + static fromJSON(json: any, result?: UnionOfConvexClipPlaneSets): UnionOfConvexClipPlaneSets; + getRangeOfAlignedPlanes(transform?: Transform, result?: Range3d): Range3d | undefined; + // (undocumented) + getRayIntersection(point: Point3d, direction: Vector3d): number | undefined; + // (undocumented) + isAlmostEqual(other: UnionOfConvexClipPlaneSets): boolean; + isAnyPointInOrOnFromSegment(segment: LineSegment3d): boolean; + // (undocumented) + isPointInside(point: Point3d): boolean; + // (undocumented) + isPointOnOrInside(point: Point3d, tolerance: number): boolean; + // (undocumented) + isSphereInside(point: Point3d, radius: number): boolean; + // (undocumented) + multiplyPlanesByMatrix(matrix: Matrix4d): void; + polygonClip(input: Point3d[], output: Point3d[][]): void; + // (undocumented) + setInvisible(invisible: boolean): void; + // (undocumented) + testRayIntersect(point: Point3d, direction: Vector3d): boolean; + // (undocumented) + toJSON(): any; + // (undocumented) + transformInPlace(transform: Transform): void; +} + +// @public +class UnionRegion extends CurveCollection { + constructor(); + // (undocumented) + protected _children: Array; + // (undocumented) + announceToCurveProcessor(processor: RecursiveCurveProcessor, indexInParent?: number): void; + // (undocumented) + readonly children: Array; + // (undocumented) + cloneEmptyPeer(): UnionRegion; + // (undocumented) + cloneStroked(options?: StrokeOptions): UnionRegion; + // (undocumented) + static create(...data: Array): UnionRegion; + // (undocumented) + dgnBoundaryType(): number; + // (undocumented) + dispatchToGeometryHandler(handler: GeometryHandler): any; + // (undocumented) + getChild(i: number): Loop | ParityRegion | undefined; + // (undocumented) + isSameGeometryClass(other: GeometryQuery): boolean; + // (undocumented) + tryAddChild(child: AnyCurve): boolean; +} + +// @public +class UnivariateBezier extends BezierCoffs { + constructor(data: number | Float64Array | number[]); + addSquaredSquaredBezier(coffA: Float64Array, scale: number): boolean; + allocateOrder(order: number): void; + basisFunctions(u: number, result?: Float64Array): Float64Array; + clone(compressToMinimalAllocation?: boolean): UnivariateBezier; + static create(other: BezierCoffs): UnivariateBezier; + static createArraySubset(coffs: number[] | Float64Array, index0: number, order: number, result?: UnivariateBezier): UnivariateBezier; + static createCoffs(data: number | number[] | Float64Array): UnivariateBezier; + static createProduct(bezierA: BezierCoffs, bezierB: BezierCoffs): UnivariateBezier; + deflateLeft(): void; + deflateRight(): void; + deflateRoot(root: number): number; + // (undocumented) + static deflateRoots01(bezier: UnivariateBezier): number[] | undefined; + evaluate(u: number): number; + // (undocumented) + readonly order: number; + runNewton(startFraction: number, tolerance?: number): number | undefined; + sumBasisFunctionDerivatives(u: number, polygon: Float64Array, blockSize: number, result?: Float64Array): Float64Array; + sumBasisFunctions(u: number, polygon: Float64Array, blockSize: number, result?: Float64Array): Float64Array; +} + +// @public +enum UVSelect { + // (undocumented) + uDirection = 0, + // (undocumented) + VDirection = 1 +} + +// @public +interface UVSurface { + uvFractionToPoint(uFraction: number, vFraction: number, result?: Point3d): Point3d; + uvFractionToPointAndTangents(uFraction: number, vFraction: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; +} + +// @public +interface UVSurfaceIsoParametricDistance { + maxIsoParametricDistance(): Vector2d; +} + +// @public +class UVSurfaceOps { + static createLinestringOnUVLine(surface: UVSurface, u0: number, v0: number, u1: number, v1: number, numEdge: number, saveUV?: boolean, saveFraction?: boolean): LineString3d; +} + +// @public +class Vector2d extends XY, implements BeJSONFunctions { + constructor(x?: number, y?: number); + angleTo(vectorB: Vector2d): Angle; + // (undocumented) + clone(): Vector2d; + // (undocumented) + static create(x?: number, y?: number, result?: Vector2d): Vector2d; + static createFrom(data: XAndY | Float64Array, result?: Vector2d): Vector2d; + static createOffsetBisector(unitPerpA: Vector2d, unitPerpB: Vector2d, offset: number): Vector2d | undefined; + // (undocumented) + static createPolar(r: number, theta: Angle): Vector2d; + // (undocumented) + static createStartEnd(point0: XAndY, point1: XAndY, result?: Vector2d): Vector2d; + // (undocumented) + static createZero(result?: Vector2d): Vector2d; + crossProduct(vectorB: Vector2d): number; + dotProduct(vectorB: Vector2d): number; + dotProductStartEnd(pointA: XAndY, pointB: XAndY): number; + fractionOfProjectionToVector(target: Vector2d, defaultFraction?: number): number; + // (undocumented) + static fromJSON(json?: XYProps): Vector2d; + interpolate(fraction: number, right: Vector2d, result?: Vector2d): Vector2d; + // (undocumented) + isParallelTo(other: Vector2d, oppositeIsParallel?: boolean): boolean; + // (undocumented) + isPerpendicularTo(other: Vector2d): boolean; + minus(vector: XAndY, result?: Vector2d): Vector2d; + negate(result?: Vector2d): Vector2d; + // (undocumented) + normalize(result?: Vector2d): Vector2d | undefined; + plus(vector: XAndY, result?: Vector2d): Vector2d; + plus2Scaled(vectorA: XAndY, scalarA: number, vectorB: XAndY, scalarB: number, result?: Vector2d): Vector2d; + plus3Scaled(vectorA: XAndY, scalarA: number, vectorB: XAndY, scalarB: number, vectorC: XAndY, scalarC: number, result?: Vector2d): Vector2d; + plusScaled(vector: XAndY, scaleFactor: number, result?: Vector2d): Vector2d; + // (undocumented) + rotate90CCWXY(result?: Vector2d): Vector2d; + // (undocumented) + rotate90CWXY(result?: Vector2d): Vector2d; + // (undocumented) + rotateXY(angle: Angle, result?: Vector2d): Vector2d; + // (undocumented) + safeDivideOrNull(denominator: number, result?: Vector2d): Vector2d | undefined; + scale(scale: number, result?: Vector2d): Vector2d; + scaleToLength(length: number, result?: Vector2d): Vector2d; + // (undocumented) + unitPerpendicularXY(result?: Vector2d): Vector2d; + // (undocumented) + static unitX(scale?: number): Vector2d; + // (undocumented) + static unitY(scale?: number): Vector2d; +} + +// @public +class Vector3d extends XYZ { + constructor(x?: number, y?: number, z?: number); + addCrossProductToTargetsInPlace(ax: number, ay: number, az: number, bx: number, by: number, bz: number, cx: number, cy: number, cz: number): void; + // (undocumented) + angleTo(vectorB: Vector3d): Angle; + // (undocumented) + angleToXY(vectorB: Vector3d): Angle; + clone(result?: Vector3d): Vector3d; + static create(x?: number, y?: number, z?: number, result?: Vector3d): Vector3d; + static createAdd2Scaled(vectorA: XYAndZ, scaleA: number, vectorB: XYAndZ, scaleB: number, result?: Vector3d): Vector3d; + static createAdd2ScaledXYZ(ax: number, ay: number, az: number, scaleA: number, bx: number, by: number, bz: number, scaleB: number, result?: Vector3d): Vector3d; + // (undocumented) + static createAdd3Scaled(vectorA: XYAndZ, scaleA: number, vectorB: XYAndZ, scaleB: number, vectorC: XYAndZ, scaleC: number, result?: Vector3d): Vector3d; + static createCrossProduct(ux: number, uy: number, uz: number, vx: number, vy: number, vz: number, result?: Vector3d): Vector3d; + static createCrossProductToPoints(origin: XYAndZ, pointA: XYAndZ, pointB: XYAndZ, result?: Vector3d): Vector3d; + static createFrom(data: XYAndZ | XAndY | Float64Array, result?: Vector3d): Vector3d; + static createPolar(r: number, theta: Angle, z?: number): Vector3d; + static createRotateVectorAroundVector(vector: Vector3d, axis: Vector3d, angle?: Angle): Vector3d | undefined; + static createSpherical(r: number, theta: Angle, phi: Angle): Vector3d; + static createStartEnd(start: XYAndZ, end: XYAndZ, result?: Vector3d): Vector3d; + // (undocumented) + static createStartEndXYZXYZ(x0: number, y0: number, z0: number, x1: number, y1: number, z1: number, result?: Vector3d): Vector3d; + static createZero(result?: Vector3d): Vector3d; + // (undocumented) + crossProduct(vectorB: Vector3d, result?: Vector3d): Vector3d; + crossProductMagnitude(vectorB: XYAndZ): number; + crossProductMagnitudeSquared(vectorB: XYAndZ): number; + crossProductStartEnd(pointA: Point3d, pointB: Point3d, result?: Vector3d): Vector3d; + crossProductStartEndXY(pointA: Point3d, pointB: Point3d): number; + crossProductXY(vectorB: Vector3d): number; + crossProductXYZ(x: number, y: number, z: number, result?: Vector3d): Vector3d; + // (undocumented) + dotProduct(vectorB: XYAndZ): number; + dotProductStart3dEnd4d(pointA: Point3d, pointB: Point4d): number; + // (undocumented) + dotProductStartEnd(pointA: XYAndZ, pointB: XYAndZ): number; + dotProductStartEndXYZ(pointA: Point3d, x: number, y: number, z: number): number; + dotProductStartEndXYZW(pointA: Point3d, x: number, y: number, z: number, w: number): number; + dotProductXY(vectorB: Vector3d): number; + dotProductXYZ(x: number, y: number, z?: number): number; + fractionOfProjectionToVector(target: Vector3d, defaultFraction?: number): number; + // (undocumented) + static fromJSON(json?: XYZProps): Vector3d; + // (undocumented) + interpolate(fraction: number, right: Vector3d, result?: Vector3d): Vector3d; + isParallelTo(other: Vector3d, oppositeIsParallel?: boolean, returnValueIfAnInputIsZeroLength?: boolean): boolean; + isPerpendicularTo(other: Vector3d, returnValueIfAnInputIsZeroLength?: boolean): boolean; + // (undocumented) + minus(vector: XYAndZ, result?: Vector3d): Vector3d; + negate(result?: Vector3d): Vector3d; + normalize(result?: Vector3d): Vector3d | undefined; + normalizeInPlace(): boolean; + // (undocumented) + normalizeWithDefault(x: number, y: number, z: number, result?: Vector3d): Vector3d; + normalizeWithLength: { + mag: number; + v: Vector3d | undefined; + } + // (undocumented) + planarAngleTo(vector: Vector3d, planeNormal: Vector3d): Angle; + // (undocumented) + planarRadiansTo(vector: Vector3d, planeNormal: Vector3d): number; + // (undocumented) + plus(vector: XYAndZ, result?: Vector3d): Vector3d; + plus2Scaled(vectorA: XYAndZ, scalarA: number, vectorB: XYAndZ, scalarB: number, result?: Vector3d): Vector3d; + plus3Scaled(vectorA: XYAndZ, scalarA: number, vectorB: XYAndZ, scalarB: number, vectorC: XYAndZ, scalarC: number, result?: Vector3d): Vector3d; + plusScaled(vector: XYAndZ, scaleFactor: number, result?: Vector3d): Vector3d; + // (undocumented) + rotate90Around(axis: Vector3d, result?: Vector3d): Vector3d | undefined; + rotate90CCWXY(result?: Vector3d): Vector3d; + // (undocumented) + rotate90Towards(target: Vector3d, result?: Vector3d): Vector3d | undefined; + // (undocumented) + rotateXY(angle: Angle, result?: Vector3d): Vector3d; + safeDivideOrNull(denominator: number, result?: Vector3d): Vector3d | undefined; + scale(scale: number, result?: Vector3d): Vector3d; + // (undocumented) + scaleToLength(length: number, result?: Vector3d): Vector3d; + setStartEnd(point0: XYAndZ, point1: XYAndZ): void; + // (undocumented) + signedAngleTo(vector1: Vector3d, vectorW: Vector3d): Angle; + // (undocumented) + signedRadiansTo(vector1: Vector3d, vectorW: Vector3d): number; + // (undocumented) + sizedCrossProduct(vectorB: Vector3d, productLength: number, result?: Vector3d): Vector3d | undefined; + tripleProduct(vectorB: Vector3d, vectorC: Vector3d): number; + // (undocumented) + tryNormalizeInPlace(smallestMagnitude?: number): boolean; + // (undocumented) + unitCrossProduct(vectorB: Vector3d, result?: Vector3d): Vector3d | undefined; + // (undocumented) + unitCrossProductWithDefault(vectorB: Vector3d, x: number, y: number, z: number, result?: Vector3d): Vector3d; + // (undocumented) + unitPerpendicularXY(result?: Vector3d): Vector3d; + static unitX(scale?: number): Vector3d; + static unitY(scale?: number): Vector3d; + static unitZ(scale?: number): Vector3d; +} + +// @public (undocumented) +class Vector3dArray { + // (undocumented) + static cloneVector3dArray(data: XYAndZ[]): Vector3d[]; + // (undocumented) + static isAlmostEqual(dataA: undefined | Vector3d[], dataB: undefined | Vector3d[]): boolean; +} + +// @public (undocumented) +enum WeightStyle { + UnWeighted = 0, + WeightsAlreadyAppliedToCoordinates = 1, + WeightsSeparateFromCoordinates = 2 +} + +// @public (undocumented) +interface WritableLowAndHighXY { + // (undocumented) + high: WritableXAndY; + // (undocumented) + low: WritableXAndY; +} + +// @public (undocumented) +interface WritableLowAndHighXYZ { + // (undocumented) + high: WritableXYAndZ; + // (undocumented) + low: WritableXYAndZ; +} + +// @public (undocumented) +interface WritableXAndY { + // (undocumented) + x: number; + // (undocumented) + y: number; +} + +// @public (undocumented) +interface WritableXYAndZ extends XAndY, WriteableHasZ { +} + +// @public (undocumented) +interface WriteableHasZ { + // (undocumented) + z: number; +} + +// @public +class XY implements XAndY { + protected constructor(x?: number, y?: number); + static crossProductToPoints(origin: XAndY, targetA: XAndY, targetB: XAndY): number; + distance(other: XAndY): number; + distanceSquared(other: XAndY): number; + // (undocumented) + freeze(): void; + isAlmostEqual(other: XAndY, tol?: number): boolean; + // (undocumented) + isAlmostEqualMetric(other: XAndY): boolean; + isAlmostEqualXY(x: number, y: number, tol?: number): boolean; + // (undocumented) + readonly isAlmostZero: boolean; + // (undocumented) + isExactEqual(other: XAndY): boolean; + magnitude(): number; + magnitudeSquared(): number; + maxAbs(): number; + maxDiff(other: XAndY): number; + set(x?: number, y?: number): void; + setFrom(other?: XAndY): void; + setFromJSON(json?: XYProps): void; + setZero(): void; + toJSON(): XYProps; + // (undocumented) + toJSONXY(): XYProps; + unitVectorTo(target: XAndY, result?: Vector2d): Vector2d | undefined; + vectorTo(other: XAndY, result?: Vector2d): Vector2d; + x: number; + y: number; +} + +// @public +class XYZ implements XYAndZ { + protected constructor(x?: number, y?: number, z?: number); + addInPlace(other: XYAndZ): void; + addScaledInPlace(other: XYAndZ, scale: number): void; + at(index: number): number; + cloneAsPoint3d(): Point3d; + distance(other: XYAndZ): number; + distanceSquared(other: XYAndZ): number; + distanceSquaredXY(other: XAndY): number; + distanceXY(other: XAndY): number; + freeze(): void; + static hasZ(arg: any): arg is HasZ; + indexOfMaxAbs(): number; + isAlmostEqual(other: XYAndZ, tol?: number): boolean; + isAlmostEqualMetric(other: XYAndZ): boolean; + isAlmostEqualXY(other: XAndY, tol?: number): boolean; + isAlmostEqualXYZ(x: number, y: number, z: number, tol?: number): boolean; + readonly isAlmostZero: boolean; + isExactEqual(other: XYAndZ): boolean; + static isXAndY(arg: any): arg is XAndY; + static isXYAndZ(arg: any): arg is XYAndZ; + magnitude(): number; + magnitudeSquared(): number; + magnitudeSquaredXY(): number; + magnitudeXY(): number; + maxAbs(): number; + maxDiff(other: XYAndZ): number; + scaledVectorTo(other: XYAndZ, scale: number, result?: Vector3d): Vector3d; + scaleInPlace(scale: number): void; + set(x?: number, y?: number, z?: number): void; + setFrom(other: Float64Array | XAndY | XYAndZ): void; + setFromJSON(json?: XYZProps): void; + setFromPoint3d(other: Point3d): void; + setFromVector3d(other: Vector3d): void; + setZero(): void; + toFloat64Array(): Float64Array; + toJSON(): XYZProps; + // (undocumented) + toJSONXYZ(): XYZProps; + unitVectorTo(target: XYAndZ, result?: Vector3d): Vector3d | undefined; + vectorTo(other: XYAndZ, result?: Vector3d): Vector3d; + // (undocumented) + x: number; + // (undocumented) + y: number; + // (undocumented) + z: number; +} + +// @public +class YawPitchRollAngles { + constructor(yaw?: Angle, pitch?: Angle, roll?: Angle); + clone(): YawPitchRollAngles; + static createDegrees(yawDegrees: number, pitchDegrees: number, rollDegrees: number): YawPitchRollAngles; + static createFromMatrix3d(matrix: Matrix3d, result?: YawPitchRollAngles): YawPitchRollAngles | undefined; + static createRadians(yawRadians: number, pitchRadians: number, rollRadians: number): YawPitchRollAngles; + freeze(): void; + // (undocumented) + static fromJSON(json?: YawPitchRollProps): YawPitchRollAngles; + isAlmostEqual(other: YawPitchRollAngles): boolean; + // (undocumented) + isIdentity(allowPeriodShift?: boolean): boolean; + maxAbsDegrees(): number; + // (undocumented) + maxAbsRadians(): number; + maxDiffRadians(other: YawPitchRollAngles): number; + // (undocumented) + pitch: Angle; + // (undocumented) + roll: Angle; + setFrom(other: YawPitchRollAngles): void; + // (undocumented) + setFromJSON(json?: YawPitchRollProps): void; + sumSquaredDegrees(): number; + sumSquaredRadians(): number; + toJSON(): YawPitchRollProps; + toMatrix3d(result?: Matrix3d): Matrix3d; + static tryFromTransform: { + angles: YawPitchRollAngles | undefined; + origin: Point3d; + } + // (undocumented) + yaw: Angle; +} + +// @public +interface YawPitchRollProps { + // (undocumented) + pitch?: AngleProps; + // (undocumented) + roll?: AngleProps; + // (undocumented) + yaw?: AngleProps; +} + +// WARNING: Unsupported export: OptionalGrowableFloat64Array +// WARNING: Unsupported export: BlockComparisonFunction +// WARNING: Unsupported export: HasZ +// WARNING: Unsupported export: XAndY +// WARNING: Unsupported export: XYAndZ +// WARNING: Unsupported export: LowAndHighXY +// WARNING: Unsupported export: LowAndHighXYZ +// WARNING: Unsupported export: XYZProps +// WARNING: Unsupported export: XYProps +// WARNING: Unsupported export: Matrix3dProps +// WARNING: Unsupported export: TransformProps +// WARNING: Unsupported export: Range3dProps +// WARNING: Unsupported export: Range2dProps +// WARNING: Unsupported export: Range1dProps +// WARNING: Unsupported export: AngleProps +// WARNING: Unsupported export: AngleSweepProps +// WARNING: Unsupported export: Point4dProps +// WARNING: Unsupported export: Matrix4dProps +// WARNING: Unsupported export: AnyCurve +// WARNING: Unsupported export: AnyRegion +// WARNING: Unsupported export: AnnounceNumberNumberCurvePrimitive +// WARNING: Unsupported export: AnnounceNumberNumber +// WARNING: Unsupported export: AnnounceCurvePrimitive +// WARNING: Unsupported export: CurvePrimitiveMutator +// WARNING: Unsupported export: NodeFunction +// WARNING: Unsupported export: NodeToNumberFunction +// WARNING: Unsupported export: HalfEdgeToBooleanFunction +// WARNING: Unsupported export: HalfEdgeAndMaskToBooleanFunction +// WARNING: Unsupported export: GraphNodeFunction +// (No @packagedocumentation comment for this package) diff --git a/common/api/imodeljs-backend.api.ts b/common/api/imodeljs-backend.api.ts new file mode 100644 index 0000000..5656b4d --- /dev/null +++ b/common/api/imodeljs-backend.api.ts @@ -0,0 +1,2786 @@ +// @public +enum AccessMode { + // (undocumented) + Exclusive = 2, + // (undocumented) + Shared = 1 +} + +// @public +class AnnotationElement2d extends GraphicalElement2d { + constructor(props: GeometricElement2dProps, iModel: IModelDb); +} + +// @public +interface AppActivityMonitor { + isIdle: boolean; +} + +// @public +class AutoPush { + constructor(iModel: IModelDb, params: AutoPushParams, accessTokenManager: IAccessTokenManager, activityMonitor?: AppActivityMonitor); + autoSchedule: boolean; + cancel(): void; + readonly durationOfLastPushMillis: number; + readonly endOfLastPushMillis: number; + event: BeEvent; + readonly iModel: IModelDb; + readonly lastError: any | undefined; + // (undocumented) + reserveCodes(): Promise; + // (undocumented) + scheduleNextAutoPushIfNecessary(): void; + scheduleNextPush(intervalSeconds?: number): void; + readonly state: AutoPushState; + static validateAutoPushParams(params: any): void; +} + +// @public +enum AutoPushEventType { + // (undocumented) + PushCancelled = 3, + // (undocumented) + PushFailed = 2, + // (undocumented) + PushFinished = 1, + // (undocumented) + PushStarted = 0 +} + +// @public +interface AutoPushParams { + activityContext: ActivityLoggingContext; + autoSchedule: boolean; + pushIntervalSecondsMax: number; + pushIntervalSecondsMin: number; +} + +// @public +enum AutoPushState { + // (undocumented) + NotRunning = 0, + // (undocumented) + Pushing = 2, + // (undocumented) + Scheduled = 1 +} + +// @public +class AuxCoordSystem extends DefinitionElement, implements AuxCoordSystemProps { + constructor(props: AuxCoordSystemProps, iModel: IModelDb); + // (undocumented) + description?: string; + // (undocumented) + type: number; +} + +// @public +class AuxCoordSystem2d extends AuxCoordSystem, implements AuxCoordSystem2dProps { + constructor(props: AuxCoordSystem2dProps, iModel: IModelDb); + // (undocumented) + angle: number; + static createCode(iModel: IModelDb, scopeModelId: CodeScopeProps, codeValue: string): Code; + // (undocumented) + origin?: Point2d; +} + +// @public +class AuxCoordSystem3d extends AuxCoordSystem, implements AuxCoordSystem3dProps { + constructor(props: AuxCoordSystem3dProps, iModel: IModelDb); + static createCode(iModel: IModelDb, scopeModelId: CodeScopeProps, codeValue: string): Code; + // (undocumented) + origin?: Point3d; + // (undocumented) + pitch: number; + // (undocumented) + roll: number; + // (undocumented) + yaw: number; +} + +// @public +class AuxCoordSystemSpatial extends AuxCoordSystem3d { + static createCode(iModel: IModelDb, scopeModelId: CodeScopeProps, codeValue: string): Code; +} + +// @public +class BackendActivityMonitor implements AppActivityMonitor { + constructor(idleIntervalSeconds?: number); + // (undocumented) + idleIntervalSeconds: number; + // (undocumented) + readonly isIdle: boolean; +} + +// @public +class BisCore extends Schema { + static registerSchema(): void; +} + +// @public +class BriefcaseEntry { + briefcaseId: number; + changeSetId: string; + changeSetIndex?: number; + conflictError?: ConflictingCodesError; + readonly currentChangeSetId: string; + readonly currentChangeSetIndex: number; + fileId?: string; + // (undocumented) + getDebugInfo(): any; + getKey(): string; + readonly hasReversedChanges: boolean; + imodelClientContext?: string; + // (undocumented) + iModelDb: IModelDb | undefined; + iModelId: GuidString; + isOpen: boolean; + isStandalone: boolean; + // WARNING: The type "IModelJsNative.DgnDb" needs to be exported by the package (e.g. added to index.ts) + nativeDb: IModelJsNative.DgnDb; + readonly onBeforeClose: BeEvent<() => void>; + readonly onBeforeVersionUpdate: BeEvent<() => void>; + readonly onChangesetApplied: BeEvent<() => void>; + openParams?: OpenParams; + pathname: string; + reversedChangeSetId?: string; + reversedChangeSetIndex?: number; + userId?: string; +} + +// @public +class BriefcaseId { + constructor(value?: number); + // (undocumented) + static readonly Illegal: number; + // (undocumented) + readonly isMaster: boolean; + // (undocumented) + readonly isStandaloneId: boolean; + // (undocumented) + readonly isValid: boolean; + // (undocumented) + static readonly Master: number; + // (undocumented) + static readonly Standalone: number; + // (undocumented) + toString(): string; + // (undocumented) + readonly value: number; +} + +// @public +class BriefcaseManager { + static applyStandaloneChangeSets(briefcase: BriefcaseEntry, changeSetTokens: ChangeSetToken[], processOption: ChangeSetApplyOption): ChangeSetStatus; + // (undocumented) + static readonly cacheDir: string; + static close(actx: ActivityLoggingContext, accessToken: AccessToken, briefcase: BriefcaseEntry, keepBriefcase: KeepBriefcase): Promise; + static closeStandalone(briefcase: BriefcaseEntry): void; + static readonly connectClient: ConnectClient; + static create(actx: ActivityLoggingContext, accessToken: AccessToken, contextId: string, iModelName: string, args: CreateIModelProps): Promise; + static createStandalone(fileName: string, args: CreateIModelProps): BriefcaseEntry; + static createStandaloneChangeSet(briefcase: BriefcaseEntry): ChangeSetToken; + // (undocumented) + static deleteAllBriefcases(actx: ActivityLoggingContext, accessToken: AccessToken, iModelId: GuidString): Promise; + static deleteClosed(actx: ActivityLoggingContext, accessToken: AccessToken): Promise; + static downloadChangeSets(actx: ActivityLoggingContext, accessToken: AccessToken, iModelId: GuidString, fromChangeSetId: string, toChangeSetId: string): Promise; + static dumpChangeSet(briefcase: BriefcaseEntry, changeSetToken: ChangeSetToken): void; + static findBriefcaseByToken(iModelToken: IModelToken): BriefcaseEntry | undefined; + // (undocumented) + static getChangeCachePathName(iModelId: GuidString): string; + // (undocumented) + static getChangedElementsPathName(iModelId: GuidString): string; + // (undocumented) + static getChangeSetsPath(iModelId: GuidString): string; + static imodelClient: IModelClient; + static open(actx: ActivityLoggingContext, accessToken: AccessToken, contextId: string, iModelId: GuidString, openParams: OpenParams, version: IModelVersion): Promise; + static openStandalone(pathname: string, openMode: OpenMode, enableTransactions: boolean): BriefcaseEntry; + static pullAndMergeChanges(actx: ActivityLoggingContext, accessToken: AccessToken, briefcase: BriefcaseEntry, mergeToVersion?: IModelVersion): Promise; + static purgeCache(actx: ActivityLoggingContext, accessToken: AccessToken): Promise; + static pushChanges(actx: ActivityLoggingContext, accessToken: AccessToken, briefcase: BriefcaseEntry, description: string, relinquishCodesLocks?: boolean): Promise; + // (undocumented) + static reinstateChanges(actx: ActivityLoggingContext, accessToken: AccessToken, briefcase: BriefcaseEntry, reinstateToVersion?: IModelVersion): Promise; + // (undocumented) + static reverseChanges(actx: ActivityLoggingContext, accessToken: AccessToken, briefcase: BriefcaseEntry, reverseToVersion: IModelVersion): Promise; +} + +// @public +class CachedECSqlStatement { + constructor(stmt: ECSqlStatement); + // (undocumented) + statement: ECSqlStatement; + // (undocumented) + useCount: number; +} + +// @public +class CachedSqliteStatement { + constructor(stmt: SqliteStatement); + // (undocumented) + statement: SqliteStatement; + // (undocumented) + useCount: number; +} + +// @public (undocumented) +class Callout extends DetailingSymbol, implements CalloutProps { + constructor(props: CalloutProps, iModel: IModelDb); +} + +// @public +class Category extends DefinitionElement, implements CategoryProps { + constructor(props: CategoryProps, iModel: IModelDb); + myDefaultSubCategoryId(): Id64String; + // (undocumented) + rank: Rank; + setDefaultAppearance(props: SubCategoryAppearance.Props): void; + // (undocumented) + toJSON(): CategoryProps; +} + +// @public +class CategoryOwnsSubCategories extends ElementOwnsChildElements { + constructor(parentId: Id64String, relClassName?: string); + // (undocumented) + static classFullName: string; +} + +// @public +class CategorySelector extends DefinitionElement, implements CategorySelectorProps { + constructor(props: CategorySelectorProps, iModel: IModelDb); + categories: string[]; + static create(iModelDb: IModelDb, definitionModelId: Id64String, name: string, categories: Id64Array): CategorySelector; + static createCode(iModel: IModelDb, scopeModelId: CodeScopeProps, codeValue: string): Code; + static insert(iModelDb: IModelDb, definitionModelId: Id64String, name: string, categories: Id64Array): Id64String; + // (undocumented) + toJSON(): CategorySelectorProps; +} + +// @public +class ChangedElementsDb implements IDisposable { + constructor(); + closeDb(): void; + static createDb(briefcase: IModelDb, pathName: string): ChangedElementsDb; + // (undocumented) + dispose(): void; + getChangedElements(startChangesetId: string, endChangesetId: string): ChangedElements | undefined; + readonly isOpen: boolean; + isProcessed(changesetId: string): boolean; + // WARNING: The type "IModelJsNative.ChangedElementsECDb" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly nativeDb: IModelJsNative.ChangedElementsECDb; + static openDb(pathName: string, openMode?: ECDbOpenMode): ChangedElementsDb; + processChangesets(accessToken: AccessToken, briefcase: IModelDb, rulesetId: string, startChangesetId: string, endChangesetId: string, filterSpatial?: boolean): Promise; +} + +// @public +class ChangeSetToken { + constructor(id: string, parentId: string, index: number, pathname: string, containsSchemaChanges: boolean, pushDate?: string | undefined); + // (undocumented) + containsSchemaChanges: boolean; + // (undocumented) + id: string; + // (undocumented) + index: number; + // (undocumented) + parentId: string; + // (undocumented) + pathname: string; + // (undocumented) + pushDate?: string | undefined; +} + +// @public +interface ChangeSummary { + // (undocumented) + changeSet: { + description: string; + parentWsgId: GuidString; + pushDate: string; + userCreated: GuidString; + wsgId: GuidString; + } + // (undocumented) + id: Id64String; +} + +// @public (undocumented) +class ChangeSummaryExtractContext { + constructor(accessToken: AccessToken, iModel: IModelDb); + // (undocumented) + readonly accessToken: AccessToken; + // (undocumented) + readonly iModel: IModelDb; + // (undocumented) + readonly iModelId: GuidString; +} + +// @public +interface ChangeSummaryExtractOptions { + currentVersionOnly?: boolean; + startVersion?: IModelVersion; +} + +// @public +class ChangeSummaryManager { + static attachChangeCache(iModel: IModelDb): void; + static buildPropertyValueChangesECSql(iModel: IModelDb, instanceChangeInfo: { + id: Id64String; + summaryId: Id64String; + changedInstance: { + id: Id64String; + className: string; + }; + }, changedValueState: ChangedValueState, changedPropertyNames?: string[]): string; + static detachChangeCache(iModel: IModelDb): void; + // (undocumented) + static downloadChangeSets(actx: ActivityLoggingContext, ctx: ChangeSummaryExtractContext, startChangeSetId: GuidString, endChangeSetId: GuidString): Promise; + static extractChangeSummaries(actx: ActivityLoggingContext, accessToken: AccessToken, iModel: IModelDb, options?: ChangeSummaryExtractOptions): Promise; + static getChangedPropertyValueNames(iModel: IModelDb, instanceChangeId: Id64String): string[]; + static isChangeCacheAttached(iModel: IModelDb): boolean; + static queryChangeSummary(iModel: IModelDb, changeSummaryId: Id64String): ChangeSummary; + static queryInstanceChange(iModel: IModelDb, instanceChangeId: Id64String): InstanceChange; +} + +// @public +class ClassRegistry { + static findRegisteredClass(classFullName: string): typeof Entity | undefined; + static getClass(fullName: string, iModel: IModelDb): typeof Entity; + // (undocumented) + static getRegisteredSchema(domainName: string): Schema | undefined; + // (undocumented) + static getSchemaBaseClass(): typeof Schema; + // (undocumented) + static isNotFoundError(err: any): boolean; + // (undocumented) + static makeMetaDataNotFoundError(className: string): IModelError; + // (undocumented) + static register(entityClass: typeof Entity, schema: Schema): void; + static registerModule(moduleObj: any, schema: Schema): void; + // (undocumented) + static registerSchema(schema: Schema): void; +} + +// @public +class CodeSpecs { + constructor(imodel: IModelDb); + getById(codeSpecId: Id64String): CodeSpec; + getByName(name: string): CodeSpec; + hasId(codeSpecId: Id64String): boolean; + hasName(name: string): boolean; + insert(codeSpec: CodeSpec): Id64String; + load(id: Id64String): CodeSpec; + queryId(name: string): Id64String; +} + +// @public (undocumented) +class ConcurrencyControl { +} + +// @public +class DefinitionElement extends InformationContentElement, implements DefinitionElementProps { + constructor(props: ElementProps, iModel: IModelDb); + isPrivate: boolean; + // (undocumented) + toJSON(): DefinitionElementProps; +} + +// @public +class DefinitionModel extends InformationModel { + static insert(iModelDb: IModelDb, parentSubjectId: Id64String, name: string): Id64String; +} + +// @public +class DefinitionPartition extends InformationPartitionElement { +} + +// @public (undocumented) +class DetailCallout extends Callout { + constructor(props: CalloutProps, iModel: IModelDb); +} + +// @public (undocumented) +class DetailingSymbol extends GraphicalElement2d { + constructor(props: GeometricElement2dProps, iModel: IModelDb); +} + +// @public +class DictionaryModel extends DefinitionModel { +} + +// @public +class DisplayStyle extends DefinitionElement, implements DisplayStyleProps { + protected constructor(props: DisplayStyleProps, iModel: IModelDb); + static createCode(iModel: IModelDb, scopeModelId: CodeScopeProps, codeValue: string): Code; + // (undocumented) + readonly settings: DisplayStyleSettings; +} + +// @public +class DisplayStyle2d extends DisplayStyle { + constructor(props: DisplayStyleProps, iModel: IModelDb); + static create(iModelDb: IModelDb, definitionModelId: Id64String, name: string): DisplayStyle2d; + static insert(iModelDb: IModelDb, definitionModelId: Id64String, name: string): Id64String; + // (undocumented) + readonly settings: DisplayStyleSettings; +} + +// @public +class DisplayStyle3d extends DisplayStyle { + constructor(props: DisplayStyleProps, iModel: IModelDb); + static create(iModelDb: IModelDb, definitionModelId: Id64String, name: string, options?: DisplayStyleCreationOptions): DisplayStyle3d; + static insert(iModelDb: IModelDb, definitionModelId: Id64String, name: string, options?: DisplayStyleCreationOptions): Id64String; + // (undocumented) + readonly settings: DisplayStyle3dSettings; +} + +// @public +interface DisplayStyleCreationOptions { + // (undocumented) + analysisStyle?: AnalysisStyleProps; + // (undocumented) + backgroundColor?: ColorDef; + // (undocumented) + contextRealityModels?: ContextRealityModelProps[]; + // (undocumented) + scheduleScript?: object; + // (undocumented) + viewFlags?: ViewFlags; +} + +// @public +class Document extends InformationContentElement { + constructor(props: ElementProps, iModel: IModelDb); +} + +// @public +class DocumentCarrier extends InformationCarrierElement { + constructor(props: ElementProps, iModel: IModelDb); +} + +// @public +class DocumentListModel extends InformationModel { + static insert(iModelDb: IModelDb, parentSubjectId: Id64String, name: string): Id64String; +} + +// @public +class DocumentPartition extends InformationPartitionElement { +} + +// @public +class Drawing extends Document { + constructor(props: ElementProps, iModel: IModelDb); + static createCode(iModel: IModelDb, scopeModelId: CodeScopeProps, codeValue: string): Code; + static insert(iModelDb: IModelDb, documentListModelId: Id64String, name: string): Id64String; +} + +// @public +class DrawingCategory extends Category { + constructor(opts: ElementProps, iModel: IModelDb); + static create(iModelDb: IModelDb, definitionModelId: Id64String, name: string): DrawingCategory; + static createCode(iModel: IModelDb, scopeModelId: CodeScopeProps, codeValue: string): Code; + static getCodeSpecName(): string; + static insert(iModelDb: IModelDb, definitionModelId: Id64String, name: string, defaultAppearance: SubCategoryAppearance.Props): Id64String; + static queryCategoryIdByName(iModel: IModelDb, scopeModelId: Id64String, categoryName: string): Id64String | undefined; +} + +// @public +class DrawingGraphic extends GraphicalElement2d { + constructor(props: GeometricElement2dProps, iModel: IModelDb); +} + +// @public +class DrawingGraphicRepresentsElement extends ElementRefersToElements { +} + +// @public +class DrawingGraphicRepresentsFunctionalElement extends DrawingGraphicRepresentsElement { +} + +// @public +class DrawingModel extends GraphicalModel2d { +} + +// @public +class DrawingViewDefinition extends ViewDefinition2d { + constructor(props: ViewDefinition2dProps, iModel: IModelDb); + static create(iModelDb: IModelDb, definitionModelId: Id64String, name: string, baseModelId: Id64String, categorySelectorId: Id64String, displayStyleId: Id64String, range: Range2d): DrawingViewDefinition; + static insert(iModelDb: IModelDb, definitionModelId: Id64String, name: string, baseModelId: Id64String, categorySelectorId: Id64String, displayStyleId: Id64String, range: Range2d): Id64String; +} + +// @public +class DriverBundleElement extends InformationContentElement { + constructor(props: ElementProps, iModel: IModelDb); +} + +// @public +class ECDb implements IDisposable { + constructor(); + abandonChanges(): void; + closeDb(): void; + createDb(pathName: string): void; + dispose(): void; + importSchema(pathName: string): void; + readonly isOpen: boolean; + // WARNING: The type "IModelJsNative.ECDb" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly nativeDb: IModelJsNative.ECDb; + openDb(pathName: string, openMode?: ECDbOpenMode): void; + prepareSqliteStatement(sql: string): SqliteStatement; + prepareStatement(ecsql: string): ECSqlStatement; + saveChanges(changeSetName?: string): void; + withPreparedSqliteStatement(sql: string, cb: (stmt: SqliteStatement) => T): T; + withPreparedStatement(ecsql: string, cb: (stmt: ECSqlStatement) => T): T; +} + +// @public +enum ECDbOpenMode { + FileUpgrade = 2, + // (undocumented) + Readonly = 0, + // (undocumented) + Readwrite = 1 +} + +// @public +interface ECEnumValue { + // (undocumented) + key: string; + // (undocumented) + name: string; + // (undocumented) + schema: string; + // (undocumented) + value: number | string; +} + +// @public (undocumented) +class ECSchemaXmlContext { + constructor(); + // (undocumented) + addSchemaPath(searchPath: string): void; + // (undocumented) + readSchemaFromXmlFile(filePath: string): any; + // WARNING: The type "IModelJsNative.ECSchemaXmlContext.SchemaLocaterCallback" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + setSchemaLocater(locater: IModelJsNative.ECSchemaXmlContext.SchemaLocaterCallback): void; +} + +// @public +class ECSqlBinder { + // WARNING: The type "IModelJsNative.ECSqlBinder" needs to be exported by the package (e.g. added to index.ts) + constructor(binder: IModelJsNative.ECSqlBinder); + addArrayElement(): ECSqlBinder; + bind(val: any): void; + bindArray(val: any[]): void; + bindBlob(blob: string | Uint8Array | ArrayBuffer | SharedArrayBuffer): void; + bindBoolean(val: boolean): void; + bindDateTime(isoDateTimeString: string): void; + bindDouble(val: number): void; + bindGuid(val: GuidString): void; + bindId(val: Id64String): void; + bindInteger(val: number | string): void; + bindMember(memberName: string): ECSqlBinder; + bindNavigation(val: NavigationBindingValue): void; + bindNull(): void; + bindPoint2d(val: XAndY): void; + bindPoint3d(val: XYAndZ): void; + bindRange3d(val: LowAndHighXYZ): void; + bindString(val: string): void; + bindStruct(val: object): void; +} + +// @public +interface ECSqlColumnInfo { + getAccessString(): string; + getPropertyName(): string; + getRootClassAlias(): string; + getRootClassName(): string; + getRootClassTableSpace(): string; + getType(): ECSqlValueType; + isEnum(): boolean; + isGeneratedProperty(): boolean; + isSystemProperty(): boolean; +} + +// @public +class ECSqlInsertResult { + constructor(status: DbResult, id?: string | undefined); + // (undocumented) + id?: string | undefined; + // (undocumented) + status: DbResult; +} + +// @public +class ECSqlStatement implements IterableIterator, IDisposable { + // WARNING: The name "__@iterator" contains unsupported characters; API names should use only letters, numbers, and underscores + [Symbol.iterator](): IterableIterator; + bindArray(parameter: number | string, val: any[]): void; + bindBlob(parameter: number | string, blob: string | Uint8Array | ArrayBuffer | SharedArrayBuffer): void; + bindBoolean(parameter: number | string, val: boolean): void; + bindDateTime(parameter: number | string, isoDateTimeString: string): void; + bindDouble(parameter: number | string, val: number): void; + bindGuid(parameter: number | string, val: GuidString): void; + bindId(parameter: number | string, val: Id64String): void; + bindInteger(parameter: number | string, val: number | string): void; + bindNavigation(parameter: number | string, val: NavigationBindingValue): void; + bindNull(parameter: number | string): void; + bindPoint2d(parameter: number | string, val: XAndY): void; + bindPoint3d(parameter: number | string, val: XYAndZ): void; + bindRange3d(parameter: number | string, val: LowAndHighXYZ): void; + bindString(parameter: number | string, val: string): void; + bindStruct(parameter: number | string, val: object): void; + bindValue(parameter: number | string, val: any): void; + bindValues(values: any[] | object): void; + clearBindings(): void; + dispose(): void; + getBinder(parameter: string | number): ECSqlBinder; + getColumnCount(): number; + getRow(): any; + getValue(columnIx: number): ECSqlValue; + readonly isPrepared: boolean; + readonly isShared: boolean; + next(): IteratorResult; + // WARNING: The type "IModelJsNative.DgnDb" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "IModelJsNative.ECDb" needs to be exported by the package (e.g. added to index.ts) + prepare(db: IModelJsNative.DgnDb | IModelJsNative.ECDb, ecsql: string): void; + reset(): void; + setIsShared(b: boolean): void; + step(): DbResult; + stepForInsert(): ECSqlInsertResult; +} + +// @public +class ECSqlStatementCache { + constructor(maxCount?: number); + // (undocumented) + add(str: string, stmt: ECSqlStatement): void; + // (undocumented) + clear(): void; + // (undocumented) + find(str: string): CachedECSqlStatement | undefined; + // (undocumented) + getCount(): number; + // (undocumented) + readonly maxCount: number; + // (undocumented) + release(stmt: ECSqlStatement): void; + // (undocumented) + removeUnusedStatementsIfNecessary(): void; +} + +// @public +class ECSqlValue { + // WARNING: The type "IModelJsNative.ECSqlValue" needs to be exported by the package (e.g. added to index.ts) + constructor(val: IModelJsNative.ECSqlValue); + readonly columnInfo: ECSqlColumnInfo; + getArray(): any[]; + getArrayIterator(): ECSqlValueIterator; + getBlob(): Uint8Array; + getBoolean(): boolean; + getClassNameForClassId(): string; + getDateTime(): string; + getDouble(): number; + getEnum(): ECEnumValue[] | undefined; + getGeometry(): any; + getGuid(): GuidString; + getId(): Id64String; + getInteger(): number; + getNavigation(): NavigationValue; + getString(): string; + getStruct(): any; + getStructIterator(): ECSqlValueIterator; + getXAndY(): XAndY; + getXYAndZ(): XYAndZ; + readonly isNull: boolean; + readonly value: any; +} + +// @public +class ECSqlValueIterator implements IterableIterator { + // WARNING: The name "__@iterator" contains unsupported characters; API names should use only letters, numbers, and underscores + // (undocumented) + [Symbol.iterator](): IterableIterator; + // WARNING: The type "IModelJsNative.ECSqlValueIterator" needs to be exported by the package (e.g. added to index.ts) + constructor(it: IModelJsNative.ECSqlValueIterator); + // (undocumented) + next(): IteratorResult; +} + +// @public +class Element extends Entity, implements ElementProps { + constructor(props: ElementProps, iModel: IModelDb); + buildConcurrencyControlRequest(opcode: DbOpcode): void; + readonly code: Code; + delete(): void; + federationGuid?: GuidString; + getClassMetaData(): EntityMetaData | undefined; + getDisplayLabel(): string; + getJsonProperty(nameSpace: string): any; + getToolTipMessage(): string[]; + getUserProperties(namespace: string): any; + insert(): string; + readonly jsonProperties: { + [key: string]: any; + } + readonly model: Id64String; + // (undocumented) + static onAllInputsHandled(_id: Id64String, _iModel: IModelDb): void; + // (undocumented) + static onBeforeOutputsHandled(_id: Id64String, _iModel: IModelDb): void; + // (undocumented) + static onDelete(_props: ElementProps, _iModel: IModelDb): IModelStatus; + // (undocumented) + static onDeleted(_props: ElementProps, _iModel: IModelDb): void; + // (undocumented) + static onInsert(_props: ElementProps, _iModel: IModelDb): IModelStatus; + // (undocumented) + static onInserted(_props: ElementProps, _iModel: IModelDb): void; + // (undocumented) + static onUpdate(_props: ElementProps, _iModel: IModelDb): IModelStatus; + // (undocumented) + static onUpdated(_props: ElementProps, _iModel: IModelDb): void; + parent?: RelatedElement; + removeUserProperties(nameSpace: string): void; + // (undocumented) + setJsonProperty(nameSpace: string, value: any): void; + setUserProperties(nameSpace: string, value: any): void; + toJSON(): ElementProps; + update(): void; + userLabel?: string; +} + +// @public +class ElementAspect extends Entity, implements ElementAspectProps { + constructor(props: ElementAspectProps, iModel: IModelDb); + // (undocumented) + element: RelatedElement; + // (undocumented) + toJSON(): ElementAspectProps; +} + +// @public +class ElementDrivesElement extends Relationship, implements ElementDrivesElementProps { + constructor(props: ElementDrivesElementProps, iModel: IModelDb); + // (undocumented) + static create(iModel: IModelDb, sourceId: Id64String, targetId: Id64String, priority?: number): T; + // (undocumented) + priority: number; + // (undocumented) + status: number; +} + +// @public +interface ElementDrivesElementProps extends RelationshipProps { + // (undocumented) + priority: number; + // (undocumented) + status: number; +} + +// @public +class ElementEncapsulatesElements extends ElementOwnsChildElements { + constructor(parentId: Id64String, relClassName?: string); + // (undocumented) + static classFullName: string; +} + +// @public +class ElementGroupsMembers extends ElementRefersToElements { + constructor(props: ElementGroupsMembersProps, iModel: IModelDb); + // (undocumented) + static create(iModel: IModelDb, sourceId: Id64String, targetId: Id64String, memberPriority?: number): T; + // (undocumented) + memberPriority: number; +} + +// @public +interface ElementGroupsMembersProps extends RelationshipProps { + // (undocumented) + memberPriority: number; +} + +// @public +class ElementMultiAspect extends ElementAspect { +} + +// @public +class ElementOwnsChildElements extends RelatedElement { + constructor(parentId: Id64String, relClassName?: string); + // (undocumented) + static classFullName: string; +} + +// @public (undocumented) +class ElementPropertyFormatter { + constructor(iModel: IModelDb); + formatProperties(elem: Element): any; +} + +// @public +class ElementRefersToElements extends Relationship { + static create(iModel: IModelDb, sourceId: Id64String, targetId: Id64String): T; + static insert(iModel: IModelDb, sourceId: Id64String, targetId: Id64String): Id64String; +} + +// @public +class ElementUniqueAspect extends ElementAspect { +} + +// @public (undocumented) +class ElevationCallout extends Callout { + constructor(props: CalloutProps, iModel: IModelDb); +} + +// @public +class EmbeddedFileLink extends LinkElement { +} + +// @public +class Entity implements EntityProps { + constructor(props: EntityProps, iModel: IModelDb); + // (undocumented) + [propName: string]: any; + buildConcurrencyControlRequest(_opcode: DbOpcode): void; + static readonly classFullName: string; + readonly className: string; + clone(): this; + forEachProperty(func: PropertyCallback, includeCustom?: boolean): void; + id: Id64String; + iModel: IModelDb; + static schema: Schema; + readonly schemaName: string; + // (undocumented) + toJSON(): EntityProps; +} + +// @public +enum ExclusiveAccessOption { + CreateNewBriefcase = 1, + TryReuseOpenBriefcase = 2 +} + +// @public (undocumented) +class Functional extends Schema { + // (undocumented) + static importSchema(activityLoggingContext: ActivityLoggingContext, iModelDb: IModelDb, accessToken?: AccessToken): Promise; + // (undocumented) + static registerSchema(): void; +} + +// @public +class FunctionalBreakdownElement extends FunctionalElement { + constructor(props: FunctionalElementProps, iModel: IModelDb); +} + +// @public +class FunctionalComponentElement extends FunctionalElement { + constructor(props: FunctionalElementProps, iModel: IModelDb); +} + +// @public (undocumented) +class FunctionalComposite extends FunctionalBreakdownElement { + constructor(props: FunctionalElementProps, iModel: IModelDb); +} + +// @public +class FunctionalElement extends RoleElement, implements FunctionalElementProps { + constructor(props: FunctionalElementProps, iModel: IModelDb); +} + +// @public +class FunctionalElementIsOfType extends RelatedElement { + constructor(id: Id64String, relClassName?: string); + // (undocumented) + static classFullName: string; +} + +// @public +class FunctionalModel extends RoleModel { + constructor(props: ModelProps, iModel: IModelDb); + static insert(iModelDb: IModelDb, parentSubjectId: Id64String, name: string): Id64String; +} + +// @public +class FunctionalPartition extends InformationPartitionElement { + constructor(props: InformationPartitionElementProps, iModel: IModelDb); +} + +// @public +class FunctionalType extends TypeDefinitionElement { + constructor(props: TypeDefinitionElementProps, iModel: IModelDb); +} + +// @public (undocumented) +class Generic extends Schema { + // (undocumented) + static registerSchema(): void; +} + +// @public +class GeometricElement extends Element, implements GeometricElementProps { + constructor(props: GeometricElementProps, iModel: IModelDb); + // (undocumented) + calculateRange3d(): AxisAlignedBox3d; + category: Id64String; + geom?: GeometryStreamProps; + getPlacementTransform(): Transform; + is2d(): this is GeometricElement2d; + is3d(): this is GeometricElement3d; + toJSON(): GeometricElementProps; +} + +// @public +class GeometricElement2d extends GeometricElement, implements GeometricElement2dProps { + constructor(props: GeometricElement2dProps, iModel: IModelDb); + // (undocumented) + placement: Placement2d; + // (undocumented) + toJSON(): GeometricElement2dProps; + // (undocumented) + typeDefinition?: TypeDefinition; +} + +// @public +class GeometricElement3d extends GeometricElement, implements GeometricElement3dProps { + constructor(props: GeometricElement3dProps, iModel: IModelDb); + // (undocumented) + placement: Placement3d; + // (undocumented) + toJSON(): GeometricElement3dProps; + // (undocumented) + typeDefinition?: TypeDefinition; +} + +// @public +class GeometricModel extends Model { + queryExtents(): AxisAlignedBox3d; +} + +// @public +class GeometricModel2d extends GeometricModel, implements GeometricModel2dProps { + // (undocumented) + globalOrigin?: Point2d; +} + +// @public +class GeometricModel3d extends GeometricModel { +} + +// @public +class GeometryPart extends DefinitionElement, implements GeometryPartProps { + constructor(props: GeometryPartProps, iModel: IModelDb); + // (undocumented) + bbox: ElementAlignedBox3d; + static createCode(iModel: IModelDb, scopeModelId: CodeScopeProps, codeValue: string): Code; + // (undocumented) + geom?: GeometryStreamProps; + toJSON(): GeometryPartProps; +} + +// @public (undocumented) +class Graphic3d extends GraphicalElement3d { + constructor(props: GeometricElement3dProps, iModel: IModelDb); +} + +// @public +class GraphicalElement2d extends GeometricElement2d { + constructor(props: GeometricElement2dProps, iModel: IModelDb); +} + +// @public +class GraphicalElement2dIsOfType extends RelatedElement { + constructor(id: Id64String, relClassName?: string); + // (undocumented) + static classFullName: string; +} + +// @public +class GraphicalElement3d extends GeometricElement3d { + constructor(props: GeometricElement3dProps, iModel: IModelDb); +} + +// @public +class GraphicalModel2d extends GeometricModel2d { +} + +// @public +class GraphicalType2d extends TypeDefinitionElement { + constructor(props: ElementProps, iModel: IModelDb); + static createCode(iModel: IModelDb, scopeModelId: CodeScopeProps, codeValue: string): Code; +} + +// @public (undocumented) +class Group extends GroupInformationElement { + constructor(props: ElementProps, iModel: IModelDb); +} + +// @public +class GroupInformationElement extends InformationReferenceElement { +} + +// @public +class GroupInformationModel extends InformationModel { +} + +// @public +class GroupInformationPartition extends InformationPartitionElement { +} + +// @public (undocumented) +class GroupModel extends GroupInformationModel { + constructor(props: ModelProps, iModel: IModelDb); +} + +// @public (undocumented) +class IModelDb { +} + +// @public +class IModelHost { + static readonly appAssetsDir: string | undefined; + // (undocumented) + static backendVersion: string; + // (undocumented) + static configuration?: IModelHostConfiguration; + // (undocumented) + static loadNative(region: number, dir?: string): void; + static readonly onAfterStartup: BeEvent<() => void>; + static readonly onBeforeShutdown: BeEvent<() => void>; + // (undocumented) + static readonly platform: typeof IModelJsNative; + static shutdown(): void; + static startup(configuration?: IModelHostConfiguration): void; +} + +// @public +class IModelHostConfiguration { + appAssetsDir?: string; + briefcaseCacheDir: string; + imodelClient?: IModelClient; + nativePlatform?: any; +} + +// @public (undocumented) +class IModelImporter { + constructor(sourceDb: IModelDb, targetDb: IModelDb); + // (undocumented) + protected _excludedCodeSpecIds: Set; + // (undocumented) + protected _excludedCodeSpecNames: Set; + // (undocumented) + protected _excludedElementClassNames: Set; + // (undocumented) + protected _excludedElementIds: Set; + // (undocumented) + addCodeSpecId(sourceId: Id64String, targetId: Id64String): void; + // (undocumented) + addElementId(sourceId: Id64String, targetId: Id64String): void; + // (undocumented) + dispose(): void; + // (undocumented) + excludeCodeSpec(codeSpecName: string): void; + // (undocumented) + excludeElementClass(classFullName: string): void; + // (undocumented) + excludeElementId(elementId: Id64String): void; + // (undocumented) + excludeSubject(subjectPath: string): void; + // (undocumented) + findCodeSpecId(sourceId: Id64String): Id64String; + // (undocumented) + findElementId(sourceId: Id64String): Id64String; + // (undocumented) + import(): void; + // (undocumented) + importCodeSpec(sourceId: Id64String): Id64String; + // (undocumented) + importCodeSpecs(): void; + // (undocumented) + importElement(sourceElementId: Id64String): Id64String; + // (undocumented) + importFonts(): void; + // (undocumented) + importModel(sourceModeledElementId: Id64String): void; + // (undocumented) + importModelContents(modelId: Id64String): void; + // (undocumented) + importModels(modeledElementClass: string): void; + // (undocumented) + importRelationships(): void; + // (undocumented) + static resolveSubjectId(iModelDb: IModelDb, subjectPath: string): Id64String | undefined; +} + +// @public +class IModelJsExpressServer { + // WARNING: The type "ExpressApp" needs to be exported by the package (e.g. added to index.ts) + constructor(app: ExpressApp, protocol: WebAppRpcProtocol); + // WARNING: The type "ExpressApp" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected _app: ExpressApp; + // (undocumented) + protected _configureHeaders(): void; + // (undocumented) + protected _configureMiddleware(): void; + // (undocumented) + protected _configureRoutes(): void; + // WARNING: The type "HttpServer" needs to be exported by the package (e.g. added to index.ts) + initialize(port: number | string): Promise; +} + +// @public +class IModelJsFs { + static appendFileSync(path: string, str: string): void; + static copySync(src: string, dest: string, opts?: any): void; + static existsSync(path: string): boolean; + static lstatSync(path: string): IModelJsFsStats | undefined; + static mkdirSync(path: string): void; + static readdirSync(path: string): string[]; + // (undocumented) + static readFileSync(path: string): string | Buffer; + static removeSync(path: string): void; + static rmdirSync(path: string): void; + static unlinkSync(path: string): void; + static writeFileSync(path: string, str: string, wflag?: string): void; +} + +// @public +class IModelJsFsStats { + constructor(size: number, atimeMs: number, mtimeMs: number, birthtimeMs: number, isDirectory: boolean, isFile: boolean, isSocket: boolean, isSymbolicLink: boolean, isReadOnly: boolean); + // (undocumented) + atimeMs: number; + // (undocumented) + birthtimeMs: number; + // (undocumented) + isDirectory: boolean; + // (undocumented) + isFile: boolean; + // (undocumented) + isReadOnly: boolean; + // (undocumented) + isSocket: boolean; + // (undocumented) + isSymbolicLink: boolean; + // (undocumented) + mtimeMs: number; + // (undocumented) + size: number; +} + +// WARNING: Unsupported export: version +// WARNING: Unsupported export: logger +// WARNING: Unsupported export: TxnIdString +// @public +module IModelJsNative { + interface BriefcaseManagerOnConflictPolicy { + deleteVsUpdate: number; + updateVsDelete: number; + updateVsUpdate: number; + } + + // (undocumented) + class BriefcaseManagerResourcesRequest { + // (undocumented) + isEmpty(): boolean; + // (undocumented) + reset(): void; + // (undocumented) + toJSON(): string; + } + + // (undocumented) + class ChangedElementsECDb implements IDisposable { + constructor(); + // (undocumented) + closeDb(): void; + // WARNING: The type "DgnDb" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + createDb(db: DgnDb, dbName: string): DbResult; + // (undocumented) + dispose(): void; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getChangedElements(startChangesetId: string, endChangesetId: string): ErrorStatusOrResult; + // (undocumented) + isOpen(): boolean; + // (undocumented) + isProcessed(changesetId: string): boolean; + // (undocumented) + openDb(dbName: string, mode: OpenMode, upgradeProfiles?: boolean): DbResult; + // WARNING: The type "DgnDb" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + processChangesets(db: DgnDb, changesets: string, rulesetId: string, filterSpatial: boolean): DbResult; + } + + class DgnDb { + constructor(); + // (undocumented) + abandonChanges(): DbResult; + // (undocumented) + abandonCreateChangeSet(): void; + // (undocumented) + addPendingChangeSet(changeSetId: string): DbResult; + // WARNING: The type "BriefcaseManagerResourcesRequest" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "BriefcaseManagerResourcesRequest" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + appendBriefcaseManagerResourcesRequest(reqOut: BriefcaseManagerResourcesRequest, reqIn: BriefcaseManagerResourcesRequest): void; + // (undocumented) + applyChangeSets(changeSets: string, processOptions: ChangeSetApplyOption): ChangeSetStatus; + // (undocumented) + attachChangeCache(changeCachePath: string): DbResult; + // (undocumented) + beginMultiTxnOperation(): DbResult; + // (undocumented) + briefcaseManagerEndBulkOperation(): RepositoryStatus; + // (undocumented) + briefcaseManagerStartBulkOperation(): RepositoryStatus; + // WARNING: The type "BriefcaseManagerResourcesRequest" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + buildBriefcaseManagerResourcesRequestForElement(req: BriefcaseManagerResourcesRequest, elemId: string, opcode: DbOpcode): RepositoryStatus; + // WARNING: The type "BriefcaseManagerResourcesRequest" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + buildBriefcaseManagerResourcesRequestForLinkTableRelationship(req: BriefcaseManagerResourcesRequest, relKey: string, opcode: DbOpcode): RepositoryStatus; + // WARNING: The type "BriefcaseManagerResourcesRequest" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + buildBriefcaseManagerResourcesRequestForModel(req: BriefcaseManagerResourcesRequest, modelId: string, opcode: DbOpcode): RepositoryStatus; + // WARNING: The type "TxnIdString" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + cancelTo(txnId: TxnIdString): IModelStatus; + // (undocumented) + closeIModel(): void; + // WARNING: The type "ECDb" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + createChangeCache(changeCacheFile: ECDb, changeCachePath: string): DbResult; + // (undocumented) + createIModel(accessToken: string, appVersion: string, projectId: GuidString, fileName: string, props: string): DbResult; + // (undocumented) + createStandaloneIModel(fileName: string, props: string): DbResult; + // (undocumented) + deleteElement(elemIdJson: string): IModelStatus; + // (undocumented) + deleteElementAspect(aspectIdJson: string): IModelStatus; + // (undocumented) + deleteLinkTableRelationship(props: string): DbResult; + // (undocumented) + deleteModel(modelIdJson: string): IModelStatus; + // (undocumented) + detachChangeCache(): number; + // (undocumented) + dumpChangeSet(changeSet: string): void; + // (undocumented) + embedFont(fontProps: string): string; + // (undocumented) + enableTxnTesting(): void; + // (undocumented) + endMultiTxnOperation(): DbResult; + // (undocumented) + executeTest(testName: string, params: string): string; + // WARNING: The type "BriefcaseManagerResourcesRequest" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "BriefcaseManagerResourcesRequest" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + extractBriefcaseManagerResourcesRequest(reqOut: BriefcaseManagerResourcesRequest, reqIn: BriefcaseManagerResourcesRequest, locks: boolean, codes: boolean): void; + // WARNING: The type "BriefcaseManagerResourcesRequest" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + extractBulkResourcesRequest(req: BriefcaseManagerResourcesRequest, locks: boolean, codes: boolean): void; + // WARNING: The type "ECDb" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + extractChangeSummary(changeCacheFile: ECDb, changesetFilePath: string): ErrorStatusOrResult; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + extractCodes(): ErrorStatusOrResult; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + extractCodesFromFile(changeSets: string): ErrorStatusOrResult; + // (undocumented) + finishCreateChangeSet(): ChangeSetStatus; + // (undocumented) + static getAssetsDir(): string; + // (undocumented) + getBriefcaseId(): number; + // WARNING: The type "TxnIdString" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getCurrentTxnId(): TxnIdString; + // (undocumented) + getDbGuid(): GuidString; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getECClassMetaData(schema: string, className: string): ErrorStatusOrResult; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getElement(opts: string): ErrorStatusOrResult; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getElementPropertiesForDisplay(id: string): ErrorStatusOrResult; + // (undocumented) + getGeoCoordinatesFromIModelCoordinates(points: string): string; + // (undocumented) + getIModelCoordinatesFromGeoCoordinates(points: string): string; + // (undocumented) + getIModelProps(): string; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getModel(opts: string): ErrorStatusOrResult; + // (undocumented) + getMultiTxnOperationDepth(): number; + // (undocumented) + getParentChangeSetId(): string; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getPendingChangeSets(): ErrorStatusOrResult; + // (undocumented) + getRedoString(): string; + // (undocumented) + getReversedChangeSetId(): string | undefined; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getSchema(name: string): ErrorStatusOrResult; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getSchemaItem(schemaName: string, itemName: string): ErrorStatusOrResult; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getTileContent(treeId: string, tileId: string, callback: (result: ErrorStatusOrResult) => void): void; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getTileTree(id: string, callback: (result: ErrorStatusOrResult) => void): void; + // WARNING: The type "TxnIdString" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getTxnDescription(txnId: TxnIdString): string; + // (undocumented) + getUndoString(): string; + // (undocumented) + hasFatalTxnError(): boolean; + // (undocumented) + hasUnsavedChanges(): boolean; + // (undocumented) + importFunctionalSchema(): DbResult; + // (undocumented) + importSchema(schemaPathname: string): DbResult; + // (undocumented) + inBulkOperation(): boolean; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + insertCodeSpec(name: string, specType: number, scopeReq: number): ErrorStatusOrResult; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + insertElement(elemProps: string): ErrorStatusOrResult; + // (undocumented) + insertElementAspect(aspectProps: string): IModelStatus; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + insertLinkTableRelationship(props: string): ErrorStatusOrResult; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + insertModel(modelProps: string): ErrorStatusOrResult; + // (undocumented) + isChangeCacheAttached(): boolean; + // (undocumented) + isOpen(): boolean; + // (undocumented) + isRedoPossible(): boolean; + // WARNING: The type "TxnIdString" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + isTxnIdValid(txnId: TxnIdString): boolean; + // (undocumented) + isUndoPossible(): boolean; + // (undocumented) + logTxnError(fatal: boolean): void; + // (undocumented) + openIModel(accessToken: string, appVersion: string, projectId: GuidString, dbName: string, mode: OpenMode): DbResult; + // (undocumented) + openIModelFile(dbName: string, mode: OpenMode): DbResult; + // (undocumented) + queryFileProperty(props: string, wantString: boolean): string | Uint8Array | undefined; + // WARNING: The type "TxnIdString" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + queryFirstTxnId(): TxnIdString; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + queryModelExtents(options: string): ErrorStatusOrResult; + // (undocumented) + queryNextAvailableFileProperty(props: string): number; + // WARNING: The type "TxnIdString" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "TxnIdString" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + queryNextTxnId(txnId: TxnIdString): TxnIdString; + // WARNING: The type "TxnIdString" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "TxnIdString" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + queryPreviousTxnId(txnId: TxnIdString): TxnIdString; + // (undocumented) + readFontMap(): string; + // (undocumented) + reinstateTxn(): IModelStatus; + // (undocumented) + removePendingChangeSet(changeSetId: string): DbResult; + // (undocumented) + reverseAll(): IModelStatus; + // WARNING: The type "TxnIdString" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + reverseTo(txnId: TxnIdString): IModelStatus; + // (undocumented) + reverseTxns(numOperations: number): IModelStatus; + // (undocumented) + saveChanges(description?: string): DbResult; + // (undocumented) + saveFileProperty(props: string, strValue: string | undefined, blobVal: Uint8Array | undefined): number; + // (undocumented) + setAsMaster(guid?: GuidString): DbResult; + // (undocumented) + setBriefcaseId(idValue: number): DbResult; + // WARNING: The type "BriefcaseManagerOnConflictPolicy" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + setBriefcaseManagerOptimisticConcurrencyControlPolicy(conflictPolicy: BriefcaseManagerOnConflictPolicy): RepositoryStatus; + // (undocumented) + setBriefcaseManagerPessimisticConcurrencyControlPolicy(): RepositoryStatus; + // (undocumented) + setDbGuid(guid: GuidString): DbResult; + // (undocumented) + setIModelDb(iModelDb?: IModelDb): void; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + startCreateChangeSet(): ErrorStatusOrResult; + // (undocumented) + updateElement(elemProps: string): IModelStatus; + // (undocumented) + updateElementAspect(aspectProps: string): IModelStatus; + // (undocumented) + updateIModelProps(props: string): void; + // (undocumented) + updateLinkTableRelationship(props: string): DbResult; + // (undocumented) + updateModel(modelProps: string): IModelStatus; + // (undocumented) + updateProjectExtents(newExtentsJson: string): void; + } + + // (undocumented) + class DisableNativeAssertions implements IDisposable { + constructor(); + // (undocumented) + dispose(): void; + } + + // (undocumented) + class ECDb implements IDisposable { + constructor(); + // (undocumented) + abandonChanges(): DbResult; + // (undocumented) + closeDb(): void; + // (undocumented) + createDb(dbName: string): DbResult; + // (undocumented) + dispose(): void; + // (undocumented) + importSchema(schemaPathName: string): DbResult; + // (undocumented) + isOpen(): boolean; + // (undocumented) + openDb(dbName: string, mode: OpenMode, upgradeProfiles?: boolean): DbResult; + // (undocumented) + saveChanges(changesetName?: string): DbResult; + } + + // (undocumented) + class ECPresentationManager implements IDisposable { + constructor(); + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "ECPresentationStatus" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + addRuleset(serializedRuleset: string): ErrorStatusOrResult; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "ECPresentationStatus" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + clearRulesets(): ErrorStatusOrResult; + // (undocumented) + dispose(): void; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "ECPresentationStatus" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getRulesets(rulesetId: string): ErrorStatusOrResult; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "ECPresentationStatus" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getRulesetVariableValue(rulesetId: string, variableId: string, type: string): ErrorStatusOrResult; + // WARNING: The type "DgnDb" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "ECPresentationStatus" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + handleRequest(db: DgnDb, options: string, callback: (result: ErrorStatusOrResult) => void): void; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "ECPresentationStatus" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + removeRuleset(rulesetId: string, hash: string): ErrorStatusOrResult; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "ECPresentationStatus" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + setRulesetVariableValue(rulesetId: string, variableId: string, type: string, value: any): ErrorStatusOrResult; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "ECPresentationStatus" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + setupLocaleDirectories(directories: string[]): ErrorStatusOrResult; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "ECPresentationStatus" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + setupRulesetDirectories(directories: string[]): ErrorStatusOrResult; + } + + // (undocumented) + enum ECPresentationStatus { + // (undocumented) + Error = 1, + // (undocumented) + InvalidArgument = 2, + // (undocumented) + Success = 0 + } + + // (undocumented) + class ECSchemaXmlContext { + constructor(); + // (undocumented) + addSchemaPath(path: string): void; + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readSchemaFromXmlFile(filePath: string): ErrorStatusOrResult; + // WARNING: The type "ECSchemaXmlContext.SchemaLocaterCallback" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + setSchemaLocater(locater: ECSchemaXmlContext.SchemaLocaterCallback): void; + } + + // (undocumented) + class ECSqlBinder { + constructor(); + // WARNING: The type "ECSqlBinder" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + addArrayElement(): ECSqlBinder; + // (undocumented) + bindBlob(base64String: string | Uint8Array | ArrayBuffer | SharedArrayBuffer): DbResult; + // (undocumented) + bindBoolean(val: boolean): DbResult; + // (undocumented) + bindDateTime(isoString: string): DbResult; + // (undocumented) + bindDouble(val: number): DbResult; + // (undocumented) + bindGuid(guidStr: GuidString): DbResult; + // (undocumented) + bindId(hexStr: Id64String): DbResult; + // (undocumented) + bindInteger(val: number | string): DbResult; + // WARNING: The type "ECSqlBinder" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + bindMember(memberName: string): ECSqlBinder; + // (undocumented) + bindNavigation(navIdHexStr: Id64String, relClassName?: string, relClassTableSpace?: string): DbResult; + // (undocumented) + bindNull(): DbResult; + // (undocumented) + bindPoint2d(x: number, y: number): DbResult; + // (undocumented) + bindPoint3d(x: number, y: number, z: number): DbResult; + // (undocumented) + bindString(val: string): DbResult; + } + + // (undocumented) + class ECSqlColumnInfo { + constructor(); + // (undocumented) + getAccessString(): string; + // (undocumented) + getPropertyName(): string; + // (undocumented) + getRootClassAlias(): string; + // (undocumented) + getRootClassName(): string; + // (undocumented) + getRootClassTableSpace(): string; + // (undocumented) + getType(): number; + // (undocumented) + isEnum(): boolean; + // (undocumented) + isGeneratedProperty(): boolean; + // (undocumented) + isSystemProperty(): boolean; + } + + // (undocumented) + class ECSqlStatement implements IDisposable { + constructor(); + // (undocumented) + clearBindings(): DbResult; + // (undocumented) + dispose(): void; + // WARNING: The type "ECSqlBinder" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getBinder(param: number | string): ECSqlBinder; + // (undocumented) + getColumnCount(): number; + // WARNING: The type "ECSqlValue" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getValue(columnIndex: number): ECSqlValue; + // WARNING: The type "DgnDb" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "ECDb" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + prepare(db: DgnDb | ECDb, ecsql: string): StatusCodeWithMessage; + // (undocumented) + reset(): DbResult; + // (undocumented) + step(): DbResult; + // (undocumented) + stepForInsert: { + id: string; + status: DbResult; + } + } + + // (undocumented) + class ECSqlValue { + constructor(); + // WARNING: The type "ECSqlValueIterator" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getArrayIterator(): ECSqlValueIterator; + // (undocumented) + getBlob(): Uint8Array; + // (undocumented) + getBoolean(): boolean; + // (undocumented) + getClassNameForClassId(): string; + // WARNING: The type "ECSqlColumnInfo" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getColumnInfo(): ECSqlColumnInfo; + // (undocumented) + getDateTime(): string; + // (undocumented) + getDouble(): number; + // (undocumented) + getEnum(): Array<{ + schema: string; + name: string; + key: string; + value: number | string; + }> | undefined; + // (undocumented) + getGeometry(): string; + // (undocumented) + getGuid(): GuidString; + // (undocumented) + getId(): Id64String; + // (undocumented) + getInt(): number; + // (undocumented) + getInt64(): number; + // (undocumented) + getNavigation: { + id: Id64String; + relClassName?: string; + } + // (undocumented) + getPoint2d: { + x: number; + y: number; + } + // (undocumented) + getPoint3d: { + x: number; + y: number; + z: number; + } + // (undocumented) + getString(): string; + // WARNING: The type "ECSqlValueIterator" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getStructIterator(): ECSqlValueIterator; + // (undocumented) + isNull(): boolean; + } + + // (undocumented) + class ECSqlValueIterator { + constructor(); + // WARNING: The type "ECSqlValue" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getCurrent(): ECSqlValue; + // (undocumented) + moveNext(): boolean; + } + + interface ErrorStatusOrResult { + error?: StatusCodeWithMessage; + result?: ResultType; + } + + // (undocumented) + class ImportContext implements IDisposable { + // WARNING: The type "DgnDb" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "DgnDb" needs to be exported by the package (e.g. added to index.ts) + constructor(sourceDb: DgnDb, targetDb: DgnDb); + // (undocumented) + addCodeSpecId(sourceId: Id64String, targetId: Id64String): BentleyStatus; + // (undocumented) + addElementId(sourceId: Id64String, targetId: Id64String): BentleyStatus; + // (undocumented) + cloneElement(sourceId: Id64String): ElementProps; + // (undocumented) + dispose(): void; + // (undocumented) + findCodeSpecId(sourceId: Id64String): Id64String; + // (undocumented) + findElementId(sourceId: Id64String): Id64String; + // (undocumented) + importCodeSpec(sourceId: Id64String): Id64String; + // (undocumented) + importFont(sourceId: number): number; + } + + // (undocumented) + function initializeRegion(region: number): void; + + // (undocumented) + class SnapRequest { + constructor(); + // (undocumented) + cancelSnap(): void; + // WARNING: The type "DgnDb" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "ErrorStatusOrResult" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + doSnap(db: DgnDb, request: any, callback: (result: ErrorStatusOrResult) => void): void; + } + + // (undocumented) + class SqliteStatement implements IDisposable { + constructor(); + // (undocumented) + bindBlob(param: number | string, val: Uint8Array | ArrayBuffer | SharedArrayBuffer): DbResult; + // (undocumented) + bindDouble(param: number | string, val: number): DbResult; + // (undocumented) + bindGuid(param: number | string, guidStr: GuidString): DbResult; + // (undocumented) + bindId(param: number | string, hexStr: Id64String): DbResult; + // (undocumented) + bindInteger(param: number | string, val: number | string): DbResult; + // (undocumented) + bindNull(param: number | string): DbResult; + // (undocumented) + bindString(param: number | string, val: string): DbResult; + // (undocumented) + clearBindings(): DbResult; + // (undocumented) + dispose(): void; + // (undocumented) + getColumnCount(): number; + // (undocumented) + getColumnName(columnIndex: number): string; + // (undocumented) + getColumnType(columnIndex: number): number; + // (undocumented) + getValueBlob(columnIndex: number): Uint8Array; + // (undocumented) + getValueDouble(columnIndex: number): number; + // (undocumented) + getValueGuid(columnIndex: number): GuidString; + // (undocumented) + getValueId(columnIndex: number): Id64String; + // (undocumented) + getValueInteger(columnIndex: number): number; + // (undocumented) + getValueString(columnIndex: number): string; + // (undocumented) + isReadonly(): boolean; + // (undocumented) + isValueNull(columnIndex: number): boolean; + // WARNING: The type "DgnDb" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "ECDb" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + prepare(db: DgnDb | ECDb, sql: string): StatusCodeWithMessage; + // (undocumented) + reset(): DbResult; + // (undocumented) + step(): DbResult; + } + +} + +// @public +class InformationCarrierElement extends Element { + constructor(props: ElementProps, iModel: IModelDb); +} + +// @public +class InformationContentElement extends Element { + constructor(props: ElementProps, iModel: IModelDb); +} + +// @public +class InformationModel extends Model { +} + +// @public +class InformationPartitionElement extends InformationContentElement, implements InformationPartitionElementProps { + constructor(props: InformationPartitionElementProps, iModel: IModelDb); + static createCode(iModel: IModelDb, scopeElementId: CodeScopeProps, codeValue: string): Code; + // (undocumented) + description?: string; +} + +// @public +class InformationRecordElement extends InformationContentElement { + constructor(props: ElementProps, iModel: IModelDb); +} + +// @public +class InformationRecordModel extends InformationModel { +} + +// @public +class InformationRecordPartition extends InformationPartitionElement { +} + +// @public +class InformationReferenceElement extends InformationContentElement { + constructor(props: ElementProps, iModel: IModelDb); +} + +// @public +interface InstanceChange { + // (undocumented) + changedInstance: { + className: string; + id: Id64String; + } + // (undocumented) + id: Id64String; + // (undocumented) + isIndirect: boolean; + // (undocumented) + opCode: ChangeOpCode; + // (undocumented) + summaryId: Id64String; +} + +// @public +enum KeepBriefcase { + // (undocumented) + No = 0, + // (undocumented) + Yes = 1 +} + +// @public +class KnownLocations { + static readonly nativeAssetsDir: string; + static readonly packageAssetsDir: string; + static readonly tmpdir: string; +} + +// @public +class LightLocation extends SpatialLocationElement, implements LightLocationProps { + constructor(props: LightLocationProps, iModel: IModelDb); + enabled: boolean; +} + +// @public +class LineStyle extends DefinitionElement, implements LineStyleProps { + constructor(props: LineStyleProps, iModel: IModelDb); + static createCode(iModel: IModelDb, scopeModelId: CodeScopeProps, codeValue: string): Code; + // (undocumented) + data: string; + // (undocumented) + description?: string; +} + +// WARNING: Unsupported export: Strokes +// WARNING: Unsupported export: Symbols +// WARNING: Unsupported export: Components +// @public +module LineStyleDefinition { + interface ComponentProps { + id: number; + offset?: number; + type: ComponentType; + } + + enum ComponentType { + Compound = 2, + Internal = 6, + PointSymbol = 1, + RasterImage = 7, + StrokePattern = 3, + StrokePoint = 4 + } + + interface CompoundProps { + // (undocumented) + comps: Components; + } + + enum PointSymbolFlags { + Is3d = 1, + None = 0, + NoScale = 2 + } + + interface PointSymbolProps { + baseX?: number; + baseY?: number; + baseZ?: number; + geomPartId: Id64String; + scale?: number; + sizeX?: number; + sizeY?: number; + sizeZ?: number; + symFlags?: PointSymbolFlags; + } + + interface RasterImageProps { + descr: string; + flags?: number; + imageId?: number; + trueWidth?: number; + x: number; + y: number; + } + + enum StrokeCap { + Arc = 30, + Closed = 0, + Decagon = 5, + Extended = 2, + Hexagon = 3, + Octagon = 4, + Open = 1 + } + + enum StrokeMode { + Dash = 1, + FirstInvert = 8, + Gap = 0, + LastInvert = 16, + Ray = 2, + Scale = 4 + } + + enum StrokePatternOptions { + AutoPhase = 1, + CenterStretch = 32, + Iteration = 8, + None = 0, + Segment = 16 + } + + interface StrokePatternProps { + descr: string; + maxIter?: number; + options?: StrokePatternOptions; + phase?: number; + strokes: Strokes; + } + + interface StrokePointProps { + descr: string; + lcId: number; + lcType?: ComponentType; + symbols: Symbols; + } + + interface StrokeProps { + capMode?: StrokeCap; + endWidth?: number; + length: number; + orgWidth?: number; + strokeMode?: StrokeMode; + widthMode?: StrokeWidth; + } + + enum StrokeWidth { + Full = 3, + Left = 1, + None = 0, + Right = 2 + } + + enum StyleFlags { + Continuous = 8, + None = 0, + NoSnap = 4, + Physical = 128 + } + + interface StyleProps { + compId: number; + compType: ComponentType; + flags?: StyleFlags; + unitDef?: number; + } + + enum SymbolOptions { + AbsoluteRotation = 64, + AdjustRotation = 32, + Center = 3, + CurveEnd = 8, + CurveOrigin = 4, + CurveVertex = 16, + End = 2, + NoClip = 512, + None = 0, + NoPartial = 1024, + NoScale = 256, + Origin = 1, + ProjectOrigin = 2048, + UseColor = 16384, + UseWeight = 32768 + } + + interface SymbolProps { + angle?: number; + mod1?: SymbolOptions; + strokeNum?: number; + symId: number; + symType?: ComponentType; + xOffset?: number; + yOffset?: number; + } + + class Utils { + // WARNING: The type "CompoundProps" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "StyleProps" needs to be exported by the package (e.g. added to index.ts) + static createCompoundComponent(iModel: IModelDb, props: CompoundProps): StyleProps | undefined; + // WARNING: The type "PointSymbolProps" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "StyleProps" needs to be exported by the package (e.g. added to index.ts) + static createPointSymbolComponent(iModel: IModelDb, props: PointSymbolProps): StyleProps | undefined; + // WARNING: The type "RasterImageProps" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "StyleProps" needs to be exported by the package (e.g. added to index.ts) + static createRasterComponent(iModel: IModelDb, props: RasterImageProps, image: Uint8Array): StyleProps | undefined; + // WARNING: The type "StrokePatternProps" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "StyleProps" needs to be exported by the package (e.g. added to index.ts) + static createStrokePatternComponent(iModel: IModelDb, props: StrokePatternProps): StyleProps | undefined; + // WARNING: The type "StrokePointProps" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "StyleProps" needs to be exported by the package (e.g. added to index.ts) + static createStrokePointComponent(iModel: IModelDb, props: StrokePointProps): StyleProps | undefined; + // WARNING: The type "StyleProps" needs to be exported by the package (e.g. added to index.ts) + static createStyle(imodel: IModelDb, scopeModelId: Id64String, name: string, props: StyleProps): Id64String; + static getOrCreateContinuousStyle(imodel: IModelDb, scopeModelId: Id64String, width?: number): Id64String; + static getOrCreateLinePixelsStyle(imodel: IModelDb, scopeModelId: Id64String, linePixels: LinePixels): Id64String; + static queryStyle(imodel: IModelDb, scopeModelId: Id64String, name: string): Id64String | undefined; + } + +} + +// @public +class LinkElement extends InformationReferenceElement { + static createCode(iModel: IModelDb, scopeModelId: CodeScopeProps, codeValue: string): Code; +} + +// @public +class LinkModel extends InformationModel { +} + +// @public +class LinkPartition extends InformationPartitionElement { +} + +// @public +class MetaDataRegistry { + add(classFullName: string, metaData: EntityMetaData): void; + find(classFullName: string): EntityMetaData | undefined; +} + +// @public +class Model extends Entity, implements ModelProps { + constructor(props: ModelProps, iModel: IModelDb); + buildConcurrencyControlRequest(opcode: DbOpcode): void; + // (undocumented) + getJsonProperty(name: string): any; + getUserProperties(namespace: string): any; + // (undocumented) + isPrivate: boolean; + // (undocumented) + isTemplate: boolean; + // (undocumented) + readonly jsonProperties: any; + // (undocumented) + readonly modeledElement: RelatedElement; + // (undocumented) + readonly name: string; + // (undocumented) + static onDelete(_props: ModelProps): IModelStatus; + // (undocumented) + static onDeleted(_props: ModelProps): void; + // (undocumented) + static onInsert(_props: ModelProps): IModelStatus; + // (undocumented) + static onInserted(_id: string): void; + // (undocumented) + static onUpdate(_props: ModelProps): IModelStatus; + // (undocumented) + static onUpdated(_props: ModelProps): void; + // (undocumented) + readonly parentModel: Id64String; + removeUserProperties(nameSpace: string): void; + // (undocumented) + setJsonProperty(name: string, value: any): void; + setUserProperties(nameSpace: string, value: any): void; + toJSON(): ModelProps; +} + +// @public +class ModelSelector extends DefinitionElement, implements ModelSelectorProps { + constructor(props: ModelSelectorProps, iModel: IModelDb); + static create(iModelDb: IModelDb, definitionModelId: Id64String, name: string, models: Id64Array): ModelSelector; + static createCode(iModel: IModelDb, scopeModelId: CodeScopeProps, codeValue: string): Code; + static insert(iModelDb: IModelDb, definitionModelId: Id64String, name: string, models: Id64Array): Id64String; + models: string[]; + // (undocumented) + toJSON(): ModelSelectorProps; +} + +// @public +class OpenParams { + constructor( + openMode: OpenMode, + accessMode?: AccessMode | undefined, + syncMode?: SyncMode | undefined, + exclusiveAccessOption?: ExclusiveAccessOption | undefined); + readonly accessMode?: AccessMode | undefined; + equals(other: OpenParams): boolean; + readonly exclusiveAccessOption?: ExclusiveAccessOption | undefined; + static fixedVersion(accessMode?: AccessMode, exclusiveAccessOption?: ExclusiveAccessOption): OpenParams; + readonly isStandalone: boolean; + readonly openMode: OpenMode; + static pullAndPush(exclusiveAccessOption?: ExclusiveAccessOption): OpenParams; + static pullOnly(accessMode?: AccessMode, exclusiveAccessOption?: ExclusiveAccessOption): OpenParams; + static standalone(openMode: OpenMode): OpenParams; + readonly syncMode?: SyncMode | undefined; +} + +// @public +class OrthographicViewDefinition extends SpatialViewDefinition { + constructor(props: SpatialViewDefinitionProps, iModel: IModelDb); + static create(iModelDb: IModelDb, definitionModelId: Id64String, name: string, modelSelectorId: Id64String, categorySelectorId: Id64String, displayStyleId: Id64String, range: Range3d, standardView?: StandardViewIndex): OrthographicViewDefinition; + static insert(iModelDb: IModelDb, definitionModelId: Id64String, name: string, modelSelectorId: Id64String, categorySelectorId: Id64String, displayStyleId: Id64String, range: Range3d, standardView?: StandardViewIndex): Id64String; + setRange(range: Range3d): void; +} + +// @public +class PhysicalElement extends SpatialElement { + constructor(props: GeometricElement3dProps, iModel: IModelDb); +} + +// @public +class PhysicalElementAssemblesElements extends ElementOwnsChildElements { + constructor(parentId: Id64String, relClassName?: string); + // (undocumented) + static classFullName: string; +} + +// @public +class PhysicalElementFulfillsFunction extends ElementRefersToElements { +} + +// @public +class PhysicalElementIsOfType extends RelatedElement { + constructor(id: Id64String, relClassName?: string); + // (undocumented) + static classFullName: string; +} + +// @public +class PhysicalModel extends SpatialModel { + static insert(iModelDb: IModelDb, parentSubjectId: Id64String, name: string): Id64String; +} + +// @public (undocumented) +class PhysicalObject extends PhysicalElement { + constructor(props: GeometricElement3dProps, iModel: IModelDb); +} + +// @public +class PhysicalPartition extends InformationPartitionElement { +} + +// @public +class PhysicalType extends TypeDefinitionElement { + constructor(props: TypeDefinitionElementProps, iModel: IModelDb); + static createCode(iModel: IModelDb, scopeModelId: CodeScopeProps, codeValue: string): Code; +} + +// @public (undocumented) +class PlanCallout extends Callout { + constructor(props: CalloutProps, iModel: IModelDb); +} + +// @public +class Platform { + static readonly electron: any; + static readonly imodeljsMobile: any; + static readonly isDesktop: boolean; + static readonly isMobile: boolean; + static readonly isNodeJs: boolean; + // (undocumented) + static load(dir?: string): typeof IModelJsNative; + static readonly platformName: string; +} + +// @public +class RecipeDefinitionElement extends DefinitionElement { + constructor(props: ElementProps, iModel: IModelDb); +} + +// @public +class Relationship extends Entity, implements RelationshipProps { + constructor(props: RelationshipProps, iModel: IModelDb); + buildConcurrencyControlRequest(opcode: DbOpcode): void; + delete(): void; + // (undocumented) + static getInstance(iModel: IModelDb, criteria: Id64String | SourceAndTarget): T; + insert(): Id64String; + // (undocumented) + static onDeletedDependency(_props: RelationshipProps, _iModel: IModelDb): void; + // (undocumented) + static onRootChanged(_props: RelationshipProps, _iModel: IModelDb): void; + // (undocumented) + static onValidateOutput(_props: RelationshipProps, _iModel: IModelDb): void; + // (undocumented) + readonly sourceId: Id64String; + // (undocumented) + readonly targetId: Id64String; + // (undocumented) + toJSON(): RelationshipProps; + update(): void; +} + +// @public +interface RelationshipProps extends EntityProps, SourceAndTarget { +} + +// @public +class Relationships { + constructor(iModel: IModelDb); + createInstance(props: RelationshipProps): Relationship; + deleteInstance(props: RelationshipProps): void; + getInstance(relClassSqlName: string, criteria: Id64String | SourceAndTarget): T; + getInstanceProps(relClassSqlName: string, criteria: Id64String | SourceAndTarget): T; + insertInstance(props: RelationshipProps): Id64String; + updateInstance(props: RelationshipProps): void; +} + +// @public (undocumented) +class RenderMaterial { +} + +// @public +class RenderMaterialOwnsRenderMaterials extends ElementOwnsChildElements { + constructor(parentId: Id64String, relClassName?: string); + // (undocumented) + static classFullName: string; +} + +// @public +class RepositoryLink extends UrlLink { +} + +// @public +class RepositoryModel extends DefinitionModel { +} + +// @public +class RoleElement extends Element { +} + +// @public +class RoleModel extends Model { +} + +// @public +class Schema { + static getClass(className: string, iModel: IModelDb): typeof Entity | undefined; + // (undocumented) + readonly name: string; +} + +// @public +class Schemas { + static getRegisteredSchema(schemaName: string): Schema | undefined; + // (undocumented) + static isRegistered(schema: Schema): boolean; + static registerSchema(schema: Schema): void; + static unregisterSchema(schemaName: string): void; +} + +// @public (undocumented) +class SectionCallout extends Callout { + constructor(props: CalloutProps, iModel: IModelDb); +} + +// @public +class SectionDrawing extends Drawing { + constructor(props: ElementProps, iModel: IModelDb); +} + +// @public +class SectionDrawingModel extends DrawingModel { +} + +// @public +class Sheet extends Document, implements SheetProps { + constructor(props: SheetProps, iModel: IModelDb); + static createCode(iModel: IModelDb, scopeModelId: CodeScopeProps, codeValue: string): Code; + // (undocumented) + height: number; + // (undocumented) + scale?: number; + // (undocumented) + sheetTemplate?: Id64String; + // (undocumented) + width: number; +} + +// @public +class SheetBorderTemplate extends Document, implements SheetBorderTemplateProps { + constructor(props: SheetBorderTemplateProps, iModel: IModelDb); + // (undocumented) + height?: number; + // (undocumented) + width?: number; +} + +// @public +class SheetModel extends GraphicalModel2d { +} + +// @public +class SheetTemplate extends Document, implements SheetTemplateProps { + constructor(props: SheetTemplateProps, iModel: IModelDb); + // (undocumented) + border?: Id64String; + // (undocumented) + height?: number; + // (undocumented) + width?: number; +} + +// @public +class SheetViewDefinition extends ViewDefinition2d { +} + +// @public +interface SourceAndTarget { + // (undocumented) + sourceId: Id64String; + // (undocumented) + targetId: Id64String; +} + +// @public +class SpatialCategory extends Category { + constructor(opts: ElementProps, iModel: IModelDb); + static create(iModelDb: IModelDb, definitionModelId: Id64String, name: string): SpatialCategory; + static createCode(iModel: IModelDb, scopeModelId: CodeScopeProps, codeValue: string): Code; + static getCodeSpecName(): string; + static insert(iModelDb: IModelDb, definitionModelId: Id64String, name: string, defaultAppearance: SubCategoryAppearance.Props): Id64String; + static queryCategoryIdByName(iModel: IModelDb, scopeModelId: Id64String, categoryName: string): Id64String | undefined; +} + +// @public +class SpatialElement extends GeometricElement3d { + constructor(props: GeometricElement3dProps, iModel: IModelDb); +} + +// @public (undocumented) +class SpatialLocation extends SpatialLocationElement { + constructor(props: GeometricElement3dProps, iModel: IModelDb); +} + +// @public +class SpatialLocationElement extends SpatialElement { + constructor(props: GeometricElement3dProps, iModel: IModelDb); +} + +// @public +class SpatialLocationIsOfType extends RelatedElement { + constructor(id: Id64String, relClassName?: string); + // (undocumented) + static classFullName: string; +} + +// @public +class SpatialLocationModel extends SpatialModel { +} + +// @public +class SpatialLocationPartition extends InformationPartitionElement { +} + +// @public +class SpatialLocationType extends TypeDefinitionElement { + constructor(props: TypeDefinitionElementProps, iModel: IModelDb); + static createCode(iModel: IModelDb, scopeModelId: CodeScopeProps, codeValue: string): Code; +} + +// @public +class SpatialModel extends GeometricModel3d { +} + +// @public +class SpatialViewDefinition extends ViewDefinition3d, implements SpatialViewDefinitionProps { + constructor(props: SpatialViewDefinitionProps, iModel: IModelDb); + loadModelSelector(): ModelSelector; + modelSelectorId: Id64String; + // (undocumented) + toJSON(): SpatialViewDefinitionProps; +} + +// @public +class SqliteStatement implements IterableIterator, IDisposable { + // WARNING: The name "__@iterator" contains unsupported characters; API names should use only letters, numbers, and underscores + [Symbol.iterator](): IterableIterator; + bindValue(parameter: number | string, value: any): void; + bindValues(values: any[] | object): void; + clearBindings(): void; + dispose(): void; + getColumnCount(): number; + getRow(): any; + getValue(columnIx: number): SqliteValue; + readonly isPrepared: boolean; + readonly isReadonly: boolean; + readonly isShared: boolean; + next(): IteratorResult; + // WARNING: The type "IModelJsNative.DgnDb" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "IModelJsNative.ECDb" needs to be exported by the package (e.g. added to index.ts) + prepare(db: IModelJsNative.DgnDb | IModelJsNative.ECDb, sql: string): void; + reset(): void; + setIsShared(b: boolean): void; + step(): DbResult; +} + +// @public +class SqliteStatementCache { + constructor(maxCount?: number); + // (undocumented) + add(str: string, stmt: SqliteStatement): void; + // (undocumented) + clear(): void; + // (undocumented) + find(str: string): CachedSqliteStatement | undefined; + // (undocumented) + getCount(): number; + // (undocumented) + readonly maxCount: number; + // (undocumented) + release(stmt: SqliteStatement): void; + // (undocumented) + removeUnusedStatementsIfNecessary(): void; +} + +// @public +class SqliteValue { + // WARNING: The type "IModelJsNative.SqliteStatement" needs to be exported by the package (e.g. added to index.ts) + constructor(stmt: IModelJsNative.SqliteStatement, colIndex: number); + readonly columnName: string; + getBlob(): Uint8Array; + getDouble(): number; + getGuid(): GuidString; + getId(): Id64String; + getInteger(): number; + getString(): string; + readonly isNull: boolean; + readonly type: SqliteValueType; + readonly value: any; +} + +// @public +enum SqliteValueType { + // (undocumented) + Blob = 4, + // (undocumented) + Double = 2, + // (undocumented) + Integer = 1, + // (undocumented) + Null = 5, + // (undocumented) + String = 3 +} + +// @public +interface StringParam { + // (undocumented) + guid?: GuidString; + // (undocumented) + id?: Id64String; +} + +// @public +class SubCategory extends DefinitionElement, implements SubCategoryProps { + constructor(props: SubCategoryProps, iModel: IModelDb); + appearance: SubCategoryAppearance; + static create(iModelDb: IModelDb, parentCategoryId: Id64String, name: string, appearance: SubCategoryAppearance.Props): SubCategory; + static createCode(iModel: IModelDb, parentCategoryId: CodeScopeProps, codeValue: string): Code; + description?: string; + getCategoryId(): Id64String; + getSubCategoryId(): Id64String; + getSubCategoryName(): string; + static insert(iModelDb: IModelDb, parentCategoryId: Id64String, name: string, appearance: SubCategoryAppearance.Props): Id64String; + readonly isDefaultSubCategory: boolean; + // (undocumented) + toJSON(): SubCategoryProps; +} + +// @public +class Subject extends InformationReferenceElement, implements SubjectProps { + constructor(props: SubjectProps, iModel: IModelDb); + static create(iModelDb: IModelDb, parentSubjectId: Id64String, name: string, description?: string): Subject; + static createCode(iModelDb: IModelDb, parentSubjectId: CodeScopeProps, codeValue: string): Code; + // (undocumented) + description?: string; + static insert(iModelDb: IModelDb, parentSubjectId: Id64String, name: string, description?: string): Id64String; +} + +// @public +class SubjectOwnsPartitionElements extends ElementOwnsChildElements { + constructor(parentId: Id64String, relClassName?: string); + // (undocumented) + static classFullName: string; +} + +// @public +class SubjectOwnsSubjects extends ElementOwnsChildElements { + constructor(parentId: Id64String, relClassName?: string); + // (undocumented) + static classFullName: string; +} + +// @public +enum SyncMode { + // (undocumented) + FixedVersion = 1, + // (undocumented) + PullAndPush = 3, + // (undocumented) + PullOnly = 2 +} + +// @public +class TemplateRecipe2d extends RecipeDefinitionElement { + constructor(props: ElementProps, iModel: IModelDb); +} + +// @public +class TemplateRecipe3d extends RecipeDefinitionElement { + constructor(props: ElementProps, iModel: IModelDb); +} + +// @public +class TemplateViewDefinition2d extends ViewDefinition2d { +} + +// @public +class TemplateViewDefinition3d extends ViewDefinition3d { +} + +// @public +class TextAnnotation2d extends AnnotationElement2d { + constructor(props: GeometricElement2dProps, iModel: IModelDb); +} + +// @public +class TextAnnotation3d extends GraphicalElement3d { + constructor(props: GeometricElement3dProps, iModel: IModelDb); +} + +// @public +class Texture extends DefinitionElement, implements TextureProps { + constructor(props: TextureProps, iModel: IModelDb); + static create(iModelDb: IModelDb, definitionModelId: Id64String, name: string, format: ImageSourceFormat, data: string, width: number, height: number, description: string, flags: TextureFlags): Texture; + static createCode(iModel: IModelDb, scopeModelId: CodeScopeProps, name: string): Code; + // (undocumented) + data: string; + // (undocumented) + description?: string; + // (undocumented) + flags: TextureFlags; + // (undocumented) + format: ImageSourceFormat; + // (undocumented) + height: number; + static insert(iModelDb: IModelDb, definitionModelId: Id64String, name: string, format: ImageSourceFormat, data: string, width: number, height: number, description: string, flags: TextureFlags): Id64String; + // (undocumented) + toJSON(): TextureProps; + // (undocumented) + width: number; +} + +// @public (undocumented) +class TitleText extends DetailingSymbol { + constructor(props: GeometricElement2dProps, iModel: IModelDb); +} + +// @public (undocumented) +enum TxnAction { + // (undocumented) + Abandon = 2, + // (undocumented) + Commit = 1, + // (undocumented) + Merge = 5, + // (undocumented) + None = 0, + // (undocumented) + Reinstate = 4, + // (undocumented) + Reverse = 3 +} + +// @public +class TxnManager { + constructor(_iModel: IModelDb); + // (undocumented) + protected _onAllInputsHandled(elClassName: string, elId: Id64String): void; + // (undocumented) + protected _onBeforeOutputsHandled(elClassName: string, elId: Id64String): void; + // (undocumented) + protected _onBeginValidate(): void; + // (undocumented) + protected _onDeletedDependency(props: RelationshipProps): void; + // (undocumented) + protected _onEndValidate(): void; + // (undocumented) + protected _onRootChanged(props: RelationshipProps): void; + // (undocumented) + protected _onValidateOutput(props: RelationshipProps): void; + beginMultiTxnOperation(): DbResult; + // WARNING: The type "IModelJsNative.TxnIdString" needs to be exported by the package (e.g. added to index.ts) + cancelTo(txnId: IModelJsNative.TxnIdString): IModelStatus; + // WARNING: The type "IModelJsNative.TxnIdString" needs to be exported by the package (e.g. added to index.ts) + describeChangeSet(endTxnId?: IModelJsNative.TxnIdString): string; + endMultiTxnOperation(): DbResult; + // WARNING: The type "IModelJsNative.TxnIdString" needs to be exported by the package (e.g. added to index.ts) + getCurrentTxnId(): IModelJsNative.TxnIdString; + getMultiTxnOperationDepth(): number; + getRedoString(): string; + // WARNING: The type "IModelJsNative.TxnIdString" needs to be exported by the package (e.g. added to index.ts) + getTxnDescription(txnId: IModelJsNative.TxnIdString): string; + getUndoString(): string; + readonly hasFatalError: boolean; + readonly hasLocalChanges: boolean; + readonly hasPendingTxns: boolean; + readonly hasUnsavedChanges: boolean; + readonly isRedoPossible: boolean; + // WARNING: The type "IModelJsNative.TxnIdString" needs to be exported by the package (e.g. added to index.ts) + isTxnIdValid(txnId: IModelJsNative.TxnIdString): boolean; + readonly isUndoPossible: boolean; + readonly onAfterUndoRedo: BeEvent<(_action: TxnAction) => void>; + readonly onBeforeUndoRedo: BeEvent<() => void>; + readonly onChangesApplied: BeEvent<() => void>; + readonly onCommit: BeEvent<() => void>; + readonly onCommitted: BeEvent<() => void>; + // WARNING: The type "IModelJsNative.TxnIdString" needs to be exported by the package (e.g. added to index.ts) + queryFirstTxnId(): IModelJsNative.TxnIdString; + // WARNING: The type "IModelJsNative.TxnIdString" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "IModelJsNative.TxnIdString" needs to be exported by the package (e.g. added to index.ts) + queryNextTxnId(txnId: IModelJsNative.TxnIdString): IModelJsNative.TxnIdString; + // WARNING: The type "IModelJsNative.TxnIdString" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "IModelJsNative.TxnIdString" needs to be exported by the package (e.g. added to index.ts) + queryPreviousTxnId(txnId: IModelJsNative.TxnIdString): IModelJsNative.TxnIdString; + reinstateTxn(): IModelStatus; + reportError(error: ValidationError): void; + reverseAll(): IModelStatus; + reverseSingleTxn(): IModelStatus; + // WARNING: The type "IModelJsNative.TxnIdString" needs to be exported by the package (e.g. added to index.ts) + reverseTo(txnId: IModelJsNative.TxnIdString): IModelStatus; + reverseTxns(numOperations: number): IModelStatus; + readonly validationErrors: ValidationError[]; +} + +// @public +class TypeDefinitionElement extends DefinitionElement, implements TypeDefinitionElementProps { + constructor(props: TypeDefinitionElementProps, iModel: IModelDb); + // (undocumented) + recipe?: RelatedElement; +} + +// @public +class UrlLink extends LinkElement { +} + +// @public +interface ValidationError { + errorType: string; + fatal: boolean; + message?: string; +} + +// @public +class ViewAttachment extends GraphicalElement2d, implements ViewAttachmentProps { + constructor(props: ViewAttachmentProps, iModel: IModelDb); + // (undocumented) + view: RelatedElement; +} + +// @public (undocumented) +class ViewAttachmentLabel extends DetailingSymbol, implements ViewAttachmentLabelProps { + constructor(props: ViewAttachmentLabelProps, iModel: IModelDb); +} + +// @public +class ViewDefinition extends DefinitionElement, implements ViewDefinitionProps { + protected constructor(props: ViewDefinitionProps, iModel: IModelDb); + categorySelectorId: Id64String; + static createCode(iModel: IModelDb, scopeModelId: CodeScopeProps, codeValue: string): Code; + displayStyleId: Id64String; + isDrawingView(): this is DrawingViewDefinition; + isSpatialView(): this is SpatialViewDefinition; + isView2d(): this is ViewDefinition2d; + isView3d(): this is ViewDefinition3d; + loadCategorySelector(): CategorySelector; + loadDisplayStyle(): DisplayStyle; + // (undocumented) + toJSON(): ViewDefinitionProps; +} + +// @public +class ViewDefinition2d extends ViewDefinition, implements ViewDefinition2dProps { + constructor(props: ViewDefinition2dProps, iModel: IModelDb); + angle: Angle; + baseModelId: Id64String; + delta: Point2d; + loadDisplayStyle2d(): DisplayStyle2d; + origin: Point2d; + // (undocumented) + toJSON(): ViewDefinition2dProps; +} + +// @public +class ViewDefinition3d extends ViewDefinition, implements ViewDefinition3dProps { + constructor(props: ViewDefinition3dProps, iModel: IModelDb); + angles: YawPitchRollAngles; + camera: Camera; + cameraOn: boolean; + extents: Vector3d; + loadDisplayStyle3d(): DisplayStyle3d; + origin: Point3d; + // (undocumented) + toJSON(): ViewDefinition3dProps; +} + +// @public +class VolumeElement extends SpatialLocationElement { + constructor(props: GeometricElement3dProps, iModel: IModelDb); +} + +// @public +class WebMercatorModel extends SpatialModel { +} + +// WARNING: Unsupported export: AutoPushEventHandler +// WARNING: Unsupported export: SchemaKey +// WARNING: Unsupported export: SchemaMatchType +// WARNING: Unsupported export: ChangeSetDescriber +// (No @packagedocumentation comment for this package) diff --git a/common/api/imodeljs-clients-backend.api.ts b/common/api/imodeljs-clients-backend.api.ts new file mode 100644 index 0000000..0ba93d7 --- /dev/null +++ b/common/api/imodeljs-clients-backend.api.ts @@ -0,0 +1,120 @@ +// @public +class AzureFileHandler implements FileHandler { + constructor(threshold?: number); + // (undocumented) + agent: https.Agent; + basename(filePath: string): string; + downloadFile(alctx: ActivityLoggingContext, downloadUrl: string, downloadToPathname: string, fileSize?: number, progressCallback?: (progress: ProgressInfo) => void): Promise; + exists(filePath: string): boolean; + getFileSize(filePath: string): number; + isDirectory(filePath: string): boolean; + join(...paths: string[]): string; + uploadFile(alctx: ActivityLoggingContext, uploadUrlString: string, uploadFromPathname: string, progressCallback?: (progress: ProgressInfo) => void): Promise; +} + +// @public +class IOSAzureFileHandler implements FileHandler { + constructor(); + // (undocumented) + agent: any; + basename(filePath: string): string; + downloadFile(alctx: ActivityLoggingContext, downloadUrl: string, downloadToPathname: string): Promise; + exists(filePath: string): boolean; + getFileSize(filePath: string): number; + isDirectory(filePath: string): boolean; + join(...paths: string[]): string; + uploadFile(alctx: ActivityLoggingContext, uploadUrlString: string, uploadFromPathname: string): Promise; +} + +// @public +class OidcAgentClient extends OidcBackendClient { + constructor(_agentConfiguration: OidcAgentClientConfiguration); + // (undocumented) + getToken(actx: ActivityLoggingContext): Promise; + // (undocumented) + refreshToken(actx: ActivityLoggingContext, jwt: AccessToken): Promise; +} + +// @public +interface OidcAgentClientConfiguration extends OidcBackendClientConfiguration { + // (undocumented) + serviceUserEmail: string; + // (undocumented) + serviceUserPassword: string; +} + +// @public +class OidcAgentClientV2 extends OidcBackendClient { + constructor(agentConfiguration: OidcAgentClientConfigurationV2); + // (undocumented) + getToken(actx: ActivityLoggingContext): Promise; + refreshToken(actx: ActivityLoggingContext, jwt: AccessToken): Promise; +} + +// @public +class OidcBackendClient extends OidcClient { + constructor(configuration: OidcBackendClientConfiguration); + // (undocumented) + protected _configuration: OidcBackendClientConfiguration; + // WARNING: The type "TokenSet" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected createToken(tokenSet: TokenSet, userInfo?: UserInfo): AccessToken; + // WARNING: The type "Issuer" needs to be exported by the package (e.g. added to index.ts) + discoverEndpoints(actx: ActivityLoggingContext): Promise; + // WARNING: The type "OpenIdClient" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected getClient(actx: ActivityLoggingContext): Promise; +} + +// @public +interface OidcBackendClientConfiguration { + clientId: string; + clientSecret: string; + scope: string; +} + +// @public +class OidcDelegationClient extends OidcBackendClient { + constructor(configuration: OidcDelegationClientConfiguration); + getJwtFromJwt(actx: ActivityLoggingContext, accessToken: AccessToken): Promise; + getJwtFromSaml(actx: ActivityLoggingContext, accessToken: AccessToken): Promise; + getSamlFromJwt(actx: ActivityLoggingContext, jwt: AccessToken): Promise; +} + +// @public (undocumented) +class OidcDeviceClient extends OidcClient, implements IOidcFrontendClient { + constructor(clientConfiguration: OidcFrontendClientConfiguration); + dispose(): void; + getAccessToken(actx: ActivityLoggingContext): Promise; + // (undocumented) + getIsSignedIn(): boolean; + initialize(actx: ActivityLoggingContext): Promise; + readonly onUserStateChanged: BeEvent<(token: AccessToken | undefined) => void>; + signIn(actx: ActivityLoggingContext): void; + signOut(actx: ActivityLoggingContext): void; +} + +// @public +class RequestHost { + static initialize(): Promise; +} + +// @public +class UrlFileHandler implements FileHandler { + constructor(); + // (undocumented) + agent: https.Agent; + basename(filePath: string): string; + // (undocumented) + downloadFile(alctx: ActivityLoggingContext, downloadUrl: string, downloadToPathname: string, fileSize?: number, progressCallback?: (progress: ProgressInfo) => void): Promise; + exists(filePath: string): boolean; + getFileSize(filePath: string): number; + isDirectory(filePath: string): boolean; + join(...paths: string[]): string; + // (undocumented) + uploadFile(_alctx: ActivityLoggingContext, uploadUrlString: string, uploadFromPathname: string, progressCallback?: (progress: ProgressInfo) => void): Promise; +} + +// WARNING: Unsupported export: OidcDelegationClientConfiguration +// WARNING: Unsupported export: OidcAgentClientConfigurationV2 +// (No @packagedocumentation comment for this package) diff --git a/common/api/imodeljs-clients.api.ts b/common/api/imodeljs-clients.api.ts new file mode 100644 index 0000000..9912ab9 --- /dev/null +++ b/common/api/imodeljs-clients.api.ts @@ -0,0 +1,1646 @@ +// @public +class AccessToken extends Token { + // (undocumented) + static foreignProjectAccessTokenJsonProperty: string; + // (undocumented) + static fromForeignProjectAccessTokenJson(foreignJsonStr: string): AccessToken | undefined; + // (undocumented) + static fromJson(jsonObj: any): AccessToken | undefined; + static fromJsonWebTokenString(jwt: string, startsAt: Date, expiresAt: Date, userInfo?: UserInfo): AccessToken; + // (undocumented) + static fromSamlAssertion(samlAssertion: string): AuthorizationToken; + static fromSamlTokenString(accessTokenString: string, includesPrefix?: IncludePrefix): AccessToken; + // (undocumented) + toTokenString(includePrefix?: IncludePrefix): string; +} + +// @public +export function addSelectFileAccessKey(query: RequestQueryOptions): void; + +// @public +class AggregateResponseError extends Error { + errors: ResponseError[]; +} + +// @public +class AllCodesDeletedEvent extends BriefcaseEvent { +} + +// @public +class AllLocksDeletedEvent extends BriefcaseEvent { +} + +// @public (undocumented) +class ArgumentCheck { + // (undocumented) + static defined(argumentName: string, argument?: any): void; + // (undocumented) + static definedNumber(argumentName: string, argument?: number): void; + // (undocumented) + static nonEmptyArray(argumentName: string, argument?: any[]): void; + // (undocumented) + static valid(argumentName: string, argument?: any): void; + static validBriefcaseId(argumentName: string, argument?: number): void; + // (undocumented) + static validChangeSetId(argumentName: string, argument?: string): void; + // (undocumented) + static validGuid(argumentName: string, argument?: string): void; +} + +// @public +class AuthenticationError extends ResponseError { +} + +// @public +class AuthorizationToken extends Token { + // (undocumented) + static clone(unTypedObj: any): AuthorizationToken; + // (undocumented) + static fromSamlAssertion(samlAssertion: string): AuthorizationToken; + // (undocumented) + toTokenString(includePrefix?: IncludePrefix): string; +} + +// @public +class Briefcase extends WsgInstance { + // (undocumented) + accessMode?: BriefcaseAccessMode; + acquiredDate?: string; + briefcaseId?: number; + downloadUrl?: string; + fileDescription?: string; + fileId?: GuidString; + fileName?: string; + fileSize?: string; + // (undocumented) + iModelId?: GuidString; + isReadOnly?: boolean; + // (undocumented) + lastAccessedAt?: Date; + // (undocumented) + localPathname?: string; + mergedChangeSetId?: string; + userId?: string; +} + +// @public +enum BriefcaseAccessMode { + // (undocumented) + Exclusive = 1, + // (undocumented) + Shared = 0 +} + +// @public +class BriefcaseDeletedEvent extends BriefcaseEvent { +} + +// @public +class BriefcaseEvent extends IModelHubEvent { + briefcaseId?: number; + fromJson(obj: any): void; +} + +// @public +class BriefcaseHandler { + constructor(handler: IModelBaseHandler, fileHandler?: FileHandler); + create(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString): Promise; + delete(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, briefcaseId: number): Promise; + download(alctx: ActivityLoggingContext, briefcase: Briefcase, path: string, progressCallback?: (progress: ProgressInfo) => void): Promise; + get(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, query?: BriefcaseQuery): Promise; +} + +// @public +class BriefcaseQuery extends Query { + byId(id: number): this; + getId(): number | undefined; + selectDownloadUrl(): this; +} + +// @public +class ChangeSet extends WsgInstance { + briefcaseId?: number; + changesType?: ChangesType; + description?: string; + downloadUrl?: string; + fileName?: string; + fileSize?: string; + id?: string; + index?: string; + isUploaded?: boolean; + parentId?: string; + pathname?: string; + pushDate?: string; + seedFileId?: GuidString; + uploadUrl?: string; + userCreated?: string; +} + +// @public +class ChangeSetCreatedEvent extends IModelHubGlobalEvent { + // (undocumented) + briefcaseId?: number; + // (undocumented) + changeSetId?: string; + // (undocumented) + changeSetIndex?: string; + fromJson(obj: any): void; +} + +// @public +class ChangeSetHandler { + constructor(handler: IModelBaseHandler, fileHandler?: FileHandler); + create(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, changeSet: ChangeSet, path: string, progressCallback?: (progress: ProgressInfo) => void): Promise; + download(alctx: ActivityLoggingContext, changeSets: ChangeSet[], path: string, progressCallback?: (progress: ProgressInfo) => void): Promise; + get(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, query?: ChangeSetQuery): Promise; +} + +// @public +class ChangeSetPostPushEvent extends BriefcaseEvent { + changeSetId?: string; + changeSetIndex?: string; + fromJson(obj: any): void; +} + +// @public +class ChangeSetPrePushEvent extends IModelHubEvent { +} + +// @public +class ChangeSetQuery extends StringIdQuery { + afterVersion(versionId: GuidString): this; + betweenChangeSets(firstChangeSetId: string, secondChangeSetId?: string): this; + betweenVersionAndChangeSet(versionId: GuidString, changeSetId: string): this; + betweenVersions(sourceVersionId: GuidString, destinationVersionId: GuidString): this; + bySeedFileId(seedFileId: GuidString): this; + // (undocumented) + protected checkValue(id: string): void; + fromId(id: string): this; + getVersionChangeSets(versionId: GuidString): this; + latest(): this; + selectDownloadUrl(): this; +} + +// @public +enum ChangesType { + Regular = 0, + Schema = 1 +} + +// @public (undocumented) +interface ClassKeyMapInfo { + classKeyPropertyName?: string; + classPropertyName?: string; + schemaPropertyName?: string; +} + +// @public +class Client { + protected constructor(); + // (undocumented) + protected _url?: string; + protected delete(alctx: ActivityLoggingContext, token: AccessToken, relativeUrlPath: string): Promise; + getUrl(alctx: ActivityLoggingContext): Promise; + protected abstract getUrlSearchKey(): string; + protected setupOptionDefaults(options: RequestOptions): Promise; +} + +// @public +class CodeBase extends WsgInstance { + briefcaseId?: number; + codeScope?: string; + codeSpecId?: Id64String; + createdDate?: string; + queryOnly?: boolean; + state?: CodeState; +} + +// @public +class CodeEvent extends BriefcaseEvent { + codeScope?: string; + codeSpecId?: Id64String; + fromJson(obj: any): void; + state?: CodeState; + values?: string[]; +} + +// @public +class CodeHandler { + constructor(handler: IModelBaseHandler); + deleteAll(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, briefcaseId: number): Promise; + get(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, query?: CodeQuery): Promise; + readonly sequences: CodeSequenceHandler; + update(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, codes: HubCode[], updateOptions?: CodeUpdateOptions): Promise; +} + +// @public +class CodeQuery extends Query { + byBriefcaseId(briefcaseId: number): this; + byCodes(codes: HubCode[]): this; + byCodeScope(codeScope: string): this; + byCodeSpecId(codeSpecId: Id64String): this; + readonly isMultiCodeQuery: boolean; + top(n: number): this; + unavailableCodes(briefcaseId: number): this; +} + +// @public +class CodeSequence extends WsgInstance { + codeScope?: string; + codeSpecId?: Id64String; + incrementBy?: number; + startIndex?: number; + type?: CodeSequenceType; + value?: string; + valuePattern?: string; +} + +// @public +class CodeSequenceHandler { + constructor(handler: IModelBaseHandler); + get(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, sequence: CodeSequence): Promise; +} + +// @public +enum CodeSequenceType { + LargestUsed = 0, + NextAvailable = 1 +} + +// @public +enum CodeState { + Available = 0, + Reserved = 1, + Retired = 3, + Used = 2 +} + +// @public +interface CodeUpdateOptions { + codesPerRequest?: number; + continueOnConflict?: boolean; + deniedCodes?: boolean; + unlimitedReporting?: boolean; +} + +// @public +class Config { + static readonly App: Config; + // WARNING: The type "ValueType" needs to be exported by the package (e.g. added to index.ts) + get(varName: string, defaultVal?: ValueType): any; + getBoolean(name: string, defaultVal?: boolean): boolean; + getContainer(): any; + getNumber(name: string, defaultVal?: number): number; + getString(name: string, defaultVal?: string): string; + getVars(): string[]; + has(varName: string): boolean; + merge(source: any): void; + remove(varName: string): void; + // WARNING: The type "ValueType" needs to be exported by the package (e.g. added to index.ts) + set(varName: string, value: ValueType): void; +} + +// @public +class ConflictingCodesError extends IModelHubError { + addCodes(error: IModelHubError): void; + conflictingCodes?: HubCode[]; + static fromError(error: IModelHubError): ConflictingCodesError | undefined; +} + +// @public +class ConflictingLocksError extends IModelHubError { + addLocks(error: IModelHubError): void; + conflictingLocks?: Lock[]; + static fromError(error: IModelHubError): ConflictingLocksError | undefined; +} + +// WARNING: configRelyingPartyUri has incomplete type information +// @public +class ConnectClient extends WsgClient { + constructor(); + getInvitedProjects(alctx: ActivityLoggingContext, token: AccessToken, queryOptions?: ConnectRequestQueryOptions): Promise; + getProject(alctx: ActivityLoggingContext, token: AccessToken, queryOptions?: ConnectRequestQueryOptions): Promise; + getProjects(alctx: ActivityLoggingContext, token: AccessToken, queryOptions?: ConnectRequestQueryOptions): Promise; + protected getRelyingPartyUrl(): string; + protected getUrlSearchKey(): string; + // (undocumented) + static readonly searchKey: string; + // (undocumented) + protected setupOptionDefaults(options: RequestOptions): Promise; +} + +// @public +interface ConnectRequestQueryOptions extends RequestQueryOptions { + isFavorite?: boolean; + isMRU?: boolean; +} + +// @public +class ConnectSettingsClient extends Client, implements SettingsAdmin { + constructor(applicationId: string); + // (undocumented) + applicationId: string; + // (undocumented) + deleteSetting(alctx: ActivityLoggingContext, settingNamespace: string, settingName: string, accessToken: AccessToken, applicationSpecific: boolean, projectId?: string, iModelId?: string): Promise; + // (undocumented) + deleteUserSetting(alctx: ActivityLoggingContext, settingNamespace: string, settingName: string, accessToken: AccessToken, applicationSpecific: boolean, projectId?: string, iModelId?: string): Promise; + getAccessToken(alctx: ActivityLoggingContext, authSamlToken: AuthorizationToken): Promise; + // (undocumented) + getSetting(alctx: ActivityLoggingContext, settingNamespace: string, settingName: string, accessToken: AccessToken, applicationSpecific: boolean, projectId?: string, iModelId?: string): Promise; + protected getUrlSearchKey(): string; + // (undocumented) + getUserSetting(alctx: ActivityLoggingContext, settingNamespace: string, settingName: string, accessToken: AccessToken, applicationSpecific: boolean, projectId?: string, iModelId?: string): Promise; + // (undocumented) + saveSetting(alctx: ActivityLoggingContext, settings: any, settingNamespace: string, settingName: string, accessToken: AccessToken, applicationSpecific: boolean, projectId?: string, iModelId?: string): Promise; + // (undocumented) + saveUserSetting(alctx: ActivityLoggingContext, settings: any, settingNamespace: string, settingName: string, accessToken: AccessToken, applicationSpecific: boolean, projectId?: string, iModelId?: string): Promise; + // (undocumented) + static readonly searchKey: string; +} + +// @public +class DefaultCodeUpdateOptionsProvider { + constructor(); + // (undocumented) + protected _defaultOptions: CodeUpdateOptions; + assignOptions(options: CodeUpdateOptions): Promise; +} + +// @public +class DefaultLockUpdateOptionsProvider { + constructor(); + // (undocumented) + protected _defaultOptions: LockUpdateOptions; + assignOptions(options: LockUpdateOptions): Promise; +} + +// @public +class DefaultRequestOptionsProvider { + constructor(); + // (undocumented) + protected _defaultOptions: RequestOptions; + assignOptions(options: RequestOptions): Promise; +} + +// @public +class DefaultWsgRequestOptionsProvider extends DefaultRequestOptionsProvider { + constructor(); +} + +// @public +class ECInstance { + // (undocumented) + [index: string]: any; + // (undocumented) + ecId: string; +} + +// @public +class ECJsonTypeMap { + static classToJson(applicationKey: string, classKey: string, classKeyMapInfo: ClassKeyMapInfo): (typedConstructor: ConstructorType) => void; + static fromJson(typedConstructor: new () => T, applicationKey: string, ecJsonInstance: any): T | undefined; + static propertyToJson(applicationKey: string, propertyAccessString: string): (object: any, propertyKey: string) => void; + static toJson(applicationKey: string, typedInstance: T): any | undefined; +} + +// @public +class EventHandler extends EventBaseHandler { + constructor(handler: IModelBaseHandler); + createListener(alctx: ActivityLoggingContext, authenticationCallback: () => Promise, subscriptionId: string, imodelId: GuidString, listener: (event: IModelHubEvent) => void): () => void; + getEvent(alctx: ActivityLoggingContext, sasToken: string, baseAddress: string, subscriptionId: string, timeout?: number): Promise; + getSASToken(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString): Promise; + readonly subscriptions: EventSubscriptionHandler; +} + +// @public +class EventSAS extends BaseEventSAS { +} + +// @public +class EventSubscription extends WsgInstance { + eventTypes?: EventType[]; +} + +// @public +class EventSubscriptionHandler { + constructor(handler: IModelBaseHandler); + create(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, events: EventType[]): Promise; + delete(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, eventSubscriptionId: string): Promise; + update(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, subscription: EventSubscription): Promise; +} + +// @public +class FeatureEndedLogEntry extends FeatureLogEntry { + constructor(featureId: GuidString, startEntryId: GuidString, hostName: string, usageType: UsageType); + static fromStartEntry(startEntry: FeatureStartedLogEntry): FeatureEndedLogEntry; + // (undocumented) + readonly startEntryId: GuidString; +} + +// @public +class FeatureLogEntry { + constructor(featureId: GuidString, hostName: string, usageType: UsageType); + readonly featureId: GuidString; + readonly hostName: string; + productId?: number; + productVersion?: ProductVersion; + projectId?: GuidString; + readonly timestamp: string; + usageData: FeatureLogEntryAttribute[]; + readonly usageType: UsageType; + userInfo?: UsageUserInfo; +} + +// @public +interface FeatureLogEntryAttribute { + // (undocumented) + name: string; + // (undocumented) + value: any; +} + +// @public (undocumented) +interface FeatureLogEntryAttributeJson { + // (undocumented) + name: string; + // (undocumented) + value: string; +} + +// @public +interface FeatureLogEntryJson extends UsageLogEntryJson { + eDateZ: string; + ftrID: GuidString; + sDateZ: string; + uData: FeatureLogEntryAttributeJson[]; +} + +// @public +class FeatureStartedLogEntry extends FeatureLogEntry { + constructor(featureId: GuidString, hostName: string, usageType: UsageType); + readonly entryId: GuidString; +} + +// @public (undocumented) +class FileAccessKey extends WsgInstance { + // (undocumented) + permissions?: string; + // (undocumented) + requiresConfirmation?: string; + // (undocumented) + type?: string; + // (undocumented) + url?: string; +} + +// @public +interface FileHandler { + // (undocumented) + agent: https.Agent; + basename(filePath: string): string; + downloadFile(alctx: ActivityLoggingContext, downloadUrl: string, path: string, fileSize?: number, progress?: (progress: ProgressInfo) => void): Promise; + exists(filePath: string): boolean; + getFileSize(filePath: string): number; + isDirectory(filePath: string): boolean; + join(...paths: string[]): string; + uploadFile(alctx: ActivityLoggingContext, uploadUrlString: string, path: string, progress?: (progress: ProgressInfo) => void): Promise; +} + +// @public +export function getArrayBuffer(alctx: ActivityLoggingContext, url: string): Promise; + +// @public +enum GetEventOperationType { + Destructive = 0, + Peek = 1 +} + +// @public +export function getJson(alctx: ActivityLoggingContext, url: string): Promise; + +// @public +class GlobalEventHandler extends EventBaseHandler { + constructor(handler: IModelBaseHandler); + createListener(alctx: ActivityLoggingContext, authenticationCallback: () => Promise, subscriptionInstanceId: string, listener: (event: IModelHubGlobalEvent) => void): () => void; + getEvent(alctx: ActivityLoggingContext, sasToken: string, baseAddress: string, subscriptionInstanceId: string, timeout?: number, getOperation?: GetEventOperationType): Promise; + getSASToken(alctx: ActivityLoggingContext, token: AccessToken): Promise; + readonly subscriptions: GlobalEventSubscriptionHandler; +} + +// @public +class GlobalEventSAS extends BaseEventSAS { +} + +// @public +class GlobalEventSubscription extends WsgInstance { + // (undocumented) + eventTypes?: GlobalEventType[]; + // (undocumented) + subscriptionId?: string; +} + +// @public +class GlobalEventSubscriptionHandler { + constructor(handler: IModelBaseHandler); + create(alctx: ActivityLoggingContext, token: AccessToken, subscriptionId: GuidString, globalEvents: GlobalEventType[]): Promise; + delete(alctx: ActivityLoggingContext, token: AccessToken, subscriptionInstanceId: string): Promise; + update(alctx: ActivityLoggingContext, token: AccessToken, subscription: GlobalEventSubscription): Promise; +} + +// @public +class HardiModelDeleteEvent extends IModelHubGlobalEvent { +} + +// @public +class HubCode extends CodeBase { + value?: string; +} + +// @public +class HubIModel extends WsgInstance { + createdDate?: string; + description?: string; + id?: GuidString; + iModelTemplate?: string; + initialized?: boolean; + name?: string; + userCreated?: string; +} + +// @public +class HubUserInfo extends WsgInstance { + email?: string; + firstName?: string; + id?: string; + lastName?: string; +} + +// @public +interface IAccessTokenManager { + getAccessToken(actx: ActivityLoggingContext): Promise; +} + +// @public (undocumented) +class IModelBankClient extends IModelClient { + constructor(url: string, handler: FileHandler | undefined); +} + +// @public (undocumented) +class IModelBankFileSystemContextClient implements ContextManagerClient { + constructor(baseUri: string); + // (undocumented) + baseUri: string; + // (undocumented) + createContext(alctx: ActivityLoggingContext, accessToken: AccessToken, name: string): Promise; + // (undocumented) + deleteContext(alctx: ActivityLoggingContext, accessToken: AccessToken, contextId: string): Promise; + // (undocumented) + queryContextByName(alctx: ActivityLoggingContext, accessToken: AccessToken, projectName: string): Promise; +} + +// @public (undocumented) +class IModelBankHandler extends IModelBaseHandler { + constructor(url: string, handler: FileHandler | undefined, keepAliveDuration?: number); + // (undocumented) + getUrl(_actx: ActivityLoggingContext, excludeApiVersion?: boolean): Promise; + // (undocumented) + protected getUrlSearchKey(): string; +} + +// WARNING: configRelyingPartyUri has incomplete type information +// @public +class IModelBaseHandler extends WsgClient { + constructor(keepAliveDuration?: number, fileHandler?: FileHandler); + // (undocumented) + protected _agent: any; + // (undocumented) + protected _fileHandler: FileHandler | undefined; + // (undocumented) + protected _url?: string; + delete(alctx: ActivityLoggingContext, token: AccessToken, relativeUrlPath: string): Promise; + deleteInstance(alctx: ActivityLoggingContext, token: AccessToken, relativeUrlPath: string, instance?: T, requestOptions?: WsgRequestOptions): Promise; + // (undocumented) + formatProjectIdForUrl(projectId: string): string; + getAccessToken(alctx: ActivityLoggingContext, authorizationToken: AuthorizationToken): Promise; + getAgent(): any; + // WARNING: The type "CustomRequestOptions" needs to be exported by the package (e.g. added to index.ts) + getCustomRequestOptions(): CustomRequestOptions; + // (undocumented) + getFileHandler(): FileHandler | undefined; + getInstances(alctx: ActivityLoggingContext, typedConstructor: new () => T, token: AccessToken, relativeUrlPath: string, queryOptions?: RequestQueryOptions): Promise; + protected getRelyingPartyUrl(): string; + getUrl(alctx: ActivityLoggingContext): Promise; + protected getUrlSearchKey(): string; + postInstance(alctx: ActivityLoggingContext, typedConstructor: new () => T, token: AccessToken, relativeUrlPath: string, instance: T, requestOptions?: WsgRequestOptions): Promise; + postInstances(alctx: ActivityLoggingContext, typedConstructor: new () => T, token: AccessToken, relativeUrlPath: string, instances: T[], requestOptions?: WsgRequestOptions): Promise; + postQuery(alctx: ActivityLoggingContext, typedConstructor: new () => T, token: AccessToken, relativeUrlPath: string, queryOptions: RequestQueryOptions): Promise; + // (undocumented) + static readonly searchKey: string; + protected setupOptionDefaults(options: RequestOptions): Promise; +} + +// @public +class IModelClient { + constructor(baseHandler: IModelBaseHandler, fileHandler?: FileHandler); + // (undocumented) + protected _handler: IModelBaseHandler; + readonly briefcases: BriefcaseHandler; + readonly changeSets: ChangeSetHandler; + readonly codes: CodeHandler; + readonly events: EventHandler; + readonly globalEvents: GlobalEventHandler; + readonly iModel: IModelHandler; + readonly iModels: IModelsHandler; + readonly locks: LockHandler; + // WARNING: The type "CustomRequestOptions" needs to be exported by the package (e.g. added to index.ts) + readonly requestOptions: CustomRequestOptions; + setFileHandler(fileHandler: FileHandler): void; + readonly thumbnails: ThumbnailHandler; + readonly users: UserInfoHandler; + readonly versions: VersionHandler; +} + +// @public +class IModelCreatedEvent extends IModelHubGlobalEvent { +} + +// @public +class IModelDeletedEvent extends IModelHubEvent { +} + +// @public (undocumented) +interface IModelFileSystemContextProps { + // (undocumented) + description: string; + // (undocumented) + id: string; + // (undocumented) + name: string; +} + +// @public +class IModelHandler { + constructor(handler: IModelsHandler); + create(alctx: ActivityLoggingContext, token: AccessToken, contextId: string, name: string, path?: string, description?: string, progressCallback?: (progress: ProgressInfo) => void, timeOutInMilliseconds?: number): Promise; + delete(alctx: ActivityLoggingContext, token: AccessToken, contextId: string): Promise; + download(alctx: ActivityLoggingContext, token: AccessToken, contextId: string, path: string, progressCallback?: (progress: ProgressInfo) => void): Promise; + get(alctx: ActivityLoggingContext, token: AccessToken, contextId: string): Promise; + getInitializationState(alctx: ActivityLoggingContext, token: AccessToken, contextId: string): Promise; + update(alctx: ActivityLoggingContext, token: AccessToken, contextId: string, imodel: HubIModel): Promise; +} + +// @public +class IModelHubClient extends IModelClient { + constructor(fileHandler?: FileHandler, iModelBaseHandler?: IModelBaseHandler); + getAccessToken(alctx: ActivityLoggingContext, authorizationToken: AuthorizationToken): Promise; +} + +// @public +class IModelHubClientError extends IModelHubError { + static browser(): IModelHubClientError; + static fileHandler(): IModelHubClientError; + static fileNotFound(): IModelHubClientError; + static fromId(id: IModelHubStatus, message: string): IModelHubClientError; + static invalidArgument(argumentName: string): IModelHubClientError; + static missingDownloadUrl(argumentName: string): IModelHubClientError; + static undefinedArgument(argumentName: string): IModelHubClientError; +} + +// @public +class IModelHubError extends WsgError { + constructor(errorNumber: number | HttpStatus, message?: string, getMetaData?: GetMetaDataFunction); + data: any; + static fromId(id: IModelHubStatus, message: string): IModelHubError; + getLogLevel(): LogFunction; + log(): void; + static parse(response: any, log?: boolean): ResponseError; + static shouldRetry(error: any, response: any): boolean; +} + +// @public +class IModelHubEvent extends IModelHubBaseEvent { + fromJson(obj: any): void; + iModelId?: GuidString; +} + +// @public +class IModelHubGlobalEvent extends IModelHubBaseEvent { + fromJson(obj: any): void; + iModelId?: GuidString; + projectId?: string; +} + +// @public +class IModelQuery extends InstanceIdQuery { + byName(name: string): this; +} + +// @public +class IModelsHandler { + constructor(handler: IModelBaseHandler, fileHandler?: FileHandler); + create(alctx: ActivityLoggingContext, token: AccessToken, contextId: string, name: string, path?: string, description?: string, progressCallback?: (progress: ProgressInfo) => void, timeOutInMilliseconds?: number): Promise; + delete(alctx: ActivityLoggingContext, token: AccessToken, contextId: string, imodelId: GuidString): Promise; + download(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, path: string, progressCallback?: (progress: ProgressInfo) => void): Promise; + get(alctx: ActivityLoggingContext, token: AccessToken, contextId: string, query?: IModelQuery): Promise; + getInitializationState(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString): Promise; + update(alctx: ActivityLoggingContext, token: AccessToken, contextId: string, imodel: HubIModel): Promise; +} + +// @public +class ImsActiveSecureTokenClient extends Client { + constructor(); + getToken(alctx: ActivityLoggingContext, user: string, password: string, appId?: string): Promise; + protected getUrlSearchKey(): string; + // (undocumented) + static readonly searchKey: string; + // (undocumented) + protected setupOptionDefaults(options: RequestOptions): Promise; +} + +// @public +class ImsDelegationSecureTokenClient extends Client { + constructor(); + getToken(alctx: ActivityLoggingContext, authorizationToken: AuthorizationToken, relyingPartyUri?: string, appId?: string): Promise; + protected getUrlSearchKey(): string; + // (undocumented) + static readonly searchKey: string; + protected setupOptionDefaults(options: RequestOptions): Promise; +} + +// @public +class ImsFederatedAuthenticationClient extends Client { + constructor(); + protected getUrlSearchKey(): string; + static parseTokenResponse(authTokenResponse: string): AuthorizationToken | undefined; + // (undocumented) + static readonly searchKey: string; +} + +// @public (undocumented) +enum IncludePrefix { + // (undocumented) + No = 1, + // (undocumented) + Yes = 0 +} + +// @public +enum InitializationState { + CodeTooLong = 5, + Failed = 3, + NotStarted = 1, + OutdatedFile = 4, + Scheduled = 2, + SeedFileIsBriefcase = 6, + Successful = 0 +} + +// @public +class InstanceIdQuery extends Query { + // (undocumented) + protected _byId?: GuidString; + byId(id: GuidString): this; + getId(): string | undefined; +} + +// @public +interface IOidcFrontendClient extends IDisposable { + getAccessToken(actx: ActivityLoggingContext): Promise; + initialize(actx: ActivityLoggingContext): Promise; + readonly onUserStateChanged: BeEvent<(token: AccessToken | undefined) => void>; + signIn(actx: ActivityLoggingContext): void; + signOut(actx: ActivityLoggingContext): void; +} + +// @public +class LargeThumbnail extends Thumbnail { +} + +// @public +class Lock extends LockBase { + objectId?: Id64String; +} + +// @public +class LockBase extends WsgInstance { + briefcaseId?: number; + lockLevel?: LockLevel; + lockType?: LockType; + releasedWithChangeSet?: string; + releasedWithChangeSetIndex?: string; + seedFileId?: GuidString; +} + +// @public +class LockEvent extends BriefcaseEvent { + fromJson(obj: any): void; + lockLevel?: LockLevel; + lockType?: LockType; + objectIds?: Id64String[]; + releasedWithChangeSet?: string; +} + +// @public +class LockHandler { + constructor(handler: IModelBaseHandler); + deleteAll(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, briefcaseId: number): Promise; + get(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, query?: LockQuery): Promise; + update(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, locks: Lock[], updateOptions?: LockUpdateOptions): Promise; +} + +// @public +enum LockLevel { + Exclusive = 2, + None = 0, + Shared = 1 +} + +// @public +class LockQuery extends Query { + byBriefcaseId(briefcaseId: number): this; + byLockLevel(lockLevel: LockLevel): this; + byLocks(locks: Lock[]): this; + byLockType(lockType: LockType): this; + byObjectId(objectId: Id64String): this; + byReleasedWithChangeSet(changeSetId: string): this; + byReleasedWithChangeSetIndex(changeSetIndex: number): this; + readonly isMultiLockQuery: boolean; + top(n: number): this; + unavailableLocks(briefcaseId: number, lastChangeSetIndex: string): this; +} + +// @public +enum LockType { + CodeSpecs = 4, + Db = 0, + Element = 2, + Model = 1, + Schemas = 3 +} + +// @public +interface LockUpdateOptions { + continueOnConflict?: boolean; + deniedLocks?: boolean; + locksPerRequest?: number; + unlimitedReporting?: boolean; +} + +// @public (undocumented) +class LogEntryConverter { + // (undocumented) + static toFeatureLogJson(entries: FeatureLogEntry[]): FeatureLogEntryJson[]; + // (undocumented) + static toUsageLogJson(entry: UsageLogEntry): UsageLogEntryJson; +} + +// @public +interface LogPostingResponse { + // (undocumented) + message: string; + // (undocumented) + requestId: GuidString; + // (undocumented) + status: BentleyStatus; + // (undocumented) + time: number; +} + +// @public +class MultiCode extends CodeBase { + // (undocumented) + values?: string[]; +} + +// @public +class MultiLock extends LockBase { + // (undocumented) + objectIds?: Id64String[]; +} + +// @public +class NamedVersionCreatedEvent extends IModelHubGlobalEvent { + // (undocumented) + changeSetId?: string; + fromJson(obj: any): void; + // (undocumented) + versionId?: GuidString; + // (undocumented) + versionName?: string; +} + +// @public (undocumented) +class OidcClient extends Client { + constructor(); + protected getUrlSearchKey(): string; + // (undocumented) + static readonly searchKey: string; +} + +// @public +interface OidcFrontendClientConfiguration { + clientId: string; + postSignoutRedirectUri?: string; + redirectUri: string; + scope: string; +} + +// @public +export function ParseEvent(response: Response): IModelHubEvent; + +// @public +export function ParseGlobalEvent(response: Response, handler?: IModelBaseHandler, sasToken?: string): IModelHubGlobalEvent; + +// @public +class Permission extends WsgInstance { + // (undocumented) + categoryId?: number; + // (undocumented) + description?: string; + // (undocumented) + name?: string; + // (undocumented) + serviceGprId?: number; +} + +// @public +interface ProductVersion { + // (undocumented) + major: number; + // (undocumented) + minor: number; + // (undocumented) + sub1?: number; + // (undocumented) + sub2?: number; +} + +// @public (undocumented) +interface ProgressInfo { + // (undocumented) + loaded: number; + // (undocumented) + percent?: number; + // (undocumented) + total?: number; +} + +// @public +class Project extends WsgInstance { + // (undocumented) + allowExternalTeamMembers?: boolean; + // (undocumented) + assetId?: string; + // (undocumented) + countryCode?: string; + // (undocumented) + dataLocationId?: string; + // (undocumented) + industry?: string; + // (undocumented) + isRbacEnabled?: boolean; + // (undocumented) + lastModifiedDate?: string; + // (undocumented) + latitude?: string; + // (undocumented) + location?: string; + // (undocumented) + longitude?: string; + // (undocumented) + name?: string; + // (undocumented) + number?: string; + // (undocumented) + registeredDate?: string; + // (undocumented) + status?: number; + // (undocumented) + timeZoneLocation?: string; + // (undocumented) + type?: string; + // (undocumented) + ultimateRefId?: string; +} + +// @public +class Query { + // (undocumented) + protected _query: RequestQueryOptions; + protected addFilter(filter: string, operator?: "and" | "or"): void; + protected addSelect(select: string): this; + filter(filter: string): this; + getQueryOptions(): RequestQueryOptions; + orderBy(orderBy: string): this; + resetQueryOptions(): void; + select(select: string): this; + skip(n: number): this; + top(n: number): this; +} + +// @public +class RbacProject extends WsgInstance { +} + +// @public (undocumented) +interface RbacRequestQueryOptions extends RequestQueryOptions { + // (undocumented) + rbacOnly?: boolean; +} + +// @public (undocumented) +class RbacUser extends WsgInstance { +} + +// @public +class RealityData extends WsgInstance { + // (undocumented) + accuracyInMeters?: string; + // (undocumented) + classification?: string; + containerName?: string; + // (undocumented) + copyright?: string; + // (undocumented) + createdTimestamp?: string; + // (undocumented) + creatorId?: string; + // (undocumented) + dataLocationGuid?: string; + // (undocumented) + dataSet?: string; + // (undocumented) + description?: string; + // (undocumented) + footprint?: string; + // (undocumented) + group?: number; + // (undocumented) + id?: string; + // (undocumented) + listable?: string; + // (undocumented) + metadataUrl?: string; + // (undocumented) + modifiedTimestamp?: string; + // (undocumented) + name?: string; + // (undocumented) + organizationId?: string; + // (undocumented) + ownedBy?: string; + // (undocumented) + resolutionInMeters?: string; + // (undocumented) + rootDocument?: string; + // (undocumented) + size?: string; + // (undocumented) + streamed?: string; + // (undocumented) + termsOfUse?: string; + // (undocumented) + thumbnailDocument?: string; + // (undocumented) + type?: string; + // (undocumented) + ultimateId?: string; + // (undocumented) + version?: string; + // (undocumented) + visibility?: string; +} + +// WARNING: configRelyingPartyUri has incomplete type information +// @public +class RealityDataServicesClient extends WsgClient { + constructor(); + cleanTilesetUrl(url: string): string; + getBlobStringUrl(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string, name: string): Promise; + getBlobUrl(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string): Promise; + getFileAccessKey(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string, name: string): Promise; + getModelData(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string, name: string): Promise; + getRealityData(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string): Promise; + getRealityDataInProject(alctx: ActivityLoggingContext, token: AccessToken, projectId: string): Promise; + getRealityDataInProjectOverlapping(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, range: Range2d): Promise; + getRealityDataUrl(alctx: ActivityLoggingContext, projectId: string, tilesId: string): Promise; + protected getRelyingPartyUrl(): string; + getRootDocumentJson(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string): Promise; + getTileContent(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string, name: string): Promise; + getTileDataBlobUrl(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string): Promise; + getTileJson(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string, name: string): Promise; + protected getUrlSearchKey(): string; + // (undocumented) + static readonly searchKey: string; +} + +// @public +export function request(alctx: ActivityLoggingContext, url: string, options: RequestOptions): Promise; + +// @public (undocumented) +interface RequestBasicCredentials { + // (undocumented) + password: string; + // (undocumented) + user: string; +} + +// @public (undocumented) +class RequestGlobalOptions { + // (undocumented) + static HTTPS_PROXY?: https.Agent; +} + +// @public (undocumented) +interface RequestOptions { + // (undocumented) + accept?: string; + // (undocumented) + agent?: https.Agent; + // (undocumented) + auth?: RequestBasicCredentials; + // (undocumented) + body?: any; + // (undocumented) + buffer?: any; + // (undocumented) + errorCallback?: (response: any) => ResponseError; + // (undocumented) + headers?: any; + // (undocumented) + method: string; + // (undocumented) + parser?: any; + // (undocumented) + progressCallback?: (progress: ProgressInfo) => void; + // (undocumented) + qs?: any | RequestQueryOptions; + // (undocumented) + readStream?: any; + // (undocumented) + redirects?: number; + // (undocumented) + responseType?: string; + // (undocumented) + retries?: number; + // (undocumented) + retryCallback?: (error: any, response: any) => boolean; + // (undocumented) + stream?: any; + // (undocumented) + timeout?: number | { + deadline?: number; + response?: number; + }; + // (undocumented) + useCorsProxy?: boolean; +} + +// @public +interface RequestQueryOptions { + // WARNING: The name "$filter" contains unsupported characters; API names should use only letters, numbers, and underscores + $filter?: string; + // WARNING: The name "$orderby" contains unsupported characters; API names should use only letters, numbers, and underscores + $orderby?: string; + // WARNING: The name "$select" contains unsupported characters; API names should use only letters, numbers, and underscores + $select?: string; + // WARNING: The name "$skip" contains unsupported characters; API names should use only letters, numbers, and underscores + $skip?: number; + // WARNING: The name "$top" contains unsupported characters; API names should use only letters, numbers, and underscores + $top?: number; +} + +// @public (undocumented) +interface RequestQueryStringifyOptions { + // (undocumented) + delimiter?: string; + // (undocumented) + encode?: boolean; +} + +// @public +interface Response { + // (undocumented) + body: any; + // (undocumented) + header: any; + // (undocumented) + status: number; +} + +// @public +class ResponseError extends BentleyError { + constructor(errorNumber: number | HttpStatus, message?: string, getMetaData?: GetMetaDataFunction); + // (undocumented) + protected _data?: any; + // (undocumented) + description?: string; + log(): void; + // (undocumented) + logMessage(): string; + static parse(response: any, log?: boolean): ResponseError; + // (undocumented) + static parseHttpStatus(statusType: number): HttpStatus; + static shouldRetry(error: any, response: any): boolean; + // (undocumented) + status?: number; +} + +// @public (undocumented) +class SeedFile extends WsgInstance { + // (undocumented) + downloadUrl?: string; + // (undocumented) + fileDescription?: string; + // (undocumented) + fileId?: GuidString; + // (undocumented) + fileName?: string; + // (undocumented) + fileSize?: string; + id?: GuidString; + // (undocumented) + iModelName?: string; + // (undocumented) + index?: number; + // (undocumented) + initializationState?: InitializationState; + // (undocumented) + isUploaded?: boolean; + // (undocumented) + mergedChangeSetId?: string; + // (undocumented) + uploadedDate?: string; + // (undocumented) + uploadUrl?: string; + // (undocumented) + userUploaded?: string; +} + +// @public +interface SettingsAdmin { + deleteSetting(alctx: ActivityLoggingContext, namespace: string, name: string, accessToken: AccessToken, applicationSpecific: boolean, projectId?: string, iModelId?: string): Promise; + deleteUserSetting(alctx: ActivityLoggingContext, namespace: string, name: string, accessToken: AccessToken, applicationSpecific: boolean, projectId?: string, iModelId?: string): Promise; + getSetting(alctx: ActivityLoggingContext, namespace: string, name: string, accessToken: AccessToken, applicationSpecific: boolean, projectId?: string, iModelId?: string): Promise; + getUserSetting(alctx: ActivityLoggingContext, namespace: string, name: string, accessToken: AccessToken, applicationSpecific: boolean, projectId?: string, iModelId?: string): Promise; + saveSetting(alctx: ActivityLoggingContext, settings: any, namespace: string, name: string, accessToken: AccessToken, applicationSpecific: boolean, projectId?: string, iModelId?: string): Promise; + saveUserSetting(alctx: ActivityLoggingContext, settings: any, namespace: string, name: string, accessToken: AccessToken, applicationSpecific: boolean, projectId?: string, iModelId?: string): Promise; +} + +// @public +class SettingsResult { + constructor(status: SettingsStatus, errorMessage?: string | undefined, setting?: any); + // (undocumented) + errorMessage?: string | undefined; + // (undocumented) + setting?: any; + // (undocumented) + status: SettingsStatus; +} + +// @public +enum SettingsStatus { + AuthorizationError = 110593, + IModelInvalid = 110596, + ProjectInvalid = 110595, + ServerError = 110598, + SettingNotFound = 110597, + // (undocumented) + SETTINGS_ERROR_BASE = 110592, + Success = 0, + UnknownError = 110600, + UrlError = 110594 +} + +// @public +class SmallThumbnail extends Thumbnail { +} + +// @public +class SoftiModelDeleteEvent extends IModelHubGlobalEvent { +} + +// @public +class StringIdQuery extends Query { + // (undocumented) + protected _byId?: string; + byId(id: string): this; + // (undocumented) + protected checkValue(id: string): void; + getId(): string | undefined; +} + +// @public +class Thumbnail extends WsgInstance { + // (undocumented) + id?: GuidString; +} + +// @public +class ThumbnailHandler { + constructor(handler: IModelBaseHandler); + download(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, thumbnail: Thumbnail | TipThumbnail): Promise; + get(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, size: ThumbnailSize, query?: ThumbnailQuery): Promise; +} + +// @public +class ThumbnailQuery extends InstanceIdQuery { + byVersionId(versionId: GuidString): this; +} + +// @public +interface TipThumbnail { + projectId: string; + size: ThumbnailSize; +} + +// @public +class Token { + protected constructor(samlAssertion: string); + // (undocumented) + protected _expiresAt?: Date; + // (undocumented) + protected _samlAssertion: string; + // (undocumented) + protected _startsAt?: Date; + // (undocumented) + protected _userInfo?: UserInfo; + // (undocumented) + protected _x509Certificate?: string; + // (undocumented) + getExpiresAt(): Date | undefined; + // (undocumented) + getSamlAssertion(): string | undefined; + // (undocumented) + getStartsAt(): Date | undefined; + // (undocumented) + getUserInfo(): UserInfo | undefined; + // (undocumented) + protected parseSamlAssertion(): boolean; +} + +// @public +class UlasClient extends Client { + constructor(); + getAccessToken(alctx: ActivityLoggingContext, authorizationToken: AuthorizationToken): Promise; + protected getUrlSearchKey(): string; + logFeature(alctx: ActivityLoggingContext, token: AccessToken, ...entries: FeatureLogEntry[]): Promise; + logUsage(alctx: ActivityLoggingContext, token: AccessToken, entry: UsageLogEntry): Promise; + // (undocumented) + protected setupOptionDefaults(options: RequestOptions): Promise; +} + +// WARNING: configURL has incomplete type information +// WARNING: configResolveUrlUsingRegion has incomplete type information +// @public +class UrlDiscoveryClient extends Client { + constructor(); + discoverUrl(alctx: ActivityLoggingContext, searchKey: string, regionId: number | undefined): Promise; + getUrl(): Promise; + protected getUrlSearchKey(): string; +} + +// @public +class UsageLogEntry { + constructor(hostName: string, usageType: UsageType); + readonly hostName: string; + productId?: number; + productVersion?: ProductVersion; + projectId?: GuidString; + readonly timestamp: string; + readonly usageType: UsageType; + userInfo?: UsageUserInfo; +} + +// @public +interface UsageLogEntryJson { + corID: GuidString; + country: string | undefined; + evTimeZ: string; + fstr: string; + hID: string; + imsID: GuidString | undefined; + lSrc: string; + lVer: number; + pid: GuidString | undefined; + polID: GuidString; + prdid: number | undefined; + projID: GuidString | undefined; + secID: string; + uID: string | undefined; + ultID: number | undefined; + uType: string; + ver: number | undefined; +} + +// @public +enum UsageType { + // (undocumented) + Beta = 2, + // (undocumented) + HomeUse = 3, + // (undocumented) + PreActivation = 4, + // (undocumented) + Production = 0, + // (undocumented) + Trial = 1 +} + +// @public +interface UsageUserInfo { + // (undocumented) + hostUserName?: string; + imsId: GuidString; + ultimateSite: number; + usageCountryIso: string; +} + +// @public +class UserInfo { + constructor( + id: string, + email?: { + id: string; + isVerified?: boolean | undefined; + } | undefined, + profile?: { + firstName: string; + lastName: string; + name?: string | undefined; + preferredUserName?: string | undefined; + } | undefined, + organization?: { + id: string; + name: string; + } | undefined, + featureTracking?: { + ultimateSite: string; + usageCountryIso: string; + } | undefined); + email?: { + id: string; + isVerified?: boolean | undefined; + } | undefined; + featureTracking?: { + ultimateSite: string; + usageCountryIso: string; + } | undefined; + // (undocumented) + static fromJson(jsonObj: any): UserInfo; + id: string; + organization?: { + id: string; + name: string; + } | undefined; + profile?: { + firstName: string; + lastName: string; + name?: string | undefined; + preferredUserName?: string | undefined; + } | undefined; +} + +// @public +class UserInfoHandler { + constructor(handler: IModelBaseHandler); + get(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, query?: UserInfoQuery): Promise; + readonly statistics: UserStatisticsHandler; +} + +// @public +class UserInfoQuery extends Query { + // (undocumented) + protected _byId?: string; + byId(id: string): this; + byIds(ids: string[]): this; + getId(): string | undefined; + // (undocumented) + readonly isQueriedByIds: boolean; +} + +// @public +class UserStatistics extends HubUserInfo { + briefcasesCount?: number; + lastChangeSetPushDate?: string; + ownedLocksCount?: number; + pushedChangeSetsCount?: number; +} + +// @public +class UserStatisticsHandler { + constructor(handler: IModelBaseHandler); + get(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, query?: UserStatisticsQuery): Promise; +} + +// @public +class UserStatisticsQuery extends Query { + constructor(); + // (undocumented) + protected _byId?: string; + byId(id: string): this; + byIds(ids: string[]): this; + getId(): string | undefined; + readonly isQueriedByIds: boolean; + selectAll(): this; + selectBriefcasesCount(): this; + selectLastChangeSetPushDate(): this; + selectOwnedLocksCount(): this; + selectPushedChangeSetsCount(): this; +} + +// @public +class Version extends WsgInstance { + changeSetId?: string; + createdDate?: string; + description?: string; + // (undocumented) + id?: GuidString; + largeThumbnailId?: GuidString; + name?: string; + smallThumbnailId?: GuidString; + userCreated?: string; +} + +// @public +class VersionEvent extends IModelHubEvent { + changeSetId?: string; + fromJson(obj: any): void; + versionId?: GuidString; + versionName?: string; +} + +// @public +class VersionHandler { + constructor(handler: IModelBaseHandler); + create(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, changeSetId: string, name: string, description?: string): Promise; + get(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, query?: VersionQuery): Promise; + update(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, version: Version): Promise; +} + +// @public +class VersionQuery extends InstanceIdQuery { + byChangeSet(changesetId: string): this; + byName(name: string): this; + selectThumbnailId(...sizes: ThumbnailSize[]): this; +} + +// WARNING: configHostRelyingPartyUri has incomplete type information +// WARNING: configUseHostRelyingPartyUriAsFallback has incomplete type information +// @public +class WsgClient extends Client { + protected constructor(apiVersion: string); + // (undocumented) + protected _url?: string; + // (undocumented) + apiVersion: string; + protected deleteInstance(alctx: ActivityLoggingContext, token: AccessToken, relativeUrlPath: string, instance?: T, requestOptions?: WsgRequestOptions): Promise; + getAccessToken(alctx: ActivityLoggingContext, authorizationToken: AuthorizationToken): Promise; + protected getInstances(alctx: ActivityLoggingContext, typedConstructor: new () => T, token: AccessToken, relativeUrlPath: string, queryOptions?: RequestQueryOptions): Promise; + protected abstract getRelyingPartyUrl(): string; + getUrl(alctx: ActivityLoggingContext, excludeApiVersion?: boolean): Promise; + protected postInstance(alctx: ActivityLoggingContext, typedConstructor: new () => T, token: AccessToken, relativeUrlPath: string, instance: T, requestOptions?: WsgRequestOptions): Promise; + protected postInstances(alctx: ActivityLoggingContext, typedConstructor: new () => T, token: AccessToken, relativeUrlPath: string, instances: T[], requestOptions?: WsgRequestOptions): Promise; + protected postQuery(alctx: ActivityLoggingContext, typedConstructor: new () => T, token: AccessToken, relativeUrlPath: string, queryOptions: RequestQueryOptions): Promise; + protected setupOptionDefaults(options: RequestOptions): Promise; +} + +// @public +class WsgError extends ResponseError { + constructor(errorNumber: number | HttpStatus, message?: string, getMetaData?: GetMetaDataFunction); + static getErrorStatus(errorId: number, httpStatusType: number): number; + static getWSStatusId(error: string): number; + log(): void; + static parse(response: any, log?: boolean): ResponseError; + static shouldRetry(error: any, response: any): boolean; +} + +// @public +class WsgInstance extends ECInstance { + // (undocumented) + changeState?: ChangeState; + // (undocumented) + eTag?: string; + // (undocumented) + wsgId: string; +} + +// @public +interface WsgRequestOptions { + // (undocumented) + CustomOptions?: any; + // (undocumented) + RefreshInstances?: boolean; + // (undocumented) + ResponseContent?: "FullInstance" | "Empty" | "InstanceId"; +} + +// WARNING: Unsupported export: ConstructorType +// WARNING: Unsupported export: ChangeState +// WARNING: Unsupported export: loggingCategory +// WARNING: Unsupported export: loggingCategoryFullUrl +// WARNING: Unsupported export: requestIdHeaderName +// WARNING: Unsupported export: EventType +// WARNING: Unsupported export: GlobalEventType +// WARNING: Unsupported export: ThumbnailSize +// (No @packagedocumentation comment for this package) diff --git a/common/api/imodeljs-common.api.ts b/common/api/imodeljs-common.api.ts new file mode 100644 index 0000000..6cec17e --- /dev/null +++ b/common/api/imodeljs-common.api.ts @@ -0,0 +1,4777 @@ +// @beta +module AmbientOcclusion { + interface Props { + readonly bias?: number; + readonly blurDelta?: number; + readonly blurSigma?: number; + // (undocumented) + readonly blurTexelStepSize?: number; + readonly intensity?: number; + readonly texelStepSize?: number; + readonly zLengthCap?: number; + } + + class Settings implements Props { + // (undocumented) + readonly bias?: number; + // (undocumented) + readonly blurDelta?: number; + // (undocumented) + readonly blurSigma?: number; + // (undocumented) + readonly blurTexelStepSize?: number; + // WARNING: The type "Settings" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + static defaults: Settings; + // WARNING: The type "Props" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Settings" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + static fromJSON(json?: Props): Settings; + // (undocumented) + readonly intensity?: number; + // (undocumented) + readonly texelStepSize?: number; + // WARNING: The type "Props" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + toJSON(): Props; + // (undocumented) + readonly zLengthCap?: number; + } + +} + +// @alpha (undocumented) +class AnalysisStyle implements AnalysisStyleProps { + // (undocumented) + clone(out?: AnalysisStyle): AnalysisStyle; + // (undocumented) + copyFrom(source: AnalysisStyle): void; + // (undocumented) + displacementChannelName?: string; + // (undocumented) + displacementScale?: number; + // (undocumented) + static fromJSON(json?: AnalysisStyleProps): AnalysisStyle; + // (undocumented) + inputName?: string; + // (undocumented) + inputRange?: Range1d; + // (undocumented) + normalChannelName?: string; + // (undocumented) + scalarChannelName?: string; + // (undocumented) + scalarRange?: Range1d; + // WARNING: The type "Gradient.ThematicSettings" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + scalarThematicSettings?: Gradient.ThematicSettings; + // (undocumented) + scalarThematicTexture?: RenderTexture; +} + +// @alpha +interface AnalysisStyleProps { + // (undocumented) + displacementChannelName?: string; + // (undocumented) + displacementScale?: number; + // (undocumented) + inputName?: string; + // (undocumented) + inputRange?: Range1d; + // (undocumented) + normalChannelName?: string; + // (undocumented) + scalarChannelName?: string; + // (undocumented) + scalarRange?: Range1d; + // (undocumented) + scalarThematicSettings?: Gradient.ThematicSettingsProps; +} + +// @public (undocumented) +enum AntiAliasPref { + // (undocumented) + Detect = 0, + // (undocumented) + Off = 2, + // (undocumented) + On = 1 +} + +// @public +interface AreaFillProps { + backgroundFill?: BackgroundFill; + color?: ColorDefProps; + display: FillDisplay; + gradient?: Gradient.SymbProps; + transparency?: number; +} + +// @public (undocumented) +module AreaPattern { + // (undocumented) + class HatchDefLine implements HatchDefLineProps { + // WARNING: The type "HatchDefLineProps" needs to be exported by the package (e.g. added to index.ts) + constructor(json: HatchDefLineProps); + // (undocumented) + angle?: Angle; + // (undocumented) + dashes?: number[]; + // (undocumented) + offset?: Point2d; + // (undocumented) + through?: Point2d; + } + + interface HatchDefLineProps { + angle?: AngleProps; + dashes?: number[]; + offset?: XYProps; + through?: XYProps; + } + + class Params implements ParamsProps { + // (undocumented) + angle1?: Angle; + // (undocumented) + angle2?: Angle; + // (undocumented) + applyTransform(transform: Transform): boolean; + // WARNING: The type "Params" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + clone(): Params; + // (undocumented) + color?: ColorDef; + // WARNING: The type "HatchDefLine" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + defLines?: HatchDefLine[]; + // WARNING: The type "Params" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + equals(other: Params): boolean; + // WARNING: The type "ParamsProps" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Params" needs to be exported by the package (e.g. added to index.ts) + static fromJSON(json?: ParamsProps): Params; + // (undocumented) + static getTransformPatternScale(transform: Transform): number; + // (undocumented) + invisibleBoundary?: boolean; + // (undocumented) + origin?: Point3d; + // (undocumented) + rotation?: YawPitchRollAngles; + // (undocumented) + scale?: number; + // (undocumented) + snappable?: boolean; + // (undocumented) + space1?: number; + // (undocumented) + space2?: number; + // (undocumented) + symbolId?: Id64String; + // (undocumented) + static transformPatternSpace(transform: Transform, oldSpace: number, patRot: Matrix3d, angle?: Angle): number; + // (undocumented) + weight?: number; + } + + interface ParamsProps { + angle1?: AngleProps; + angle2?: AngleProps; + color?: ColorDefProps; + defLines?: HatchDefLineProps[]; + invisibleBoundary?: boolean; + origin?: XYZProps; + rotation?: YawPitchRollProps; + scale?: number; + snappable?: boolean; + space1?: number; + space2?: number; + symbolId?: Id64String; + weight?: number; + } + +} + +// @beta +enum AuthStatus { + // (undocumented) + AUTHSTATUS_BASE = 131072, + // (undocumented) + Error = 131072, + // (undocumented) + Success = 0 +} + +// @public +interface AuxCoordSystem2dProps extends AuxCoordSystemProps { + angle?: AngleProps; + origin?: XYProps; +} + +// @public +interface AuxCoordSystem3dProps extends AuxCoordSystemProps { + origin?: XYZProps; + pitch?: AngleProps; + // (undocumented) + roll?: AngleProps; + // (undocumented) + yaw?: AngleProps; +} + +// @public (undocumented) +interface AuxCoordSystemProps extends ElementProps { + // (undocumented) + description?: string; + // (undocumented) + type?: number; +} + +// @public +enum BackgroundFill { + None = 0, + Outline = 2, + Solid = 1 +} + +// @public +interface BackgroundMapProps { + // (undocumented) + groundBias?: number; + // (undocumented) + providerData?: { + mapType?: BackgroundMapType; + } + providerName?: string; +} + +// @public +enum BackgroundMapType { + // (undocumented) + Aerial = 2, + // (undocumented) + Hybrid = 3, + // (undocumented) + Street = 1 +} + +// @beta +enum BatchType { + Classifier = 1, + Primary = 0 +} + +// @public +class BentleyCloudRpcConfiguration extends RpcConfiguration { + applicationAuthorizationKey: string; + applicationVersionKey: string; + // WARNING: The type "BentleyCloudRpcProtocol" needs to be exported by the package (e.g. added to index.ts) + readonly protocol: BentleyCloudRpcProtocol; +} + +// @public +class BentleyCloudRpcManager extends RpcManager { + static initializeClient(params: BentleyCloudRpcParams, interfaces: RpcInterfaceDefinition[]): BentleyCloudRpcConfiguration; + static initializeImpl(params: BentleyCloudRpcParams, interfaces: RpcInterfaceDefinition[]): BentleyCloudRpcConfiguration; +} + +// @public +interface BentleyCloudRpcParams { + info: OpenAPIInfo; + pendingRequestListener?: RpcRequestEventHandler; + protocol?: typeof BentleyCloudRpcProtocol; + uriPrefix?: string; +} + +// @public +class BentleyError extends Error { + constructor(errorNumber: number | IModelStatus | DbResult | BentleyStatus | BriefcaseStatus | RepositoryStatus | ChangeSetStatus | HttpStatus | WSStatus | IModelHubStatus, message?: string, log?: LogFunction, category?: string, getMetaData?: GetMetaDataFunction); + protected _initName(): string; + // (undocumented) + errorNumber: number; + // (undocumented) + getMetaData(): any; + // (undocumented) + readonly hasMetaData: boolean; +} + +// @public +enum BentleyStatus { + // (undocumented) + ERROR = 32768, + // (undocumented) + SUCCESS = 0 +} + +// @public +enum BisCodeSpec { + // (undocumented) + annotationFrameStyle = "bis:AnnotationFrameStyle", + // (undocumented) + annotationLeaderStyle = "bis:AnnotationLeaderStyle", + // (undocumented) + annotationTextStyle = "bis:AnnotationTextStyle", + // (undocumented) + auxCoordSystem2d = "bis:AuxCoordSystem2d", + // (undocumented) + auxCoordSystem3d = "bis:AuxCoordSystem3d", + // (undocumented) + auxCoordSystemSpatial = "bis:AuxCoordSystemSpatial", + // (undocumented) + categorySelector = "bis:CategorySelector", + // (undocumented) + colorBook = "bis:ColorBook", + // (undocumented) + displayStyle = "bis:DisplayStyle", + // (undocumented) + drawing = "bis:Drawing", + // (undocumented) + drawingCategory = "bis:DrawingCategory", + // (undocumented) + geometryPart = "bis:GeometryPart", + // (undocumented) + graphicalType2d = "bis:GraphicalType2d", + // (undocumented) + informationPartitionElement = "bis:InformationPartitionElement", + // (undocumented) + lineStyle = "bis:LineStyle", + // (undocumented) + linkElement = "bis:LinkElement", + // (undocumented) + modelSelector = "bis:ModelSelector", + // (undocumented) + nullCodeSpec = "bis:NullCodeSpec", + // (undocumented) + physicalMaterial = "bis:PhysicalMaterial", + // (undocumented) + physicalType = "bis:PhysicalType", + // (undocumented) + renderMaterial = "bis:RenderMaterial", + // (undocumented) + sheet = "bis:Sheet", + // (undocumented) + spatialCategory = "bis:SpatialCategory", + // (undocumented) + spatialLocationType = "bis:SpatialLocationType", + // (undocumented) + subCategory = "bis:SubCategory", + // (undocumented) + subject = "bis:Subject", + // (undocumented) + templateRecipe2d = "bis:TemplateRecipe2d", + // (undocumented) + templateRecipe3d = "bis:TemplateRecipe3d", + // (undocumented) + textAnnotationSeed = "bis:TextAnnotationSeed", + // (undocumented) + texture = "bis:Texture", + // (undocumented) + viewDefinition = "bis:ViewDefinition" +} + +// @beta (undocumented) +module BRepEntity { + interface DataProps { + data?: string; + faceSymbology?: FaceSymbologyProps[]; + transform?: TransformProps; + type?: Type; + } + + interface FaceSymbologyProps { + color?: ColorDefProps; + materialId?: Id64String; + transparency?: number; + } + + enum Type { + Sheet = 1, + Solid = 0, + Wire = 2 + } + +} + +// @beta +enum BriefcaseStatus { + // (undocumented) + CannotAcquire = 131072, + // (undocumented) + CannotApplyChanges = 131078, + // (undocumented) + CannotCopy = 131075, + // (undocumented) + CannotDelete = 131076, + // (undocumented) + CannotDownload = 131073, + // (undocumented) + CannotUpload = 131074, + // (undocumented) + VersionNotFound = 131077 +} + +// @public (undocumented) +interface CalloutProps extends GeometricElement2dProps { + // (undocumented) + drawingModel?: RelatedElementProps; +} + +// @public +class Camera implements CameraProps { + constructor(props?: CameraProps); + // (undocumented) + clone(): Camera; + // (undocumented) + copyFrom(rhs: Camera): void; + // (undocumented) + equals(other: Camera): boolean; + // (undocumented) + readonly eye: Point3d; + // (undocumented) + focusDist: number; + // (undocumented) + getEyePoint(): Point3d; + // (undocumented) + getFocusDistance(): number; + // (undocumented) + getLensAngle(): Angle; + // (undocumented) + invalidateFocus(): void; + // (undocumented) + readonly isFocusValid: boolean; + // (undocumented) + readonly isLensValid: boolean; + // (undocumented) + readonly isValid: boolean; + // (undocumented) + static isValidLensAngle(val: Angle): boolean; + // (undocumented) + readonly lens: Angle; + // (undocumented) + setEyePoint(pt: XYAndZ): void; + // (undocumented) + setFocusDistance(dist: number): void; + // (undocumented) + setLensAngle(angle: Angle): void; + // (undocumented) + validateLens(): void; + // (undocumented) + static validateLensAngle(val: Angle): void; +} + +// @public +interface CameraProps { + // (undocumented) + eye: XYZProps; + // (undocumented) + focusDist: number; + // (undocumented) + lens: AngleProps; +} + +// @public +class Cartographic implements LatLongAndHeight { + constructor(longitude?: number, latitude?: number, height?: number); + clone(result?: Cartographic): Cartographic; + equals(right: LatLongAndHeight): boolean; + equalsEpsilon(right: LatLongAndHeight, epsilon: number): boolean; + static fromAngles(longitude: Angle, latitude: Angle, height: number, result?: Cartographic): Cartographic; + static fromDegrees(longitude: number, latitude: number, height: number, result?: Cartographic): Cartographic; + static fromEcef(cartesian: Point3d, result?: Cartographic): Cartographic | undefined; + static fromRadians(longitude: number, latitude: number, height?: number, result?: Cartographic): Cartographic; + // (undocumented) + height: number; + // (undocumented) + latitude: number; + // (undocumented) + longitude: number; + toEcef(result?: Point3d): Point3d; + toString(): string; +} + +// @public +class CartographicRange { + constructor(spatialRange: Range3d, spatialToEcef: Transform); + getLongitudeLatitudeBoundingBox(): Range2d; + // (undocumented) + intersectsRange(other: CartographicRange): boolean; +} + +// @public +interface CategoryProps extends ElementProps { + // (undocumented) + description?: string; + // (undocumented) + rank?: Rank; +} + +// @public +interface CategorySelectorProps extends DefinitionElementProps { + // (undocumented) + categories: Id64Array; +} + +// @beta (undocumented) +interface ChangedElements { + // (undocumented) + classIds: Id64String[]; + // (undocumented) + elements: Id64String[]; + // (undocumented) + opcodes: number[]; +} + +// @public +enum ChangedValueState { + // (undocumented) + AfterInsert = 1, + // (undocumented) + AfterUpdate = 3, + // (undocumented) + BeforeDelete = 4, + // (undocumented) + BeforeUpdate = 2 +} + +// @public +enum ChangeOpCode { + // (undocumented) + Delete = 4, + // (undocumented) + Insert = 1, + // (undocumented) + Update = 2 +} + +// @beta +enum ChangeSetStatus { + ApplyError = 90113, + CannotMergeIntoMaster = 90136, + CannotMergeIntoReadonly = 90135, + CannotMergeIntoReversed = 90137, + // (undocumented) + CHANGESET_ERROR_BASE = 90112, + ChangeTrackingNotEnabled = 90114, + CorruptedChangeStream = 90115, + CouldNotOpenDgnDb = 90131, + FileNotFound = 90116, + FileWriteError = 90117, + HasLocalChanges = 90118, + HasUncommittedChanges = 90119, + InDynamicTransaction = 90122, + InvalidId = 90120, + InvalidVersion = 90121, + IsCreatingChangeSet = 90123, + IsNotCreatingChangeSet = 90124, + MergePropagationError = 90125, + MergeSchemaChangesOnOpen = 90132, + NothingToMerge = 90126, + NoTransactions = 90127, + ParentMismatch = 90128, + ProcessSchemaChangesOnOpen = 90134, + ReverseOrReinstateSchemaChangesOnOpen = 90133, + SQLiteError = 90129, + // (undocumented) + Success = 0, + WrongDgnDb = 90130 +} + +// @public +class Code implements CodeProps { + constructor(val: CodeProps); + static createEmpty(): Code; + // (undocumented) + equals(other: Code): boolean; + // (undocumented) + static fromJSON(json?: any): Code; + // (undocumented) + getValue(): string; + scope: string; + spec: Id64String; + value?: string; +} + +// @public +interface CodeProps { + // (undocumented) + scope: CodeScopeProps; + // (undocumented) + spec: Id64String; + // (undocumented) + value?: string; +} + +// @public +module CodeScopeSpec { + // @public + enum ScopeRequirement { + ElementId = 1, + FederationGuid = 2 + } + + // @public + enum Type { + Model = 2, + ParentElement = 3, + RelatedElement = 4, + Repository = 1 + } + +} + +// @public +class CodeSpec { + // WARNING: The type "CodeScopeSpec.Type" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "CodeScopeSpec.ScopeRequirement" needs to be exported by the package (e.g. added to index.ts) + constructor(iModel: IModel, id: Id64String, name: string, specScopeType: CodeScopeSpec.Type, scopeReq?: CodeScopeSpec.ScopeRequirement, properties?: any); + id: Id64String; + iModel: IModel; + // (undocumented) + readonly isValid: boolean; + // (undocumented) + name: string; + // (undocumented) + properties: any; + // WARNING: The type "CodeScopeSpec.ScopeRequirement" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + scopeReq: CodeScopeSpec.ScopeRequirement; + // WARNING: The type "CodeScopeSpec.Type" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + specScopeType: CodeScopeSpec.Type; +} + +// @public +enum ColorByName { + // (undocumented) + aliceBlue = 16775408, + // (undocumented) + amber = 49151, + // (undocumented) + antiqueWhite = 14150650, + // (undocumented) + aqua = 16776960, + // (undocumented) + aquamarine = 13959039, + // (undocumented) + azure = 16777200, + // (undocumented) + beige = 14480885, + // (undocumented) + bisque = 12903679, + // (undocumented) + black = 0, + // (undocumented) + blanchedAlmond = 13495295, + // (undocumented) + blue = 16711680, + // (undocumented) + blueViolet = 14822282, + // (undocumented) + brown = 2763429, + // (undocumented) + burlyWood = 8894686, + // (undocumented) + cadetBlue = 10526303, + // (undocumented) + chartreuse = 65407, + // (undocumented) + chocolate = 1993170, + // (undocumented) + coral = 5275647, + // (undocumented) + cornflowerBlue = 15570276, + // (undocumented) + cornSilk = 14481663, + // (undocumented) + crimson = 3937500, + // (undocumented) + cyan = 16776960, + // (undocumented) + darkBlue = 9109504, + // (undocumented) + darkBrown = 2179941, + // (undocumented) + darkCyan = 9145088, + // (undocumented) + darkGoldenrod = 755384, + // (undocumented) + darkGray = 11119017, + // (undocumented) + darkGreen = 25600, + // (undocumented) + darkGrey = 11119017, + // (undocumented) + darkKhaki = 7059389, + // (undocumented) + darkMagenta = 9109643, + // (undocumented) + darkOliveGreen = 3107669, + // (undocumented) + darkOrange = 36095, + // (undocumented) + darkOrchid = 13382297, + // (undocumented) + darkRed = 139, + // (undocumented) + darkSalmon = 8034025, + // (undocumented) + darkSeagreen = 9419919, + // (undocumented) + darkSlateBlue = 9125192, + // (undocumented) + darkSlateGray = 5197615, + // (undocumented) + darkSlateGrey = 5197615, + // (undocumented) + darkTurquoise = 13749760, + // (undocumented) + darkViolet = 13828244, + // (undocumented) + deepPink = 9639167, + // (undocumented) + deepSkyBlue = 16760576, + // (undocumented) + dimGray = 6908265, + // (undocumented) + dimGrey = 6908265, + // (undocumented) + dodgerBlue = 16748574, + // (undocumented) + fireBrick = 2237106, + // (undocumented) + floralWhite = 15792895, + // (undocumented) + forestGreen = 2263842, + // (undocumented) + fuchsia = 16711935, + // (undocumented) + gainsboro = 14474460, + // (undocumented) + ghostWhite = 16775416, + // (undocumented) + gold = 55295, + // (undocumented) + goldenrod = 2139610, + // (undocumented) + gray = 8421504, + // (undocumented) + green = 32768, + // (undocumented) + greenYellow = 3145645, + // (undocumented) + grey = 8421504, + // (undocumented) + honeydew = 15794160, + // (undocumented) + hotPink = 11823615, + // (undocumented) + indianRed = 6053069, + // (undocumented) + indigo = 8519755, + // (undocumented) + ivory = 15794175, + // (undocumented) + khaki = 9234160, + // (undocumented) + lavender = 16443110, + // (undocumented) + lavenderBlush = 16118015, + // (undocumented) + lawnGreen = 64636, + // (undocumented) + lemonChiffon = 13499135, + // (undocumented) + lightBlue = 15128749, + // (undocumented) + lightCoral = 8421616, + // (undocumented) + lightCyan = 16777184, + // (undocumented) + lightGoldenrodYellow = 13826810, + // (undocumented) + lightGray = 13882323, + // (undocumented) + lightGreen = 9498256, + // (undocumented) + lightGrey = 13882323, + // (undocumented) + lightPink = 12695295, + // (undocumented) + lightSalmon = 8036607, + // (undocumented) + lightSeagreen = 11186720, + // (undocumented) + lightSkyBlue = 16436871, + // (undocumented) + lightSlateGray = 10061943, + // (undocumented) + lightSlateGrey = 10061943, + // (undocumented) + lightSteelBlue = 14599344, + // (undocumented) + lightyellow = 14745599, + // (undocumented) + lime = 65280, + // (undocumented) + limeGreen = 3329330, + // (undocumented) + linen = 15134970, + // (undocumented) + magenta = 16711935, + // (undocumented) + maroon = 128, + // (undocumented) + mediumAquamarine = 11193702, + // (undocumented) + mediumBlue = 13434880, + // (undocumented) + mediumOrchid = 13850042, + // (undocumented) + mediumPurple = 14381203, + // (undocumented) + mediumSeaGreen = 7451452, + // (undocumented) + mediumSlateBlue = 15624315, + // (undocumented) + mediumSpringGreen = 10156544, + // (undocumented) + mediumTurquoise = 13422920, + // (undocumented) + mediumVioletRed = 8721863, + // (undocumented) + midnightBlue = 7346457, + // (undocumented) + mintCream = 16449525, + // (undocumented) + mistyRose = 14804223, + // (undocumented) + moccasin = 11920639, + // (undocumented) + navajoWhite = 11394815, + // (undocumented) + navy = 8388608, + // (undocumented) + oldLace = 15136253, + // (undocumented) + olive = 32896, + // (undocumented) + oliveDrab = 2330219, + // (undocumented) + orange = 42495, + // (undocumented) + orangeRed = 17919, + // (undocumented) + orchid = 14053594, + // (undocumented) + paleGoldenrod = 11200750, + // (undocumented) + paleGreen = 10025880, + // (undocumented) + paleTurquoise = 15658671, + // (undocumented) + paleVioletRed = 9662683, + // (undocumented) + papayaWhip = 14020607, + // (undocumented) + peachPuff = 12180223, + // (undocumented) + peru = 4163021, + // (undocumented) + pink = 13353215, + // (undocumented) + plum = 14524637, + // (undocumented) + powderBlue = 15130800, + // (undocumented) + purple = 8388736, + // (undocumented) + rebeccaPurple = 10040166, + // (undocumented) + red = 255, + // (undocumented) + rosyBrown = 9408444, + // (undocumented) + royalBlue = 14772545, + // (undocumented) + saddleBrown = 1262987, + // (undocumented) + salmon = 7504122, + // (undocumented) + sandyBrown = 6333684, + // (undocumented) + seaGreen = 5737262, + // (undocumented) + seaShell = 15660543, + // (undocumented) + sienna = 2970272, + // (undocumented) + silver = 12632256, + // (undocumented) + skyBlue = 15453831, + // (undocumented) + slateBlue = 13458026, + // (undocumented) + slateGray = 9470064, + // (undocumented) + slateGrey = 9470064, + // (undocumented) + snow = 16448255, + // (undocumented) + springGreen = 8388352, + // (undocumented) + steelBlue = 11829830, + // (undocumented) + tan = 9221330, + // (undocumented) + teal = 8421376, + // (undocumented) + thistle = 14204888, + // (undocumented) + tomato = 4678655, + // (undocumented) + turquoise = 13688896, + // (undocumented) + violet = 15631086, + // (undocumented) + wheat = 11788021, + // (undocumented) + white = 16777215, + // (undocumented) + whiteSmoke = 16119285, + // (undocumented) + yellow = 65535, + // (undocumented) + yellowGreen = 3329434 +} + +// @public +class ColorDef { + constructor(val?: string | ColorDefProps); + adjustForContrast(other: ColorDef, alpha?: number): ColorDef; + static readonly black: ColorDef; + static readonly blue: ColorDef; + clone(): ColorDef; + readonly colors: { + b: number; + g: number; + r: number; + t: number; + } + equals(other: ColorDef): boolean; + static from(red: number, green: number, blue: number, transparency?: number, result?: ColorDef): ColorDef; + static fromHSL(h: number, s: number, l: number, out?: ColorDef): ColorDef; + static fromHSV(hsv: HSVColor, out?: ColorDef): ColorDef; + static fromJSON(json?: any): ColorDef; + getAbgr(): number; + getAlpha(): number; + getRgb(): number; + static readonly green: ColorDef; + invert(): ColorDef; + readonly isOpaque: boolean; + lerp(color2: ColorDef, weight: number, result?: ColorDef): ColorDef; + readonly name: string | undefined; + static readonly red: ColorDef; + static rgb2bgr(val: number): number; + setAlpha(alpha: number): void; + setFrom(other: ColorDef): void; + setTransparency(transparency: number): void; + tbgr: number; + toHexString(): string; + toHSL(opt?: HSLColor): HSLColor; + toHSV(out?: HSVColor): HSVColor; + toJSON(): ColorDefProps; + toRgbString(): string; + static readonly white: ColorDef; +} + +// @public (undocumented) +class ColorIndex { + constructor(); + // (undocumented) + readonly hasAlpha: boolean; + // (undocumented) + initNonUniform(colors: Uint32Array, indices: Uint16Array, hasAlpha: boolean): void; + // (undocumented) + initUniform(color: ColorDef | number): void; + // (undocumented) + readonly isUniform: boolean; + // (undocumented) + readonly nonUniform: NonUniformColor | undefined; + // (undocumented) + readonly numColors: number; + // (undocumented) + reset(): void; + // (undocumented) + readonly uniform: ColorDef | undefined; +} + +// @public +interface ContextRealityModelProps { + // (undocumented) + description?: string; + // (undocumented) + name?: string; + // (undocumented) + tilesetUrl: string; +} + +// @public +interface CreateIModelProps extends IModelProps { + client?: string; + guid?: GuidString; + thumbnail?: ThumbnailProps; +} + +// @beta +interface CustomAttribute { + ecclass: string; + properties: { + [propName: string]: any; + } +} + +// @public +enum DbResult { + BE_SQLITE_ABORT = 4, + // (undocumented) + BE_SQLITE_ABORT_ROLLBACK = 516, + BE_SQLITE_AUTH = 23, + BE_SQLITE_BUSY = 5, + // (undocumented) + BE_SQLITE_BUSY_RECOVERY = 261, + BE_SQLITE_CANTOPEN = 14, + // (undocumented) + BE_SQLITE_CANTOPEN_FULLPATH = 782, + // (undocumented) + BE_SQLITE_CANTOPEN_ISDIR = 526, + // (undocumented) + BE_SQLITE_CANTOPEN_NOTEMPDIR = 270, + BE_SQLITE_CONSTRAINT_BASE = 19, + // (undocumented) + BE_SQLITE_CONSTRAINT_CHECK = 275, + // (undocumented) + BE_SQLITE_CONSTRAINT_COMMITHOOK = 531, + // (undocumented) + BE_SQLITE_CONSTRAINT_FOREIGNKEY = 787, + // (undocumented) + BE_SQLITE_CONSTRAINT_FUNCTION = 1043, + // (undocumented) + BE_SQLITE_CONSTRAINT_NOTNULL = 1299, + // (undocumented) + BE_SQLITE_CONSTRAINT_PRIMARYKEY = 1555, + // (undocumented) + BE_SQLITE_CONSTRAINT_TRIGGER = 1811, + // (undocumented) + BE_SQLITE_CONSTRAINT_UNIQUE = 2067, + // (undocumented) + BE_SQLITE_CONSTRAINT_VTAB = 2323, + BE_SQLITE_CORRUPT = 11, + // (undocumented) + BE_SQLITE_CORRUPT_VTAB = 267, + BE_SQLITE_DONE = 101, + BE_SQLITE_EMPTY = 16, + BE_SQLITE_ERROR = 1, + BE_SQLITE_ERROR_AlreadyOpen = 33554442, + BE_SQLITE_ERROR_BadDbProfile = 100663306, + BE_SQLITE_ERROR_ChangeTrackError = 218103818, + BE_SQLITE_ERROR_CouldNotAcquireLocksOrCodes = 352321546, + BE_SQLITE_ERROR_FileExists = 16777226, + BE_SQLITE_ERROR_FileNotFound = 67108874, + BE_SQLITE_ERROR_InvalidChangeSetVersion = 234881034, + BE_SQLITE_ERROR_InvalidProfileVersion = 117440522, + BE_SQLITE_ERROR_NoPropertyTable = 50331658, + BE_SQLITE_ERROR_NoTxnActive = 83886090, + BE_SQLITE_ERROR_ProfileTooNew = 201326602, + BE_SQLITE_ERROR_ProfileTooNewForReadWrite = 184549386, + BE_SQLITE_ERROR_ProfileTooOld = 167772170, + BE_SQLITE_ERROR_ProfileTooOldForReadWrite = 150994954, + BE_SQLITE_ERROR_ProfileUpgradeFailed = 134217738, + BE_SQLITE_ERROR_SchemaImportFailed = 335544330, + BE_SQLITE_ERROR_SchemaLockFailed = 301989898, + BE_SQLITE_ERROR_SchemaTooNew = 268435466, + BE_SQLITE_ERROR_SchemaTooOld = 285212682, + BE_SQLITE_ERROR_SchemaUpgradeFailed = 318767114, + BE_SQLITE_ERROR_SchemaUpgradeRequired = 251658250, + BE_SQLITE_FORMAT = 24, + BE_SQLITE_FULL = 13, + BE_SQLITE_INTERNAL = 2, + BE_SQLITE_INTERRUPT = 9, + BE_SQLITE_IOERR = 10, + // (undocumented) + BE_SQLITE_IOERR_ACCESS = 3338, + // (undocumented) + BE_SQLITE_IOERR_BLOCKED = 2826, + // (undocumented) + BE_SQLITE_IOERR_CHECKRESERVEDLOCK = 3594, + // (undocumented) + BE_SQLITE_IOERR_CLOSE = 4106, + // (undocumented) + BE_SQLITE_IOERR_DELETE = 2570, + // (undocumented) + BE_SQLITE_IOERR_DELETE_NOENT = 5898, + // (undocumented) + BE_SQLITE_IOERR_DIR_CLOSE = 4362, + // (undocumented) + BE_SQLITE_IOERR_DIR_FSYNC = 1290, + // (undocumented) + BE_SQLITE_IOERR_FSTAT = 1802, + // (undocumented) + BE_SQLITE_IOERR_FSYNC = 1034, + // (undocumented) + BE_SQLITE_IOERR_LOCK = 3850, + // (undocumented) + BE_SQLITE_IOERR_NOMEM = 3082, + // (undocumented) + BE_SQLITE_IOERR_RDLOCK = 2314, + // (undocumented) + BE_SQLITE_IOERR_READ = 266, + // (undocumented) + BE_SQLITE_IOERR_SEEK = 5642, + // (undocumented) + BE_SQLITE_IOERR_SHMLOCK = 5130, + // (undocumented) + BE_SQLITE_IOERR_SHMMAP = 5386, + // (undocumented) + BE_SQLITE_IOERR_SHMOPEN = 4618, + // (undocumented) + BE_SQLITE_IOERR_SHMSIZE = 4874, + // (undocumented) + BE_SQLITE_IOERR_SHORT_READ = 522, + // (undocumented) + BE_SQLITE_IOERR_TRUNCATE = 1546, + // (undocumented) + BE_SQLITE_IOERR_UNLOCK = 2058, + // (undocumented) + BE_SQLITE_IOERR_WRITE = 778, + BE_SQLITE_LOCKED = 6, + // (undocumented) + BE_SQLITE_LOCKED_SHAREDCACHE = 262, + BE_SQLITE_MISMATCH = 20, + BE_SQLITE_MISUSE = 21, + BE_SQLITE_NOLFS = 22, + BE_SQLITE_NOMEM = 7, + BE_SQLITE_NOTADB = 26, + BE_SQLITE_NOTFOUND = 12, + // (undocumented) + BE_SQLITE_OK = 0, + BE_SQLITE_PERM = 3, + BE_SQLITE_PROTOCOL = 15, + BE_SQLITE_RANGE = 25, + BE_SQLITE_READONLY = 8, + // (undocumented) + BE_SQLITE_READONLY_CANTLOCK = 520, + // (undocumented) + BE_SQLITE_READONLY_RECOVERY = 264, + // (undocumented) + BE_SQLITE_READONLY_ROLLBACK = 776, + BE_SQLITE_ROW = 100, + BE_SQLITE_SCHEMA = 17, + BE_SQLITE_TOOBIG = 18 +} + +// @beta +class DecorationGeometryProps { + constructor(id: Id64String, geometryStream: GeometryStreamProps); + // (undocumented) + readonly geometryStream: GeometryStreamProps; + // (undocumented) + readonly id: Id64String; +} + +// @public +interface DefinitionElementProps extends ElementProps { + // (undocumented) + isPrivate?: boolean; +} + +// @beta +interface DisplayStyle3dProps extends DisplayStyleProps { + jsonProperties?: { + styles?: DisplayStyle3dSettingsProps; + } +} + +// @beta +class DisplayStyle3dSettings extends DisplayStyleSettings { + constructor(jsonProperties: { + styles?: DisplayStyle3dSettingsProps; + }); + // WARNING: The type "AmbientOcclusion.Settings" needs to be exported by the package (e.g. added to index.ts) + ambientOcclusionSettings: AmbientOcclusion.Settings; + // (undocumented) + environment: EnvironmentProps; + // WARNING: The type "HiddenLine.Settings" needs to be exported by the package (e.g. added to index.ts) + hiddenLineSettings: HiddenLine.Settings; + // (undocumented) + toJSON(): DisplayStyle3dSettingsProps; +} + +// @beta +interface DisplayStyle3dSettingsProps extends DisplayStyleSettingsProps { + ao?: AmbientOcclusion.Props; + environment?: EnvironmentProps; + hline?: HiddenLine.SettingsProps; +} + +// @beta +interface DisplayStyleProps extends DefinitionElementProps { + jsonProperties?: { + styles?: DisplayStyleSettingsProps; + } +} + +// @beta +class DisplayStyleSettings { + constructor(jsonProperties: { + styles?: DisplayStyleSettingsProps; + }); + // (undocumented) + protected readonly _json: DisplayStyleSettingsProps; + backgroundColor: ColorDef; + // (undocumented) + backgroundMap: BackgroundMapProps | undefined; + dropSubCategoryOverride(id: Id64String): void; + getSubCategoryOverride(id: Id64String): SubCategoryOverride | undefined; + readonly hasSubCategoryOverride: boolean; + monochromeColor: ColorDef; + overrideSubCategory(id: Id64String, ovr: SubCategoryOverride): void; + // (undocumented) + toJSON(): DisplayStyleSettingsProps; + viewFlags: ViewFlags; +} + +// @beta +interface DisplayStyleSettingsProps { + analysisStyle?: AnalysisStyleProps; + backgroundColor?: ColorDefProps; + backgroundMap?: BackgroundMapProps; + ContextRealityModels?: ContextRealityModelProps[]; + monochromeColor?: ColorDefProps; + scheduleScript?: RenderSchedule.ElementTimelineProps[]; + subCategoryOvr?: DisplayStyleSubCategoryProps[]; + // (undocumented) + viewflags?: ViewFlagProps; +} + +// @public +interface DisplayStyleSubCategoryProps extends SubCategoryAppearance.Props { + subCategory?: Id64String; +} + +// @public +class EcefLocation implements EcefLocationProps { + constructor(props: EcefLocationProps); + static createFromCartographicOrigin(origin: Cartographic): EcefLocation; + getTransform(): Transform; + readonly orientation: YawPitchRollAngles; + readonly origin: Point3d; +} + +// @public +interface EcefLocationProps { + orientation: YawPitchRollProps; + origin: XYZProps; +} + +// @public +class ECJsNames { + static systemPropertyToJsName(systemPropertyType: ECSqlSystemProperty): string; + static toJsName(propName: string, isSystemProperty?: boolean): string; +} + +// @public +enum ECSqlSystemProperty { + // (undocumented) + ECClassId = 1, + // (undocumented) + ECInstanceId = 0, + // (undocumented) + NavigationId = 6, + // (undocumented) + NavigationRelClassId = 7, + // (undocumented) + PointX = 8, + // (undocumented) + PointY = 9, + // (undocumented) + PointZ = 10, + // (undocumented) + SourceECClassId = 3, + // (undocumented) + SourceECInstanceId = 2, + // (undocumented) + TargetECClassId = 5, + // (undocumented) + TargetECInstanceId = 4 +} + +// @public +enum ECSqlValueType { + // (undocumented) + Blob = 1, + // (undocumented) + Boolean = 2, + // (undocumented) + DateTime = 3, + // (undocumented) + Double = 4, + // (undocumented) + Geometry = 5, + // (undocumented) + Guid = 16, + // (undocumented) + Id = 6, + // (undocumented) + Int = 7, + // (undocumented) + Int64 = 8, + // (undocumented) + Navigation = 12, + // (undocumented) + Point2d = 9, + // (undocumented) + Point3d = 10, + // (undocumented) + PrimitiveArray = 14, + // (undocumented) + String = 11, + // (undocumented) + Struct = 13, + // (undocumented) + StructArray = 15 +} + +// @public (undocumented) +class EdgeArgs { + // (undocumented) + clear(): void; + // (undocumented) + edges?: MeshEdge[]; + // (undocumented) + init(meshEdges?: MeshEdges): boolean; + // (undocumented) + readonly isValid: boolean; + // (undocumented) + readonly numEdges: number; +} + +// @public +class ElectronRpcConfiguration extends RpcConfiguration { + // (undocumented) + static readonly isElectron: boolean; + // WARNING: The type "ElectronRpcProtocol" needs to be exported by the package (e.g. added to index.ts) + protocol: ElectronRpcProtocol; +} + +// @public +class ElectronRpcManager extends RpcManager { + static initializeClient(params: ElectronRpcParams, interfaces: RpcInterfaceDefinition[]): ElectronRpcConfiguration; + static initializeImpl(params: ElectronRpcParams, interfaces: RpcInterfaceDefinition[]): ElectronRpcConfiguration; +} + +// @public +interface ElectronRpcParams { + // (undocumented) + protocol?: typeof ElectronRpcProtocol; +} + +// @public +interface ElementAspectProps extends EntityProps { + // (undocumented) + element: RelatedElementProps; +} + +// @public +interface ElementLoadProps { + // (undocumented) + code?: CodeProps; + // (undocumented) + federationGuid?: GuidString; + // (undocumented) + id?: Id64String; + wantBRepData?: boolean; + wantGeometry?: boolean; +} + +// @public +interface ElementProps extends EntityProps { + code: CodeProps; + federationGuid?: GuidString; + jsonProperties?: any; + model: Id64String; + parent?: RelatedElementProps; + userLabel?: string; +} + +// @beta +class EntityMetaData implements EntityMetaDataProps { + constructor(jsonObj: EntityMetaDataProps); + readonly baseClasses: string[]; + readonly customAttributes?: CustomAttribute[]; + // (undocumented) + readonly description?: string; + // (undocumented) + readonly displayLabel?: string; + readonly ecclass: string; + // (undocumented) + readonly modifier?: string; + readonly properties: { + [propName: string]: PropertyMetaData; + } +} + +// @beta (undocumented) +interface EntityMetaDataProps { + baseClasses: string[]; + customAttributes?: CustomAttribute[]; + // (undocumented) + description?: string; + // (undocumented) + displayLabel?: string; + // (undocumented) + ecclass: string; + // (undocumented) + modifier?: string; + properties: { + [propName: string]: PropertyMetaData; + } +} + +// @public +interface EntityProps { + // (undocumented) + [propName: string]: any; + classFullName: string; + id?: Id64String; +} + +// @alpha +interface EntityQueryParams { + from?: string; + limit?: number; + offset?: number; + only?: boolean; + orderBy?: string; + where?: string; +} + +// @public +interface EnvironmentProps { + // (undocumented) + ground?: GroundPlaneProps; + // (undocumented) + sky?: SkyBoxProps; +} + +// @beta +class Feature { + constructor(elementId?: Id64String, subCategoryId?: Id64String, geometryClass?: GeometryClass); + // (undocumented) + compare(rhs: Feature): number; + // (undocumented) + readonly elementId: string; + // (undocumented) + equals(other: Feature): boolean; + // (undocumented) + readonly geometryClass: GeometryClass; + // (undocumented) + readonly isDefined: boolean; + // (undocumented) + readonly isUndefined: boolean; + // (undocumented) + readonly subCategoryId: string; +} + +// @public +class FeatureGates { + addMonitor(feature: string, monitor: (val: GateValue) => void): () => void; + check(feature: string, defaultVal?: GateValue): GateValue; + readonly gates: Map; + onChanged: BeEvent<(feature: string, val: GateValue) => void>; + setGate(feature: string, val: GateValue): void; +} + +// @public (undocumented) +class FeatureIndex { + constructor(); + // (undocumented) + featureID: number; + // (undocumented) + featureIDs?: Uint32Array; + // (undocumented) + readonly isEmpty: boolean; + // (undocumented) + readonly isUniform: boolean; + // (undocumented) + reset(): void; + // (undocumented) + type: FeatureIndexType; +} + +// @public (undocumented) +enum FeatureIndexType { + // (undocumented) + Empty = 0, + // (undocumented) + NonUniform = 2, + // (undocumented) + Uniform = 1 +} + +// @beta +class FeatureTable extends IndexMap { + constructor(maxFeatures: number, modelId?: Id64String, type?: BatchType); + // (undocumented) + readonly anyDefined: boolean; + findFeature(index: number): Feature | undefined; + // (undocumented) + getArray(): Array>; + // (undocumented) + insertWithIndex(feature: Feature, index: number): void; + readonly isClassifier: boolean; + readonly isUniform: boolean; + readonly maxFeatures: number; + // (undocumented) + readonly modelId: Id64String; + // (undocumented) + readonly type: BatchType; + readonly uniform: Feature | undefined; +} + +// @public (undocumented) +interface FilePropertyProps { + // (undocumented) + id?: number | string; + // (undocumented) + name: string; + // (undocumented) + namespace: string; + // (undocumented) + subId?: number | string; +} + +// @public +enum FillDisplay { + Always = 2, + Blanking = 3, + ByView = 1, + Never = 0 +} + +// @public +enum FillFlags { + Always = 2, + Background = 8, + Behind = 4, + Blanking = 6, + ByView = 1, + // (undocumented) + None = 0 +} + +// @public +class FontMap { + constructor(props: FontMapProps); + // (undocumented) + readonly fonts: Map; + getFont(arg: string | number): FontProps | undefined; + // (undocumented) + toJSON(): FontMapProps; +} + +// @public +interface FontMapProps { + // (undocumented) + fonts: FontProps[]; +} + +// @public +interface FontProps { + // (undocumented) + id: number; + // (undocumented) + name: string; + // (undocumented) + type: FontType; +} + +// @public +enum FontType { + // (undocumented) + Rsc = 2, + // (undocumented) + Shx = 3, + // (undocumented) + TrueType = 1 +} + +// @public (undocumented) +interface FormDataCommon { + // (undocumented) + append(name: string, value: string | Blob | Buffer, fileName?: string): void; +} + +// @public +class Frustum { + constructor(); + clone(result?: Frustum): Frustum; + distance(corner1: number, corner2: number): number; + equals(rhs: Frustum): boolean; + fixPointOrder(): void; + static fromRange(range: LowAndHighXYZ | LowAndHighXY, out?: Frustum): Frustum; + getCenter(): Point3d; + getCorner(i: number): Point3d; + getFraction(): number; + getRangePlanes(clipFront: boolean, clipBack: boolean, expandPlaneDistance: number): ConvexClipPlaneSet; + readonly hasMirror: boolean; + initFromRange(range: LowAndHighXYZ | LowAndHighXY): void; + initNpc(): this; + invalidate(): void; + isSame(other: Frustum): boolean; + multiply(trans: Transform): void; + readonly points: Point3d[]; + scaleAboutCenter(scale: number): void; + setFrom(other: Frustum): void; + toMap4d(): Map4d | undefined; + toRange(range?: Range3d): Range3d; + transformBy(trans: Transform, result?: Frustum): Frustum; + translate(offset: Vector3d): void; +} + +// @public (undocumented) +class FrustumPlanes { +} + +// @public (undocumented) +interface FunctionalElementProps extends ElementProps { + // (undocumented) + typeDefinition?: RelatedElementProps; +} + +// @beta +interface GeoCoordinatesRequestProps { + // (undocumented) + iModelCoords: XYZProps[]; + // (undocumented) + targetDatum: string; +} + +// @beta +interface GeoCoordinatesResponseProps { + // (undocumented) + fromCache: number; + // (undocumented) + geoCoords: PointWithStatus[]; +} + +// @public (undocumented) +enum GeoCoordStatus { + // (undocumented) + CSMapError = 4096, + // (undocumented) + NoDatumConverter = 25, + // (undocumented) + NoGCSDefined = 100, + // (undocumented) + OutOfMathematicalDomain = 2, + // (undocumented) + OutOfUsefulRange = 1, + // (undocumented) + Pending = -41556, + // (undocumented) + Success = 0, + // (undocumented) + VerticalDatumConvertError = 26 +} + +// @public +interface GeometricElement2dProps extends GeometricElementProps { + // (undocumented) + placement?: Placement2dProps; + // (undocumented) + typeDefinition?: RelatedElementProps; +} + +// @public +interface GeometricElement3dProps extends GeometricElementProps { + // (undocumented) + placement?: Placement3dProps; + // (undocumented) + typeDefinition?: RelatedElementProps; +} + +// @public +interface GeometricElementProps extends ElementProps { + category: Id64String; + // (undocumented) + geom?: GeometryStreamProps; +} + +// @public +interface GeometricModel2dProps extends ModelProps { + // (undocumented) + globalOrigin?: XYProps; +} + +// @public +interface GeometryAppearanceProps { + color?: ColorDefProps; + displayPriority?: number; + geometryClass?: GeometryClass; + style?: Id64String; + subCategory?: Id64String; + transparency?: number; + weight?: number; +} + +// @alpha +enum GeometryClass { + Construction = 1, + Dimension = 2, + Pattern = 3, + Primary = 0 +} + +// @public +class GeometryParams { + constructor(categoryId: Id64String, subCategoryId?: string); + backgroundFill?: BackgroundFill; + // (undocumented) + categoryId: Id64String; + // (undocumented) + clone(): GeometryParams; + elmPriority?: number; + elmTransparency?: number; + fillColor?: ColorDef; + fillDisplay?: FillDisplay; + fillTransparency?: number; + geometryClass?: GeometryClass; + // WARNING: The type "Gradient.Symb" needs to be exported by the package (e.g. added to index.ts) + gradient?: Gradient.Symb; + isEquivalent(other: GeometryParams): boolean; + lineColor?: ColorDef; + materialId?: Id64String; + // WARNING: The type "AreaPattern.Params" needs to be exported by the package (e.g. added to index.ts) + pattern?: AreaPattern.Params; + resetAppearance(): void; + setCategoryId(categoryId: Id64String, clearAppearanceOverrides?: boolean): void; + setSubCategoryId(subCategoryId: Id64String, clearAppearanceOverrides?: boolean): void; + // WARNING: The type "LineStyle.Info" needs to be exported by the package (e.g. added to index.ts) + styleInfo?: LineStyle.Info; + // (undocumented) + subCategoryId: string; + weight?: number; +} + +// @public +interface GeometryPartInstanceProps { + origin?: XYZProps; + part: Id64String; + rotation?: YawPitchRollProps; + scale?: number; +} + +// @public +interface GeometryPartProps extends ElementProps { + // (undocumented) + bbox?: LowAndHighXYZ; + // (undocumented) + geom?: GeometryStreamProps; +} + +// @public +class GeometryStreamBuilder { + // WARNING: The type "BRepEntity.DataProps" needs to be exported by the package (e.g. added to index.ts) + appendBRepData(brep: BRepEntity.DataProps): boolean; + appendGeometry(geometry: GeometryQuery): boolean; + appendGeometryParamsChange(geomParams: GeometryParams): boolean; + appendGeometryPart2d(partId: Id64String, instanceOrigin?: Point2d, instanceRotation?: Angle, instanceScale?: number): boolean; + appendGeometryPart3d(partId: Id64String, instanceOrigin?: Point3d, instanceRotation?: YawPitchRollAngles, instanceScale?: number): boolean; + appendGeometryRanges(): void; + appendSubCategoryChange(subCategoryId: Id64String): boolean; + appendTextString(textString: TextString): boolean; + readonly geometryStream: GeometryStreamProps; + setLocalToWorld(localToWorld?: Transform): void; + setLocalToWorld2d(origin: Point2d, angle?: Angle): void; + setLocalToWorld3d(origin: Point3d, angles?: YawPitchRollAngles): void; +} + +// @public +interface GeometryStreamEntryProps extends GeomJson.GeometryProps { + // (undocumented) + appearance?: GeometryAppearanceProps; + // (undocumented) + brep?: BRepEntity.DataProps; + // (undocumented) + fill?: AreaFillProps; + // (undocumented) + geomPart?: GeometryPartInstanceProps; + // (undocumented) + material?: MaterialProps; + // (undocumented) + pattern?: AreaPattern.ParamsProps; + // (undocumented) + styleMod?: LineStyle.ModifierProps; + // (undocumented) + subRange?: LowAndHighXYZ; + // (undocumented) + textString?: TextStringProps; +} + +// @public +class GeometryStreamIterator implements IterableIterator { + // WARNING: The name "__@iterator" contains unsupported characters; API names should use only letters, numbers, and underscores + // (undocumented) + [Symbol.iterator](): IterableIterator; + constructor(geometryStream: GeometryStreamProps, category?: Id64String); + entry: GeometryStreamIteratorEntry; + static fromGeometricElement2d(element: GeometricElement2dProps): GeometryStreamIterator; + static fromGeometricElement3d(element: GeometricElement3dProps): GeometryStreamIterator; + static fromGeometryPart(geomPart: GeometryPartProps, geomParams?: GeometryParams, partTransform?: Transform): GeometryStreamIterator; + geometryStream: GeometryStreamProps; + next(): IteratorResult; + partToWorld(): Transform | undefined; + setLocalToWorld(localToWorld?: Transform): void; + setLocalToWorld2d(origin: Point2d, angle?: Angle): void; + setLocalToWorld3d(origin: Point3d, angles?: YawPitchRollAngles): void; +} + +// @public +class GeometryStreamIteratorEntry { + constructor(category?: Id64String); + // WARNING: The type "BRepEntity.DataProps" needs to be exported by the package (e.g. added to index.ts) + brep?: BRepEntity.DataProps; + geometryQuery?: GeometryQuery; + geomParams: GeometryParams; + localRange?: Range3d; + localToWorld?: Transform; + partId?: Id64String; + partToLocal?: Transform; + textString?: TextString; +} + +// @beta (undocumented) +module Gradient { + enum Flags { + Invert = 1, + // (undocumented) + None = 0, + Outline = 2 + } + + class KeyColor implements KeyColorProps { + // WARNING: The type "KeyColorProps" needs to be exported by the package (e.g. added to index.ts) + constructor(json: KeyColorProps); + // (undocumented) + color: ColorDef; + // (undocumented) + value: number; + } + + interface KeyColorProps { + color: ColorDefProps; + value: number; + } + + enum Mode { + // (undocumented) + Curved = 2, + // (undocumented) + Cylindrical = 3, + // (undocumented) + Hemispherical = 5, + // (undocumented) + Linear = 1, + // (undocumented) + None = 0, + // (undocumented) + Spherical = 4, + // (undocumented) + Thematic = 6 + } + + class Symb implements SymbProps { + // (undocumented) + angle?: Angle; + // WARNING: The type "Symb" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + clone(): Symb; + // WARNING: The type "Symb" needs to be exported by the package (e.g. added to index.ts) + compare(other: Symb): number; + // WARNING: The type "Gradient.Symb" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Gradient.Symb" needs to be exported by the package (e.g. added to index.ts) + static compareSymb(lhs: Gradient.Symb, rhs: Gradient.Symb): number; + // WARNING: The type "ThematicSettings" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Symb" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + static createThematic(settings: ThematicSettings): Symb; + // WARNING: The type "Symb" needs to be exported by the package (e.g. added to index.ts) + equals(other: Symb): boolean; + // WARNING: The type "Flags" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + flags: Flags; + // WARNING: The type "SymbProps" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Symb" needs to be exported by the package (e.g. added to index.ts) + static fromJSON(json?: SymbProps): Symb; + getImage(width: number, height: number): ImageBuffer; + // (undocumented) + readonly hasTranslucency: boolean; + readonly isOutlined: boolean; + // WARNING: The type "KeyColor" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + keys: KeyColor[]; + // WARNING: The type "Mode" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + mode: Mode; + // (undocumented) + shift: number; + // WARNING: The type "ThematicSettings" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + thematicSettings?: ThematicSettings; + // (undocumented) + tint?: number; + } + + interface SymbProps { + angle?: AngleProps; + flags?: Flags; + keys: KeyColorProps[]; + mode: Mode; + shift?: number; + thematicSettings?: ThematicSettingsProps; + tint?: number; + } + + // (undocumented) + enum ThematicColorScheme { + // (undocumented) + BlueRed = 0, + // (undocumented) + Custom = 5, + // (undocumented) + Monochrome = 2, + // (undocumented) + RedBlue = 1, + // (undocumented) + SeaMountain = 4, + // (undocumented) + Topographic = 3 + } + + // (undocumented) + enum ThematicMode { + // (undocumented) + IsoLines = 3, + // (undocumented) + Smooth = 0, + // (undocumented) + Stepped = 1, + // (undocumented) + SteppedWithDelimiter = 2 + } + + class ThematicSettings implements ThematicSettingsProps { + // WARNING: The type "ThematicSettings" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "ThematicSettings" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + clone(out?: ThematicSettings): ThematicSettings; + // (undocumented) + colorScheme: number; + // (undocumented) + static readonly contentMax: number; + // (undocumented) + static readonly contentRange: number; + // WARNING: The type "ThematicSettingsProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + copyFrom(other: ThematicSettingsProps): void; + // WARNING: The type "ThematicSettings" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + static defaults: ThematicSettings; + // WARNING: The type "ThematicSettingsProps" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "ThematicSettings" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + static fromJSON(json: ThematicSettingsProps): ThematicSettings; + // (undocumented) + static readonly margin: number; + // (undocumented) + marginColor: ColorDef; + // WARNING: The type "ThematicMode" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + mode: ThematicMode; + // (undocumented) + range: Range1d; + // (undocumented) + rangeHigh: number; + // (undocumented) + rangeLow: number; + // (undocumented) + stepCount: number; + } + + // (undocumented) + interface ThematicSettingsProps { + // (undocumented) + colorScheme: number; + // (undocumented) + marginColor: ColorDefProps; + // (undocumented) + mode: ThematicMode; + // (undocumented) + rangeHigh: number; + // (undocumented) + rangeLow: number; + // (undocumented) + stepCount: number; + } + +} + +// @beta +class GraphicParams { + // (undocumented) + readonly fillColor: ColorDef; + // (undocumented) + fillFlags: FillFlags; + // (undocumented) + static fromBlankingFill(fillColor: ColorDef): GraphicParams; + // (undocumented) + static fromSymbology(lineColor: ColorDef, fillColor: ColorDef, lineWidth: number, linePixels?: LinePixels): GraphicParams; + // WARNING: The type "Gradient.Symb" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + gradient?: Gradient.Symb; + // (undocumented) + readonly lineColor: ColorDef; + // (undocumented) + linePixels: LinePixels; + // (undocumented) + lineTexture?: RenderTexture; + // (undocumented) + material?: RenderMaterial; + // (undocumented) + rasterWidth: number; + setFillColor(fillColor: ColorDef): void; + // (undocumented) + setFillTransparency(transparency: number): void; + setLineColor(lineColor: ColorDef): void; + setLinePixels(code: LinePixels): void; + // (undocumented) + setLineTransparency(transparency: number): void; + // (undocumented) + trueWidthEnd: number; + // (undocumented) + trueWidthStart: number; +} + +// @public +class GroundPlane implements GroundPlaneProps { + constructor(ground?: GroundPlaneProps); + aboveColor: ColorDef; + belowColor: ColorDef; + display: boolean; + elevation: number; + // WARNING: The type "Gradient.Symb" needs to be exported by the package (e.g. added to index.ts) + getGroundPlaneGradient(aboveGround: boolean): Gradient.Symb; + // (undocumented) + toJSON(): GroundPlaneProps; +} + +// @public +interface GroundPlaneProps { + aboveColor?: ColorDefProps; + belowColor?: ColorDefProps; + display?: boolean; + elevation?: number; +} + +// @beta +module HiddenLine { + class Settings { + // WARNING: The type "Settings" needs to be exported by the package (e.g. added to index.ts) + static defaults: Settings; + // WARNING: The type "SettingsProps" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Settings" needs to be exported by the package (e.g. added to index.ts) + static fromJSON(json?: SettingsProps): Settings; + // WARNING: The type "Style" needs to be exported by the package (e.g. added to index.ts) + readonly hidden: Style; + // WARNING: The type "SettingsProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + toJSON(): SettingsProps; + readonly transparencyThreshold: number; + // (undocumented) + readonly transThreshold: number; + // WARNING: The type "Style" needs to be exported by the package (e.g. added to index.ts) + readonly visible: Style; + } + + interface SettingsProps { + readonly hidden?: StyleProps; + readonly transThreshold?: number; + readonly visible?: StyleProps; + } + + class Style implements StyleProps { + readonly color?: ColorDef; + // WARNING: The type "Style" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + static defaults: Style; + // WARNING: The type "Style" needs to be exported by the package (e.g. added to index.ts) + equals(other: Style): boolean; + // WARNING: The type "StyleProps" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Style" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + static fromJSON(json?: StyleProps): Style; + // WARNING: The type "Style" needs to be exported by the package (e.g. added to index.ts) + overrideColor(color: ColorDef): Style; + // (undocumented) + readonly ovrColor: boolean; + readonly pattern?: LinePixels; + // WARNING: The type "StyleProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + toJSON(): StyleProps; + readonly width?: number; + } + + interface StyleProps { + readonly color?: ColorDefProps; + readonly ovrColor?: boolean; + readonly pattern?: LinePixels; + readonly width?: number; + } + +} + +// @public +module Hilite { + class Settings { + // WARNING: The type "Silhouette" needs to be exported by the package (e.g. added to index.ts) + constructor(color?: ColorDef, visibleRatio?: number, hiddenRatio?: number, silhouette?: Silhouette); + readonly color: ColorDef; + readonly hiddenRatio: number; + // WARNING: The type "Silhouette" needs to be exported by the package (e.g. added to index.ts) + silhouette: Silhouette; + readonly visibleRatio: number; + } + + enum Silhouette { + None = 0, + Thick = 2, + Thin = 1 + } + +} + +// @public +class HSLColor { + // (undocumented) + clone(): HSLColor; + // (undocumented) + static fromColorDef(val: ColorDef, out?: HSLColor): HSLColor; + // (undocumented) + h: number; + // (undocumented) + l: number; + // (undocumented) + s: number; + // (undocumented) + toColorDef(out?: ColorDef): ColorDef; +} + +// @public +class HSVColor { + // (undocumented) + adjustColor(darkenColor: boolean, delta: number): void; + // (undocumented) + clone(): HSVColor; + // (undocumented) + static fromColorDef(val: ColorDef, out?: HSVColor): HSVColor; + // (undocumented) + h: number; + // (undocumented) + s: number; + // (undocumented) + toColorDef(out?: ColorDef): ColorDef; + // (undocumented) + v: number; +} + +// @public +interface HttpServerRequest extends Readable { + // (undocumented) + body: string | Buffer; + // (undocumented) + connection: any; + // (undocumented) + destroy(error?: Error): void; + // (undocumented) + header: (field: string) => string | undefined; + // (undocumented) + headers: { + [header: string]: string | string[] | undefined; + } + // (undocumented) + httpVersion: string; + // (undocumented) + httpVersionMajor: number; + // (undocumented) + httpVersionMinor: number; + // (undocumented) + method: string; + // (undocumented) + path: string; + // (undocumented) + rawHeaders: string[]; + // (undocumented) + rawTrailers: string[]; + // (undocumented) + setTimeout(msecs: number, callback: () => void): this; + // (undocumented) + socket: any; + // (undocumented) + statusCode?: number; + // (undocumented) + statusMessage?: string; + // (undocumented) + trailers: { + [key: string]: string | undefined; + } + // (undocumented) + url?: string; +} + +// @public +interface HttpServerResponse extends Writable { + // (undocumented) + send(body?: any): HttpServerResponse; + // (undocumented) + set(field: string, value: string): void; + // (undocumented) + status(code: number): HttpServerResponse; +} + +// @public +class ImageBuffer { + protected constructor(data: Uint8Array, format: ImageBufferFormat, width: number); + // (undocumented) + protected static computeHeight(data: Uint8Array, format: ImageBufferFormat, width: number): number; + static create(data: Uint8Array, format: ImageBufferFormat, width: number): ImageBuffer | undefined; + readonly data: Uint8Array; + readonly format: ImageBufferFormat; + static getNumBytesPerPixel(format: ImageBufferFormat): number; + readonly height: number; + // (undocumented) + protected static isValidData(data: Uint8Array, format: ImageBufferFormat, width: number): boolean; + readonly numBytesPerPixel: number; + readonly width: number; +} + +// @public +enum ImageBufferFormat { + Alpha = 5, + Rgb = 2, + Rgba = 0 +} + +// @public (undocumented) +module ImageLight { + // (undocumented) + class Solar { + constructor(direction?: Vector3d, color?: ColorDef, intensity?: number); + // (undocumented) + color: ColorDef; + // (undocumented) + direction: Vector3d; + // (undocumented) + intensity: number; + } + +} + +// @public +class ImageSource { + constructor(data: Uint8Array, format: ImageSourceFormat); + readonly data: Uint8Array; + readonly format: ImageSourceFormat; +} + +// @public +enum ImageSourceFormat { + Jpeg = 0, + Png = 2 +} + +// @public +class IModel implements IModelProps { + protected constructor(iModelToken: IModelToken); + // (undocumented) + protected _token: IModelToken; + cartographicToSpatialFromEcef(cartographic: Cartographic, result?: Point3d): Point3d; + static readonly dictionaryId: Id64String; + readonly ecefLocation: EcefLocation | undefined; + ecefToSpatial(ecef: XYAndZ, result?: Point3d): Point3d; + static getDefaultSubCategoryId(categoryId: Id64String): Id64String; + getEcefTransform(): Transform; + readonly globalOrigin: Point3d; + readonly iModelToken: IModelToken; + // (undocumented) + protected initialize(name: string, props: IModelProps): void; + readonly isGeoLocated: boolean; + name: string; + projectExtents: AxisAlignedBox3d; + static readonly repositoryModelId: Id64String; + rootSubject: RootSubjectProps; + static readonly rootSubjectId: Id64String; + setEcefLocation(ecef: EcefLocationProps): void; + spatialToCartographicFromEcef(spatial: XYAndZ, result?: Cartographic): Cartographic; + spatialToEcef(spatial: XYAndZ, result?: Point3d): Point3d; + // (undocumented) + toJSON(): IModelProps; +} + +// @beta +interface IModelCoordinatesRequestProps { + // (undocumented) + geoCoords: XYZProps[]; + // (undocumented) + sourceDatum: string; +} + +// @beta (undocumented) +interface IModelCoordinatesResponseProps { + // (undocumented) + fromCache: number; + // (undocumented) + iModelCoords: PointWithStatus[]; +} + +// @public +class IModelError extends BentleyError { + constructor(errorNumber: number | IModelStatus | DbResult | BentleyStatus | BriefcaseStatus | RepositoryStatus | ChangeSetStatus | RpcInterfaceStatus | AuthStatus, message: string, log?: LogFunction, category?: string, getMetaData?: GetMetaDataFunction); +} + +// @public +class IModelNotFoundResponse extends RpcNotFoundResponse { +} + +// @public +interface IModelProps { + ecefLocation?: EcefLocationProps; + globalOrigin?: XYZProps; + projectExtents?: Range3dProps; + rootSubject: RootSubjectProps; +} + +// @public +class IModelReadRpcInterface extends RpcInterface { + // (undocumented) + cancelSnap(_iModelToken: IModelToken, _sessionId: string): Promise; + // (undocumented) + close(_accessToken: AccessToken, _iModelToken: IModelToken): Promise; + // (undocumented) + executeQuery(_iModelToken: IModelToken, _ecsql: string, _bindings?: any[] | object): Promise; + // (undocumented) + getAllCodeSpecs(_iModelToken: IModelToken): Promise; + // (undocumented) + getClassHierarchy(_iModelToken: IModelToken, _startClassName: string): Promise; + static getClient(): IModelReadRpcInterface; + // (undocumented) + getDefaultViewId(_iModelToken: IModelToken): Promise; + // (undocumented) + getElementProps(_iModelToken: IModelToken, _elementIds: Id64Set): Promise; + // (undocumented) + getGeoCoordinatesFromIModelCoordinates(_iModelToken: IModelToken, _props: string): Promise; + // (undocumented) + getIModelCoordinatesFromGeoCoordinates(_iModelToken: IModelToken, _props: string): Promise; + // (undocumented) + getModelProps(_iModelToken: IModelToken, _modelIds: Id64Set): Promise; + // (undocumented) + getToolTipMessage(_iModelToken: IModelToken, _elementId: string): Promise; + // (undocumented) + getViewStateData(_iModelToken: IModelToken, _viewDefinitionId: string): Promise; + // (undocumented) + getViewThumbnail(_iModelToken: IModelToken, _viewId: string): Promise; + // (undocumented) + openForRead(_accessToken: AccessToken, _iModelToken: IModelToken): Promise; + // (undocumented) + queryElementProps(_iModelToken: IModelToken, _params: EntityQueryParams): Promise; + // (undocumented) + queryEntityIds(_iModelToken: IModelToken, _params: EntityQueryParams): Promise; + // (undocumented) + queryModelProps(_iModelToken: IModelToken, _params: EntityQueryParams): Promise; + // (undocumented) + readFontJson(_iModelToken: IModelToken): Promise; + // (undocumented) + requestSnap(_iModelToken: IModelToken, _sessionId: string, _props: SnapRequestProps): Promise; + static types: () => (typeof Point3d | typeof Vector3d | typeof Point2d | typeof IModelToken | typeof Code | typeof IModelNotFoundResponse | typeof AccessToken | typeof Vector2d)[]; + static version: string; +} + +// @public +enum IModelStatus { + // (undocumented) + AlreadyLoaded = 65537, + // (undocumented) + AlreadyOpen = 65538, + // (undocumented) + BadArg = 65539, + // (undocumented) + BadElement = 65540, + // (undocumented) + BadModel = 65541, + // (undocumented) + BadRequest = 65542, + // (undocumented) + BadSchema = 65543, + // (undocumented) + CannotUndo = 65544, + // (undocumented) + CodeNotReserved = 65545, + // (undocumented) + ConstraintNotUnique = 65601, + // (undocumented) + DeletionProhibited = 65546, + // (undocumented) + DuplicateCode = 65547, + // (undocumented) + DuplicateName = 65548, + // (undocumented) + ElementBlockedChange = 65549, + // (undocumented) + FileAlreadyExists = 65550, + // (undocumented) + FileNotFound = 65551, + // (undocumented) + FileNotLoaded = 65552, + // (undocumented) + ForeignKeyConstraint = 65553, + // (undocumented) + IdExists = 65554, + // (undocumented) + IMODEL_ERROR_BASE = 65536, + // (undocumented) + InDynamicTransaction = 65555, + // (undocumented) + InvalidCategory = 65556, + // (undocumented) + InvalidCode = 65557, + // (undocumented) + InvalidCodeSpec = 65558, + // (undocumented) + InvalidId = 65559, + // (undocumented) + InvalidName = 65560, + // (undocumented) + InvalidParent = 65561, + // (undocumented) + InvalidProfileVersion = 65562, + // (undocumented) + IsCreatingChangeSet = 65563, + // (undocumented) + LockNotHeld = 65564, + // (undocumented) + Mismatch2d3d = 65565, + // (undocumented) + MismatchGcs = 65566, + // (undocumented) + MissingDomain = 65567, + // (undocumented) + MissingHandler = 65568, + // (undocumented) + MissingId = 65569, + // (undocumented) + NoGeoLocation = 65602, + // (undocumented) + NoGeometry = 65570, + // (undocumented) + NoMultiTxnOperation = 65571, + // (undocumented) + NotDgnMarkupProject = 65572, + // (undocumented) + NotEnabled = 65573, + // (undocumented) + NotFound = 65574, + // (undocumented) + NothingToRedo = 65578, + // (undocumented) + NothingToUndo = 65579, + // (undocumented) + NotOpen = 65575, + // (undocumented) + NotOpenForWrite = 65576, + // (undocumented) + NotSameUnitBase = 65577, + // (undocumented) + ParentBlockedChange = 65580, + // (undocumented) + ReadError = 65581, + // (undocumented) + ReadOnly = 65582, + // (undocumented) + ReadOnlyDomain = 65583, + // (undocumented) + RepositoryManagerError = 65584, + // (undocumented) + SQLiteError = 65585, + // (undocumented) + Success = 0, + // (undocumented) + TransactionActive = 65586, + // (undocumented) + UnitsMissing = 65587, + // (undocumented) + UnknownFormat = 65588, + // (undocumented) + UpgradeFailed = 65589, + // (undocumented) + ValidationFailed = 65590, + // (undocumented) + VersionTooNew = 65591, + // (undocumented) + VersionTooOld = 65592, + // (undocumented) + ViewNotFound = 65593, + // (undocumented) + WriteError = 65594, + // (undocumented) + WrongClass = 65595, + // (undocumented) + WrongDomain = 65597, + // (undocumented) + WrongElement = 65598, + // (undocumented) + WrongHandler = 65599, + // (undocumented) + WrongIModel = 65596, + // (undocumented) + WrongModel = 65600 +} + +// @public (undocumented) +class IModelTileRpcInterface extends RpcInterface { + // (undocumented) + static getClient(): IModelTileRpcInterface; + // (undocumented) + getTileContent(_iModelToken: IModelToken, _treeId: string, _contentId: string): Promise; + // (undocumented) + getTileTreeProps(_iModelToken: IModelToken, _id: string): Promise; + // (undocumented) + static types: () => (typeof IModelToken)[]; + static version: string; +} + +// @public +class IModelToken { + constructor( + key?: string | undefined, + contextId?: string | undefined, + iModelId?: string | undefined, + changeSetId?: string | undefined, + openMode?: OpenMode | undefined); + changeSetId?: string | undefined; + readonly contextId?: string | undefined; + readonly iModelId?: string | undefined; + readonly key?: string | undefined; + openMode?: OpenMode | undefined; +} + +// @public +class IModelVersion { + static asOfChangeSet(changeSetId: string): IModelVersion; + evaluateChangeSet(alctx: ActivityLoggingContext, accessToken: AccessToken, iModelId: string, imodelClient: IModelClient): Promise; + static first(): IModelVersion; + static fromJson(jsonObj: any): IModelVersion; + getAsOfChangeSet(): string | undefined; + getName(): string | undefined; + readonly isFirst: boolean; + readonly isLatest: boolean; + static latest(): IModelVersion; + static named(versionName: string): IModelVersion; +} + +// @alpha +class IModelWriteRpcInterface extends RpcInterface { + static getClient(): IModelWriteRpcInterface; + // (undocumented) + openForWrite(_accessToken: AccessToken, _iModelToken: IModelToken): Promise; + // (undocumented) + saveChanges(_iModelToken: IModelToken, _description?: string): Promise; + // (undocumented) + saveThumbnail(_iModelToken: IModelToken, _val: Uint8Array): Promise; + static types: () => (typeof Point3d | typeof Range3d | typeof IModelToken | typeof IModelNotFoundResponse | typeof AccessToken)[]; + // (undocumented) + updateProjectExtents(_iModelToken: IModelToken, _newExtents: AxisAlignedBox3d): Promise; + static version: string; +} + +// @public +interface InformationPartitionElementProps extends DefinitionElementProps { + // (undocumented) + description?: string; +} + +// @public +export function isPowerOfTwo(num: number): boolean; + +// @public (undocumented) +export function isValidImageSourceFormat(format: ImageSourceFormat): boolean; + +// @public (undocumented) +interface LatAndLong { + // (undocumented) + latitude: number; + // (undocumented) + longitude: number; +} + +// @public (undocumented) +interface LatLongAndHeight extends LatAndLong { + // (undocumented) + height: number; +} + +// @public +class Light { + constructor(opts?: LightProps); + // (undocumented) + bulbs: number; + // (undocumented) + color: ColorDef; + // (undocumented) + color2?: ColorDef; + // (undocumented) + intensity: number; + // (undocumented) + intensity2?: number; + // (undocumented) + readonly isValid: boolean; + // (undocumented) + readonly isVisible: boolean; + // (undocumented) + kelvin: number; + // (undocumented) + lightType: LightType; + // (undocumented) + lumens: number; + // (undocumented) + shadows: number; +} + +// @beta +interface LightLocationProps extends GeometricElement3dProps { + // (undocumented) + enabled?: boolean; +} + +// @public +interface LightProps { + // (undocumented) + bulbs?: number; + // (undocumented) + color?: ColorDefProps; + // (undocumented) + color2?: ColorDefProps; + // (undocumented) + intensity?: number; + // (undocumented) + intensity2?: number; + // (undocumented) + kelvin?: number; + // (undocumented) + lightType?: LightType; + // (undocumented) + lumens?: number; + // (undocumented) + shadows?: number; +} + +// @public +enum LightType { + // (undocumented) + Ambient = 2, + // (undocumented) + Area = 7, + // (undocumented) + Distant = 8, + // (undocumented) + Flash = 3, + // (undocumented) + Invalid = 0, + // (undocumented) + Point = 5, + // (undocumented) + Portrait = 4, + // (undocumented) + SkyOpening = 9, + // (undocumented) + Solar = 1, + // (undocumented) + Spot = 6 +} + +// @public +enum LinePixels { + // (undocumented) + Code0 = 0, + // (undocumented) + Code1 = 2155905152, + // (undocumented) + Code2 = 4177066232, + // (undocumented) + Code3 = 4292935648, + // (undocumented) + Code4 = 4262526480, + // (undocumented) + Code5 = 3772834016, + // (undocumented) + Code6 = 4169726088, + // (undocumented) + Code7 = 4279828248, + // (undocumented) + HiddenLine = 3435973836, + // (undocumented) + Invalid = -1, + // (undocumented) + Invisible = 1, + // (undocumented) + Solid = 0 +} + +// @public (undocumented) +module LineStyle { + class Info { + // WARNING: The type "Modifier" needs to be exported by the package (e.g. added to index.ts) + constructor(styleId: Id64String, styleMod?: Modifier); + // WARNING: The type "Info" needs to be exported by the package (e.g. added to index.ts) + clone(): Info; + // WARNING: The type "Info" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + equals(other: Info): boolean; + // (undocumented) + styleId: Id64String; + // WARNING: The type "Modifier" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + styleMod?: Modifier; + } + + class Modifier implements ModifierProps { + // WARNING: The type "ModifierProps" needs to be exported by the package (e.g. added to index.ts) + constructor(props: ModifierProps); + // (undocumented) + applyTransform(transform: Transform): boolean; + // (undocumented) + centerPhase?: boolean; + // WARNING: The type "Modifier" needs to be exported by the package (e.g. added to index.ts) + clone(): Modifier; + // (undocumented) + dashScale?: number; + // (undocumented) + distPhase?: number; + // (undocumented) + endWidth?: number; + // WARNING: The type "Modifier" needs to be exported by the package (e.g. added to index.ts) + equals(other: Modifier): boolean; + // (undocumented) + fractPhase?: number; + // (undocumented) + gapScale?: number; + // (undocumented) + normal?: Vector3d; + // (undocumented) + physicalWidth?: boolean; + // (undocumented) + rotation?: YawPitchRollAngles; + // (undocumented) + scale?: number; + // (undocumented) + segmentMode?: boolean; + // (undocumented) + startWidth?: number; + } + + interface ModifierProps { + centerPhase?: boolean; + dashScale?: number; + distPhase?: number; + endWidth?: number; + fractPhase?: number; + gapScale?: number; + normal?: XYZProps; + physicalWidth?: boolean; + rotation?: YawPitchRollProps; + scale?: number; + segmentMode?: boolean; + startWidth?: number; + } + +} + +// @beta +interface LineStyleProps extends ElementProps { + // (undocumented) + data: string; + // (undocumented) + description?: string; +} + +// @public (undocumented) +module MarshalingBinaryMarker { + // (undocumented) + function createDefault(): MarshalingBinaryMarker; + +} + +// @public +interface MaterialProps { + materialId?: Id64String; + // (undocumented) + origin?: XYZProps; + // (undocumented) + rotation?: YawPitchRollProps; + // (undocumented) + size?: XYZProps; +} + +// @public (undocumented) +class MeshEdge { + constructor(index0?: number, index1?: number); + // (undocumented) + indices: number[]; +} + +// @public (undocumented) +class MeshEdges { + constructor(); + // (undocumented) + polylines: MeshPolylineList; + // (undocumented) + silhouette: MeshEdge[]; + // (undocumented) + silhouetteNormals: OctEncodedNormalPair[]; + // (undocumented) + visible: MeshEdge[]; +} + +// @public (undocumented) +class MeshPolyline { + constructor(indices?: number[]); + // (undocumented) + addIndex(index: number): void; + // (undocumented) + clear(): void; + // (undocumented) + readonly indices: number[]; +} + +// @public (undocumented) +class MeshPolylineList extends Array { + constructor(...args: MeshPolyline[]); +} + +// @public +class MobileRpcConfiguration extends RpcConfiguration { + static readonly isIOSFrontend: any; + static readonly isMobileBackend: boolean; + static readonly isMobileFrontend: boolean; + static readonly platform: RpcMobilePlatform; + // WARNING: The type "MobileRpcProtocol" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protocol: MobileRpcProtocol; +} + +// @public +class MobileRpcManager { + static initializeClient(interfaces: RpcInterfaceDefinition[]): MobileRpcConfiguration; + static initializeImpl(interfaces: RpcInterfaceDefinition[]): MobileRpcConfiguration; +} + +// @public +interface ModelProps extends EntityProps { + // (undocumented) + isPrivate?: boolean; + // (undocumented) + isTemplate?: boolean; + // (undocumented) + jsonProperties?: any; + // (undocumented) + modeledElement: RelatedElementProps; + // (undocumented) + name?: string; + // (undocumented) + parentModel?: Id64String; +} + +// @alpha +interface ModelQueryParams extends EntityQueryParams { + // (undocumented) + wantPrivate?: boolean; + // (undocumented) + wantTemplate?: boolean; +} + +// @public +interface ModelSelectorProps extends DefinitionElementProps { + // (undocumented) + models: Id64Array; +} + +// @public +interface NavigationBindingValue { + id: Id64String; + relClassName?: string; + relClassTableSpace?: string; +} + +// @public +interface NavigationValue { + id: Id64String; + relClassName?: string; +} + +// @public +export function nextHighestPowerOfTwo(num: number): number; + +// @public (undocumented) +class NonUniformColor { + constructor(colors: Uint32Array, indices: Uint16Array, hasAlpha: boolean); + // (undocumented) + readonly colors: Uint32Array; + // (undocumented) + readonly indices: Uint16Array; + // (undocumented) + readonly isOpaque: boolean; +} + +// @public +enum Npc { + _000 = 0, + _001 = 4, + _010 = 2, + _011 = 6, + _100 = 1, + _101 = 5, + _110 = 3, + _111 = 7, + CORNER_COUNT = 8, + // (undocumented) + LeftBottomFront = 4, + // (undocumented) + LeftBottomRear = 0, + // (undocumented) + LeftTopFront = 6, + // (undocumented) + LeftTopRear = 2, + // (undocumented) + RightBottomFront = 5, + // (undocumented) + RightBottomRear = 1, + // (undocumented) + RightTopFront = 7, + // (undocumented) + RightTopRear = 3 +} + +// @public (undocumented) +class OctEncodedNormal { + constructor(val: number); + // (undocumented) + decode(): Vector3d | undefined; + // (undocumented) + static fromVector(val: XYAndZ): OctEncodedNormal; + // (undocumented) + readonly value: number; +} + +// @public (undocumented) +class OctEncodedNormalPair { + constructor(first: OctEncodedNormal, second: OctEncodedNormal); + // (undocumented) + first: OctEncodedNormal; + // (undocumented) + second: OctEncodedNormal; +} + +// @public +class Placement2d implements Placement2dProps { + constructor(origin: Point2d, angle: Angle, bbox: ElementAlignedBox2d); + // (undocumented) + angle: Angle; + // (undocumented) + bbox: ElementAlignedBox2d; + calculateRange(): AxisAlignedBox3d; + static fromJSON(json?: Placement2dProps): Placement2d; + getWorldCorners(out?: Frustum): Frustum; + readonly isValid: boolean; + // (undocumented) + origin: Point2d; + readonly rotation: Matrix3d; + setFrom(other: Placement2d): void; + readonly transform: Transform; +} + +// @public +interface Placement2dProps { + // (undocumented) + angle: AngleProps; + // (undocumented) + bbox?: LowAndHighXY; + // (undocumented) + origin: XYProps; +} + +// @public +class Placement3d implements Placement3dProps { + constructor(origin: Point3d, angles: YawPitchRollAngles, bbox: ElementAlignedBox3d); + // (undocumented) + angles: YawPitchRollAngles; + // (undocumented) + bbox: ElementAlignedBox3d; + calculateRange(): AxisAlignedBox3d; + static fromJSON(json?: Placement3dProps): Placement3d; + getWorldCorners(out?: Frustum): Frustum; + readonly isValid: boolean; + // (undocumented) + origin: Point3d; + readonly rotation: Matrix3d; + setFrom(other: Placement3d): void; + readonly transform: Transform; +} + +// @public +interface Placement3dProps { + // (undocumented) + angles: YawPitchRollProps; + // (undocumented) + bbox?: LowAndHighXYZ; + // (undocumented) + origin: XYZProps; +} + +// @beta +interface PointWithStatus { + // (undocumented) + p: XYZProps; + // (undocumented) + s: GeoCoordStatus; +} + +// @public (undocumented) +class PolylineData { + constructor(vertIndices?: number[], numIndices?: number); + // (undocumented) + init(polyline: MeshPolyline): boolean; + // (undocumented) + readonly isValid: boolean; + // (undocumented) + numIndices: number; + // (undocumented) + reset(): void; + // (undocumented) + vertIndices: number[]; +} + +// @public (undocumented) +class PolylineEdgeArgs { + constructor(lines?: PolylineData[]); + // (undocumented) + clear(): void; + // (undocumented) + init(lines?: PolylineData[]): boolean; + // (undocumented) + readonly isValid: boolean; + // (undocumented) + lines?: PolylineData[]; + // (undocumented) + readonly numLines: number; +} + +// @public +class PolylineFlags { + constructor(is2d?: boolean, isPlanar?: boolean, isDisjoint?: boolean, type?: PolylineTypeFlags); + // (undocumented) + clone(): PolylineFlags; + // (undocumented) + equals(other: PolylineFlags): boolean; + // (undocumented) + initDefaults(): void; + // (undocumented) + is2d: boolean; + // (undocumented) + readonly isAnyEdge: boolean; + // (undocumented) + isDisjoint: boolean; + // (undocumented) + readonly isNormalEdge: boolean; + // (undocumented) + readonly isOutlineEdge: boolean; + // (undocumented) + isPlanar: boolean; + pack(): number; + // (undocumented) + setIsNormalEdge(): void; + // (undocumented) + setIsOutlineEdge(): void; + // (undocumented) + type: PolylineTypeFlags; + static unpack(value: number): PolylineFlags; +} + +// @public (undocumented) +enum PolylineTypeFlags { + // (undocumented) + Edge = 1, + // (undocumented) + Normal = 0, + // (undocumented) + Outline = 2 +} + +// @beta +enum PrimitiveTypeCode { + // (undocumented) + Binary = 257, + // (undocumented) + Boolean = 513, + // (undocumented) + DateTime = 769, + // (undocumented) + Double = 1025, + // (undocumented) + Integer = 1281, + // (undocumented) + Long = 1537, + // (undocumented) + Point2d = 1793, + // (undocumented) + Point3d = 2049, + // (undocumented) + String = 2305, + // (undocumented) + Uninitialized = 0 +} + +// @beta +class PropertyMetaData implements PropertyMetaDataProps { + constructor(jsonObj: PropertyMetaDataProps); + createProperty(jsonObj: any): any; + // (undocumented) + customAttributes?: CustomAttribute[]; + // (undocumented) + description?: string; + // (undocumented) + direction?: string; + // (undocumented) + displayLabel?: string; + // (undocumented) + extendedType?: string; + // (undocumented) + isCustomHandled?: boolean; + // (undocumented) + isCustomHandledOrphan?: boolean; + // (undocumented) + kindOfQuantity?: string; + // (undocumented) + maximumLength?: number; + // (undocumented) + maximumValue?: any; + // (undocumented) + maxOccurs?: number; + // (undocumented) + minimumLength?: number; + // (undocumented) + minimumValue?: any; + // (undocumented) + minOccurs?: number; + // (undocumented) + primitiveType?: PrimitiveTypeCode; + // (undocumented) + readOnly?: boolean; + // (undocumented) + relationshipClass?: string; + // (undocumented) + structName?: string; +} + +// @beta (undocumented) +interface PropertyMetaDataProps { + // (undocumented) + customAttributes?: CustomAttribute[]; + // (undocumented) + description?: string; + // (undocumented) + direction?: string; + // (undocumented) + displayLabel?: string; + // (undocumented) + extendedType?: string; + // (undocumented) + isCustomHandled?: boolean; + // (undocumented) + isCustomHandledOrphan?: boolean; + // (undocumented) + kindOfQuantity?: string; + // (undocumented) + maximumLength?: number; + // (undocumented) + maximumValue?: any; + // (undocumented) + maxOccurs?: number; + // (undocumented) + minimumLength?: number; + // (undocumented) + minimumValue?: any; + // (undocumented) + minOccurs?: number; + // (undocumented) + primitiveType?: number; + // (undocumented) + readOnly?: boolean; + // (undocumented) + relationshipClass?: string; + // (undocumented) + structName?: string; +} + +// @public +class QParams2d { + // (undocumented) + clone(out?: QParams2d): QParams2d; + // (undocumented) + copyFrom(src: QParams2d): void; + static fromNormalizedRange(): QParams2d; + static fromRange(range: Range2d, out?: QParams2d): QParams2d; + static fromZeroToOne(): QParams2d; + // (undocumented) + readonly origin: Point2d; + // (undocumented) + readonly scale: Point2d; + setFromRange(range: Range2d): void; +} + +// @public +class QParams3d { + // (undocumented) + clone(out?: QParams3d): QParams3d; + // (undocumented) + copyFrom(src: QParams3d): void; + static fromNormalizedRange(): QParams3d; + static fromOriginAndScale(origin: Point3d, scale: Point3d, out?: QParams3d): QParams3d; + static fromRange(range: Range3d, out?: QParams3d): QParams3d; + static fromZeroToOne(): QParams3d; + // (undocumented) + readonly origin: Point3d; + // (undocumented) + readonly scale: Point3d; + setFromOriginAndScale(origin: Point3d, scale: Point3d): void; + setFromRange(range: Range3d): void; +} + +// @public +class QPoint2d { + constructor(); + // (undocumented) + clone(out?: QPoint2d): QPoint2d; + // (undocumented) + copyFrom(src: QPoint2d): void; + static create(pos: Point2d, params: QParams2d): QPoint2d; + static fromScalars(x: number, y: number): QPoint2d; + init(pos: Point2d, params: QParams2d): void; + setFromScalars(x: number, y: number): void; + unquantize(params: QParams2d, out?: Point2d): Point2d; + // (undocumented) + x: number; + // (undocumented) + y: number; +} + +// @public +class QPoint2dList { + constructor(params: QParams2d); + add(pt: Point2d): void; + clear(): void; + static fromPoints(points: Point2d[], out?: QPoint2dList): QPoint2dList; + readonly length: number; + // (undocumented) + readonly params: QParams2d; + push(qpt: QPoint2d): void; + requantize(params: QParams2d): void; + reset(params: QParams2d): void; + toTypedArray(): Uint16Array; + unquantize(index: number, out?: Point2d): Point2d; +} + +// @public +class QPoint3d { + // (undocumented) + clone(out?: QPoint3d): QPoint3d; + // (undocumented) + compare(rhs: QPoint3d): number; + // (undocumented) + copyFrom(src: QPoint3d): void; + static create(pos: Point3d, params: QParams3d): QPoint3d; + // (undocumented) + equals(other: QPoint3d): boolean; + static fromScalars(x: number, y: number, z: number, out?: QPoint3d): QPoint3d; + init(pos: Point3d, params: QParams3d): void; + setFromScalars(x: number, y: number, z: number): void; + unquantize(params: QParams3d, out?: Point3d): Point3d; + // (undocumented) + x: number; + // (undocumented) + y: number; + // (undocumented) + z: number; +} + +// @public +class QPoint3dList { + // WARNING: The name "__@iterator" contains unsupported characters; API names should use only letters, numbers, and underscores + // (undocumented) + __@iterator: { + next: () => IteratorResult; + } + constructor(paramsIn?: QParams3d); + add(pt: Point3d): void; + clear(): void; + // (undocumented) + static createFrom(points: Point3d[], params: QParams3d): QPoint3dList; + static fromPoints(points: Point3d[], out?: QPoint3dList): QPoint3dList; + readonly length: number; + // (undocumented) + readonly list: QPoint3d[]; + // (undocumented) + readonly params: QParams3d; + push(qpt: QPoint3d): void; + requantize(params: QParams3d): void; + reset(params: QParams3d): void; + toTypedArray(): Uint16Array; + unquantize(index: number, out?: Point3d): Point3d; +} + +// @public +module Quantization { + // (undocumented) + function computeScale(extent: number): number; + + // (undocumented) + function isInRange(qpos: number): boolean; + + // (undocumented) + function isQuantizable(pos: number, origin: number, scale: number): boolean; + + // (undocumented) + function isQuantized(qpos: number): boolean; + + // (undocumented) + function quantize(pos: number, origin: number, scale: number): number; + + // (undocumented) + function unquantize(qpos: number, origin: number, scale: number): number; + +} + +// @public +enum Rank { + Application = 2, + Domain = 1, + System = 0, + User = 3 +} + +// @public (undocumented) +interface ReadableFormData extends Readable { + // (undocumented) + getHeaders: { + [key: string]: any; + } +} + +// @public +class RelatedElement implements RelatedElementProps { + constructor(props: RelatedElementProps); + // (undocumented) + static fromJSON(json?: RelatedElementProps): RelatedElement | undefined; + readonly id: Id64String; + static idFromJson(json: any): Id64String; + readonly relClassName?: string; +} + +// @public +interface RelatedElementProps { + id: Id64String; + relClassName?: string; +} + +// @beta +class RenderMaterial { +} + +// @beta +interface RenderMaterialProps extends DefinitionElementProps { + description?: string; + // (undocumented) + jsonProperties?: { + materialAssets?: { + renderMaterial?: { + color?: RgbFactorProps; + diffuse?: number; + finish?: number; + HasBaseColor?: boolean; + HasDiffuse?: boolean; + HasFinish?: boolean; + HasReflect?: boolean; + HasReflectColor?: boolean; + HasSpecular?: boolean; + HasSpecularColor?: boolean; + HasTransmit?: boolean; + Map?: { + Pattern?: TextureMapProps; + } + reflect?: number; + reflect_color?: RgbFactorProps; + specular?: number; + specular_color?: RgbFactorProps; + transmit?: number; + } + } + } + paletteName: string; +} + +// @public +enum RenderMode { + HiddenLine = 3, + SmoothShade = 6, + SolidFill = 4, + Wireframe = 0 +} + +// @beta +module RenderSchedule { + // (undocumented) + interface ColorEntryProps extends TimelineEntryProps { + // (undocumented) + value: { + blue: number; + green: number; + red: number; + } + } + + // (undocumented) + interface CuttingPlaneEntryProps extends TimelineEntryProps { + // (undocumented) + value: CuttingPlaneProps; + } + + // (undocumented) + interface CuttingPlaneProps { + // (undocumented) + direction: number[]; + // (undocumented) + hidden?: boolean; + // (undocumented) + position: number[]; + // (undocumented) + visible?: boolean; + } + + // (undocumented) + interface ElementTimelineProps { + // (undocumented) + batchId: number; + // (undocumented) + colorTimeline?: ColorEntryProps[]; + // (undocumented) + cuttingPlaneTimeline?: CuttingPlaneEntryProps[]; + // (undocumented) + elementIds: Id64String[]; + // (undocumented) + transformTimeline?: TransformEntryProps[]; + // (undocumented) + visibilityTimeline?: VisibilityEntryProps[]; + } + + // (undocumented) + interface ModelTimelineProps { + // (undocumented) + elementTimelines: ElementTimelineProps[]; + // (undocumented) + modelId: Id64String; + } + + // (undocumented) + interface TimelineEntryProps { + // (undocumented) + interpolation: number; + // (undocumented) + time: number; + } + + // (undocumented) + interface TransformEntryProps extends TimelineEntryProps { + // (undocumented) + value: TransformProps; + } + + // (undocumented) + interface TransformProps { + // (undocumented) + orientation: number[]; + // (undocumented) + pivot: number[]; + // (undocumented) + position: number[]; + // (undocumented) + transform: number[][]; + } + + // (undocumented) + interface VisibilityEntryProps extends TimelineEntryProps { + // (undocumented) + value: number; + } + +} + +// @beta +class RenderTexture { +} + +// @beta +enum RepositoryStatus { + CannotCreateChangeSet = 86023, + ChangeSetRequired = 86025, + CodeNotReserved = 86027, + CodeUnavailable = 86026, + CodeUsed = 86028, + InvalidRequest = 86024, + InvalidResponse = 86020, + LockAlreadyHeld = 86018, + LockNotHeld = 86029, + LockUsed = 86022, + PendingTransactions = 86021, + RepositoryIsLocked = 86030, + ServerUnavailable = 86017, + // (undocumented) + Success = 0, + SyncError = 86019 +} + +// @public +class RgbColor { + constructor(r: number, g: number, b: number); + // (undocumented) + readonly b: number; + // (undocumented) + equals(other: RgbColor): boolean; + static fromColorDef(colorDef: ColorDef): RgbColor; + // (undocumented) + readonly g: number; + // (undocumented) + readonly r: number; +} + +// @alpha +interface RootSubjectProps { + description?: string; + name: string; +} + +// @public +class RpcConfiguration { + applicationAuthorizationKey: string; + applicationAuthorizationValue: string; + applicationVersionKey: string; + static applicationVersionValue: string; + static assign(definition: RpcInterfaceDefinition, supplier: RpcConfigurationSupplier): void; + readonly controlChannel: RpcControlChannel; + static developmentMode: boolean; + static initializeInterfaces(configuration: RpcConfiguration): void; + readonly interfaces: () => RpcInterfaceDefinition[]; + static obtain(configurationConstructor: { + new (): T; + }): T; + // (undocumented) + onRpcClientInitialized(definition: RpcInterfaceDefinition, client: RpcInterface): void; + // (undocumented) + onRpcClientTerminated(definition: RpcInterfaceDefinition, client: RpcInterface): void; + // (undocumented) + onRpcImplInitialized(definition: RpcInterfaceDefinition, impl: RpcInterface): void; + // (undocumented) + onRpcImplTerminated(definition: RpcInterfaceDefinition, impl: RpcInterface): void; + pendingOperationRetryInterval: number; + readonly protocol: RpcProtocol; + static strictMode: boolean; + // (undocumented) + static supply(definition: RpcInterface): RpcConfiguration; +} + +// @public +enum RpcContentType { + // (undocumented) + Binary = 2, + // (undocumented) + Multipart = 3, + // (undocumented) + Text = 1, + // (undocumented) + Unknown = 0 +} + +// @public +class RpcControlChannel { + // (undocumented) + static channels: RpcControlChannel[]; + // (undocumented) + describeEndpoints(): Promise; + // (undocumented) + handleUnknownOperation(invocation: RpcInvocation, _error: any): boolean; + // (undocumented) + initialize(): void; + // (undocumented) + static obtain(configuration: RpcConfiguration): RpcControlChannel; +} + +// @public +class RpcControlResponse { +} + +// @public (undocumented) +class RpcDefaultConfiguration extends RpcConfiguration { + // (undocumented) + applicationAuthorizationKey: string; + // (undocumented) + applicationAuthorizationValue: string; + // (undocumented) + interfaces: () => never[]; + // (undocumented) + protocol: RpcProtocol; +} + +// @public (undocumented) +class RpcDirectProtocol extends RpcProtocol { + // (undocumented) + readonly requestType: typeof RpcDirectRequest; +} + +// @public (undocumented) +class RpcDirectRequest extends RpcRequest { + // (undocumented) + fulfillment: RpcRequestFulfillment | undefined; + // (undocumented) + headers: Map; + // (undocumented) + protected load(): Promise; + // (undocumented) + protected send(): Promise; + // (undocumented) + protected setHeader(name: string, value: string): void; +} + +// @public +enum RpcEndpoint { + // (undocumented) + Backend = 1, + // (undocumented) + Frontend = 0 +} + +// @public +class RpcInterface { + readonly configuration: RpcConfiguration; + // (undocumented) + configurationSupplier: RpcConfigurationSupplier | undefined; + forward(parameters: IArguments): Promise; + static isVersionCompatible(backend: string, frontend: string): boolean; +} + +// @public (undocumented) +interface RpcInterfaceDefinition { + // (undocumented) + name: string; + // (undocumented) + prototype: T; + // (undocumented) + types: () => Function[]; + // (undocumented) + version: string; +} + +// @public +interface RpcInterfaceEndpoints { + // (undocumented) + compatible: boolean; + // (undocumented) + interfaceName: string; + // (undocumented) + interfaceVersion: string; + // (undocumented) + operationNames: string[]; +} + +// @beta +enum RpcInterfaceStatus { + IncompatibleVersion = 135168, + // (undocumented) + RPC_INTERFACE_ERROR_BASE = 135168, + // (undocumented) + Success = 0 +} + +// @public +class RpcInvocation { + constructor(protocol: RpcProtocol, request: SerializedRpcRequest); + static current(rpcImpl: RpcInterface): RpcInvocation; + readonly elapsed: number; + readonly fulfillment: Promise; + readonly operation: RpcOperation; + readonly protocol: RpcProtocol; + readonly request: SerializedRpcRequest; + readonly result: Promise; + readonly status: RpcRequestStatus; +} + +// @public +class RpcManager { + static describeAvailableEndpoints(): Promise; + static getClientForInterface(definition: RpcInterfaceDefinition): T; + static initializeInterface(definition: RpcInterfaceDefinition): void; + static registerImpl(definition: RpcInterfaceDefinition, implementation: RpcInterfaceImplementation): void; + static supplyImplInstance(definition: RpcInterfaceDefinition, instance: TImplementation): void; + static terminateInterface(definition: RpcInterfaceDefinition): void; + static unregisterImpl(definition: RpcInterfaceDefinition): void; +} + +// @public (undocumented) +class RpcMarshaling { + static deserialize(_operation: RpcOperation, _protocol: RpcProtocol | undefined, value: RpcSerializedValue): any; + static serialize(operation: RpcOperation | string, _protocol: RpcProtocol | undefined, value: any): RpcSerializedValue; +} + +// @public +enum RpcMarshalingDirective { + // (undocumented) + Binary = "__binary__", + // (undocumented) + Error = "__error__", + // (undocumented) + ErrorMessage = "__error_message__", + // (undocumented) + ErrorName = "__error_name__", + // (undocumented) + ErrorStack = "__error_stack__", + // (undocumented) + JSON = "__JSON__", + // (undocumented) + Map = "__map__", + // (undocumented) + Name = "__name__", + // (undocumented) + Set = "__set__", + // (undocumented) + Undefined = "__undefined__", + // (undocumented) + Unregistered = "__unregistered__" +} + +// @public +enum RpcMobilePlatform { + // (undocumented) + Android = 2, + // (undocumented) + iOS = 3, + // (undocumented) + Unknown = 0, + // (undocumented) + Window = 1 +} + +// @public +class RpcMultipart { + static createForm(value: RpcSerializedValue): FormData; + static createStream(_value: RpcSerializedValue): ReadableFormData; + static parseRequest(_req: HttpServerRequest): Promise; + // (undocumented) + static writeValueToForm(form: FormDataCommon, value: RpcSerializedValue): void; +} + +// @public +class RpcNotFoundResponse extends RpcControlResponse { +} + +// @public (undocumented) +class RpcOperation { +} + +// @public +class RpcOperationPolicy { + allowResponseCaching: RpcResponseCachingCallback_T; + invocationCallback: RpcInvocationCallback_T; + requestCallback: RpcRequestCallback_T; + requestId: RpcRequestIdSupplier_T; + retryInterval: RpcRequestInitialRetryIntervalSupplier_T; + sentCallback: RpcRequestCallback_T; + token: RpcRequestTokenSupplier_T; +} + +// @public +interface RpcOperationsProfile { + // (undocumented) + readonly lastRequest: number; + // (undocumented) + readonly lastResponse: number; +} + +// @public +class RpcPendingQueue { + // (undocumented) + static initialize(): void; + // (undocumented) + static instance: RpcPendingQueue; +} + +// @public +class RpcPendingResponse extends RpcControlResponse { + constructor(message?: string); + message: string; +} + +// @public +class RpcProtocol { + constructor(configuration: RpcConfiguration); + readonly authorizationHeaderName: string; + readonly configuration: RpcConfiguration; + static readonly events: BeEvent; + fulfill(request: SerializedRpcRequest): Promise; + getCode(status: RpcRequestStatus): number; + getOperationFromPath(path: string): SerializedRpcOperation; + getStatus(code: number): RpcRequestStatus; + readonly invocationType: typeof RpcInvocation; + // (undocumented) + onRpcClientInitialized(_definition: RpcInterfaceDefinition, _client: RpcInterface): void; + // (undocumented) + onRpcClientTerminated(_definition: RpcInterfaceDefinition, _client: RpcInterface): void; + // (undocumented) + onRpcImplInitialized(_definition: RpcInterfaceDefinition, _impl: RpcInterface): void; + // (undocumented) + onRpcImplTerminated(_definition: RpcInterfaceDefinition, _impl: RpcInterface): void; + requestIdHeaderName: string; + readonly requestType: typeof RpcRequest; + serialize(request: RpcRequest): SerializedRpcRequest; + supplyPathForOperation(operation: RpcOperation, _request: RpcRequest | undefined): string; + readonly versionHeaderName: string; +} + +// @public +enum RpcProtocolEvent { + // (undocumented) + BackendErrorOccurred = 11, + // (undocumented) + BackendErrorReceived = 5, + // (undocumented) + BackendReportedNotFound = 10, + // (undocumented) + BackendReportedPending = 9, + // (undocumented) + BackendResponseCreated = 8, + // (undocumented) + ConnectionAborted = 6, + // (undocumented) + ConnectionErrorReceived = 3, + // (undocumented) + RequestCreated = 0, + // (undocumented) + RequestReceived = 7, + // (undocumented) + ResponseLoaded = 1, + // (undocumented) + ResponseLoading = 2, + // (undocumented) + UnknownErrorReceived = 4 +} + +// @public (undocumented) +class RpcRegistry { + // (undocumented) + definitionClasses: Map; + // (undocumented) + describeAvailableEndpoints(): Promise; + // (undocumented) + getClientForInterface(definition: RpcInterfaceDefinition): T; + // (undocumented) + getImplForInterface(definition: RpcInterfaceDefinition): T; + // (undocumented) + id: () => number; + // (undocumented) + implementationClasses: Map; + // (undocumented) + implementations: Map; + // (undocumented) + initializeRpcInterface(definition: RpcInterfaceDefinition): void; + // (undocumented) + static readonly instance: RpcRegistry; + // (undocumented) + isRpcInterfaceInitialized(definition: RpcInterfaceDefinition): boolean; + // (undocumented) + lookupImpl(interfaceName: string): T; + // (undocumented) + lookupInterfaceDefinition(name: string): RpcInterfaceDefinition; + // (undocumented) + proxies: Map; + // (undocumented) + registerImpl(definition: RpcInterfaceDefinition, implementation: RpcInterfaceImplementation): void; + // (undocumented) + suppliedImplementations: Map; + // (undocumented) + supplyImplInstance(definition: RpcInterfaceDefinition, instance: TImplementation): void; + // (undocumented) + terminateRpcInterface(definition: RpcInterfaceDefinition): void; + // (undocumented) + types: Map; + // (undocumented) + unregisterImpl(definition: RpcInterfaceDefinition): void; +} + +// @public +class RpcRequest { + constructor(client: RpcInterface, operation: string, parameters: any[]); + static readonly aggregateLoad: RpcOperationsProfile; + readonly client: RpcInterface; + readonly connecting: boolean; + static current(context: RpcInterface): RpcRequest; + // (undocumented) + dispose(): void; + readonly elapsed: number; + static readonly events: BeEvent; + readonly extendedStatus: string; + findParameterOfType(requestConstructor: { + new (...args: any[]): T; + }): T | undefined; + readonly id: string; + readonly lastSubmitted: number; + readonly lastUpdated: number; + protected abstract load(): Promise; + method: string; + static readonly notFoundHandlers: BeEvent; + readonly operation: RpcOperation; + parameters: any[]; + path: string; + readonly pending: boolean; + readonly protocol: RpcProtocol; + readonly response: Promise; + retryInterval: number; + protected abstract send(): Promise; + protected abstract setHeader(name: string, value: string): void; + protected setLastUpdatedTime(): void; + readonly status: RpcRequestStatus; + // (undocumented) + submit(): Promise; +} + +// @public +enum RpcRequestEvent { + // (undocumented) + PendingUpdateReceived = 1, + // (undocumented) + StatusChanged = 0 +} + +// @public (undocumented) +interface RpcRequestFulfillment { +} + +// @public +enum RpcRequestStatus { + // (undocumented) + Created = 1, + // (undocumented) + Disposed = 6, + // (undocumented) + NotFound = 7, + // (undocumented) + Pending = 3, + // (undocumented) + Rejected = 5, + // (undocumented) + Resolved = 4, + // (undocumented) + Submitted = 2, + // (undocumented) + Unknown = 0 +} + +// @public +enum RpcResponseCacheControl { + // (undocumented) + Immutable = 1, + // (undocumented) + None = 0 +} + +// @public (undocumented) +interface RpcSerializedValue { +} + +// @public (undocumented) +class SceneLights { + // WARNING: The type "ImageLight.Solar" needs to be exported by the package (e.g. added to index.ts) + constructor(imageBased: { + environmentalMap: RenderTexture; + diffuseImage: RenderTexture; + solar: ImageLight.Solar; + }, fstop?: number); + // (undocumented) + addLight(light: Light): void; + // (undocumented) + fstop: number; + // WARNING: The type "ImageLight.Solar" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + imageBased: { + diffuseImage: RenderTexture; + environmentalMap: RenderTexture; + solar: ImageLight.Solar; + } + // (undocumented) + readonly isEmpty: boolean; +} + +// @public +interface SerializedRpcOperation { + // (undocumented) + encodedRequest?: string; + // (undocumented) + interfaceDefinition: string; + // (undocumented) + interfaceVersion: string; + // (undocumented) + operationName: string; +} + +// @public +interface SerializedRpcRequest { + // (undocumented) + authorization: string; + // (undocumented) + caching: RpcResponseCacheControl; + // (undocumented) + id: string; + // (undocumented) + method: string; + // (undocumented) + operation: SerializedRpcOperation; + // (undocumented) + parameters: RpcSerializedValue; + // (undocumented) + path: string; + // (undocumented) + version: string; +} + +// @public (undocumented) +class ServerError extends IModelError { + constructor(errorNumber: number, message: string, log?: LogFunction); +} + +// @beta +interface SheetBorderTemplateProps extends ElementProps { + // (undocumented) + height?: number; + // (undocumented) + width?: number; +} + +// @beta +interface SheetProps extends ElementProps { + // (undocumented) + attachments?: Id64String[]; + // (undocumented) + height?: number; + // (undocumented) + scale?: number; + // (undocumented) + sheetTemplate?: Id64String; + // (undocumented) + width?: number; +} + +// @beta +interface SheetTemplateProps extends ElementProps { + // (undocumented) + border?: Id64String; + // (undocumented) + height?: number; + // (undocumented) + width?: number; +} + +// @public (undocumented) +class SilhouetteEdgeArgs extends EdgeArgs { + // (undocumented) + clear(): void; + // (undocumented) + init(meshEdges?: MeshEdges): boolean; + // (undocumented) + normals?: OctEncodedNormalPair[]; +} + +// @public +interface SkyBoxImageProps { + texture?: Id64String; + textures?: SkyCubeProps; + type?: SkyBoxImageType; +} + +// @public +enum SkyBoxImageType { + Cube = 2, + Cylindrical = 3, + // (undocumented) + None = 0, + Spherical = 1 +} + +// @public +interface SkyBoxProps { + display?: boolean; + groundColor?: ColorDefProps; + groundExponent?: number; + image?: SkyBoxImageProps; + nadirColor?: ColorDefProps; + skyColor?: ColorDefProps; + skyExponent?: number; + twoColor?: boolean; + zenithColor?: ColorDefProps; +} + +// @public +interface SkyCubeProps { + // (undocumented) + back?: Id64String; + // (undocumented) + bottom?: Id64String; + // (undocumented) + front?: Id64String; + // (undocumented) + left?: Id64String; + // (undocumented) + right?: Id64String; + // (undocumented) + top?: Id64String; +} + +// @beta +interface SnapRequestProps { + // (undocumented) + closePoint: XYZProps; + // (undocumented) + decorationGeometry?: DecorationGeometryProps[]; + // (undocumented) + geometryClass?: GeometryClass; + // (undocumented) + id: Id64String; + // (undocumented) + intersectCandidates?: Id64Array; + // (undocumented) + snapAperture?: number; + // (undocumented) + snapDivisor?: number; + // (undocumented) + snapModes?: number[]; + // (undocumented) + subCategoryId?: Id64String; + // (undocumented) + testPoint: XYZProps; + // (undocumented) + viewFlags?: any; + // (undocumented) + worldToView: Matrix4dProps; +} + +// @beta +interface SnapResponseProps { + // (undocumented) + curve?: any; + // (undocumented) + geomType?: number; + // (undocumented) + heat?: number; + // (undocumented) + hitPoint?: XYZProps; + // (undocumented) + intersectCurve?: any; + // (undocumented) + intersectId?: string; + // (undocumented) + normal?: XYZProps; + // (undocumented) + parentGeomType?: number; + // (undocumented) + snapMode?: number; + // (undocumented) + snapPoint?: XYZProps; + // (undocumented) + status: number; +} + +// @public +interface SpatialViewDefinitionProps extends ViewDefinition3dProps { + // (undocumented) + modelSelectorId: Id64String; +} + +// @public +class Spot extends Light { + constructor(opts?: SpotProps); + // (undocumented) + inner: Angle; + // (undocumented) + outer: Angle; +} + +// @public +interface SpotProps extends LightProps { + // (undocumented) + inner?: AngleProps; + // (undocumented) + outer?: AngleProps; +} + +// @alpha +class StandaloneIModelRpcInterface extends RpcInterface { + // (undocumented) + closeStandalone(_iModelToken: IModelToken): Promise; + static getClient(): StandaloneIModelRpcInterface; + // (undocumented) + openStandalone(_fileName: string, _openMode: OpenMode): Promise; + static types: () => (typeof IModelToken)[]; + static version: string; +} + +// @public (undocumented) +class SubCategoryAppearance { +} + +// @public +class SubCategoryOverride { + readonly anyOverridden: boolean; + readonly color?: ColorDef; + static defaults: SubCategoryOverride; + // WARNING: The type "SubCategoryAppearance.Props" needs to be exported by the package (e.g. added to index.ts) + static fromJSON(json?: SubCategoryAppearance.Props): SubCategoryOverride; + readonly invisible?: boolean; + readonly material?: Id64String; + override(appearance: SubCategoryAppearance): SubCategoryAppearance; + readonly priority?: number; + readonly style?: Id64String; + // WARNING: The type "SubCategoryAppearance.Props" needs to be exported by the package (e.g. added to index.ts) + toJSON(): SubCategoryAppearance.Props; + readonly transparency?: number; + readonly weight?: number; +} + +// @public +interface SubCategoryProps extends ElementProps { + // (undocumented) + appearance?: SubCategoryAppearance.Props; + // (undocumented) + description?: string; +} + +// @public +interface SubjectProps extends ElementProps { + // (undocumented) + description?: string; +} + +// @public +class TestRpcManager { + // (undocumented) + static initialize(interfaces: RpcInterfaceDefinition[]): void; +} + +// @public +class TextString { + constructor(props: TextStringProps); + bold?: boolean; + font: number; + // (undocumented) + height: number; + italic?: boolean; + readonly origin: Point3d; + readonly rotation: YawPitchRollAngles; + text: string; + // (undocumented) + toJSON(): TextStringProps; + // (undocumented) + transformInPlace(transform: Transform): boolean; + underline?: boolean; + // (undocumented) + readonly width: number; + // (undocumented) + widthFactor?: number; +} + +// @public +interface TextStringProps { + bold?: boolean; + font: number; + // (undocumented) + height: number; + italic?: boolean; + origin?: XYZProps; + rotation?: YawPitchRollProps; + text: string; + underline?: boolean; + // (undocumented) + widthFactor?: number; +} + +// @beta (undocumented) +enum TextureFlags { + // (undocumented) + None = 0 +} + +// @beta (undocumented) +class TextureMapping { +} + +// @beta +interface TextureMapProps { + pattern_angle?: number; + pattern_flip?: boolean; + pattern_mapping?: TextureMapping.Mode; + pattern_offset?: DPoint2dProps; + pattern_scale?: DPoint2dProps; + pattern_scalemode?: TextureMapUnits; + pattern_u_flip?: boolean; + pattern_weight?: number; + TextureId: Id64String; +} + +// @beta (undocumented) +enum TextureMapUnits { + // (undocumented) + Feet = 5, + // (undocumented) + Inches = 6, + // (undocumented) + Meters = 3, + // (undocumented) + Millimeters = 4, + // (undocumented) + Relative = 0 +} + +// @beta +interface TextureProps extends DefinitionElementProps { + data: string; + description?: string; + flags: TextureFlags; + format: ImageSourceFormat; + height: number; + width: number; +} + +// @alpha +interface ThumbnailFormatProps { + format: "jpeg" | "png"; + height: number; + width: number; +} + +// @alpha +interface ThumbnailProps extends ThumbnailFormatProps { + // (undocumented) + image: Uint8Array; +} + +// @public (undocumented) +interface TileProps { + contentId: string; + contentRange?: Range3dProps; + isLeaf?: boolean; + maximumSize: number; + range: Range3dProps; + sizeMultiplier?: number; + transformToRoot?: TransformProps; +} + +// @public (undocumented) +interface TileTreeProps { + id: string; + isTerrain?: boolean; + location: TransformProps; + maxTilesToSkip?: number; + rootTile: TileProps; + yAxisUp?: boolean; +} + +// @public +class TypeDefinition extends RelatedElement { +} + +// @public +interface TypeDefinitionElementProps extends DefinitionElementProps { + // (undocumented) + recipe?: RelatedElementProps; +} + +// @public (undocumented) +interface ViewAttachmentLabelProps extends GeometricElement2dProps { + // (undocumented) + viewAttachment?: RelatedElementProps; +} + +// @public +interface ViewAttachmentProps extends GeometricElement2dProps { + // (undocumented) + view: RelatedElementProps; +} + +// @public +interface ViewDefinition2dProps extends ViewDefinitionProps { + // (undocumented) + angle: AngleProps; + // (undocumented) + baseModelId: Id64String; + // (undocumented) + delta: XYProps; + // (undocumented) + origin: XYProps; +} + +// @public +interface ViewDefinition3dProps extends ViewDefinitionProps { + angles?: YawPitchRollProps; + camera: CameraProps; + cameraOn: boolean; + extents: XYZProps; + origin: XYZProps; +} + +// @public +interface ViewDefinitionProps extends DefinitionElementProps { + // (undocumented) + categorySelectorId: Id64String; + // (undocumented) + description?: string; + // (undocumented) + displayStyleId: Id64String; +} + +// @public (undocumented) +module ViewFlag { + class Overrides { + constructor(flags?: ViewFlags); + // (undocumented) + anyOverridden(): boolean; + apply(base: ViewFlags): ViewFlags; + // (undocumented) + clear(): void; + // WARNING: The type "Overrides" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Overrides" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + clone(out?: Overrides): Overrides; + // WARNING: The type "Overrides" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + copyFrom(other: Overrides): void; + // WARNING: The type "PresenceFlag" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + isPresent(flag: PresenceFlag): boolean; + // (undocumented) + overrideAll(flags?: ViewFlags): void; + // (undocumented) + setEdgeMask(val: number): void; + // (undocumented) + setIgnoreGeometryMap(val: boolean): void; + // (undocumented) + setMonochrome(val: boolean): void; + // WARNING: The type "PresenceFlag" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + setPresent(flag: PresenceFlag): void; + // (undocumented) + setRenderMode(val: RenderMode): void; + // (undocumented) + setShowBackgroundMap(val: boolean): void; + // (undocumented) + setShowCameraLights(val: boolean): void; + // (undocumented) + setShowClipVolume(val: boolean): void; + // (undocumented) + setShowConstructions(val: boolean): void; + // (undocumented) + setShowDimensions(val: boolean): void; + // (undocumented) + setShowFill(val: boolean): void; + // (undocumented) + setShowHiddenEdges(val: boolean): void; + // (undocumented) + setShowMaterials(val: boolean): void; + // (undocumented) + setShowPatterns(val: boolean): void; + // (undocumented) + setShowShadows(val: boolean): void; + // (undocumented) + setShowSolarLight(val: boolean): void; + // (undocumented) + setShowSourceLights(val: boolean): void; + // (undocumented) + setShowStyles(val: boolean): void; + // (undocumented) + setShowTextures(val: boolean): void; + // (undocumented) + setShowTransparency(val: boolean): void; + // (undocumented) + setShowVisibleEdges(val: boolean): void; + // (undocumented) + setShowWeights(val: boolean): void; + // (undocumented) + setUseHlineMaterialColors(val: boolean): void; + } + + // (undocumented) + enum PresenceFlag { + // (undocumented) + kBackgroundMap = 23, + // (undocumented) + kCameraLights = 14, + // (undocumented) + kClipVolume = 17, + // (undocumented) + kConstructions = 18, + // (undocumented) + kContinuousRendering = 7, + // (undocumented) + kDimensions = 2, + // (undocumented) + kEdgeMask = 22, + // (undocumented) + kFill = 8, + // (undocumented) + kGeometryMap = 20, + // (undocumented) + kHiddenEdges = 12, + // (undocumented) + kHlineMaterialColors = 21, + // (undocumented) + kMaterials = 10, + // (undocumented) + kMonochrome = 19, + // (undocumented) + kPatterns = 3, + // (undocumented) + kRenderMode = 0, + // (undocumented) + kShadows = 16, + // (undocumented) + kSolarLight = 15, + // (undocumented) + kSourceLights = 13, + // (undocumented) + kStyles = 5, + // (undocumented) + kText = 1, + // (undocumented) + kTextures = 9, + // (undocumented) + kTransparency = 6, + // (undocumented) + kVisibleEdges = 11, + // (undocumented) + kWeights = 4 + } + +} + +// @public +interface ViewFlagProps { + acs?: boolean; + ambientOcclusion?: boolean; + backgroundMap?: boolean; + clipVol?: boolean; + contRend?: boolean; + // (undocumented) + edgeMask?: number; + grid?: boolean; + hidEdges?: boolean; + hlMatColors?: boolean; + monochrome?: boolean; + noCameraLights?: boolean; + noConstruct?: boolean; + noDim?: boolean; + noFill?: boolean; + noMaterial?: boolean; + noPattern?: boolean; + noSolarLight?: boolean; + noSourceLights?: boolean; + noStyle?: boolean; + noTexture?: boolean; + noTransp?: boolean; + noWeight?: boolean; + renderMode?: number; + shadows?: boolean; + visEdges?: boolean; +} + +// @public +class ViewFlags { + acsTriad: boolean; + ambientOcclusion: boolean; + backgroundMap: boolean; + cameraLights: boolean; + clipVolume: boolean; + // (undocumented) + clone(out?: ViewFlags): ViewFlags; + constructions: boolean; + continuousRendering: boolean; + // (undocumented) + static createFrom(other?: ViewFlags, out?: ViewFlags): ViewFlags; + dimensions: boolean; + edgeMask: number; + // (undocumented) + edgesRequired(): boolean; + // (undocumented) + equals(other: ViewFlags): boolean; + fill: boolean; + // (undocumented) + static fromJSON(json?: ViewFlagProps): ViewFlags; + grid: boolean; + hiddenEdges: boolean; + // (undocumented) + hiddenEdgesVisible(): boolean; + hLineMaterialColors: boolean; + materials: boolean; + monochrome: boolean; + noGeometryMap: boolean; + patterns: boolean; + renderMode: RenderMode; + shadows: boolean; + solarLight: boolean; + sourceLights: boolean; + styles: boolean; + textures: boolean; + // (undocumented) + toJSON(): ViewFlagProps; + transparency: boolean; + visibleEdges: boolean; + weights: boolean; +} + +// @alpha +interface ViewQueryParams extends EntityQueryParams { + // (undocumented) + wantPrivate?: boolean; +} + +// @public +interface ViewStateProps { + // (undocumented) + categorySelectorProps: CategorySelectorProps; + // (undocumented) + displayStyleProps: DisplayStyleProps; + // (undocumented) + modelSelectorProps?: ModelSelectorProps; + // (undocumented) + sheetAttachments?: Id64Array; + // (undocumented) + sheetProps?: SheetProps; + // (undocumented) + viewDefinitionProps: ViewDefinitionProps; +} + +// @public +class WebAppRpcProtocol extends RpcProtocol { + constructor(configuration: RpcConfiguration); + static computeContentType(httpType: string | null | undefined): RpcContentType; + getCode(status: RpcRequestStatus): number; + getStatus(code: number): RpcRequestStatus; + handleOpenApiDescriptionRequest(_req: HttpServerRequest, res: HttpServerResponse): void; + handleOperationGetRequest(req: HttpServerRequest, res: HttpServerResponse): Promise; + handleOperationPostRequest(req: HttpServerRequest, res: HttpServerResponse): Promise; + // WARNING: The type "OpenAPIInfo" needs to be exported by the package (e.g. added to index.ts) + info: OpenAPIInfo; + // WARNING: The type "RpcOpenAPIDescription" needs to be exported by the package (e.g. added to index.ts) + readonly openAPIDescription: RpcOpenAPIDescription; + pathPrefix: string; + readonly requestType: typeof WebAppRpcRequest; + // WARNING: The type "OpenAPIParameter" needs to be exported by the package (e.g. added to index.ts) + abstract supplyPathParametersForOperation(_operation: RpcOperation): OpenAPIParameter[]; +} + +// @public +class WebAppRpcRequest extends RpcRequest { + constructor(client: RpcInterface, operation: string, parameters: any[]); + protected static computeTransportType(value: RpcSerializedValue, source: any): RpcContentType; + // (undocumented) + protected load(): Promise; + static maxUrlComponentSize: number; + metadata: { + message: string; + status: number; + } + method: HttpMethod_T; + static parseRequest(protocol: WebAppRpcProtocol, req: HttpServerRequest): Promise; + readonly protocol: WebAppRpcProtocol; + protected send(): Promise; + static sendResponse(_protocol: WebAppRpcProtocol, request: SerializedRpcRequest, fulfillment: RpcRequestFulfillment, res: HttpServerResponse): void; + protected setHeader(name: string, value: string): void; +} + +// @public +class WipRpcInterface extends RpcInterface { + // (undocumented) + attachChangeCache(_iModelToken: IModelToken): Promise; + // (undocumented) + detachChangeCache(_iModelToken: IModelToken): Promise; + // (undocumented) + getChangedElements(_iModelToken: IModelToken, _startChangesetId: string, _endChangesetId: string): Promise; + static getClient(): WipRpcInterface; + // (undocumented) + isChangeCacheAttached(_iModelToken: IModelToken): Promise; + // (undocumented) + isChangesetProcessed(_iModelToken: IModelToken, _changesetId: string): Promise; + // (undocumented) + placeholder(_iModelToken: IModelToken): Promise; + static types: () => (typeof IModelToken)[]; + static version: string; +} + +// WARNING: Unsupported export: CodeScopeProps +// WARNING: Unsupported export: ColorDefProps +// WARNING: Unsupported export: PlacementProps +// WARNING: Unsupported export: PropertyCallback +// WARNING: Unsupported export: GateValue +// WARNING: Unsupported export: NpcCorners +// WARNING: Unsupported export: NpcCenter +// WARNING: Unsupported export: RpcInterfaceImplementation +// WARNING: Unsupported export: GetMetaDataFunction +// WARNING: Unsupported export: LogFunction +// WARNING: Unsupported export: RgbFactorProps +// WARNING: Unsupported export: DPoint2dProps +// WARNING: Unsupported export: GeometryStreamProps +// WARNING: Unsupported export: AxisAlignedBox3d +// WARNING: Unsupported export: ElementAlignedBox3d +// WARNING: Unsupported export: ElementAlignedBox2d +// WARNING: Unsupported export: LocalAlignedBox3d +// WARNING: Unsupported export: WEB_RPC_CONSTANTS +// WARNING: Unsupported export: RpcConfigurationSupplier +// WARNING: Unsupported export: RpcInvocationCallback_T +// WARNING: Unsupported export: RpcProtocolEventHandler +// WARNING: Unsupported export: REGISTRY +// WARNING: Unsupported export: OPERATION +// WARNING: Unsupported export: POLICY +// WARNING: Unsupported export: INSTANCE +// WARNING: Unsupported export: CURRENT_REQUEST +// WARNING: Unsupported export: CURRENT_INVOCATION +// WARNING: Unsupported export: builtins +// WARNING: Unsupported export: RpcRequestTokenSupplier_T +// WARNING: Unsupported export: RpcRequestIdSupplier_T +// WARNING: Unsupported export: RpcRequestInitialRetryIntervalSupplier_T +// WARNING: Unsupported export: RpcRequestCallback_T +// WARNING: Unsupported export: RpcResponseCachingCallback_T +// WARNING: Unsupported export: RpcRequestEventHandler +// WARNING: Unsupported export: RpcRequestNotFoundHandler +// WARNING: Unsupported export: initializeRpcRequest +// WARNING: Unsupported export: HttpMethod_T +// (No @packagedocumentation comment for this package) diff --git a/common/api/imodeljs-frontend.api.ts b/common/api/imodeljs-frontend.api.ts new file mode 100644 index 0000000..4d36272 --- /dev/null +++ b/common/api/imodeljs-frontend.api.ts @@ -0,0 +1,6557 @@ +// @public +class AccuDraw { + // (undocumented) + protected _acsPickId?: string; + // (undocumented) + protected _animationFrames: number; + // (undocumented) + protected _compassSizeInches: number; + // (undocumented) + protected readonly _fillColor: ColorDef; + // (undocumented) + protected readonly _fillColorNoFocus: ColorDef; + // (undocumented) + protected readonly _frameColor: ColorDef; + // (undocumented) + protected readonly _frameColorNoFocus: ColorDef; + // (undocumented) + protected readonly _indexColor: ColorDef; + // (undocumented) + protected _indexToleranceInches: number; + // (undocumented) + protected readonly _xColor: ColorDef; + // (undocumented) + protected readonly _yColor: ColorDef; + // (undocumented) + accountForAuxRotationPlane(rot: ThreeAxes, plane: RotationMode): void; + // (undocumented) + activate(): void; + // (undocumented) + adjustPoint(pointActive: Point3d, vp: ScreenViewport, fromSnap: boolean): boolean; + // (undocumented) + alwaysShowCompass: boolean; + // (undocumented) + angleLock(): void; + // (undocumented) + autoFocusFields: boolean; + // (undocumented) + autoPointPlacement: boolean; + // (undocumented) + readonly axes: ThreeAxes; + // (undocumented) + axisIndexing: boolean; + // (undocumented) + readonly baseAxes: ThreeAxes; + // (undocumented) + changeBaseRotationMode(mode: RotationMode): void; + // (undocumented) + changeCompassMode(animate?: boolean): void; + // (undocumented) + clearTentative(): boolean; + // (undocumented) + compassMode: CompassMode; + // (undocumented) + contextSensitive: boolean; + // (undocumented) + currentState: CurrentState; + // (undocumented) + currentView?: ScreenViewport; + // (undocumented) + deactivate(): void; + // (undocumented) + decorate(context: DecorateContext): void; + // (undocumented) + readonly delta: Vector3d; + // (undocumented) + disableForSession(): void; + // (undocumented) + distanceIndexing: boolean; + // (undocumented) + distanceLock(synchText: boolean, saveInHistory: boolean): void; + // (undocumented) + doAutoPoint(index: ItemField, mode: CompassMode): void; + // (undocumented) + doLockAngle(isSnapped: boolean): void; + // (undocumented) + dontMoveFocus: boolean; + // (undocumented) + downgradeInactiveState(): boolean; + // (undocumented) + enableForSession(): void; + // (undocumented) + fixPointPolar(vp: Viewport): void; + // (undocumented) + fixPointRectangular(vp: Viewport): void; + // (undocumented) + readonly flags: Flags; + // (undocumented) + floatingOrigin: boolean; + // (undocumented) + static getCurrentOrientation(vp: Viewport, checkAccuDraw: boolean, checkACS: boolean, rMatrix?: Matrix3d): Matrix3d | undefined; + // (undocumented) + getDecorationGeometry(hit: HitDetail): GeometryStreamProps | undefined; + // (undocumented) + getFieldLock(index: ItemField): boolean; + // (undocumented) + getKeyinStatus(index: ItemField): KeyinStatus; + // (undocumented) + getRotation(rMatrix?: Matrix3d): Matrix3d; + // (undocumented) + static getSnapRotation(snap: SnapDetail, currentVp: Viewport | undefined, out?: Matrix3d): Matrix3d | undefined; + // (undocumented) + static getStandardRotation(nStandard: StandardViewId, vp: Viewport | undefined, useACS: boolean, out?: Matrix3d): Matrix3d; + // (undocumented) + getValueByIndex(index: ItemField): number; + grabInputFocus(): void; + hardConstructionPlane(outPtP: Point3d, inPtP: Point3d, pointOnPlaneP: Point3d, normalVectorP: Vector3d, vp: Viewport, isSnap: boolean): boolean; + // (undocumented) + readonly hasInputFocus: boolean; + // (undocumented) + indexed: LockedStates; + // (undocumented) + readonly isActive: boolean; + // (undocumented) + readonly isDeactivated: boolean; + // (undocumented) + readonly isEnabled: boolean; + // (undocumented) + readonly isInactive: boolean; + // (undocumented) + isZLocked(vp: Viewport): boolean; + // (undocumented) + readonly lastAxes: ThreeAxes; + // (undocumented) + locked: LockedStates; + // (undocumented) + newFocus: ItemField; + // (undocumented) + onBeginDynamics(): boolean; + // (undocumented) + onCompassModeChange(): void; + // (undocumented) + onEndDynamics(): boolean; + // (undocumented) + onFieldLockChange(_index: ItemField): void; + // (undocumented) + onFieldValueChange(_index: ItemField): void; + // (undocumented) + onInitialized(): void; + // (undocumented) + onInputCollectorExit(): boolean; + // (undocumented) + onInputCollectorInstall(): boolean; + onMotion(_ev: BeButtonEvent): void; + // (undocumented) + onPostButtonEvent(ev: BeButtonEvent): boolean; + // (undocumented) + onPreButtonEvent(ev: BeButtonEvent): boolean; + // (undocumented) + onPrimitiveToolInstall(): boolean; + // (undocumented) + onRotationModeChange(): void; + // (undocumented) + onSelectedViewportChanged(previous: ScreenViewport | undefined, current: ScreenViewport | undefined): void; + // (undocumented) + onSnap(snap: SnapDetail): boolean; + // (undocumented) + onTentative(): boolean; + // (undocumented) + onViewToolExit(): boolean; + // (undocumented) + onViewToolInstall(): boolean; + // (undocumented) + readonly origin: Point3d; + // (undocumented) + readonly planePt: Point3d; + // (undocumented) + readonly point: Point3d; + // (undocumented) + processFieldInput(index: ItemField, input: string, synchText: boolean): void; + // (undocumented) + processHints(): void; + // (undocumented) + readonly published: AccudrawData; + // (undocumented) + refreshDecorationsAndDynamics(): void; + // (undocumented) + restoreState(stateBuffer: SavedState): void; + // (undocumented) + rotationMode: RotationMode; + // (undocumented) + saveCoordinate(index: ItemField, value: number): void; + // (undocumented) + readonly savedStateInputCollector: SavedState; + // (undocumented) + readonly savedStateViewTool: SavedState; + // (undocumented) + saveState(stateBuffer: SavedState): void; + // (undocumented) + sendDataPoint(pt: Point3d, vp: ScreenViewport): void; + // (undocumented) + setCompassMode(mode: CompassMode): void; + // (undocumented) + setContext(flags: AccuDrawFlags, originP?: Point3d, orientationP?: Matrix3d | Vector3d, deltaP?: Vector3d, distanceP?: number, angleP?: number, transP?: Transform): BentleyStatus; + // (undocumented) + setContextRotation(rMatrix: Matrix3d, locked: boolean, animate: boolean): void; + // (undocumented) + setFieldLock(index: ItemField, locked: boolean): void; + // (undocumented) + setFocusItem(_index: ItemField): void; + // (undocumented) + setKeyinStatus(index: ItemField, status: KeyinStatus): void; + // (undocumented) + setLastPoint(pt: Point3d): void; + // (undocumented) + protected setNewFocus(index: ItemField): void; + // (undocumented) + setRotationMode(mode: RotationMode): void; + // (undocumented) + setValueByIndex(index: ItemField, value: number): void; + // (undocumented) + smartKeyin: boolean; + // (undocumented) + softConstructionPlane(outPtP: Point3d, inPtP: Point3d, pointOnPlaneP: Point3d, normalVectorP: Vector3d, vp: Viewport, isSnap: boolean): boolean; + // (undocumented) + stickyZLock: boolean; + // (undocumented) + testDecorationHit(id: string): boolean; + // (undocumented) + unlockAllFields(): void; + // (undocumented) + static updateAuxCoordinateSystem(acs: AuxCoordSystemState, vp: Viewport, allViews?: boolean): void; + // (undocumented) + updateFieldLock(index: ItemField, locked: boolean): void; + // (undocumented) + updateRotation(animate?: boolean, newRotationIn?: Matrix3d): void; + // (undocumented) + upgradeToActiveState(): boolean; + // (undocumented) + readonly vector: Vector3d; +} + +// @public (undocumented) +class AccudrawData { + // (undocumented) + angle: number; + // (undocumented) + readonly delta: Point3d; + // (undocumented) + distance: number; + // (undocumented) + flags: number; + // (undocumented) + readonly origin: Point3d; + // (undocumented) + readonly rMatrix: Matrix3d; + // (undocumented) + readonly vector: Vector3d; + // (undocumented) + zero(): void; +} + +// @public (undocumented) +enum AccuDrawFlags { + // (undocumented) + AlwaysSetOrigin = 2097156, + // (undocumented) + Disable = 4096, + // (undocumented) + FixedOrigin = 8, + // (undocumented) + Lock_X = 512, + // (undocumented) + Lock_Y = 1024, + // (undocumented) + Lock_Z = 2048, + // (undocumented) + LockAngle = 524288, + // (undocumented) + LockDistance = 256, + // (undocumented) + OrientACS = 131072, + // (undocumented) + OrientDefault = 16384, + // (undocumented) + RedrawCompass = 4194304, + // (undocumented) + SetDistance = 128, + // (undocumented) + SetFocus = 32768, + // (undocumented) + SetModePolar = 1, + // (undocumented) + SetModeRect = 2, + // (undocumented) + SetNormal = 64, + // (undocumented) + SetOrigin = 4, + // (undocumented) + SetRMatrix = 16, + // (undocumented) + SetXAxis = 32, + // (undocumented) + SetXAxis2 = 262144, + // (undocumented) + SmartRotation = 16777216, + // (undocumented) + UpdateRotation = 8388608 +} + +// @public +class AccuDrawHintBuilder { + // (undocumented) + enableSmartRotation: boolean; + sendHints(activate?: boolean): boolean; + // (undocumented) + setAngle(angle: number): void; + // (undocumented) + setDistance(distance: number): void; + // (undocumented) + setLockAngle: boolean; + // (undocumented) + setLockDistance: boolean; + // (undocumented) + setLockX: boolean; + // (undocumented) + setLockY: boolean; + // (undocumented) + setLockZ: boolean; + // (undocumented) + setModePolar(): void; + // (undocumented) + setModeRectangular(): void; + // (undocumented) + setNormal(normal: Vector3d): void; + // (undocumented) + setOrigin(origin: Point3d): void; + // (undocumented) + setOriginAlways: boolean; + // (undocumented) + setOriginFixed: boolean; + // (undocumented) + setRotation(rMatrix: Matrix3d): void; + // (undocumented) + setXAxis(xAxis: Vector3d): void; + // (undocumented) + setXAxis2(xAxis: Vector3d): void; +} + +// @public +class AccuDrawShortcuts { + // (undocumented) + static alignView(): void; + // (undocumented) + static changeCompassMode(): void; + // (undocumented) + static defineACSByElement(): void; + // (undocumented) + static defineACSByPoints(): void; + // (undocumented) + static getACS(acsName: string | undefined, useOrigin: boolean, useRotation: boolean): BentleyStatus; + // (undocumented) + static itemFieldAcceptInput(index: ItemField, str: string): void; + // (undocumented) + static itemFieldLockToggle(index: ItemField): void; + // (undocumented) + static itemFieldNavigate(index: ItemField, str: string, forward: boolean): void; + // (undocumented) + static itemFieldNewInput(index: ItemField): void; + // (undocumented) + static itemFieldUnlockAll(): void; + // (undocumented) + static itemRotationModeChange(rotation: RotationMode): void; + // (undocumented) + static lockAngle(): void; + // (undocumented) + static lockDistance(): void; + // (undocumented) + lockIndex(): void; + // (undocumented) + static lockSmart(): void; + // (undocumented) + static lockX(): void; + // (undocumented) + static lockY(): void; + // (undocumented) + static lockZ(): void; + // (undocumented) + static processPendingHints(): void; + // (undocumented) + static requestInputFocus(): void; + // (undocumented) + static rotate90(axis: number): void; + // (undocumented) + static rotateAxes(aboutCurrentZ: boolean): void; + // (undocumented) + static rotateAxesByPoint(isSnapped: boolean, aboutCurrentZ: boolean): boolean; + // (undocumented) + static rotateCycle(updateCurrentACS: boolean): void; + // (undocumented) + static rotateToACS(): void; + // (undocumented) + static rotateToBase(): void; + // (undocumented) + static rotateToElement(updateCurrentACS: boolean): void; + // (undocumented) + static setOrigin(explicitOrigin?: Point3d): void; + // (undocumented) + static setStandardRotation(rotation: RotationMode): void; + // (undocumented) + static updateACSByPoints(acs: AuxCoordSystemState, vp: Viewport, points: Point3d[], isDynamics: boolean): boolean; + // (undocumented) + static writeACS(_acsName: string): BentleyStatus; +} + +// @public (undocumented) +class AccuDrawTool { + // (undocumented) + activateAccuDrawOnStart(): boolean; + // (undocumented) + abstract doManipulation(ev: BeButtonEvent | undefined, isMotion: boolean): Promise; + // (undocumented) + doManipulationStart(): void; + // (undocumented) + doManipulationStop(cancel: boolean): void; + // (undocumented) + static installTool(shortcut: AccuDrawTool): boolean; + // (undocumented) + onDecorate(_context: DecorateContext): void; + // (undocumented) + onManipulationComplete(): AccuDrawFlags; + // (undocumented) + static outputPrompt(messageKey: string): void; +} + +// @public (undocumented) +class AccuSnap { +} + +// @public (undocumented) +enum ACSDisplayOptions { + // (undocumented) + Active = 1, + // (undocumented) + CheckVisible = 8, + // (undocumented) + Deemphasized = 2, + // (undocumented) + Dynamics = 16, + // (undocumented) + Hilite = 4, + // (undocumented) + None = 0 +} + +// @public (undocumented) +enum ACSType { + // (undocumented) + Cylindrical = 2, + // (undocumented) + None = 0, + // (undocumented) + Rectangular = 1, + // (undocumented) + Spherical = 3 +} + +// @public +class ActivityMessageDetails { + constructor(showProgressBar: boolean, showPercentInMessage: boolean, supportsCancellation: boolean, showDialogInitially?: boolean); + onActivityCancelled(): void; + onActivityCompleted(): void; + // (undocumented) + showDialogInitially: boolean; + // (undocumented) + showPercentInMessage: boolean; + // (undocumented) + showProgressBar: boolean; + // (undocumented) + supportsCancellation: boolean; + // (undocumented) + wasCancelled: boolean; +} + +// @public +enum ActivityMessageEndReason { + // (undocumented) + Cancelled = 1, + // (undocumented) + Completed = 0 +} + +// @public +class AnimationBranchState { + // WARNING: The type "ClipPlanesVolume" needs to be exported by the package (e.g. added to index.ts) + constructor(transform?: Transform, clip?: ClipPlanesVolume, omit?: boolean); + // WARNING: The type "ClipPlanesVolume" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly clip?: ClipPlanesVolume; + // (undocumented) + readonly omit?: boolean; + // (undocumented) + readonly transform?: Transform; +} + +// @public (undocumented) +interface AppearanceOverrideProps { + // (undocumented) + color?: ColorDefProps; + // (undocumented) + ids?: Id64Set; + // (undocumented) + overrideType?: FeatureOverrideType; +} + +// @public +interface ArrayValue extends BasePropertyValue { + // (undocumented) + items: PropertyRecord[]; + // (undocumented) + itemsTypeName: string; + // (undocumented) + valueFormat: PropertyValueFormat.Array; +} + +// @public (undocumented) +module Attachments { + // (undocumented) + class Attachment { + protected constructor(props: ViewAttachmentProps, view: ViewState); + // WARNING: The type "Tree" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected _tree?: Tree; + clearClipping(): void; + // (undocumented) + clip: ClipVector; + static readonly DEBUG_BOUNDING_BOX_COLOR: ColorDef; + debugDrawBorder(context: SceneContext): void; + // (undocumented) + displayPriority: number; + getOrCreateClip(transform?: Transform): ClipVector; + // (undocumented) + id: Id64String; + readonly is2d: boolean; + readonly isReady: boolean; + // WARNING: The type "State" needs to be exported by the package (e.g. added to index.ts) + abstract load(sheetView: SheetViewState, sceneContext: SceneContext): State; + // (undocumented) + placement: Placement2d; + // (undocumented) + scale: number; + // WARNING: The type "Tree" needs to be exported by the package (e.g. added to index.ts) + tree: Tree | undefined; + // (undocumented) + readonly view: ViewState; + } + + // (undocumented) + class Attachment2d extends Attachment { + constructor(props: ViewAttachmentProps, view: ViewState2d); + // (undocumented) + readonly is2d: boolean; + // WARNING: The type "State" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + load(_sheetView: SheetViewState, _sceneContext: SceneContext): State; + } + + // (undocumented) + class Attachment3d extends Attachment { + constructor(props: ViewAttachmentProps, view: ViewState3d); + // WARNING: The type "State" needs to be exported by the package (e.g. added to index.ts) + getState(depth: number): State; + // (undocumented) + readonly is2d: boolean; + // WARNING: The type "State" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + load(sheetView: SheetViewState, sceneContext: SceneContext): State; + // WARNING: The type "State" needs to be exported by the package (e.g. added to index.ts) + setState(depth: number, state: State): void; + } + + // (undocumented) + class AttachmentList { + constructor(); + // WARNING: The type "Attachment" needs to be exported by the package (e.g. added to index.ts) + add(attachment: Attachment): void; + readonly allReady: boolean; + clear(): void; + // WARNING: The type "Attachment" needs to be exported by the package (e.g. added to index.ts) + drop(attachment: Attachment): void; + readonly length: number; + // WARNING: The type "Attachment" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly list: Attachment[]; + // WARNING: The type "State" needs to be exported by the package (e.g. added to index.ts) + load(idx: number, sheetView: SheetViewState, sceneContext: SceneContext): State; + } + + // (undocumented) + class AttachmentTileLoader extends TileLoader { + // (undocumented) + getChildrenProps(_parent: Tile): Promise; + // (undocumented) + readonly is3dAttachment: boolean; + // WARNING: The type "Tile.LoadPriority" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly priority: Tile.LoadPriority; + // WARNING: The type "TileRequest.Response" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + requestTileContent(_tile: Tile): Promise; + // WARNING: The type "Tile.Params" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + tileRequiresLoading(_params: Tile.Params): boolean; + } + + // (undocumented) + class AttachmentViewport extends OffScreenViewport { + // WARNING: The type "State" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "State" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + createScene(currentState: State): State; + // (undocumented) + readonly isAspectRatioLocked: boolean; + // (undocumented) + renderImage(): ImageBuffer | undefined; + // (undocumented) + rendering: boolean; + // (undocumented) + renderTexture(): void; + // WARNING: The type "Tree3d" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + setSceneDepth(depth: number, tree: Tree3d): void; + // (undocumented) + readonly texture: RenderTexture | undefined; + // (undocumented) + toParent: Transform; + } + + enum State { + // (undocumented) + Empty = 1, + // (undocumented) + Loading = 2, + // (undocumented) + NotLoaded = 0, + // (undocumented) + Ready = 3 + } + + // (undocumented) + class Tile2d extends Tile { + // WARNING: The type "Tree2d" needs to be exported by the package (e.g. added to index.ts) + constructor(root: Tree2d, range: ElementAlignedBox2d); + // WARNING: The type "Tile.DrawArgs" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + drawGraphics(args: Tile.DrawArgs): void; + // (undocumented) + readonly hasChildren: boolean; + // (undocumented) + readonly hasGraphics: boolean; + } + + // (undocumented) + class Tile3d extends Tile { + // WARNING: The type "Tree3d" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Tile3d" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Tile3dPlacement" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Tile3d" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + static create(root: Tree3d, parent: Tile3d | undefined, placement: Tile3dPlacement): Tile3d; + // (undocumented) + createGraphics(context: SceneContext): void; + // (undocumented) + createPolyfaces(context: SceneContext): void; + // WARNING: The type "Tile.DrawArgs" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + drawGraphics(args: Tile.DrawArgs): void; + // (undocumented) + readonly hasChildren: boolean; + // (undocumented) + readonly hasGraphics: boolean; + // (undocumented) + prepareChildren(): Tile[] | undefined; + // WARNING: The type "Tile.DrawArgs" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Tile.SelectParent" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + selectTiles(selected: Tile[], args: Tile.DrawArgs, _numSkipped?: number): Tile.SelectParent; + } + + enum Tile3dPlacement { + // (undocumented) + LowerLeft = 2, + // (undocumented) + LowerRight = 3, + // (undocumented) + Root = 4, + // (undocumented) + UpperLeft = 0, + // (undocumented) + UpperRight = 1 + } + + // (undocumented) + class Tree extends TileTree { + // WARNING: The type "AttachmentTileLoader" needs to be exported by the package (e.g. added to index.ts) + constructor(loader: AttachmentTileLoader, iModel: IModelConnection, modelId: Id64String); + // (undocumented) + graphicsClip?: ClipVector; + } + + // (undocumented) + class Tree2d extends Tree { + // WARNING: The type "Attachment2d" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "State" needs to be exported by the package (e.g. added to index.ts) + static create(attachment: Attachment2d): State; + // (undocumented) + readonly drawingToAttachment: Transform; + // WARNING: The type "FeatureSymbology.Overrides" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly symbologyOverrides: FeatureSymbology.Overrides; + // (undocumented) + readonly view: ViewState2d; + // (undocumented) + readonly viewRoot: TileTree; + } + + // (undocumented) + class Tree3d extends Tree { + // WARNING: The type "Attachment3d" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly attachment: Attachment3d; + // (undocumented) + readonly biasDistance: number; + // WARNING: The type "Attachment3d" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Tree3d" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + static create(sheetView: SheetViewState, attachment: Attachment3d, sceneContext: SceneContext): Tree3d; + // (undocumented) + readonly featureTable: PackedFeatureTable; + getRootRange(result?: Range3d): Range3d; + // WARNING: The type "State" needs to be exported by the package (e.g. added to index.ts) + getState(depth: number): State; + // WARNING: The type "State" needs to be exported by the package (e.g. added to index.ts) + setState(depth: number, state: State): void; + // (undocumented) + readonly sheetView: SheetViewState; + // (undocumented) + readonly tileColor: ColorDef; + // WARNING: The type "AttachmentViewport" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly viewport: AttachmentViewport; + } + +} + +// @public +class AuxCoordSystem2dState extends AuxCoordSystemState, implements AuxCoordSystem2dProps { + constructor(props: AuxCoordSystem2dProps, iModel: IModelConnection); + // (undocumented) + angle: number; + // (undocumented) + getOrigin(result?: Point3d): Point3d; + // (undocumented) + getRotation(result?: Matrix3d): Matrix3d; + // (undocumented) + readonly origin: Point2d; + // (undocumented) + setOrigin(val: XYAndZ | XAndY): void; + // (undocumented) + setRotation(val: Matrix3d): void; + // (undocumented) + toJSON(): AuxCoordSystem2dProps; +} + +// @public +class AuxCoordSystem3dState extends AuxCoordSystemState, implements AuxCoordSystem3dProps { + constructor(props: AuxCoordSystem3dProps, iModel: IModelConnection); + // (undocumented) + getOrigin(result?: Point3d): Point3d; + // (undocumented) + getRotation(result?: Matrix3d): Matrix3d; + // (undocumented) + readonly origin: Point3d; + // (undocumented) + pitch: number; + // (undocumented) + roll: number; + // (undocumented) + setOrigin(val: XYAndZ | XAndY): void; + // (undocumented) + setRotation(rMatrix: Matrix3d): void; + // (undocumented) + toJSON(): AuxCoordSystem3dProps; + // (undocumented) + yaw: number; +} + +// @public +class AuxCoordSystemSpatialState extends AuxCoordSystem3dState { +} + +// @public +class AuxCoordSystemState extends ElementState, implements AuxCoordSystemProps { + constructor(props: AuxCoordSystemProps, iModel: IModelConnection); + static createNew(acsName: string, iModel: IModelConnection): AuxCoordSystemState; + // (undocumented) + description?: string; + // (undocumented) + display(context: DecorateContext, options: ACSDisplayOptions): void; + // (undocumented) + drawGrid(context: DecorateContext): void; + // (undocumented) + static fromProps(props: AuxCoordSystemProps, iModel: IModelConnection): AuxCoordSystemState; + // (undocumented) + abstract getOrigin(result?: Point3d): Point3d; + abstract getRotation(result?: Matrix3d): Matrix3d; + // (undocumented) + readonly is3d: boolean; + static isOriginInView(drawOrigin: Point3d, viewport: Viewport, adjustOrigin: boolean): boolean; + // (undocumented) + isValidForView(view: ViewState): boolean; + // (undocumented) + abstract setOrigin(val: XYAndZ | XAndY): void; + // (undocumented) + abstract setRotation(val: Matrix3d): void; + // (undocumented) + toJSON(): AuxCoordSystemProps; + // (undocumented) + type: number; +} + +// @public +interface BasePropertyEditorParams { + // (undocumented) + type: PropertyEditorParamTypes; +} + +// @public +interface BasePropertyValue { + // (undocumented) + valueFormat: PropertyValueFormat; +} + +// @public (undocumented) +enum BeButton { + // (undocumented) + Data = 0, + // (undocumented) + Middle = 2, + // (undocumented) + Reset = 1 +} + +// @public (undocumented) +class BeButtonEvent { + // (undocumented) + button: BeButton; + // (undocumented) + clone(result?: BeButtonEvent): BeButtonEvent; + // (undocumented) + coordsFrom: CoordSource; + // (undocumented) + getDisplayPoint(): Point2d; + // (undocumented) + initEvent(point: Point3d, rawPoint: Point3d, viewPt: Point3d, vp: ScreenViewport, from: CoordSource, keyModifiers?: BeModifierKeys, button?: BeButton, isDown?: boolean, doubleClick?: boolean, isDragging?: boolean, source?: InputSource): void; + // (undocumented) + inputSource: InputSource; + // (undocumented) + invalidate(): void; + // (undocumented) + readonly isAltKey: boolean; + // (undocumented) + readonly isControlKey: boolean; + // (undocumented) + isDoubleClick: boolean; + // (undocumented) + isDown: boolean; + // (undocumented) + isDragging: boolean; + // (undocumented) + readonly isShiftKey: boolean; + // (undocumented) + readonly isValid: boolean; + // (undocumented) + keyModifiers: BeModifierKeys; + // (undocumented) + point: Point3d; + // (undocumented) + rawPoint: Point3d; + // (undocumented) + setFrom(src: BeButtonEvent): void; + // (undocumented) + viewPoint: Point3d; + // (undocumented) + viewport?: ScreenViewport; +} + +// @public (undocumented) +class BeButtonState { + // (undocumented) + downRawPt: Point3d; + // (undocumented) + downTime: number; + // (undocumented) + downUorPt: Point3d; + // (undocumented) + init(downUorPt: Point3d, downRawPt: Point3d, downTime: number, isDown: boolean, isDoubleClick: boolean, isDragging: boolean, source: InputSource): void; + // (undocumented) + inputSource: InputSource; + // (undocumented) + isDoubleClick: boolean; + // (undocumented) + isDown: boolean; + // (undocumented) + isDragging: boolean; +} + +// @public +enum BeModifierKeys { + // (undocumented) + Alt = 4, + // (undocumented) + Control = 1, + // (undocumented) + None = 0, + // (undocumented) + Shift = 2 +} + +// @public +class BeTouchEvent extends BeButtonEvent { + constructor(touchInfo: TouchEvent); + // (undocumented) + clone(result?: BeTouchEvent): BeTouchEvent; + // (undocumented) + static findTouchById(list: TouchList, id: number): Touch | undefined; + // (undocumented) + static getTouchListCentroid(list: TouchList, vp: ScreenViewport): Point2d | undefined; + // (undocumented) + static getTouchPosition(touch: Touch, vp: ScreenViewport): Point2d; + // (undocumented) + readonly isDoubleTap: boolean; + // (undocumented) + readonly isSingleTap: boolean; + // (undocumented) + readonly isSingleTouch: boolean; + // (undocumented) + readonly isTwoFingerTap: boolean; + // (undocumented) + readonly isTwoFingerTouch: boolean; + // (undocumented) + setFrom(src: BeTouchEvent): void; + // (undocumented) + tapCount: number; + // (undocumented) + readonly touchCount: number; + // (undocumented) + touchInfo: TouchEvent; +} + +// @public +class BeWheelEvent extends BeButtonEvent { + constructor(wheelDelta?: number); + // (undocumented) + clone(result?: BeWheelEvent): BeWheelEvent; + // (undocumented) + setFrom(src: BeWheelEvent): void; + // (undocumented) + wheelDelta: number; +} + +// @public +interface ButtonGroupEditorParams extends BasePropertyEditorParams { + // (undocumented) + buttons: IconDefinition[]; + // (undocumented) + type: PropertyEditorParamTypes.ButtonGroupData; +} + +// @public +interface CanvasDecoration { + decorationCursor?: string; + drawDecoration(ctx: CanvasRenderingContext2D): void; + onMouseButton?(ev: BeButtonEvent): boolean; + onMouseEnter?(ev: BeButtonEvent): void; + onMouseLeave?(): void; + onMouseMove?(ev: BeButtonEvent): void; + onWheel?(ev: BeWheelEvent): boolean; + pick?(pt: XAndY): boolean; + position?: XAndY; +} + +// @public +class CategorySelectorState extends ElementState { + constructor(props: CategorySelectorProps, iModel: IModelConnection); + addCategories(arg: Id64Arg): void; + // (undocumented) + categories: Set; + changeCategoryDisplay(arg: Id64Arg, add: boolean): void; + dropCategories(arg: Id64Arg): void; + // (undocumented) + equalState(other: CategorySelectorState): boolean; + has(id: Id64String): boolean; + isCategoryViewed(categoryId: Id64String): boolean; + readonly name: string; + // (undocumented) + toJSON(): CategorySelectorProps; +} + +// @public +interface CheckBoxIconsEditorParams extends BasePropertyEditorParams { + // (undocumented) + offIconDefinition?: IconDefinition; + // (undocumented) + onIconDefinition?: IconDefinition; + // (undocumented) + type: PropertyEditorParamTypes.CheckBoxIcons; +} + +// @public +enum ClippingType { + Mask = 1, + None = 0, + Planes = 2 +} + +// @public +enum ClipResult { + NewElements = 1, + NotSupported = 0, + OriginalElements = 2 +} + +// @public +class Clips { + // (undocumented) + clear(): void; + // (undocumented) + readonly count: number; + // (undocumented) + readonly isValid: boolean; + // WARNING: The type "TextureHandle" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + set(numPlanes: number, texture: TextureHandle): void; + // WARNING: The type "TextureHandle" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly texture: TextureHandle | undefined; +} + +// @public +class Cluster { + constructor(markers: T[]); + // (undocumented) + clusterMarker?: Marker; + // (undocumented) + readonly markers: T[]; + // (undocumented) + readonly rect: ViewRect; +} + +// @public (undocumented) +enum CompassMode { + // (undocumented) + Polar = 0, + // (undocumented) + Rectangular = 1 +} + +// @public (undocumented) +enum ContextMode { + // (undocumented) + Locked = 0, + // (undocumented) + None = 15, + // (undocumented) + XAxis = 1, + // (undocumented) + XAxis2 = 4, + // (undocumented) + YAxis = 2, + // (undocumented) + ZAxis = 3 +} + +// @public (undocumented) +class ContextRealityModelState implements TileTreeModelState { + constructor(props: ContextRealityModelProps, iModel: IModelConnection); + // (undocumented) + protected _iModel: IModelConnection; + // (undocumented) + protected _modelId: Id64String; + // (undocumented) + protected _name: string; + // (undocumented) + protected _tilesetUrl: string; + // (undocumented) + protected _tileTreeState: TileTreeState; + static findAvailableRealityModels(projectid: string, modelCartographicRange?: CartographicRange | undefined): Promise; + intersectsProjectExtents(): Promise; + // WARNING: The type "TileTree.LoadStatus" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly loadStatus: TileTree.LoadStatus; + // WARNING: The type "TileTree.LoadStatus" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + loadTileTree(_edgesRequired: boolean, _animationId?: Id64String, _asClassifier?: boolean, _classifierExpansion?: number): TileTree.LoadStatus; + matches(other: ContextRealityModelState): boolean; + // (undocumented) + readonly name: string; + // (undocumented) + readonly tileTree: TileTree | undefined; + // (undocumented) + readonly treeModelId: Id64String; + // (undocumented) + readonly url: string; +} + +// @public +class ConversionData implements UnitConversion { + // (undocumented) + factor: number; + // (undocumented) + offset: number; +} + +// @public (undocumented) +enum CoordinateLockOverrides { + // (undocumented) + ACS = 2, + // (undocumented) + All = 65535, + // (undocumented) + Grid = 4, + // (undocumented) + None = 0 +} + +// @public +enum CoordSource { + ElemSnap = 3, + Precision = 1, + TentativePoint = 2, + User = 0 +} + +// @public +enum CoordSystem { + Npc = 1, + View = 0, + World = 2 +} + +// @public (undocumented) +class CurrentInputState { + // (undocumented) + adjustLastDataPoint(ev: BeButtonEvent): void; + // (undocumented) + button: BeButtonState[]; + // (undocumented) + changeButtonToDownPoint(ev: BeButtonEvent): void; + // (undocumented) + clearKeyQualifiers(): void; + // (undocumented) + clearViewport(vp: Viewport): void; + // (undocumented) + fromButton(vp: ScreenViewport, pt: XAndY, source: InputSource, applyLocks: boolean): void; + // (undocumented) + fromPoint(vp: ScreenViewport, pt: XAndY, source: InputSource): void; + // (undocumented) + readonly hasMotionStopped: boolean; + // (undocumented) + inputSource: InputSource; + // (undocumented) + readonly isAltDown: boolean; + // (undocumented) + readonly isControlDown: boolean; + // (undocumented) + isDragging(button: BeButton): boolean; + // (undocumented) + readonly isShiftDown: boolean; + // (undocumented) + isStartDrag(button: BeButton): boolean; + // (undocumented) + lastButton: BeButton; + // (undocumented) + lastMotion: Point2d; + // (undocumented) + lastTouchStart?: BeTouchEvent; + // (undocumented) + lastWheelEvent?: BeWheelEvent; + // (undocumented) + motionTime: number; + // (undocumented) + onButtonDown(button: BeButton): void; + // (undocumented) + onButtonUp(button: BeButton): void; + // (undocumented) + onInstallTool(): void; + // (undocumented) + onMotion(pt2d: XAndY): void; + // (undocumented) + onStartDrag(button: BeButton): void; + // (undocumented) + qualifiers: BeModifierKeys; + // (undocumented) + rawPoint: Point3d; + // (undocumented) + setKeyQualifiers(ev: MouseEvent | KeyboardEvent | TouchEvent): void; + // (undocumented) + toEvent(ev: BeButtonEvent, useSnap: boolean): void; + // (undocumented) + toEventFromLastDataPoint(ev: BeButtonEvent): void; + // (undocumented) + touchTapCount?: number; + // (undocumented) + touchTapTimer?: number; + // (undocumented) + uorPoint: Point3d; + // (undocumented) + updateDownPoint(ev: BeButtonEvent): void; + // (undocumented) + viewPoint: Point3d; + // (undocumented) + viewport?: ScreenViewport; + // (undocumented) + readonly wasMotion: boolean; +} + +// @public (undocumented) +enum CurrentState { + // (undocumented) + Active = 3, + // (undocumented) + Deactivated = 1, + // (undocumented) + Inactive = 2, + // (undocumented) + NotEnabled = 0 +} + +// @public +class DecorateContext extends RenderContext { + constructor(vp: ScreenViewport, _decorations: Decorations); + addCanvasDecoration(decoration: CanvasDecoration, atFront?: boolean): void; + addDecoration(type: GraphicType, decoration: RenderGraphic): void; + addDecorationFromBuilder(builder: GraphicBuilder): void; + addHtmlDecoration(decoration: HTMLElement): void; + createGraphicBuilder(type: GraphicType, transform?: Transform, id?: Id64String): GraphicBuilder; + decorationDiv: HTMLDivElement; + // (undocumented) + static drawGrid(graphic: GraphicBuilder, doIsogrid: boolean, drawDots: boolean, gridOrigin: Point3d, xVec: Vector3d, yVec: Vector3d, gridsPerRef: number, repetitions: Point2d, vp: Viewport): void; + // (undocumented) + drawStandardGrid(gridOrigin: Point3d, rMatrix: Matrix3d, spacing: XAndY, gridsPerRef: number, isoGrid?: boolean, fixedRepetitions?: Point2d): void; + // (undocumented) + static getGridDimension(props: { + nRepetitions: number; + min: number; + }, gridSize: number, org: Point3d, dir: Point3d, points: Point3d[]): boolean; + // (undocumented) + static getGridPlaneViewIntersections(planePoint: Point3d, planeNormal: Vector3d, vp: Viewport, useProjectExtents: boolean): Point3d[]; + readonly screenViewport: ScreenViewport; + setSkyBox(graphic: RenderGraphic): void; + setViewBackground(graphic: RenderGraphic): void; +} + +// @public +class DecorationAnimator implements ViewportAnimator { + constructor(duration: BeDuration); + // (undocumented) + animate(vp: Viewport): RemoveMe; + animateDecorations(_viewport: Viewport, _durationPercent: number): RemoveMe; + // (undocumented) + onInterrupted(vp: Viewport): void; +} + +// @public +class Decorations implements IDisposable { + // (undocumented) + canvasDecorations?: CanvasDecorationList; + // (undocumented) + dispose(): void; + normal: GraphicList | undefined; + // (undocumented) + skyBox: RenderGraphic | undefined; + viewBackground: RenderGraphic | undefined; + viewOverlay: GraphicList | undefined; + world: GraphicList | undefined; + worldOverlay: GraphicList | undefined; +} + +// @public +interface Decorator { + decorate(context: DecorateContext): void; + getDecorationGeometry?(hit: HitDetail): GeometryStreamProps | undefined; + getDecorationToolTip?(hit: HitDetail): Promise; + onDecorationButtonEvent?(hit: HitDetail, ev: BeButtonEvent): Promise; + testDecorationHit?(id: string): boolean; +} + +// @public (undocumented) +class DefaultViewTouchTool extends ViewManip { + constructor(startEv: BeTouchEvent, ev: BeTouchEvent); + // (undocumented) + onDataButtonDown(_ev: BeButtonEvent): Promise; + // (undocumented) + onDataButtonUp(_ev: BeButtonEvent): Promise; + // (undocumented) + onStart(ev: BeTouchEvent): void; + // (undocumented) + onTouchCancel(_ev: BeTouchEvent): Promise; + // (undocumented) + onTouchComplete(_ev: BeTouchEvent): Promise; + // (undocumented) + onTouchMove(ev: BeTouchEvent): Promise; + // (undocumented) + static toolId: string; +} + +// @public +class DepthRangeNpc { + constructor(minimum?: number, maximum?: number); + // (undocumented) + maximum: number; + middle(): number; + // (undocumented) + minimum: number; +} + +// @public +class DisplayStyle2dState extends DisplayStyleState { + constructor(props: DisplayStyleProps, iModel: IModelConnection); + // (undocumented) + readonly settings: DisplayStyleSettings; +} + +// @public +class DisplayStyle3dState extends DisplayStyleState { + constructor(props: DisplayStyleProps, iModel: IModelConnection); + environment: Environment; + // WARNING: The type "SkyBox.CreateParams" needs to be exported by the package (e.g. added to index.ts) + loadSkyBoxParams(system: RenderSystem): SkyBox.CreateParams | undefined; + // (undocumented) + readonly settings: DisplayStyle3dSettings; + // (undocumented) + skyboxMaterial: RenderMaterial | undefined; +} + +// @public +class DisplayStyleState extends ElementState, implements DisplayStyleProps { + constructor(props: DisplayStyleProps, iModel: IModelConnection); + analysisStyle: AnalysisStyle | undefined; + backgroundColor: ColorDef; + // WARNING: The type "BackgroundMapState" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly backgroundMap: BackgroundMapState; + // (undocumented) + readonly backgroundMapPlane: Plane3dByOriginAndUnitNormal | undefined; + // (undocumented) + containsContextRealityModel(contextRealityModel: ContextRealityModelState): boolean; + // (undocumented) + contextRealityModels: ContextRealityModelState[]; + dropSubCategoryOverride(id: Id64String): void; + // (undocumented) + equalState(other: DisplayStyleState): boolean; + // (undocumented) + forEachContextRealityModel(func: (model: TileTreeModelState) => void): void; + // (undocumented) + getAnimationBranches(scheduleTime: number): AnimationBranchStates | undefined; + getSubCategoryOverride(id: Id64String): SubCategoryOverride | undefined; + readonly hasSubCategoryOverride: boolean; + // (undocumented) + is3d(): this is DisplayStyle3dState; + monochromeColor: ColorDef; + readonly name: string; + overrideSubCategory(id: Id64String, ovr: SubCategoryOverride): void; + // WARNING: The type "RenderScheduleState.Script" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly scheduleScript: RenderScheduleState.Script | undefined; + // (undocumented) + setBackgroundMap(mapProps: BackgroundMapProps): void; + // (undocumented) + readonly settings: DisplayStyleSettings; + viewFlags: ViewFlags; +} + +// @public +class DrawingModelState extends GeometricModel2dState { +} + +// @public +class DrawingViewState extends ViewState2d { + // (undocumented) + static readonly className: string; + // (undocumented) + static createFromProps(props: ViewStateProps, iModel: IModelConnection): ViewState | undefined; + // (undocumented) + getExtentLimits: { + max: number; + min: number; + } +} + +// @public +class DynamicsContext extends RenderContext { + addGraphic(graphic: RenderGraphic): void; + // (undocumented) + changeDynamics(): void; +} + +// @public +module EditManipulator { + // (undocumented) + enum EventType { + // (undocumented) + Accept = 2, + // (undocumented) + Cancel = 1, + // (undocumented) + Synch = 0 + } + + // (undocumented) + class HandleProvider { + constructor(iModel: IModelConnection); + // (undocumented) + protected _isActive: boolean; + // (undocumented) + protected _removeDecorationListener?: () => void; + // (undocumented) + protected _removeManipulatorToolListener?: () => void; + // (undocumented) + protected _removeSelectionListener?: () => void; + // (undocumented) + protected clearControls(): void; + protected abstract createControls(): Promise; + // (undocumented) + decorate(_context: DecorateContext): void; + // (undocumented) + iModel: IModelConnection; + protected abstract modifyControls(_hit: HitDetail, _ev: BeButtonEvent): boolean; + // (undocumented) + onDecorationButtonEvent(hit: HitDetail, ev: BeButtonEvent): Promise; + // (undocumented) + protected onDoubleClick(_hit: HitDetail, _ev: BeButtonEvent): Promise; + // WARNING: The type "EventType" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + onManipulatorEvent(_eventType: EventType): void; + // (undocumented) + onManipulatorToolEvent(_tool: Tool, event: ManipulatorToolEvent): void; + // (undocumented) + protected onRightClick(_hit: HitDetail, _ev: BeButtonEvent): Promise; + // (undocumented) + onSelectionChanged(iModel: IModelConnection, _eventType: SelectEventType, _ids?: Set): void; + // (undocumented) + protected stop(): void; + // (undocumented) + protected updateControls(): Promise; + // (undocumented) + protected updateDecorationListener(add: boolean): void; + } + + // (undocumented) + class HandleTool extends InputCollector { + // WARNING: The type "HandleProvider" needs to be exported by the package (e.g. added to index.ts) + constructor(manipulator: HandleProvider); + // (undocumented) + protected abstract accept(_ev: BeButtonEvent): boolean; + // (undocumented) + protected cancel(_ev: BeButtonEvent): boolean; + // (undocumented) + static hidden: boolean; + protected init(): void; + // WARNING: The type "HandleProvider" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + manipulator: HandleProvider; + // (undocumented) + onDataButtonDown(ev: BeButtonEvent): Promise; + // (undocumented) + onPostInstall(): void; + // (undocumented) + onResetButtonUp(ev: BeButtonEvent): Promise; + // (undocumented) + onTouchCancel(ev: BeTouchEvent): Promise; + // (undocumented) + onTouchComplete(ev: BeTouchEvent): Promise; + // (undocumented) + onTouchMove(ev: BeTouchEvent): Promise; + // (undocumented) + static toolId: string; + } + + // (undocumented) + class HandleUtils { + static getArrowShape(baseStart?: number, baseWidth?: number, tipStart?: number, tipEnd?: number, tipWidth?: number, flangeStart?: number, flangeWidth?: number): Point3d[]; + static getArrowTransform(vp: Viewport, base: Point3d, direction: Vector3d, sizeInches: number): Transform | undefined; + // (undocumented) + static getBoresite(origin: Point3d, vp: Viewport, checkAccuDraw?: boolean, checkACS?: boolean): Ray3d; + // (undocumented) + static projectPointToLineInView(spacePt: Point3d, linePt: Point3d, lineDirection: Vector3d, vp: Viewport, checkAccuDraw?: boolean, checkACS?: boolean): Point3d | undefined; + // (undocumented) + static projectPointToPlaneInView(spacePt: Point3d, planePt: Point3d, planeNormal: Vector3d, vp: Viewport, checkAccuDraw?: boolean, checkACS?: boolean): Point3d | undefined; + } + +} + +// @public +interface EditorPosition { + // (undocumented) + columnPriority: number; + columnSpan?: number; + horizontalAlignment?: TsHorizontalAlignment; + // (undocumented) + rowPriority: number; + rowSpan?: number; + verticalAlignment?: TsVerticalAlignment; +} + +// @public (undocumented) +class ElementAgenda { + constructor(iModel: IModelConnection); + add(arg: Id64Arg): boolean; + clear(): void; + // (undocumented) + readonly count: number; + // (undocumented) + readonly elements: string[]; + find(id: Id64String): boolean; + // WARNING: The type "ModifyElementSource.Unknown" needs to be exported by the package (e.g. added to index.ts) + getSource(): ModifyElementSource.Unknown | ModifyElementSource; + // (undocumented) + readonly groupMarks: GroupMark[]; + // (undocumented) + has(id: string): boolean; + hilite(): void; + // (undocumented) + hilitedState: HilitedState; + hiliteOnAdd: boolean; + // (undocumented) + iModel: IModelConnection; + invert(arg: Id64Arg): boolean; + // (undocumented) + readonly isEmpty: boolean; + // (undocumented) + readonly length: number; + popGroup(): void; + // (undocumented) + remove(arg: Id64Arg): boolean; + setSource(val: ModifyElementSource): void; +} + +// @public (undocumented) +class ElementLocateManager { + // (undocumented) + readonly apertureInches: number; + // (undocumented) + clear(): void; + // (undocumented) + currHit?: HitDetail; + // (undocumented) + doLocate(response: LocateResponse, newSearch: boolean, testPoint: Point3d, view: ScreenViewport | undefined, source: InputSource, filterHits?: boolean): Promise; + // (undocumented) + filterHit(hit: HitDetail, _action: LocateAction, out: LocateResponse): Promise; + static getFailureMessageKey(key: string): string; + // (undocumented) + getNextHit(): HitDetail | undefined; + getPreLocatedHit(): HitDetail | undefined; + // (undocumented) + hitList?: HitList; + // (undocumented) + initLocateOptions(): void; + // (undocumented) + initToolLocate(): void; + // (undocumented) + onInitialized(): void; + // (undocumented) + readonly options: LocateOptions; + // (undocumented) + readonly picker: ElementPicker; + // (undocumented) + setCurrHit(hit?: HitDetail): void; + // (undocumented) + setHitList(list?: HitList): void; + // (undocumented) + readonly touchApertureInches: number; +} + +// @public (undocumented) +class ElementPicker { + doPick(vp: ScreenViewport, pickPointWorld: Point3d, pickRadiusView: number, options: LocateOptions): number; + // (undocumented) + empty(): void; + getHit(i: number): HitDetail | undefined; + getHitList(takeOwnership: boolean): HitList; + // (undocumented) + getNextHit(): HitDetail | undefined; + // (undocumented) + hitList?: HitList; + // (undocumented) + readonly pickPointWorld: Point3d; + // (undocumented) + resetCurrentHit(): void; + // (undocumented) + testHit(hit: HitDetail, vp: ScreenViewport, pickPointWorld: Point3d, pickRadiusView: number, options: LocateOptions): boolean; + // (undocumented) + viewport?: Viewport; +} + +// @public +class ElementState extends EntityState, implements ElementProps { + constructor(props: ElementProps, iModel: IModelConnection); + // (undocumented) + readonly code: Code; + // (undocumented) + readonly federationGuid?: GuidString; + // (undocumented) + readonly model: Id64String; + // (undocumented) + readonly parent?: RelatedElement; + // (undocumented) + toJSON(): ElementProps; + // (undocumented) + readonly userLabel?: string; +} + +// @public +enum ElemMethod { + Add = 0, + Invert = 1 +} + +// @public +enum ElemSource { + Fence = 1, + Pick = 0, + SelectionSet = 2 +} + +// @public +class EmphasizeElements implements FeatureOverrideProvider { + // WARNING: The type "FeatureSymbology.Overrides" needs to be exported by the package (e.g. added to index.ts) + addFeatureOverrides(overrides: FeatureSymbology.Overrides, vp: Viewport): void; + static clear(vp: Viewport, inactiveOnly?: boolean): void; + clearAlwaysDrawnElements(vp: Viewport): boolean; + clearEmphasizedElements(vp: Viewport): boolean; + clearHiddenElements(vp: Viewport): boolean; + clearIsolatedElements(vp: Viewport): boolean; + clearNeverDrawnElements(vp: Viewport): boolean; + clearOverriddenElements(vp: Viewport, key?: number): boolean; + // WARNING: The type "FeatureSymbology.Appearance" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected createAppearanceFromKey(key: number): FeatureSymbology.Appearance; + // WARNING: The type "FeatureSymbology.Appearance" needs to be exported by the package (e.g. added to index.ts) + createDefaultAppearance(): FeatureSymbology.Appearance; + // (undocumented) + protected createOverrideKey(color: ColorDef, override: FeatureOverrideType): number | undefined; + // WARNING: The type "FeatureSymbology.Appearance" needs to be exported by the package (e.g. added to index.ts) + readonly defaultAppearance: FeatureSymbology.Appearance | undefined; + // WARNING: The type "FeatureSymbology.Appearance" needs to be exported by the package (e.g. added to index.ts) + emphasizeElements(ids: Id64Arg, vp: Viewport, defaultAppearance?: FeatureSymbology.Appearance, replace?: boolean): boolean; + // WARNING: The type "FeatureSymbology.Appearance" needs to be exported by the package (e.g. added to index.ts) + emphasizeSelectedElements(vp: Viewport, defaultAppearance?: FeatureSymbology.Appearance, replace?: boolean, clearSelection?: boolean): boolean; + // (undocumented) + fromJSON(props: EmphasizeElementsProps, vp: Viewport): boolean; + static get(vp: Viewport): EmphasizeElements | undefined; + getAlwaysDrawnElements(vp: Viewport): Id64Set | undefined; + getEmphasizedElements(vp: Viewport): Id64Set | undefined; + getHiddenElements(vp: Viewport): Id64Set | undefined; + getIsolatedElements(vp: Viewport): Id64Set | undefined; + getNeverDrawnElements(vp: Viewport): Id64Set | undefined; + static getOrCreate(vp: Viewport): EmphasizeElements; + getOverriddenElements(): Map | undefined; + getOverriddenElementsByKey(key: number): Id64Set | undefined; + getOverrideFromKey(key: number, color: ColorDef): FeatureOverrideType; + hideElements(ids: Id64Arg, vp: Viewport, replace?: boolean): boolean; + hideSelectedElements(vp: Viewport, replace?: boolean, clearSelection?: boolean): boolean; + isActive(vp: Viewport): boolean; + isolateElements(ids: Id64Arg, vp: Viewport, replace?: boolean): boolean; + isolateSelectedElements(vp: Viewport, replace?: boolean, clearSelection?: boolean): boolean; + overrideElements(ids: Id64Arg, vp: Viewport, color: ColorDef, override?: FeatureOverrideType, replace?: boolean): boolean; + overrideSelectedElements(vp: Viewport, color: ColorDef, override?: FeatureOverrideType, replace?: boolean, clearSelection?: boolean): boolean; + setAlwaysDrawnElements(ids: Id64Arg, vp: Viewport, exclusive?: boolean, replace?: boolean): boolean; + setNeverDrawnElements(ids: Id64Arg, vp: Viewport, replace?: boolean): boolean; + // (undocumented) + toJSON(vp: Viewport): EmphasizeElementsProps; +} + +// @public (undocumented) +interface EmphasizeElementsProps { + // (undocumented) + alwaysDrawn?: Id64Set; + // (undocumented) + appearanceOverride?: AppearanceOverrideProps[]; + // (undocumented) + defaultAppearance?: FeatureSymbology.AppearanceProps; + // (undocumented) + isAlwaysDrawnExclusive?: boolean; + // (undocumented) + neverDrawn?: Id64Set; +} + +// @public +class EntityState implements EntityProps { + constructor(props: EntityProps, iModel: IModelConnection, _state?: EntityState); + // (undocumented) + readonly classFullName: string; + static readonly className: string; + clone(iModel?: IModelConnection): this; + // (undocumented) + equals(other: this): boolean; + static getClassFullName(): string; + // (undocumented) + readonly id: Id64String; + // (undocumented) + readonly iModel: IModelConnection; + // (undocumented) + readonly jsonProperties: { + [key: string]: any; + } + // (undocumented) + static schemaName: string; + // (undocumented) + static readonly sqlName: string; + // (undocumented) + toJSON(): EntityProps; +} + +// @public +interface EnumerationChoice { + // (undocumented) + label: string; + // (undocumented) + value: string | number; +} + +// @public +interface EnumerationChoicesInfo { + // (undocumented) + choices: EnumerationChoice[]; + // (undocumented) + isStrict?: boolean; + // (undocumented) + maxDisplayedRows?: number; +} + +// @public +class Environment implements EnvironmentProps { + constructor(json?: EnvironmentProps); + // (undocumented) + readonly ground: GroundPlane; + // (undocumented) + readonly sky: SkyBox; + // (undocumented) + toJSON(): EnvironmentProps; +} + +// @public +enum ErrorNums { + NoFence = 0, + NoFenceElems = 1, + NoFenceElemsOutside = 2, + NoSSElems = 3, + NotSupportedElmType = 4 +} + +// @public +class EventController { + constructor(vp: ScreenViewport); + // (undocumented) + destroy(): void; + // (undocumented) + vp: ScreenViewport; +} + +// @public (undocumented) +enum EventHandled { + // (undocumented) + No = 0, + // (undocumented) + Yes = 1 +} + +// @public +export function extractImageSourceDimensions(source: ImageSource): Promise; + +// @public +interface FeatureOverrideProvider { + // WARNING: The type "FeatureSymbology.Overrides" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + addFeatureOverrides(overrides: FeatureSymbology.Overrides, viewport: Viewport): void; +} + +// @public +enum FeatureOverrideType { + // (undocumented) + AlphaOnly = 1, + // (undocumented) + ColorAndAlpha = 2, + // (undocumented) + ColorOnly = 0 +} + +// @public +module FeatureSymbology { + class Appearance implements AppearanceProps { + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + static readonly defaults: Appearance; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + equals(other: Appearance): boolean; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + extendAppearance(base: Appearance): Appearance; + // WARNING: The type "AppearanceProps" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + static fromJSON(props?: AppearanceProps): Appearance; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + static fromRgb(color: ColorDef): Appearance; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + static fromRgba(color: ColorDef): Appearance; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + static fromSubCategoryOverride(ovr: SubCategoryOverride): Appearance; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + static fromTransparency(transparencyValue: number): Appearance; + readonly ignoresMaterial?: true | undefined; + // (undocumented) + readonly isFullyTransparent: boolean; + readonly linePixels?: LinePixels; + readonly nonLocatable?: true | undefined; + // (undocumented) + readonly overridesLinePixels: boolean; + // (undocumented) + readonly overridesRgb: boolean; + // (undocumented) + readonly overridesSymbology: boolean; + // (undocumented) + readonly overridesTransparency: boolean; + // (undocumented) + readonly overridesWeight: boolean; + readonly rgb?: RgbColor; + // WARNING: The type "AppearanceProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + toJSON(): AppearanceProps; + readonly transparency?: number; + readonly weight?: number; + } + + interface AppearanceProps { + ignoresMaterial?: true | undefined; + linePixels?: LinePixels; + nonLocatable?: true | undefined; + rgb?: RgbColor; + transparency?: number; + weight?: number; + } + + class Overrides { + constructor(view?: ViewState); + protected readonly _alwaysDrawn: Id64.Uint32Set; + protected _constructions: boolean; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + protected _defaultOverrides: Appearance; + protected _dimensions: boolean; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + protected readonly _elementOverrides: Id64.Uint32Map; + protected _lineWeights: boolean; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + protected readonly _modelOverrides: Id64.Uint32Map; + protected readonly _neverDrawn: Id64.Uint32Set; + protected _patterns: boolean; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + protected readonly _subCategoryOverrides: Id64.Uint32Map; + protected readonly _visibleSubCategories: Id64.Uint32Set; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + readonly animationNodeOverrides: Map; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + readonly defaultOverrides: Appearance; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + getAppearance(elemLo: number, elemHi: number, subcatLo: number, subcatHi: number, geomClass: GeometryClass, modelLo: number, modelHi: number, type: BatchType, animationNodeId: number): Appearance | undefined; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + protected getClassifierAppearance(elemLo: number, elemHi: number, subcatLo: number, subcatHi: number, modelLo: number, modelHi: number): Appearance | undefined; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected getElementOverrides(idLo: number, idHi: number, animationNodeId: number): Appearance | undefined; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + getElementOverridesById(id: Id64String): Appearance | undefined; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + getFeatureAppearance(feature: Feature, modelId: Id64String, type?: BatchType): Appearance | undefined; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected getModelOverrides(idLo: number, idHi: number): Appearance | undefined; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + getModelOverridesById(id: Id64String): Appearance | undefined; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected getSubCategoryOverrides(idLo: number, idHi: number): Appearance | undefined; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + getSubCategoryOverridesById(id: Id64String): Appearance | undefined; + initFromView(view: ViewState): void; + // (undocumented) + protected isAlwaysDrawn(idLo: number, idHi: number): boolean; + isAlwaysDrawnExclusive: boolean; + // (undocumented) + isClassVisible(geomClass: GeometryClass): boolean; + isFeatureVisible(feature: Feature): boolean; + // (undocumented) + protected isNeverDrawn(elemIdLo: number, elemIdHi: number, animationNodeId: number): boolean; + isSubCategoryIdVisible(id: Id64String): boolean; + protected isSubCategoryVisible(idLo: number, idHi: number): boolean; + readonly lineWeights: boolean; + readonly neverDrawnAnimationNodes: Set; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + overrideAnimationNode(id: number, app: Appearance): void; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + overrideElement(id: Id64String, app: Appearance, replaceExisting?: boolean): void; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + overrideModel(id: Id64String, app: Appearance, replaceExisting?: boolean): void; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + overrideSubCategory(id: Id64String, app: Appearance, replaceExisting?: boolean): void; + setAlwaysDrawn(id: Id64String): void; + setAlwaysDrawnSet(ids: Id64Set, exclusive: boolean): void; + setAnimationNodeNeverDrawn(id: number): void; + // WARNING: The type "Appearance" needs to be exported by the package (e.g. added to index.ts) + setDefaultOverrides(appearance: Appearance, replaceExisting?: boolean): void; + setNeverDrawn(id: Id64String): void; + setNeverDrawnSet(ids: Id64Set): void; + setVisibleSubCategory(id: Id64String): void; + } + +} + +// @public +enum FenceClipMode { + Copy = 3, + None = 0, + Original = 1 +} + +// @public +class FenceParams { + // (undocumented) + clip?: ClipVector; + // (undocumented) + clipMode: FenceClipMode; + // (undocumented) + readonly fenceRangeNPC: Range3d; + // (undocumented) + hasOverlaps: boolean; + // (undocumented) + onTolerance: number; + // (undocumented) + overlapMode: boolean; + // (undocumented) + viewport?: Viewport; +} + +// @public +class FitViewTool extends ViewTool { + constructor(viewport: ScreenViewport, oneShot: boolean, doAnimate?: boolean); + // (undocumented) + doAnimate: boolean; + // (undocumented) + doFit(viewport: ScreenViewport, oneShot: boolean, doAnimate?: boolean): boolean; + // (undocumented) + onDataButtonDown(_ev: BeButtonEvent): Promise; + // (undocumented) + oneShot: boolean; + // (undocumented) + onPostInstall(): void; + // (undocumented) + static toolId: string; +} + +// @public (undocumented) +class Flags { + // (undocumented) + animateRotation: boolean; + // (undocumented) + auxRotationPlane: RotationMode; + // (undocumented) + baseMode: number; + // (undocumented) + baseRotation: RotationMode; + // (undocumented) + bearingFixToPlane2D: boolean; + // (undocumented) + contextRotMode: number; + // (undocumented) + dialogNeedsUpdate: boolean; + // (undocumented) + fixedOrg: boolean; + // (undocumented) + haveValidOrigin: boolean; + // (undocumented) + ignoreDataButton: boolean; + // (undocumented) + inDataPoint: boolean; + // (undocumented) + indexLocked: boolean; + // (undocumented) + lockedRotation: boolean; + // (undocumented) + pointIsOnPlane: boolean; + // (undocumented) + redrawCompass: boolean; + // (undocumented) + rotationNeedsUpdate: boolean; + // (undocumented) + softAngleLock: boolean; +} + +// @public +class FlyViewTool extends ViewManip { + constructor(vp: ScreenViewport, oneShot?: boolean, isDraggingRequired?: boolean); + // (undocumented) + onReinitialize(): void; + // (undocumented) + static toolId: string; +} + +// @public (undocumented) +export function fromSumOf(p: Point3d, v: Vector3d, scale: number, out?: Point3d): Point3d; + +// @public +class FrustumUniforms { + constructor(); + // (undocumented) + readonly farPlane: number; + // (undocumented) + readonly frustum: Float32Array; + // (undocumented) + readonly frustumPlanes: Float32Array; + // (undocumented) + readonly is2d: boolean; + // (undocumented) + readonly nearPlane: number; + // (undocumented) + setFrustum(nearPlane: number, farPlane: number, type: FrustumUniformType): void; + // (undocumented) + setPlanes(top: number, bottom: number, left: number, right: number): void; + // (undocumented) + readonly type: FrustumUniformType; +} + +// @public (undocumented) +enum FrustumUniformType { + // (undocumented) + Orthographic = 1, + // (undocumented) + Perspective = 2, + // (undocumented) + TwoDee = 0 +} + +// @public (undocumented) +class FuzzySearch { + onGetMultiWordSearchOptions(): Fuse.FuseOptions; + onGetSingleWordSearchOptions(): Fuse.FuseOptions; + search(searchedObjects: T[], keys: Array, pattern: string): FuzzySearchResults; +} + +// @public +interface FuzzySearchResult { + getBoldMask(): boolean[]; + getMatchedKey(): string; + getMatchedValue(): string; + getResult(): T; +} + +// @public +class FuzzySearchResults implements Iterable { + // WARNING: The name "__@iterator" contains unsupported characters; API names should use only letters, numbers, and underscores + // (undocumented) + [Symbol.iterator](): any; + constructor(results: any[] | undefined); + // (undocumented) + getResult(resultIndex: number): FuzzySearchResult | undefined; + // (undocumented) + readonly length: number; + // (undocumented) + results: any[]; +} + +// @public (undocumented) +class GeoConverter { + constructor(iModel: IModelConnection, datum: string); + // (undocumented) + getGeoCoordinatesFromIModelCoordinates(iModelPoints: XYZProps[]): Promise; + // (undocumented) + getIModelCoordinatesFromGeoCoordinates(geoPoints: XYZProps[]): Promise; +} + +// @public +class GeometricModel2dState extends GeometricModelState, implements GeometricModel2dProps { + constructor(props: GeometricModel2dProps, iModel: IModelConnection); + // (undocumented) + readonly asGeometricModel2d: GeometricModel2dState; + // (undocumented) + readonly globalOrigin: Point2d; + readonly is3d: boolean; + // (undocumented) + toJSON(): GeometricModel2dProps; +} + +// @public +class GeometricModel3dState extends GeometricModelState { + // (undocumented) + readonly asGeometricModel3d: GeometricModel3dState; + readonly is3d: boolean; +} + +// @public +class GeometricModelState extends ModelState, implements TileTreeModelState { + // (undocumented) + protected _classifierTileTreeState: TileTreeState; + // (undocumented) + protected _tileTreeState: TileTreeState; + // (undocumented) + readonly asGeometricModel: GeometricModelState; + // (undocumented) + readonly classifierTileTree: TileTree | undefined; + // (undocumented) + getOrLoadTileTree(edgesRequired: boolean): TileTree | undefined; + readonly is2d: boolean; + readonly is3d: boolean; + // (undocumented) + readonly isGeometricModel: boolean; + // WARNING: The type "TileTree.LoadStatus" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + loadStatus: TileTree.LoadStatus; + // WARNING: The type "TileTree.LoadStatus" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + loadTileTree(edgesRequired: boolean, animationId?: Id64String, asClassifier?: boolean, classifierExpansion?: number): TileTree.LoadStatus; + // (undocumented) + onIModelConnectionClose(): void; + // (undocumented) + readonly tileTree: TileTree | undefined; + // (undocumented) + readonly treeModelId: Id64String; +} + +// @public +class GeoServices { + constructor(iModel: IModelConnection); + // (undocumented) + getConverter(datum?: string): GeoConverter; +} + +// @public +export function getImageSourceFormatForMimeType(mimeType: string): ImageSourceFormat | undefined; + +// @public +export function getImageSourceMimeType(format: ImageSourceFormat): string; + +// @public +class GraphicBranch implements IDisposable, RenderMemory.Consumer { + constructor(ownsEntries?: boolean); + // (undocumented) + add(graphic: RenderGraphic): void; + animationId?: string; + clear(): void; + // WARNING: The type "RenderMemory.Statistics" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + collectStatistics(stats: RenderMemory.Statistics): void; + // (undocumented) + dispose(): void; + readonly entries: RenderGraphic[]; + // (undocumented) + getViewFlags(flags: ViewFlags, out?: ViewFlags): ViewFlags; + // (undocumented) + readonly isEmpty: boolean; + readonly ownsEntries: boolean; + // (undocumented) + setViewFlagOverrides(ovr: ViewFlag.Overrides): void; + // (undocumented) + setViewFlags(flags: ViewFlags): void; + // WARNING: The type "FeatureSymbology.Overrides" needs to be exported by the package (e.g. added to index.ts) + symbologyOverrides?: FeatureSymbology.Overrides; +} + +// @public +class GraphicBuilder { + protected constructor(placement: Transform | undefined, type: GraphicType, viewport: Viewport, pickId?: Id64String); + abstract activateGraphicParams(graphicParams: GraphicParams): void; + abstract addArc(arc: Arc3d, isEllipse: boolean, filled: boolean): void; + abstract addArc2d(ellipse: Arc3d, isEllipse: boolean, filled: boolean, zDepth: number): void; + abstract addLineString(points: Point3d[]): void; + abstract addLineString2d(points: Point2d[], zDepth: number): void; + abstract addLoop(loop: Loop): void; + abstract addPath(path: Path): void; + abstract addPointString(points: Point3d[]): void; + abstract addPointString2d(points: Point2d[], zDepth: number): void; + abstract addPolyface(meshData: Polyface, filled: boolean): void; + addRangeBox(range: Range3d): void; + abstract addShape(points: Point3d[]): void; + abstract addShape2d(points: Point2d[], zDepth: number): void; + convertToLineStringParams(...lines: Array<[number, Point3d[]]>): Array<{ + numPoints: number; + points: Point3d[]; + }>; + abstract finish(): RenderGraphic; + // (undocumented) + readonly iModel: IModelConnection; + // (undocumented) + readonly isOverlay: boolean; + // (undocumented) + readonly isSceneGraphic: boolean; + // (undocumented) + readonly isViewBackground: boolean; + // (undocumented) + readonly isViewCoordinates: boolean; + // (undocumented) + readonly isWorldCoordinates: boolean; + // (undocumented) + pickId?: string; + placement: Transform; + setBlankingFill(fillColor: ColorDef): void; + setSymbology(lineColor: ColorDef, fillColor: ColorDef, lineWidth: number, linePixels?: LinePixels): void; + readonly type: GraphicType; + readonly viewport: Viewport; +} + +// @public +enum GraphicType { + Scene = 1, + ViewBackground = 0, + ViewOverlay = 4, + WorldDecoration = 2, + WorldOverlay = 3 +} + +// @public +enum GridOrientationType { + AuxCoord = 4, + View = 0, + // (undocumented) + WorldXY = 1, + // (undocumented) + WorldXZ = 3, + // (undocumented) + WorldYZ = 2 +} + +// @public (undocumented) +interface GroupMark { + // (undocumented) + source: ModifyElementSource; + // (undocumented) + start: number; +} + +// @public +class HilitedSet { + constructor(iModel: IModelConnection); + clearAll(): void; + readonly elements: Set; + has(id: string): boolean; + // (undocumented) + iModel: IModelConnection; + isHilited(id: Id64String): boolean; + setHilite(arg: Id64Arg, onOff: boolean): void; + readonly size: number; +} + +// @public (undocumented) +enum HilitedState { + No = 2, + Unknown = 0, + Yes = 1 +} + +// @public +class HitDetail { + constructor(testPoint: Point3d, viewport: ScreenViewport, hitSource: HitSource, hitPoint: Point3d, sourceId: string, priority: HitPriority, distXY: number, distFraction: number, subCategoryId?: string | undefined, geometryClass?: GeometryClass | undefined); + clone(): HitDetail; + // (undocumented) + readonly distFraction: number; + // (undocumented) + readonly distXY: number; + draw(_context: DecorateContext): void; + // (undocumented) + readonly geometryClass?: GeometryClass | undefined; + getHitType(): HitDetailType; + getPoint(): Point3d; + getToolTip(): Promise; + // (undocumented) + readonly hitPoint: Point3d; + // (undocumented) + readonly hitSource: HitSource; + readonly isElementHit: boolean; + // (undocumented) + readonly isModelHit: boolean; + isSameHit(otherHit?: HitDetail): boolean; + // (undocumented) + readonly priority: HitPriority; + // (undocumented) + readonly sourceId: string; + // (undocumented) + readonly subCategoryId?: string | undefined; + // (undocumented) + readonly testPoint: Point3d; + // (undocumented) + readonly viewport: ScreenViewport; +} + +// @public (undocumented) +enum HitDetailType { + // (undocumented) + Hit = 1, + // (undocumented) + Intersection = 3, + // (undocumented) + Snap = 2 +} + +// @public +enum HitGeomType { + // (undocumented) + Arc = 4, + // (undocumented) + Curve = 3, + // (undocumented) + None = 0, + // (undocumented) + Point = 1, + // (undocumented) + Segment = 2, + // (undocumented) + Surface = 5 +} + +// @public +class HitList { + addHit(newHit: T): number; + compare(hit1: HitDetail | undefined, hit2: HitDetail | undefined): -1 | 1 | 0; + // (undocumented) + currHit: number; + // (undocumented) + dropNulls(): void; + // (undocumented) + empty(): void; + // (undocumented) + getCurrentHit(): T | undefined; + getHit(hitNum: number): T | undefined; + // (undocumented) + getNextHit(): T | undefined; + // (undocumented) + hits: T[]; + insertHit(i: number, hit: T): void; + // (undocumented) + readonly length: number; + removeCurrentHit(): void; + removeHit(hitNum: number): void; + removeHitsFrom(sourceId: string): boolean; + // (undocumented) + resetCurrentHit(): void; + // (undocumented) + setCurrentHit(hit: T): void; + setHit(i: number, p: T | undefined): void; +} + +// @public (undocumented) +interface HitListHolder { + // (undocumented) + setHitList(list: HitList | undefined): void; +} + +// @public +enum HitParentGeomType { + // (undocumented) + Mesh = 4, + // (undocumented) + None = 0, + // (undocumented) + Sheet = 2, + // (undocumented) + Solid = 3, + // (undocumented) + Text = 5, + // (undocumented) + Wire = 1 +} + +// @public (undocumented) +enum HitPriority { + // (undocumented) + NonPlanarEdge = 2, + // (undocumented) + NonPlanarSurface = 5, + // (undocumented) + PlanarEdge = 1, + // (undocumented) + PlanarSurface = 4, + // (undocumented) + SilhouetteEdge = 3, + // (undocumented) + Unknown = 6, + // (undocumented) + WireEdge = 0 +} + +// @public +enum HitSource { + // (undocumented) + AccuSnap = 3, + // (undocumented) + Application = 6, + // (undocumented) + DataPoint = 5, + // (undocumented) + EditAction = 7, + // (undocumented) + EditActionSS = 8, + // (undocumented) + FromUser = 1, + // (undocumented) + MotionLocate = 2, + // (undocumented) + None = 0, + // (undocumented) + TentativeSnap = 4 +} + +// @public +interface IconDefinition { + iconClass: string; + // (undocumented) + isEnabledFunction?: () => boolean; +} + +// @public +interface IconEditorParams extends BasePropertyEditorParams { + // (undocumented) + definition: IconDefinition; + // (undocumented) + type: PropertyEditorParamTypes.Icon; +} + +// @public +class IconSprites { + static emptyAll(): void; + static getSpriteFromUrl(spriteUrl: string): Sprite; +} + +// @public +class IdleTool extends InteractiveTool { + // (undocumented) + exitTool(): void; + // (undocumented) + static hidden: boolean; + // (undocumented) + onMiddleButtonUp(ev: BeButtonEvent): Promise; + // (undocumented) + onMouseStartDrag(ev: BeButtonEvent): Promise; + // (undocumented) + onMouseWheel(ev: BeWheelEvent): Promise; + // (undocumented) + onTouchMoveStart(ev: BeTouchEvent, startEv: BeTouchEvent): Promise; + // (undocumented) + onTouchTap(ev: BeTouchEvent): Promise; + // (undocumented) + run(): boolean; + // (undocumented) + static toolId: string; +} + +// @public +export function imageBufferToBase64EncodedPng(buffer: ImageBuffer): string | undefined; + +// @public +export function imageBufferToPngDataUrl(buffer: ImageBuffer): string | undefined; + +// @public +export function imageElementFromImageSource(source: ImageSource): Promise; + +// @public (undocumented) +export function imageElementFromUrl(url: string): Promise; + +// @public +class IModelApp { + // (undocumented) + protected static _imodelClient?: IModelClient; + // (undocumented) + protected static _initialized: boolean; + static accessToken?: AccessToken; + static accuDraw: AccuDraw; + static accuSnap: AccuSnap; + static applicationId: string; + // (undocumented) + static readonly features: FeatureGates; + // (undocumented) + static readonly hasRenderSystem: boolean; + static i18n: I18N; + // (undocumented) + static iModelClient: IModelClient; + // (undocumented) + static readonly initialized: boolean; + // (undocumented) + static locateManager: ElementLocateManager; + static notifications: NotificationManager; + protected static onStartup(): void; + static quantityFormatter: QuantityFormatter; + static readonly renderSystem: RenderSystem; + static sessionId: GuidString; + static settings: SettingsAdmin; + static shutdown(): void; + static startup(imodelClient?: IModelClient): void; + protected static supplyI18NOptions(): I18NOptions | undefined; + protected static supplyRenderSystem(): RenderSystem; + // (undocumented) + static tentativePoint: TentativePoint; + static tileAdmin: TileAdmin; + static toolAdmin: ToolAdmin; + static readonly tools: ToolRegistry; + static viewManager: ViewManager; +} + +// @public (undocumented) +class IModelConnection { +} + +// @public (undocumented) +class IModelTileLoader extends TileLoader { + constructor(iModel: IModelConnection, batchType: BatchType, edgesRequired?: boolean); + // (undocumented) + protected readonly _batchType: BatchType; + // (undocumented) + protected readonly _loadEdges: boolean; + // (undocumented) + protected static _viewFlagOverrides: ViewFlag.Overrides; + // (undocumented) + getChildrenProps(parent: Tile): Promise; + // (undocumented) + readonly maxDepth: number; + // WARNING: The type "Tile.LoadPriority" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly priority: Tile.LoadPriority; + // WARNING: The type "TileRequest.Response" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + requestTileContent(tile: Tile): Promise; + // WARNING: The type "Tile.Params" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + tileRequiresLoading(params: Tile.Params): boolean; + // (undocumented) + readonly viewFlagOverrides: ViewFlag.Overrides; +} + +// @public +class InputCollector extends InteractiveTool { + // (undocumented) + exitTool(): void; + // (undocumented) + onResetButtonUp(_ev: BeButtonEvent): Promise; + // (undocumented) + run(): boolean; +} + +// @public +enum InputSource { + Mouse = 1, + Touch = 2, + Unknown = 0 +} + +// @public +interface InstancedGraphicParams { + readonly count: number; + readonly featureIds?: Uint8Array; + readonly transforms: Float32Array; +} + +// @public +class InteractiveTool extends Tool { + applyToolSettingPropertyChange(_updatedValue: ToolSettingsPropertySyncItem): boolean; + beginDynamics(): void; + changeLocateState(enableLocate: boolean, enableSnap?: boolean, cursor?: string, coordLockOvr?: CoordinateLockOverrides): void; + decorate(_context: DecorateContext): void; + decorateSuspended(_context: DecorateContext): void; + endDynamics(): void; + // (undocumented) + abstract exitTool(): void; + filterHit(_hit: HitDetail, _out?: LocateResponse): Promise; + getCurrentButtonEvent(ev: BeButtonEvent): void; + getDecorationGeometry(_hit: HitDetail): GeometryStreamProps | undefined; + getToolTip(_hit: HitDetail): Promise; + initLocateElements(enableLocate?: boolean, enableSnap?: boolean, cursor?: string, coordLockOvr?: CoordinateLockOverrides): void; + // (undocumented) + isCompatibleViewport(_vp: Viewport, _isSelectedViewChange: boolean): boolean; + readonly isDynamicsStarted: boolean; + // (undocumented) + isValidLocation(_ev: BeButtonEvent, _isButtonEvent: boolean): boolean; + onCleanup(): void; + onDataButtonDown(_ev: BeButtonEvent): Promise; + onDataButtonUp(_ev: BeButtonEvent): Promise; + onDynamicFrame(_ev: BeButtonEvent, _context: DynamicsContext): void; + onInstall(): boolean; + onKeyTransition(_wentDown: boolean, _keyEvent: KeyboardEvent): Promise; + onMiddleButtonDown(_ev: BeButtonEvent): Promise; + onMiddleButtonUp(_ev: BeButtonEvent): Promise; + onModifierKeyTransition(_wentDown: boolean, _modifier: BeModifierKeys, _event: KeyboardEvent): Promise; + onMouseEndDrag(ev: BeButtonEvent): Promise; + onMouseMotion(_ev: BeButtonEvent): Promise; + onMouseMotionStopped(_ev: BeButtonEvent): Promise; + onMouseNoMotion(_ev: BeButtonEvent): Promise; + onMouseStartDrag(_ev: BeButtonEvent): Promise; + onMouseWheel(_ev: BeWheelEvent): Promise; + onPostInstall(): void; + onReinitialize(): void; + onResetButtonDown(_ev: BeButtonEvent): Promise; + onResetButtonUp(_ev: BeButtonEvent): Promise; + onSelectedViewportChanged(_previous: Viewport | undefined, _current: Viewport | undefined): void; + onSuspend(): void; + onTouchCancel(_ev: BeTouchEvent): Promise; + onTouchComplete(_ev: BeTouchEvent): Promise; + onTouchEnd(_ev: BeTouchEvent): Promise; + onTouchMove(_ev: BeTouchEvent): Promise; + onTouchMoveStart(_ev: BeTouchEvent, _startEv: BeTouchEvent): Promise; + onTouchStart(_ev: BeTouchEvent): Promise; + onTouchTap(_ev: BeTouchEvent): Promise; + onUnsuspend(): void; + receivedDownEvent: boolean; + supplyToolSettingsProperties(): ToolSettingsPropertyRecord[] | undefined; + syncToolSettingsProperties(syncData: ToolSettingsPropertySyncItem[]): void; + testDecorationHit(_id: string): boolean; +} + +// @public (undocumented) +class IntersectDetail extends SnapDetail { + constructor(from: SnapDetail, heat: SnapHeat | undefined, snapPoint: XYZProps, otherPrimitive: CurvePrimitive, otherId: string); + // (undocumented) + draw(context: DecorateContext): void; + // (undocumented) + readonly otherId: string; + // (undocumented) + readonly otherPrimitive: CurvePrimitive; +} + +// @public (undocumented) +enum ItemField { + // (undocumented) + ANGLE_Item = 1, + // (undocumented) + DIST_Item = 0, + // (undocumented) + X_Item = 2, + // (undocumented) + Y_Item = 3, + // (undocumented) + Z_Item = 4 +} + +// @public +interface JsonEditorParams extends BasePropertyEditorParams { + // (undocumented) + json: any; + // (undocumented) + type: PropertyEditorParamTypes.JSON; +} + +// @public (undocumented) +enum KeyinStatus { + // (undocumented) + DontUpdate = 2, + // (undocumented) + Dynamic = 0, + // (undocumented) + Partial = 1 +} + +// @public (undocumented) +export function linePlaneIntersect(outP: Point3d, linePt: Point3d, lineNormal: Vector3d | undefined, planePt: Point3d, planeNormal: Vector3d, perpendicular: boolean): void; + +// @public +enum LocateAction { + // (undocumented) + AutoLocate = 1, + // (undocumented) + Identify = 0 +} + +// @public +enum LocateFilterStatus { + // (undocumented) + Accept = 0, + // (undocumented) + Reject = 1 +} + +// @public +class LocateOptions { + allowDecorations: boolean; + allowNonLocatable: boolean; + clone(): LocateOptions; + hitSource: HitSource; + // (undocumented) + init(): void; + maxHits: number; +} + +// @public (undocumented) +class LocateResponse { + // (undocumented) + explanation: string; + // (undocumented) + reason?: string; + // (undocumented) + snapStatus: SnapStatus; +} + +// @public (undocumented) +enum LockedStates { + // (undocumented) + ANGLE_BM = 7, + // (undocumented) + DIST_BM = 8, + // (undocumented) + NONE_LOCKED = 0, + // (undocumented) + VEC_BM = 4, + // (undocumented) + X_BM = 1, + // (undocumented) + XY_BM = 3, + // (undocumented) + Y_BM = 2 +} + +// @public +class LookViewTool extends ViewManip { + constructor(vp: ScreenViewport, oneShot?: boolean, isDraggingRequired?: boolean); + // (undocumented) + onReinitialize(): void; + // (undocumented) + static toolId: string; +} + +// @public (undocumented) +enum ManipulatorToolEvent { + // (undocumented) + Start = 1, + // (undocumented) + Stop = 2, + // (undocumented) + Suspend = 3, + // (undocumented) + Unsuspend = 4 +} + +// @public +class MarginPercent { + constructor(left: number, top: number, right: number, bottom: number); + // (undocumented) + bottom: number; + // (undocumented) + left: number; + // (undocumented) + right: number; + // (undocumented) + top: number; +} + +// @public +class Marker implements CanvasDecoration { + constructor(worldLocation: XYAndZ, size: XAndY); + protected _hiliteColor?: ColorDef; + protected _isHilited: boolean; + // (undocumented) + protected _scaleFactor?: Point2d; + // (undocumented) + protected _scaleFactorRange?: Range1d; + addDecoration(context: DecorateContext): void; + addMarker(context: DecorateContext): void; + drawDecoration(ctx: CanvasRenderingContext2D): void; + drawFunc?(ctx: CanvasRenderingContext2D): void; + protected drawHilited(ctx: CanvasRenderingContext2D): boolean; + image?: MarkerImage; + imageOffset?: XAndY; + imageSize?: XAndY; + label?: string; + labelAlign?: MarkerTextAlign; + labelBaseline?: MarkerTextBaseline; + labelColor?: MarkerFillStyle; + labelFont?: string; + labelOffset?: XAndY; + static makeFrom(other: Marker, ...args: any[]): T; + onMouseButton?(_ev: BeButtonEvent): boolean; + onMouseEnter(ev: BeButtonEvent): void; + onMouseLeave(): void; + onMouseMove(ev: BeButtonEvent): void; + pick(pt: XAndY): boolean; + position: Point3d; + readonly rect: ViewRect; + setImage(image: MarkerImage | Promise): void; + setImageUrl(url: string): void; + setPosition(vp: Viewport): boolean; + setScaleFactor(range: Range1dProps): void; + size: Point2d; + title?: string; + tooltipOptions?: ToolTipOptions; + visible: boolean; + readonly wantImage: boolean; + worldLocation: Point3d; +} + +// @public +class MarkerSet { + // (undocumented) + protected _entries: Array>; + // (undocumented) + protected readonly _worldToViewMap: Matrix4d; + addDecoration(context: DecorateContext): void; + protected abstract getClusterMarker(cluster: Cluster): Marker; + readonly markers: Set; + minimumClusterSize: number; +} + +// @public (undocumented) +class MeasureDistanceTool extends PrimitiveTool { + // WARNING: The type "MeasureMarker" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected readonly _acceptedSegments: { + distance: number; + slope: number; + start: Point3d; + end: Point3d; + delta: Vector3d; + refAxes: Matrix3d; + marker: MeasureMarker; + }[]; + // (undocumented) + protected readonly _locationData: { + point: Point3d; + refAxes: Matrix3d; + }[]; + // (undocumented) + protected _snapGeomId?: string; + // (undocumented) + protected _totalDistance: number; + // WARNING: The type "MeasureLabel" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected _totalDistanceMarker?: MeasureLabel; + // (undocumented) + protected acceptNewSegments(): Promise; + // (undocumented) + decorate(context: DecorateContext): void; + // (undocumented) + decorateSuspended(context: DecorateContext): void; + // (undocumented) + protected displayDelta(context: DecorateContext, seg: any): void; + // (undocumented) + protected displayDynamicDistance(context: DecorateContext, points: Point3d[]): void; + // (undocumented) + getDecorationGeometry(_hit: HitDetail): GeometryStreamProps | undefined; + // (undocumented) + protected getMarkerToolTip(distance: number, slope: number, start: Point3d, end: Point3d, delta?: Vector3d): Promise; + // (undocumented) + protected getReferenceAxes(vp?: Viewport): Matrix3d; + // (undocumented) + protected getSnapPoints(): Point3d[] | undefined; + // (undocumented) + isCompatibleViewport(vp: Viewport | undefined, isSelectedViewChange: boolean): boolean; + // (undocumented) + onDataButtonDown(ev: BeButtonEvent): Promise; + // (undocumented) + onMouseMotion(ev: BeButtonEvent): Promise; + // (undocumented) + onPostInstall(): void; + // (undocumented) + onResetButtonUp(ev: BeButtonEvent): Promise; + // (undocumented) + onRestartTool(): void; + // (undocumented) + onUndoPreviousStep(): Promise; + // (undocumented) + onUnsuspend(): void; + // (undocumented) + protected reportMeasurements(): void; + // (undocumented) + requireWriteableTarget(): boolean; + // (undocumented) + protected setupAndPromptForNextAction(): void; + // (undocumented) + protected showPrompt(): void; + // (undocumented) + testDecorationHit(id: string): boolean; + // (undocumented) + static toolId: string; + // (undocumented) + protected updateSelectedMarkerToolTip(seg: any): Promise; + // (undocumented) + protected updateTotals(): Promise; +} + +// @public (undocumented) +class MeasureLocationTool extends PrimitiveTool { + // WARNING: The type "MeasureMarker" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected readonly _acceptedLocations: MeasureMarker[]; + // (undocumented) + decorate(context: DecorateContext): void; + // (undocumented) + decorateSuspended(context: DecorateContext): void; + // (undocumented) + protected getMarkerToolTip(point: Point3d): Promise; + // (undocumented) + isCompatibleViewport(vp: Viewport | undefined, isSelectedViewChange: boolean): boolean; + // (undocumented) + onDataButtonDown(ev: BeButtonEvent): Promise; + // (undocumented) + onPostInstall(): void; + // (undocumented) + onResetButtonUp(_ev: BeButtonEvent): Promise; + // (undocumented) + onRestartTool(): void; + // (undocumented) + onUndoPreviousStep(): Promise; + // (undocumented) + onUnsuspend(): void; + // (undocumented) + protected reportMeasurements(): void; + // (undocumented) + requireWriteableTarget(): boolean; + // (undocumented) + protected setupAndPromptForNextAction(): void; + // (undocumented) + protected showPrompt(): void; + // (undocumented) + static toolId: string; +} + +// @public +enum MessageBoxIconType { + // (undocumented) + Critical = 4, + // (undocumented) + Information = 1, + // (undocumented) + NoSymbol = 0, + // (undocumented) + Question = 2, + // (undocumented) + Warning = 3 +} + +// @public +enum MessageBoxType { + // (undocumented) + LargeOk = 2, + // (undocumented) + MediumAlert = 3, + // (undocumented) + Ok = 1, + // (undocumented) + OkCancel = 0, + // (undocumented) + YesNo = 5, + // (undocumented) + YesNoCancel = 4 +} + +// @public +enum MessageBoxValue { + // (undocumented) + Apply = 1, + // (undocumented) + Cancel = 4, + // (undocumented) + Default = 5, + // (undocumented) + Help = 10, + // (undocumented) + No = 7, + // (undocumented) + NoToAll = 12, + // (undocumented) + Ok = 3, + // (undocumented) + Reset = 2, + // (undocumented) + Retry = 8, + // (undocumented) + Stop = 9, + // (undocumented) + Yes = 6, + // (undocumented) + YesToAll = 11 +} + +// WARNING: Unsupported export: SystemFactory +// @public +module MockRender { + class App extends IModelApp { + // WARNING: The type "System" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected static createDefaultRenderSystem(): System; + // (undocumented) + static shutdown(): void; + // (undocumented) + protected static supplyRenderSystem(): RenderSystem; + // WARNING: The type "SystemFactory" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + static systemFactory: SystemFactory; + } + + // (undocumented) + class Batch extends Graphic { + constructor(graphic: RenderGraphic, featureTable: PackedFeatureTable, range: ElementAlignedBox3d); + // (undocumented) + dispose(): void; + // (undocumented) + readonly featureTable: PackedFeatureTable; + // (undocumented) + readonly graphic: RenderGraphic; + // (undocumented) + readonly range: ElementAlignedBox3d; + } + + // (undocumented) + class Branch extends Graphic { + constructor(branch: GraphicBranch, transform: Transform, clips?: RenderClipVolume | undefined); + // (undocumented) + readonly branch: GraphicBranch; + // (undocumented) + readonly clips?: RenderClipVolume | undefined; + // (undocumented) + dispose(): void; + // (undocumented) + readonly transform: Transform; + } + + // (undocumented) + class Builder extends PrimitiveBuilder { + // WARNING: The type "System" needs to be exported by the package (e.g. added to index.ts) + constructor(system: System, placement: Transform | undefined, type: GraphicType, viewport: Viewport, pickId?: Id64String); + } + + // (undocumented) + class Graphic extends RenderGraphic { + constructor(); + // WARNING: The type "RenderMemory.Statistics" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + collectStatistics(_stats: RenderMemory.Statistics): void; + // (undocumented) + dispose(): void; + } + + // (undocumented) + class List extends Graphic { + constructor(graphics: RenderGraphic[]); + // (undocumented) + dispose(): void; + // (undocumented) + readonly graphics: RenderGraphic[]; + } + + // (undocumented) + class OffScreenTarget extends Target { + // WARNING: The type "System" needs to be exported by the package (e.g. added to index.ts) + constructor(system: System, _viewRect: ViewRect); + // (undocumented) + setViewRect(rect: ViewRect, _temp: boolean): void; + // (undocumented) + readonly viewRect: ViewRect; + } + + // (undocumented) + class OnScreenTarget extends Target { + // WARNING: The type "System" needs to be exported by the package (e.g. added to index.ts) + constructor(system: System, _canvas: HTMLCanvasElement); + // (undocumented) + setViewRect(_rect: ViewRect, _temp: boolean): void; + // (undocumented) + readonly viewRect: ViewRect; + } + + // (undocumented) + class System extends RenderSystem { + // WARNING: The type "Batch" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + createBatch(graphic: RenderGraphic, features: PackedFeatureTable, range: ElementAlignedBox3d): Batch; + // WARNING: The type "Branch" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + createBranch(branch: GraphicBranch, transform: Transform, clips?: RenderClipVolume): Branch; + // WARNING: The type "Builder" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + createGraphicBuilder(placement: Transform, type: GraphicType, viewport: Viewport, pickableId?: Id64String): Builder; + // WARNING: The type "List" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + createGraphicList(primitives: RenderGraphic[]): List; + // WARNING: The type "MeshParams" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Graphic" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + createMesh(_params: MeshParams): Graphic; + // (undocumented) + createOffscreenTarget(rect: ViewRect): RenderTarget; + // WARNING: The type "PointCloudArgs" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Graphic" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + createPointCloud(_args: PointCloudArgs, _imodel: IModelConnection): Graphic; + // WARNING: The type "PointStringParams" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Graphic" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + createPointString(_params: PointStringParams): Graphic; + // WARNING: The type "PolylineParams" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Graphic" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + createPolyline(_params: PolylineParams): Graphic; + // WARNING: The type "OnScreenTarget" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + createTarget(canvas: HTMLCanvasElement): OnScreenTarget; + // (undocumented) + dispose(): void; + // (undocumented) + readonly isValid: boolean; + // (undocumented) + readonly maxTextureSize: number; + } + + // (undocumented) + class Target extends RenderTarget { + // WARNING: The type "System" needs to be exported by the package (e.g. added to index.ts) + protected constructor(_system: System); + // (undocumented) + animationFraction: number; + // (undocumented) + readonly cameraFrustumNearScaleLimit: number; + // (undocumented) + changeDecorations(_decs: Decorations): void; + // (undocumented) + changeDynamics(_dynamics?: GraphicList): void; + // (undocumented) + changeRenderPlan(_plan: RenderPlan): void; + // (undocumented) + changeScene(_scene: GraphicList): void; + // (undocumented) + changeTerrain(_terrain: GraphicList): void; + // (undocumented) + drawFrame(_sceneTime?: number): void; + // WARNING: The type "Pixel.Selector" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Pixel.Receiver" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readPixels(_rect: ViewRect, _selector: Pixel.Selector, receiver: Pixel.Receiver, _excludeNonLocatable: boolean): void; + // (undocumented) + readonly renderSystem: RenderSystem; + // (undocumented) + updateViewRect(): boolean; + // (undocumented) + readonly wantInvertBlackBackground: boolean; + } + +} + +// @public +class ModelSelectorState extends ElementState { + constructor(props: ModelSelectorProps, iModel: IModelConnection); + addModels(arg: Id64Arg): void; + containsModel(modelId: Id64String): boolean; + dropModels(arg: Id64Arg): void; + // (undocumented) + equalState(other: ModelSelectorState): boolean; + has(id: string): boolean; + load(): Promise; + readonly models: Set; + readonly name: string; + // (undocumented) + toJSON(): ModelSelectorProps; +} + +// @public +class ModelState extends EntityState, implements ModelProps { + constructor(props: ModelProps, iModel: IModelConnection); + // (undocumented) + readonly asGeometricModel: GeometricModelState | undefined; + // (undocumented) + readonly asGeometricModel2d: GeometricModel2dState | undefined; + // (undocumented) + readonly asGeometricModel3d: GeometricModel3dState | undefined; + // (undocumented) + getExtents(): AxisAlignedBox3d; + readonly isGeometricModel: boolean; + // (undocumented) + readonly isPrivate: boolean; + // (undocumented) + readonly isTemplate: boolean; + // (undocumented) + readonly modeledElement: RelatedElement; + // (undocumented) + readonly name: string; + onIModelConnectionClose(): void; + // (undocumented) + parentModel: Id64String; + toJSON(): ModelProps; +} + +// @public (undocumented) +enum ModifyElementSource { + DragSelect = 5, + Fence = 3, + Group = 4, + Selected = 1, + SelectionSet = 2, + Unknown = 0 +} + +// @public +interface MultilineTextEditorParams extends BasePropertyEditorParams { + // (undocumented) + heightInRows: number; + // (undocumented) + type: PropertyEditorParamTypes.MultilineText; +} + +// @public +class NoRenderApp extends IModelApp { + // (undocumented) + protected static supplyRenderSystem(): RenderSystem; +} + +// @public +class NotificationManager { + protected _showToolTip(_htmlElement: HTMLElement, _message: HTMLElement | string, _location?: XAndY, _options?: ToolTipOptions): void; + clearToolTip(): void; + closePointerMessage(): void; + endActivityMessage(_reason: ActivityMessageEndReason): boolean; + readonly isToolTipOpen: boolean; + readonly isToolTipSupported: boolean; + openMessageBox(_mbType: MessageBoxType, _message: string, _icon: MessageBoxIconType): Promise; + openToolTip(_htmlElement: HTMLElement, message: HTMLElement | string, location?: XAndY, options?: ToolTipOptions): void; + outputActivityMessage(_messageText: string, _percentComplete: number): boolean; + outputMessage(_message: NotifyMessageDetails): void; + outputPrompt(_prompt: string): void; + outputPromptByKey(key: string): void; + setupActivityMessage(_details: ActivityMessageDetails): boolean; + // (undocumented) + readonly toolTipLocation: Point2d; +} + +// @public +class NotifyMessageDetails { + constructor(priority: OutputMessagePriority, briefMessage: string, detailedMessage?: string | undefined, msgType?: OutputMessageType, openAlert?: OutputMessageAlert); + // (undocumented) + briefMessage: string; + // (undocumented) + detailedMessage?: string | undefined; + // (undocumented) + displayPoint?: Point2d; + // (undocumented) + displayTime: BeDuration; + // (undocumented) + msgType: OutputMessageType; + // (undocumented) + openAlert: OutputMessageAlert; + // (undocumented) + priority: OutputMessagePriority; + // (undocumented) + relativePosition: RelativePosition; + setPointerTypeDetails(viewport: HTMLElement, displayPoint: XAndY, relativePosition?: RelativePosition): void; + // (undocumented) + viewport?: HTMLElement; +} + +// @public +class NullRenderSystem extends RenderSystem { + constructor(); + // (undocumented) + createBatch(): any; + // (undocumented) + createBranch(): any; + // (undocumented) + createGraphicBuilder(): any; + // (undocumented) + createGraphicList(): any; + // (undocumented) + createOffscreenTarget(): NullTarget; + // (undocumented) + createTarget(): NullTarget; + // (undocumented) + dispose(): void; + // (undocumented) + readonly isValid: boolean; +} + +// @public +class NullTarget extends RenderTarget { + // (undocumented) + animationBranches: AnimationBranchStates | undefined; + // (undocumented) + animationFraction: number; + // (undocumented) + readonly cameraFrustumNearScaleLimit: number; + // (undocumented) + changeDecorations(): void; + // (undocumented) + changeDynamics(): void; + // (undocumented) + changeRenderPlan(): void; + // (undocumented) + changeScene(): void; + // (undocumented) + changeTerrain(): void; + // (undocumented) + dispose(): void; + // (undocumented) + drawFrame(_sceneMilSecElapsed?: number): void; + // (undocumented) + onDestroy(): void; + // (undocumented) + onResized(): void; + // (undocumented) + overrideFeatureSymbology(): void; + // (undocumented) + readImage(): undefined; + // (undocumented) + readPixels(): void; + // (undocumented) + readonly renderSystem: any; + // (undocumented) + reset(): void; + // (undocumented) + setFlashed(): void; + // (undocumented) + setHiliteSet(): void; + // (undocumented) + setViewRect(): void; + // (undocumented) + updateViewRect(): boolean; + // (undocumented) + readonly viewRect: ViewRect; + // (undocumented) + readonly wantInvertBlackBackground: boolean; +} + +// @public (undocumented) +class OffScreenTarget extends Target { + constructor(rect: ViewRect); + // (undocumented) + protected _assignDC(): boolean; + // (undocumented) + protected _beginPaint(): void; + // (undocumented) + protected _endPaint(): void; + // (undocumented) + animationFraction: number; + // (undocumented) + onResized(): void; + // (undocumented) + setViewRect(rect: ViewRect, temporary: boolean): void; + // (undocumented) + updateViewRect(): boolean; + // (undocumented) + readonly viewRect: ViewRect; +} + +// @public (undocumented) +class OffScreenViewport extends Viewport { + // (undocumented) + static create(view: ViewState, viewRect?: ViewRect): OffScreenViewport; + // (undocumented) + setRect(rect: ViewRect, temporary?: boolean): void; + // (undocumented) + readonly viewRect: ViewRect; +} + +// @public +class OidcBrowserClient extends OidcClient, implements IOidcFrontendClient { + constructor(_configuration: OidcFrontendClientConfiguration); + dispose(): void; + getAccessToken(_actx: ActivityLoggingContext): Promise; + initialize(actx: ActivityLoggingContext): Promise; + readonly onUserStateChanged: BeEvent<(token: AccessToken | undefined) => void>; + signIn(_actx: ActivityLoggingContext): void; + signOut(_actx: ActivityLoggingContext): void; +} + +// @public (undocumented) +class OidcClientWrapper { + // (undocumented) + static initialize(actx: ActivityLoggingContext, config: OidcFrontendClientConfiguration): Promise; + // (undocumented) + static readonly oidcClient: IOidcFrontendClient; +} + +// @public +class OnScreenTarget extends Target { + constructor(canvas: HTMLCanvasElement); + // (undocumented) + protected _assignDC(): boolean; + // (undocumented) + protected _beginPaint(): void; + // (undocumented) + protected _endPaint(): void; + // (undocumented) + animationFraction: number; + // (undocumented) + protected debugPaint(): void; + // (undocumented) + dispose(): void; + // (undocumented) + protected drawOverlayDecorations(): void; + // (undocumented) + onResized(): void; + // (undocumented) + pickOverlayDecoration(pt: XAndY): CanvasDecoration | undefined; + // (undocumented) + setViewRect(_rect: ViewRect, _temporary: boolean): void; + // (undocumented) + updateViewRect(): boolean; + // (undocumented) + readonly viewRect: ViewRect; +} + +// @public +class OrthographicViewState extends SpatialViewState { + constructor(props: SpatialViewDefinitionProps, iModel: IModelConnection, categories: CategorySelectorState, displayStyle: DisplayStyle3dState, modelSelector: ModelSelectorState); + // (undocumented) + static readonly className: string; + // (undocumented) + supportsCamera(): boolean; +} + +// @public +enum OutputMessageAlert { + // (undocumented) + Balloon = 2, + // (undocumented) + Dialog = 1, + // (undocumented) + None = 0 +} + +// @public +enum OutputMessagePriority { + // (undocumented) + Debug = 13, + // (undocumented) + Error = 10, + // (undocumented) + Fatal = 17, + // (undocumented) + Info = 12, + // (undocumented) + None = 0, + // (undocumented) + Warning = 11 +} + +// @public +enum OutputMessageType { + Alert = 4, + // (undocumented) + InputField = 3, + // (undocumented) + Pointer = 1, + // (undocumented) + Sticky = 2, + Toast = 0 +} + +// @public (undocumented) +interface PackedFeature { + // (undocumented) + animationNodeId: number; + // (undocumented) + elementId: Id64.Uint32Pair; + // (undocumented) + geometryClass: GeometryClass; + // (undocumented) + subCategoryId: Id64.Uint32Pair; +} + +// @public +class PackedFeatureTable { + constructor(data: Uint32Array, modelId: Id64String, numFeatures: number, maxFeatures: number, type: BatchType, animationNodeIds?: Uint8Array | Uint16Array | Uint32Array); + // (undocumented) + readonly anyDefined: boolean; + // (undocumented) + readonly byteLength: number; + findElementId(featureIndex: number): Id64String | undefined; + findFeature(featureIndex: number): Feature | undefined; + // (undocumented) + getAnimationNodeId(featureIndex: number): number; + // (undocumented) + getElementIdPair(featureIndex: number): Id64.Uint32Pair; + getFeature(featureIndex: number): Feature; + // (undocumented) + getPackedFeature(featureIndex: number): PackedFeature; + // (undocumented) + readonly isClassifier: boolean; + readonly isUniform: boolean; + // (undocumented) + readonly maxFeatures: number; + // (undocumented) + readonly modelId: Id64String; + // (undocumented) + readonly numFeatures: number; + static pack(featureTable: FeatureTable): PackedFeatureTable; + // (undocumented) + readonly type: BatchType; + readonly uniform: Feature | undefined; + unpack(): FeatureTable; +} + +// @public +class PanViewTool extends ViewManip { + constructor(vp: ScreenViewport | undefined, oneShot?: boolean, isDraggingRequired?: boolean); + // (undocumented) + onReinitialize(): void; + // (undocumented) + static toolId: string; +} + +// @public (undocumented) +class PerformanceMetrics { + constructor(gatherGlFinish?: boolean, gatherCurPerformanceMetrics?: boolean); + // (undocumented) + curSpfTimeIndex: number; + // (undocumented) + endFrame(operationName?: string): void; + // (undocumented) + fpsTimer: StopWatch; + // (undocumented) + fpsTimerStart: number; + // (undocumented) + frameTimings: Map; + // (undocumented) + gatherCurPerformanceMetrics: boolean; + // (undocumented) + gatherGlFinish: boolean; + // (undocumented) + loadTileSum: number; + // (undocumented) + loadTileTimes: number[]; + // (undocumented) + recordTime(operationName: string): void; + // (undocumented) + renderSpfSum: number; + // (undocumented) + renderSpfTimes: number[]; + // (undocumented) + spfSum: number; + // (undocumented) + spfTimes: number[]; + // (undocumented) + startNewFrame(sceneTime?: number): void; +} + +// WARNING: Unsupported export: Receiver +// @public +module Pixel { + interface Buffer { + // WARNING: The type "Data" needs to be exported by the package (e.g. added to index.ts) + getPixel(x: number, y: number): Data; + } + + class Data { + // WARNING: The type "GeometryType" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Planarity" needs to be exported by the package (e.g. added to index.ts) + constructor(feature?: Feature | undefined, distanceFraction?: number, type?: GeometryType, planarity?: Planarity, featureTable?: PackedFeatureTable | undefined); + // (undocumented) + readonly distanceFraction: number; + // (undocumented) + readonly elementId: Id64String | undefined; + // (undocumented) + readonly feature?: Feature | undefined; + // (undocumented) + readonly featureTable?: PackedFeatureTable | undefined; + // (undocumented) + readonly geometryClass: GeometryClass | undefined; + // WARNING: The type "Planarity" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly planarity: Planarity; + // (undocumented) + readonly subCategoryId: Id64String | undefined; + // WARNING: The type "GeometryType" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly type: GeometryType; + } + + enum GeometryType { + Edge = 4, + Linear = 3, + None = 1, + Silhouette = 5, + Surface = 2, + Unknown = 0 + } + + enum Planarity { + None = 1, + NonPlanar = 3, + Planar = 2, + Unknown = 0 + } + + enum Selector { + All = 5, + Feature = 1, + GeometryAndDistance = 4, + // (undocumented) + None = 0 + } + +} + +// @public +class Plugin { + constructor(name: string, versionsRequired: string); + // (undocumented) + name: string; + onExecute(_args: string[]): void; + onLoad(_args: string[]): void; + // (undocumented) + versionsRequired: string; +} + +// @public +class PluginAdmin { + static loadPlugin(packageName: string, args?: string[]): Promise; + static register(plugin: Plugin): string[] | undefined; +} + +// WARNING: Unsupported export: Text +// WARNING: Unsupported export: String +// WARNING: Unsupported export: ShortDate +// WARNING: Unsupported export: Boolean +// WARNING: Unsupported export: Float +// WARNING: Unsupported export: Int +// WARNING: Unsupported export: Hexadecimal +// WARNING: Unsupported export: Enum +// WARNING: Unsupported export: Numeric +// WARNING: Unsupported export: Point2d +// WARNING: Unsupported export: Point3d +// WARNING: Unsupported export: Point +// WARNING: Unsupported export: Value +// @public +module Primitives { +} + +// @public +class PrimitiveTool extends InteractiveTool { + autoLockTarget(): void; + // (undocumented) + exitTool(): void; + getPrompt(): string; + readonly iModel: IModelConnection; + isCompatibleViewport(vp: Viewport | undefined, isSelectedViewChange: boolean): boolean; + isValidLocation(ev: BeButtonEvent, isButtonEvent: boolean): boolean; + onRedoPreviousStep(): Promise; + onReinitialize(): void; + abstract onRestartTool(): void; + onSelectedViewportChanged(_previous: Viewport | undefined, current: Viewport | undefined): void; + onUndoPreviousStep(): Promise; + // (undocumented) + redoPreviousStep(): Promise; + requireWriteableTarget(): boolean; + run(): boolean; + saveChanges(): Promise; + // (undocumented) + targetIsLocked: boolean; + // (undocumented) + targetModelId?: string; + // (undocumented) + targetView?: Viewport; + // (undocumented) + undoPreviousStep(): Promise; +} + +// @public +interface PrimitiveValue extends BasePropertyValue { + // (undocumented) + displayValue?: string; + // (undocumented) + value?: Primitives.Value; + // (undocumented) + valueFormat: PropertyValueFormat.Primitive; +} + +// @public +interface PropertyDescription { + dataController?: string; + // (undocumented) + displayLabel: string; + // (undocumented) + editor?: PropertyEditorInfo; + // (undocumented) + enum?: EnumerationChoicesInfo; + // (undocumented) + name: string; + quantityType?: QuantityType | string; + // (undocumented) + typename: string; +} + +// @public +interface PropertyEditorInfo { + // (undocumented) + name?: string; + // (undocumented) + params?: PropertyEditorParams[]; +} + +// @public +enum PropertyEditorParamTypes { + // (undocumented) + ButtonGroupData = 8, + // (undocumented) + CheckBoxIcons = 5, + // (undocumented) + Icon = 4, + // (undocumented) + JSON = 0, + // (undocumented) + MultilineText = 3, + // (undocumented) + Range = 1, + // (undocumented) + Slider = 2, + // (undocumented) + SuppressEditorLabel = 7, + // (undocumented) + SuppressUnitLabel = 6 +} + +// @public +class PropertyRecord { + constructor(value: PropertyValue, property: PropertyDescription); + copyWithNewValue(newValue: PropertyValue): PropertyRecord; + // (undocumented) + description?: string; + // (undocumented) + isMerged?: boolean; + // (undocumented) + isReadonly?: boolean; + // (undocumented) + readonly property: PropertyDescription; + // (undocumented) + readonly value: PropertyValue; +} + +// @public +enum PropertyValueFormat { + // (undocumented) + Array = 1, + // (undocumented) + Primitive = 0, + // (undocumented) + Struct = 2 +} + +// @public +class QuantityFormatter implements UnitsProvider { + // (undocumented) + protected _activeSystemIsImperial: boolean; + // (undocumented) + protected _formatSpecsByKoq: Map; + // (undocumented) + protected _imperialFormatsByType: Map; + // (undocumented) + protected _imperialFormatSpecsByType: Map; + // (undocumented) + protected _metricFormatsByType: Map; + // (undocumented) + protected _metricFormatSpecsByType: Map; + findFormatterSpecByQuantityType(type: QuantityType, imperial?: boolean): FormatterSpec | undefined; + protected findKoqFormatterSpec(koq: string, useImperial: boolean): FormatterSpec | undefined; + findUnit(unitLabel: string, unitFamily?: string): Promise; + findUnitByName(unitName: string): Promise; + // WARNING: The type "UnitDefinition" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected findUnitDefinition(name: string): UnitDefinition | undefined; + formatQuantity(magnitude: number, formatSpec: FormatterSpec): string; + getConversion(fromUnit: UnitProps, toUnit: UnitProps): Promise; + // (undocumented) + protected getFormatByQuantityType(type: QuantityType, imperial: boolean): Promise; + getFormatterSpecByQuantityType(type: QuantityType, imperial?: boolean): Promise; + protected getKoqFormatterSpec(koq: string, useImperial: boolean): Promise; + protected getKoqFormatterSpecsAsync(koq: string, useImperial: boolean): Promise; + protected getUnitByQuantityType(type: QuantityType): Promise; + protected loadFormatSpecsForQuantityTypes(useImperial: boolean): Promise; + protected loadKoqFormatSpecs(koq: string): Promise; + // (undocumented) + protected loadStdFormat(type: QuantityType, imperial: boolean): Promise; + useImperialFormats: boolean; +} + +// @public +enum QuantityType { + // (undocumented) + Angle = 2, + // (undocumented) + Area = 3, + // (undocumented) + Coordinate = 6, + // (undocumented) + LatLong = 5, + // (undocumented) + Length = 1, + // (undocumented) + Volume = 4 +} + +// @public +interface RangeEditorParams extends BasePropertyEditorParams { + maximum?: number; + minimum?: number; + // (undocumented) + type: PropertyEditorParamTypes.Range; +} + +// @public +enum RelativePosition { + // (undocumented) + Bottom = 3, + // (undocumented) + BottomLeft = 6, + // (undocumented) + BottomRight = 7, + // (undocumented) + Left = 0, + // (undocumented) + Right = 2, + // (undocumented) + Top = 1, + // (undocumented) + TopLeft = 4, + // (undocumented) + TopRight = 5 +} + +// @public +enum RemoveMe { + // (undocumented) + No = 0, + // (undocumented) + Yes = 1 +} + +// @public +class RenderClipVolume implements IDisposable { + // (undocumented) + abstract dispose(): void; + readonly type: ClippingType; +} + +// @public +class RenderContext { + constructor(vp: Viewport); + // (undocumented) + protected _createGraphicBuilder(type: GraphicType, transform?: Transform, id?: Id64String): GraphicBuilder; + createBranch(branch: GraphicBranch, location: Transform, clip?: RenderClipVolume): RenderGraphic; + createSceneGraphicBuilder(transform?: Transform): GraphicBuilder; + readonly frustum: Frustum; + readonly frustumPlanes: FrustumPlanes; + getPixelSizeAtPoint(inPoint?: Point3d): number; + // (undocumented) + readonly target: RenderTarget; + readonly viewFlags: ViewFlags; + readonly viewport: Viewport; +} + +// @public (undocumented) +enum RenderDiagnostics { + All = 6, + DebugOutput = 2, + None = 0, + WebGL = 4 +} + +// @public +class RenderGraphic implements IDisposable, RenderMemory.Consumer { + // WARNING: The type "RenderMemory.Statistics" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + abstract collectStatistics(stats: RenderMemory.Statistics): void; + // (undocumented) + abstract dispose(): void; +} + +// @public +module RenderMemory { + class Buffers extends Consumers { + constructor(); + // WARNING: The type "BufferType" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + addBuffer(type: BufferType, numBytes: number): void; + // (undocumented) + clear(): void; + // WARNING: The type "Consumers" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly consumers: Consumers[]; + // WARNING: The type "Consumers" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly instances: Consumers; + // WARNING: The type "Consumers" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly pointClouds: Consumers; + // WARNING: The type "Consumers" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly pointStrings: Consumers; + // WARNING: The type "Consumers" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly polylineEdges: Consumers; + // WARNING: The type "Consumers" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly polylines: Consumers; + // WARNING: The type "Consumers" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly silhouetteEdges: Consumers; + // WARNING: The type "Consumers" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly surfaces: Consumers; + // WARNING: The type "Consumers" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly visibleEdges: Consumers; + } + + // (undocumented) + enum BufferType { + // (undocumented) + COUNT = 8, + // (undocumented) + Instances = 7, + // (undocumented) + PointClouds = 6, + // (undocumented) + PointStrings = 5, + // (undocumented) + PolylineEdges = 3, + // (undocumented) + Polylines = 4, + // (undocumented) + SilhouetteEdges = 2, + // (undocumented) + Surfaces = 0, + // (undocumented) + VisibleEdges = 1 + } + + // (undocumented) + interface Consumer { + // WARNING: The type "Statistics" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + collectStatistics(stats: Statistics): void; + } + + class Consumers { + // (undocumented) + addConsumer(numBytes: number): void; + // (undocumented) + clear(): void; + // (undocumented) + count: number; + // (undocumented) + maxBytes: number; + // (undocumented) + totalBytes: number; + } + + // (undocumented) + enum ConsumerType { + // (undocumented) + ClipVolumes = 4, + // (undocumented) + COUNT = 5, + // (undocumented) + FeatureOverrides = 3, + // (undocumented) + FeatureTables = 2, + // (undocumented) + Textures = 0, + // (undocumented) + VertexTables = 1 + } + + // (undocumented) + class Statistics { + constructor(); + // WARNING: The type "BufferType" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + addBuffer(type: BufferType, numBytes: number): void; + // (undocumented) + addClipVolume(numBytes: number): void; + // WARNING: The type "ConsumerType" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + addConsumer(type: ConsumerType, numBytes: number): void; + // (undocumented) + addFeatureOverrides(numBytes: number): void; + // (undocumented) + addFeatureTable(numBytes: number): void; + // (undocumented) + addInstances(numBytes: number): void; + // (undocumented) + addPointCloud(numBytes: number): void; + // (undocumented) + addPointString(numBytes: number): void; + // (undocumented) + addPolyline(numBytes: number): void; + // (undocumented) + addPolylineEdges(numBytes: number): void; + // (undocumented) + addSilhouetteEdges(numBytes: number): void; + // (undocumented) + addSurface(numBytes: number): void; + // (undocumented) + addTexture(numBytes: number): void; + // (undocumented) + addVertexTable(numBytes: number): void; + // (undocumented) + addVisibleEdges(numBytes: number): void; + // WARNING: The type "Buffers" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly buffers: Buffers; + // (undocumented) + clear(): void; + // WARNING: The type "Consumers" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly clipVolumes: Consumers; + // WARNING: The type "Consumers" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly consumers: Consumers[]; + // WARNING: The type "Consumers" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly featureOverrides: Consumers; + // WARNING: The type "Consumers" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly featureTables: Consumers; + // WARNING: The type "Consumers" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly textures: Consumers; + // (undocumented) + readonly totalBytes: number; + // WARNING: The type "Consumers" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly vertexTables: Consumers; + } + +} + +// @public (undocumented) +class RenderPlan { + // (undocumented) + readonly aaLines: AntiAliasPref; + // (undocumented) + readonly aaText: AntiAliasPref; + // (undocumented) + readonly activeVolume?: RenderClipVolume; + // (undocumented) + readonly analysisStyle?: AnalysisStyle; + // (undocumented) + analysisTexture?: RenderTexture; + // (undocumented) + readonly ao?: AmbientOcclusion.Settings; + // (undocumented) + readonly bgColor: ColorDef; + // (undocumented) + static createFromViewport(vp: Viewport): RenderPlan; + // (undocumented) + readonly fraction: number; + // (undocumented) + readonly frustum: Frustum; + // (undocumented) + readonly hiliteSettings: Hilite.Settings; + // (undocumented) + readonly hline?: HiddenLine.Settings; + // (undocumented) + readonly is3d: boolean; + // (undocumented) + readonly isFadeOutActive: boolean; + // (undocumented) + readonly lights?: SceneLights; + // (undocumented) + readonly monoColor: ColorDef; + // (undocumented) + selectTerrainFrustum(): void; + // (undocumented) + selectViewFrustum(): void; + // (undocumented) + readonly terrainFrustum: ViewFrustum | undefined; + // (undocumented) + readonly viewFlags: ViewFlags; + // (undocumented) + readonly viewFrustum: ViewFrustum; +} + +// @public +class RenderSystem implements IDisposable { + abstract createBatch(graphic: RenderGraphic, features: PackedFeatureTable, range: ElementAlignedBox3d): RenderGraphic; + abstract createBranch(branch: GraphicBranch, transform: Transform, clips?: RenderClipVolume): RenderGraphic; + abstract createGraphicBuilder(placement: Transform, type: GraphicType, viewport: Viewport, pickableId?: Id64String): GraphicBuilder; + abstract createGraphicList(primitives: RenderGraphic[]): RenderGraphic; + // WARNING: The type "PolylineArgs" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + createIndexedPolylines(args: PolylineArgs, instances?: InstancedGraphicParams): RenderGraphic | undefined; + createMaterial(_params: RenderMaterial.Params, _imodel: IModelConnection): RenderMaterial | undefined; + // WARNING: The type "MeshParams" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + createMesh(_params: MeshParams, _instances?: InstancedGraphicParams): RenderGraphic | undefined; + // (undocumented) + abstract createOffscreenTarget(rect: ViewRect): RenderTarget; + // WARNING: The type "PointCloudArgs" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + createPointCloud(_args: PointCloudArgs, _imodel: IModelConnection): RenderGraphic | undefined; + // WARNING: The type "PointStringParams" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + createPointString(_params: PointStringParams, _instances?: InstancedGraphicParams): RenderGraphic | undefined; + // WARNING: The type "PolylineParams" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + createPolyline(_params: PolylineParams, _instances?: InstancedGraphicParams): RenderGraphic | undefined; + // (undocumented) + createSheetTile(_tile: RenderTexture, _polyfaces: IndexedPolyface[], _tileColor: ColorDef): GraphicList; + // (undocumented) + createSheetTilePolyfaces(_corners: Point3d[], _clip?: ClipVector): IndexedPolyface[]; + // WARNING: The type "SkyBox.CreateParams" needs to be exported by the package (e.g. added to index.ts) + createSkyBox(_params: SkyBox.CreateParams): RenderGraphic | undefined; + // (undocumented) + abstract createTarget(canvas: HTMLCanvasElement): RenderTarget; + createTextureFromCubeImages(_posX: HTMLImageElement, _negX: HTMLImageElement, _posY: HTMLImageElement, _negY: HTMLImageElement, _posZ: HTMLImageElement, _negZ: HTMLImageElement, _imodel: IModelConnection, _params: RenderTexture.Params): RenderTexture | undefined; + createTextureFromImage(_image: HTMLImageElement, _hasAlpha: boolean, _imodel: IModelConnection | undefined, _params: RenderTexture.Params): RenderTexture | undefined; + createTextureFromImageBuffer(_image: ImageBuffer, _imodel: IModelConnection, _params: RenderTexture.Params): RenderTexture | undefined; + createTextureFromImageSource(source: ImageSource, imodel: IModelConnection | undefined, params: RenderTexture.Params): Promise; + // (undocumented) + createTile(tileTexture: RenderTexture, corners: Point3d[]): RenderGraphic | undefined; + // WARNING: The type "MeshArgs" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + createTriMesh(args: MeshArgs, instances?: InstancedGraphicParams): RenderGraphic | undefined; + // (undocumented) + abstract dispose(): void; + // (undocumented) + enableDiagnostics(_enable: RenderDiagnostics): void; + findMaterial(_key: string, _imodel: IModelConnection): RenderMaterial | undefined; + findTexture(_key: string, _imodel: IModelConnection): RenderTexture | undefined; + // (undocumented) + getClipVolume(_clipVector: ClipVector, _imodel: IModelConnection): RenderClipVolume | undefined; + getGradientTexture(_symb: Gradient.Symb, _imodel: IModelConnection): RenderTexture | undefined; + // (undocumented) + readonly isValid: boolean; + loadTexture(id: Id64String, iModel: IModelConnection): Promise; + loadTextureImage(id: Id64String, iModel: IModelConnection): Promise; + // (undocumented) + readonly maxTextureSize: number; + // (undocumented) + onInitialized(): void; +} + +// @public +class RenderTarget implements IDisposable { + // (undocumented) + animationBranches: AnimationBranchStates | undefined; + // (undocumented) + animationFraction: number; + // (undocumented) + readonly cameraFrustumNearScaleLimit: number; + // (undocumented) + abstract changeDecorations(decorations: Decorations): void; + // (undocumented) + abstract changeDynamics(dynamics?: GraphicList): void; + // (undocumented) + abstract changeRenderPlan(plan: RenderPlan): void; + // (undocumented) + abstract changeScene(scene: GraphicList): void; + // (undocumented) + abstract changeTerrain(_scene: GraphicList): void; + // (undocumented) + createGraphicBuilder(type: GraphicType, viewport: Viewport, placement?: Transform, pickableId?: Id64String): GraphicBuilder; + static depthFromDisplayPriority(priority: number): number; + // (undocumented) + dispose(): void; + // (undocumented) + abstract drawFrame(sceneMilSecElapsed?: number): void; + // (undocumented) + static readonly frustumDepth2d: number; + // (undocumented) + static readonly maxDisplayPriority: number; + // (undocumented) + static readonly minDisplayPriority: number; + // (undocumented) + onResized(): void; + // WARNING: The type "FeatureSymbology.Overrides" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + overrideFeatureSymbology(_ovr: FeatureSymbology.Overrides): void; + // (undocumented) + pickOverlayDecoration(_pt: XAndY): CanvasDecoration | undefined; + // (undocumented) + readImage(_rect: ViewRect, _targetSize: Point2d, _flipVertically: boolean): ImageBuffer | undefined; + // WARNING: The type "Pixel.Selector" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Pixel.Receiver" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + abstract readPixels(rect: ViewRect, selector: Pixel.Selector, receiver: Pixel.Receiver, excludeNonLocatable: boolean): void; + // (undocumented) + readonly renderSystem: RenderSystem; + // (undocumented) + reset(): void; + // (undocumented) + setFlashed(_elementId: Id64String, _intensity: number): void; + // (undocumented) + setHiliteSet(_hilited: Set): void; + // (undocumented) + abstract setViewRect(_rect: ViewRect, _temporary: boolean): void; + // (undocumented) + abstract updateViewRect(): boolean; + // (undocumented) + readonly viewRect: ViewRect; + // (undocumented) + readonly wantInvertBlackBackground: boolean; +} + +// @public +class RotateViewTool extends ViewManip { + constructor(vp: ScreenViewport, oneShot?: boolean, isDraggingRequired?: boolean); + // (undocumented) + onReinitialize(): void; + // (undocumented) + static toolId: string; +} + +// @public (undocumented) +enum RotationMode { + // (undocumented) + ACS = 5, + // (undocumented) + Context = 6, + // (undocumented) + Front = 2, + // (undocumented) + Side = 3, + // (undocumented) + Top = 1, + // (undocumented) + View = 4 +} + +// @public (undocumented) +class RoundOff { + // (undocumented) + active: boolean; + // (undocumented) + units: Set; +} + +// @public (undocumented) +class SavedState { + // (undocumented) + auxRotationPlane: number; + // (undocumented) + readonly axes: ThreeAxes; + // (undocumented) + contextRotMode: number; + // (undocumented) + fixedOrg: boolean; + // (undocumented) + ignoreDataButton: boolean; + // (undocumented) + ignoreFlags: AccuDrawFlags; + // (undocumented) + mode: CompassMode; + // (undocumented) + readonly origin: Point3d; + // (undocumented) + rotationMode: RotationMode; + // (undocumented) + state: CurrentState; +} + +// @public (undocumented) +class SceneContext extends RenderContext { + constructor(vp: Viewport); + // (undocumented) + readonly backgroundGraphics: RenderGraphic[]; + // WARNING: The type "BackgroundMapState" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + backgroundMap?: BackgroundMapState; + // (undocumented) + readonly graphics: RenderGraphic[]; + // (undocumented) + hasMissingTiles: boolean; + // (undocumented) + insertMissingTile(tile: Tile): void; + // (undocumented) + readonly missingTiles: Set; + // (undocumented) + outputGraphic(graphic: RenderGraphic): void; + // (undocumented) + requestMissingTiles(): void; +} + +// @public +class ScreenViewport extends Viewport { + constructor(canvas: HTMLCanvasElement, parentDiv: HTMLDivElement, target: RenderTarget); + addChildDiv(element: HTMLElement, zIndex: number): void; + // (undocumented) + addDecorations(decorations: Decorations): void; + // (undocumented) + addNewDiv(className: string, overflowHidden: boolean, z: number): HTMLDivElement; + readonly canvas: HTMLCanvasElement; + changeView(view: ViewState): void; + clearViewUndo(): void; + static create(parentDiv: HTMLDivElement, view: ViewState): ScreenViewport; + readonly decorationDiv: HTMLDivElement; + doRedo(animationTime?: BeDuration): void; + doUndo(animationTime?: BeDuration): void; + // (undocumented) + drawLocateCursor(context: DecorateContext, pt: Point3d, aperture: number, isLocateCircleOn: boolean, hit?: HitDetail): void; + getClientRect(): ClientRect; + readonly isRedoPossible: boolean; + readonly isUndoPossible: boolean; + maxUndoSteps: number; + openToolTip(message: HTMLElement | string, location?: XAndY, options?: ToolTipOptions): void; + readonly parentDiv: HTMLDivElement; + // (undocumented) + pickCanvasDecoration(pt: XAndY): import("./render/System").CanvasDecoration | undefined; + pickNearestVisibleGeometry(pickPoint: Point3d, radius: number, allowNonLocatable?: boolean, out?: Point3d): Point3d | undefined; + static removeAllChildren(el: HTMLDivElement): void; + resetUndo(): void; + saveViewUndo(): void; + setCursor(cursor?: string): void; + setEventController(controller: EventController | undefined): void; + // (undocumented) + synchWithView(saveInUndo: boolean): void; + readonly toolTipDiv: HTMLDivElement; + // (undocumented) + viewCmdTargetCenter: Point3d | undefined; + readonly viewRect: ViewRect; +} + +// @public +class ScrollViewTool extends ViewManip { + constructor(vp: ScreenViewport, oneShot?: boolean, isDraggingRequired?: boolean); + // (undocumented) + onReinitialize(): void; + // (undocumented) + static toolId: string; +} + +// @public +class SectionDrawingModelState extends DrawingModelState { +} + +// @public +interface SelectedViewportChangedArgs { + // (undocumented) + current?: ScreenViewport; + // (undocumented) + previous?: ScreenViewport; +} + +// @public +enum SelectEventType { + // (undocumented) + Add = 0, + // (undocumented) + Clear = 3, + // (undocumented) + Remove = 1, + // (undocumented) + Replace = 2 +} + +// @public +enum SelectionMethod { + Box = 2, + Line = 1, + Pick = 0 +} + +// @public +enum SelectionMode { + Add = 1, + Remove = 2, + Replace = 0 +} + +// @public +enum SelectionProcessing { + AddElementToSelection = 0, + InvertElementInSelection = 2, + RemoveElementFromSelection = 1, + ReplaceSelectionWithElement = 3 +} + +// @public (undocumented) +enum SelectionScope { + Assembly = 1, + Element = 0 +} + +// @public +class SelectionSet { + constructor(iModel: IModelConnection); + add(elem: Id64Arg, sendEvent?: boolean): boolean; + addAndRemove(adds: Id64Arg, removes: Id64Arg): boolean; + readonly elements: Set; + emptyAll(): void; + has(elemId?: string): boolean; + // (undocumented) + iModel: IModelConnection; + invert(elem: Id64Arg): boolean; + readonly isActive: boolean; + isSelected(elemId?: Id64String): boolean; + readonly onChanged: BeEvent<(iModel: IModelConnection, evType: SelectEventType, ids?: Set | undefined) => void>; + remove(elem: Id64Arg, sendEvent?: boolean): boolean; + replace(elem: Id64Arg): void; + readonly size: number; +} + +// @public +class SelectionTool extends PrimitiveTool { + // (undocumented) + protected _selectionScopeValue: ToolSettingsValue; + applyToolSettingPropertyChange(updatedValue: ToolSettingsPropertySyncItem): boolean; + // (undocumented) + autoLockTarget(): void; + // (undocumented) + decorate(context: DecorateContext): void; + // (undocumented) + filterHit(hit: HitDetail, _out?: LocateResponse): Promise; + // (undocumented) + protected getSelectionMethod(): SelectionMethod; + // (undocumented) + protected getSelectionMode(): SelectionMode; + // (undocumented) + static hidden: boolean; + // (undocumented) + protected initSelectTool(): void; + // (undocumented) + isSelectByPoints: boolean; + // (undocumented) + isSuspended: boolean; + // (undocumented) + onCleanup(): void; + // (undocumented) + onDataButtonUp(ev: BeButtonEvent): Promise; + // (undocumented) + onModifierKeyTransition(_wentDown: boolean, modifier: BeModifierKeys, _event: KeyboardEvent): Promise; + // (undocumented) + onMouseEndDrag(ev: BeButtonEvent): Promise; + // (undocumented) + onMouseMotion(ev: BeButtonEvent): Promise; + // (undocumented) + onMouseStartDrag(ev: BeButtonEvent): Promise; + // (undocumented) + onPostInstall(): void; + // (undocumented) + onResetButtonUp(ev: BeButtonEvent): Promise; + // (undocumented) + onRestartTool(): void; + // (undocumented) + onSuspend(): void; + // (undocumented) + onTouchCancel(ev: BeTouchEvent): Promise; + // (undocumented) + onTouchComplete(ev: BeTouchEvent): Promise; + // (undocumented) + onTouchMove(ev: BeTouchEvent): Promise; + // (undocumented) + onTouchMoveStart(ev: BeTouchEvent, startEv: BeTouchEvent): Promise; + // (undocumented) + onUnsuspend(): void; + // (undocumented) + readonly points: Point3d[]; + // (undocumented) + processSelection(elementId: Id64Arg, process: SelectionProcessing): Promise; + // (undocumented) + requireWriteableTarget(): boolean; + // (undocumented) + protected selectByPointsEnd(ev: BeButtonEvent): boolean; + // (undocumented) + protected selectByPointsProcess(origin: Point3d, corner: Point3d, ev: BeButtonEvent, method: SelectionMethod, overlap: boolean): void; + // (undocumented) + protected selectByPointsStart(ev: BeButtonEvent): boolean; + // (undocumented) + selectDecoration(ev: BeButtonEvent, currHit?: HitDetail): Promise; + // (undocumented) + selectionOption: SelectOptions; + // (undocumented) + selectionScope: SelectionScope; + // (undocumented) + protected setSelectionMethod(method: SelectionMethod): void; + // (undocumented) + protected setSelectionMode(mode: SelectionMode): void; + // (undocumented) + protected showPrompt(mode: SelectionMode, method: SelectionMethod): void; + // (undocumented) + static startTool(): boolean; + supplyToolSettingsProperties(): ToolSettingsPropertyRecord[] | undefined; + // (undocumented) + static toolId: string; + // (undocumented) + updateSelection(elementId: Id64Arg, process: SelectionProcessing): boolean; + // (undocumented) + protected useOverlapSelection(ev: BeButtonEvent): boolean; + // (undocumented) + protected wantEditManipulators(): boolean; + // (undocumented) + protected wantPickableDecorations(): boolean; + // (undocumented) + protected wantSelectionClearOnMiss(_ev: BeButtonEvent): boolean; + // (undocumented) + protected wantSelectionScopeInToolSettings(): boolean; + // (undocumented) + protected wantToolSettings(): boolean; +} + +// @public +enum SelectOptions { + // (undocumented) + BoxAndReplace = 2, + // (undocumented) + LineAndReplace = 1, + // (undocumented) + PickAndAdd = 3, + // (undocumented) + PickAndRemove = 4, + // (undocumented) + PickAndReplace = 0 +} + +// @public +class SheetBorder { + addToBuilder(builder: GraphicBuilder): void; + static create(width: number, height: number, context?: DecorateContext): SheetBorder; + // (undocumented) + getRange(): Range2d; +} + +// @public +class SheetModelState extends GeometricModel2dState { +} + +// @public +class SheetViewState extends ViewState2d { + constructor(props: ViewDefinition2dProps, iModel: IModelConnection, categories: CategorySelectorState, displayStyle: DisplayStyle2dState, sheetProps: SheetProps, attachments: Id64Array); + // (undocumented) + static readonly className: string; + // (undocumented) + computeFitRange(): Range3d; + // (undocumented) + static createFromProps(viewStateData: ViewStateProps, iModel: IModelConnection): ViewState | undefined; + createScene(context: SceneContext): void; + // (undocumented) + decorate(context: DecorateContext): void; + // (undocumented) + getExtentLimits: { + max: number; + min: number; + } + load(): Promise; + markAttachment3dSceneIncomplete(): void; + onRenderFrame(_viewport: Viewport): void; + // (undocumented) + readonly sheetSize: Point2d; +} + +// @public +class SkyBox { +} + +// @public +class SkyCube extends SkyBox, implements SkyCubeProps { + // (undocumented) + readonly back: Id64String; + // (undocumented) + readonly bottom: Id64String; + // (undocumented) + static create(front: Id64String, back: Id64String, top: Id64String, bottom: Id64String, right: Id64String, left: Id64String, display?: boolean): SkyCube | undefined; + // (undocumented) + static fromJSON(skyboxJson: SkyBoxProps): SkyCube | undefined; + // (undocumented) + readonly front: Id64String; + // (undocumented) + readonly left: Id64String; + // WARNING: The type "SkyBox.CreateParams" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + loadParams(system: RenderSystem, iModel: IModelConnection): Promise; + // (undocumented) + readonly right: Id64String; + // (undocumented) + toJSON(): SkyBoxProps; + // (undocumented) + readonly top: Id64String; +} + +// @public +class SkyGradient extends SkyBox { + constructor(sky?: SkyBoxProps); + // (undocumented) + readonly groundColor: ColorDef; + // (undocumented) + readonly groundExponent: number; + // WARNING: The type "SkyBox.CreateParams" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + loadParams(_system: RenderSystem, iModel: IModelConnection): Promise; + // (undocumented) + readonly nadirColor: ColorDef; + // (undocumented) + readonly skyColor: ColorDef; + // (undocumented) + readonly skyExponent: number; + // (undocumented) + toJSON(): SkyBoxProps; + // (undocumented) + readonly twoColor: boolean; + // (undocumented) + readonly zenithColor: ColorDef; +} + +// @public +class SkySphere extends SkyBox { + // (undocumented) + static fromJSON(json: SkyBoxProps): SkySphere | undefined; + // WARNING: The type "SkyBox.CreateParams" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + loadParams(system: RenderSystem, iModel: IModelConnection): Promise; + textureId: Id64String; + // (undocumented) + toJSON(): SkyBoxProps; +} + +// @public +interface SliderEditorParams extends BasePropertyEditorParams { + intervals?: boolean; + maximum: number; + minimum: number; + numButtons?: number; + // (undocumented) + type: PropertyEditorParamTypes.Slider; + valueFactor?: number; + vertical?: boolean; +} + +// @public +class SnapDetail extends HitDetail { + constructor(from: HitDetail, snapMode?: SnapMode, heat?: SnapHeat, snapPoint?: XYZProps); + readonly adjustedPoint: Point3d; + clone(): SnapDetail; + // (undocumented) + draw(context: DecorateContext): void; + geomType?: HitGeomType; + // (undocumented) + getCurvePrimitive(singleSegment?: boolean): CurvePrimitive | undefined; + getHitType(): HitDetailType; + getPoint(): Point3d; + // (undocumented) + heat: SnapHeat; + readonly isHot: boolean; + readonly isPointAdjusted: boolean; + normal?: Vector3d; + parentGeomType?: HitParentGeomType; + primitive?: CurvePrimitive; + setCurvePrimitive(primitive?: CurvePrimitive, localToWorld?: Transform, geomType?: HitGeomType): void; + setSnapPoint(point: Point3d, heat: SnapHeat): void; + // (undocumented) + snapMode: SnapMode; + readonly snapPoint: Point3d; + sprite?: Sprite; +} + +// @public (undocumented) +enum SnapHeat { + // (undocumented) + InRange = 2, + // (undocumented) + None = 0, + // (undocumented) + NotInRange = 1 +} + +// @public (undocumented) +enum SnapMode { + // (undocumented) + Bisector = 32, + // (undocumented) + Center = 8, + // (undocumented) + Intersection = 64, + // (undocumented) + MidPoint = 4, + // (undocumented) + Nearest = 1, + // (undocumented) + NearestKeypoint = 2, + // (undocumented) + Origin = 16 +} + +// @public (undocumented) +enum SnapStatus { + // (undocumented) + Aborted = 1, + // (undocumented) + Disabled = 100, + // (undocumented) + FilteredByApp = 600, + // (undocumented) + FilteredByAppQuietly = 700, + // (undocumented) + NoElements = 2, + // (undocumented) + NoSnapPossible = 200, + // (undocumented) + NotSnappable = 300, + // (undocumented) + Success = 0 +} + +// @public +class SpatialModelState extends GeometricModel3dState { +} + +// @public +class SpatialViewState extends ViewState3d { + constructor(props: SpatialViewDefinitionProps, iModel: IModelConnection, arg3: CategorySelectorState, displayStyle: DisplayStyle3dState, modelSelector: ModelSelectorState); + // (undocumented) + addViewedModel(id: Id64String): void; + // (undocumented) + static readonly className: string; + // (undocumented) + clearViewedModels(): void; + // (undocumented) + computeFitRange(): AxisAlignedBox3d; + // (undocumented) + createAuxCoordSystem(acsName: string): AuxCoordSystemState; + // (undocumented) + static createFromProps(props: ViewStateProps, iModel: IModelConnection): ViewState | undefined; + // (undocumented) + equals(other: this): boolean; + // (undocumented) + equalState(other: SpatialViewState): boolean; + // (undocumented) + forEachModel(func: (model: GeometricModelState) => void): void; + // (undocumented) + forEachTileTreeModel(func: (model: TileTreeModelState) => void): void; + // (undocumented) + getExtentLimits: { + max: number; + min: number; + } + // (undocumented) + getViewedExtents(): AxisAlignedBox3d; + // (undocumented) + load(): Promise; + // (undocumented) + modelSelector: ModelSelectorState; + // (undocumented) + removeViewedModel(id: Id64String): void; + // (undocumented) + toJSON(): SpatialViewDefinitionProps; + // (undocumented) + viewsModel(modelId: Id64String): boolean; +} + +// @public +class Sprite { + fromImageSource(src: ImageSource): void; + fromUrl(url: string): void; + image?: HTMLImageElement; + readonly isLoaded: boolean; + readonly offset: Point2d; + readonly size: Point2d; +} + +// @public +class SpriteLocation implements CanvasDecoration { + activate(sprite: Sprite, viewport: ScreenViewport, locationWorld: XYAndZ, alpha?: number): void; + deactivate(): void; + decorate(context: DecorateContext): void; + drawDecoration(ctx: CanvasRenderingContext2D): void; + // (undocumented) + readonly isActive: boolean; + readonly position: Point3d; +} + +// @public +class StandardView { + static adjustToStandardRotation(matrix: Matrix3d): void; + // (undocumented) + static readonly back: Matrix3d; + // (undocumented) + static readonly bottom: Matrix3d; + // (undocumented) + static readonly front: Matrix3d; + static getStandardRotation(id: StandardViewId): Matrix3d; + // (undocumented) + static readonly iso: Matrix3d; + // (undocumented) + static readonly left: Matrix3d; + // (undocumented) + static readonly right: Matrix3d; + // (undocumented) + static readonly rightIso: Matrix3d; + // (undocumented) + static readonly top: Matrix3d; +} + +// @public +enum StandardViewId { + // (undocumented) + Back = 5, + // (undocumented) + Bottom = 1, + // (undocumented) + Front = 4, + // (undocumented) + Iso = 6, + // (undocumented) + Left = 2, + NotStandard = -1, + // (undocumented) + Right = 3, + // (undocumented) + RightIso = 7, + // (undocumented) + Top = 0 +} + +// @public +class StandardViewTool extends ViewTool { + constructor(viewport: ScreenViewport, _standardViewId: StandardViewId); + // (undocumented) + onPostInstall(): void; + // (undocumented) + static toolId: string; +} + +// @public (undocumented) +enum StartOrResume { + // (undocumented) + Resume = 2, + // (undocumented) + Start = 1 +} + +// @public +interface StructValue extends BasePropertyValue { + // (undocumented) + members: { + [name: string]: PropertyRecord; + } + // (undocumented) + valueFormat: PropertyValueFormat.Struct; +} + +// @public +class SubCategoriesRequest { + constructor(subcategories: ViewSubCategories, categoryIds: Set, imodel: IModelConnection); + // (undocumented) + cancel(): void; + // (undocumented) + dispatch(): Promise; +} + +// @public +interface SuppressLabelEditorParams extends BasePropertyEditorParams { + // (undocumented) + type: PropertyEditorParamTypes.SuppressEditorLabel; +} + +// @public +interface SuppressUnitLabelEditorParams extends BasePropertyEditorParams { + // (undocumented) + type: PropertyEditorParamTypes.SuppressUnitLabel; +} + +// @public (undocumented) +class SuspendedToolState { + constructor(); + // (undocumented) + stop(): void; +} + +// @public +class SyncFlags { + // (undocumented) + initFrom(other: SyncFlags): void; + // (undocumented) + invalidateAnimationFraction(): void; + // (undocumented) + invalidateController(): void; + // (undocumented) + invalidateDecorations(): void; + // (undocumented) + invalidateRedrawPending(): void; + // (undocumented) + invalidateRenderPlan(): void; + // (undocumented) + invalidateRotatePoint(): void; + // (undocumented) + invalidateScene(): void; + // (undocumented) + readonly isRedrawPending: boolean; + // (undocumented) + readonly isValidAnimationFraction: boolean; + // (undocumented) + readonly isValidController: boolean; + // (undocumented) + readonly isValidDecorations: boolean; + // (undocumented) + readonly isValidRenderPlan: boolean; + // (undocumented) + readonly isValidRotatePoint: boolean; + // (undocumented) + readonly isValidScene: boolean; + // (undocumented) + setRedrawPending(): void; + // (undocumented) + setValidAnimationFraction(): void; + // (undocumented) + setValidController(): void; + // (undocumented) + setValidDecorations(): void; + // (undocumented) + setValidRenderPlan(): void; + // (undocumented) + setValidRotatePoint(): void; + // (undocumented) + setValidScene(): void; +} + +// @public (undocumented) +class Target extends RenderTarget { + protected constructor(rect?: ViewRect); + // (undocumented) + protected abstract _assignDC(): boolean; + // (undocumented) + protected abstract _beginPaint(): void; + // (undocumented) + protected _dcAssigned: boolean; + // (undocumented) + protected _decorations?: Decorations; + // (undocumented) + protected abstract _endPaint(): void; + // WARNING: The type "FrameBuffer" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected _fbo?: FrameBuffer; + // WARNING: The type "Batch" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + addBatch(batch: Batch): void; + // (undocumented) + readonly ambientLight: Float32Array; + // (undocumented) + ambientOcclusionSettings: AmbientOcclusion.Settings; + // (undocumented) + analysisStyle?: AnalysisStyle; + // (undocumented) + analysisTexture?: RenderTexture; + // (undocumented) + animationBranches: AnimationBranchStates | undefined; + // WARNING: The type "BatchState" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly batchState: BatchState; + // (undocumented) + readonly bgColor: ColorDef; + // (undocumented) + readonly cameraFrustumNearScaleLimit: number; + // (undocumented) + changeDecorations(decs: Decorations): void; + // (undocumented) + changeDynamics(dynamics?: GraphicList): void; + // (undocumented) + changeFrustum(plan: RenderPlan): void; + // (undocumented) + changeRenderPlan(plan: RenderPlan): void; + // (undocumented) + changeScene(scene: GraphicList): void; + // (undocumented) + changeTerrain(terrain: GraphicList): void; + // WARNING: The type "ClipDef" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly clipDef: ClipDef; + // WARNING: The type "TextureHandle" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + clipMask: TextureHandle | undefined; + // (undocumented) + readonly clips: Clips; + // WARNING: The type "SceneCompositor" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly compositor: SceneCompositor; + // (undocumented) + readonly currentBatchId: number; + // WARNING: The type "FeatureSymbology.Overrides" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly currentFeatureSymbologyOverrides: FeatureSymbology.Overrides; + // WARNING: The type "FeatureOverrides" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + currentOverrides: FeatureOverrides | undefined; + // WARNING: The type "ShaderFlags" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly currentShaderFlags: ShaderFlags; + // (undocumented) + readonly currentTransform: Transform; + // (undocumented) + readonly currentViewFlags: ViewFlags; + // (undocumented) + protected debugPaint(): void; + // WARNING: The type "BranchState" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly decorationState: BranchState; + // (undocumented) + dispose(): void; + // (undocumented) + drawFrame(sceneMilSecElapsed?: number): void; + // (undocumented) + readonly drawNonLocatable: boolean; + // (undocumented) + protected drawOverlayDecorations(): void; + // (undocumented) + readonly dynamics: GraphicList | undefined; + // WARNING: The type "ColorInfo" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly edgeColor: ColorInfo; + // (undocumented) + readonly flashedElemId: Id64String; + // (undocumented) + readonly flashedUpdateTime: BeTimePoint; + // (undocumented) + readonly flashIntensity: number; + // (undocumented) + readonly frustumUniforms: FrustumUniforms; + // (undocumented) + readonly fStop: number; + // WARNING: The type "ShaderProgramParams" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getEdgeLineCode(params: ShaderProgramParams, baseCode: number): number; + // WARNING: The type "RenderPass" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "EdgeOverrides" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getEdgeOverrides(pass: RenderPass): EdgeOverrides | undefined; + // WARNING: The type "ShaderProgramParams" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getEdgeWeight(params: ShaderProgramParams, baseWeight: number): number; + // WARNING: The type "Branch" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getWorldDecorations(decs: GraphicList): Branch; + // (undocumented) + readonly hasClipMask: boolean; + // (undocumented) + readonly hasClipVolume: boolean; + // WARNING: The type "EdgeOverrides" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly hiddenEdgeOverrides: EdgeOverrides | undefined; + // (undocumented) + readonly hilite: Id64.Uint32Set; + // (undocumented) + hiliteSettings: Hilite.Settings; + // (undocumented) + readonly hiliteUpdateTime: BeTimePoint; + // (undocumented) + readonly is2d: boolean; + // (undocumented) + readonly is3d: boolean; + // (undocumented) + readonly isEdgeColorOverridden: boolean; + // (undocumented) + readonly isEdgeWeightOverridden: boolean; + // (undocumented) + isFadeOutActive: boolean; + // (undocumented) + readonly isReadPixelsInProgress: boolean; + // (undocumented) + readonly monoColor: ColorDef; + // (undocumented) + readonly nearPlaneCenter: Point3d; + // WARNING: The type "Batch" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + onBatchDisposed(batch: Batch): void; + // WARNING: The type "FeatureSymbology.Overrides" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + overrideFeatureSymbology(ovr: FeatureSymbology.Overrides): void; + // (undocumented) + readonly overridesUpdateTime: BeTimePoint; + // (undocumented) + performanceMetrics?: PerformanceMetrics; + // (undocumented) + plan?: RenderPlan; + // (undocumented) + readonly planFraction: number; + // (undocumented) + readonly planFrustum: Frustum; + // (undocumented) + popActiveVolume(): void; + // (undocumented) + popBatch(): void; + // (undocumented) + popBranch(): void; + // (undocumented) + readonly projectionMatrix: Matrix4d; + // (undocumented) + pushActiveVolume(): void; + // WARNING: The type "Batch" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + pushBatch(batch: Batch): void; + // WARNING: The type "ShaderProgramExecutor" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Branch" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + pushBranch(exec: ShaderProgramExecutor, branch: Branch): void; + // WARNING: The type "BranchState" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + pushState(state: BranchState): void; + // (undocumented) + readImage(wantRectIn: ViewRect, targetSizeIn: Point2d, flipVertically: boolean): ImageBuffer | undefined; + // (undocumented) + protected readImagePixels(out: Uint8Array, x: number, y: number, w: number, h: number): boolean; + // WARNING: The type "Pixel.Selector" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Pixel.Receiver" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readPixels(rect: ViewRect, selector: Pixel.Selector, receiver: Pixel.Receiver, excludeNonLocatable: boolean): void; + // (undocumented) + recordPerformanceMetric(operation: string): void; + // (undocumented) + readonly renderRect: ViewRect; + // (undocumented) + readonly renderSystem: RenderSystem; + // (undocumented) + reset(): void; + // (undocumented) + readonly scene: GraphicList; + // (undocumented) + setFlashed(id: Id64String, intensity: number): void; + // (undocumented) + setHiliteSet(hilite: Set): void; + // WARNING: The type "ShaderLights" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly shaderLights: ShaderLights | undefined; + // WARNING: The type "Techniques" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly techniques: Techniques; + // (undocumented) + readonly transparencyThreshold: number; + // (undocumented) + readonly viewMatrix: Transform; + // WARNING: The type "EdgeOverrides" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly visibleEdgeOverrides: EdgeOverrides | undefined; + // (undocumented) + readonly wantAmbientOcclusion: boolean; + // (undocumented) + readonly wantInvertBlackBackground: boolean; +} + +// @public (undocumented) +class TentativeOrAccuSnap { + // (undocumented) + static getCurrentPoint(): Point3d; + // (undocumented) + static getCurrentSnap(checkIsHot?: boolean): SnapDetail | undefined; + // (undocumented) + static getCurrentView(): ScreenViewport | undefined; + // (undocumented) + static readonly isHot: boolean; +} + +// @public (undocumented) +class TentativePoint { + // (undocumented) + clear(doErase: boolean): void; + // (undocumented) + currSnap?: SnapDetail; + // (undocumented) + decorate(context: DecorateContext): void; + getCurrSnap(): SnapDetail | undefined; + // (undocumented) + getHitAndList(holder: HitListHolder): SnapDetail | undefined; + // (undocumented) + getPoint(): Point3d; + // (undocumented) + isActive: boolean; + readonly isSnapped: boolean; + // (undocumented) + onButtonEvent(ev: BeButtonEvent): void; + // (undocumented) + onInitialized(): void; + // (undocumented) + process(ev: BeButtonEvent): Promise; + // (undocumented) + removeTentative(): void; + // (undocumented) + setCurrSnap(newSnap?: SnapDetail): void; + // (undocumented) + setHitList(list?: HitList): void; + // (undocumented) + setPoint(point: Point3d): void; + // (undocumented) + showTentative(): void; + // (undocumented) + tpHits?: HitList; + // (undocumented) + viewport?: ScreenViewport; +} + +// @public +interface TextureImage { + format: ImageSourceFormat | undefined; + image: HTMLImageElement | undefined; +} + +// @public (undocumented) +class ThreeAxes { + // (undocumented) + clone(): ThreeAxes; + // (undocumented) + static createFromMatrix3d(rMatrix: Matrix3d, result?: ThreeAxes): ThreeAxes; + // (undocumented) + equals(other: ThreeAxes): boolean; + // (undocumented) + fromMatrix3d(rMatrix: Matrix3d): void; + // (undocumented) + setFrom(other: ThreeAxes): void; + // (undocumented) + toMatrix3d(out?: Matrix3d): Matrix3d; + // (undocumented) + readonly x: Vector3d; + // (undocumented) + readonly y: Vector3d; + // (undocumented) + readonly z: Vector3d; +} + +// @public (undocumented) +class Tile { +} + +// @public (undocumented) +class TileAdmin { +} + +// @public (undocumented) +class TileLoader { + // (undocumented) + protected readonly _batchType: BatchType; + // (undocumented) + protected readonly _loadEdges: boolean; + compareTilePriorities(lhs: Tile, rhs: Tile): number; + // (undocumented) + abstract getChildrenProps(parent: Tile): Promise; + // WARNING: The type "TileRequest.ResponseData" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "TileRequest.Graphic" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + loadTileGraphic(tile: Tile, data: TileRequest.ResponseData, isCanceled?: () => boolean): Promise; + // WARNING: The type "TileIO.StreamBuffer" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "TileRequest.Graphic" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + loadTileGraphicFromStream(tile: Tile, streamBuffer: TileIO.StreamBuffer, isCanceled?: () => boolean): Promise; + // (undocumented) + readonly maxDepth: number; + // (undocumented) + readonly parentsAndChildrenExclusive: boolean; + // WARNING: The type "Tile.LoadPriority" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + readonly priority: Tile.LoadPriority; + // WARNING: The type "Tile.DrawArgs" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + processSelectedTiles(selected: Tile[], _args: Tile.DrawArgs): Tile[]; + // WARNING: The type "TileRequest.Response" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + abstract requestTileContent(tile: Tile): Promise; + // WARNING: The type "Tile.Params" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + abstract tileRequiresLoading(params: Tile.Params): boolean; + // (undocumented) + readonly viewFlagOverrides: ViewFlag.Overrides; +} + +// @public (undocumented) +class TileTree { +} + +// @public (undocumented) +interface TileTreeModelState { + // (undocumented) + readonly loadStatus: TileTree.LoadStatus; + // WARNING: The type "TileTree.LoadStatus" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + loadTileTree(edgesRequired: boolean, animationId?: Id64String, asClassifier?: boolean, classifierExpansion?: number): TileTree.LoadStatus; + // (undocumented) + readonly tileTree: TileTree | undefined; + // (undocumented) + readonly treeModelId: Id64String; +} + +// @public (undocumented) +class TileTreeState { + constructor(_iModel: IModelConnection, _is3d: boolean, _modelId: Id64String); + // (undocumented) + clearTileTree(): void; + // (undocumented) + edgesOmitted: boolean; + // (undocumented) + readonly iModel: IModelConnection; + // WARNING: The type "TileTree.LoadStatus" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + loadStatus: TileTree.LoadStatus; + // (undocumented) + setTileTree(props: TileTreeProps, loader: TileLoader): void; + // (undocumented) + tileTree?: TileTree; +} + +// @public +class Tool { + constructor(..._args: any[]); + static readonly description: string; + static readonly flyover: string; + static hidden: boolean; + static readonly keyin: string; + static namespace: I18NNamespace; + static register(namespace?: I18NNamespace): void; + run(..._arg: any[]): boolean; + static toolId: string; +} + +// @public +class ToolAdmin { + acsContextLock: boolean; + acsPlaneSnapLock: boolean; + readonly activeTool: InteractiveTool | undefined; + readonly activeToolChanged: BeEvent<(tool: Tool, start: StartOrResume) => void>; + addEvent(ev: Event, vp?: ScreenViewport): void; + // (undocumented) + adjustPoint(pointActive: Point3d, vp: ScreenViewport, projectToACS?: boolean, applyLocks?: boolean): void; + // (undocumented) + adjustPointToACS(pointActive: Point3d, vp: Viewport, perpendicular: boolean): void; + // (undocumented) + adjustPointToGrid(pointActive: Point3d, vp: Viewport): void; + // (undocumented) + adjustSnapPoint(perpendicular?: boolean): void; + assemblyLock: boolean; + // (undocumented) + beginDynamics(): void; + // (undocumented) + callOnCleanup(): void; + convertTouchEndToButtonUp(ev: BeTouchEvent, button?: BeButton): Promise; + convertTouchMoveStartToButtonDownAndMotion(startEv: BeTouchEvent, ev: BeTouchEvent, button?: BeButton): Promise; + convertTouchMoveToMotion(ev: BeTouchEvent): Promise; + convertTouchStartToButtonDown(ev: BeTouchEvent, button?: BeButton): Promise; + convertTouchTapToButtonDownAndUp(ev: BeTouchEvent, button?: BeButton): Promise; + // (undocumented) + readonly currentInputState: CurrentInputState; + readonly currentTool: InteractiveTool; + readonly cursorView: ScreenViewport | undefined; + // (undocumented) + decorate(context: DecorateContext): void; + defaultToolArgs: any[] | undefined; + defaultToolId: string; + doRedoOperation(): Promise; + doUndoOperation(): Promise; + // (undocumented) + endDynamics(): void; + // (undocumented) + exitInputCollector(): void; + // (undocumented) + exitViewTool(): void; + // (undocumented) + fillEventFromCursorLocation(ev: BeButtonEvent): void; + // (undocumented) + fillEventFromLastDataButton(ev: BeButtonEvent): void; + protected filterViewport(vp: Viewport): boolean; + // (undocumented) + getDecorationGeometry(hit: HitDetail): GeometryStreamProps | undefined; + getToolTip(hit: HitDetail): Promise; + gridLock: boolean; + readonly idleTool: IdleTool; + // (undocumented) + readonly isLocateCircleOn: boolean; + readonly manipulatorToolEvent: BeEvent<(tool: Tool, event: ManipulatorToolEvent) => void>; + // (undocumented) + markupView?: ScreenViewport; + // (undocumented) + onInitialized(): void; + // (undocumented) + onInstallTool(tool: InteractiveTool): boolean; + // (undocumented) + onMouseLeave(vp: ScreenViewport): Promise; + // (undocumented) + onPostInstallTool(tool: InteractiveTool): void; + // (undocumented) + onSelectedViewportChanged(previous: ScreenViewport | undefined, current: ScreenViewport | undefined): void; + // (undocumented) + onShutDown(): void; + // (undocumented) + readonly primitiveTool: PrimitiveTool | undefined; + processWheelEvent(ev: BeWheelEvent, doUpdate: boolean): Promise; + // (undocumented) + sendButtonEvent(ev: BeButtonEvent): Promise; + // (undocumented) + sendEndDragEvent(ev: BeButtonEvent): Promise; + // (undocumented) + setAdjustedDataPoint(ev: BeButtonEvent): void; + // (undocumented) + setCursor(cursor: string | undefined): void; + // (undocumented) + setIncompatibleViewportCursor(restore: boolean): void; + // (undocumented) + setInputCollector(newTool?: InputCollector): void; + // (undocumented) + setLocateCircleOn(locateOn: boolean): void; + // (undocumented) + setLocateCursor(enableLocate: boolean): void; + // (undocumented) + setPrimitiveTool(newTool?: PrimitiveTool): void; + // (undocumented) + setViewTool(newTool?: ViewTool): void; + startDefaultTool(): void; + // (undocumented) + startEventLoop(): void; + // (undocumented) + startInputCollector(newTool: InputCollector): void; + // (undocumented) + startPrimitiveTool(newTool?: PrimitiveTool): void; + // (undocumented) + startViewTool(newTool: ViewTool): void; + syncToolSettingsProperties(toolId: string, syncProperties: ToolSettingsPropertySyncItem[]): void; + // (undocumented) + testDecorationHit(id: string): boolean; + // (undocumented) + toolSettingsChangeHandler: ((toolId: string, syncProperties: ToolSettingsPropertySyncItem[]) => void) | undefined; + // (undocumented) + readonly toolState: ToolState; + // (undocumented) + updateDynamics(ev?: BeButtonEvent, useLastData?: boolean, adjustPoint?: boolean): void; + // (undocumented) + readonly viewTool: ViewTool | undefined; +} + +// @public +class ToolRegistry { + create(toolId: string, ...args: any[]): Tool | undefined; + executeExactMatch(keyin: string, ...args: any[]): boolean; + find(toolId: string): ToolType | undefined; + findExactMatch(keyin: string): ToolType | undefined; + findPartialMatches(keyin: string): FuzzySearchResults; + getToolList(): ToolList; + register(toolClass: ToolType, namespace?: I18NNamespace): void; + registerModule(moduleObj: any, namespace?: I18NNamespace): void; + run(toolId: string, ...args: any[]): boolean; + // (undocumented) + readonly tools: Map; + unRegister(toolId: string): void; +} + +// @public +class ToolSettings { + static animationTime: BeDuration; + static doubleClickTimeout: BeDuration; + static doubleClickToleranceInches: number; + static doubleTapTimeout: BeDuration; + static noMotionTimeout: BeDuration; + static preserveWorldUp: boolean; + static startDragDelay: BeDuration; + static startDragDistanceInches: number; + static touchMoveDelay: BeDuration; + static touchMoveDistanceInches: number; + static viewToolPickRadiusInches: number; + static walkCameraAngle: Angle; + static walkEnforceZUp: boolean; + static walkVelocity: number; + static wheelLineFactor: number; + static wheelPageFactor: number; + static wheelZoomBumpDistance: number; + static wheelZoomRatio: number; +} + +// @public +class ToolSettingsPropertyRecord extends PropertyRecord { + constructor(value: PropertyValue, property: PropertyDescription, editorPosition: EditorPosition); + // (undocumented) + editorPosition: EditorPosition; +} + +// @public +class ToolSettingsPropertySyncItem { + constructor(value: ToolSettingsValue, propertyName: string); + // (undocumented) + propertyName: string; + // (undocumented) + value: ToolSettingsValue; +} + +// WARNING: valueFormat has incomplete type information +// @public +class ToolSettingsValue implements PrimitiveValue { + constructor(value?: number | string | boolean | Date, displayValue?: string); + // (undocumented) + clone(): ToolSettingsValue; + // (undocumented) + displayValue?: string; + // (undocumented) + readonly hasDisplayValue: boolean; + // (undocumented) + readonly isNullValue: boolean; + // (undocumented) + update(newValue: ToolSettingsValue): boolean; + // (undocumented) + value?: number | string | boolean | Date; +} + +// @public (undocumented) +class ToolState { + // (undocumented) + clone(): ToolState; + // (undocumented) + coordLockOvr: CoordinateLockOverrides; + // (undocumented) + locateCircleOn: boolean; + // (undocumented) + setFrom(other: ToolState): void; +} + +// @public +interface ToolTipOptions { + // (undocumented) + duration?: BeDuration; + // (undocumented) + placement?: string; +} + +// @public +class TouchCursor implements CanvasDecoration { + protected constructor(vp: ScreenViewport); + // (undocumented) + protected _fillColor: string; + // (undocumented) + protected _inTouchTap: boolean; + // (undocumented) + protected _isDragging: boolean; + // (undocumented) + protected _offsetPosition: Point3d; + // (undocumented) + protected _outlineColor: string; + // (undocumented) + protected _size: number; + // (undocumented) + protected _yOffset: number; + // (undocumented) + static createFromTouchTap(ev: BeTouchEvent): TouchCursor | undefined; + // (undocumented) + doTouchEnd(ev: BeTouchEvent): void; + // (undocumented) + doTouchMove(ev: BeTouchEvent): boolean; + // (undocumented) + doTouchMoveStart(ev: BeTouchEvent, startEv: BeTouchEvent): boolean; + // (undocumented) + doTouchStart(ev: BeTouchEvent): void; + // (undocumented) + doTouchTap(ev: BeTouchEvent): Promise; + // (undocumented) + drawDecoration(ctx: CanvasRenderingContext2D): void; + // (undocumented) + protected getFillColor(isSelected: boolean): string; + // (undocumented) + isButtonHandled(ev: BeButtonEvent): boolean; + // (undocumented) + protected isSelected(pt: XAndY): boolean; + // (undocumented) + position: Point3d; + // (undocumented) + protected setPosition(vp: Viewport, worldLocation: Point3d): boolean; +} + +// @public +enum TsHorizontalAlignment { + // (undocumented) + Center = 2, + // (undocumented) + Justify = 4, + // (undocumented) + Left = 1, + // (undocumented) + Right = 3 +} + +// @public +enum TsVerticalAlignment { + // (undocumented) + Bottom = 3, + // (undocumented) + Middle = 2, + // (undocumented) + Top = 1 +} + +// @public +class TwoWayViewportSync { + connect(view1: Viewport, view2: Viewport): void; + disconnect(): void; +} + +// @public +class Unit implements UnitProps { + constructor(name: string, label: string, unitFamily: string); + // (undocumented) + isValid: boolean; + // (undocumented) + label: string; + // (undocumented) + name: string; + // (undocumented) + unitFamily: string; +} + +// @public +enum UsesDragSelect { + Box = 0, + Line = 1, + None = 2 +} + +// @public +enum UsesFence { + Check = 0, + None = 2, + Required = 1 +} + +// @public +enum UsesSelection { + Check = 0, + None = 2, + Required = 1 +} + +// @public +interface ViewChangeOptions { + animateFrustumChange?: boolean; + animationTime?: BeDuration; + marginPercent?: MarginPercent; + saveInUndo?: boolean; +} + +// @public +class ViewFrustum { + protected adjustAspectRatio(origin: Point3d, delta: Vector3d): void; + // (undocumented) + static createFromViewport(vp: Viewport, view?: ViewState): ViewFrustum | undefined; + // (undocumented) + static createFromViewportAndPlane(vp: Viewport, plane: Plane3dByOriginAndUnitNormal): ViewFrustum | undefined; + // (undocumented) + fromView(from: XYZ, to?: XYZ): void; + // (undocumented) + frustFraction: number; + getFrustum(sys?: CoordSystem, adjustedBox?: boolean, box?: Frustum): Frustum; + // (undocumented) + getPixelSizeAtPoint(inPoint?: Point3d): number; + // (undocumented) + getViewCorners(): Range3d; + // (undocumented) + readonly invalidFrustum: boolean; + static nearScale24: number; + npcToView(pt: Point3d, out?: Point3d): Point3d; + npcToViewArray(pts: Point3d[]): void; + npcToWorld(pt: XYAndZ, out?: Point3d): Point3d; + npcToWorldArray(pts: Point3d[]): void; + readonly rotation: Matrix3d; + // (undocumented) + toView(from: XYZ, to?: XYZ): void; + view: ViewState; + view4dToWorld(input: Point4d, out?: Point3d): Point3d; + view4dToWorldArray(viewPts: Point4d[], worldPts: Point3d[]): void; + readonly viewDelta: Vector3d; + readonly viewDeltaUnexpanded: Vector3d; + readonly viewOrigin: Point3d; + readonly viewOriginUnexpanded: Point3d; + viewToNpc(pt: Point3d, out?: Point3d): Point3d; + viewToNpcArray(pts: Point3d[]): void; + viewToWorld(input: XYAndZ, out?: Point3d): Point3d; + viewToWorldArray(pts: Point3d[]): void; + worldToNpc(pt: XYAndZ, out?: Point3d): Point3d; + worldToNpcArray(pts: Point3d[]): void; + // (undocumented) + readonly worldToNpcMap: Map4d; + worldToView(input: XYAndZ, out?: Point3d): Point3d; + worldToView4d(input: XYAndZ, out?: Point4d): Point4d; + worldToView4dArray(worldPts: Point3d[], viewPts: Point4d[]): void; + worldToViewArray(pts: Point3d[]): void; + // (undocumented) + readonly worldToViewMap: Map4d; + // (undocumented) + readonly zClipAdjusted: boolean; +} + +// @public (undocumented) +class ViewHandleArray { + constructor(viewTool: ViewManip); + // (undocumented) + add(handle: ViewingToolHandle): void; + // (undocumented) + readonly count: number; + // (undocumented) + drawHandles(context: DecorateContext): void; + // (undocumented) + empty(): void; + // (undocumented) + focus: number; + // (undocumented) + focusDrag: boolean; + // (undocumented) + readonly focusHandle: ViewingToolHandle | undefined; + // (undocumented) + focusHitHandle(): void; + // (undocumented) + getByIndex(index: number): ViewingToolHandle | undefined; + // (undocumented) + handles: ViewingToolHandle[]; + hasHandle(handleType: ViewHandleType): boolean; + // (undocumented) + readonly hitHandle: ViewingToolHandle | undefined; + // (undocumented) + hitHandleIndex: number; + // (undocumented) + motion(ev: BeButtonEvent): void; + // (undocumented) + onReinitialize(): void; + // (undocumented) + setFocus(index: number): void; + // (undocumented) + testHit(ptScreen: Point3d, forced?: ViewHandleType): boolean; + // (undocumented) + viewport?: Viewport; + // (undocumented) + viewTool: ViewManip; +} + +// @public (undocumented) +enum ViewHandleType { + // (undocumented) + Fly = 64, + // (undocumented) + Look = 128, + // (undocumented) + None = 0, + // (undocumented) + Pan = 4, + // (undocumented) + Rotate = 1, + // (undocumented) + Scroll = 8, + // (undocumented) + TargetCenter = 2, + // (undocumented) + Walk = 32, + // (undocumented) + Zoom = 16 +} + +// @public (undocumented) +enum ViewHandleWeight { + // (undocumented) + Bold = 3, + // (undocumented) + FatDot = 8, + // (undocumented) + Normal = 2, + // (undocumented) + Thin = 1, + // (undocumented) + VeryBold = 4 +} + +// @public (undocumented) +class ViewingToolHandle { + constructor(viewTool: ViewManip); + // (undocumented) + checkOneShot(): boolean; + // (undocumented) + abstract doManipulation(ev: BeButtonEvent, inDynamics: boolean): boolean; + // (undocumented) + drawHandle(_context: DecorateContext, _hasFocus: boolean): void; + // (undocumented) + abstract firstPoint(ev: BeButtonEvent): boolean; + // (undocumented) + focusIn(): void; + // (undocumented) + focusOut(): void; + // (undocumented) + getHandleCursor(): string; + // (undocumented) + readonly handleType: ViewHandleType; + // (undocumented) + motion(_ev: BeButtonEvent): boolean; + // (undocumented) + noMotion(_ev: BeButtonEvent): boolean; + // (undocumented) + onReinitialize(): void; + // (undocumented) + abstract testHandleForHit(ptScreen: Point3d, out: { + distance: number; + priority: ViewManipPriority; + }): boolean; + // (undocumented) + viewTool: ViewManip; +} + +// @public +class ViewManager { + addDecorator(decorator: Decorator): () => void; + addViewport(newVp: ScreenViewport): BentleyStatus; + // (undocumented) + beginDynamicsMode(): void; + clearSelectedView(): void; + // (undocumented) + readonly crossHairCursor: string; + // (undocumented) + cursor: string; + // (undocumented) + readonly decorators: Decorator[]; + // (undocumented) + readonly doesHostHaveFocus: boolean; + dropDecorator(decorator: Decorator): void; + dropViewport(vp: ScreenViewport, disposeOfViewport?: boolean): BentleyStatus; + // (undocumented) + readonly dynamicsCursor: string; + // (undocumented) + endDynamicsMode(): void; + forEachViewport(func: (vp: ScreenViewport) => void): void; + getDecorationGeometry(hit: HitDetail): GeometryStreamProps | undefined; + getDecorationToolTip(hit: HitDetail): Promise; + getFirstOpenView(): ScreenViewport | undefined; + // (undocumented) + readonly grabbingCursor: string; + // (undocumented) + readonly grabCursor: string; + // (undocumented) + inDynamicsMode: boolean; + invalidateDecorationsAllViews(): void; + // (undocumented) + invalidateScenes(): void; + // (undocumented) + invalidateViewportScenes(): void; + // (undocumented) + notifySelectedViewportChanged(previous: ScreenViewport | undefined, current: ScreenViewport | undefined): void; + readonly onBeginRender: BeEvent<() => void>; + onDecorationButtonEvent(hit: HitDetail, ev: BeButtonEvent): Promise; + readonly onFinishRender: BeEvent<() => void>; + // (undocumented) + onInitialized(): void; + // (undocumented) + onNewTilesReady(): void; + readonly onSelectedViewportChanged: BeUiEvent; + // (undocumented) + onSelectionSetChanged(_iModel: IModelConnection): void; + // (undocumented) + onShutDown(): void; + readonly onViewClose: BeUiEvent; + readonly onViewOpen: BeUiEvent; + readonly onViewResume: BeUiEvent; + readonly onViewSuspend: BeUiEvent; + renderLoop(): void; + // (undocumented) + readonly sceneInvalidated: boolean; + readonly selectedView: ScreenViewport | undefined; + setSelectedView(vp: ScreenViewport | undefined): BentleyStatus; + setViewCursor(cursor?: string): void; + // (undocumented) + validateViewportScenes(): void; +} + +// @public +class ViewManip extends ViewTool { + constructor(viewport: ScreenViewport | undefined, handleMask: number, oneShot: boolean, isDraggingRequired?: boolean); + // (undocumented) + protected _forcedHandle: ViewHandleType; + // (undocumented) + protected static _useViewAlignedVolume: boolean; + // (undocumented) + changeViewport(vp?: ScreenViewport): void; + // (undocumented) + decorate(context: DecorateContext): void; + // (undocumented) + enforceZUp(pivotPoint: Point3d): boolean; + // (undocumented) + static fitView(viewport: ScreenViewport, doAnimate: boolean, marginPercent?: MarginPercent): void; + // (undocumented) + frustumValid: boolean; + // (undocumented) + static getFocusPlaneNpc(vp: Viewport): number; + // (undocumented) + handleMask: number; + // (undocumented) + inHandleModify: boolean; + // (undocumented) + isDragging: boolean; + // (undocumented) + isDraggingRequired: boolean; + isPointVisible(testPt: Point3d): boolean; + // (undocumented) + readonly isZUp: boolean; + // (undocumented) + lensAngleMatches(angle: Angle, tolerance: number): boolean; + // (undocumented) + nPts: number; + // (undocumented) + onCleanup(): void; + // (undocumented) + onDataButtonDown(ev: BeButtonEvent): Promise; + // (undocumented) + onDataButtonUp(_ev: BeButtonEvent): Promise; + // (undocumented) + oneShot: boolean; + // (undocumented) + onMouseEndDrag(ev: BeButtonEvent): Promise; + // (undocumented) + onMouseMotion(ev: BeButtonEvent): Promise; + // (undocumented) + onMouseMotionStopped(ev: BeButtonEvent): Promise; + // (undocumented) + onMouseNoMotion(ev: BeButtonEvent): Promise; + // (undocumented) + onMouseStartDrag(ev: BeButtonEvent): Promise; + // (undocumented) + onMouseWheel(inputEv: BeWheelEvent): Promise; + // (undocumented) + onPostInstall(): void; + // (undocumented) + onReinitialize(): void; + // (undocumented) + onTouchCancel(ev: BeTouchEvent): Promise; + // (undocumented) + onTouchComplete(ev: BeTouchEvent): Promise; + // (undocumented) + onTouchMove(ev: BeTouchEvent): Promise; + // (undocumented) + onTouchMoveStart(ev: BeTouchEvent, startEv: BeTouchEvent): Promise; + // (undocumented) + onTouchTap(ev: BeTouchEvent): Promise; + // (undocumented) + processFirstPoint(ev: BeButtonEvent): boolean; + // (undocumented) + processPoint(ev: BeButtonEvent, inDynamics: boolean): boolean; + // (undocumented) + setCameraLensAngle(lensAngle: Angle, retainEyePoint: boolean): ViewStatus; + setTargetCenterWorld(pt: Point3d, lockTarget: boolean, saveTarget: boolean): void; + // (undocumented) + startHandleDrag(ev: BeButtonEvent, forcedHandle?: ViewHandleType): Promise; + // (undocumented) + stoppedOverHandle: boolean; + // (undocumented) + targetCenterLocked: boolean; + // (undocumented) + targetCenterValid: boolean; + // (undocumented) + readonly targetCenterWorld: Point3d; + // (undocumented) + updateTargetCenter(): void; + // (undocumented) + viewHandles: ViewHandleArray; +} + +// @public (undocumented) +enum ViewManipPriority { + // (undocumented) + High = 1000, + // (undocumented) + Low = 1, + // (undocumented) + Medium = 100, + // (undocumented) + Normal = 10 +} + +// @public +class Viewport implements IDisposable { + protected constructor(target: RenderTarget); + // (undocumented) + protected readonly _viewRange: ViewRect; + // (undocumented) + addDecorations(_decorations: Decorations): void; + readonly alwaysDrawn: Id64Set | undefined; + // (undocumented) + readonly analysisStyle: AnalysisStyle | undefined; + // (undocumented) + animate(): void; + // (undocumented) + animateFrustumChange(start: Frustum, end: Frustum, animationTime?: BeDuration): void; + // (undocumented) + animationFraction: number; + // (undocumented) + applyViewState(val: ViewState, animationTime?: BeDuration): void; + // (undocumented) + readonly auxCoordSystem: AuxCoordSystemState; + // (undocumented) + readonly backgroundMapPlane: Plane3dByOriginAndUnitNormal | undefined; + // (undocumented) + changeDynamics(dynamics: GraphicList | undefined): void; + changeView(view: ViewState): void; + clearAlwaysDrawn(): void; + clearNeverDrawn(): void; + // (undocumented) + computeViewRange(): Range3d; + // (undocumented) + continuousRendering: boolean; + // (undocumented) + createSceneContext(): SceneContext; + // WARNING: The type "Tile.DebugBoundingBoxes" needs to be exported by the package (e.g. added to index.ts) + debugBoundingBoxes: Tile.DebugBoundingBoxes; + determineVisibleDepthRange(rect?: ViewRect, result?: DepthRangeNpc): DepthRangeNpc | undefined; + // (undocumented) + dispose(): void; + featureOverrideProvider: FeatureOverrideProvider | undefined; + flashDuration: number; + flashIntensity: number; + flashUpdateTime?: BeTimePoint; + freezeScene: boolean; + // (undocumented) + fromView(from: XYZ, to?: XYZ): void; + // (undocumented) + readonly frustFraction: number; + // (undocumented) + getAuxCoordOrigin(result?: Point3d): Point3d; + // (undocumented) + getAuxCoordRotation(result?: Matrix3d): Matrix3d; + getContrastToBackgroundColor(): ColorDef; + getFrustum(sys?: CoordSystem, adjustedBox?: boolean, box?: Frustum): Frustum; + // WARNING: The type "Pixel.Buffer" needs to be exported by the package (e.g. added to index.ts) + getPixelDataNpcPoint(pixels: Pixel.Buffer, x: number, y: number, out?: Point3d): Point3d | undefined; + // WARNING: The type "Pixel.Buffer" needs to be exported by the package (e.g. added to index.ts) + getPixelDataWorldPoint(pixels: Pixel.Buffer, x: number, y: number, out?: Point3d): Point3d | undefined; + getPixelSizeAtPoint(point?: Point3d): number; + getWorldFrustum(box?: Frustum): Frustum; + hilite: Hilite.Settings; + readonly iModel: IModelConnection; + // (undocumented) + invalidateDecorations(): void; + // (undocumented) + invalidateRenderPlan(): void; + // (undocumented) + invalidateScene(): void; + readonly isAlwaysDrawnExclusive: boolean; + // (undocumented) + readonly isAspectRatioLocked: boolean; + readonly isCameraOn: boolean; + // (undocumented) + readonly isContextRotationRequired: boolean; + isFadeOutActive: boolean; + readonly isGridOn: boolean; + // (undocumented) + readonly isPointAdjustmentRequired: boolean; + // (undocumented) + readonly isSnapAdjustmentRequired: boolean; + lastFlashedElem?: string; + static nearScale24: number; + readonly neverDrawn: Id64Set | undefined; + npcToView(pt: Point3d, out?: Point3d): Point3d; + npcToViewArray(pts: Point3d[]): void; + npcToWorld(pt: XYAndZ, out?: Point3d): Point3d; + npcToWorldArray(pts: Point3d[]): void; + numReadyTiles: number; + readonly numRequestedTiles: number; + numSelectedTiles: number; + readonly onAlwaysDrawnChanged: BeEvent<(vp: Viewport) => void>; + readonly onNeverDrawnChanged: BeEvent<(vp: Viewport) => void>; + readonly onRender: BeEvent<(vp: Viewport) => void>; + readonly onViewChanged: BeEvent<(vp: Viewport) => void>; + pixelsFromInches(inches: number): number; + // (undocumented) + readonly pixelsPerInch: number; + // (undocumented) + pointToGrid(point: Point3d): void; + readImage(rect?: ViewRect, targetSize?: Point2d, flipVertically?: boolean): ImageBuffer | undefined; + // WARNING: The type "Pixel.Selector" needs to be exported by the package (e.g. added to index.ts) + // WARNING: The type "Pixel.Receiver" needs to be exported by the package (e.g. added to index.ts) + readPixels(rect: ViewRect, selector: Pixel.Selector, receiver: Pixel.Receiver, excludeNonLocatable?: boolean): void; + // (undocumented) + removeAnimator(): void; + // (undocumented) + renderFrame(): boolean; + readonly rotation: Matrix3d; + scroll(screenDist: Point2d, options?: ViewChangeOptions): void; + setAlwaysDrawn(ids: Id64Set, exclusive?: boolean): void; + setFlashed(id: string | undefined, duration: number): void; + setNeverDrawn(ids: Id64Set): void; + setStandardRotation(id: StandardViewId): void; + setupFromView(): ViewStatus; + setupViewFromFrustum(inFrustum: Frustum): boolean; + // (undocumented) + readonly sync: SyncFlags; + synchWithView(_saveInUndo: boolean): void; + // (undocumented) + readonly target: RenderTarget; + // (undocumented) + toView(from: XYZ, to?: XYZ): void; + turnCameraOn(lensAngle?: Angle): ViewStatus; + static undoDelay: BeDuration; + readonly view: ViewState; + view4dToWorld(input: Point4d, out?: Point3d): Point3d; + view4dToWorldArray(viewPts: Point4d[], worldPts: Point3d[]): void; + readonly viewDelta: Vector3d; + readonly viewFlags: ViewFlags; + // (undocumented) + readonly viewFrustum: ViewFrustum; + readonly viewportId: number; + readonly viewRect: ViewRect; + viewToNpc(pt: Point3d, out?: Point3d): Point3d; + viewToNpcArray(pts: Point3d[]): void; + viewToWorld(input: XYAndZ, out?: Point3d): Point3d; + viewToWorldArray(pts: Point3d[]): void; + // (undocumented) + readonly wantAntiAliasLines: AntiAliasPref; + // (undocumented) + readonly wantAntiAliasText: AntiAliasPref; + worldToNpc(pt: XYAndZ, out?: Point3d): Point3d; + worldToNpcArray(pts: Point3d[]): void; + worldToView(input: XYAndZ, out?: Point3d): Point3d; + worldToView4d(input: XYAndZ, out?: Point4d): Point4d; + worldToView4dArray(worldPts: Point3d[], viewPts: Point4d[]): void; + worldToViewArray(pts: Point3d[]): void; + readonly worldToViewMap: Map4d; + zoom(newCenter: Point3d | undefined, factor: number, options?: ViewChangeOptions): void; + zoomToElementProps(elementProps: ElementProps[], options?: ViewChangeOptions & ZoomToOptions): void; + zoomToElements(ids: Id64Arg, options?: ViewChangeOptions & ZoomToOptions): Promise; + zoomToPlacementProps(placementProps: PlacementProps[], options?: ViewChangeOptions & ZoomToOptions): void; + zoomToVolume(volume: LowAndHighXYZ | LowAndHighXY, options?: ViewChangeOptions): void; +} + +// @public +interface ViewportAnimator { + animate(viewport: Viewport): RemoveMe; + onInterrupted(viewport: Viewport): void; +} + +// @public +class ViewRect { + constructor(left?: number, top?: number, right?: number, bottom?: number); + readonly area: number; + readonly aspect: number; + bottom: number; + clone(result?: ViewRect): ViewRect; + computeOverlap(other: ViewRect, out?: ViewRect): ViewRect | undefined; + containsPoint(point: XAndY): boolean; + equals(other: ViewRect): boolean; + // (undocumented) + extend(other: ViewRect): void; + height: number; + init(left: number, top: number, right: number, bottom: number): void; + initFromPoints(topLeft: XAndY, bottomRight: XAndY): void; + initFromRange(input: LowAndHighXY): void; + inset(deltaX: number, deltaY: number): void; + insetByPercent(percent: number): void; + insetUniform(offset: number): void; + isContained(other: ViewRect): boolean; + readonly isNull: boolean; + readonly isValid: boolean; + left: number; + overlaps(other: ViewRect): boolean; + right: number; + scaleAboutCenter(xScale: number, yScale: number): void; + setFrom(other: ViewRect): void; + top: number; + width: number; +} + +// @public +class ViewRedoTool extends ViewTool { + // (undocumented) + onPostInstall(): void; + // (undocumented) + static toolId: string; +} + +// @public +class ViewState extends ElementState { + protected constructor(props: ViewDefinitionProps, iModel: IModelConnection, categorySelector: CategorySelectorState, displayStyle: DisplayStyleState); + // (undocumented) + protected _featureOverridesDirty: boolean; + // (undocumented) + protected _selectionSetDirty: boolean; + // (undocumented) + protected adjustAspectRatio(windowAspect: number): void; + abstract allow3dManipulations(): boolean; + readonly analysisStyle: AnalysisStyle | undefined; + // (undocumented) + readonly areAllTileTreesLoaded: boolean; + readonly areFeatureOverridesDirty: boolean; + readonly auxiliaryCoordinateSystem: AuxCoordSystemState; + readonly backgroundColor: ColorDef; + calculateFrustum(result?: Frustum): Frustum | undefined; + // (undocumented) + categorySelector: CategorySelectorState; + changeCategoryDisplay(categories: Id64Arg, display: boolean, enableAllSubCategories?: boolean): void; + // (undocumented) + static readonly className: string; + abstract computeFitRange(): Range3d; + // (undocumented) + computeWorldToNpc: { + frustFraction: number; + map: Map4d | undefined; + } + // (undocumented) + abstract createAuxCoordSystem(acsName: string): AuxCoordSystemState; + // (undocumented) + createClassification(context: SceneContext): void; + static createFromProps(_props: ViewStateProps, _iModel: IModelConnection): ViewState | undefined; + // (undocumented) + createScene(context: SceneContext): void; + // (undocumented) + createTerrain(context: SceneContext): void; + decorate(context: DecorateContext): void; + // (undocumented) + description?: string; + // (undocumented) + displayStyle: DisplayStyleState; + // (undocumented) + drawGrid(context: DecorateContext): void; + dropSubCategoryOverride(id: Id64String): void; + equals(other: this): boolean; + equalState(other: ViewState): boolean; + abstract forEachModel(func: (model: GeometricModelState) => void): void; + forEachTileTreeModel(func: (model: TileTreeModelState) => void): void; + getAspectRatio(): number; + getAspectRatioSkew(): number; + getAuxiliaryCoordinateSystemId(): Id64String; + getCenter(result?: Point3d): Point3d; + getDetail(name: string): any; + // (undocumented) + getDetails(): any; + getExtentLimits: { + max: number; + min: number; + } + abstract getExtents(): Vector3d; + getGridOrientation(): GridOrientationType; + getGridSettings(vp: Viewport, origin: Point3d, rMatrix: Matrix3d, orientation: GridOrientationType): void; + // (undocumented) + getGridSpacing(): XAndY; + // (undocumented) + getGridsPerRef(): number; + abstract getOrigin(): Point3d; + abstract getRotation(): Matrix3d; + // (undocumented) + static getStandardViewMatrix(id: StandardViewId): Matrix3d; + getSubCategoryAppearance(id: Id64String): SubCategoryAppearance; + getSubCategoryOverride(id: Id64String): SubCategoryOverride | undefined; + getTargetPoint(result?: Point3d): Point3d; + getViewClip(): ClipVector | undefined; + abstract getViewedExtents(): AxisAlignedBox3d; + getXVector(result?: Vector3d): Vector3d; + getYVector(result?: Vector3d): Vector3d; + getZVector(result?: Vector3d): Vector3d; + // (undocumented) + is3d(): this is ViewState3d; + // (undocumented) + isPrivate?: boolean; + // (undocumented) + readonly isSelectionSetDirty: boolean; + // (undocumented) + isSpatialView(): this is SpatialViewState; + // (undocumented) + isSubCategoryVisible(id: Id64String): boolean; + load(): Promise; + lookAtViewAlignedVolume(volume: Range3d, aspect?: number, margin?: MarginPercent): void; + lookAtVolume(volume: LowAndHighXYZ | LowAndHighXY, aspect?: number, margin?: MarginPercent): void; + readonly name: string; + abstract onRenderFrame(_viewport: Viewport): void; + overrideSubCategory(id: Id64String, ovr: SubCategoryOverride): void; + peekDetail(name: string): any; + removeDetail(name: string): void; + // WARNING: The type "RenderScheduleState.Script" needs to be exported by the package (e.g. added to index.ts) + readonly scheduleScript: RenderScheduleState.Script | undefined; + // (undocumented) + scheduleTime: number; + setAspectRatioSkew(val: number): void; + setAuxiliaryCoordinateSystem(acs?: AuxCoordSystemState): void; + setCategorySelector(categories: CategorySelectorState): void; + setDetail(name: string, value: any): void; + // (undocumented) + setDisplayStyle(style: DisplayStyleState): void; + abstract setExtents(viewDelta: Vector3d): void; + setFeatureOverridesDirty(dirty?: boolean): void; + setGridSettings(orientation: GridOrientationType, spacing: Point2d, gridsPerRef: number): void; + abstract setOrigin(viewOrg: Point3d): void; + abstract setRotation(viewRot: Matrix3d): void; + setRotationAboutPoint(rotation: Matrix3d, point?: Point3d): void; + // (undocumented) + setSelectionSetDirty(dirty?: boolean): void; + setStandardRotation(id: StandardViewId): void; + setupFromFrustum(inFrustum: Frustum): ViewStatus; + setViewClip(clip?: ClipVector): void; + // (undocumented) + showFrustumErrorMessage(status: ViewStatus): void; + readonly subCategories: ViewSubCategories; + // (undocumented) + toJSON(): ViewDefinitionProps; + undoTime?: BeTimePoint; + // (undocumented) + validateViewDelta(delta: Vector3d, messageNeeded?: boolean): ViewStatus; + viewFlags: ViewFlags; + viewsCategory(id: Id64String): boolean; + abstract viewsModel(modelId: Id64String): boolean; +} + +// @public +class ViewState2d extends ViewState { + constructor(props: ViewDefinition2dProps, iModel: IModelConnection, categories: CategorySelectorState, displayStyle: DisplayStyle2dState); + // (undocumented) + allow3dManipulations(): boolean; + // (undocumented) + readonly angle: Angle; + // (undocumented) + readonly baseModelId: Id64String; + // (undocumented) + static readonly className: string; + // (undocumented) + computeFitRange(): Range3d; + // (undocumented) + createAuxCoordSystem(acsName: string): AuxCoordSystemState; + // (undocumented) + readonly delta: Point2d; + // (undocumented) + equalState(other: ViewState2d): boolean; + // (undocumented) + forEachModel(func: (model: GeometricModelState) => void): void; + // (undocumented) + getExtents(): Vector3d; + // (undocumented) + getOrigin(): Point3d; + // (undocumented) + getRotation(): Matrix3d; + // (undocumented) + getViewedExtents(): AxisAlignedBox3d; + getViewedModel(): GeometricModel2dState | undefined; + // (undocumented) + load(): Promise; + // (undocumented) + onRenderFrame(_viewport: Viewport): void; + // (undocumented) + readonly origin: Point2d; + // (undocumented) + setExtents(delta: Vector3d): void; + // (undocumented) + setOrigin(origin: Point3d): void; + // (undocumented) + setRotation(rot: Matrix3d): void; + // (undocumented) + toJSON(): ViewDefinition2dProps; + // (undocumented) + viewsModel(modelId: Id64String): boolean; +} + +// @public +class ViewState3d extends ViewState { + constructor(props: ViewDefinition3dProps, iModel: IModelConnection, categories: CategorySelectorState, displayStyle: DisplayStyle3dState); + protected _cameraOn: boolean; + // (undocumented) + allow3dManipulations(): boolean; + calcLensAngle(): Angle; + // (undocumented) + protected static calculateMaxDepth(delta: Vector3d, zVec: Vector3d): number; + readonly camera: Camera; + centerEyePoint(backDistance?: number): void; + centerFocusDistance(): void; + // (undocumented) + static readonly className: string; + // (undocumented) + createAuxCoordSystem(acsName: string): AuxCoordSystemState; + // (undocumented) + decorate(context: DecorateContext): void; + // (undocumented) + protected drawGroundPlane(context: DecorateContext): void; + // (undocumented) + protected drawSkyBox(context: DecorateContext): void; + // (undocumented) + protected enableCamera(): void; + // (undocumented) + equalState(other: ViewState3d): boolean; + readonly extents: Vector3d; + forceMinFrontDist: number; + getBackDistance(): number; + // (undocumented) + getDisplayStyle3d(): DisplayStyle3dState; + // (undocumented) + getExtents(): Vector3d; + getEyePoint(): Point3d; + getFocusDistance(): number; + getFrontDistance(): number; + getGroundElevation(): number; + getGroundExtents(vp?: Viewport): AxisAlignedBox3d; + getLensAngle(): Angle; + // (undocumented) + getOrigin(): Point3d; + // (undocumented) + getRotation(): Matrix3d; + getTargetPoint(result?: Point3d): Point3d; + // (undocumented) + readonly isCameraOn: boolean; + readonly isCameraValid: boolean; + // (undocumented) + isEyePointAbove(elevation: number): boolean; + lookAt(eyePoint: XYAndZ, targetPoint: XYAndZ, upVector: Vector3d, newExtents?: XAndY, frontDistance?: number, backDistance?: number): ViewStatus; + lookAtUsingLensAngle(eyePoint: Point3d, targetPoint: Point3d, upVector: Vector3d, fov: Angle, frontDistance?: number, backDistance?: number): ViewStatus; + // (undocumented) + minimumFrontDistance(): number; + moveCameraLocal(distance: Vector3d): ViewStatus; + moveCameraWorld(distance: Vector3d): ViewStatus; + // (undocumented) + onRenderFrame(_viewport: Viewport): void; + readonly origin: Point3d; + rotateCameraLocal(angle: Angle, axis: Vector3d, aboutPt?: Point3d): ViewStatus; + rotateCameraWorld(angle: Angle, axis: Vector3d, aboutPt?: Point3d): ViewStatus; + readonly rotation: Matrix3d; + // (undocumented) + setExtents(extents: XYAndZ): void; + setEyePoint(pt: XYAndZ): void; + setFocusDistance(dist: number): void; + setLensAngle(angle: Angle): void; + // (undocumented) + setOrigin(origin: XYAndZ): void; + // (undocumented) + setRotation(rot: Matrix3d): void; + // (undocumented) + setupFromFrustum(frustum: Frustum): ViewStatus; + // (undocumented) + supportsCamera(): boolean; + // (undocumented) + toJSON(): ViewDefinition3dProps; + turnCameraOff(): void; + verifyFocusPlane(): void; +} + +// @public +enum ViewStatus { + // (undocumented) + AlreadyAttached = 2, + // (undocumented) + DrawFailure = 4, + // (undocumented) + InvalidLens = 14, + // (undocumented) + InvalidTargetPoint = 13, + // (undocumented) + InvalidUpVector = 12, + // (undocumented) + InvalidViewport = 15, + // (undocumented) + InvalidWindow = 7, + // (undocumented) + MaxDisplayDepth = 11, + // (undocumented) + MaxWindow = 9, + // (undocumented) + MaxZoom = 10, + // (undocumented) + MinWindow = 8, + // (undocumented) + ModelNotFound = 6, + // (undocumented) + NotAttached = 3, + // (undocumented) + NotResized = 5, + // (undocumented) + Success = 0, + // (undocumented) + ViewNotInitialized = 1 +} + +// @public +class ViewSubCategories { + getSubCategories(categoryId: string): Id64Set | undefined; + getSubCategoryAppearance(subCategoryId: Id64String): SubCategoryAppearance | undefined; + load(categoryIds: Set, iModel: IModelConnection): Promise; + // (undocumented) + loadFromRows(rows: any[]): void; + update(addedCategoryIds: Set, iModel: IModelConnection): Promise; +} + +// @public +class ViewToggleCameraTool extends ViewTool { + // (undocumented) + onInstall(): boolean; + // (undocumented) + onPostInstall(): void; + // (undocumented) + static toolId: string; +} + +// @public +class ViewTool extends InteractiveTool { + constructor(viewport?: ScreenViewport | undefined); + // (undocumented) + beginDynamicUpdate(): void; + // (undocumented) + endDynamicUpdate(): void; + exitTool(): void; + // (undocumented) + inDynamicUpdate: boolean; + // (undocumented) + onResetButtonUp(_ev: BeButtonEvent): Promise; + // (undocumented) + run(): boolean; + // (undocumented) + static showPrompt(prompt: string): void; + // (undocumented) + viewport?: ScreenViewport | undefined; +} + +// @public +class ViewUndoTool extends ViewTool { + // (undocumented) + onPostInstall(): void; + // (undocumented) + static toolId: string; +} + +// @public +class WalkViewTool extends ViewManip { + constructor(vp: ScreenViewport, oneShot?: boolean, isDraggingRequired?: boolean); + // (undocumented) + onReinitialize(): void; + // (undocumented) + static toolId: string; +} + +// @public +class WheelEventProcessor { + // (undocumented) + static process(ev: BeWheelEvent, doUpdate: boolean): Promise; +} + +// @public +class WindowAreaTool extends ViewTool { + // (undocumented) + decorate(context: DecorateContext): void; + // (undocumented) + onDataButtonDown(ev: BeButtonEvent): Promise; + // (undocumented) + onMouseMotion(ev: BeButtonEvent): Promise; + // (undocumented) + onPostInstall(): void; + // (undocumented) + onReinitialize(): void; + // (undocumented) + onResetButtonUp(ev: BeButtonEvent): Promise; + // (undocumented) + onTouchCancel(ev: BeTouchEvent): Promise; + // (undocumented) + onTouchComplete(ev: BeTouchEvent): Promise; + // (undocumented) + onTouchMove(ev: BeTouchEvent): Promise; + // (undocumented) + onTouchMoveStart(ev: BeTouchEvent, startEv: BeTouchEvent): Promise; + // (undocumented) + onTouchTap(ev: BeTouchEvent): Promise; + // (undocumented) + static toolId: string; +} + +// @public +interface ZoomToOptions { + placementRelativeId?: StandardViewId; + standardViewId?: StandardViewId; + viewRotation?: Matrix3d; +} + +// @public +class ZoomViewTool extends ViewManip { + constructor(vp: ScreenViewport, oneShot?: boolean, isDraggingRequired?: boolean); + // (undocumented) + onReinitialize(): void; + // (undocumented) + static toolId: string; +} + +// WARNING: Unsupported export: ToolType +// WARNING: Unsupported export: ToolList +// WARNING: Unsupported export: MarkerImage +// WARNING: Unsupported export: MarkerFillStyle +// WARNING: Unsupported export: MarkerTextAlign +// WARNING: Unsupported export: MarkerTextBaseline +// WARNING: Unsupported export: GraphicList +// WARNING: Unsupported export: CanvasDecorationList +// WARNING: Unsupported export: AnimationBranchStates +// WARNING: Unsupported export: PropertyEditorParams +// WARNING: Unsupported export: PropertyValue +// (No @packagedocumentation comment for this package) diff --git a/common/api/imodeljs-i18n.api.ts b/common/api/imodeljs-i18n.api.ts new file mode 100644 index 0000000..264d162 --- /dev/null +++ b/common/api/imodeljs-i18n.api.ts @@ -0,0 +1,33 @@ +// @public +class I18N { + constructor(nameSpaces: string[], defaultNameSpace: string, options?: I18NOptions, renderFunction?: i18next.Callback); + // (undocumented) + languageList(): string[]; + // (undocumented) + loadNamespace(name: string, i18nCallback: any): void; + // (undocumented) + registerNamespace(name: string): I18NNamespace; + translate(key: string | string[], options?: i18next.TranslationOptions): any; + translateKeys(line: string): string; + // (undocumented) + unregisterNamespace(name: string): void; + // (undocumented) + waitForAllRead(): Promise; +} + +// @public (undocumented) +class I18NNamespace { + constructor(name: string, readFinished: Promise); + // (undocumented) + name: string; + // (undocumented) + readFinished: Promise; +} + +// @public (undocumented) +interface I18NOptions { + // (undocumented) + urlTemplate?: string; +} + +// (No @packagedocumentation comment for this package) diff --git a/common/api/imodeljs-quantity.api.ts b/common/api/imodeljs-quantity.api.ts new file mode 100644 index 0000000..a86f5da --- /dev/null +++ b/common/api/imodeljs-quantity.api.ts @@ -0,0 +1,349 @@ +// @alpha +class BadUnit implements UnitProps { + // (undocumented) + isValid: boolean; + // (undocumented) + label: string; + // (undocumented) + name: string; + // (undocumented) + unitFamily: string; +} + +// @alpha (undocumented) +enum DecimalPrecision { + // (undocumented) + Eight = 8, + // (undocumented) + Eleven = 11, + // (undocumented) + Five = 5, + // (undocumented) + Four = 4, + // (undocumented) + Nine = 9, + // (undocumented) + One = 1, + // (undocumented) + Seven = 7, + // (undocumented) + Six = 6, + // (undocumented) + Ten = 10, + // (undocumented) + Three = 3, + // (undocumented) + Twelve = 12, + // (undocumented) + Two = 2, + // (undocumented) + Zero = 0 +} + +// @alpha +class Format implements FormatProps { + constructor(name: string); + // (undocumented) + protected _decimalSeparator: string; + // (undocumented) + protected _formatTraits: FormatTraits; + // (undocumented) + protected _includeZero: boolean; + // (undocumented) + protected _minWidth?: number; + // (undocumented) + protected _precision: number; + // (undocumented) + protected _roundFactor: number; + // (undocumented) + protected _scientificType?: ScientificType; + // (undocumented) + protected _showSignOption: ShowSignOption; + // (undocumented) + protected _spacer: string; + // (undocumented) + protected _stationOffsetSize?: number; + // (undocumented) + protected _stationSeparator: string; + // (undocumented) + protected _thousandSeparator: string; + // (undocumented) + protected _type: FormatType; + // (undocumented) + protected _units?: Array<[UnitProps, string | undefined]>; + // (undocumented) + protected _uomSeparator: string; + // (undocumented) + readonly decimalSeparator: string; + // (undocumented) + readonly formatTraits: FormatTraits; + static formatTraitsToArray(currentFormatTrait: FormatTraits): string[]; + static formatTypeToString(type: FormatType): string; + fromJson(unitsProvider: UnitsProvider, jsonObj: any): Promise; + hasFormatTraitSet(formatTrait: FormatTraits): boolean; + // (undocumented) + readonly hasUnits: boolean; + // (undocumented) + readonly includeZero: boolean | undefined; + // (undocumented) + readonly minWidth: number | undefined; + // (undocumented) + readonly name: string; + static parseDecimalPrecision(jsonObjPrecision: number): DecimalPrecision; + static parseFormatTrait(stringToCheck: string, currentFormatTrait: number): FormatTraits; + static parseFormatType(jsonObjType: string, formatName: string): FormatType; + static parseFractionalPrecision(jsonObjPrecision: number, formatName: string): FractionalPrecision; + static parsePrecision(precision: number, formatName: string, type: FormatType): DecimalPrecision | FractionalPrecision; + static parseScientificType(scientificType: string, formatName: string): ScientificType; + static parseShowSignOption(showSignOption: string, formatName: string): ShowSignOption; + // (undocumented) + readonly precision: DecimalPrecision | FractionalPrecision; + // (undocumented) + readonly roundFactor: number; + // (undocumented) + readonly scientificType: ScientificType | undefined; + // (undocumented) + static scientificTypeToString(scientificType: ScientificType): string; + // (undocumented) + readonly showSignOption: ShowSignOption; + static showSignOptionToString(showSign: ShowSignOption): string; + // (undocumented) + readonly spacer: string | undefined; + // (undocumented) + readonly stationOffsetSize: number | undefined; + // (undocumented) + readonly stationSeparator: string; + // (undocumented) + readonly thousandSeparator: string; + toJson: { + [value: string]: any; + } + // (undocumented) + readonly type: FormatType; + // (undocumented) + readonly units: Array<[UnitProps, string | undefined]> | undefined; + // (undocumented) + readonly uomSeparator: string; +} + +// @alpha +interface FormatProps { + // (undocumented) + readonly decimalSeparator: string; + // (undocumented) + readonly formatTraits: FormatTraits; + // (undocumented) + readonly includeZero?: boolean; + // (undocumented) + readonly minWidth: number | undefined; + // (undocumented) + readonly name: string; + // (undocumented) + readonly precision: DecimalPrecision | FractionalPrecision; + // (undocumented) + readonly roundFactor: number; + // (undocumented) + readonly scientificType?: ScientificType; + // (undocumented) + readonly showSignOption: ShowSignOption; + // (undocumented) + readonly spacer?: string; + // (undocumented) + readonly stationOffsetSize?: number; + // (undocumented) + readonly stationSeparator?: string; + // (undocumented) + readonly thousandSeparator: string; + // (undocumented) + readonly type: FormatType; + // (undocumented) + readonly units?: Array<[UnitProps, string | undefined]>; + // (undocumented) + readonly uomSeparator: string; +} + +// @alpha +class Formatter { + static formatQuantity(magnitude: number, spec: FormatterSpec): string; +} + +// @public +class FormatterSpec { + constructor(name: string, format: Format, conversions?: UnitConversionSpec[]); + static create(name: string, format: Format, unitsProvider: UnitsProvider, inputUnit?: UnitProps): Promise; + // (undocumented) + readonly format: Format; + // (undocumented) + readonly name: string; + readonly unitConversions: UnitConversionSpec[]; +} + +// @alpha (undocumented) +enum FormatTraits { + // (undocumented) + ApplyRounding = 16, + // (undocumented) + ExponentOnlyNegative = 512, + // (undocumented) + FractionDash = 32, + // (undocumented) + KeepDecimalPoint = 8, + // (undocumented) + KeepSingleZero = 2, + // (undocumented) + PrependUnitLabel = 128, + // (undocumented) + ShowUnitLabel = 64, + // (undocumented) + TrailZeroes = 1, + // (undocumented) + Use1000Separator = 256, + // (undocumented) + ZeroEmpty = 4 +} + +// @alpha (undocumented) +enum FormatType { + // (undocumented) + Decimal = 0, + // (undocumented) + Fractional = 1, + // (undocumented) + Scientific = 2, + // (undocumented) + Station = 3 +} + +// @alpha (undocumented) +enum FractionalPrecision { + // (undocumented) + Eight = 8, + // (undocumented) + Four = 4, + // (undocumented) + One = 1, + // (undocumented) + OneHundredTwentyEight = 128, + // (undocumented) + Sixteen = 16, + // (undocumented) + SixtyFour = 64, + // (undocumented) + ThirtyTwo = 32, + // (undocumented) + Two = 2, + // (undocumented) + TwoHundredFiftySix = 256 +} + +// @alpha +class Parser { + static parseIntoQuantity(inString: string, format: Format, unitsProvider: UnitsProvider): Promise; + // WARNING: The type "ParseToken" needs to be exported by the package (e.g. added to index.ts) + static parseQuantitySpecification(quantitySpecification: string, format: Format): ParseToken[]; +} + +// @alpha +class Quantity implements QuantityProps { + constructor(unit?: UnitProps, magnitude?: number); + // (undocumented) + protected _isValid: boolean; + // (undocumented) + protected _magnitude: number; + // (undocumented) + protected _unit: UnitProps; + convertTo(toUnit: UnitProps, conversion: UnitConversion): Quantity | undefined; + // (undocumented) + readonly isValid: boolean; + // (undocumented) + readonly magnitude: number; + // (undocumented) + readonly unit: UnitProps; +} + +// @alpha +class QuantityError extends BentleyError { + constructor(errorNumber: number, message?: string); + // (undocumented) + readonly errorNumber: number; +} + +// @alpha +interface QuantityProps { + // (undocumented) + readonly isValid: boolean; + // (undocumented) + readonly magnitude: number; + // (undocumented) + readonly unit: UnitProps; +} + +// @alpha +enum QuantityStatus { + // (undocumented) + InvalidCompositeFormat = 35041, + // (undocumented) + InvalidJson = 35040, + // (undocumented) + QUANTITY_ERROR_BASE = 35039, + // (undocumented) + Success = 0 +} + +// @alpha (undocumented) +enum ScientificType { + // (undocumented) + Normalized = 0, + // (undocumented) + ZeroNormalized = 1 +} + +// @alpha (undocumented) +enum ShowSignOption { + // (undocumented) + NegativeParentheses = 3, + // (undocumented) + NoSign = 0, + // (undocumented) + OnlyNegative = 1, + // (undocumented) + SignAlways = 2 +} + +// @alpha +interface UnitConversion { + // (undocumented) + factor: number; + // (undocumented) + offset: number; +} + +// @public +interface UnitConversionSpec { + // (undocumented) + conversion: UnitConversion; + // (undocumented) + label: string; + // (undocumented) + name: string; +} + +// @alpha +interface UnitProps { + readonly isValid: boolean; + readonly label: string; + readonly name: string; + readonly unitFamily: string; +} + +// @alpha +interface UnitsProvider { + // (undocumented) + findUnit(unitLabel: string, unitFamily?: string): Promise; + // (undocumented) + findUnitByName(unitName: string): Promise; + // (undocumented) + getConversion(fromUnit: UnitProps, toUnit: UnitProps): Promise; +} + +// (No @packagedocumentation comment for this package) diff --git a/common/api/presentation-backend.api.ts b/common/api/presentation-backend.api.ts new file mode 100644 index 0000000..1de1891 --- /dev/null +++ b/common/api/presentation-backend.api.ts @@ -0,0 +1,13 @@ +// @public +class Presentation { + // WARNING: The type "PresentationManager" needs to be exported by the package (e.g. added to index.ts) + static getManager(clientId?: string): PresentationManager; + // WARNING: The type "Props" needs to be exported by the package (e.g. added to index.ts) + static initialize(props?: Props): void; + // WARNING: The type "Props" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + static readonly initProps: Props | undefined; + static terminate(): void; +} + +// (No @packagedocumentation comment for this package) diff --git a/common/api/presentation-common.api.ts b/common/api/presentation-common.api.ts new file mode 100644 index 0000000..a2799ad --- /dev/null +++ b/common/api/presentation-common.api.ts @@ -0,0 +1,1355 @@ +// @public +interface AllInstanceNodesSpecification extends ChildNodeSpecificationBase, DefaultGroupingPropertiesContainer { + specType: RuleSpecificationTypes.AllInstanceNodes; + supportedSchemas?: SchemasSpecification; +} + +// @public +interface AllRelatedInstanceNodesSpecification extends ChildNodeSpecificationBase, DefaultGroupingPropertiesContainer { + requiredDirection?: RelationshipDirection; + skipRelatedLevel?: number; + specType: RuleSpecificationTypes.AllRelatedInstanceNodes; + supportedSchemas?: SchemasSpecification; +} + +// @public +interface ArrayTypeDescription extends BaseTypeDescription { + // (undocumented) + memberType: TypeDescription; + // (undocumented) + valueFormat: PropertyValueFormat.Array; +} + +// @public +interface BaseFieldJSON { + // (undocumented) + category: CategoryDescription; + // (undocumented) + editor?: EditorDescription; + // (undocumented) + isReadonly: boolean; + // (undocumented) + label: string; + // (undocumented) + name: string; + // (undocumented) + priority: number; + // (undocumented) + type: TypeDescription; +} + +// @public +interface BaseNodeKey { + pathFromRoot: string[]; + // (undocumented) + type: string; +} + +// @public +interface CalculatedPropertiesSpecification { + label: string; + priority?: number; + value: string; +} + +// @public +interface CategoryDescription { + description: string; + expand: boolean; + label: string; + name: string; + priority: number; +} + +// @public +interface CheckBoxRule extends RuleBase, ConditionContainer { + condition?: string; + defaultValue?: boolean; + isEnabled?: string | boolean; + propertyName?: string; + ruleType: RuleTypes.CheckBox; + useInversedPropertyValue?: boolean; +} + +// @public +interface ChildNodeRule extends NavigationRuleBase, ConditionContainer { + condition?: string; + ruleType: RuleTypes.ChildNodes; +} + +// @public +interface ClassGroup extends GroupingSpecificationBase { + baseClass?: SingleSchemaClassSpecification; + createGroupForSingleItem?: boolean; + specType: GroupingSpecificationTypes.Class; +} + +// @public +interface ClassInfo { + // (undocumented) + id: ClassId; + // (undocumented) + label: string; + // (undocumented) + name: string; +} + +// @public +interface ClassInfoJSON { + // (undocumented) + id: string; + // (undocumented) + label: string; + // (undocumented) + name: string; +} + +// @public +class Content { + contentSet: Array>; + descriptor: Readonly; + static fromJSON(json: ContentJSON | string | undefined): Content | undefined; + static reviver(key: string, value: any): any; +} + +// @public +enum ContentFlags { + DistinctValues = 16, + KeysOnly = 1, + MergeResults = 8, + NoFields = 32, + ShowImages = 2, + ShowLabels = 4 +} + +// @public +interface ContentInstancesOfSpecificClassesSpecification extends ContentSpecificationBase { + arePolymorphic?: boolean; + classes: MultiSchemaClassesSpecification | MultiSchemaClassesSpecification[]; + instanceFilter?: string; + specType: RuleSpecificationTypes.ContentInstancesOfSpecificClasses; +} + +// @public +interface ContentJSON { + // (undocumented) + contentSet: ItemJSON[]; + // (undocumented) + descriptor: DescriptorJSON; +} + +// @public +interface ContentModifier extends RuleBase { + calculatedProperties?: CalculatedPropertiesSpecification[]; + class?: SingleSchemaClassSpecification; + propertiesDisplay?: PropertiesDisplaySpecification[]; + propertyEditors?: PropertyEditorsSpecification[]; + relatedProperties?: RelatedPropertiesSpecification[]; + ruleType: RuleTypes.ContentModifier; +} + +// @public +interface ContentRelatedInstancesSpecification extends ContentSpecificationBase { + instanceFilter?: string; + isRecursive?: boolean; + relatedClasses?: MultiSchemaClassesSpecification | MultiSchemaClassesSpecification[]; + relationships?: MultiSchemaClassesSpecification | MultiSchemaClassesSpecification[]; + requiredDirection?: RelationshipDirection; + skipRelatedLevel?: number; + specType: RuleSpecificationTypes.ContentRelatedInstances; +} + +// @public +interface ContentRequestOptions extends RequestOptions { +} + +// @public +interface ContentRule extends RuleBase, ConditionContainer { + condition?: string; + ruleType: RuleTypes.Content; + specifications: ContentSpecification[]; +} + +// @public +interface CustomNodeSpecification extends ChildNodeSpecificationBase { + description?: string; + imageId?: string; + label: string; + specType: RuleSpecificationTypes.CustomNode; + type: string; +} + +// @public +interface CustomQueryInstanceNodesSpecification extends ChildNodeSpecificationBase, DefaultGroupingPropertiesContainer { + queries?: QuerySpecification[]; + specType: RuleSpecificationTypes.CustomQueryInstanceNodes; +} + +// WARNING: UNDEFINED has incomplete type information +// WARNING: GRID has incomplete type information +// WARNING: PROPERTY_PANE has incomplete type information +// WARNING: LIST has incomplete type information +// WARNING: VIEWPORT has incomplete type information +// @public +class DefaultContentDisplayTypes { +} + +// @public +class Descriptor { + connectionId: string; + contentFlags: number; + contentOptions: any; + // WARNING: The type "DescriptorOverrides" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + createDescriptorOverrides(): DescriptorOverrides; + // (undocumented) + createStrippedDescriptor(): Descriptor; + displayType: string; + fields: Field[]; + filterExpression?: string; + static fromJSON(json: DescriptorJSON | string | undefined): Descriptor | undefined; + getFieldByName(name: string, recurse?: boolean): Field | undefined; + inputKeysHash: string; + // (undocumented) + rebuildParentship(): void; + // (undocumented) + resetParentship(): void; + static reviver(key: string, value: any): any; + selectClasses: SelectClassInfo[]; + selectionInfo?: SelectionInfo; + sortDirection?: SortDirection; + sortingField?: Field; +} + +// @public +interface DescriptorJSON { + // (undocumented) + connectionId: string; + // (undocumented) + contentFlags: number; + // (undocumented) + contentOptions: any; + // (undocumented) + displayType: string; + // (undocumented) + fields: FieldJSON[]; + // (undocumented) + filterExpression?: string; + // (undocumented) + inputKeysHash: string; + // (undocumented) + selectClasses: SelectClassInfoJSON[]; + // (undocumented) + selectionInfo?: SelectionInfo; + // (undocumented) + sortDirection?: SortDirection; + // (undocumented) + sortingFieldName?: string; +} + +// @public (undocumented) +export function displayValueFromJSON(json: DisplayValueJSON): DisplayValue; + +// @public (undocumented) +interface DisplayValuesArray extends Array { +} + +// @public (undocumented) +export function displayValuesArrayFromJSON(json: DisplayValuesArrayJSON): DisplayValuesArray; + +// @public (undocumented) +interface DisplayValuesArrayJSON extends Array { +} + +// @public (undocumented) +interface DisplayValuesMap extends ValuesDictionary { +} + +// @public (undocumented) +export function displayValuesMapFromJSON(json: DisplayValuesMapJSON): DisplayValuesMap; + +// @public (undocumented) +interface DisplayValuesMapJSON extends ValuesDictionary { +} + +// @public +interface ECClassGroupingNodeKey extends GroupingNodeKey { + className: string; + // (undocumented) + type: StandardNodeTypes.ECClassGroupingNode; +} + +// @public +interface ECInstanceNodeKey extends BaseNodeKey { + instanceKey: InstanceKey; + // (undocumented) + type: StandardNodeTypes.ECInstanceNode; +} + +// @public +interface ECInstanceNodeKeyJSON extends BaseNodeKey { + // (undocumented) + instanceKey: InstanceKeyJSON; + // (undocumented) + type: StandardNodeTypes.ECInstanceNode; +} + +// @public +interface ECPropertyGroupingNodeKey extends GroupingNodeKey { + className: string; + groupingValue: any; + propertyName: string; + // (undocumented) + type: StandardNodeTypes.ECPropertyGroupingNode; +} + +// @public +interface ECPropertyValueQuerySpecification extends QuerySpecificationBase { + parentPropertyName: string; + specType: QuerySpecificationTypes.ECPropertyValue; +} + +// @public +interface EditorDescription { + name: string; + params: any; +} + +// @public +interface EnumerationChoice { + // (undocumented) + label: string; + // (undocumented) + value: string | number; +} + +// @public +interface EnumerationInfo { + // (undocumented) + choices: EnumerationChoice[]; + // (undocumented) + isStrict: boolean; +} + +// @public +class Field { + constructor(category: CategoryDescription, name: string, label: string, type: TypeDescription, isReadonly: boolean, priority: number, editor?: EditorDescription); + category: Readonly; + editor?: Readonly; + static fromJSON(json: FieldJSON | string | undefined): Field | undefined; + isNestedContentField(): this is NestedContentField; + isPropertiesField(): this is PropertiesField; + isReadonly: boolean; + label: string; + name: string; + // (undocumented) + readonly parent: Readonly | undefined; + priority: number; + // (undocumented) + rebuildParentship(parentField?: NestedContentField): void; + // (undocumented) + resetParentship(): void; + static reviver(key: string, value: any): any; + type: Readonly; +} + +// @public +interface GroupingRule extends RuleBase, ConditionContainer { + class: SingleSchemaClassSpecification; + condition?: string; + groups: GroupingSpecification[]; + ruleType: RuleTypes.Grouping; +} + +// @public +enum GroupingSpecificationTypes { + // (undocumented) + Class = "Class", + // (undocumented) + Property = "Property", + // (undocumented) + SameLabelInstance = "SameLabelInstance" +} + +// @public +interface HierarchyRequestOptions extends RequestOptions { +} + +// @public +interface IClientStateHolder { + // (undocumented) + key: string; + // (undocumented) + onStateChanged: BeEvent<() => void>; + // (undocumented) + state: TState | undefined; +} + +// @public +interface ImageIdOverride extends RuleBase, ConditionContainer { + condition?: string; + imageIdExpression: string; + ruleType: RuleTypes.ImageIdOverride; +} + +// @public +interface InstanceKey { + // (undocumented) + className: string; + // (undocumented) + id: InstanceId; +} + +// @public +interface InstanceKeyJSON { + // (undocumented) + className: string; + // (undocumented) + id: string; +} + +// @public +interface InstanceLabelOverride extends RuleBase { + class: SingleSchemaClassSpecification; + propertyNames: string[]; + ruleType: RuleTypes.InstanceLabelOverride; +} + +// @public +interface InstanceNodesOfSpecificClassesSpecification extends ChildNodeSpecificationBase, DefaultGroupingPropertiesContainer { + arePolymorphic?: boolean; + classes: MultiSchemaClassesSpecification | MultiSchemaClassesSpecification[]; + instanceFilter?: string; + specType: RuleSpecificationTypes.InstanceNodesOfSpecificClasses; +} + +// @public (undocumented) +export function isArray(v: Value | ValueJSON | DisplayValue | DisplayValueJSON): v is ValuesArray | ValuesArrayJSON | DisplayValuesArray | DisplayValuesArrayJSON; + +// @public (undocumented) +export function isMap(v: Value | ValueJSON | DisplayValue | DisplayValueJSON): v is ValuesMap | ValuesMapJSON | DisplayValuesMap | DisplayValuesMapJSON; + +// @public (undocumented) +export function isNestedContentValue(v: Value | ValueJSON): v is NestedContentValue[] | NestedContentValueJSON[]; + +// @public (undocumented) +export function isPrimitive(v: Value | DisplayValue): v is string | number | boolean | undefined; + +// @public +class Item { + constructor(primaryKeys: ec.InstanceKey[], label: string, imageId: string, classInfo: ec.ClassInfo | undefined, values: ValuesDictionary, displayValues: ValuesDictionary, mergedFieldNames: string[]); + classInfo?: Readonly; + displayValues: Readonly>; + static fromJSON(json: ItemJSON | string | undefined): Item | undefined; + imageId: string; + isFieldMerged(fieldName: string): boolean; + label: string; + mergedFieldNames: string[]; + primaryKeys: Array>; + static reviver(key: string, value: any): any; + values: Readonly>; +} + +// @public +interface ItemJSON { + // (undocumented) + classInfo?: ec.ClassInfoJSON; + // (undocumented) + displayValues: ValuesDictionary; + // (undocumented) + imageId: string; + // (undocumented) + label: string; + // (undocumented) + mergedFieldNames: string[]; + // (undocumented) + primaryKeys: ec.InstanceKeyJSON[]; + // (undocumented) + values: ValuesDictionary; +} + +// @public +class KeySet { + constructor(source?: Keys); + // WARNING: The type "Key" needs to be exported by the package (e.g. added to index.ts) + add(value: Keys | Key): KeySet; + clear(): KeySet; + // WARNING: The type "Key" needs to be exported by the package (e.g. added to index.ts) + delete(value: Keys | Key): KeySet; + readonly guid: GuidString; + // WARNING: The type "Key" needs to be exported by the package (e.g. added to index.ts) + has(value: Key): boolean; + hasAll(keys: Keys): boolean; + hasAny(keys: Keys): boolean; + readonly instanceKeys: Map>; + readonly instanceKeysCount: number; + readonly isEmpty: boolean; + readonly nodeKeys: Set; + readonly nodeKeysCount: number; + readonly size: number; + // WARNING: The type "KeySetJSON" needs to be exported by the package (e.g. added to index.ts) + toJSON(): KeySetJSON; +} + +// @public +interface KindOfQuantityInfo { + // (undocumented) + currentFormatId: string; + // (undocumented) + label: string; + // (undocumented) + name: string; + // (undocumented) + persistenceUnit: string; +} + +// @public +interface LabelGroupingNodeKey extends GroupingNodeKey { + label: string; + // (undocumented) + type: StandardNodeTypes.DisplayLabelGroupingNode; +} + +// @public +interface LabelOverride extends RuleBase, ConditionContainer { + condition?: string; + description?: string; + label?: string; + ruleType: RuleTypes.LabelOverride; +} + +// @public +enum LoggingNamespaces { + // (undocumented) + ECObjects = "ECObjects", + // (undocumented) + ECObjects_ECExpressions = "ECObjects.ECExpressions", + // (undocumented) + ECObjects_ECExpressions_Evaluate = "ECObjects.ECExpressions.Evaluate", + // (undocumented) + ECObjects_ECExpressions_Parse = "ECObjects.ECExpressions.Parse", + // (undocumented) + ECPresentation = "ECPresentation", + // (undocumented) + ECPresentation_Connections = "ECPresentation.Connections", + // (undocumented) + ECPresentation_RulesEngine = "ECPresentation.RulesEngine", + // (undocumented) + ECPresentation_RulesEngine_Content = "ECPresentation.RulesEngine.Content", + // (undocumented) + ECPresentation_RulesEngine_Localization = "ECPresentation.RulesEngine.Localization", + // (undocumented) + ECPresentation_RulesEngine_Navigation = "ECPresentation.RulesEngine.Navigation", + // (undocumented) + ECPresentation_RulesEngine_Navigation_Cache = "ECPresentation.RulesEngine.Navigation.Cache", + // (undocumented) + ECPresentation_RulesEngine_RulesetVariables = "ECPresentation.RulesEngine.RulesetVariables", + // (undocumented) + ECPresentation_RulesEngine_Threads = "ECPresentation.RulesEngine.Threads", + // (undocumented) + ECPresentation_RulesEngine_Update = "ECPresentation.RulesEngine.Update" +} + +// @public +interface MultiSchemaClassesSpecification { + classNames: string[]; + schemaName: string; +} + +// @public +class NestedContentField extends Field { + constructor(category: CategoryDescription, name: string, label: string, description: TypeDescription, isReadonly: boolean, priority: number, contentClassInfo: ec.ClassInfo, pathToPrimaryClass: ec.RelationshipPathInfo, nestedFields: Field[], editor?: EditorDescription); + contentClassInfo: ec.ClassInfo; + static fromJSON(json: NestedContentFieldJSON | string | undefined): NestedContentField | undefined; + nestedFields: Array>; + pathToPrimaryClass: ec.RelationshipPathInfo; + // (undocumented) + rebuildParentship(parentField?: NestedContentField): void; + // (undocumented) + resetParentship(): void; +} + +// @public +interface NestedContentFieldJSON extends BaseFieldJSON { + // (undocumented) + contentClassInfo: ec.ClassInfoJSON; + // (undocumented) + nestedFields: FieldJSON[]; + // (undocumented) + pathToPrimaryClass: ec.RelationshipPathInfoJSON; +} + +// @public +interface NestedContentValue { + // (undocumented) + displayValues: ValuesDictionary; + // (undocumented) + mergedFieldNames: string[]; + // (undocumented) + primaryKeys: InstanceKey[]; + // (undocumented) + values: ValuesDictionary; +} + +// @public (undocumented) +export function nestedContentValueFromJSON(json: NestedContentValueJSON): NestedContentValue; + +// @public +interface NestedContentValueJSON { + // (undocumented) + displayValues: ValuesDictionary; + // (undocumented) + mergedFieldNames: string[]; + // (undocumented) + primaryKeys: InstanceKeyJSON[]; + // (undocumented) + values: ValuesDictionary; +} + +// @public +interface Node { + backColor?: string; + description?: string; + // (undocumented) + fontStyle?: string; + foreColor?: string; + hasChildren?: boolean; + // (undocumented) + imageId?: string; + isCheckboxEnabled?: boolean; + isCheckboxVisible?: boolean; + isChecked?: boolean; + isEditable?: boolean; + isExpanded?: boolean; + isSelectionDisabled?: boolean; + key: NodeKey; + label: string; +} + +// @public +interface NodeJSON { + // (undocumented) + backColor?: string; + // (undocumented) + description?: string; + // (undocumented) + fontStyle?: string; + // (undocumented) + foreColor?: string; + // (undocumented) + hasChildren?: boolean; + // (undocumented) + imageId?: string; + // (undocumented) + isCheckboxEnabled?: boolean; + // (undocumented) + isCheckboxVisible?: boolean; + // (undocumented) + isChecked?: boolean; + // (undocumented) + isEditable?: boolean; + // (undocumented) + isExpanded?: boolean; + // (undocumented) + isSelectionDisabled?: boolean; + // (undocumented) + key: NodeKeyJSON; + // (undocumented) + label: string; +} + +// @public +interface NodePathElement { + // (undocumented) + children: NodePathElement[]; + // (undocumented) + filteringData?: NodePathFilteringData; + // (undocumented) + index: number; + // (undocumented) + isMarked: boolean; + // (undocumented) + node: Node; +} + +// @public +interface PageOptions { + size?: number; + start?: number; +} + +// @public +interface PersistentKeysContainer { + // (undocumented) + elements: Id64String[]; + // (undocumented) + models: Id64String[]; + // (undocumented) + nodes: NodeKey[]; +} + +// @public +class PresentationError extends BentleyError { + constructor(errorNumber: PresentationStatus, message?: string, log?: LogFunction, getMetaData?: GetMetaDataFunction); + protected _initName(): string; +} + +// @public +class PresentationRpcInterface extends RpcInterface { + // WARNING: The type "SelectionScopeRpcRequestOptions" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + computeSelection(_token: IModelToken, _options: SelectionScopeRpcRequestOptions, _keys: Readonly, _scopeId: string): Promise; + getChildren(_token: IModelToken, _options: Paged, _parentKey: Readonly): Promise; + getChildrenCount(_token: IModelToken, _options: HierarchyRpcRequestOptions, _parentKey: Readonly): Promise; + // WARNING: The type "ContentRpcRequestOptions" needs to be exported by the package (e.g. added to index.ts) + getContent(_token: IModelToken, _options: ContentRpcRequestOptions, _descriptor: Readonly, _keys: Readonly): Promise; + // WARNING: The type "ContentRpcRequestOptions" needs to be exported by the package (e.g. added to index.ts) + getContentDescriptor(_token: IModelToken, _options: ContentRpcRequestOptions, _displayType: string, _keys: Readonly, _selection: Readonly | undefined): Promise; + // WARNING: The type "ContentRpcRequestOptions" needs to be exported by the package (e.g. added to index.ts) + getContentSetSize(_token: IModelToken, _options: ContentRpcRequestOptions, _descriptor: Readonly, _keys: Readonly): Promise; + // WARNING: The type "ContentRpcRequestOptions" needs to be exported by the package (e.g. added to index.ts) + getDistinctValues(_token: IModelToken, _options: ContentRpcRequestOptions, _descriptor: Readonly, _keys: Readonly, _fieldName: string, _maximumValueCount: number): Promise; + getFilteredNodePaths(_token: IModelToken, _options: HierarchyRpcRequestOptions, _filterText: string): Promise; + getNodePaths(_token: IModelToken, _options: HierarchyRpcRequestOptions, _paths: InstanceKey[][], _markedIndex: number): Promise; + getRootNodes(_token: IModelToken, _options: Paged): Promise; + getRootNodesCount(_token: IModelToken, _options: HierarchyRpcRequestOptions): Promise; + // WARNING: The type "SelectionScopeRpcRequestOptions" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + getSelectionScopes(_token: IModelToken, _options: SelectionScopeRpcRequestOptions): Promise; + // (undocumented) + syncClientState(_token: IModelToken, _options: ClientStateSyncRequestOptions): Promise; + static types: () => (typeof Field | typeof PropertiesField | typeof NestedContentField | typeof Descriptor | typeof Item | typeof Content)[]; + static version: string; +} + +// @public +enum PresentationStatus { + // (undocumented) + BackendOutOfSync = 65542, + // (undocumented) + Error = 65536, + // (undocumented) + InvalidArgument = 65539, + // (undocumented) + InvalidResponse = 65540, + // (undocumented) + NoContent = 65541, + // (undocumented) + NotInitialized = 65537, + // (undocumented) + Success = 0, + // (undocumented) + UseAfterDisposal = 65538 +} + +// @public +interface PrimitiveTypeDescription extends BaseTypeDescription { + // (undocumented) + valueFormat: PropertyValueFormat.Primitive; +} + +// @public +interface PropertiesDisplaySpecification { + isDisplayed?: boolean; + priority?: number; + propertyNames: string[]; +} + +// @public +class PropertiesField extends Field { + constructor(category: CategoryDescription, name: string, label: string, description: TypeDescription, isReadonly: boolean, priority: number, properties: Property[], editor?: EditorDescription); + static fromJSON(json: PropertiesFieldJSON | string | undefined): PropertiesField | undefined; + properties: Array>; +} + +// @public +interface PropertiesFieldJSON extends BaseFieldJSON { + // (undocumented) + properties: PropertyJSON[]; +} + +// @public +interface Property { + property: Readonly; + relatedClassPath: Readonly; +} + +// @public +interface PropertyEditorJsonParameters extends PropertyEditorParametersBase { + json: any; + paramsType: PropertyEditorParameterTypes.Json; +} + +// @public +interface PropertyEditorMultilineParameters extends PropertyEditorParametersBase { + height?: number; + paramsType: PropertyEditorParameterTypes.Multiline; +} + +// @public +enum PropertyEditorParameterTypes { + // (undocumented) + Json = "Json", + // (undocumented) + Multiline = "Multiline", + // (undocumented) + Range = "Range", + // (undocumented) + Slider = "Slider" +} + +// @public +interface PropertyEditorRangeParameters extends PropertyEditorParametersBase { + max?: number; + min?: number; + paramsType: PropertyEditorParameterTypes.Range; +} + +// @public +interface PropertyEditorSliderParameters extends PropertyEditorParametersBase { + intervalsCount?: number; + isVertical?: boolean; + max: number; + min: number; + paramsType: PropertyEditorParameterTypes.Slider; +} + +// @public +interface PropertyEditorsSpecification { + editorName: string; + parameters?: PropertyEditorParameters[]; + propertyName: string; +} + +// @public +interface PropertyGroup extends GroupingSpecificationBase { + createGroupForSingleItem?: boolean; + createGroupForUnspecifiedValues?: boolean; + groupingValue?: PropertyGroupingValue; + imageId?: string; + propertyName: string; + ranges?: PropertyRangeGroupSpecification[]; + sortingValue?: PropertyGroupingValue; + specType: GroupingSpecificationTypes.Property; +} + +// @public +enum PropertyGroupingValue { + DisplayLabel = "DisplayLabel", + PropertyValue = "PropertyValue" +} + +// @public +interface PropertyInfo { + // (undocumented) + classInfo: ClassInfo; + // (undocumented) + enumerationInfo?: EnumerationInfo; + // (undocumented) + kindOfQuantity?: KindOfQuantityInfo; + // (undocumented) + name: string; + // (undocumented) + type: string; +} + +// @public +interface PropertyInfoJSON { + // (undocumented) + classInfo: ClassInfoJSON; + // (undocumented) + enumerationInfo?: EnumerationInfo; + // (undocumented) + kindOfQuantity?: KindOfQuantityInfo; + // (undocumented) + name: string; + // (undocumented) + type: string; +} + +// @public +interface PropertyJSON { + // (undocumented) + property: ec.PropertyInfoJSON; + // (undocumented) + relatedClassPath: ec.RelationshipPathInfoJSON; +} + +// @public +interface PropertyRangeGroupSpecification { + fromValue: string; + imageId?: string; + label?: string; + toValue: string; +} + +// @public +enum PropertyValueFormat { + // (undocumented) + Array = "Array", + // (undocumented) + Primitive = "Primitive", + // (undocumented) + Struct = "Struct" +} + +// @public +enum QuerySpecificationTypes { + // (undocumented) + ECPropertyValue = "ECPropertyValue", + // (undocumented) + String = "String" +} + +// @public +class RegisteredRuleset implements IDisposable, Ruleset { + constructor(ruleset: Ruleset, uniqueIdentifier: string, disposeFunc: (ruleset: RegisteredRuleset) => void); + // (undocumented) + dispose(): void; + // (undocumented) + readonly id: string; + // (undocumented) + readonly rules: Rule[]; + // (undocumented) + readonly supplementationInfo: SupplementationInfo | undefined; + // (undocumented) + readonly supportedSchemas: SchemasSpecification | undefined; + // (undocumented) + toJSON(): Ruleset; + // (undocumented) + readonly uniqueIdentifier: string; + // (undocumented) + readonly vars: VariablesGroup[] | undefined; +} + +// @public +interface RelatedClassInfo { + isForwardRelationship: boolean; + isPolymorphicRelationship: boolean; + relationshipInfo: ClassInfo; + sourceClassInfo: ClassInfo; + targetClassInfo: ClassInfo; +} + +// @public +interface RelatedClassInfoJSON { + // (undocumented) + isForwardRelationship: boolean; + // (undocumented) + isPolymorphicRelationship: boolean; + // (undocumented) + relationshipInfo: ClassInfoJSON; + // (undocumented) + sourceClassInfo: ClassInfoJSON; + // (undocumented) + targetClassInfo: ClassInfoJSON; +} + +// @public +interface RelatedInstanceNodesSpecification extends ChildNodeSpecificationBase, DefaultGroupingPropertiesContainer { + instanceFilter?: string; + relatedClasses?: MultiSchemaClassesSpecification | MultiSchemaClassesSpecification[]; + relationships?: MultiSchemaClassesSpecification | MultiSchemaClassesSpecification[]; + requiredDirection?: RelationshipDirection; + skipRelatedLevel?: number; + specType: RuleSpecificationTypes.RelatedInstanceNodes; + supportedSchemas?: string[]; +} + +// @public +interface RelatedInstanceSpecification { + alias: string; + class: SingleSchemaClassSpecification; + isRequired?: boolean; + relationship: SingleSchemaClassSpecification; + requiredDirection: RelationshipDirection.Forward | RelationshipDirection.Backward; +} + +// @public +interface RelatedPropertiesSpecification { + isPolymorphic?: boolean; + nestedRelatedProperties?: RelatedPropertiesSpecification[]; + propertyNames?: string[] | RelatedPropertiesSpecialValues; + relatedClasses?: MultiSchemaClassesSpecification | MultiSchemaClassesSpecification[]; + relationshipMeaning?: RelationshipMeaning; + relationships?: MultiSchemaClassesSpecification | MultiSchemaClassesSpecification[]; + requiredDirection?: RelationshipDirection; +} + +// @public +enum RelationshipDirection { + Backward = "Backward", + Both = "Both", + Forward = "Forward" +} + +// @public +enum RelationshipMeaning { + RelatedInstance = "RelatedInstance", + SameInstance = "SameInstance" +} + +// @public +interface RequestOptions { + imodel: TIModel; + locale?: string; + rulesetId: string; +} + +// @public +interface RootNodeRule extends NavigationRuleBase { + autoExpand?: boolean; + ruleType: RuleTypes.RootNodes; +} + +// @public (undocumented) +interface RpcRequestOptions { + // (undocumented) + clientId?: string; + // (undocumented) + clientStateId?: string; +} + +// @public +class RpcRequestsHandler implements IDisposable { + // WARNING: The type "Props" needs to be exported by the package (e.g. added to index.ts) + constructor(props?: Props); + readonly clientId: string; + readonly clientStateId: string | undefined; + // (undocumented) + computeSelection(options: SelectionScopeRequestOptions, keys: EntityProps[], scopeId: string): Promise; + // (undocumented) + dispose(): void; + // (undocumented) + getChildren(options: Paged>, parentKey: Readonly): Promise; + // (undocumented) + getChildrenCount(options: HierarchyRequestOptions, parentKey: Readonly): Promise; + // (undocumented) + getContent(options: ContentRequestOptions, descriptor: Readonly, keys: Readonly): Promise; + // (undocumented) + getContentDescriptor(options: ContentRequestOptions, displayType: string, keys: Readonly, selection: Readonly | undefined): Promise; + // (undocumented) + getContentSetSize(options: ContentRequestOptions, descriptor: Readonly, keys: Readonly): Promise; + // (undocumented) + getDistinctValues(options: ContentRequestOptions, descriptor: Readonly, keys: Readonly, fieldName: string, maximumValueCount: number): Promise; + // (undocumented) + getFilteredNodePaths(options: HierarchyRequestOptions, filterText: string): Promise; + // (undocumented) + getNodePaths(options: HierarchyRequestOptions, paths: InstanceKey[][], markedIndex: number): Promise; + // (undocumented) + getRootNodes(options: Paged>): Promise; + // (undocumented) + getRootNodesCount(options: HierarchyRequestOptions): Promise; + // (undocumented) + getSelectionScopes(options: SelectionScopeRequestOptions): Promise; + // (undocumented) + registerClientStateHolder(holder: IClientStateHolder): void; + request(context: any, func: (token: IModelToken, options: Omit, ...args: TArg[]) => Promise, options: TOptions, ...args: TArg[]): Promise; + sync(token: IModelToken): Promise; + // (undocumented) + unregisterClientStateHolder(holder: IClientStateHolder): void; +} + +// @public +interface Ruleset { + id: string; + rules: Rule[]; + supplementationInfo?: SupplementationInfo; + supportedSchemas?: SchemasSpecification; + vars?: VariablesGroup[]; +} + +// WARNING: Unsupported export: STATE_ID +// @public (undocumented) +module RulesetManagerState { +} + +// @public +class RulesetsFactory { + createSimilarInstancesRuleset: { + description: string; + ruleset: Ruleset; + } +} + +// @public (undocumented) +interface RulesetVariablesState { +} + +// @public +enum RuleSpecificationTypes { + // (undocumented) + AllInstanceNodes = "AllInstanceNodes", + // (undocumented) + AllRelatedInstanceNodes = "AllRelatedInstanceNodes", + // (undocumented) + ContentInstancesOfSpecificClasses = "ContentInstancesOfSpecificClasses", + // (undocumented) + ContentRelatedInstances = "ContentRelatedInstances", + // (undocumented) + CustomNode = "CustomNode", + // (undocumented) + CustomQueryInstanceNodes = "CustomQueryInstanceNodes", + // (undocumented) + InstanceNodesOfSpecificClasses = "InstanceNodesOfSpecificClasses", + // (undocumented) + RelatedInstanceNodes = "RelatedInstanceNodes", + // (undocumented) + SelectedNodeInstances = "SelectedNodeInstances" +} + +// @public +enum RuleTypes { + // (undocumented) + CheckBox = "CheckBox", + // (undocumented) + ChildNodes = "ChildNodes", + // (undocumented) + Content = "Content", + // (undocumented) + ContentModifier = "ContentModifier", + // (undocumented) + DisabledSorting = "DisabledSorting", + // (undocumented) + Grouping = "Grouping", + // (undocumented) + ImageIdOverride = "ImageIdOverride", + // (undocumented) + InstanceLabelOverride = "InstanceLabelOverride", + // (undocumented) + LabelOverride = "LabelOverride", + // (undocumented) + PropertySorting = "PropertySorting", + // (undocumented) + RootNodes = "RootNodes", + // (undocumented) + StyleOverride = "StyleOverride" +} + +// @public +interface SameLabelInstanceGroup extends GroupingSpecificationBase { + specType: GroupingSpecificationTypes.SameLabelInstance; +} + +// @public +interface SchemasSpecification { + isExclude?: boolean; + schemaNames: string[]; +} + +// @public +interface SelectClassInfo { + isSelectPolymorphic: boolean; + pathToPrimaryClass: ec.RelationshipPathInfo; + relatedPropertyPaths: ec.RelationshipPathInfo[]; + selectClassInfo: ec.ClassInfo; +} + +// @public +interface SelectClassInfoJSON { + // (undocumented) + isSelectPolymorphic: boolean; + // (undocumented) + pathToPrimaryClass: ec.RelationshipPathInfoJSON; + // (undocumented) + relatedPropertyPaths: ec.RelationshipPathInfoJSON[]; + // (undocumented) + selectClassInfo: ec.ClassInfoJSON; +} + +// @public +interface SelectedNodeInstancesSpecification extends ContentSpecificationBase { + acceptableClassNames?: string; + acceptablePolymorphically?: boolean; + acceptableSchemaName?: string; + onlyIfNotHandled?: boolean; + specType: RuleSpecificationTypes.SelectedNodeInstances; +} + +// @public +interface SelectionInfo { + // (undocumented) + level?: number; + // (undocumented) + providerName: string; +} + +// @public +interface SelectionScope { + // (undocumented) + description?: string; + // (undocumented) + id: string; + // (undocumented) + label: string; +} + +// @public +interface SelectionScopeRequestOptions { + imodel: TIModel; + locale?: string; +} + +// @public +interface SingleSchemaClassSpecification { + className: string; + schemaName: string; +} + +// @public +enum SortDirection { + // (undocumented) + Ascending = 0, + // (undocumented) + Descending = 1 +} + +// @public +enum StandardNodeTypes { + // (undocumented) + DisplayLabelGroupingNode = "DisplayLabelGroupingNode", + // (undocumented) + ECClassGroupingNode = "ECClassGroupingNode", + // (undocumented) + ECInstanceNode = "ECInstanceNode", + // (undocumented) + ECPropertyGroupingNode = "ECPropertyGroupingNode" +} + +// @public +interface StringQuerySpecification extends QuerySpecificationBase { + query: string; + specType: QuerySpecificationTypes.String; +} + +// @public +interface StructTypeDescription extends BaseTypeDescription { + // (undocumented) + members: StructFieldMemberDescription[]; + // (undocumented) + valueFormat: PropertyValueFormat.Struct; +} + +// @public +interface StyleOverride extends RuleBase, ConditionContainer { + backColor?: string; + condition?: string; + fontStyle?: FontStyle; + foreColor?: string; + ruleType: RuleTypes.StyleOverride; +} + +// @public +interface SubCondition extends ConditionContainer { + condition?: string; + specifications?: ChildNodeSpecification[]; + subConditions?: SubCondition[]; +} + +// @public +interface SupplementationInfo { + supplementationPurpose: string; +} + +// @public (undocumented) +export function valueFromJSON(json: ValueJSON): Value; + +// @public (undocumented) +interface ValuesArray extends Array { +} + +// @public (undocumented) +export function valuesArrayFromJSON(json: ValuesArrayJSON): ValuesArray; + +// @public (undocumented) +interface ValuesArrayJSON extends Array { +} + +// @public +interface ValuesDictionary { + // (undocumented) + [key: string]: T; +} + +// @public (undocumented) +interface ValuesMap extends ValuesDictionary { +} + +// @public (undocumented) +export function valuesMapFromJSON(json: ValuesMapJSON): ValuesMap; + +// @public (undocumented) +interface ValuesMapJSON extends ValuesDictionary { +} + +// @public +interface Variable { + defaultValue?: string; + id: string; + label: string; + type?: VariableValueType; +} + +// @public +interface VariablesGroup { + label: string; + nestedGroups?: VariablesGroup[]; + vars: Variable[]; +} + +// @public +enum VariableValueType { + Int = "IntValue", + ShowHide = "ShowHide", + String = "StringValue", + YesNo = "YesNo" +} + +// @public +enum VariableValueTypes { + Bool = "bool", + Id64 = "id64", + Id64Array = "id64[]", + Int = "int", + IntArray = "int[]", + String = "string" +} + +// WARNING: Unsupported export: Keys +// WARNING: Unsupported export: HierarchyRpcRequestOptions +// WARNING: Unsupported export: ClientStateSyncRequestOptions +// WARNING: Unsupported export: VariableValue +// WARNING: Unsupported export: FieldJSON +// WARNING: Unsupported export: TypeDescription +// WARNING: Unsupported export: Value +// WARNING: Unsupported export: DisplayValue +// WARNING: Unsupported export: NodeKey +// WARNING: Unsupported export: NodeKeyPath +// WARNING: Unsupported export: NodeKeyJSON +// WARNING: Unsupported export: nodeKeyFromJSON +// WARNING: Unsupported export: isInstanceNodeKey +// WARNING: Unsupported export: isClassGroupingNodeKey +// WARNING: Unsupported export: isPropertyGroupingNodeKey +// WARNING: Unsupported export: isLabelGroupingNodeKey +// WARNING: Unsupported export: isGroupingNodeKey +// WARNING: Unsupported export: SortingRule +// WARNING: Unsupported export: GroupingSpecification +// WARNING: Unsupported export: Rule +// WARNING: Unsupported export: ClassId +// WARNING: Unsupported export: InstanceId +// WARNING: Unsupported export: instanceKeyFromJSON +// WARNING: Unsupported export: InstanceKeysList +// WARNING: Unsupported export: classInfoFromJSON +// WARNING: Unsupported export: propertyInfoFromJSON +// WARNING: Unsupported export: relatedClassInfoFromJSON +// WARNING: Unsupported export: RelationshipPathInfo +// WARNING: Unsupported export: RelationshipPathInfoJSON +// WARNING: Unsupported export: ValueJSON +// WARNING: Unsupported export: DisplayValueJSON +// WARNING: Unsupported export: Paged +// WARNING: Unsupported export: Omit +// WARNING: Unsupported export: Subtract +// WARNING: Unsupported export: getInstancesCount +// (No @packagedocumentation comment for this package) diff --git a/common/api/presentation-components.api.ts b/common/api/presentation-components.api.ts new file mode 100644 index 0000000..7b52dc8 --- /dev/null +++ b/common/api/presentation-components.api.ts @@ -0,0 +1,118 @@ +// @public +class ContentBuilder { + static createPropertyDescription(field: Field): PropertyDescription; + static createPropertyRecord(field: Field, item: Item, path?: Field[]): PropertyRecord; +} + +// @public +class ContentDataProvider implements IContentDataProvider { + constructor(imodel: IModelConnection, ruleset: string | Ruleset, displayType: string); + protected configureContentDescriptor(descriptor: Readonly): Descriptor; + readonly displayType: string; + // (undocumented) + dispose(): void; + getContent: ((pageOptions?: PageOptions | undefined) => Promise | undefined>) & _.MemoizedFunction; + getContentDescriptor: (() => Promise | undefined>) & _.MemoizedFunction; + getContentSetSize: (() => Promise) & _.MemoizedFunction; + imodel: IModelConnection; + // WARNING: The type "CacheInvalidationProps" needs to be exported by the package (e.g. added to index.ts) + protected invalidateCache(props: CacheInvalidationProps): void; + protected isFieldHidden(_field: Field): boolean; + keys: Readonly; + rulesetId: string; + selectionInfo: Readonly | undefined; + protected shouldExcludeFromDescriptor(field: Field): boolean; +} + +// @public +class DataProvidersFactory { + constructor(props?: DataProvidersFactoryProps); + // WARNING: The type "PresentationTableDataProviderProps" needs to be exported by the package (e.g. added to index.ts) + createSimilarInstancesTableDataProvider(propertiesProvider: IPresentationPropertyDataProvider, record: PropertyRecord, props: Omit): Promise; +} + +// @public +interface DataProvidersFactoryProps { + // (undocumented) + rulesetsFactory?: RulesetsFactory; +} + +// @public +interface IPresentationTreeDataProvider extends ITreeDataProvider, IPresentationDataProvider { + getFilteredNodePaths(filter: string): Promise; + getNodeKey(node: TreeNodeItem): NodeKey; +} + +// @public +class PresentationPropertyDataProvider extends ContentDataProvider, implements IPresentationPropertyDataProvider { + constructor(imodel: IModelConnection, rulesetId: string); + // (undocumented) + protected configureContentDescriptor(descriptor: Readonly): Descriptor; + getData(): Promise; + protected getMemoizedData: (() => Promise) & _.MemoizedFunction; + includeFieldsWithNoValues: boolean; + // WARNING: The type "CacheInvalidationProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected invalidateCache(props: CacheInvalidationProps): void; + protected isFieldFavorite(_field: Field): boolean; + // (undocumented) + onDataChanged: PropertyDataChangeEvent; + protected shouldExcludeFromDescriptor(field: Field): boolean; + protected sortCategories(categories: CategoryDescription[]): void; + protected sortFields(_category: CategoryDescription, fields: Field[]): void; +} + +// @public +class PresentationTableDataProvider extends ContentDataProvider, implements IPresentationTableDataProvider { + // WARNING: The type "PresentationTableDataProviderProps" needs to be exported by the package (e.g. added to index.ts) + constructor(props: PresentationTableDataProviderProps); + protected configureContentDescriptor(descriptor: Readonly): Descriptor; + filterExpression: string | undefined; + getColumns: (() => Promise) & _.MemoizedFunction; + getLoadedRow(rowIndex: number): Readonly | undefined; + getRow(rowIndex: number): Promise; + getRowsCount(): Promise; + // WARNING: The type "CacheInvalidationProps" needs to be exported by the package (e.g. added to index.ts) + // (undocumented) + protected invalidateCache(props: CacheInvalidationProps): void; + // (undocumented) + onColumnsChanged: TableDataChangeEvent; + // (undocumented) + onRowsChanged: TableDataChangeEvent; + sort(columnIndex: number, sortDirection: UiSortDirection): Promise; + readonly sortColumn: Promise; + readonly sortColumnKey: string | undefined; + readonly sortDirection: UiSortDirection; +} + +// @public +class PresentationTreeDataProvider implements IPresentationTreeDataProvider { + constructor(imodel: IModelConnection, rulesetId: string); + getFilteredNodePaths: (filter: string) => Promise; + getNodeKey(node: TreeNodeItem): NodeKey; + getNodes: ((parentNode?: TreeNodeItem | undefined, pageOptions?: PageOptions | undefined) => Promise) & _.MemoizedFunction; + getNodesCount: ((parentNode?: TreeNodeItem | undefined) => Promise) & _.MemoizedFunction; + readonly imodel: IModelConnection; + readonly rulesetId: string; +} + +// @public +export function propertyGridWithUnifiedSelection

(PropertyGridComponent: React.ComponentType

): React.ComponentType & Props>; + +// @public +export function tableWithUnifiedSelection

(TableComponent: React.ComponentType

): React.ComponentType & Props>; + +// @public +export function treeWithFilteringSupport

(TreeComponent: React.ComponentType

): React.ComponentType

; + +// @public +export function treeWithUnifiedSelection

(TreeComponent: React.ComponentType

): React.ComponentType, Props> & Props>; + +// @public +export function viewWithUnifiedSelection

(ViewportComponent: React.ComponentType

): React.ComponentType

; + +// WARNING: Unsupported export: IPresentationPropertyDataProvider +// WARNING: Unsupported export: IPresentationTableDataProvider +// (No @packagedocumentation comment for this package) diff --git a/common/api/presentation-frontend.api.ts b/common/api/presentation-frontend.api.ts new file mode 100644 index 0000000..27d71af --- /dev/null +++ b/common/api/presentation-frontend.api.ts @@ -0,0 +1,137 @@ +// @public +interface ISelectionProvider { + getSelection(imodel: IModelConnection, level: number): Readonly; + selectionChange: SelectionChangeEvent; +} + +// @public +class PersistenceHelper { + static createKeySet(imodel: IModelConnection, container: PersistentKeysContainer): Promise; + static createPersistentKeysContainer(imodel: IModelConnection, keyset: KeySet): Promise; +} + +// @public +class Presentation { + // (undocumented) + static i18n: I18N; + // WARNING: The type "PresentationManagerProps" needs to be exported by the package (e.g. added to index.ts) + static initialize(props?: PresentationManagerProps): void; + // (undocumented) + static presentation: PresentationManager; + // (undocumented) + static selection: SelectionManager; + static terminate(): void; +} + +// @public +class PresentationManager implements IDisposable { + activeLocale: string | undefined; + // WARNING: The type "Props" needs to be exported by the package (e.g. added to index.ts) + static create(props?: Props): PresentationManager; + // (undocumented) + dispose(): void; + getChildren(requestOptions: Paged>, parentKey: Readonly): Promise>>; + getChildrenCount(requestOptions: HierarchyRequestOptions, parentKey: Readonly): Promise; + getContent(requestOptions: Paged>, descriptor: Readonly, keys: Readonly): Promise>; + getContentDescriptor(requestOptions: ContentRequestOptions, displayType: string, keys: Readonly, selection: Readonly | undefined): Promise | undefined>; + getContentSetSize(requestOptions: ContentRequestOptions, descriptor: Readonly, keys: Readonly): Promise; + getDistinctValues(requestOptions: ContentRequestOptions, descriptor: Readonly, keys: Readonly, fieldName: string, maximumValueCount?: number): Promise; + getFilteredNodePaths(requestOptions: HierarchyRequestOptions, filterText: string): Promise; + getNodePaths(requestOptions: HierarchyRequestOptions, paths: InstanceKey[][], markedIndex: number): Promise; + getRootNodes(requestOptions: Paged>): Promise>>; + getRootNodesCount(requestOptions: HierarchyRequestOptions): Promise; + // (undocumented) + readonly rpcRequestsHandler: RpcRequestsHandler; + // WARNING: The type "RulesetManager" needs to be exported by the package (e.g. added to index.ts) + rulesets(): RulesetManager; + vars(rulesetId: string): RulesetVariablesManager; +} + +// @public (undocumented) +class RulesetVariablesManager implements IClientStateHolder { + constructor(rulesetId: string); + getBool(variableId: string): Promise; + getId64(variableId: string): Promise; + getId64s(variableId: string): Promise; + getInt(variableId: string): Promise; + getInts(variableId: string): Promise; + getString(variableId: string): Promise; + // (undocumented) + key: string; + // (undocumented) + onStateChanged: BeEvent<() => void>; + setBool(variableId: string, value: boolean): Promise; + setId64(variableId: string, value: Id64String): Promise; + setId64s(variableId: string, value: Id64String[]): Promise; + setInt(variableId: string, value: number): Promise; + setInts(variableId: string, value: number[]): Promise; + setString(variableId: string, value: string): Promise; + // (undocumented) + readonly state: RulesetVariablesState; +} + +// @public +class SelectionChangeEvent extends BeEvent { +} + +// @public +interface SelectionChangeEventArgs { + changeType: SelectionChangeType; + imodel: IModelConnection; + keys: Readonly; + level: number; + rulesetId?: string; + source: string; + timestamp: Date; +} + +// @public +enum SelectionChangeType { + Add = 0, + Clear = 3, + Remove = 1, + Replace = 2 +} + +// @public +class SelectionHandler implements IDisposable { + constructor(manager: SelectionManager, name: string, imodel: IModelConnection, rulesetId?: string, onSelect?: SelectionChangesListener); + addToSelection(keys: Keys, level?: number): void; + clearSelection(level?: number): void; + dispose(): void; + getSelection(level?: number): Readonly; + getSelectionLevels(): number[]; + // (undocumented) + imodel: IModelConnection; + // (undocumented) + name: string; + // (undocumented) + onSelect?: SelectionChangesListener; + protected onSelectionChanged: (evt: SelectionChangeEventArgs, provider: ISelectionProvider) => void; + removeFromSelection(keys: Keys, level?: number): void; + replaceSelection(keys: Keys, level?: number): void; + // (undocumented) + rulesetId?: string; + protected shouldHandle(evt: SelectionChangeEventArgs): boolean; +} + +// @public +class SelectionManager implements ISelectionProvider { + // WARNING: The type "SelectionManagerProps" needs to be exported by the package (e.g. added to index.ts) + constructor(props: SelectionManagerProps); + addToSelection(source: string, imodel: IModelConnection, keys: Keys, level?: number, rulesetId?: string): void; + addToSelectionWithScope(source: string, imodel: IModelConnection, keys: EntityProps | EntityProps[], scope: SelectionScope | string, level?: number, rulesetId?: string): Promise; + clearSelection(source: string, imodel: IModelConnection, level?: number, rulesetId?: string): void; + getSelection(imodel: IModelConnection, level?: number): Readonly; + getSelectionLevels(imodel: IModelConnection): number[]; + removeFromSelection(source: string, imodel: IModelConnection, keys: Keys, level?: number, rulesetId?: string): void; + removeFromSelectionWithScope(source: string, imodel: IModelConnection, keys: EntityProps | EntityProps[], scope: SelectionScope | string, level?: number, rulesetId?: string): Promise; + replaceSelection(source: string, imodel: IModelConnection, keys: Keys, level?: number, rulesetId?: string): void; + replaceSelectionWithScope(source: string, imodel: IModelConnection, keys: EntityProps | EntityProps[], scope: SelectionScope | string, level?: number, rulesetId?: string): Promise; + // WARNING: The type "SelectionScopesManager" needs to be exported by the package (e.g. added to index.ts) + readonly scopes: SelectionScopesManager; + readonly selectionChange: SelectionChangeEvent; +} + +// WARNING: Unsupported export: SelectionChangesListener +// (No @packagedocumentation comment for this package) diff --git a/common/config/rush/browser-approved-packages.json b/common/config/rush/browser-approved-packages.json index cfde6ba..d2a7d53 100644 --- a/common/config/rush/browser-approved-packages.json +++ b/common/config/rush/browser-approved-packages.json @@ -12,7 +12,7 @@ }, { "name": "@bentley/bwc", - "allowedCategories": [ "frontend", "internal" ] + "allowedCategories": [ "internal" ] }, { "name": "@bentley/config-loader", @@ -70,6 +70,10 @@ "name": "@bentley/imodeljs-webserver", "allowedCategories": [ "internal" ] }, + { + "name": "@bentley/logger-config", + "allowedCategories": [ "internal" ] + }, { "name": "@bentley/presentation-backend", "allowedCategories": [ "internal", "tools" ] @@ -110,10 +114,18 @@ "name": "@bentley/webpack-tools", "allowedCategories": [ "common", "frontend", "internal" ] }, + { + "name": "@microsoft/api-extractor", + "allowedCategories": [ "tools" ] + }, { "name": "@openid/appauth", "allowedCategories": [ "common" ] }, + { + "name": "@svgdotjs/svg.js", + "allowedCategories": [ "frontend" ] + }, { "name": "app-root-path", "allowedCategories": [ "tools" ] @@ -126,17 +138,21 @@ "name": "autoprefixer", "allowedCategories": [ "internal", "tools" ] }, + { + "name": "benchmark", + "allowedCategories": [ "common" ] + }, { "name": "body-parser", "allowedCategories": [ "backend", "internal" ] }, { "name": "bunyan", - "allowedCategories": [ "common", "internal" ] + "allowedCategories": [ "backend", "common", "internal" ] }, { "name": "bunyan-seq", - "allowedCategories": [ "common" ] + "allowedCategories": [ "backend", "common" ] }, { "name": "cache-require-paths", @@ -156,7 +172,7 @@ }, { "name": "chai-as-promised", - "allowedCategories": [ "backend", "common", "frontend", "internal" ] + "allowedCategories": [ "backend", "common", "frontend", "internal", "tools" ] }, { "name": "chai-jest-snapshot", @@ -182,6 +198,10 @@ "name": "chokidar", "allowedCategories": [ "tools" ] }, + { + "name": "chrome-launcher", + "allowedCategories": [ "internal" ] + }, { "name": "chromedriver", "allowedCategories": [ "tools" ] @@ -250,6 +270,10 @@ "name": "dotenv", "allowedCategories": [ "tools" ] }, + { + "name": "draco3d", + "allowedCategories": [ "frontend" ] + }, { "name": "electron", "allowedCategories": [ "backend", "internal" ] @@ -314,6 +338,10 @@ "name": "fs-extra", "allowedCategories": [ "backend", "common", "internal", "tools" ] }, + { + "name": "fs-write-stream-atomic", + "allowedCategories": [ "common" ] + }, { "name": "fuse.js", "allowedCategories": [ "frontend", "internal" ] @@ -322,6 +350,10 @@ "name": "glob", "allowedCategories": [ "backend", "common", "internal", "tools" ] }, + { + "name": "highlight.js", + "allowedCategories": [ "frontend" ] + }, { "name": "html-webpack-plugin", "allowedCategories": [ "frontend", "tools" ] @@ -652,7 +684,7 @@ }, { "name": "semver", - "allowedCategories": [ "backend", "common", "internal" ] + "allowedCategories": [ "backend", "common", "frontend", "internal" ] }, { "name": "sinon", @@ -668,7 +700,7 @@ }, { "name": "source-map-support", - "allowedCategories": [ "backend", "common" ] + "allowedCategories": [ "backend", "common", "frontend" ] }, { "name": "style-loader", @@ -684,7 +716,11 @@ }, { "name": "svg-sprite-loader", - "allowedCategories": [ "internal", "tools" ] + "allowedCategories": [ "frontend", "internal", "tools" ] + }, + { + "name": "svg.js", + "allowedCategories": [ "frontend" ] }, { "name": "sw-precache-webpack-plugin", @@ -740,7 +776,11 @@ }, { "name": "typedoc-plugin-external-module-name", - "allowedCategories": [ "backend", "common", "frontend" ] + "allowedCategories": [ "tools" ] + }, + { + "name": "typedoc-plugin-internal-external", + "allowedCategories": [ "tools" ] }, { "name": "typemoq", @@ -756,7 +796,7 @@ }, { "name": "uglifyjs-webpack-plugin", - "allowedCategories": [ "internal", "tools" ] + "allowedCategories": [ "frontend", "internal", "tools" ] }, { "name": "url-loader", @@ -812,7 +852,7 @@ }, { "name": "webpack-merge", - "allowedCategories": [ "tools" ] + "allowedCategories": [ "frontend", "tools" ] }, { "name": "webpack-node-externals", diff --git a/common/config/rush/command-line.json b/common/config/rush/command-line.json index 3c4ba17..67b63e3 100644 --- a/common/config/rush/command-line.json +++ b/common/config/rush/command-line.json @@ -1,6 +1,7 @@ { "$schema": "https://dev.office.com/json-schemas/rush/command-line.schema.json", - "commands": [{ + "commands": [ + { "name": "audit", "commandKind": "global", "summary": "Runs npm audit for the entire monorepo", @@ -20,7 +21,7 @@ "commandKind": "bulk", "summary": "Run cover script for each package", "description": "Iterates through each package in the monorepo and runs the 'cover' script", - "enableParallelism": false, + "enableParallelism": true, "ignoreMissingScript": false }, { @@ -31,6 +32,14 @@ "enableParallelism": true, "ignoreMissingScript": false }, + { + "name": "extract-api", + "commandKind": "bulk", + "summary": "Run api-extractor to generate API signature files", + "description": "Iterates through each package in the monorepo and runs the 'extract-api' script", + "enableParallelism": true, + "ignoreMissingScript": true + }, { "name": "lint", "commandKind": "bulk", @@ -54,22 +63,60 @@ "description": "Iterates through each package in the monorepo and runs the 'test:integration' script", "enableParallelism": true, "ignoreMissingScript": true + } + ], + "parameters": [ + { + "parameterKind": "flag", + "longName": "--production", + "description": "Sets production build for iModelJs modules", + "associatedCommands": [ + "build", + "rebuild" + ] }, { - "name": "webpacklib-dev", - "commandKind": "bulk", - "summary": "Create the module for every iModelJs library package", - "description": "Iterates through each package in the monorepo and runs the 'webpacklib:dev' script", - "enableParallelism": true, - "ignoreMissingScript": true + "parameterKind": "flag", + "longName": "--stats", + "shortName": "-s", + "description": "Stores the webpack json stats for iModelJs system modules", + "associatedCommands": [ + "build", + "rebuild" + ] }, { - "name": "webpacklib-prod", - "commandKind": "bulk", - "summary": "Create the module for every iModelJs library package", - "description": "Iterates through each package in the monorepo and runs the 'webpacklib:dev' script", - "enableParallelism": true, - "ignoreMissingScript": true + "parameterKind": "choice", + "longName": "--detail", + "shortName": "-d", + "description": "Selects the level of output when building iModelJs modules", + "alternatives": [ + { + "name": "0", + "description": "silent except for errors" + }, + { + "name": "1", + "description": "reports steps" + }, + { + "name": "2", + "description": "shows webpack stats" + }, + { + "name": "3", + "description": "reports skipped steps" + }, + { + "name": "4", + "description": "reports sub-steps" + } + ], + "defaultValue": "0", + "associatedCommands": [ + "build", + "rebuild" + ] } ] } \ No newline at end of file diff --git a/common/config/rush/common-versions.json b/common/config/rush/common-versions.json index fddd577..3574f3c 100644 --- a/common/config/rush/common-versions.json +++ b/common/config/rush/common-versions.json @@ -1,8 +1,8 @@ { "preferredVersions": { - "@types/react": "^16.4.14", + "@types/react": "16.7.22", "event-stream": "~4.0.1", "ssri": "^6.0.1", - "typescript": "~3.1.0" + "typescript": "~3.2.2" } } \ No newline at end of file diff --git a/common/config/rush/shrinkwrap.yaml b/common/config/rush/shrinkwrap.yaml index 0b61bbf..8c5f776 100644 --- a/common/config/rush/shrinkwrap.yaml +++ b/common/config/rush/shrinkwrap.yaml @@ -1,9 +1,9 @@ dependencies: - '@bentley/bwc': 7.1.0 - '@bentley/dev-cors-proxy-server': 0.0.9 - '@bentley/icons-generic-webfont': 0.0.6 - '@bentley/imodeljs-native': 0.86.0 - '@openid/appauth': 1.2.0 + '@bentley/dev-cors-proxy-server': npm.bentley.com/@bentley/dev-cors-proxy-server/0.0.9 + '@bentley/icons-generic-webfont': npm.bentley.com/@bentley/icons-generic-webfont/0.0.9 + '@bentley/imodeljs-native': npm.bentley.com/@bentley/imodeljs-native/0.99.0 + '@microsoft/api-extractor': 6.3.0 + '@openid/appauth': 1.2.1 '@rush-temp/agent-test-app': 'file:projects/agent-test-app.tgz' '@rush-temp/bentleyjs-core': 'file:projects/bentleyjs-core.tgz' '@rush-temp/build-tools': 'file:projects/build-tools.tgz' @@ -25,6 +25,8 @@ dependencies: '@rush-temp/imodeljs-i18n': 'file:projects/imodeljs-i18n.tgz' '@rush-temp/imodeljs-quantity': 'file:projects/imodeljs-quantity.tgz' '@rush-temp/imodeljs-webserver': 'file:projects/imodeljs-webserver.tgz' + '@rush-temp/logger-config': 'file:projects/logger-config.tgz' + '@rush-temp/plugin-markup': 'file:projects/plugin-markup.tgz' '@rush-temp/presentation-backend': 'file:projects/presentation-backend.tgz' '@rush-temp/presentation-common': 'file:projects/presentation-common.tgz' '@rush-temp/presentation-components': 'file:projects/presentation-components.tgz' @@ -41,8 +43,10 @@ dependencies: '@rush-temp/ui-ninezone': 'file:projects/ui-ninezone.tgz' '@rush-temp/ui-test-app': 'file:projects/ui-test-app.tgz' '@rush-temp/webpack-tools': 'file:projects/webpack-tools.tgz' + '@rush-temp/webworker-test-app': 'file:projects/webworker-test-app.tgz' '@types/body-parser': 1.17.0 '@types/bunyan': 1.8.5 + '@types/bunyan-seq': 0.2.1 '@types/chai': 4.1.7 '@types/chai-as-promised': 7.1.0 '@types/chai-jest-snapshot': 1.3.4 @@ -52,35 +56,34 @@ dependencies: '@types/cpx': 1.5.0 '@types/deep-assign': 0.1.1 '@types/deep-equal': 1.0.1 - '@types/enzyme': 3.1.15 - '@types/express': 4.16.0 - '@types/express-session': 1.15.11 - '@types/faker': 4.1.4 + '@types/enzyme': 3.9.0 + '@types/express': 4.16.1 + '@types/express-session': 1.15.12 + '@types/faker': 4.1.5 '@types/form-data': 2.2.1 '@types/fs-extra': 4.0.8 '@types/glob': 5.0.36 + '@types/highlight.js': 9.12.3 '@types/i18next': 8.4.6 - '@types/i18next-browser-languagedetector': 2.0.1 - '@types/i18next-node-fs-backend': 0.0.30 - '@types/i18next-xhr-backend': 1.4.1 + '@types/i18next-browser-languagedetector': 2.0.2 '@types/js-base64': 2.3.1 - '@types/jsdom': 12.2.1 + '@types/jsdom': 12.2.2 '@types/json5': 0.0.30 - '@types/lodash': 4.14.119 + '@types/lodash': 4.14.121 '@types/lolex': 2.1.3 '@types/minimist': 1.2.0 - '@types/mocha': 5.2.5 + '@types/mocha': 5.2.6 '@types/multiparty': 0.0.31 - '@types/nock': 9.3.0 - '@types/node': 10.10.3 + '@types/nock': 9.3.1 + '@types/node': 10.12.18 '@types/passport': 0.4.7 '@types/qs': 6.5.1 - '@types/react': 16.7.18 - '@types/react-data-grid': 4.0.1 - '@types/react-dom': 16.0.7 + '@types/react': 16.7.22 + '@types/react-data-grid': 4.0.2 + '@types/react-dom': 16.0.11 '@types/react-highlight-words': 0.11.1 - '@types/react-redux': 5.0.21 - '@types/react-resize-detector': 3.1.0 + '@types/react-redux': 7.0.1 + '@types/react-resize-detector': 3.1.1 '@types/react-router-dom': 4.3.1 '@types/react-virtualized': 9.18.12 '@types/request-promise-native': 1.0.15 @@ -88,14 +91,14 @@ dependencies: '@types/semver': 5.5.0 '@types/sinon': 5.0.7 '@types/sinon-chai': 3.2.2 - '@types/source-map-support': 0.4.1 - '@types/superagent': 3.8.6 + '@types/source-map-support': 0.4.2 + '@types/superagent': 3.8.7 '@types/url-search-params': 0.10.2 - '@types/webdriverio': 4.13.1 + '@types/webdriverio': 4.13.3 '@types/webpack': 3.8.17 '@types/ws': 6.0.1 '@types/xmldom': 0.1.29 - '@types/yargs': 12.0.5 + '@types/yargs': 12.0.9 app-root-path: 2.1.0 asn1: 0.2.3 autoprefixer: 8.6.5 @@ -104,7 +107,7 @@ dependencies: bunyan-seq: 0.2.0 cache-require-paths: 0.3.0 callable-instance2: 1.0.0 - case-sensitive-paths-webpack-plugin: 2.1.2 + case-sensitive-paths-webpack-plugin: 2.2.0 chai: 4.2.0 chai-as-promised: 7.1.1 chai-jest-snapshot: 2.0.0 @@ -112,27 +115,28 @@ dependencies: chai-string: 1.5.0 chalk: 2.4.2 child_process: 1.0.2 - chokidar: 2.0.4 + chokidar: 2.1.2 + chrome-launcher: 0.10.5 classnames: 2.2.6 cli-highlight: 2.0.0 commander: 2.19.0 comment-json: 1.1.3 concurrently: 3.6.1 - cookie-parser: 1.4.3 + cookie-parser: 1.4.4 cpx: 1.5.0 cross-env: 5.2.0 cross-spawn: 6.0.5 css-loader: 0.28.11 - csstype: 2.6.0 + csstype: 2.6.2 debug: 2.6.9 deep-assign: 2.0.0 deep-equal: 1.0.1 delay-cli: 1.1.0 dotenv: 6.2.0 - electron: 4.0.1 + electron: 4.0.6 electron-chromedriver: 2.0.0 - enzyme: 3.8.0 - enzyme-adapter-react-16: 1.7.1 + enzyme: 3.9.0 + enzyme-adapter-react-16: 1.10.0 enzyme-to-json: 3.3.5 escape-string-regexp: 1.0.5 event-stream: 4.0.1 @@ -146,14 +150,15 @@ dependencies: fork-ts-checker-webpack-plugin: 0.4.15 form-data: 2.3.2 fs-extra: 6.0.1 - fuse.js: 3.3.0 + fs-write-stream-atomic: 1.0.10 + fuse.js: 3.4.2 glob: 7.1.3 + highlight.js: 9.14.2 html-webpack-plugin: 4.0.0-alpha.2 https-proxy-agent: 2.2.1 i18next: 10.6.0 i18next-browser-languagedetector: 2.2.4 - i18next-node-fs-backend: 2.1.0 - i18next-xhr-backend: 1.5.1 + i18next-xhr-backend: 2.0.1 ignore-styles: 5.0.1 immutable: 3.8.2 imports-loader: 0.8.0 @@ -161,7 +166,7 @@ dependencies: istanbul-instrumenter-loader: 3.0.1 istanbul-lib-hook: 1.2.2 istanbul-lib-instrument: 2.3.2 - js-base64: 2.5.0 + js-base64: 2.5.1 jsdom: 11.12.0 jsdom-global: 3.0.2 json-schema-faker: 0.5.0-rc16 @@ -170,11 +175,10 @@ dependencies: lodash: 4.17.11 lolex: 2.7.5 make-dir: 1.3.0 - make-dir-cli: 1.0.0 merge-json: 0.1.0-b.3 mini-css-extract-plugin: 0.4.5 minimist: 1.2.0 - mobx: 5.8.0 + mobx: 5.9.0 mobx-react: 5.4.3 mocha: 5.2.0 mocha-junit-reporter: 1.18.0 @@ -184,17 +188,17 @@ dependencies: node-glob: 1.2.0 node-gyp: 3.6.2 node-sass: 4.11.0 - nodemon: 1.18.9 + nodemon: 1.18.10 npm-run-all: 4.1.5 null-loader: 0.1.1 - nyc: 13.1.0 + nyc: 13.3.0 obj-traverse: 1.0.0 object-assign: 4.1.1 oidc-client: 1.6.1 openid-client: 2.4.5 passport: 0.4.0 path: 0.12.7 - popper.js: 1.14.6 + popper.js: 1.14.7 postcss-flexbugs-fixes: 3.3.1 postcss-loader: 2.1.6 promise: 8.0.2 @@ -202,34 +206,35 @@ dependencies: raf: 3.4.1 raf-schd: 4.0.0 raw-loader: 0.5.1 - react: 16.7.0 + react: 16.8.3 react-data-grid: 6.0.1 react-dev-utils: 6.1.1 react-dnd: 5.0.0 react-dnd-html5-backend: 5.0.1 react-dnd-test-backend: 5.0.1 - react-dom: 16.7.0 + react-dom: 16.8.3 react-highlight-words: 0.14.0 react-markdown: 3.6.0 react-redux: 5.1.1 react-resize-detector: 3.4.0 react-router-dom: 4.3.1 react-split-pane: 0.1.77 - react-test-renderer: 16.7.0 - react-testing-library: 5.4.4 + react-test-renderer: 16.8.3 + react-testing-library: 5.9.0 react-virtualized: 9.21.0 readline: 1.3.0 recursive-readdir: 2.2.2 redux: 4.0.1 request: 2.88.0 - request-promise-native: 1.0.5 + request-promise: 4.2.4 + request-promise-native: 1.0.7 resize-observer-polyfill: 1.5.1 - resolve: 1.9.0 + resolve: 1.10.0 rimraf: 2.6.3 sass-loader: 7.1.0 save: 2.3.3 semver: 5.6.0 - sinon: 7.2.2 + sinon: 7.2.5 sinon-chai: 3.3.0 source-map-loader: 0.2.4 source-map-support: 0.5.10 @@ -238,22 +243,24 @@ dependencies: superagent: 3.8.3 svg-react-loader: 0.4.6 svg-sprite-loader: 3.9.2 + svg.js: 2.7.1 sw-precache-webpack-plugin: 0.11.5 tooltip.js: 1.3.1 tree-kill: 1.2.1 ts-key-enum: 2.0.0 ts-loader: 4.5.0 ts-node: 7.0.1 - tsconfig-paths: 3.7.0 + tsconfig-paths: 3.8.0 tslib: 1.9.3 - tslint: 5.12.1 + tslint: 5.13.0 tslint-consistent-codestyle: 1.15.0 tslint-loader: 3.6.0 - tsutils: 3.7.0 + tsutils: 3.8.0 typedoc: 0.11.1 - typedoc-plugin-external-module-name: 1.1.3 + typedoc-plugin-external-module-name: 1.1.1 + typedoc-plugin-internal-external: 1.0.10 typemoq: 2.1.0 - typescript: 3.1.6 + typescript: 3.2.4 typescript-json-schema: 0.28.0 uglifyjs-webpack-plugin: 1.3.0 url-loader: 1.1.2 @@ -265,14 +272,14 @@ dependencies: wdio-spec-reporter: 0.1.5 wdio-visual-regression-service: 0.9.0 webdriverio: 4.14.2 - webpack: 4.28.4 - webpack-cli: 3.2.1 - webpack-dev-server: 3.1.14 + webpack: 4.29.6 + webpack-cli: 3.2.3 + webpack-dev-server: 3.2.1 webpack-manifest-plugin: 2.0.3 webpack-merge: 4.2.1 webpack-node-externals: 1.7.2 whatwg-fetch: 2.0.4 - ws: 6.1.2 + ws: 6.1.4 xmldom: 0.1.27 xmlhttprequest: 1.8.0 xpath: 0.0.27 @@ -301,16 +308,16 @@ packages: dev: false resolution: integrity: sha1-bHV1/952HQdIXgS67cA5LG2eMPY= - /@babel/generator/7.2.2: + /@babel/generator/7.3.4: dependencies: - '@babel/types': 7.2.2 + '@babel/types': 7.3.4 jsesc: 2.5.2 lodash: 4.17.11 source-map: 0.5.7 trim-right: 1.0.1 dev: false resolution: - integrity: sha512-I4o675J/iS8k+P38dvJ3IBGqObLXyQLTxtrR4u9cSUJOURvafeEWb/pFMOTwtNrmq73mJzyF6ueTbO1BtN0Zeg== + integrity: sha512-8EXhHRFqlVVWXPezBW5keTiQi/rJMQTg/Y9uVCEZ0CAF3PKtCCaVRnp64Ii1ujhkoDhhF1fVsImoN4yJ2uz4Wg== /@babel/helper-function-name/7.0.0-beta.51: dependencies: '@babel/helper-get-function-arity': 7.0.0-beta.51 @@ -323,13 +330,13 @@ packages: dependencies: '@babel/helper-get-function-arity': 7.0.0 '@babel/template': 7.2.2 - '@babel/types': 7.2.2 + '@babel/types': 7.3.4 dev: false resolution: integrity: sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw== /@babel/helper-get-function-arity/7.0.0: dependencies: - '@babel/types': 7.2.2 + '@babel/types': 7.3.4 dev: false resolution: integrity: sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ== @@ -341,7 +348,7 @@ packages: integrity: sha1-MoGy0EWvlcFyzpGyCCXYXqRnZBE= /@babel/helper-split-export-declaration/7.0.0: dependencies: - '@babel/types': 7.2.2 + '@babel/types': 7.3.4 dev: false resolution: integrity: sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag== @@ -374,19 +381,19 @@ packages: hasBin: true resolution: integrity: sha1-J87C30Cd9gr1gnDtj2qlVAnqhvY= - /@babel/parser/7.2.3: + /@babel/parser/7.3.4: dev: false engines: node: '>=6.0.0' hasBin: true resolution: - integrity: sha512-0LyEcVlfCoFmci8mXx8A5oIkpkOgyo8dRHtxBnK9RRBwxO2+JZPNsqtVEZQ7mJFPxnXF9lfmU24mHOPI0qnlkA== - /@babel/runtime/7.2.0: + integrity: sha512-tXZCqWtlOOP4wgCp6RjRvLmfuhnqTLy9VHwRochJBCP2nDm27JnnuFEnXFASVyQNHk36jD1tAammsCEEqgscIQ== + /@babel/runtime/7.3.4: dependencies: regenerator-runtime: 0.12.1 dev: false resolution: - integrity: sha512-oouEibCbHMVdZSDlJBO6bZmID/zA/G/Qx3H1d3rSNPTD+L8UNKvCat7aKWSJ74zYbm5zWGh0GQN0hKj8zYFTCg== + integrity: sha512-IvfvnMdSaLBateu0jfsYIpZTxAc2cKEXEMiezGGN75QcBcecDUKd3PgLAncT0oOgxKy8dd8hrJKj9MfzgfZd6g== /@babel/template/7.0.0-beta.51: dependencies: '@babel/code-frame': 7.0.0-beta.51 @@ -399,8 +406,8 @@ packages: /@babel/template/7.2.2: dependencies: '@babel/code-frame': 7.0.0 - '@babel/parser': 7.2.3 - '@babel/types': 7.2.2 + '@babel/parser': 7.3.4 + '@babel/types': 7.3.4 dev: false resolution: integrity: sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g== @@ -413,26 +420,26 @@ packages: '@babel/parser': 7.0.0-beta.51 '@babel/types': 7.0.0-beta.51 debug: 3.2.6 - globals: 11.10.0 + globals: 11.11.0 invariant: 2.2.4 lodash: 4.17.11 dev: false resolution: integrity: sha1-mB2vLOw0emIx06odnhgDsDqqpKg= - /@babel/traverse/7.2.3: + /@babel/traverse/7.3.4: dependencies: '@babel/code-frame': 7.0.0 - '@babel/generator': 7.2.2 + '@babel/generator': 7.3.4 '@babel/helper-function-name': 7.1.0 '@babel/helper-split-export-declaration': 7.0.0 - '@babel/parser': 7.2.3 - '@babel/types': 7.2.2 + '@babel/parser': 7.3.4 + '@babel/types': 7.3.4 debug: 4.1.1 - globals: 11.10.0 + globals: 11.11.0 lodash: 4.17.11 dev: false resolution: - integrity: sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw== + integrity: sha512-TvTHKp6471OYEcE/91uWmhR6PrrYywQntCHSaZ8CM8Vmp+pjAusal4nGB2WCCQd0rvI7nOMKn9GnbcvTUz3/ZQ== /@babel/types/7.0.0-beta.51: dependencies: esutils: 2.0.2 @@ -441,82 +448,34 @@ packages: dev: false resolution: integrity: sha1-2AK3tUO1g2x3iqaReXq/APPZfqk= - /@babel/types/7.2.2: + /@babel/types/7.3.4: dependencies: esutils: 2.0.2 lodash: 4.17.11 to-fast-properties: 2.0.0 dev: false resolution: - integrity: sha512-fKCuD6UFUMkR541eDWL+2ih/xFZBXPOg/7EQFeTluMDebfqR4jrpaCjLhkWlQS4hT6nRa2PMEgXKbRB5/H2fpg== - /@bentley/bwc/7.1.0: - dependencies: - classnames: 2.2.6 - highlight.js: 9.13.1 - js-beautify: 1.8.9 - react: 16.7.0 - react-dom: /react-dom/16.7.0/react@16.7.0 - react-syntax-highlighter: /react-syntax-highlighter/3.0.2/react@16.7.0 - dev: false - peerDependencies: - '@bentley/icons-generic-webfont': 0.0.x - resolution: - integrity: sha512-USw95zSq8MrhJBJKhzk52mN+YUgtvt06afx6GYyxhHMUDLG3gv3GZ/hcVLhiMxzNDdHLyTp/ONpQcMZLb0pUow== - /@bentley/bwc/7.1.0/205ff1ecb36db98d7643dc5de57c3f08: - dependencies: - '@bentley/icons-generic-webfont': 0.0.6 - classnames: 2.2.6 - highlight.js: 9.13.1 - js-beautify: 1.8.9 - react: 16.7.0 - react-dom: /react-dom/16.7.0/react@16.7.0 - react-syntax-highlighter: /react-syntax-highlighter/3.0.2/react@16.7.0 - dev: false - id: registry.npmjs.org/@bentley/bwc/7.1.0 - peerDependencies: - '@bentley/icons-generic-webfont': 0.0.x - resolution: - integrity: sha512-USw95zSq8MrhJBJKhzk52mN+YUgtvt06afx6GYyxhHMUDLG3gv3GZ/hcVLhiMxzNDdHLyTp/ONpQcMZLb0pUow== - /@bentley/dev-cors-proxy-server/0.0.9: - dependencies: - http-proxy: 1.11.1 - https-proxy-agent: 2.2.1 - proxy-from-env: 0.0.1 - dev: false - engines: - node: '>=0.10.0' - npm: '>=1.1.0' - resolution: - integrity: sha512-dYPVStmvToySKZeHxanY8CnS2WOzem+E3CnhKgOAlnIbIWUt8R5PIzgjz2PVFQElEMdkYQSiI69BK7yT3sFeiA== - /@bentley/icons-generic-webfont/0.0.6: - dev: false - resolution: - integrity: sha512-TtDucaP5eyDbYvFjOBsf8DWQ6BA8HUlSMqjdj0F3bfuX3SxEOlwN9jxQzpquFie+T0k3smr/ueuEtsnKaVOl8g== - /@bentley/imodeljs-native/0.86.0: - dev: false - requiresBuild: true - resolution: - integrity: sha512-3f4Wq08Y7unHVKhl+OLVrzsguz86UEt2LT//sfwQiWiZ69HmTdUWwCOHcx1jRVv94Zn8NWhnvdf7zTi18rbRaQ== + integrity: sha512-WEkp8MsLftM7O/ty580wAmZzN1nDmCACc5+jFzUt+GUFNNIi3LdRlueYz0YIlmJhlZx1QYDMZL5vdWCL0fNjFQ== /@fimbul/bifrost/0.17.0: dependencies: - '@fimbul/ymir': /@fimbul/ymir/0.17.0/tsutils@3.7.0 + '@fimbul/ymir': /@fimbul/ymir/0.17.0/tsutils@3.8.0 get-caller-file: 2.0.1 tslib: 1.9.3 - tsutils: 3.7.0 + tsutils: 3.8.0 dev: false peerDependencies: tslint: ^5.0.0 typescript: '>= 3.0.1 || >= 3.3.0-dev || >= 3.4.0-dev' resolution: integrity: sha512-gVTkJAOef5HtN6LPmrtt5fAUmBywwlgmObsU3FBhPoNeXPLaIl2zywXkJEtvvVLQnaFmtff3x+wIj5lHRCDE3Q== - /@fimbul/bifrost/0.17.0/tslint@5.12.1+typescript@3.1.6: + /@fimbul/bifrost/0.17.0/tslint@5.13.0+typescript@3.2.4: dependencies: - '@fimbul/ymir': /@fimbul/ymir/0.17.0/tsutils@3.7.0+typescript@3.1.6 + '@fimbul/ymir': /@fimbul/ymir/0.17.0/tsutils@3.8.0+typescript@3.2.4 get-caller-file: 2.0.1 tslib: 1.9.3 - tslint: /tslint/5.12.1/typescript@3.1.6 - tsutils: /tsutils/3.7.0/typescript@3.1.6 - typescript: 3.1.6 + tslint: /tslint/5.13.0/typescript@3.2.4 + tsutils: /tsutils/3.8.0/typescript@3.2.4 + typescript: 3.2.4 dev: false id: registry.npmjs.org/@fimbul/bifrost/0.17.0 peerDependencies: @@ -524,12 +483,12 @@ packages: typescript: '>= 3.0.1 || >= 3.3.0-dev || >= 3.4.0-dev' resolution: integrity: sha512-gVTkJAOef5HtN6LPmrtt5fAUmBywwlgmObsU3FBhPoNeXPLaIl2zywXkJEtvvVLQnaFmtff3x+wIj5lHRCDE3Q== - /@fimbul/ymir/0.17.0/tsutils@3.7.0: + /@fimbul/ymir/0.17.0/tsutils@3.8.0: dependencies: inversify: 5.0.1 reflect-metadata: 0.1.13 tslib: 1.9.3 - tsutils: 3.7.0 + tsutils: 3.8.0 dev: false id: registry.npmjs.org/@fimbul/ymir/0.17.0 peerDependencies: @@ -537,13 +496,13 @@ packages: typescript: '>= 3.0.1 || >= 3.3.0-dev || >= 3.4.0-dev' resolution: integrity: sha512-xMXM9KTXRLHLVS6dnX1JhHNEkmWHcAVCQ/4+DA1KKwC/AFnGHzu/7QfQttEPgw3xplT+ILf9e3i64jrFwB3JtA== - /@fimbul/ymir/0.17.0/tsutils@3.7.0+typescript@3.1.6: + /@fimbul/ymir/0.17.0/tsutils@3.8.0+typescript@3.2.4: dependencies: inversify: 5.0.1 reflect-metadata: 0.1.13 tslib: 1.9.3 - tsutils: /tsutils/3.7.0/typescript@3.1.6 - typescript: 3.1.6 + tsutils: /tsutils/3.8.0/typescript@3.2.4 + typescript: 3.2.4 dev: false id: registry.npmjs.org/@fimbul/ymir/0.17.0 peerDependencies: @@ -551,6 +510,48 @@ packages: typescript: '>= 3.0.1 || >= 3.3.0-dev || >= 3.4.0-dev' resolution: integrity: sha512-xMXM9KTXRLHLVS6dnX1JhHNEkmWHcAVCQ/4+DA1KKwC/AFnGHzu/7QfQttEPgw3xplT+ILf9e3i64jrFwB3JtA== + /@microsoft/api-extractor/6.3.0: + dependencies: + '@microsoft/node-core-library': 3.7.0 + '@microsoft/ts-command-line': 4.2.2 + '@microsoft/tsdoc': 0.12.2 + '@types/node': 8.5.8 + '@types/z-schema': 3.16.31 + colors: 1.2.5 + jju: 1.3.0 + lodash: 4.17.11 + resolve: 1.8.1 + typescript: 3.1.6 + z-schema: 3.18.4 + dev: false + hasBin: true + resolution: + integrity: sha512-rhC3Wc/Zaj44W8hJYJlaf+gFGxVwLvSEOnPslYUi3anFbtXBNZpK3ocF10SgtSaca2QfKA8UNN1rCIwXUCdz7g== + /@microsoft/node-core-library/3.7.0: + dependencies: + '@types/fs-extra': 5.0.4 + '@types/node': 8.5.8 + '@types/z-schema': 3.16.31 + colors: 1.2.5 + fs-extra: 7.0.1 + jju: 1.3.0 + z-schema: 3.18.4 + dev: false + resolution: + integrity: sha512-Mae+MFnlcrcEmd6cmNzSv7xG74jWtQRlcOVjIwi10lECwm2LJIaiCol/PrsyARnjroDzf1eDH3quXSY7dTxLaA== + /@microsoft/ts-command-line/4.2.2: + dependencies: + '@types/argparse': 1.0.33 + '@types/node': 8.5.8 + argparse: 1.0.10 + colors: 1.2.5 + dev: false + resolution: + integrity: sha512-CLLVG+zWmUvD6jZD5oq7QCFYj3WOvrBSc3H6KejXCH6q2ntP5/ZHlmKVzQVvN1cEOSWP+jN9ml2AvUcDY/l6Tw== + /@microsoft/tsdoc/0.12.2: + dev: false + resolution: + integrity: sha512-L/srhENhBtbZLUD9FfJ2ZQdnYv3A3MT3UI2EMbC06fHUzIxLjjbkomD6o42UrbsRMwlS9p1BtxExeaCdX73q2Q== /@mrmlnc/readdir-enhanced/2.2.1: dependencies: call-me-maybe: 1.0.1 @@ -566,17 +567,17 @@ packages: node: '>= 6' resolution: integrity: sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== - /@openid/appauth/1.2.0: + /@openid/appauth/1.2.1: dependencies: '@types/base64-js': 1.2.5 '@types/jquery': 3.3.29 base64-js: 1.3.0 - follow-redirects: 1.6.1 + follow-redirects: 1.7.0 form-data: 2.3.3 opener: 1.5.1 dev: false resolution: - integrity: sha512-V48VqMgtwC3SANI1/yp3Lakpsn7Vjp0CauUSGEuQ4QRK+/jYDJ7ajNYwZBXze5UK3i/DYWtiHyzdHfyiH13QJA== + integrity: sha512-Xjwt82lrfi+4iJ9S0SZ2fa68gSQRC0EfK6bPWUosen8AROnYkXg+5cs+ID6wEFwEFPJxgNbKR5sObeRmlRsFhA== /@sheerun/mutationobserver-shim/0.3.2: dev: false resolution: @@ -595,18 +596,26 @@ packages: integrity: sha512-j4ZwhaHmwsCb4DlDOIWnI5YyKDNMoNThsmwEpfHx6a1EpsGZ9qYLxP++LMlmBRjtGptGHFsGItJ768snllFWpA== /@sinonjs/formatio/3.1.0: dependencies: - '@sinonjs/samsam': 3.0.2 + '@sinonjs/samsam': 3.2.0 dev: false resolution: integrity: sha512-ZAR2bPHOl4Xg6eklUGpsdiIJ4+J1SNag1DHHrG/73Uz/nVwXqjgUtRPLoS+aVyieN9cSbc0E4LsU984tWcDyNg== - /@sinonjs/samsam/3.0.2: + /@sinonjs/samsam/3.2.0: dependencies: '@sinonjs/commons': 1.3.0 array-from: 2.1.1 - lodash.get: 4.4.2 + lodash: 4.17.11 + dev: false + resolution: + integrity: sha512-j5F1rScewLtx6pbTK0UAjA3jJj4RYiSKOix53YWv+Jzy/AZ69qHxUpU8fwVLjyKbEEud9QrLpv6Ggs7WqTimYw== + /@sinonjs/text-encoding/0.7.1: + dev: false + resolution: + integrity: sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== + /@types/argparse/1.0.33: dev: false resolution: - integrity: sha512-m08g4CS3J6lwRQk1pj1EO+KEVWbrbXsmi9Pw0ySmrIbcVxVaedoFgLvFsV8wHLwh01EpROVz3KvVcD1Jmks9FQ== + integrity: sha512-VQgHxyPMTj3hIlq9SY1mctqx+Jj8kpQfoLvDlVSDNOyuYs8JYfkuY3OW/4+dO657yPmNhHpePRx0/Tje5ImNVQ== /@types/base64-js/1.2.5: dev: false resolution: @@ -614,13 +623,20 @@ packages: /@types/body-parser/1.17.0: dependencies: '@types/connect': 3.4.32 - '@types/node': 10.10.3 + '@types/node': 10.12.18 dev: false resolution: integrity: sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w== + /@types/bunyan-seq/0.2.1: + dependencies: + '@types/bunyan': 1.8.5 + '@types/node': 10.12.18 + dev: false + resolution: + integrity: sha512-l49TlYOQvubCn0d8gnrasKTIxQ+vTEIFfK5R2K5USf03jlaLlszSG13fj/aF6SzfZlq3JueI081o+pPrm/hxpg== /@types/bunyan/1.8.5: dependencies: - '@types/node': 10.10.3 + '@types/node': 10.12.18 dev: false resolution: integrity: sha512-7n8ANtxh2c5A/NfCuv8cVtWcgSLdq76MQbtmbInpzXuPw4TSAReUJ+MGHK4m67I4zI3ynCJoABfaeHYJaYSeRg== @@ -637,7 +653,7 @@ packages: /@types/chai-jest-snapshot/1.3.4: dependencies: '@types/chai': 4.1.7 - '@types/mocha': 5.2.5 + '@types/mocha': 5.2.6 dev: false resolution: integrity: sha512-l048YOiFs/6L4kZnvSTZ6Pdus024LGlYlF8cqQvU/BYL7TEJZKKnvEkQUHnbrLDMavYSu6bblHNz+ocYbdGBcw== @@ -667,17 +683,21 @@ packages: integrity: sha512-rzOhiQ55WzAiFgXRtitP/ZUT8iVNyllEpylJ5zHzR4vArUvMB39GTk+Zon/uAM0JxEFAWnwsxC2gH8s+tZ3Myg== /@types/connect/3.4.32: dependencies: - '@types/node': 10.10.3 + '@types/node': 10.12.18 dev: false resolution: integrity: sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg== - /@types/cookiejar/2.1.0: + /@types/cookiejar/2.1.1: + dev: false + resolution: + integrity: sha512-aRnpPa7ysx3aNW60hTiCtLHlQaIFsXFCgQlpakNgDNVFzbtusSY8PwjAQgRWfSk0ekNoBjO51eQRB6upA9uuyw== + /@types/core-js/0.9.46: dev: false resolution: - integrity: sha512-EIjmpvnHj+T4nMcKwHwxZKUfDmphIKJc2qnEMhSoOvr1lYEQpuRKRz8orWr//krYIIArS/KGGLfL2YGVUYXmIA== + integrity: sha512-LooLR6XHes9V+kNYRz1Qm8w3atw9QMn7XeZUmIpUelllF9BdryeUKd/u0Wh5ErcjpWfG39NrToU9MF7ngsTFVw== /@types/cpx/1.5.0: dependencies: - '@types/node': 10.10.3 + '@types/node': 10.12.18 dev: false resolution: integrity: sha512-kuGK3lZqEvHTSDbJcaA6tcPoEXV4/e88YrltZMcQUewZhzYQwNSTMGIiPBqeeFd4LCBo1CX5U6CV6LaHG3wXSg== @@ -689,68 +709,72 @@ packages: dev: false resolution: integrity: sha512-mMUu4nWHLBlHtxXY17Fg6+ucS/MnndyOWyOe7MmwkoMYxvfQU2ajtRaEvqSUv+aVkMqH/C0NCI8UoVfRNQ10yg== - /@types/enzyme/3.1.15: + /@types/enzyme/3.9.0: dependencies: '@types/cheerio': 0.22.10 - '@types/react': 16.7.18 + '@types/react': 16.7.22 dev: false resolution: - integrity: sha512-6b4JWgV+FNec1c4+8HauGbXg5gRc1oQK93t2+4W+bHjG/PzO+iPvagY6d6bXAZ+t+ps51Zb2F9LQ4vl0S0Epog== - /@types/events/1.2.0: + integrity: sha512-o0C7ooyBtj9NKyMzn2BWN53W4J21KPhO+/v+qqQX28Pcz0Z1B3DjL9bq2ZR4TN70PVw8O7gkhuFtC7VN3tausg== + /@types/events/3.0.0: dev: false resolution: - integrity: sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA== - /@types/express-serve-static-core/4.16.0: + integrity: sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== + /@types/express-serve-static-core/4.16.1: dependencies: - '@types/events': 1.2.0 - '@types/node': 10.10.3 + '@types/node': 10.12.18 '@types/range-parser': 1.2.3 dev: false resolution: - integrity: sha512-lTeoCu5NxJU4OD9moCgm0ESZzweAx0YqsAcab6OB0EB3+As1OaHtKnaGJvcngQxYsi9UNv0abn4/DRavrRxt4w== - /@types/express-session/1.15.11: + integrity: sha512-QgbIMRU1EVRry5cIu1ORCQP4flSYqLM1lS5LYyGWfKnFT3E58f0gKto7BR13clBFVrVZ0G0rbLZ1hUpSkgQQOA== + /@types/express-session/1.15.12: dependencies: - '@types/events': 1.2.0 - '@types/express': 4.16.0 - '@types/node': 10.10.3 + '@types/express': 4.16.1 + '@types/node': 10.12.18 dev: false resolution: - integrity: sha512-BRgOXYBhmt71CuOR/PQvKBRgcS567xJmE7dT3r/FvTmWc1ZsR5joVH32dgH+z6F4FugniF04tMyXc4UF9M8Hfg== - /@types/express/4.16.0: + integrity: sha512-DHZXzWy6Nu5Ng0syXUiVFRpZ6/1DOXoTCWa6RG3itGrub2ioBYvgtDbkT6VHHNo3iOdHRROyWANsMBJVaflblQ== + /@types/express/4.16.1: dependencies: '@types/body-parser': 1.17.0 - '@types/express-serve-static-core': 4.16.0 + '@types/express-serve-static-core': 4.16.1 '@types/serve-static': 1.13.2 dev: false resolution: - integrity: sha512-TtPEYumsmSTtTetAPXlJVf3kEqb6wZK0bZojpJQrnD/djV4q1oB6QQ8aKvKqwNPACoe02GNiy5zDzcYivR5Z2w== - /@types/faker/4.1.4: + integrity: sha512-V0clmJow23WeyblmACoxbHBu2JKlE5TiIme6Lem14FnPW9gsttyHtk6wq7njcdIWH1njAaFgR8gW09lgY98gQg== + /@types/faker/4.1.5: dev: false resolution: - integrity: sha512-6vQk2Ky+s3oehh//1bB/4I4ApYcRN2dLcHm0fuUOcXvnv9fXCfSKkI6K/dRicJkyjcAO23r4lAwbk9j9fOB0Ug== + integrity: sha512-YSDqoBEWYGdNk53xSkkb6REaUaVSlIjxIAGjj/nbLzlZOit7kUU+nA2zC2qQkIVO4MQ+3zl4Sz7aw+kbpHHHUQ== /@types/form-data/2.2.1: dependencies: - '@types/node': 10.10.3 + '@types/node': 10.12.18 dev: false resolution: integrity: sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ== /@types/fs-extra/4.0.8: dependencies: - '@types/node': 10.10.3 + '@types/node': 10.12.18 dev: false resolution: integrity: sha512-Z5nu9Pbxj9yNeXIK3UwGlRdJth4cZ5sCq05nI7FaI6B0oz28nxkOtp6Lsz0ZnmLHJGvOJfB/VHxSTbVq/i6ujA== /@types/fs-extra/5.0.1: dependencies: - '@types/node': 10.10.3 + '@types/node': 10.12.18 dev: false resolution: integrity: sha512-h3wnflb+jMTipvbbZnClgA2BexrT4w0GcfoCz5qyxd0IRsbqhLSyesM6mqZTAnhbVmhyTm5tuxfRu9R+8l+lGw== + /@types/fs-extra/5.0.4: + dependencies: + '@types/node': 10.12.18 + dev: false + resolution: + integrity: sha512-DsknoBvD8s+RFfSGjmERJ7ZOP1HI0UZRA3FSI+Zakhrc/Gy26YQsLI+m5V5DHxroHRJqCDLKJp7Hixn8zyaF7g== /@types/glob/5.0.36: dependencies: - '@types/events': 1.2.0 + '@types/events': 3.0.0 '@types/minimatch': 3.0.3 - '@types/node': 10.10.3 + '@types/node': 10.12.18 dev: false resolution: integrity: sha512-KEzSKuP2+3oOjYYjujue6Z3Yqis5HKA1BsIC+jZ1v3lrRNdsqyNNtX0rQf6LSuI4DJJ2z5UV//zBZCcvM0xikg== @@ -762,28 +786,18 @@ packages: dev: false resolution: integrity: sha512-y5x0XD/WXDaGSyiTaTcKS4FurULJtSiYbGTeQd0m2LYZGBcZZ/7fM6t5H/DzeUF+kv8y6UfmF6yJABQsHcp9VQ== - /@types/history/4.7.2: - dev: false - resolution: - integrity: sha512-ui3WwXmjTaY73fOQ3/m3nnajU/Orhi6cEu5rzX+BrAAJxa3eITXZ5ch9suPqtM03OWhAHhPSyBGCN4UKoxO20Q== - /@types/i18next-browser-languagedetector/2.0.1: + /@types/highlight.js/9.12.3: dev: false resolution: - integrity: sha512-9TwP0tw0PSXUtBtM12PFUjBsl9sP5bNHd54qdSXBrrZ4HJmtVdqbKvmXLH4Qd3KQzIS2g/Z7HhLhygL2LUSb8w== - /@types/i18next-node-fs-backend/0.0.30: - dependencies: - '@types/i18next': 2.3.41 - dev: false - resolution: - integrity: sha512-IFiNtEGzQ/Q3t6VcgubDoLCDl2oAuC78VMrwvkLCVZIUq7bqDOUtAIKfvRJM48g+hHj2g6uT7CykVUIew3T2Lw== - /@types/i18next-xhr-backend/1.4.1: + integrity: sha512-pGF/zvYOACZ/gLGWdQH8zSwteQS1epp68yRcVLJMgUck/MjEn/FBYmPub9pXT8C1e4a8YZfHo1CKyV8q1vKUnQ== + /@types/history/4.7.2: dev: false resolution: - integrity: sha512-k8YOdiXidKc3Dc9LRQoCceAwPoj34U+PXrUdrbxjz/RttN9SyYeU0YJWmrp1mhp23/TF9q5mJl25G3a4xV/YzA== - /@types/i18next/2.3.41: + integrity: sha512-ui3WwXmjTaY73fOQ3/m3nnajU/Orhi6cEu5rzX+BrAAJxa3eITXZ5ch9suPqtM03OWhAHhPSyBGCN4UKoxO20Q== + /@types/i18next-browser-languagedetector/2.0.2: dev: false resolution: - integrity: sha512-8BLa2+5itmjj3eJI8zbE58Z+I5UEYP7YKHlEn6E4ZJsr8DkpFePtIvt8s0FtBHKPHA1K+HP0LkEgmQRAb9Kbqw== + integrity: sha512-5KhnWbUIfncsFQvZO2/k0P4qqvWpI8Isd3wa/56zVqORl9db3J+aPFgoeO0FN8oKozaT+s0U9RoQnSlqI5YRhw== /@types/i18next/8.4.6: dev: false resolution: @@ -798,14 +812,14 @@ packages: dev: false resolution: integrity: sha512-4RKbhIDGC87s4EBy2Cp2/5S2O6kmCRcZnD5KRCq1q9z2GhBte1+BdsfVKCpG8yKpDGNyEE2G6IqFIh6W2YwWPA== - /@types/jsdom/12.2.1: + /@types/jsdom/12.2.2: dependencies: - '@types/node': 10.10.3 - '@types/tough-cookie': 2.3.4 + '@types/node': 10.12.18 + '@types/tough-cookie': 2.3.5 parse5: 4.0.0 dev: false resolution: - integrity: sha512-VnLP1qW70OkzpMVuFsJPhxeIzEW1y+t91Fa2rE+b3UZ3ZiTwB28pYrdNj58wa0AQ+dV7eIBcdMFl3ql9C+cc9g== + integrity: sha512-+k4AsH9TXsuvucs6EiMxxPPWiXOGyM48P+jL+IPDYhtJ+L7Cl5xbN3Vig6z8tzvo9Lv7mjLGv54j3D/KExQl7Q== /@types/json5/0.0.29: dev: false resolution: @@ -818,10 +832,10 @@ packages: dev: false resolution: integrity: sha512-ufQcVg4daO8xQ5kopxRHanqFdL4AI7ondQkV+2f+7mz3gvp0LkBx2zBRC6hfs3T87mzQFmf5Fck7Fi145Ul6NQ== - /@types/lodash/4.14.119: + /@types/lodash/4.14.121: dev: false resolution: - integrity: sha512-Z3TNyBL8Vd/M9D9Ms2S3LmFq2sSMzahodD6rCS9V2N44HUMINb75jNkSuwAx7eo2ufqTdfOdtGQpNbieUjPQmw== + integrity: sha512-ORj7IBWj13iYufXt/VXrCNMbUuCTJfhzme5kx9U/UtcIPdJYuvPDUAlHlbNhz/8lKCLy9XGIZnGrqXOtQbPGoQ== /@types/lolex/2.1.3: dev: false resolution: @@ -830,10 +844,10 @@ packages: dev: false resolution: integrity: sha512-CSf9YWJdX1DkTNu9zcNtdCcn6hkRtB5ILjbhRId4ZOQqx30fXmdecuaXhugQL6eyrhuXtaHJ7PHI+Vm7k9ZJjg== - /@types/mime/2.0.0: + /@types/mime/2.0.1: dev: false resolution: - integrity: sha512-A2TAGbTFdBw9azHbpVd+/FkdW2T6msN1uct1O9bH3vTerEHKZhTXJUQXy+hNq1B0RagfU8U+KBdqiZpxjhOUQA== + integrity: sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw== /@types/minimatch/3.0.3: dev: false resolution: @@ -842,40 +856,48 @@ packages: dev: false resolution: integrity: sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= - /@types/mocha/5.2.5: + /@types/mkdirp/0.3.29: + dev: false + resolution: + integrity: sha1-fyrX7FX5FEgvybHsS7GuYCjUYGY= + /@types/mocha/5.2.6: dev: false resolution: - integrity: sha512-lAVp+Kj54ui/vLUFxsJTMtWvZraZxum3w3Nwkble2dNuV5VnPA+Mi2oGX9XYJAaIvZi3tn3cbjS/qcJXRb6Bww== + integrity: sha512-1axi39YdtBI7z957vdqXI4Ac25e7YihYQtJa+Clnxg1zTJEaIRbndt71O3sP4GAMgiAm0pY26/b9BrY4MR/PMw== /@types/multiparty/0.0.31: dependencies: - '@types/node': 10.10.3 + '@types/node': 10.12.18 dev: false resolution: integrity: sha1-MB/S2wWl1PNAhhPrW5ZSzWELeeI= - /@types/nock/9.3.0: + /@types/nock/9.3.1: dependencies: - '@types/node': 10.10.3 - dev: false - resolution: - integrity: sha512-ZHf/X8rTQ5Tb1rHjxIJYqm55uO265agE3G7NoSXVa2ep+EcJXgB2fsme+zBvK7MhrxTwkC/xkB6THyv50u0MGw== - /@types/node/10.10.3: + '@types/node': 10.12.18 dev: false resolution: - integrity: sha512-dWk7F3b0m6uDLHero7tsnpAi9r2RGPQHGbb0/VCv7DPJRMFtk3RonY1/29vfJKnheRMBa7+uF+vunlg/mBGlxg== + integrity: sha512-eOVHXS5RnWOjTVhu3deCM/ruy9E6JCgeix2g7wpFiekQh3AaEAK1cz43tZDukKmtSmQnwvSySq7ubijCA32I7Q== /@types/node/10.12.18: dev: false resolution: integrity: sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== + /@types/node/8.5.8: + dev: false + resolution: + integrity: sha512-8KmlRxwbKZfjUHFIt3q8TF5S2B+/E5BaAoo/3mgc5h6FJzqxXkCK/VMetO+IRDtwtU6HUvovHMBn+XRj7SV9Qg== + /@types/node/9.6.42: + dev: false + resolution: + integrity: sha512-SpeVQJFekfnEaZZO1yl4je/36upII36L7gOT4DBx51B1GeAB45mmDb3a5OBQB+ZeFxVVOP37r8Owsl940G/fBg== /@types/passport/0.4.7: dependencies: - '@types/express': 4.16.0 + '@types/express': 4.16.1 dev: false resolution: integrity: sha512-EePlxNYx5tf3n0yjdPXX0/zDOv0UCwjMyQo4UkWGlhHteNDItAj7TfDdLttSThVMKQz3uCW7lsGzMuml0f8g9Q== - /@types/prop-types/15.5.8: + /@types/prop-types/15.7.0: dev: false resolution: - integrity: sha512-3AQoUxQcQtLHsK25wtTWIoIpgYjH3vSDroZOUr7PpCHw/jLY1RB9z9E8dBT/OSmwStVgkRNvdh+ZHNiomRieaw== + integrity: sha512-eItQyV43bj4rR3JPV0Skpl1SncRCdziTEK9/v8VwXmV6d/qOUO8/EuWeHBbCZcsfSHfzI5UyMJLCSXtxxznyZg== /@types/qs/6.5.1: dev: false resolution: @@ -884,67 +906,66 @@ packages: dev: false resolution: integrity: sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== - /@types/react-data-grid/4.0.1: + /@types/react-data-grid/4.0.2: dependencies: - '@types/react': 16.7.18 + '@types/react': 16.7.22 dev: false resolution: - integrity: sha512-7ZmendIWApoRS1LGgBFYXitDiU+AZWrBnA1jJDhn7jUW1W9xs2J4b6KxiKVub3QDt0IdAV0s64DH1YrgTEm70A== - /@types/react-dom/16.0.7: + integrity: sha512-no7HnLfm5CSLicuLixZjgsfq0myt6+aBxyVCIo9XiJdNLiZUC0uogMa2f4wq+xdTaslYHeyzwsVL6KF0B765Yg== + /@types/react-dom/16.0.11: dependencies: - '@types/node': 10.10.3 - '@types/react': 16.7.18 + '@types/react': 16.7.22 dev: false resolution: - integrity: sha512-vaq4vMaJOaNgFff1t3LnHYr6vRa09vRspMkmLdXtFZmO1fwDI2snP+dpOkwrtlU8UC8qsqemCu4RmVM2OLq/fA== + integrity: sha512-x6zUx9/42B5Kl2Vl9HlopV8JF64wLpX3c+Pst9kc1HgzrsH+mkehe/zmHMQTplIrR48H2gpU7ZqurQolYu8XBA== /@types/react-highlight-words/0.11.1: dependencies: - '@types/react': 16.7.18 + '@types/react': 16.7.22 dev: false resolution: integrity: sha512-zBrtCb0qhn6XqmkIPZhDzLjZ41k4ppeGoC5iaZotAwBEThGO0BrcpRTLWX69oWplnuBsbiNFvHj0IcT6gl9mdg== - /@types/react-redux/5.0.21: + /@types/react-redux/7.0.1: dependencies: - '@types/react': 16.7.18 - redux: 3.7.2 + '@types/react': 16.7.22 + redux: 4.0.1 dev: false resolution: - integrity: sha512-ewkOW4GjnyXq5L++T31utI8yRmwj8iCIahZohYi1Ef7Xkrw0V/q92ao7x20rm38FKgImDaCCsaRGWfCJmF/Ukg== - /@types/react-resize-detector/3.1.0: + integrity: sha512-+DIH7TI2MT4Ke4lOrRMgNy//DzTDIzv5QwkJSD6AVrlsIgzf7yMM0JoWL5wJUXYwKQ2f1FgvwlvIVGD2QWQnew== + /@types/react-resize-detector/3.1.1: dependencies: - '@types/react': 16.7.18 + '@types/react': 16.7.22 dev: false resolution: - integrity: sha512-q/kuav2WHLqCOypvNMWR7S3UKSphE0urlvgkiaKpnGXOPsy6/3BCrr+HzcoaMOvuZW7bFngbheS2gITRl4B1xQ== + integrity: sha512-3FVExywb3kC0p+dKRd2IC6wmUlfurx/9J0b1mtHd0rigwZWNJC79fnbTJaMmLNM8PhhgqDwaNOhFdv0u9YGAzA== /@types/react-router-dom/4.3.1: dependencies: '@types/history': 4.7.2 - '@types/react': 16.7.18 - '@types/react-router': 4.4.3 + '@types/react': 16.7.22 + '@types/react-router': 4.4.4 dev: false resolution: integrity: sha512-GbztJAScOmQ/7RsQfO4cd55RuH1W4g6V1gDW3j4riLlt+8yxYLqqsiMzmyuXBLzdFmDtX/uU2Bpcm0cmudv44A== - /@types/react-router/4.4.3: + /@types/react-router/4.4.4: dependencies: '@types/history': 4.7.2 - '@types/react': 16.7.18 + '@types/react': 16.7.22 dev: false resolution: - integrity: sha512-8GmjakEBFNCLJbpg9jtDp1EDvFP0VkIPPKBpVwmB3Q+9whFoHu8rluMUXUE5SoGkEQvVOtgJzWmUsJojNpFMQQ== + integrity: sha512-TZVfpT6nvUv/lbho/nRtckEtgkhspOQr3qxrnpXixwgQRKKyg5PvDfNKc8Uend/p/Pi70614VCmC0NPAKWF+0g== /@types/react-virtualized/9.18.12: dependencies: - '@types/prop-types': 15.5.8 - '@types/react': 16.7.18 + '@types/prop-types': 15.7.0 + '@types/react': 16.7.22 dev: false resolution: integrity: sha512-Msdpt9zvYlb5Ul4PA339QUkJ0/z2O+gaFxed1rG+2rZjbe6XdYo7jWfJe206KBnjj84DwPPIbPFQCtoGuNwNTQ== - /@types/react/16.7.18: + /@types/react/16.7.22: dependencies: - '@types/prop-types': 15.5.8 - csstype: 2.6.0 + '@types/prop-types': 15.7.0 + csstype: 2.6.2 dev: false resolution: - integrity: sha512-Tx4uu3ppK53/iHk6VpamMP3f3ahfDLEVt3ZQc8TFm30a1H3v9lMsCntBREswZIW/SKrvJjkb3Hq8UwO6GREBng== + integrity: sha512-j/3tVoY09kHcTfbia4l67ofQn9xvktUvlC/4QN0KuBHAXlbU/wuGKMb8WfEb/vIcWxsOxHv559uYprkFDFfP8Q== /@types/request-promise-native/1.0.15: dependencies: '@types/request': 2.48.1 @@ -955,15 +976,19 @@ packages: dependencies: '@types/caseless': 0.12.1 '@types/form-data': 2.2.1 - '@types/node': 10.10.3 - '@types/tough-cookie': 2.3.4 + '@types/node': 10.12.18 + '@types/tough-cookie': 2.3.5 dev: false resolution: integrity: sha512-ZgEZ1TiD+KGA9LiAAPPJL68Id2UWfeSO62ijSXZjFJArVV+2pKcsVHmrcu+1oiE3q6eDGiFiSolRc4JHoerBBg== + /@types/rimraf/0.0.28: + dev: false + resolution: + integrity: sha1-VWJRm8eWPKyoq/fxKMrjtZTUHQY= /@types/rimraf/2.0.2: dependencies: '@types/glob': 5.0.36 - '@types/node': 10.10.3 + '@types/node': 10.12.18 dev: false resolution: integrity: sha512-Hm/bnWq0TCy7jmjeN5bKYij9vw5GrDFWME4IuxV08278NtU/VdGbzsBohcCUJ7+QMqmUq5hpRKB39HeQWJjztQ== @@ -973,15 +998,15 @@ packages: integrity: sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ== /@types/serve-static/1.13.2: dependencies: - '@types/express-serve-static-core': 4.16.0 - '@types/mime': 2.0.0 + '@types/express-serve-static-core': 4.16.1 + '@types/mime': 2.0.1 dev: false resolution: integrity: sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q== /@types/shelljs/0.7.8: dependencies: '@types/glob': 5.0.36 - '@types/node': 10.10.3 + '@types/node': 10.12.18 dev: false resolution: integrity: sha512-M2giRw93PxKS7YjU6GZjtdV9HASdB7TWqizBXe4Ju7AqbKlWvTr0gNO92XH56D/gMxqD/jNHLNfC5hA34yGqrQ== @@ -1000,19 +1025,19 @@ packages: dev: false resolution: integrity: sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg== - /@types/source-map-support/0.4.1: + /@types/source-map-support/0.4.2: dependencies: - '@types/node': 10.10.3 + '@types/node': 10.12.18 dev: false resolution: - integrity: sha512-eoyZxYGwaeHq5zCVeoNgY1dQy6dVdm1b7K9k1FRnWkf997Tji3NLBoLAjK5WCobeh1Qs6Q5KUV1rZCmHvzaDBw== - /@types/superagent/3.8.6: + integrity: sha512-GbGWx39O8NdUHSChdrU0XeigBAgu1Teg3llwE0slSVcH2qISaQT70ftAiH+h4HIt3VIObFU34PSpXIKJuXCybQ== + /@types/superagent/3.8.7: dependencies: - '@types/cookiejar': 2.1.0 - '@types/node': 10.10.3 + '@types/cookiejar': 2.1.1 + '@types/node': 10.12.18 dev: false resolution: - integrity: sha512-YQjdsk27MLb6uyXjjywGyYeuqavwV3CirHt6btBz00HkKJyowdB8gjjB1zIZxrOybDRqO8FLjTZeEtmtC2hqxA== + integrity: sha512-9KhCkyXv268A2nZ1Wvu7rQWM+BmdYUVkycFeNnYrUL5Zwu7o8wPQ3wBfW59dDP+wuoxw0ww8YKgTNv8j/cgscA== /@types/tapable/0.2.5: dev: false resolution: @@ -1021,10 +1046,10 @@ packages: dev: false resolution: integrity: sha512-42zEJkBpNfMEAvWR5WlwtTH22oDzcMjFsL9gDGExwF8X8WvAiw7Vwop7hPw03QT8TKfec83LwbHj6SvpqM4ELQ== - /@types/tough-cookie/2.3.4: + /@types/tough-cookie/2.3.5: dev: false resolution: - integrity: sha512-Set5ZdrAaKI/qHdFlVMgm/GsAv/wkXhSTuZFkJ+JI7HK+wIkIlOaUXSXieIvJ0+OvGIqtREFoE+NHJtEq0gtEw== + integrity: sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg== /@types/uglify-js/3.0.4: dependencies: source-map: 0.6.1 @@ -1035,15 +1060,15 @@ packages: dev: false resolution: integrity: sha512-AaM3cTaHIbIJecuqf15ystbsUL3rtiJHZHhv6hTu1hBeKbvGwUNdHXfqNiY2LMWIT8ip6SqA+M9wYWIsQiMH2g== - /@types/webdriverio/4.13.1: + /@types/webdriverio/4.13.3: dependencies: - '@types/node': 10.10.3 + '@types/node': 10.12.18 dev: false resolution: - integrity: sha512-ZrH2jokLZp8qE1wPN1dRMfgiag6JQqZEoMJ7217tj8bTqR5J+ziLVfTnG7wh9yRPlrS0mXlrOQ5qa7ZaJwpA6Q== + integrity: sha512-AfSQM1xTO9Ax+u9uSQPDuw69DQ0qA2RMoKHn86jCgWNcwKVUjGMSP4sfSl3JOfcZN8X/gWvn7znVPp2/g9zcJA== /@types/webpack/3.8.17: dependencies: - '@types/node': 10.10.3 + '@types/node': 10.12.18 '@types/tapable': 0.2.5 '@types/uglify-js': 3.0.4 source-map: 0.6.1 @@ -1052,8 +1077,8 @@ packages: integrity: sha512-tPw9kaDqwIufHhl6tctCCKCzyeZwXqheeOJ0DRJP5AvQIA7CxeWq4xdSUdcOYHTYOBDoVHYEsXqoo4/vxIVHWQ== /@types/ws/6.0.1: dependencies: - '@types/events': 1.2.0 - '@types/node': 10.10.3 + '@types/events': 3.0.0 + '@types/node': 10.12.18 dev: false resolution: integrity: sha512-EzH8k1gyZ4xih/MaZTXwT2xOkPiIMSrhQ9b8wrlX88L0T02eYsddatQlwVFlEPyEqV0ChpdpNnE51QPH6NVT4Q== @@ -1061,143 +1086,150 @@ packages: dev: false resolution: integrity: sha1-xEKLDKhtO4gUdXJv2UmAs4onw4E= - /@types/yargs/12.0.5: + /@types/yargs/12.0.9: + dev: false + resolution: + integrity: sha512-sCZy4SxP9rN2w30Hlmg5dtdRwgYQfYRiLo9usw8X9cxlf+H4FqM1xX7+sNH7NNKVdbXMJWqva7iyy+fxh/V7fA== + /@types/z-schema/3.16.31: dev: false resolution: - integrity: sha512-y51XRWWg1GiW4+mV2zmFQUqJT3bX7nuUaXW4j8a2dFvT9ZlaNLjb7ZxcUSDyklt/UBWrW2+31x+hQpRzxaTFzw== - /@webassemblyjs/ast/1.7.11: + integrity: sha1-LrHQCl5Ow/pYx2r94S4YK2bcXBw= + /@webassemblyjs/ast/1.8.5: dependencies: - '@webassemblyjs/helper-module-context': 1.7.11 - '@webassemblyjs/helper-wasm-bytecode': 1.7.11 - '@webassemblyjs/wast-parser': 1.7.11 + '@webassemblyjs/helper-module-context': 1.8.5 + '@webassemblyjs/helper-wasm-bytecode': 1.8.5 + '@webassemblyjs/wast-parser': 1.8.5 dev: false resolution: - integrity: sha512-ZEzy4vjvTzScC+SH8RBssQUawpaInUdMTYwYYLh54/s8TuT0gBLuyUnppKsVyZEi876VmmStKsUs28UxPgdvrA== - /@webassemblyjs/floating-point-hex-parser/1.7.11: + integrity: sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ== + /@webassemblyjs/floating-point-hex-parser/1.8.5: dev: false resolution: - integrity: sha512-zY8dSNyYcgzNRNT666/zOoAyImshm3ycKdoLsyDw/Bwo6+/uktb7p4xyApuef1dwEBo/U/SYQzbGBvV+nru2Xg== - /@webassemblyjs/helper-api-error/1.7.11: + integrity: sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ== + /@webassemblyjs/helper-api-error/1.8.5: dev: false resolution: - integrity: sha512-7r1qXLmiglC+wPNkGuXCvkmalyEstKVwcueZRP2GNC2PAvxbLYwLLPr14rcdJaE4UtHxQKfFkuDFuv91ipqvXg== - /@webassemblyjs/helper-buffer/1.7.11: + integrity: sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA== + /@webassemblyjs/helper-buffer/1.8.5: dev: false resolution: - integrity: sha512-MynuervdylPPh3ix+mKZloTcL06P8tenNH3sx6s0qE8SLR6DdwnfgA7Hc9NSYeob2jrW5Vql6GVlsQzKQCa13w== - /@webassemblyjs/helper-code-frame/1.7.11: + integrity: sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q== + /@webassemblyjs/helper-code-frame/1.8.5: dependencies: - '@webassemblyjs/wast-printer': 1.7.11 + '@webassemblyjs/wast-printer': 1.8.5 dev: false resolution: - integrity: sha512-T8ESC9KMXFTXA5urJcyor5cn6qWeZ4/zLPyWeEXZ03hj/x9weSokGNkVCdnhSabKGYWxElSdgJ+sFa9G/RdHNw== - /@webassemblyjs/helper-fsm/1.7.11: + integrity: sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ== + /@webassemblyjs/helper-fsm/1.8.5: dev: false resolution: - integrity: sha512-nsAQWNP1+8Z6tkzdYlXT0kxfa2Z1tRTARd8wYnc/e3Zv3VydVVnaeePgqUzFrpkGUyhUUxOl5ML7f1NuT+gC0A== - /@webassemblyjs/helper-module-context/1.7.11: + integrity: sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow== + /@webassemblyjs/helper-module-context/1.8.5: + dependencies: + '@webassemblyjs/ast': 1.8.5 + mamacro: 0.0.3 dev: false resolution: - integrity: sha512-JxfD5DX8Ygq4PvXDucq0M+sbUFA7BJAv/GGl9ITovqE+idGX+J3QSzJYz+LwQmL7fC3Rs+utvWoJxDb6pmC0qg== - /@webassemblyjs/helper-wasm-bytecode/1.7.11: + integrity: sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g== + /@webassemblyjs/helper-wasm-bytecode/1.8.5: dev: false resolution: - integrity: sha512-cMXeVS9rhoXsI9LLL4tJxBgVD/KMOKXuFqYb5oCJ/opScWpkCMEz9EJtkonaNcnLv2R3K5jIeS4TRj/drde1JQ== - /@webassemblyjs/helper-wasm-section/1.7.11: + integrity: sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ== + /@webassemblyjs/helper-wasm-section/1.8.5: dependencies: - '@webassemblyjs/ast': 1.7.11 - '@webassemblyjs/helper-buffer': 1.7.11 - '@webassemblyjs/helper-wasm-bytecode': 1.7.11 - '@webassemblyjs/wasm-gen': 1.7.11 + '@webassemblyjs/ast': 1.8.5 + '@webassemblyjs/helper-buffer': 1.8.5 + '@webassemblyjs/helper-wasm-bytecode': 1.8.5 + '@webassemblyjs/wasm-gen': 1.8.5 dev: false resolution: - integrity: sha512-8ZRY5iZbZdtNFE5UFunB8mmBEAbSI3guwbrsCl4fWdfRiAcvqQpeqd5KHhSWLL5wuxo53zcaGZDBU64qgn4I4Q== - /@webassemblyjs/ieee754/1.7.11: + integrity: sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA== + /@webassemblyjs/ieee754/1.8.5: dependencies: '@xtuc/ieee754': 1.2.0 dev: false resolution: - integrity: sha512-Mmqx/cS68K1tSrvRLtaV/Lp3NZWzXtOHUW2IvDvl2sihAwJh4ACE0eL6A8FvMyDG9abes3saB6dMimLOs+HMoQ== - /@webassemblyjs/leb128/1.7.11: + integrity: sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g== + /@webassemblyjs/leb128/1.8.5: dependencies: - '@xtuc/long': 4.2.1 + '@xtuc/long': 4.2.2 dev: false resolution: - integrity: sha512-vuGmgZjjp3zjcerQg+JA+tGOncOnJLWVkt8Aze5eWQLwTQGNgVLcyOTqgSCxWTR4J42ijHbBxnuRaL1Rv7XMdw== - /@webassemblyjs/utf8/1.7.11: + integrity: sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A== + /@webassemblyjs/utf8/1.8.5: dev: false resolution: - integrity: sha512-C6GFkc7aErQIAH+BMrIdVSmW+6HSe20wg57HEC1uqJP8E/xpMjXqQUxkQw07MhNDSDcGpxI9G5JSNOQCqJk4sA== - /@webassemblyjs/wasm-edit/1.7.11: + integrity: sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw== + /@webassemblyjs/wasm-edit/1.8.5: dependencies: - '@webassemblyjs/ast': 1.7.11 - '@webassemblyjs/helper-buffer': 1.7.11 - '@webassemblyjs/helper-wasm-bytecode': 1.7.11 - '@webassemblyjs/helper-wasm-section': 1.7.11 - '@webassemblyjs/wasm-gen': 1.7.11 - '@webassemblyjs/wasm-opt': 1.7.11 - '@webassemblyjs/wasm-parser': 1.7.11 - '@webassemblyjs/wast-printer': 1.7.11 + '@webassemblyjs/ast': 1.8.5 + '@webassemblyjs/helper-buffer': 1.8.5 + '@webassemblyjs/helper-wasm-bytecode': 1.8.5 + '@webassemblyjs/helper-wasm-section': 1.8.5 + '@webassemblyjs/wasm-gen': 1.8.5 + '@webassemblyjs/wasm-opt': 1.8.5 + '@webassemblyjs/wasm-parser': 1.8.5 + '@webassemblyjs/wast-printer': 1.8.5 dev: false resolution: - integrity: sha512-FUd97guNGsCZQgeTPKdgxJhBXkUbMTY6hFPf2Y4OedXd48H97J+sOY2Ltaq6WGVpIH8o/TGOVNiVz/SbpEMJGg== - /@webassemblyjs/wasm-gen/1.7.11: + integrity: sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q== + /@webassemblyjs/wasm-gen/1.8.5: dependencies: - '@webassemblyjs/ast': 1.7.11 - '@webassemblyjs/helper-wasm-bytecode': 1.7.11 - '@webassemblyjs/ieee754': 1.7.11 - '@webassemblyjs/leb128': 1.7.11 - '@webassemblyjs/utf8': 1.7.11 + '@webassemblyjs/ast': 1.8.5 + '@webassemblyjs/helper-wasm-bytecode': 1.8.5 + '@webassemblyjs/ieee754': 1.8.5 + '@webassemblyjs/leb128': 1.8.5 + '@webassemblyjs/utf8': 1.8.5 dev: false resolution: - integrity: sha512-U/KDYp7fgAZX5KPfq4NOupK/BmhDc5Kjy2GIqstMhvvdJRcER/kUsMThpWeRP8BMn4LXaKhSTggIJPOeYHwISA== - /@webassemblyjs/wasm-opt/1.7.11: + integrity: sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg== + /@webassemblyjs/wasm-opt/1.8.5: dependencies: - '@webassemblyjs/ast': 1.7.11 - '@webassemblyjs/helper-buffer': 1.7.11 - '@webassemblyjs/wasm-gen': 1.7.11 - '@webassemblyjs/wasm-parser': 1.7.11 + '@webassemblyjs/ast': 1.8.5 + '@webassemblyjs/helper-buffer': 1.8.5 + '@webassemblyjs/wasm-gen': 1.8.5 + '@webassemblyjs/wasm-parser': 1.8.5 dev: false resolution: - integrity: sha512-XynkOwQyiRidh0GLua7SkeHvAPXQV/RxsUeERILmAInZegApOUAIJfRuPYe2F7RcjOC9tW3Cb9juPvAC/sCqvg== - /@webassemblyjs/wasm-parser/1.7.11: + integrity: sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q== + /@webassemblyjs/wasm-parser/1.8.5: dependencies: - '@webassemblyjs/ast': 1.7.11 - '@webassemblyjs/helper-api-error': 1.7.11 - '@webassemblyjs/helper-wasm-bytecode': 1.7.11 - '@webassemblyjs/ieee754': 1.7.11 - '@webassemblyjs/leb128': 1.7.11 - '@webassemblyjs/utf8': 1.7.11 + '@webassemblyjs/ast': 1.8.5 + '@webassemblyjs/helper-api-error': 1.8.5 + '@webassemblyjs/helper-wasm-bytecode': 1.8.5 + '@webassemblyjs/ieee754': 1.8.5 + '@webassemblyjs/leb128': 1.8.5 + '@webassemblyjs/utf8': 1.8.5 dev: false resolution: - integrity: sha512-6lmXRTrrZjYD8Ng8xRyvyXQJYUQKYSXhJqXOBLw24rdiXsHAOlvw5PhesjdcaMadU/pyPQOJ5dHreMjBxwnQKg== - /@webassemblyjs/wast-parser/1.7.11: + integrity: sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw== + /@webassemblyjs/wast-parser/1.8.5: dependencies: - '@webassemblyjs/ast': 1.7.11 - '@webassemblyjs/floating-point-hex-parser': 1.7.11 - '@webassemblyjs/helper-api-error': 1.7.11 - '@webassemblyjs/helper-code-frame': 1.7.11 - '@webassemblyjs/helper-fsm': 1.7.11 - '@xtuc/long': 4.2.1 + '@webassemblyjs/ast': 1.8.5 + '@webassemblyjs/floating-point-hex-parser': 1.8.5 + '@webassemblyjs/helper-api-error': 1.8.5 + '@webassemblyjs/helper-code-frame': 1.8.5 + '@webassemblyjs/helper-fsm': 1.8.5 + '@xtuc/long': 4.2.2 dev: false resolution: - integrity: sha512-lEyVCg2np15tS+dm7+JJTNhNWq9yTZvi3qEhAIIOaofcYlUp0UR5/tVqOwa/gXYr3gjwSZqw+/lS9dscyLelbQ== - /@webassemblyjs/wast-printer/1.7.11: + integrity: sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg== + /@webassemblyjs/wast-printer/1.8.5: dependencies: - '@webassemblyjs/ast': 1.7.11 - '@webassemblyjs/wast-parser': 1.7.11 - '@xtuc/long': 4.2.1 + '@webassemblyjs/ast': 1.8.5 + '@webassemblyjs/wast-parser': 1.8.5 + '@xtuc/long': 4.2.2 dev: false resolution: - integrity: sha512-m5vkAsuJ32QpkdkDOUPGSltrg8Cuk3KBx4YrmAGQwCZPRdUHXxG4phIOuuycLemHFr74sWL9Wthqss4fzdzSwg== + integrity: sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg== /@xtuc/ieee754/1.2.0: dev: false resolution: integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== - /@xtuc/long/4.2.1: + /@xtuc/long/4.2.2: dev: false resolution: - integrity: sha512-FZdkNBDqBRHKQ2MEbSC17xnPFOhZxeJ2YGSfr2BKf3sujG49Qe3bB+rGCwQfIaA7WHnGeGkSijX4FuBCdrzW/g== + integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== /JSONSelect/0.4.0: dev: false engines: @@ -1214,22 +1246,25 @@ packages: integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== /accepts/1.3.5: dependencies: - mime-types: 2.1.21 + mime-types: 2.1.22 negotiator: 0.6.1 dev: false engines: node: '>= 0.6' resolution: integrity: sha1-63d99gEXI6OxTopywIBcjoZ0a9I= - /acorn-dynamic-import/3.0.0: + /acorn-dynamic-import/4.0.0/acorn@6.1.1: dependencies: - acorn: 5.7.3 + acorn: 6.1.1 dev: false + id: registry.npmjs.org/acorn-dynamic-import/4.0.0 + peerDependencies: + acorn: ^6.0.0 resolution: - integrity: sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg== + integrity: sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw== /acorn-globals/4.3.0: dependencies: - acorn: 6.0.5 + acorn: 6.1.1 acorn-walk: 6.1.1 dev: false resolution: @@ -1247,13 +1282,13 @@ packages: hasBin: true resolution: integrity: sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== - /acorn/6.0.5: + /acorn/6.1.1: dev: false engines: node: '>=0.4.0' hasBin: true resolution: - integrity: sha512-i33Zgp3XWtmZBMNvCr4azvOFeWVw1Rk6p3hfi3LUDvIFraOMywb1kAtrbi+med14m4Xfpqm3zRZMT+c0FNE7kg== + integrity: sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA== /address/1.0.3: dev: false engines: @@ -1277,24 +1312,24 @@ packages: node: '>=4' resolution: integrity: sha1-iINE2tAiCnLjr1CQYRf0h3GSX6w= - /ajv-errors/1.0.1/ajv@6.7.0: + /ajv-errors/1.0.1/ajv@6.9.2: dependencies: - ajv: 6.7.0 + ajv: 6.9.2 dev: false id: registry.npmjs.org/ajv-errors/1.0.1 peerDependencies: ajv: '>=5.0.0' resolution: integrity: sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== - /ajv-keywords/3.2.0/ajv@6.7.0: + /ajv-keywords/3.4.0/ajv@6.9.2: dependencies: - ajv: 6.7.0 + ajv: 6.9.2 dev: false - id: registry.npmjs.org/ajv-keywords/3.2.0 + id: registry.npmjs.org/ajv-keywords/3.4.0 peerDependencies: - ajv: ^6.0.0 + ajv: ^6.9.1 resolution: - integrity: sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo= + integrity: sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw== /ajv/5.5.2: dependencies: co: 4.6.0 @@ -1304,7 +1339,7 @@ packages: dev: false resolution: integrity: sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU= - /ajv/6.7.0: + /ajv/6.9.2: dependencies: fast-deep-equal: 2.0.1 fast-json-stable-stringify: 2.0.0 @@ -1312,7 +1347,7 @@ packages: uri-js: 4.2.2 dev: false resolution: - integrity: sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg== + integrity: sha512-4UFy0/LgDo7Oa/+wOAlj44tp9K78u38E5/359eSrqEp1Z5PdVfimCcs7SluXMP755RUQu6d2b4AvF0R1C9RZjg== /alphanum-sort/1.0.2: dev: false resolution: @@ -1335,12 +1370,12 @@ packages: node: '>=6' resolution: integrity: sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== - /ansi-escapes/3.1.0: + /ansi-escapes/3.2.0: dev: false engines: node: '>=4' resolution: - integrity: sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw== + integrity: sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== /ansi-html/0.0.7: dev: false engines: @@ -1360,6 +1395,12 @@ packages: node: '>=4' resolution: integrity: sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + /ansi-regex/4.0.0: + dev: false + engines: + node: '>=6' + resolution: + integrity: sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w== /ansi-styles/2.2.1: dev: false engines: @@ -1426,7 +1467,7 @@ packages: /archiver/2.1.1: dependencies: archiver-utils: 1.3.0 - async: 2.6.1 + async: 2.6.2 buffer-crc32: 0.2.13 glob: 7.1.3 lodash: 4.17.11 @@ -1485,6 +1526,10 @@ packages: dev: false resolution: integrity: sha1-fajPLiZijtcygDWB/SH2fKzS7uw= + /array-filter/1.0.0: + dev: false + resolution: + integrity: sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= /array-find-index/1.0.2: dev: false engines: @@ -1615,12 +1660,12 @@ packages: dev: false resolution: integrity: sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= - /async/2.6.1: + /async/2.6.2: dependencies: lodash: 4.17.11 dev: false resolution: - integrity: sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ== + integrity: sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg== /asynckit/0.4.0: dev: false resolution: @@ -1642,7 +1687,7 @@ packages: /autoprefixer/6.7.7: dependencies: browserslist: 1.7.7 - caniuse-db: 1.0.30000928 + caniuse-db: 1.0.30000939 normalize-range: 0.1.2 num2fraction: 1.2.2 postcss: 5.2.18 @@ -1653,7 +1698,7 @@ packages: /autoprefixer/8.6.5: dependencies: browserslist: 3.2.8 - caniuse-lite: 1.0.30000928 + caniuse-lite: 1.0.30000939 normalize-range: 0.1.2 num2fraction: 1.2.2 postcss: 6.0.23 @@ -1700,7 +1745,7 @@ packages: /babel-polyfill/6.26.0: dependencies: babel-runtime: 6.26.0 - core-js: 2.6.2 + core-js: 2.6.5 regenerator-runtime: 0.10.5 dev: false optional: true @@ -1708,7 +1753,7 @@ packages: integrity: sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM= /babel-runtime/6.26.0: dependencies: - core-js: 2.6.2 + core-js: 2.6.5 regenerator-runtime: 0.11.1 dev: false resolution: @@ -1809,12 +1854,12 @@ packages: dev: false resolution: integrity: sha1-g4qZLan51zfg9LLbC+YrsJ3Qxeg= - /binary-extensions/1.12.0: + /binary-extensions/1.13.0: dev: false engines: node: '>=0.10.0' resolution: - integrity: sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg== + integrity: sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw== /bl/1.2.2: dependencies: readable-stream: 2.3.6 @@ -1969,7 +2014,7 @@ packages: /browserify-rsa/4.0.1: dependencies: bn.js: 4.11.8 - randombytes: 2.0.6 + randombytes: 2.1.0 dev: false resolution: integrity: sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= @@ -1981,7 +2026,7 @@ packages: create-hmac: 1.1.7 elliptic: 6.4.1 inherits: 2.0.3 - parse-asn1: 5.1.1 + parse-asn1: 5.1.4 dev: false resolution: integrity: sha1-qk62jl17ZYuqa/alfmMMvXqT0pg= @@ -1993,8 +2038,8 @@ packages: integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== /browserslist/1.7.7: dependencies: - caniuse-db: 1.0.30000928 - electron-to-chromium: 1.3.103 + caniuse-db: 1.0.30000939 + electron-to-chromium: 1.3.113 deprecated: Browserslist 2 could fail on reading Browserslist >3.0 config used in other tools. dev: false hasBin: true @@ -2002,17 +2047,17 @@ packages: integrity: sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk= /browserslist/3.2.8: dependencies: - caniuse-lite: 1.0.30000928 - electron-to-chromium: 1.3.103 + caniuse-lite: 1.0.30000939 + electron-to-chromium: 1.3.113 dev: false hasBin: true resolution: integrity: sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ== /browserslist/4.1.1: dependencies: - caniuse-lite: 1.0.30000928 - electron-to-chromium: 1.3.103 - node-releases: 1.1.3 + caniuse-lite: 1.0.30000939 + electron-to-chromium: 1.3.113 + node-releases: 1.1.8 dev: false hasBin: true resolution: @@ -2092,7 +2137,7 @@ packages: hasBin: true optionalDependencies: dtrace-provider: 0.8.7 - moment: 2.23.0 + moment: 2.24.0 mv: 2.1.1 safe-json-stringify: 1.2.0 resolution: @@ -2233,32 +2278,32 @@ packages: /caniuse-api/1.6.1: dependencies: browserslist: 1.7.7 - caniuse-db: 1.0.30000928 + caniuse-db: 1.0.30000939 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 dev: false resolution: integrity: sha1-tTTnxzTE+B7F++isoq0kNUuWLGw= - /caniuse-db/1.0.30000928: + /caniuse-db/1.0.30000939: dev: false resolution: - integrity: sha512-nAoeTspAEzLjqGSeibzM09WojORi08faeOOI5GBmFWC3/brydovb9lYJWM+p48rEQsdevfpufK58gPiDtwOWKw== - /caniuse-lite/1.0.30000928: + integrity: sha512-nB5tLf3hOs+biXl1lhKjHRgNC0J1I7H52h/t1FP7qxARKKwpB0z+P/JewJLYAlxCBP/q7rxJzQzHHrQMl0viKg== + /caniuse-lite/1.0.30000939: dev: false resolution: - integrity: sha512-aSpMWRXL6ZXNnzm8hgE4QDLibG5pVJ2Ujzsuj3icazlIkxXkPXtL+BWnMx6FBkWmkZgBHGUxPZQvrbRw2ZTxhg== + integrity: sha512-oXB23ImDJOgQpGjRv1tCtzAvJr4/OvrHi5SO2vUgB0g0xpdZZoA/BxfImiWfdwoYdUTtQrPsXsvYU/dmCSM8gg== /capture-stack-trace/1.0.1: dev: false engines: node: '>=0.10.0' resolution: integrity: sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw== - /case-sensitive-paths-webpack-plugin/2.1.2: + /case-sensitive-paths-webpack-plugin/2.2.0: dev: false engines: node: '>=4' resolution: - integrity: sha512-oEZgAFfEvKtjSRCu6VgYkuGxwrWXMnQzyBmlLPP7r6PWQVtHxP5Z5N6XsuJvtoVax78am/r7lr46bwo3IVEBOg== + integrity: sha512-u5ElzokS8A1pm9vM3/iDgTcI3xqHxuCao94Oz8etI3cf0Tio0p8izkDYbTIn09uP3yUUr6+veaE6IkjnTYS46g== /caseless/0.12.0: dev: false resolution: @@ -2415,9 +2460,9 @@ packages: /cheerio/1.0.0-rc.2: dependencies: css-select: 1.2.0 - dom-serializer: 0.1.0 + dom-serializer: 0.1.1 entities: 1.1.2 - htmlparser2: 3.10.0 + htmlparser2: 3.10.1 lodash: 4.17.11 parse5: 3.0.3 dev: false @@ -2441,10 +2486,10 @@ packages: readdirp: 2.2.1 dev: false optionalDependencies: - fsevents: 1.2.6 + fsevents: 1.2.7 resolution: integrity: sha1-eY5ol3gVHIB2tLNg5e3SjNortGg= - /chokidar/2.0.4: + /chokidar/2.1.2: dependencies: anymatch: 2.0.0 async-each: 1.0.1 @@ -2453,20 +2498,32 @@ packages: inherits: 2.0.3 is-binary-path: 1.0.1 is-glob: 4.0.0 - lodash.debounce: 4.0.8 - normalize-path: 2.1.1 + normalize-path: 3.0.0 path-is-absolute: 1.0.1 readdirp: 2.2.1 upath: 1.1.0 dev: false optionalDependencies: - fsevents: 1.2.6 + fsevents: 1.2.7 resolution: - integrity: sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ== + integrity: sha512-IwXUx0FXc5ibYmPC2XeEj5mpXoV66sR+t3jqu2NS2GYwCktt3KF1/Qqjws/NkegajBA4RbZ5+DDwlOiJsxDHEg== /chownr/1.1.1: dev: false resolution: integrity: sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g== + /chrome-launcher/0.10.5: + dependencies: + '@types/core-js': 0.9.46 + '@types/mkdirp': 0.3.29 + '@types/node': 9.6.42 + '@types/rimraf': 0.0.28 + is-wsl: 1.1.0 + lighthouse-logger: 1.2.0 + mkdirp: 0.5.1 + rimraf: 2.6.3 + dev: false + resolution: + integrity: sha512-Gbzg8HlWhyuoVqflhiXwfFXhzNfNWvAkSWv2QR1Yl6mwsMo1oCLAVjp2tIySuS4lrZLEjzVx1fOy584yE76P4g== /chrome-trace-event/1.0.0: dependencies: tslib: 1.9.3 @@ -2475,18 +2532,6 @@ packages: node: '>=6.0' resolution: integrity: sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A== - /chromedriver/2.45.0: - dependencies: - del: 3.0.0 - extract-zip: 1.6.7 - mkdirp: 0.5.1 - request: 2.88.0 - tcp-port-used: 1.0.1 - dev: false - hasBin: true - requiresBuild: true - resolution: - integrity: sha512-Qwmcr+2mU3INeR6mVsQ8gO00vZpL8ZeTJLclX44C0dcs88jrSDgckPqbG+qkVX+m2L/aOPnF0lYgPdOiOiLt5w== /ci-info/1.6.0: dev: false resolution: @@ -2563,7 +2608,7 @@ packages: /cli-highlight/2.0.0: dependencies: chalk: 2.4.2 - highlight.js: 9.13.1 + highlight.js: 9.14.2 mz: 2.7.0 parse5: 4.0.0 yargs: 11.1.0 @@ -2705,6 +2750,12 @@ packages: node: '>=0.1.90' resolution: integrity: sha1-FopHAXVran9RoSzgyXv6KMCE7WM= + /colors/1.2.5: + dev: false + engines: + node: '>=0.1.90' + resolution: + integrity: sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg== /combined-stream/1.0.6: dependencies: delayed-stream: 1.0.0 @@ -2770,21 +2821,21 @@ packages: node: '>= 0.10.0' resolution: integrity: sha1-UkqfEJA/OoEzibAiXSfEi7dRiQ8= - /compressible/2.0.15: + /compressible/2.0.16: dependencies: - mime-db: 1.37.0 + mime-db: 1.38.0 dev: false engines: node: '>= 0.6' resolution: - integrity: sha512-4aE67DL33dSW9gw4CI2H/yTxqHLNcxp0yS6jB+4h+wr3e43+1z7vm0HU9qXOH8j+qjKuL8+UtkOxYQSMq60Ylw== + integrity: sha512-JQfEOdnI7dASwCuSPWIeVYwc/zMsu/+tRhoUvEfXz2gxOA2DNjmG5vhtFdBlhWPPGo+RdT9S3tgc/uH5qgDiiA== /compression/1.7.3: dependencies: accepts: 1.3.5 bytes: 3.0.0 - compressible: 2.0.15 + compressible: 2.0.16 debug: 2.6.9 - on-headers: 1.0.1 + on-headers: 1.0.2 safe-buffer: 5.1.2 vary: 1.1.2 dev: false @@ -2824,20 +2875,13 @@ packages: hasBin: true resolution: integrity: sha512-/+ugz+gwFSEfTGUxn0KHkY+19XPRTXR8+7oUK/HxgiN1n7FjeJmkrbSiXAJfyQ0zORgJYPaenmymwon51YXH9Q== - /config-chain/1.1.12: - dependencies: - ini: 1.3.5 - proto-list: 1.2.4 - dev: false - resolution: - integrity: sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== /configstore/3.1.2: dependencies: dot-prop: 4.2.0 graceful-fs: 4.1.15 make-dir: 1.3.0 unique-string: 1.0.0 - write-file-atomic: 2.3.0 + write-file-atomic: 2.4.2 xdg-basedir: 3.0.0 dev: false engines: @@ -2882,7 +2926,7 @@ packages: dev: false resolution: integrity: sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A== - /cookie-parser/1.4.3: + /cookie-parser/1.4.4: dependencies: cookie: 0.3.1 cookie-signature: 1.0.6 @@ -2890,7 +2934,7 @@ packages: engines: node: '>= 0.8.0' resolution: - integrity: sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU= + integrity: sha512-lo13tqF3JEtFO7FyA49CqbhaFkskRJ0u/UAiINgrIXeRCY41c88/zxtrECl8AKH3B0hj9q10+h3Kt8I7KlW4tw== /cookie-signature/1.0.6: dev: false resolution: @@ -2926,10 +2970,10 @@ packages: dev: false resolution: integrity: sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY= - /core-js/2.6.2: + /core-js/2.6.5: dev: false resolution: - integrity: sha512-NdBPF/RVwPW6jr0NCILuyN9RiqLo2b1mddWHkUL+VnvcB7dzlnBJ1bXYntjpTGOgkZiiLWj2JxmOr7eGE3qK6g== + integrity: sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A== /core-util-is/1.0.2: dev: false resolution: @@ -2937,7 +2981,7 @@ packages: /cosmiconfig/4.0.0: dependencies: is-directory: 0.3.1 - js-yaml: 3.12.1 + js-yaml: 3.12.2 parse-json: 4.0.0 require-from-string: 2.0.2 dev: false @@ -2954,7 +2998,7 @@ packages: glob2base: 0.0.12 minimatch: 3.0.4 mkdirp: 0.5.1 - resolve: 1.9.0 + resolve: 1.10.0 safe-buffer: 5.1.2 shell-quote: 1.6.1 subarg: 1.0.0 @@ -3076,7 +3120,7 @@ packages: inherits: 2.0.3 pbkdf2: 3.0.17 public-encrypt: 4.0.3 - randombytes: 2.0.6 + randombytes: 2.1.0 randomfill: 1.0.4 dev: false resolution: @@ -3093,7 +3137,7 @@ packages: integrity: sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= /css-in-js-utils/2.0.1: dependencies: - hyphenate-style-name: 1.0.2 + hyphenate-style-name: 1.0.3 isobject: 3.0.1 dev: false resolution: @@ -3128,7 +3172,7 @@ packages: /css-select/1.2.0: dependencies: boolbase: 1.0.0 - css-what: 2.1.2 + css-what: 2.1.3 domutils: 1.5.1 nth-check: 1.0.2 dev: false @@ -3146,10 +3190,10 @@ packages: dev: false resolution: integrity: sha1-Xv1sLupeof1rasV+wEJ7GEUkJOo= - /css-what/2.1.2: + /css-what/2.1.3: dev: false resolution: - integrity: sha512-wan8dMWQ0GUeF7DGEPVjhHemVW/vy6xUYmFzRY8RYqgA0JtXC9rJmbScBjqSu6dg9q0lwPQy6ZAmJVr3PPTvqQ== + integrity: sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== /css/2.2.4: dependencies: inherits: 2.0.3 @@ -3211,20 +3255,20 @@ packages: hasBin: true resolution: integrity: sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U= - /cssom/0.3.4: + /cssom/0.3.6: dev: false resolution: - integrity: sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog== - /cssstyle/1.1.1: + integrity: sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A== + /cssstyle/1.2.1: dependencies: - cssom: 0.3.4 + cssom: 0.3.6 dev: false resolution: - integrity: sha512-364AI1l/M5TYcFH83JnOH/pSqgaNnKmYgKrm0didZMGKWjQB60dymwWy1rKUgL3J1ffdq9xVi2yGLHdSjjSNog== - /csstype/2.6.0: + integrity: sha512-7DYm8qe+gPx/h77QlCyFmX80+fGaE/6A/Ekl0zaszYOubvySO2saYFdQ78P29D0UsULxFKCetDGNaNRUdSF+2A== + /csstype/2.6.2: dev: false resolution: - integrity: sha512-by8hi8BlLbowQq0qtkx54d9aN73R9oUW20HISpka5kmgsR9F7nnxgfsemuR2sdCKZh+CDNf5egW9UZMm4mgJRg== + integrity: sha512-Rl7PvTae0pflc1YtxtKbiSqq20Ts6vpIYOD5WBafl4y123DyHUeLrRdQP66sQW8/6gmX8jrYJLXwNeMqYVJcow== /currently-unhandled/0.4.1: dependencies: array-find-index: 1.0.2 @@ -3283,12 +3327,6 @@ packages: dev: false resolution: integrity: sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - /debug/4.1.0: - dependencies: - ms: 2.1.1 - dev: false - resolution: - integrity: sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg== /debug/4.1.1: dependencies: ms: 2.1.1 @@ -3380,15 +3418,15 @@ packages: node: '>=0.10.0' resolution: integrity: sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA== - /default-gateway/2.7.2: + /default-gateway/4.1.2: dependencies: - execa: 0.10.0 + execa: 1.0.0 ip-regex: 2.1.0 dev: false engines: - node: '>=4' + node: '>=6' resolution: - integrity: sha512-lAc4i9QJR0YHSDFdzeBQKfZ1SRDG3hsJNEkrpcZa8QhBfidLAilT60BDEIVUUGqosFp425KOgB3uYqcnQrWafQ== + integrity: sha512-xhJUAp3u02JsBGovj0V6B6uYhKCUOmiNc8xGmReUwGu77NmvcpxPVB0pCielxMFumO7CmXBG02XjM8HB97k8Hw== /default-require-extensions/1.0.0: dependencies: strip-bom: 2.0.0 @@ -3399,7 +3437,7 @@ packages: integrity: sha1-836hXT4T/9m0N9M+GnW1+5eHTLg= /define-properties/1.1.3: dependencies: - object-keys: 1.0.12 + object-keys: 1.1.0 dev: false engines: node: '>= 0.4' @@ -3528,18 +3566,18 @@ packages: dependencies: bn.js: 4.11.8 miller-rabin: 4.0.1 - randombytes: 2.0.6 + randombytes: 2.1.0 dev: false resolution: integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== - /dir-glob/2.2.1: + /dir-glob/2.2.2: dependencies: path-type: 3.0.0 dev: false engines: node: '>=4' resolution: - integrity: sha512-UN6X6XwRjllabfRhBdkVSo63uurJ8nSvMGrwl94EYVz6g+exhTV+yVSYk5VC/xl3MBFBTtC0J20uFKce4Brrng== + integrity: sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== /discontinuous-range/1.0.0: dev: false resolution: @@ -3578,28 +3616,28 @@ packages: integrity: sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== /dom-helpers/3.4.0: dependencies: - '@babel/runtime': 7.2.0 + '@babel/runtime': 7.3.4 dev: false resolution: integrity: sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA== - /dom-serializer/0.1.0: + /dom-serializer/0.1.1: dependencies: - domelementtype: 1.1.3 + domelementtype: 1.3.1 entities: 1.1.2 dev: false resolution: - integrity: sha1-BzxpdUbOB4DOI75KKOKT5AvDDII= - /dom-testing-library/3.16.3: + integrity: sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== + /dom-testing-library/3.16.8: dependencies: - '@babel/runtime': 7.2.0 + '@babel/runtime': 7.3.4 '@sheerun/mutationobserver-shim': 0.3.2 - pretty-format: 23.6.0 + pretty-format: 24.0.0 wait-for-expect: 1.1.0 dev: false engines: node: '>=8' resolution: - integrity: sha512-AZJJ/lmw+/yZxUVWoua5BofYK9EAQ/7Ai2wldUb6mSjE1XZy2H/+IrhG57lp3GqYN5WK7c2HzBLu8lW3c6/bXQ== + integrity: sha512-VGn2piehGoN9lmZDYd+xoTZwwcS+FoXebvZMw631UhS5LshiLTFNJs9bxRa9W7fVb1cAn9AYKAKZXh67rCDaqw== /dom-urls/1.1.0: dependencies: urijs: 1.19.1 @@ -3619,10 +3657,6 @@ packages: npm: '>=1.2' resolution: integrity: sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== - /domelementtype/1.1.3: - dev: false - resolution: - integrity: sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs= /domelementtype/1.3.1: dev: false resolution: @@ -3633,12 +3667,6 @@ packages: dev: false resolution: integrity: sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== - /domhandler/2.1.0: - dependencies: - domelementtype: 1.3.1 - dev: false - resolution: - integrity: sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ= /domhandler/2.4.2: dependencies: domelementtype: 1.3.1 @@ -3649,22 +3677,16 @@ packages: dev: false resolution: integrity: sha1-kfJS5Ze2Wvd+dFriTdAYXV4m1Yw= - /domutils/1.1.6: - dependencies: - domelementtype: 1.3.1 - dev: false - resolution: - integrity: sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU= /domutils/1.5.1: dependencies: - dom-serializer: 0.1.0 + dom-serializer: 0.1.1 domelementtype: 1.3.1 dev: false resolution: integrity: sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= /domutils/1.7.0: dependencies: - dom-serializer: 0.1.0 + dom-serializer: 0.1.1 domelementtype: 1.3.1 dev: false resolution: @@ -3707,7 +3729,7 @@ packages: dev: false resolution: integrity: sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= - /duplexify/3.6.1: + /duplexify/3.7.1: dependencies: end-of-stream: 1.4.1 inherits: 2.0.3 @@ -3715,7 +3737,7 @@ packages: stream-shift: 1.0.0 dev: false resolution: - integrity: sha512-vM58DwdnKmty+FSPzT14K9JXb90H+j5emaR4KYbr2KTIz00WHGbWOe5ghQTx233ZCLZtrGDALzKwcjEtSt35mA== + integrity: sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== /ebnf-parser/0.1.10: dev: false resolution: @@ -3727,18 +3749,6 @@ packages: dev: false resolution: integrity: sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - /editorconfig/0.15.2: - dependencies: - '@types/node': 10.12.18 - '@types/semver': 5.5.0 - commander: 2.19.0 - lru-cache: 4.1.5 - semver: 5.6.0 - sigmund: 1.0.1 - dev: false - hasBin: true - resolution: - integrity: sha512-GWjSI19PVJAM9IZRGOS+YKI8LN+/sjkSjNyvxL5ucqP9/IqtYNXBaQ/6c/hkPNYQHyOHra2KoXZI/JVpuqwmcQ== /ee-first/1.1.1: dev: false resolution: @@ -3781,11 +3791,11 @@ packages: hasBin: true resolution: integrity: sha512-FjEWG9Jb/ppK/2zToP+U5dds114fM1ZOJqMAR4aXXL5CvyPE9fiqBK/9YcwC9poIFQTEJk/EM/zyRwziziRZrg== - /electron-to-chromium/1.3.103: + /electron-to-chromium/1.3.113: dev: false resolution: - integrity: sha512-tObPqGmY9X8MUM8i3MEimYmbnLLf05/QV5gPlkR8MQ3Uj8G8B2govE1U4cQcBYtv3ymck9Y8cIOu4waoiykMZQ== - /electron/4.0.1: + integrity: sha512-De+lPAxEcpxvqPTyZAXELNpRZXABRxf+uL/rSykstQhzj/B0l1150G/ExIIxKc16lI89Hgz81J0BHAcbTqK49g== + /electron/4.0.6: dependencies: '@types/node': 10.12.18 electron-download: 4.1.1 @@ -3796,7 +3806,7 @@ packages: hasBin: true requiresBuild: true resolution: - integrity: sha512-kBWDLn1Vq8Tm6+/HpQc8gkjX7wJyQI8v/lf2kAirfi0Q4cXh6vBjozFvV1U/9gGCbyKnIDM+m8/wpyJIjg4w7g== + integrity: sha512-r2ow/EmDibjoCNJp35mB1CcPs2xBF9fp2eoWuUOJmpVQyzdba6EnqsSD1BxwXzF9vQ5WkQ2UbfXBIvpomrdioQ== /elliptic/6.4.1: dependencies: bn.js: 4.11.8 @@ -3853,66 +3863,66 @@ packages: node: '>=4' resolution: integrity: sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA= - /enzyme-adapter-react-16/1.7.1: + /enzyme-adapter-react-16/1.10.0: dependencies: - enzyme-adapter-utils: 1.9.1 - function.prototype.name: 1.1.0 + enzyme-adapter-utils: 1.10.0 object.assign: 4.1.0 object.values: 1.1.0 - prop-types: 15.6.2 - react-is: 16.7.0 - react-test-renderer: 16.7.0 + prop-types: 15.7.2 + react-is: 16.8.3 + react-test-renderer: 16.8.3 dev: false peerDependencies: enzyme: ^3.0.0 react: ^16.0.0-0 react-dom: ^16.0.0-0 resolution: - integrity: sha512-OQXKgfHWyHN3sFu2nKj3mhgRcqIPIJX6aOzq5AHVFES4R9Dw/vCBZFMPyaG81g2AZ5DogVh39P3MMNUbqNLTcw== - /enzyme-adapter-react-16/1.7.1/84ec9ac6a98d71b6c46b2abae79a4882: + integrity: sha512-0QqwEZcBv1xEEla+a3H7FMci+y4ybLia9cZzsdIrId7qcig4MK0kqqf6iiCILH1lsKS6c6AVqL3wGPhCevv5aQ== + /enzyme-adapter-react-16/1.10.0/ca330bebd2626bd9cb8e393ae8a11715: dependencies: - enzyme: 3.8.0 - enzyme-adapter-utils: /enzyme-adapter-utils/1.9.1/react@16.7.0 - function.prototype.name: 1.1.0 + enzyme: 3.9.0 + enzyme-adapter-utils: /enzyme-adapter-utils/1.10.0/react@16.8.3 object.assign: 4.1.0 object.values: 1.1.0 - prop-types: 15.6.2 - react: 16.7.0 - react-dom: /react-dom/16.7.0/react@16.7.0 - react-is: 16.7.0 - react-test-renderer: /react-test-renderer/16.7.0/react@16.7.0 + prop-types: 15.7.2 + react: 16.8.3 + react-dom: /react-dom/16.8.3/react@16.8.3 + react-is: 16.8.3 + react-test-renderer: /react-test-renderer/16.8.3/react@16.8.3 dev: false - id: registry.npmjs.org/enzyme-adapter-react-16/1.7.1 + id: registry.npmjs.org/enzyme-adapter-react-16/1.10.0 peerDependencies: enzyme: ^3.0.0 react: ^16.0.0-0 react-dom: ^16.0.0-0 resolution: - integrity: sha512-OQXKgfHWyHN3sFu2nKj3mhgRcqIPIJX6aOzq5AHVFES4R9Dw/vCBZFMPyaG81g2AZ5DogVh39P3MMNUbqNLTcw== - /enzyme-adapter-utils/1.9.1: + integrity: sha512-0QqwEZcBv1xEEla+a3H7FMci+y4ybLia9cZzsdIrId7qcig4MK0kqqf6iiCILH1lsKS6c6AVqL3wGPhCevv5aQ== + /enzyme-adapter-utils/1.10.0: dependencies: function.prototype.name: 1.1.0 object.assign: 4.1.0 - prop-types: 15.6.2 + object.fromentries: 2.0.0 + prop-types: 15.7.2 semver: 5.6.0 dev: false peerDependencies: react: 0.13.x || 0.14.x || ^15.0.0-0 || ^16.0.0-0 resolution: - integrity: sha512-LWc88BbKztLXlpRf5Ba/pSMJRaNezAwZBvis3N/IuB65ltZEh2E2obWU9B36pAbw7rORYeBUuqc79OL17ZzN1A== - /enzyme-adapter-utils/1.9.1/react@16.7.0: + integrity: sha512-VnIXJDYVTzKGbdW+lgK8MQmYHJquTQZiGzu/AseCZ7eHtOMAj4Rtvk8ZRopodkfPves0EXaHkXBDkVhPa3t0jA== + /enzyme-adapter-utils/1.10.0/react@16.8.3: dependencies: function.prototype.name: 1.1.0 object.assign: 4.1.0 - prop-types: 15.6.2 - react: 16.7.0 + object.fromentries: 2.0.0 + prop-types: 15.7.2 + react: 16.8.3 semver: 5.6.0 dev: false - id: registry.npmjs.org/enzyme-adapter-utils/1.9.1 + id: registry.npmjs.org/enzyme-adapter-utils/1.10.0 peerDependencies: react: 0.13.x || 0.14.x || ^15.0.0-0 || ^16.0.0-0 resolution: - integrity: sha512-LWc88BbKztLXlpRf5Ba/pSMJRaNezAwZBvis3N/IuB65ltZEh2E2obWU9B36pAbw7rORYeBUuqc79OL17ZzN1A== + integrity: sha512-VnIXJDYVTzKGbdW+lgK8MQmYHJquTQZiGzu/AseCZ7eHtOMAj4Rtvk8ZRopodkfPves0EXaHkXBDkVhPa3t0jA== /enzyme-to-json/3.3.5: dependencies: lodash: 4.17.11 @@ -3923,9 +3933,9 @@ packages: enzyme: ^3.0.0 resolution: integrity: sha512-DmH1wJ68HyPqKSYXdQqB33ZotwfUhwQZW3IGXaNXgR69Iodaoj8TF/D9RjLdz4pEhGq2Tx2zwNUIjBuqoZeTgA== - /enzyme-to-json/3.3.5/enzyme@3.8.0: + /enzyme-to-json/3.3.5/enzyme@3.9.0: dependencies: - enzyme: 3.8.0 + enzyme: 3.9.0 lodash: 4.17.11 dev: false engines: @@ -3935,15 +3945,17 @@ packages: enzyme: ^3.0.0 resolution: integrity: sha512-DmH1wJ68HyPqKSYXdQqB33ZotwfUhwQZW3IGXaNXgR69Iodaoj8TF/D9RjLdz4pEhGq2Tx2zwNUIjBuqoZeTgA== - /enzyme/3.8.0: + /enzyme/3.9.0: dependencies: array.prototype.flat: 1.2.1 cheerio: 1.0.0-rc.2 function.prototype.name: 1.1.0 has: 1.0.3 + html-element-map: 1.0.0 is-boolean-object: 1.0.0 is-callable: 1.1.4 is-number-object: 1.0.3 + is-regex: 1.0.4 is-string: 1.0.4 is-subset: 0.1.1 lodash.escape: 4.0.1 @@ -3958,7 +3970,7 @@ packages: string.prototype.trim: 1.1.2 dev: false resolution: - integrity: sha512-bfsWo5nHyZm1O1vnIsbwdfhU989jk+squU9NKvB+Puwo5j6/Wg9pN5CO0YJelm98Dao3NPjkDZk+vvgwpMwYxw== + integrity: sha512-JqxI2BRFHbmiP7/UFqvsjxTirWoM1HfeaJrmVSZ9a1EADKkZgdPcAuISPMpoUiHlac9J4dYt81MC5BBIrbJGMg== /errno/0.1.7: dependencies: prr: 1.0.1 @@ -3979,7 +3991,7 @@ packages: has: 1.0.3 is-callable: 1.1.4 is-regex: 1.0.4 - object-keys: 1.0.12 + object-keys: 1.1.0 dev: false engines: node: '>= 0.4' @@ -3999,13 +4011,13 @@ packages: dev: false resolution: integrity: sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM= - /es6-promise/4.2.5: + /es6-promise/4.2.6: dev: false resolution: - integrity: sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg== + integrity: sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q== /es6-promisify/5.0.0: dependencies: - es6-promise: 4.2.5 + es6-promise: 4.2.6 dev: false resolution: integrity: sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= @@ -4031,7 +4043,7 @@ packages: source-map: 0.7.3 resolution: integrity: sha1-U9ZSz6EDA4gnlFilJmxf/HCcY8M= - /escodegen/1.11.0: + /escodegen/1.11.1: dependencies: esprima: 3.1.3 estraverse: 4.2.0 @@ -4044,7 +4056,7 @@ packages: optionalDependencies: source-map: 0.6.1 resolution: - integrity: sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw== + integrity: sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw== /eslint-scope/4.0.0: dependencies: esrecurse: 4.2.1 @@ -4145,12 +4157,12 @@ packages: dev: false resolution: integrity: sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA== - /events/1.1.1: + /events/3.0.0: dev: false engines: - node: '>=0.4.x' + node: '>=0.8.x' resolution: - integrity: sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= + integrity: sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA== /eventsource/0.1.6: dependencies: original: 1.0.2 @@ -4174,20 +4186,6 @@ packages: dev: false resolution: integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== - /execa/0.10.0: - dependencies: - cross-spawn: 6.0.5 - get-stream: 3.0.0 - is-stream: 1.1.0 - npm-run-path: 2.0.2 - p-finally: 1.0.0 - signal-exit: 3.0.2 - strip-eof: 1.0.0 - dev: false - engines: - node: '>=4' - resolution: - integrity: sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw== /execa/0.7.0: dependencies: cross-spawn: 5.1.0 @@ -4252,7 +4250,7 @@ packages: integrity: sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= /expand-tilde/2.0.2: dependencies: - homedir-polyfill: 1.0.1 + homedir-polyfill: 1.0.3 dev: false engines: node: '>=0.10.0' @@ -4274,7 +4272,7 @@ packages: crc: 3.4.4 debug: 2.6.9 depd: 1.1.2 - on-headers: 1.0.1 + on-headers: 1.0.2 parseurl: 1.3.2 uid-safe: 2.1.5 utils-merge: 1.0.1 @@ -4443,12 +4441,6 @@ packages: dev: false resolution: integrity: sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ== - /fault/1.0.2: - dependencies: - format: 0.2.2 - dev: false - resolution: - integrity: sha512-o2eo/X2syzzERAtN5LcGbiVQ0WwZSlN3qLtadwAz3X8Bu+XWD16dja/KMsjZLiQr+BLGPDnHGkc4yUJf1Xpkpw== /faye-websocket/0.10.0: dependencies: websocket-driver: 0.7.0 @@ -4527,11 +4519,11 @@ packages: webpack: ^2.0.0 || ^3.0.0 || ^4.0.0 resolution: integrity: sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg== - /file-loader/1.1.11/webpack@4.28.4: + /file-loader/1.1.11/webpack@4.29.6: dependencies: loader-utils: 1.2.3 schema-utils: 0.4.7 - webpack: 4.28.4 + webpack: 4.29.6 dev: false engines: node: '>= 4.3 < 5.0.0 || >= 5.10' @@ -4663,21 +4655,21 @@ packages: dev: false resolution: integrity: sha1-2uRqnXj74lKSJYzB54CkHZXAN4I= - /flush-write-stream/1.0.3: + /flush-write-stream/1.1.1: dependencies: inherits: 2.0.3 readable-stream: 2.3.6 dev: false resolution: - integrity: sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw== - /follow-redirects/1.6.1: + integrity: sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== + /follow-redirects/1.7.0: dependencies: - debug: 3.1.0 + debug: 3.2.6 dev: false engines: node: '>=4.0' resolution: - integrity: sha512-t2JCjbzxQpWvbhts3l6SH1DKzSrx8a+SsaVf4h6bG4kOXUuPYS/kg2Lr4gQSb7eemaHqJkOThF1BGyjlUkO1GQ== + integrity: sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ== /for-each/0.3.3: dependencies: is-callable: 1.1.4 @@ -4720,11 +4712,11 @@ packages: dependencies: babel-code-frame: 6.26.0 chalk: 2.4.2 - chokidar: 2.0.4 + chokidar: 2.1.2 lodash: 4.17.11 micromatch: 3.1.10 minimatch: 3.0.4 - resolve: 1.9.0 + resolve: 1.10.0 tapable: 1.1.1 dev: false engines: @@ -4735,19 +4727,19 @@ packages: webpack: ^2.3.0 || ^3.0.0 || ^4.0.0 resolution: integrity: sha512-qNYuygh2GxXehBvQZ5rI5YlQFn+7ZV6kmkyD9Sgs33dWl73NZdUOB5aCp8v0EXJn176AhPrZP8YCMT3h01fs+g== - /fork-ts-checker-webpack-plugin/0.4.15/3d5b451f3b58df190ea699c3e34587f4: + /fork-ts-checker-webpack-plugin/0.4.15/7d74fab92916d4959c8fedd113bb5af2: dependencies: babel-code-frame: 6.26.0 chalk: 2.4.2 - chokidar: 2.0.4 + chokidar: 2.1.2 lodash: 4.17.11 micromatch: 3.1.10 minimatch: 3.0.4 - resolve: 1.9.0 + resolve: 1.10.0 tapable: 1.1.1 - tslint: /tslint/5.12.1/typescript@3.1.6 - typescript: 3.1.6 - webpack: 4.28.4 + tslint: /tslint/5.13.0/typescript@3.2.4 + typescript: 3.2.4 + webpack: 4.29.6 dev: false engines: node: '>=6.11.5' @@ -4762,7 +4754,7 @@ packages: dependencies: asynckit: 0.4.0 combined-stream: 1.0.6 - mime-types: 2.1.21 + mime-types: 2.1.22 dev: false engines: node: '>= 0.12' @@ -4772,7 +4764,7 @@ packages: dependencies: asynckit: 0.4.0 combined-stream: 1.0.7 - mime-types: 2.1.21 + mime-types: 2.1.22 dev: false engines: node: '>= 0.12' @@ -4782,12 +4774,6 @@ packages: dev: false resolution: integrity: sha1-Ay3KShFiYqEsQ/TD7IVmQWxbLZU= - /format/0.2.2: - dev: false - engines: - node: '>=0.4.x' - resolution: - integrity: sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs= /formidable/1.2.1: dev: false resolution: @@ -4869,6 +4855,16 @@ packages: dev: false resolution: integrity: sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA== + /fs-extra/7.0.1: + dependencies: + graceful-fs: 4.1.15 + jsonfile: 4.0.0 + universalify: 0.1.2 + dev: false + engines: + node: '>=6 <7 || >=8' + resolution: + integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== /fs-write-stream-atomic/1.0.10: dependencies: graceful-fs: 4.1.15 @@ -4882,7 +4878,7 @@ packages: dev: false resolution: integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - /fsevents/1.2.6: + /fsevents/1.2.7: bundledDependencies: - node-pre-gyp dependencies: @@ -4893,7 +4889,7 @@ packages: optional: true requiresBuild: true resolution: - integrity: sha512-BalK54tfK0pMC0jQFb2oHn1nz7JNQD/2ex5pBnCHgBi2xG7VV0cAOGy2RS2VbCqUXx5/6obMrMcQTJ8yjcGzbg== + integrity: sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw== /fstream/1.0.11: dependencies: graceful-fs: 4.1.15 @@ -4919,10 +4915,10 @@ packages: node: '>= 0.4' resolution: integrity: sha512-Bs0VRrTz4ghD8pTmbJQD1mZ8A/mN0ur/jGz+A6FBxPDUPkm1tNfF6bhTYPA7i7aF4lZJVr+OXTNNrnnIl58Wfg== - /fuse.js/3.3.0: + /fuse.js/3.4.2: dev: false resolution: - integrity: sha512-ESBRkGLWMuVkapqYCcNO1uqMg5qbCKkgb+VS6wsy17Rix0/cMS9kSOZoYkjH8Ko//pgJ/EEGu0GTjk2mjX2LGQ== + integrity: sha512-WVbrm+cAxPtyMqdtL7cYhR7aZJPhtOfjNClPya8GKMVukKDYs7pEnPINeRVX1C9WmWgU8MdYGYbUPAP2AJXdoQ== /gauge/2.7.4: dependencies: aproba: 1.2.0 @@ -5069,10 +5065,6 @@ packages: node: '>=4' resolution: integrity: sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU= - /global-modules-path/2.3.1: - dev: false - resolution: - integrity: sha512-y+shkf4InI7mPRHSo2b/k6ix6+NLDtyccYv86whhxrSGX9wjPX1VMITmrDbE1eh7zkzhiWtW2sHklJYoQ62Cxg== /global-modules/1.0.0: dependencies: global-prefix: 1.0.2 @@ -5086,7 +5078,7 @@ packages: /global-prefix/1.0.2: dependencies: expand-tilde: 2.0.2 - homedir-polyfill: 1.0.1 + homedir-polyfill: 1.0.3 ini: 1.3.5 is-windows: 1.0.2 which: 1.3.1 @@ -5102,12 +5094,12 @@ packages: dev: false resolution: integrity: sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8= - /globals/11.10.0: + /globals/11.11.0: dev: false engines: node: '>=4' resolution: - integrity: sha512-0GZF1RiPKU97IHUO5TORo9w1PwrH/NBPl+fS7oMLdaTRiYmYbwK4NWoZWrAdd0/abG9R2BU+OiwyQpTpE6pdfQ== + integrity: sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw== /globals/9.18.0: dev: false engines: @@ -5129,7 +5121,7 @@ packages: /globby/7.1.1: dependencies: array-union: 1.0.2 - dir-glob: 2.2.1 + dir-glob: 2.2.2 glob: 7.1.3 ignore: 3.3.10 pify: 3.0.0 @@ -5142,7 +5134,7 @@ packages: /globby/8.0.1: dependencies: array-union: 1.0.2 - dir-glob: 2.2.1 + dir-glob: 2.2.2 fast-glob: 2.2.6 glob: 7.1.3 ignore: 3.3.10 @@ -5243,9 +5235,9 @@ packages: dev: false resolution: integrity: sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ== - /handlebars/4.0.12: + /handlebars/4.1.0: dependencies: - async: 2.6.1 + async: 2.6.2 optimist: 0.6.1 source-map: 0.6.1 dev: false @@ -5255,7 +5247,7 @@ packages: optionalDependencies: uglify-js: 3.4.9 resolution: - integrity: sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA== + integrity: sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w== /har-schema/2.0.0: dev: false engines: @@ -5264,7 +5256,7 @@ packages: integrity: sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= /har-validator/5.1.3: dependencies: - ajv: 6.7.0 + ajv: 6.9.2 har-schema: 2.0.0 dev: false engines: @@ -5390,14 +5382,10 @@ packages: dev: false resolution: integrity: sha512-BXUKIkUuh6cmmxzi5OIbUJxrG8OAk2MqoL1DtO3Wo9D2faJg2ph5ntyuQeLqaHJmzER6H5tllCDA9ZnNe9BVGg== - /highlight.js/9.13.1: + /highlight.js/9.14.2: dev: false resolution: - integrity: sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A== - /highlight.js/9.8.0: - dev: false - resolution: - integrity: sha1-OO7vQM1F6t2+yMnlI4+3p4OjtoU= + integrity: sha512-Nc6YNECYpxyJABGYJAyw7dBAYbXEuIzwzkqoJnwbc1nIpCiN+3ioYf0XrBnLiyyG0JLuJhpPtt2iTSbXiKLoyA== /history/4.7.2: dependencies: invariant: 2.2.4 @@ -5420,20 +5408,20 @@ packages: dev: false resolution: integrity: sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw== - /hoist-non-react-statics/3.2.1: + /hoist-non-react-statics/3.3.0: dependencies: - react-is: 16.7.0 + react-is: 16.8.3 dev: false resolution: - integrity: sha512-TFsu3TV3YLY+zFTZDrN8L2DTFanObwmBLpWvJs1qfUuEQ5bTAdFcwfx2T/bsCXfM9QHSLvjfP+nihEl0yvozxw== - /homedir-polyfill/1.0.1: + integrity: sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA== + /homedir-polyfill/1.0.3: dependencies: parse-passwd: 1.0.0 dev: false engines: node: '>=0.10.0' resolution: - integrity: sha1-TCu8inWJmP7r9e1oWA921GdotLw= + integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== /hosted-git-info/2.7.1: dev: false resolution: @@ -5451,6 +5439,12 @@ packages: dev: false resolution: integrity: sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== + /html-element-map/1.0.0: + dependencies: + array-filter: 1.0.0 + dev: false + resolution: + integrity: sha512-/SP6aOiM5Ai9zALvCxDubIeez0LvG3qP7R9GcRDnJEP/HBmv0A8A9K0o8+HFudcFt46+i921ANjzKsjPjb7Enw== /html-encoding-sniffer/1.0.2: dependencies: whatwg-encoding: 1.0.5 @@ -5494,7 +5488,7 @@ packages: webpack: ^4.0.0 resolution: integrity: sha512-tyvhjVpuGqD7QYHi1l1drMQTg5i+qRxpQEGbdnYFREgOKy7aFDf/ocQ/V1fuEDlQx7jV2zMap3Hj2nE9i5eGXw== - /html-webpack-plugin/4.0.0-alpha.2/webpack@4.28.4: + /html-webpack-plugin/4.0.0-alpha.2/webpack@4.29.6: dependencies: '@types/tapable': 1.0.2 html-minifier: 3.5.21 @@ -5503,7 +5497,7 @@ packages: pretty-error: 2.1.1 tapable: 1.1.1 util.promisify: 1.0.0 - webpack: 4.28.4 + webpack: 4.29.6 dev: false engines: node: '>=6.9' @@ -5512,7 +5506,7 @@ packages: webpack: ^4.0.0 resolution: integrity: sha512-tyvhjVpuGqD7QYHi1l1drMQTg5i+qRxpQEGbdnYFREgOKy7aFDf/ocQ/V1fuEDlQx7jV2zMap3Hj2nE9i5eGXw== - /htmlparser2/3.10.0: + /htmlparser2/3.10.1: dependencies: domelementtype: 1.3.1 domhandler: 2.4.2 @@ -5522,16 +5516,7 @@ packages: readable-stream: 3.1.1 dev: false resolution: - integrity: sha512-J1nEUGv+MkXS0weHNWVKJJ+UrLfePxRWpN3C9bEi9fLxL2+ggW94DQvgYVXsaT30PGwYRIZKNZXuyMhp3Di4bQ== - /htmlparser2/3.3.0: - dependencies: - domelementtype: 1.3.1 - domhandler: 2.1.0 - domutils: 1.1.6 - readable-stream: 1.0.34 - dev: false - resolution: - integrity: sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4= + integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== /http-cache-semantics/3.8.1: dev: false resolution: @@ -5551,23 +5536,23 @@ packages: node: '>= 0.6' resolution: integrity: sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= - /http-errors/1.7.1: + /http-errors/1.7.2: dependencies: depd: 1.1.2 inherits: 2.0.3 - setprototypeof: 1.1.0 + setprototypeof: 1.1.1 statuses: 1.5.0 toidentifier: 1.0.0 dev: false engines: node: '>= 0.6' resolution: - integrity: sha512-jWEUgtZWGSMba9I1N3gc1HmvpBUaNC9vDdA46yScAdp+C5rdEuKWUBLWTQpW9FwSWSbYYs++b6SDCxf9UEJzfw== + integrity: sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== /http-parser-js/0.5.0: dev: false resolution: integrity: sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w== - /http-proxy-middleware/0.18.0: + /http-proxy-middleware/0.19.1: dependencies: http-proxy: 1.17.0 is-glob: 4.0.0 @@ -5577,7 +5562,7 @@ packages: engines: node: '>=4.0.0' resolution: - integrity: sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q== + integrity: sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== /http-proxy/1.11.1: dependencies: eventemitter3: 1.2.0 @@ -5590,7 +5575,7 @@ packages: /http-proxy/1.17.0: dependencies: eventemitter3: 3.1.0 - follow-redirects: 1.6.1 + follow-redirects: 1.7.0 requires-port: 1.0.0 dev: false engines: @@ -5601,7 +5586,7 @@ packages: dependencies: assert-plus: 1.0.0 jsprim: 1.4.1 - sshpk: 1.16.0 + sshpk: 1.16.1 dev: false engines: node: '>=0.8' @@ -5625,25 +5610,18 @@ packages: dev: false resolution: integrity: sha512-BMz6w8p3NVa6QP9wDtqUkXfwgBqDaZ5z/np0EYdoWrLqL849Onp6JWMXMhbHtuvO9jUThLN5H1ThRQ8dUWnYkA== - /hyphenate-style-name/1.0.2: + /hyphenate-style-name/1.0.3: dev: false resolution: - integrity: sha1-MRYKNpMK2vH8BMYHT360FGXU7Es= + integrity: sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ== /i18next-browser-languagedetector/2.2.4: dev: false resolution: integrity: sha512-wPbtH18FdOuB245I8Bhma5/XSDdN/HpYlX+wga1eMy+slhaFQSnrWX6fp+aYSL2eEuj0RlfHeEVz6Fo/lxAj6A== - /i18next-node-fs-backend/2.1.0: - dependencies: - js-yaml: 3.12.0 - json5: 2.0.0 - dev: false - resolution: - integrity: sha512-ULb+uQYQj1njn/93cxkAtgrw9STCv1zEM8QySH24c0pFxw8ZJPCV9yq4zTqEWtKaNRd8apvzyr2euoV1k05SbA== - /i18next-xhr-backend/1.5.1: + /i18next-xhr-backend/2.0.1: dev: false resolution: - integrity: sha512-9OLdC/9YxDvTFcgsH5t2BHCODHEotHCa6h7Ly0EUlUC7Y2GS09UeoHOGj3gWKQ3HCqXz8NlH4gOrK3NNc9vPuw== + integrity: sha512-CP0XPjJsTE4hY1rM1KXFYo63Ib61EBLEcTvMDyJwr0vs9p/UTuA3ENCmzSs9+ghZgWSjdOigc0oUERHaxctbsQ== /i18next/10.6.0: dev: false resolution: @@ -5813,7 +5791,7 @@ packages: integrity: sha1-hVG45bTVcyROZqNLBPfTIHaitTQ= /inquirer/3.3.0: dependencies: - ansi-escapes: 3.1.0 + ansi-escapes: 3.2.0 chalk: 2.4.2 cli-cursor: 2.1.0 cli-width: 2.2.0 @@ -5832,7 +5810,7 @@ packages: integrity: sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ== /inquirer/6.2.0: dependencies: - ansi-escapes: 3.1.0 + ansi-escapes: 3.2.0 chalk: 2.4.2 cli-cursor: 2.1.0 cli-width: 2.2.0 @@ -5841,7 +5819,7 @@ packages: lodash: 4.17.11 mute-stream: 0.0.7 run-async: 2.3.0 - rxjs: 6.3.3 + rxjs: 6.4.0 string-width: 2.1.1 strip-ansi: 4.0.0 through: 2.3.8 @@ -5852,22 +5830,22 @@ packages: integrity: sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg== /inspire-tree/5.0.2: dependencies: - es6-promise: 4.2.5 + es6-promise: 4.2.6 eventemitter2: 5.0.1 lodash: 4.17.11 uuid: 3.3.2 dev: false resolution: integrity: sha512-G4uNWK04SOcLK6qtUaRNf15EvLP4h9Y+sm12qFPOvj6WY2nmflF5uF2CrTmV4R4aRKpGOY1Qn9scRlt3QRnVHg== - /internal-ip/3.0.1: + /internal-ip/4.2.0: dependencies: - default-gateway: 2.7.2 - ipaddr.js: 1.8.1 + default-gateway: 4.1.2 + ipaddr.js: 1.9.0 dev: false engines: - node: '>=4' + node: '>=6' resolution: - integrity: sha512-NXXgESC2nNVtU+pqmC9e6R8B1GpKxzsAQhffvh5AL79qKnodd+L7tnEQmTiUAVngqLalPbSqRA7XGIEL5nCd0Q== + integrity: sha512-ZY8Rk+hlvFeuMmG5uH1MXhhdeMntmIaxaInvAmzMq/SHV8rv4Kh+6GiQNNDQd0wZFrcO+FiTBo8lui/osKOyJw== /interpret/1.2.0: dev: false engines: @@ -5917,12 +5895,6 @@ packages: node: '>=4' resolution: integrity: sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= - /ip-regex/3.0.0: - dev: false - engines: - node: '>=8' - resolution: - integrity: sha512-T8wDtjy+Qf2TAPDQmBp0eGKJ8GavlWlUnamr3wRn6vvdZlKVuJXXMlSncYFRYgVHOM3If5NR1H4+OvVQU9Idvg== /ip/1.1.5: dev: false resolution: @@ -5933,12 +5905,12 @@ packages: node: '>= 0.10' resolution: integrity: sha1-6qM9bd16zo9/b+DJygRA5wZzix4= - /ipaddr.js/1.8.1: + /ipaddr.js/1.9.0: dev: false engines: node: '>= 0.10' resolution: - integrity: sha1-+kt5+kf9Pe9eOxWYJRYcClGclCc= + integrity: sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA== /is-absolute-url/2.1.0: dev: false engines: @@ -5978,7 +5950,7 @@ packages: integrity: sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= /is-binary-path/1.0.1: dependencies: - binary-extensions: 1.12.0 + binary-extensions: 1.13.0 dev: false engines: node: '>=0.10.0' @@ -5994,14 +5966,6 @@ packages: dev: false resolution: integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - /is-builtin-module/1.0.0: - dependencies: - builtin-modules: 1.1.1 - dev: false - engines: - node: '>=0.10.0' - resolution: - integrity: sha1-VAVy0096wxGfj3bDDLwbHgN6/74= /is-callable/1.1.4: dev: false engines: @@ -6328,10 +6292,6 @@ packages: dev: false resolution: integrity: sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - /is-url/1.2.4: - dev: false - resolution: - integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== /is-utf8/0.2.1: dev: false resolution: @@ -6356,16 +6316,6 @@ packages: node: '>=4' resolution: integrity: sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= - /is2/2.0.1: - dependencies: - deep-is: 0.1.3 - ip-regex: 2.1.0 - is-url: 1.2.4 - dev: false - engines: - node: '>=v0.10.0' - resolution: - integrity: sha512-+WaJvnaA7aJySz2q/8sLjMb2Mw14KTplHmSwcSpZ/fWJPkUmqw3YTzSWbPJ7OAwRvdYTWF2Wg+yYJ1AdP5Z8CA== /isarray/0.0.1: dev: false resolution: @@ -6416,13 +6366,13 @@ packages: webpack: ^2.0.0 || ^3.0.0 || ^4.0.0 resolution: integrity: sha512-a5SPObZgS0jB/ixaKSMdn6n/gXSrK2S6q/UfRJBT3e6gQmVjwZROTODQsYW5ZNwOu78hG62Y3fWlebaVOL0C+w== - /istanbul-instrumenter-loader/3.0.1/webpack@4.28.4: + /istanbul-instrumenter-loader/3.0.1/webpack@4.29.6: dependencies: convert-source-map: 1.6.0 istanbul-lib-instrument: 1.10.2 loader-utils: 1.2.3 schema-utils: 0.3.0 - webpack: 4.28.4 + webpack: 4.29.6 dev: false engines: node: '>= 4.8 < 5.0.0 || >= 5.10' @@ -6435,12 +6385,12 @@ packages: dev: false resolution: integrity: sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ== - /istanbul-lib-coverage/2.0.1: + /istanbul-lib-coverage/2.0.3: dev: false engines: node: '>=6' resolution: - integrity: sha512-nPvSZsVlbG9aLhZYaC3Oi1gT/tpyo3Yt5fNyf6NmcKIayz4VV/txxJFFKAK/gU4dcNn8ehsanBbVHVl0+amOLA== + integrity: sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw== /istanbul-lib-hook/1.2.2: dependencies: append-transform: 0.4.0 @@ -6466,27 +6416,27 @@ packages: '@babel/template': 7.0.0-beta.51 '@babel/traverse': 7.0.0-beta.51 '@babel/types': 7.0.0-beta.51 - istanbul-lib-coverage: 2.0.1 + istanbul-lib-coverage: 2.0.3 semver: 5.6.0 dev: false engines: node: '>=6' resolution: integrity: sha512-l7TD/VnBsIB2OJvSyxaLW/ab1+92dxZNH9wLH7uHPPioy3JZ8tnx2UXUdKmdkgmP2EFPzg64CToUP6dAS3U32Q== - /istanbul-lib-instrument/3.0.0: + /istanbul-lib-instrument/3.1.0: dependencies: - '@babel/generator': 7.2.2 - '@babel/parser': 7.2.3 + '@babel/generator': 7.3.4 + '@babel/parser': 7.3.4 '@babel/template': 7.2.2 - '@babel/traverse': 7.2.3 - '@babel/types': 7.2.2 - istanbul-lib-coverage: 2.0.1 + '@babel/traverse': 7.3.4 + '@babel/types': 7.3.4 + istanbul-lib-coverage: 2.0.3 semver: 5.6.0 dev: false engines: node: '>=6' resolution: - integrity: sha512-eQY9vN9elYjdgN9Iv6NS/00bptm02EBBk70lRMaVjeA6QYocQgenVrSgC28TJurdnZa80AGO3ASdFN+w/njGiQ== + integrity: sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA== /isurl/1.0.0: dependencies: has-to-string-tag-x: 1.4.1 @@ -6575,25 +6525,18 @@ packages: hasBin: true resolution: integrity: sha1-kEFwfWIkE2f1iDRTK58ZwsNvrHg= - /jpeg-js/0.2.0: + /jju/1.3.0: dev: false resolution: - integrity: sha1-U+RI7J0mPmgyZkZ+lELSxaLvVII= - /js-base64/2.5.0: + integrity: sha1-2t2e8BkkvHKLA/L3l5vb1i96Kqo= + /jpeg-js/0.2.0: dev: false resolution: - integrity: sha512-wlEBIZ5LP8usDylWbDNhKPEFVFdI5hCHpnVoT/Ysvoi/PRhJENm/Rlh9TvjYB38HFfKZN7OzEbRjmjvLkFw11g== - /js-beautify/1.8.9: - dependencies: - config-chain: 1.1.12 - editorconfig: 0.15.2 - glob: 7.1.3 - mkdirp: 0.5.1 - nopt: 4.0.1 + integrity: sha1-U+RI7J0mPmgyZkZ+lELSxaLvVII= + /js-base64/2.5.1: dev: false - hasBin: true resolution: - integrity: sha512-MwPmLywK9RSX0SPsUJjN7i+RQY9w/yC17Lbrq9ViEefpLRgqAR2BgrMN2AbifkUuhDV8tRauLhLda/9+bE0YQA== + integrity: sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw== /js-tokens/3.0.2: dev: false resolution: @@ -6602,22 +6545,14 @@ packages: dev: false resolution: integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - /js-yaml/3.12.0: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - dev: false - hasBin: true - resolution: - integrity: sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A== - /js-yaml/3.12.1: + /js-yaml/3.12.2: dependencies: argparse: 1.0.10 esprima: 4.0.1 dev: false hasBin: true resolution: - integrity: sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA== + integrity: sha512-QHn/Lh/7HhZ/Twc7vJYQTkjuCa0kaCcDcjK5Zlk2rvnUpy7DxMJ23+Jc2dcyvltwQVg1nygAVlB2oRDFHoRS5Q== /js-yaml/3.7.0: dependencies: argparse: 1.0.10 @@ -6651,18 +6586,18 @@ packages: acorn: 5.7.3 acorn-globals: 4.3.0 array-equal: 1.0.0 - cssom: 0.3.4 - cssstyle: 1.1.1 + cssom: 0.3.6 + cssstyle: 1.2.1 data-urls: 1.1.0 domexception: 1.0.1 - escodegen: 1.11.0 + escodegen: 1.11.1 html-encoding-sniffer: 1.0.2 left-pad: 1.3.0 - nwsapi: 2.0.9 + nwsapi: 2.1.1 parse5: 4.0.0 pn: 1.1.0 request: 2.88.0 - request-promise-native: /request-promise-native/1.0.5/request@2.88.0 + request-promise-native: /request-promise-native/1.0.7/request@2.88.0 sax: 1.2.4 symbol-tree: 3.2.2 tough-cookie: 2.5.0 @@ -6712,7 +6647,7 @@ packages: /json-schema-faker/0.5.0-rc16: dependencies: json-schema-ref-parser: 5.1.3 - jsonpath: 1.0.0 + jsonpath: 1.0.1 randexp: 0.5.3 dev: false resolution: @@ -6721,7 +6656,7 @@ packages: dependencies: call-me-maybe: 1.0.1 debug: 3.2.6 - js-yaml: 3.12.1 + js-yaml: 3.12.2 ono: 4.0.11 dev: false resolution: @@ -6764,15 +6699,6 @@ packages: hasBin: true resolution: integrity: sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== - /json5/2.0.0: - dependencies: - minimist: 1.2.0 - dev: false - engines: - node: '>=6' - hasBin: true - resolution: - integrity: sha512-0EdQvHuLm7yJ7lyG5dp7Q3X2ku++BG5ZHaJ5FTnaXpKqDrw4pMxel5Bt3oAYMthnrthFBdnZ1FcsXTPyrQlV0w== /json5/2.1.0: dependencies: minimist: 1.2.0 @@ -6804,16 +6730,16 @@ packages: dev: false resolution: integrity: sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= - /jsonpath/1.0.0: + /jsonpath/1.0.1: dependencies: esprima: 1.2.2 jison: 0.4.13 - static-eval: 2.0.0 + static-eval: 2.0.2 underscore: 1.7.0 dev: false requiresBuild: true resolution: - integrity: sha1-Rc2dTE0NaCXZC9fkD4PxGCsT3Qc= + integrity: sha512-HY5kSg82LHIs0r0h9gYBwpNc1w1qGY0qJ7al01W1bJltsN2lp+mjjA/a79gXWuvD6Xf8oPkD2d5uKMZQXTGzqA== /jsprim/1.4.1: dependencies: assert-plus: 1.0.0 @@ -6829,7 +6755,7 @@ packages: dev: false resolution: integrity: sha1-Iqu5ZW00owuVMENnIINeicLlwxY= - /junit-report-builder/1.3.1: + /junit-report-builder/1.3.2: dependencies: date-format: 0.0.2 lodash: 4.17.11 @@ -6837,7 +6763,7 @@ packages: xmlbuilder: 10.1.1 dev: false resolution: - integrity: sha512-KTueBpPsmjfiyrAxxhKlEMwXb3aRmDHG5tRYwtRF3ujLQ7/e/5MH3b2p9ND2P84rU8z5dQq40vWJv6TtEdS16Q== + integrity: sha512-TPpe1hWatrBnBxiRT1M8ss6nCaaoEzZ0fFEdRkv45jVwrpZm9HAqNz1vBVfsrN4Z2PLwhIxpxPAoWfW/b5Kzpw== /just-extend/4.0.2: dev: false resolution: @@ -6943,11 +6869,13 @@ packages: webpack-sources: '>=1.0.0' resolution: integrity: sha512-Of/H79rZqm2aeg4RnP9SMSh19qkKemoLT5VaJV58uH5AxeYWEcBgGFs753JEJ/Hm6BPvQVfIlrrjoBwYj8p7Tw== - /lightercollective/0.1.0: + /lighthouse-logger/1.2.0: + dependencies: + debug: 2.6.9 + marky: 1.2.1 dev: false - hasBin: true resolution: - integrity: sha512-J9tg5uraYoQKaWbmrzDDexbG6hHnMcWS1qLYgJSWE+mpA3U5OCSeMUhb+K55otgZJ34oFdR0ECvdIb3xuO5JOQ== + integrity: sha512-wzUvdIeJZhRsG6gpZfmSCfysaxNEr43i+QT+Hie94wvHDKFLi4n7C2GqZ4sTC+PH5b5iktmXJvU87rWvhP3lHw== /load-bmfont/1.4.0: dependencies: buffer-equal: 0.0.1 @@ -7052,10 +6980,6 @@ packages: dev: false resolution: integrity: sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= - /lodash.debounce/4.0.8: - dev: false - resolution: - integrity: sha1-gteb/zCmfEAF/9XiUVMArZyk168= /lodash.defaults/4.2.0: dev: false resolution: @@ -7155,10 +7079,10 @@ packages: dev: false resolution: integrity: sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q== - /lolex/3.0.0: + /lolex/3.1.0: dev: false resolution: - integrity: sha512-hcnW80h3j2lbUfFdMArd5UPA/vxZJ+G8vobd+wg3nVEQA0EigStbYcrG030FJxL6xiDDPEkoMatV9xIh5OecQQ== + integrity: sha512-zFo5MgCJ0rZ7gQg69S4pqBsLURbFw11X68C18OcJjJQbqaXm2NoTrGl1IMM3TIz0/BnN1tIs2tzmmqvCsOMMjw== /long/4.0.0: dev: false resolution: @@ -7195,14 +7119,7 @@ packages: node: '>=0.10.0' resolution: integrity: sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== - /lowlight/1.11.0: - dependencies: - fault: 1.0.2 - highlight.js: 9.13.1 - dev: false - resolution: - integrity: sha512-xrGGN6XLL7MbTMdPD6NfWPwY43SNkjf/d0mecSx/CW36fUZTjRHEq0/Cdug3TWKtRXLWi7iMl1eP0olYxj/a4A== - /lru-cache/4.1.5: + /lru-cache/4.1.5: dependencies: pseudomap: 1.0.2 yallist: 2.1.2 @@ -7215,16 +7132,6 @@ packages: dev: false resolution: integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - /make-dir-cli/1.0.0: - dependencies: - make-dir: 1.3.0 - meow: 3.7.0 - dev: false - engines: - node: '>=4' - hasBin: true - resolution: - integrity: sha1-HHcOXHMRBsUcEQlbAr6qbJdJ3Fk= /make-dir/1.3.0: dependencies: pify: 3.0.0 @@ -7237,6 +7144,10 @@ packages: dev: false resolution: integrity: sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== + /mamacro/0.0.3: + dev: false + resolution: + integrity: sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA== /map-age-cleaner/0.1.3: dependencies: p-defer: 1.0.0 @@ -7286,6 +7197,10 @@ packages: hasBin: true resolution: integrity: sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg== + /marky/1.2.1: + dev: false + resolution: + integrity: sha512-md9k+Gxa3qLH6sUKpeC2CNkJK/Ld+bEz5X96nYwloqphQE0CKCVEKco/6jxEZixinqNdz5RFi/KaCyfbMDMAXQ== /math-expression-evaluator/1.2.17: dev: false resolution: @@ -7330,16 +7245,16 @@ packages: node: '>=4' resolution: integrity: sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y= - /mem/4.0.0: + /mem/4.1.0: dependencies: map-age-cleaner: 0.1.3 mimic-fn: 1.2.0 - p-is-promise: 1.1.0 + p-is-promise: 2.0.0 dev: false engines: node: '>=6' resolution: - integrity: sha512-WQxG/5xYc3tMbYLXoXPm81ET2WDULiU5FxbuIoNbJqLOOI8zehXFdZuiUEgfdrU2mVB1pxBZUGlYORSrpuJreA== + integrity: sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg== /memoize-one/4.0.3: dev: false resolution: @@ -7364,7 +7279,7 @@ packages: loud-rejection: 1.6.0 map-obj: 1.0.1 minimist: 1.2.0 - normalize-package-data: 2.4.0 + normalize-package-data: 2.5.0 object-assign: 4.1.1 read-pkg-up: 1.0.1 redent: 1.0.0 @@ -7381,7 +7296,7 @@ packages: loud-rejection: 1.6.0 minimist: 1.2.0 minimist-options: 3.0.2 - normalize-package-data: 2.4.0 + normalize-package-data: 2.5.0 read-pkg-up: 3.0.0 redent: 2.0.0 trim-newlines: 2.0.0 @@ -7486,20 +7401,20 @@ packages: hasBin: true resolution: integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== - /mime-db/1.37.0: + /mime-db/1.38.0: dev: false engines: node: '>= 0.6' resolution: - integrity: sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg== - /mime-types/2.1.21: + integrity: sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg== + /mime-types/2.1.22: dependencies: - mime-db: 1.37.0 + mime-db: 1.38.0 dev: false engines: node: '>= 0.6' resolution: - integrity: sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg== + integrity: sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog== /mime/1.4.1: dev: false hasBin: true @@ -7553,11 +7468,11 @@ packages: webpack: ^4.4.0 resolution: integrity: sha512-dqBanNfktnp2hwL2YguV9Jh91PFX7gu7nRLs4TGsbAfAG6WOtlynFRYzwDwmmeSb5uIwHo9nx1ta0f7vAZVp2w== - /mini-css-extract-plugin/0.4.5/webpack@4.28.4: + /mini-css-extract-plugin/0.4.5/webpack@4.29.6: dependencies: loader-utils: 1.2.3 schema-utils: 1.0.0 - webpack: 4.28.4 + webpack: 4.29.6 webpack-sources: 1.3.0 dev: false engines: @@ -7605,9 +7520,9 @@ packages: /mississippi/2.0.0: dependencies: concat-stream: 1.6.2 - duplexify: 3.6.1 + duplexify: 3.7.1 end-of-stream: 1.4.1 - flush-write-stream: 1.0.3 + flush-write-stream: 1.1.1 from2: 2.3.0 parallel-transform: 1.1.0 pump: 2.0.1 @@ -7622,9 +7537,9 @@ packages: /mississippi/3.0.0: dependencies: concat-stream: 1.6.2 - duplexify: 3.6.1 + duplexify: 3.7.1 end-of-stream: 1.4.1 - flush-write-stream: 1.0.3 + flush-write-stream: 1.1.1 from2: 2.3.0 parallel-transform: 1.1.0 pump: 3.0.0 @@ -7667,7 +7582,7 @@ packages: integrity: sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= /mobx-react/5.4.3: dependencies: - hoist-non-react-statics: 3.2.1 + hoist-non-react-statics: 3.3.0 react-lifecycles-compat: 3.0.4 dev: false peerDependencies: @@ -7675,11 +7590,11 @@ packages: react: ^0.13.0 || ^0.14.0 || ^15.0.0 || ^16.0.0 resolution: integrity: sha512-WC8yFlwvJ91hy8j6CrydAuFteUafcuvdITFQeHl3LRIf5ayfT/4W3M/byhEYD2BcJWejeXr8y4Rh2H26RunCRQ== - /mobx-react/5.4.3/mobx@5.8.0+react@16.7.0: + /mobx-react/5.4.3/mobx@5.9.0+react@16.8.3: dependencies: - hoist-non-react-statics: 3.2.1 - mobx: 5.8.0 - react: 16.7.0 + hoist-non-react-statics: 3.3.0 + mobx: 5.9.0 + react: 16.8.3 react-lifecycles-compat: 3.0.4 dev: false id: registry.npmjs.org/mobx-react/5.4.3 @@ -7688,10 +7603,10 @@ packages: react: ^0.13.0 || ^0.14.0 || ^15.0.0 || ^16.0.0 resolution: integrity: sha512-WC8yFlwvJ91hy8j6CrydAuFteUafcuvdITFQeHl3LRIf5ayfT/4W3M/byhEYD2BcJWejeXr8y4Rh2H26RunCRQ== - /mobx/5.8.0: + /mobx/5.9.0: dev: false resolution: - integrity: sha512-NsZB+9bF5j+nv9Qwk6bNeE3np26a4TbTGkMpOLf6o1zXoM9BtHPQn/00px4uZ2AXJXtQML5P4MEWdMm6icMIfQ== + integrity: sha512-D3mY1uM3H9BP5duk5tTanrOq92yqetYKsprPJWvkKDrbs+fro59xrpWX+vtr10YoLgJIFz+a4A8lI+4YtqmCUQ== /mocha-junit-reporter/1.18.0: dependencies: debug: 2.6.9 @@ -7722,7 +7637,7 @@ packages: dependencies: babel-runtime: 6.26.0 chalk: 2.4.2 - chokidar: 2.0.4 + chokidar: 2.1.2 glob-parent: 3.1.0 globby: 7.1.1 interpret: 1.2.0 @@ -7744,11 +7659,11 @@ packages: webpack: ^4.0.0 resolution: integrity: sha512-2ezbW0h5cYWr874F/hzytQCqINxk+GVelMY4xWTSHwwH1LrPAOzjlUljZ+/PhpaP6QeqYbL5x5vK/bnaXqkfEw== - /mocha-webpack/2.0.0-beta.0/mocha@5.2.0+webpack@4.28.4: + /mocha-webpack/2.0.0-beta.0/mocha@5.2.0+webpack@4.29.6: dependencies: babel-runtime: 6.26.0 chalk: 2.4.2 - chokidar: 2.0.4 + chokidar: 2.1.2 glob-parent: 3.1.0 globby: 7.1.1 interpret: 1.2.0 @@ -7763,7 +7678,7 @@ packages: source-map-support: 0.5.10 strip-ansi: 4.0.0 toposort: 1.0.7 - webpack: 4.28.4 + webpack: 4.29.6 yargs: 11.1.0 dev: false hasBin: true @@ -7792,11 +7707,11 @@ packages: hasBin: true resolution: integrity: sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== - /moment/2.23.0: + /moment/2.24.0: dev: false optional: true resolution: - integrity: sha512-3IE39bHVqFbWWaPOMHZF98Q9c3LDKGTmypMiTM2QygGXXElkFWIH7GxfmlwmY2vwa+wmNsoYZmG2iusf1ZjJoA== + integrity: sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== /moo/0.4.3: dev: false resolution: @@ -7835,7 +7750,7 @@ packages: /multiparty/4.2.1: dependencies: fd-slicer: 1.1.0 - http-errors: 1.7.1 + http-errors: 1.7.2 safe-buffer: 5.1.2 uid-safe: 2.1.5 dev: false @@ -7923,16 +7838,16 @@ packages: dev: false resolution: integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - /nise/1.4.8: + /nise/1.4.10: dependencies: '@sinonjs/formatio': 3.1.0 + '@sinonjs/text-encoding': 0.7.1 just-extend: 4.0.2 lolex: 2.7.5 path-to-regexp: 1.7.0 - text-encoding: 0.6.4 dev: false resolution: - integrity: sha512-kGASVhuL4tlAV0tvA34yJYZIVihrUt/5bDwpp4tTluigxUr2bBlJeDXmivb6NuEdFkqvdv/Ybb9dm16PSKUhtw== + integrity: sha512-sa0RRbj53dovjc7wombHmVli9ZihXbXCQ2uH3TNm03DyvOSIQbxg+pbqDKrk2oxMK1rtLGVlKxcB9rrc6X5YjA== /no-case/2.3.2: dependencies: lower-case: 1.1.4 @@ -7966,10 +7881,10 @@ packages: dev: false resolution: integrity: sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ== - /node-forge/0.7.6: + /node-forge/0.8.1: dev: false resolution: - integrity: sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw== + integrity: sha512-C/42HVb5eRtnDgRKOFx4bJH6LwbGQwbEHOC/trQwQSR6xWAUR/jlWoUJnxOmFTvdmDIZtjl2VH4dl3VpQuIz5g== /node-glob/1.2.0: dependencies: async: 1.5.2 @@ -8018,10 +7933,10 @@ packages: hasBin: true resolution: integrity: sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA== - /node-jose/1.1.0: + /node-jose/1.1.1: dependencies: base64url: 3.0.1 - es6-promise: 4.2.5 + es6-promise: 4.2.6 lodash.assign: 4.2.0 lodash.clone: 4.5.0 lodash.fill: 3.4.0 @@ -8033,12 +7948,12 @@ packages: lodash.pick: 4.4.0 lodash.uniq: 4.5.0 long: 4.0.0 - node-forge: 0.7.6 + node-forge: 0.8.1 uuid: 3.3.2 dev: false resolution: - integrity: sha512-Ux5MDElyiAlBQyOdFcwznK2TWMJbG8ZUfIZ28UtBvTB/VUz5HA/1WJV7s+YCah5NilPhkMkNi6xjnFRI+MQAVg== - /node-libs-browser/2.1.0: + integrity: sha512-GuMQE1UNZGoOX2mPoQ4pYYkynSrDNiq7CzY1YKoJBmFYBZMsAVoAmzpAKBz2uBLNnjet7A4idGXdE3Lck1gjqQ== + /node-libs-browser/2.2.0: dependencies: assert: 1.4.1 browserify-zlib: 0.2.0 @@ -8047,7 +7962,7 @@ packages: constants-browserify: 1.0.0 crypto-browserify: 3.12.0 domain-browser: 1.2.0 - events: 1.1.1 + events: 3.0.0 https-browserify: 1.0.0 os-browserify: 0.3.0 path-browserify: 0.0.0 @@ -8055,23 +7970,23 @@ packages: punycode: 1.4.1 querystring-es3: 0.2.1 readable-stream: 2.3.6 - stream-browserify: 2.0.1 + stream-browserify: 2.0.2 stream-http: 2.8.3 string_decoder: 1.2.0 timers-browserify: 2.0.10 tty-browserify: 0.0.0 url: 0.11.0 - util: 0.10.4 + util: 0.11.1 vm-browserify: 0.0.4 dev: false resolution: - integrity: sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg== - /node-releases/1.1.3: + integrity: sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA== + /node-releases/1.1.8: dependencies: semver: 5.6.0 dev: false resolution: - integrity: sha512-6VrvH7z6jqqNFY200kdB6HdzkgM96Oaj9v3dqGfgp6mF+cHmU4wyQKZ2/WPDRVoR0Jz9KqbamaBN0ZhdUaysUQ== + integrity: sha512-gQm+K9mGCiT/NXHy+V/ZZS1N/LOaGGqRAAJJs3X9Ah1g+CIbRcBgNyoNYQ+SEtcyAtB9KqDruu+fF7nWjsqRaA== /node-resemble-js/0.0.5: dependencies: pngjs: 2.2.0 @@ -8109,13 +8024,13 @@ packages: /nodeclient-spectre/1.0.5: dependencies: request: 2.88.0 - request-promise-native: /request-promise-native/1.0.5/request@2.88.0 + request-promise-native: /request-promise-native/1.0.7/request@2.88.0 dev: false resolution: integrity: sha512-bTVRrNgR8iIJrlj1c51JdRrp7eO5KIQzXjl0IuQPUI9THIOcsAwtO16kdr8r+Sp/ycXaFocyR3WfFNj7H8AocA== - /nodemon/1.18.9: + /nodemon/1.18.10: dependencies: - chokidar: 2.0.4 + chokidar: 2.1.2 debug: 3.2.6 ignore-by-default: 1.0.1 minimatch: 3.0.4 @@ -8131,7 +8046,7 @@ packages: hasBin: true requiresBuild: true resolution: - integrity: sha512-oj/eEVTEI47pzYAjGkpcNw0xYwTl4XSTUQv2NPQI6PpN3b75PhpuYk3Vb3U80xHCyM2Jm+1j68ULHXl4OR3Afw== + integrity: sha512-we51yBb1TfEvZamFchRgcfLbVYgg0xlGbyXmOtbBzDwxwgewYS/YbZ5tnlnsH51+AoSTTsT3A2E/FloUbtH8cQ== /nodent-runtime/3.2.1: dev: false requiresBuild: true @@ -8159,23 +8074,15 @@ packages: hasBin: true resolution: integrity: sha1-xkZdvwirzU2zWTF/eaxopkayj/k= - /nopt/4.0.1: - dependencies: - abbrev: 1.1.1 - osenv: 0.1.5 - dev: false - hasBin: true - resolution: - integrity: sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= - /normalize-package-data/2.4.0: + /normalize-package-data/2.5.0: dependencies: hosted-git-info: 2.7.1 - is-builtin-module: 1.0.0 + resolve: 1.10.0 semver: 5.6.0 validate-npm-package-license: 3.0.4 dev: false resolution: - integrity: sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw== + integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== /normalize-path/2.1.1: dependencies: remove-trailing-separator: 1.1.0 @@ -8184,6 +8091,12 @@ packages: node: '>=0.10.0' resolution: integrity: sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + /normalize-path/3.0.0: + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== /normalize-range/0.1.2: dev: false engines: @@ -8282,17 +8195,16 @@ packages: node: '>=0.10.0' resolution: integrity: sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - /nwsapi/2.0.9: + /nwsapi/2.1.1: dev: false resolution: - integrity: sha512-nlWFSCTYQcHk/6A9FFnfhKc14c3aFhfdNBXgo8Qgi9QTBu/qg3Ww+Uiz9wMzXd1T8GFxPc2QIHB6Qtf2XFryFQ== - /nyc/13.1.0: + integrity: sha512-T5GaA1J/d34AC8mkrFD2O0DR17kwJ702ZOtJOsS8RpbsQZVOC2/xYFb1i/cw+xdM54JIlMuojjDOYct8GIWtwg== + /nyc/13.3.0: bundledDependencies: - archy - arrify - caching-transform - convert-source-map - - debug-log - find-cache-dir - find-up - foreground-child @@ -8313,13 +8225,13 @@ packages: - yargs - yargs-parser dependencies: - istanbul-lib-instrument: 3.0.0 + istanbul-lib-instrument: 3.1.0 dev: false engines: node: '>=6' hasBin: true resolution: - integrity: sha512-3GyY6TpQ58z9Frpv4GMExE1SV2tAgYqC7HSy2omEhNiCT3mhT9NyiOvIE8zkbuJVFzmvvNTnE4h/7/wQae7xLg== + integrity: sha512-P+FwIuro2aFG6B0Esd9ZDWUd51uZrAEoGutqZxzrVmYl3qSfkLgcQpBPBjtDFsUQLFY1dvTQJPOyeqr8S9GF8w== /oauth-sign/0.9.0: dev: false resolution: @@ -8360,12 +8272,12 @@ packages: dev: false resolution: integrity: sha1-KKaq50KN0sOpLz2V8hM13SBOAzY= - /object-keys/1.0.12: + /object-keys/1.1.0: dev: false engines: node: '>= 0.4' resolution: - integrity: sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag== + integrity: sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg== /object-visit/1.0.1: dependencies: isobject: 3.0.1 @@ -8379,7 +8291,7 @@ packages: define-properties: 1.1.3 function-bind: 1.1.1 has-symbols: 1.0.0 - object-keys: 1.0.12 + object-keys: 1.1.0 dev: false engines: node: '>= 0.4' @@ -8396,6 +8308,17 @@ packages: node: '>= 0.4' resolution: integrity: sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA== + /object.fromentries/2.0.0: + dependencies: + define-properties: 1.1.3 + es-abstract: 1.13.0 + function-bind: 1.1.1 + has: 1.0.3 + dev: false + engines: + node: '>= 0.4' + resolution: + integrity: sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA== /object.getownpropertydescriptors/2.0.3: dependencies: define-properties: 1.1.3 @@ -8445,14 +8368,12 @@ packages: babel-polyfill: 6.26.0 resolution: integrity: sha512-buA9G0hlFjUwxoL/xuHunBtTgoICtJSojnZtATqMliUTKxPYAzHprOQ85Lj0hgF+Zv8lI/ViqaNFDG0Z5KCUKA== - /oidc-token-hash/3.0.1: - dependencies: - base64url: 3.0.1 + /oidc-token-hash/3.0.2: dev: false engines: node: '>=6.9.0' resolution: - integrity: sha512-oLnVSEcNZkw01sB5aFR+2iJmW4oyC1PIMJmd3FMBGDuPTy5ZtEuX5WNhKMRarJIMOq8NiOwIB6eJB9AhgYwBTg== + integrity: sha512-dTzp80/y/da+um+i+sOucNqiPpwRL7M/xPwj7pH1TFA2/bqQ+OK2sJahSXbemEoLtPkHcFLyhLhLWZa9yW5+RA== /on-finished/2.3.0: dependencies: ee-first: 1.1.1 @@ -8461,12 +8382,12 @@ packages: node: '>= 0.8' resolution: integrity: sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= - /on-headers/1.0.1: + /on-headers/1.0.2: dev: false engines: node: '>= 0.8' resolution: - integrity: sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c= + integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== /once/1.4.0: dependencies: wrappy: 1.0.2 @@ -8498,8 +8419,8 @@ packages: got: 8.3.2 lodash: 4.17.11 lru-cache: 4.1.5 - node-jose: 1.1.0 - oidc-token-hash: 3.0.1 + node-jose: 1.1.1 + oidc-token-hash: 3.0.2 p-any: 1.1.0 dev: false engines: @@ -8572,7 +8493,7 @@ packages: dependencies: execa: 1.0.0 lcid: 2.0.0 - mem: 4.0.0 + mem: 4.1.0 dev: false engines: node: '>=6' @@ -8623,6 +8544,12 @@ packages: node: '>=4' resolution: integrity: sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4= + /p-is-promise/2.0.0: + dev: false + engines: + node: '>=6' + resolution: + integrity: sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg== /p-limit/1.3.0: dependencies: p-try: 1.0.0 @@ -8724,16 +8651,17 @@ packages: node: '>= 0.4.0' resolution: integrity: sha1-dGoWdjgIOoYLDu9nMssn7UbDKXc= - /parse-asn1/5.1.1: + /parse-asn1/5.1.4: dependencies: asn1.js: 4.10.1 browserify-aes: 1.2.0 create-hash: 1.2.0 evp_bytestokey: 1.0.3 pbkdf2: 3.0.17 + safe-buffer: 5.1.2 dev: false resolution: - integrity: sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw== + integrity: sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw== /parse-bmfont-ascii/1.0.6: dev: false resolution: @@ -8749,7 +8677,7 @@ packages: dev: false resolution: integrity: sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ== - /parse-entities/1.2.0: + /parse-entities/1.2.1: dependencies: character-entities: 1.2.2 character-entities-legacy: 1.1.2 @@ -8759,7 +8687,7 @@ packages: is-hexadecimal: 1.0.2 dev: false resolution: - integrity: sha512-XXtDdOPLSB0sHecbEapQi6/58U/ODj/KWfIXmmMCJF/eRn8laX6LZbOyioMoETOOJoWRW8/qTSl5VQkUIfKM5g== + integrity: sha512-NBWYLQm1KSoDKk7GAHyioLTvCZ5QjdH/ASBBQTD3iLiAWJXS5bg1jEWI8nIJ+vgVvsceBVBcDGRWSo0KVQBvvg== /parse-glob/3.0.4: dependencies: glob-base: 0.3.0 @@ -8771,13 +8699,13 @@ packages: node: '>=0.10.0' resolution: integrity: sha1-ssN2z7EfNVE7rdFz7wu246OIORw= - /parse-headers/2.0.1: + /parse-headers/2.0.2: dependencies: for-each: 0.3.3 - trim: 0.0.1 + string.prototype.trim: 1.1.2 dev: false resolution: - integrity: sha1-aug6eqJanZtwCswoaYzR8e1+lTY= + integrity: sha512-/LypJhzFmyBIDYP9aDVgeyEb5sQfbfY5mnDq4hVhlQ69js87wXfmEI5V3xI6vvXasqebp0oCytYFLxsBVfCzSg== /parse-json/2.2.0: dependencies: error-ex: 1.3.2 @@ -8803,7 +8731,7 @@ packages: integrity: sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= /parse5/3.0.3: dependencies: - '@types/node': 10.10.3 + '@types/node': 10.12.18 dev: false resolution: integrity: sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA== @@ -9038,10 +8966,10 @@ packages: node: '>=4.0.0' resolution: integrity: sha512-1n3Z4p3IOxArEs1VRXnZ/RXdfEniAUS9jb68g58FIXMNkPJeZd+Qh4Uq7/e0LVxAQGos1eIUrqrt4FpjdnEd+Q== - /popper.js/1.14.6: + /popper.js/1.14.7: dev: false resolution: - integrity: sha512-AGwHGQBKumlk/MDfrSOf0JHhJCImdDMcGNoqKmKkU+68GFazv3CQ6q9r7Ja1sKDZmYWTckY/uLyEznheTDycnA== + integrity: sha512-4q1hNvoUre/8srWsH7hnoSJ5xVmIL4qgz+s4qf2TnJIMyZFUFMGH+9vE7mXynAlHSZ/NdTmmow86muD0myUkVQ== /portfinder/1.0.20: dependencies: async: 1.5.2 @@ -9254,12 +9182,12 @@ packages: dev: false resolution: integrity: sha1-7sbCpntsQSqNsgQud/6NpD+VwR0= - /postcss-prefix-selector/1.6.0: + /postcss-prefix-selector/1.7.1: dependencies: - postcss: 5.2.18 + postcss: 7.0.14 dev: false resolution: - integrity: sha1-tJWUnWOcYxRxRWSDJoUyFvPBCQA= + integrity: sha512-eNgr0xRk2IUPWpPkzfLL/Dlrewo4vd4vYpDj92/TEPNsLKTArAL/Y9VWFpJzrK8iPEpekPA06Ux97v8ByZ9QCQ== /postcss-reduce-idents/2.4.0: dependencies: postcss: 5.2.18 @@ -9321,7 +9249,7 @@ packages: /postcss/5.2.18: dependencies: chalk: 1.1.3 - js-base64: 2.5.0 + js-base64: 2.5.1 source-map: 0.5.7 supports-color: 3.2.3 dev: false @@ -9339,9 +9267,19 @@ packages: node: '>=4.0.0' resolution: integrity: sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag== + /postcss/7.0.14: + dependencies: + chalk: 2.4.2 + source-map: 0.6.1 + supports-color: 6.1.0 + dev: false + engines: + node: '>=6.0.0' + resolution: + integrity: sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg== /posthtml-parser/0.2.1: dependencies: - htmlparser2: 3.10.0 + htmlparser2: 3.10.1 isobject: 2.1.0 dev: false resolution: @@ -9422,7 +9360,7 @@ packages: integrity: sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk= /pretty-error/2.1.1: dependencies: - renderkid: 2.0.2 + renderkid: 2.0.3 utila: 0.4.0 dev: false resolution: @@ -9434,13 +9372,15 @@ packages: dev: false resolution: integrity: sha512-ZdWPGYAnYfcVP8yKA3zFjCn8s4/17TeYH28MXuC8vTp0o21eXjbFGcOAXZEaDaOFJjc3h2qa7HQNHNshhvoh2A== - /pretty-format/23.6.0: + /pretty-format/24.0.0: dependencies: - ansi-regex: 3.0.0 + ansi-regex: 4.0.0 ansi-styles: 3.2.1 dev: false + engines: + node: '>= 6' resolution: - integrity: sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw== + integrity: sha512-LszZaKG665djUcqg5ZQq+XzezHLKrxsA86ZABTozp+oNhkdqa+tG2dX4qa6ERl5c/sRDrAa3lHmwnvKoP+OG/g== /process-nextick-args/2.0.0: dev: false resolution: @@ -9486,23 +9426,20 @@ packages: dev: false resolution: integrity: sha512-EIyzM39FpVOMbqgzEHhxdrEhtOSDOtjMZQ0M6iVfCE+kWNgCkAyOdnuCWqfmflylftfadU6FkiMgHZA2kUzwRw== - /prop-types/15.6.2: + /prop-types/15.7.2: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 + react-is: 16.8.3 dev: false resolution: - integrity: sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ== + integrity: sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== /propagate/1.0.0: dev: false engines: '0': node >= 0.8.1 resolution: integrity: sha1-AMLa7t2iDofjeCs0Stuhzd1q1wk= - /proto-list/1.2.4: - dev: false - resolution: - integrity: sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= /proxy-addr/2.0.4: dependencies: forwarded: 0.1.2 @@ -9537,8 +9474,8 @@ packages: bn.js: 4.11.8 browserify-rsa: 4.0.1 create-hash: 1.2.0 - parse-asn1: 5.1.1 - randombytes: 2.0.6 + parse-asn1: 5.1.4 + randombytes: 2.1.0 safe-buffer: 5.1.2 dev: false resolution: @@ -9559,7 +9496,7 @@ packages: integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== /pumpify/1.5.1: dependencies: - duplexify: 3.6.1 + duplexify: 3.7.1 inherits: 2.0.3 pump: 2.0.1 dev: false @@ -9691,15 +9628,15 @@ packages: node: '>= 0.10.0' resolution: integrity: sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw== - /randombytes/2.0.6: + /randombytes/2.1.0: dependencies: safe-buffer: 5.1.2 dev: false resolution: - integrity: sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A== + integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== /randomfill/1.0.4: dependencies: - randombytes: 2.0.6 + randombytes: 2.1.0 safe-buffer: 5.1.2 dev: false resolution: @@ -9742,10 +9679,10 @@ packages: react-dom: ^16.0.0 resolution: integrity: sha512-a4OGEjWTOCJ64GvXwTXEHDEQ8iXZ8Xk30izUKyAVmP5q+Q2AOTMq/qUcHQwpL+XJ81/vTPTIhR//ELjjzYGwFQ== - /react-data-grid/6.0.1/react-dom@16.7.0+react@16.7.0: + /react-data-grid/6.0.1/react-dom@16.8.3+react@16.8.3: dependencies: - react: 16.7.0 - react-dom: /react-dom/16.7.0/react@16.7.0 + react: 16.8.3 + react-dom: /react-dom/16.8.3/react@16.8.3 dev: false id: registry.npmjs.org/react-data-grid/6.0.1 peerDependencies: @@ -9773,7 +9710,7 @@ packages: loader-utils: 1.1.0 opn: 5.4.0 pkg-up: 2.0.0 - react-error-overlay: 5.1.2 + react-error-overlay: 5.1.3 recursive-readdir: 2.2.2 shell-quote: 1.6.1 sockjs-client: 1.1.5 @@ -9813,14 +9750,14 @@ packages: react: '>= 16.3' resolution: integrity: sha1-xKF8cBCeRW2tiQa+g45u6PMrBrU= - /react-dnd/5.0.0/react@16.7.0: + /react-dnd/5.0.0/react@16.8.3: dependencies: dnd-core: 4.0.5 hoist-non-react-statics: 2.5.5 invariant: 2.2.4 lodash: 4.17.11 - react: 16.7.0 - recompose: /recompose/0.27.1/react@16.7.0 + react: 16.8.3 + recompose: /recompose/0.27.1/react@16.8.3 shallowequal: 1.1.0 dev: false id: registry.npmjs.org/react-dnd/5.0.0 @@ -9828,60 +9765,60 @@ packages: react: '>= 16.3' resolution: integrity: sha1-xKF8cBCeRW2tiQa+g45u6PMrBrU= - /react-dom/16.7.0: + /react-dom/16.8.3: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 - prop-types: 15.6.2 - scheduler: 0.12.0 + prop-types: 15.7.2 + scheduler: 0.13.3 dev: false peerDependencies: react: ^16.0.0 resolution: - integrity: sha512-D0Ufv1ExCAmF38P2Uh1lwpminZFRXEINJe53zRAbm4KPwSyd6DY/uDoS0Blj9jvPpn1+wivKpZYc8aAAN/nAkg== - /react-dom/16.7.0/react@16.7.0: + integrity: sha512-ttMem9yJL4/lpItZAQ2NTFAbV7frotHk5DZEHXUOws2rMmrsvh1Na7ThGT0dTzUIl6pqTOi5tYREfL8AEna3lA== + /react-dom/16.8.3/react@16.8.3: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 - prop-types: 15.6.2 - react: 16.7.0 - scheduler: 0.12.0 + prop-types: 15.7.2 + react: 16.8.3 + scheduler: 0.13.3 dev: false - id: registry.npmjs.org/react-dom/16.7.0 + id: registry.npmjs.org/react-dom/16.8.3 peerDependencies: react: ^16.0.0 resolution: - integrity: sha512-D0Ufv1ExCAmF38P2Uh1lwpminZFRXEINJe53zRAbm4KPwSyd6DY/uDoS0Blj9jvPpn1+wivKpZYc8aAAN/nAkg== - /react-error-overlay/5.1.2: + integrity: sha512-ttMem9yJL4/lpItZAQ2NTFAbV7frotHk5DZEHXUOws2rMmrsvh1Na7ThGT0dTzUIl6pqTOi5tYREfL8AEna3lA== + /react-error-overlay/5.1.3: dev: false resolution: - integrity: sha512-7kEBKwU9R8fKnZJBRa5RSIfay4KJwnYvKB6gODGicUmDSAhQJ7Tdnll5S0RLtYrzRfMVXlqYw61rzrSpP4ThLQ== + integrity: sha512-GoqeM3Xadie7XUApXOjkY3Qhs8RkwB/Za4WMedBGrOKH1eTuKGyoAECff7jiVonJchOx6KZ9i8ILO5XIoHB+Tg== /react-highlight-words/0.14.0: dependencies: highlight-words-core: 1.2.2 memoize-one: 4.0.3 - prop-types: 15.6.2 + prop-types: 15.7.2 dev: false peerDependencies: react: ^0.14.0 || ^15.0.0 || ^16.0.0-0 resolution: integrity: sha512-DNp1zAIiNqs1nunFv5K67ca0KVSKihbmpZLNwqQj61yMK6zf8YHoGrpLlEt7WEqj1227FOm41FuXiv9KZgcwdA== - /react-highlight-words/0.14.0/react@16.7.0: + /react-highlight-words/0.14.0/react@16.8.3: dependencies: highlight-words-core: 1.2.2 memoize-one: 4.0.3 - prop-types: 15.6.2 - react: 16.7.0 + prop-types: 15.7.2 + react: 16.8.3 dev: false id: registry.npmjs.org/react-highlight-words/0.14.0 peerDependencies: react: ^0.14.0 || ^15.0.0 || ^16.0.0-0 resolution: integrity: sha512-DNp1zAIiNqs1nunFv5K67ca0KVSKihbmpZLNwqQj61yMK6zf8YHoGrpLlEt7WEqj1227FOm41FuXiv9KZgcwdA== - /react-is/16.7.0: + /react-is/16.8.3: dev: false resolution: - integrity: sha512-Z0VRQdF4NPDoI0tsXVMLkJLiwEBa+RP66g0xDHxgxysxSoCUccSten4RTF/UFvZF1dZvZ9Zu1sx+MDXwcOR34g== + integrity: sha512-Y4rC1ZJmsxxkkPuMLwvKvlL1Zfpbcu+Bf4ZigkHup3v9EfdYhAlWAaVyA19olXq2o2mGn0w+dFKvk3pVVlYcIA== /react-lifecycles-compat/3.0.4: dev: false resolution: @@ -9889,7 +9826,7 @@ packages: /react-markdown/3.6.0: dependencies: mdast-add-list-metadata: 1.0.1 - prop-types: 15.6.2 + prop-types: 15.7.2 remark-parse: 5.0.0 unified: 6.2.0 unist-util-visit: 1.4.0 @@ -9899,11 +9836,11 @@ packages: react: ^15.0.0 || ^16.0.0 resolution: integrity: sha512-TV0wQDHHPCEeKJHWXFfEAKJ8uSEsJ9LgrMERkXx05WV/3q6Ig+59KDNaTmjcoqlCpE/sH5PqqLMh4t0QWKrJ8Q== - /react-markdown/3.6.0/react@16.7.0: + /react-markdown/3.6.0/react@16.8.3: dependencies: mdast-add-list-metadata: 1.0.1 - prop-types: 15.6.2 - react: 16.7.0 + prop-types: 15.7.2 + react: 16.8.3 remark-parse: 5.0.0 unified: 6.2.0 unist-util-visit: 1.4.0 @@ -9916,12 +9853,12 @@ packages: integrity: sha512-TV0wQDHHPCEeKJHWXFfEAKJ8uSEsJ9LgrMERkXx05WV/3q6Ig+59KDNaTmjcoqlCpE/sH5PqqLMh4t0QWKrJ8Q== /react-redux/5.1.1: dependencies: - '@babel/runtime': 7.2.0 - hoist-non-react-statics: 3.2.1 + '@babel/runtime': 7.3.4 + hoist-non-react-statics: 3.3.0 invariant: 2.2.4 loose-envify: 1.4.0 - prop-types: 15.6.2 - react-is: 16.7.0 + prop-types: 15.7.2 + react-is: 16.8.3 react-lifecycles-compat: 3.0.4 dev: false peerDependencies: @@ -9929,15 +9866,15 @@ packages: redux: ^2.0.0 || ^3.0.0 || ^4.0.0-0 resolution: integrity: sha512-LE7Ned+cv5qe7tMV5BPYkGQ5Lpg8gzgItK07c67yHvJ8t0iaD9kPFPAli/mYkiyJYrs2pJgExR2ZgsGqlrOApg== - /react-redux/5.1.1/react@16.7.0+redux@4.0.1: + /react-redux/5.1.1/react@16.8.3+redux@4.0.1: dependencies: - '@babel/runtime': 7.2.0 - hoist-non-react-statics: 3.2.1 + '@babel/runtime': 7.3.4 + hoist-non-react-statics: 3.3.0 invariant: 2.2.4 loose-envify: 1.4.0 - prop-types: 15.6.2 - react: 16.7.0 - react-is: 16.7.0 + prop-types: 15.7.2 + react: 16.8.3 + react-is: 16.8.3 react-lifecycles-compat: 3.0.4 redux: 4.0.1 dev: false @@ -9951,19 +9888,19 @@ packages: dependencies: lodash: 4.17.11 lodash-es: 4.17.11 - prop-types: 15.6.2 + prop-types: 15.7.2 resize-observer-polyfill: 1.5.1 dev: false peerDependencies: react: ^16.0.0 resolution: integrity: sha512-T96I8Iqa1hGWyooeFA2Sl6FdPoMhXWINfEKg2/EJLxhP37+/94VNuyuyz9CRqpmApD83IWRR+lbB3r0ADMoKJg== - /react-resize-detector/3.4.0/react@16.7.0: + /react-resize-detector/3.4.0/react@16.8.3: dependencies: lodash: 4.17.11 lodash-es: 4.17.11 - prop-types: 15.6.2 - react: 16.7.0 + prop-types: 15.7.2 + react: 16.8.3 resize-observer-polyfill: 1.5.1 dev: false id: registry.npmjs.org/react-resize-detector/3.4.0 @@ -9976,23 +9913,23 @@ packages: history: 4.7.2 invariant: 2.2.4 loose-envify: 1.4.0 - prop-types: 15.6.2 + prop-types: 15.7.2 react-router: 4.3.1 - warning: 4.0.2 + warning: 4.0.3 dev: false peerDependencies: react: '>=15' resolution: integrity: sha512-c/MlywfxDdCp7EnB7YfPMOfMD3tOtIjrQlj/CKfNMBxdmpJP8xcz5P/UAFn3JbnQCNUxsHyVVqllF9LhgVyFCA== - /react-router-dom/4.3.1/react@16.7.0: + /react-router-dom/4.3.1/react@16.8.3: dependencies: history: 4.7.2 invariant: 2.2.4 loose-envify: 1.4.0 - prop-types: 15.6.2 - react: 16.7.0 - react-router: /react-router/4.3.1/react@16.7.0 - warning: 4.0.2 + prop-types: 15.7.2 + react: 16.8.3 + react-router: /react-router/4.3.1/react@16.8.3 + warning: 4.0.3 dev: false id: registry.npmjs.org/react-router-dom/4.3.1 peerDependencies: @@ -10006,23 +9943,23 @@ packages: invariant: 2.2.4 loose-envify: 1.4.0 path-to-regexp: 1.7.0 - prop-types: 15.6.2 - warning: 4.0.2 + prop-types: 15.7.2 + warning: 4.0.3 dev: false peerDependencies: react: '>=15' resolution: integrity: sha512-yrvL8AogDh2X42Dt9iknk4wF4V8bWREPirFfS9gLU1huk6qK41sg7Z/1S81jjTrGHxa3B8R3J6xIkDAA6CVarg== - /react-router/4.3.1/react@16.7.0: + /react-router/4.3.1/react@16.8.3: dependencies: history: 4.7.2 hoist-non-react-statics: 2.5.5 invariant: 2.2.4 loose-envify: 1.4.0 path-to-regexp: 1.7.0 - prop-types: 15.6.2 - react: 16.7.0 - warning: 4.0.2 + prop-types: 15.7.2 + react: 16.8.3 + warning: 4.0.3 dev: false id: registry.npmjs.org/react-router/4.3.1 peerDependencies: @@ -10032,82 +9969,75 @@ packages: /react-split-pane/0.1.77: dependencies: inline-style-prefixer: 3.0.8 - prop-types: 15.6.2 + prop-types: 15.7.2 react-style-proptype: 3.2.2 dev: false resolution: integrity: sha512-xq0PPsbkNI9xEd6yTrGPr7hzf6XfIgnsxuUEdRJELq+kLPHMsO3ymFCjhiYP35wlDPn9W46+rHDsJd7LFYteMw== /react-style-proptype/3.2.2: dependencies: - prop-types: 15.6.2 + prop-types: 15.7.2 dev: false resolution: integrity: sha512-ywYLSjNkxKHiZOqNlso9PZByNEY+FTyh3C+7uuziK0xFXu9xzdyfHwg4S9iyiRRoPCR4k2LqaBBsWVmSBwCWYQ== - /react-syntax-highlighter/3.0.2/react@16.7.0: - dependencies: - babel-runtime: 6.26.0 - highlight.js: 9.8.0 - lowlight: 1.11.0 - react: 16.7.0 - dev: false - id: registry.npmjs.org/react-syntax-highlighter/3.0.2 - peerDependencies: - react: '>= 0.14.0' - resolution: - integrity: sha1-d8VWOFu/cdwul0upa/3Q7e9cMZI= - /react-test-renderer/16.7.0: + /react-test-renderer/16.8.3: dependencies: object-assign: 4.1.1 - prop-types: 15.6.2 - react-is: 16.7.0 - scheduler: 0.12.0 + prop-types: 15.7.2 + react-is: 16.8.3 + scheduler: 0.13.3 dev: false peerDependencies: react: ^16.0.0 resolution: - integrity: sha512-tFbhSjknSQ6+ttzmuGdv+SjQfmvGcq3PFKyPItohwhhOBmRoTf1We3Mlt3rJtIn85mjPXOkKV+TaKK4irvk9Yg== - /react-test-renderer/16.7.0/react@16.7.0: + integrity: sha512-rjJGYebduKNZH0k1bUivVrRLX04JfIQ0FKJLPK10TAb06XWhfi4gTobooF9K/DEFNW98iGac3OSxkfIJUN9Mdg== + /react-test-renderer/16.8.3/react@16.8.3: dependencies: object-assign: 4.1.1 - prop-types: 15.6.2 - react: 16.7.0 - react-is: 16.7.0 - scheduler: 0.12.0 + prop-types: 15.7.2 + react: 16.8.3 + react-is: 16.8.3 + scheduler: 0.13.3 dev: false - id: registry.npmjs.org/react-test-renderer/16.7.0 + id: registry.npmjs.org/react-test-renderer/16.8.3 peerDependencies: react: ^16.0.0 resolution: - integrity: sha512-tFbhSjknSQ6+ttzmuGdv+SjQfmvGcq3PFKyPItohwhhOBmRoTf1We3Mlt3rJtIn85mjPXOkKV+TaKK4irvk9Yg== - /react-testing-library/5.4.4: + integrity: sha512-rjJGYebduKNZH0k1bUivVrRLX04JfIQ0FKJLPK10TAb06XWhfi4gTobooF9K/DEFNW98iGac3OSxkfIJUN9Mdg== + /react-testing-library/5.9.0: dependencies: - dom-testing-library: 3.16.3 + '@babel/runtime': 7.3.4 + dom-testing-library: 3.16.8 dev: false engines: node: '>=8' peerDependencies: + react: '*' react-dom: '*' resolution: - integrity: sha512-/TiERZ+URSNhZQfjrUXh0VLsiLSmhqP1WP+2e2wWqWqrRIWpcAxrfuBxzlT75LYMDNmicEikaXJqRDi/pqCEDg== - /react-testing-library/5.4.4/react-dom@16.7.0: + integrity: sha512-T303PJZvrLKeeiPpjmMD1wxVpzEg9yI0qteH/cUvpFqNHOzPe3yN+Pu+jo9JlxuTMvVGPAmCAcgZ3sEtEDpJUQ== + /react-testing-library/5.9.0/react-dom@16.8.3+react@16.8.3: dependencies: - dom-testing-library: 3.16.3 - react-dom: /react-dom/16.7.0/react@16.7.0 + '@babel/runtime': 7.3.4 + dom-testing-library: 3.16.8 + react: 16.8.3 + react-dom: /react-dom/16.8.3/react@16.8.3 dev: false engines: node: '>=8' - id: registry.npmjs.org/react-testing-library/5.4.4 + id: registry.npmjs.org/react-testing-library/5.9.0 peerDependencies: + react: '*' react-dom: '*' resolution: - integrity: sha512-/TiERZ+URSNhZQfjrUXh0VLsiLSmhqP1WP+2e2wWqWqrRIWpcAxrfuBxzlT75LYMDNmicEikaXJqRDi/pqCEDg== + integrity: sha512-T303PJZvrLKeeiPpjmMD1wxVpzEg9yI0qteH/cUvpFqNHOzPe3yN+Pu+jo9JlxuTMvVGPAmCAcgZ3sEtEDpJUQ== /react-virtualized/9.21.0: dependencies: babel-runtime: 6.26.0 classnames: 2.2.6 dom-helpers: 3.4.0 loose-envify: 1.4.0 - prop-types: 15.6.2 + prop-types: 15.7.2 react-lifecycles-compat: 3.0.4 dev: false peerDependencies: @@ -10115,15 +10045,15 @@ packages: react-dom: ^15.3.0 || ^16.0.0-alpha resolution: integrity: sha512-duKD2HvO33mqld4EtQKm9H9H0p+xce1c++2D5xn59Ma7P8VT7CprfAe5hwjd1OGkyhqzOZiTMlTal7LxjH5yBQ== - /react-virtualized/9.21.0/react-dom@16.7.0+react@16.7.0: + /react-virtualized/9.21.0/react-dom@16.8.3+react@16.8.3: dependencies: babel-runtime: 6.26.0 classnames: 2.2.6 dom-helpers: 3.4.0 loose-envify: 1.4.0 - prop-types: 15.6.2 - react: 16.7.0 - react-dom: /react-dom/16.7.0/react@16.7.0 + prop-types: 15.7.2 + react: 16.8.3 + react-dom: /react-dom/16.8.3/react@16.8.3 react-lifecycles-compat: 3.0.4 dev: false id: registry.npmjs.org/react-virtualized/9.21.0 @@ -10132,17 +10062,17 @@ packages: react-dom: ^15.3.0 || ^16.0.0-alpha resolution: integrity: sha512-duKD2HvO33mqld4EtQKm9H9H0p+xce1c++2D5xn59Ma7P8VT7CprfAe5hwjd1OGkyhqzOZiTMlTal7LxjH5yBQ== - /react/16.7.0: + /react/16.8.3: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 - prop-types: 15.6.2 - scheduler: 0.12.0 + prop-types: 15.7.2 + scheduler: 0.13.3 dev: false engines: node: '>=0.10.0' resolution: - integrity: sha512-StCz3QY8lxTb5cl2HJxjwLFOXPIFQp+p+hxQfc8WE0QiLfCtIlKj8/+5tjjKm8uSTlAW+fCPaavGFS06V9Ar3A== + integrity: sha512-3UoSIsEq8yTJuSu0luO1QQWYbgGEILm+eJl2QN/VLDi7hL+EN18M3q3oVZwmVzzBJ3DkM7RMdRwBmZZ+b4IzSA== /read-chunk/1.0.1: dev: false engines: @@ -10170,7 +10100,7 @@ packages: /read-pkg/1.1.0: dependencies: load-json-file: 1.1.0 - normalize-package-data: 2.4.0 + normalize-package-data: 2.5.0 path-type: 1.1.0 dev: false engines: @@ -10180,22 +10110,13 @@ packages: /read-pkg/3.0.0: dependencies: load-json-file: 4.0.0 - normalize-package-data: 2.4.0 + normalize-package-data: 2.5.0 path-type: 3.0.0 dev: false engines: node: '>=4' resolution: integrity: sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= - /readable-stream/1.0.34: - dependencies: - core-util-is: 1.0.2 - inherits: 2.0.3 - isarray: 0.0.1 - string_decoder: 0.10.31 - dev: false - resolution: - integrity: sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= /readable-stream/1.1.14: dependencies: core-util-is: 1.0.2 @@ -10243,7 +10164,7 @@ packages: integrity: sha1-xYDXfvLPyHUrEySYBg3JeTp6wBw= /rechoir/0.6.2: dependencies: - resolve: 1.9.0 + resolve: 1.10.0 dev: false engines: node: '>= 0.10' @@ -10262,13 +10183,13 @@ packages: react: ^0.14.0 || ^15.0.0 || ^16.0.0 resolution: integrity: sha512-p7xsyi/rfNjHfdP7vPU02uSFa+Q1eHhjKrvO+3+kRP4Ortj+MxEmpmd+UQtBGM2D2iNAjzNI5rCyBKp9Ob5McA== - /recompose/0.27.1/react@16.7.0: + /recompose/0.27.1/react@16.8.3: dependencies: babel-runtime: 6.26.0 change-emitter: 0.1.6 fbjs: 0.8.17 hoist-non-react-statics: 2.5.5 - react: 16.7.0 + react: 16.8.3 react-lifecycles-compat: 3.0.4 symbol-observable: 1.2.0 dev: false @@ -10317,15 +10238,6 @@ packages: dev: false resolution: integrity: sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk= - /redux/3.7.2: - dependencies: - lodash: 4.17.11 - lodash-es: 4.17.11 - loose-envify: 1.4.0 - symbol-observable: 1.2.0 - dev: false - resolution: - integrity: sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A== /redux/4.0.1: dependencies: loose-envify: 1.4.0 @@ -10419,7 +10331,7 @@ packages: is-whitespace-character: 1.0.2 is-word-character: 1.0.2 markdown-escapes: 1.0.2 - parse-entities: 1.2.0 + parse-entities: 1.2.1 repeat-string: 1.6.1 state-toggle: 1.0.1 trim: 0.0.1 @@ -10435,16 +10347,16 @@ packages: dev: false resolution: integrity: sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - /renderkid/2.0.2: + /renderkid/2.0.3: dependencies: css-select: 1.2.0 dom-converter: 0.2.0 - htmlparser2: 3.3.0 + htmlparser2: 3.10.1 strip-ansi: 3.0.1 utila: 0.4.0 dev: false resolution: - integrity: sha512-FsygIxevi1jSiPY9h7vZmBFUbAOcbYm9UwyiLNdVsLRs/5We9Ob5NMPbGYUTWiLq5L+ezlVdE0A8bbME5CWTpg== + integrity: sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA== /repeat-element/1.1.3: dev: false engines: @@ -10471,7 +10383,7 @@ packages: node: '>= 0.10' resolution: integrity: sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs= - /request-promise-core/1.1.1: + /request-promise-core/1.1.2: dependencies: lodash: 4.17.11 dev: false @@ -10480,45 +10392,73 @@ packages: peerDependencies: request: ^2.34 resolution: - integrity: sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY= - /request-promise-core/1.1.1/request@2.88.0: + integrity: sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag== + /request-promise-core/1.1.2/request@2.88.0: dependencies: lodash: 4.17.11 request: 2.88.0 dev: false engines: node: '>=0.10.0' - id: registry.npmjs.org/request-promise-core/1.1.1 + id: registry.npmjs.org/request-promise-core/1.1.2 peerDependencies: request: ^2.34 resolution: - integrity: sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY= - /request-promise-native/1.0.5: + integrity: sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag== + /request-promise-native/1.0.7: dependencies: - request-promise-core: 1.1.1 + request-promise-core: 1.1.2 stealthy-require: 1.1.1 - tough-cookie: 3.0.0 + tough-cookie: 2.5.0 dev: false engines: node: '>=0.12.0' peerDependencies: request: ^2.34 resolution: - integrity: sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU= - /request-promise-native/1.0.5/request@2.88.0: + integrity: sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w== + /request-promise-native/1.0.7/request@2.88.0: dependencies: request: 2.88.0 - request-promise-core: /request-promise-core/1.1.1/request@2.88.0 + request-promise-core: /request-promise-core/1.1.2/request@2.88.0 stealthy-require: 1.1.1 - tough-cookie: 3.0.0 + tough-cookie: 2.5.0 dev: false engines: node: '>=0.12.0' - id: registry.npmjs.org/request-promise-native/1.0.5 + id: registry.npmjs.org/request-promise-native/1.0.7 + peerDependencies: + request: ^2.34 + resolution: + integrity: sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w== + /request-promise/4.2.4: + dependencies: + bluebird: 3.5.3 + request-promise-core: 1.1.2 + stealthy-require: 1.1.1 + tough-cookie: 2.5.0 + dev: false + engines: + node: '>=0.10.0' + peerDependencies: + request: ^2.34 + resolution: + integrity: sha512-8wgMrvE546PzbR5WbYxUQogUnUDfM0S7QIFZMID+J73vdFARkFy+HElj4T+MWYhpXwlLp0EQ8Zoj8xUA0he4Vg== + /request-promise/4.2.4/request@2.88.0: + dependencies: + bluebird: 3.5.3 + request: 2.88.0 + request-promise-core: /request-promise-core/1.1.2/request@2.88.0 + stealthy-require: 1.1.1 + tough-cookie: 2.5.0 + dev: false + engines: + node: '>=0.10.0' + id: registry.npmjs.org/request-promise/4.2.4 peerDependencies: request: ^2.34 resolution: - integrity: sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU= + integrity: sha512-8wgMrvE546PzbR5WbYxUQogUnUDfM0S7QIFZMID+J73vdFARkFy+HElj4T+MWYhpXwlLp0EQ8Zoj8xUA0he4Vg== /request/2.88.0: dependencies: aws-sign2: 0.7.0 @@ -10533,7 +10473,7 @@ packages: is-typedarray: 1.0.0 isstream: 0.1.2 json-stringify-safe: 5.0.1 - mime-types: 2.1.21 + mime-types: 2.1.22 oauth-sign: 0.9.0 performance-now: 2.1.0 qs: 6.5.2 @@ -10605,12 +10545,18 @@ packages: dev: false resolution: integrity: sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= - /resolve/1.9.0: + /resolve/1.10.0: dependencies: path-parse: 1.0.6 dev: false resolution: - integrity: sha512-TZNye00tI67lwYvzxCxHGjwTNlUV70io54/Ed4j6PscB8xVfuBJpRenI/o6dVk0cY0PYTY27AgCoGGxRnYuItQ== + integrity: sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg== + /resolve/1.8.1: + dependencies: + path-parse: 1.0.6 + dev: false + resolution: + integrity: sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA== /responselike/1.0.2: dependencies: lowercase-keys: 1.0.1 @@ -10703,14 +10649,14 @@ packages: dev: false resolution: integrity: sha1-pfE/957zt0D+MKqAP7CfmIBdR4I= - /rxjs/6.3.3: + /rxjs/6.4.0: dependencies: tslib: 1.9.3 dev: false engines: npm: '>=2.0.0' resolution: - integrity: sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw== + integrity: sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw== /safe-buffer/5.1.2: dev: false resolution: @@ -10755,7 +10701,7 @@ packages: webpack: ^3.0.0 || ^4.0.0 resolution: integrity: sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w== - /sass-loader/7.1.0/webpack@4.28.4: + /sass-loader/7.1.0/webpack@4.29.6: dependencies: clone-deep: 2.0.2 loader-utils: 1.2.3 @@ -10763,7 +10709,7 @@ packages: neo-async: 2.6.0 pify: 3.0.0 semver: 5.6.0 - webpack: 4.28.4 + webpack: 4.29.6 dev: false engines: node: '>= 6.9.0 || >= 8.9.0' @@ -10774,7 +10720,7 @@ packages: integrity: sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w== /save/2.3.3: dependencies: - async: 2.6.1 + async: 2.6.2 event-stream: 4.0.1 lodash.assign: 4.2.0 mingo: 1.3.3 @@ -10785,13 +10731,13 @@ packages: dev: false resolution: integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - /scheduler/0.12.0: + /scheduler/0.13.3: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 dev: false resolution: - integrity: sha512-t7MBR28Akcp4Jm+QoR63XgAi9YgCUmgvDHqf5otgAj4QvdoBE4ImCX0ffehefePPG+aitiYHp0g/mW6s4Tp+dw== + integrity: sha512-UxN5QRYWtpR1egNWzJcVLk8jlegxAugswQc984lD3kU7NuobsO37/sRfbpTdBjtnD5TBNFA2Q2oLV5+UmPSmEQ== /schema-utils/0.3.0: dependencies: ajv: 5.5.2 @@ -10802,8 +10748,8 @@ packages: integrity: sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8= /schema-utils/0.4.7: dependencies: - ajv: 6.7.0 - ajv-keywords: /ajv-keywords/3.2.0/ajv@6.7.0 + ajv: 6.9.2 + ajv-keywords: /ajv-keywords/3.4.0/ajv@6.9.2 dev: false engines: node: '>= 4' @@ -10811,9 +10757,9 @@ packages: integrity: sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ== /schema-utils/1.0.0: dependencies: - ajv: 6.7.0 - ajv-errors: /ajv-errors/1.0.1/ajv@6.7.0 - ajv-keywords: /ajv-keywords/3.2.0/ajv@6.7.0 + ajv: 6.9.2 + ajv-errors: /ajv-errors/1.0.1/ajv@6.9.2 + ajv-keywords: /ajv-keywords/3.4.0/ajv@6.9.2 dev: false engines: node: '>= 4' @@ -10821,7 +10767,7 @@ packages: integrity: sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== /scss-tokenizer/0.2.3: dependencies: - js-base64: 2.5.0 + js-base64: 2.5.1 source-map: 0.4.4 dev: false resolution: @@ -10889,7 +10835,7 @@ packages: debug: 2.6.9 escape-html: 1.0.3 http-errors: 1.6.3 - mime-types: 2.1.21 + mime-types: 2.1.22 parseurl: 1.3.2 dev: false engines: @@ -10945,6 +10891,10 @@ packages: dev: false resolution: integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + /setprototypeof/1.1.1: + dev: false + resolution: + integrity: sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== /sha.js/2.4.11: dependencies: inherits: 2.0.3 @@ -11001,10 +10951,6 @@ packages: hasBin: true resolution: integrity: sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A== - /sigmund/1.0.1: - dev: false - resolution: - integrity: sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA= /signal-exit/3.0.2: dev: false resolution: @@ -11022,10 +10968,10 @@ packages: sinon: '>=4.0.0 <8.0.0' resolution: integrity: sha512-r2JhDY7gbbmh5z3Q62pNbrjxZdOAjpsqW/8yxAZRSqLZqowmfGZPGUZPFf3UX36NLis0cv8VEM5IJh9HgkSOAA== - /sinon-chai/3.3.0/chai@4.2.0+sinon@7.2.2: + /sinon-chai/3.3.0/chai@4.2.0+sinon@7.2.5: dependencies: chai: 4.2.0 - sinon: 7.2.2 + sinon: 7.2.5 dev: false id: registry.npmjs.org/sinon-chai/3.3.0 peerDependencies: @@ -11033,18 +10979,18 @@ packages: sinon: '>=4.0.0 <8.0.0' resolution: integrity: sha512-r2JhDY7gbbmh5z3Q62pNbrjxZdOAjpsqW/8yxAZRSqLZqowmfGZPGUZPFf3UX36NLis0cv8VEM5IJh9HgkSOAA== - /sinon/7.2.2: + /sinon/7.2.5: dependencies: '@sinonjs/commons': 1.3.0 '@sinonjs/formatio': 3.1.0 - '@sinonjs/samsam': 3.0.2 + '@sinonjs/samsam': 3.2.0 diff: 3.5.0 - lolex: 3.0.0 - nise: 1.4.8 + lolex: 3.1.0 + nise: 1.4.10 supports-color: 5.5.0 dev: false resolution: - integrity: sha512-WLagdMHiEsrRmee3jr6IIDntOF4kbI6N2pfbi8wkv50qaUQcBglkzkjtoOEbeJ2vf1EsrHhLI+5Ny8//WHdMoA== + integrity: sha512-1c2KK6g5NQr9XNYCEcUbeFtBpKZD1FXEw0VX7gNhWUBtkchguT2lNdS7XmS7y64OpQWfSNeeV/f8py3NNcQ63Q== /slash/1.0.0: dev: false engines: @@ -11135,7 +11081,7 @@ packages: integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== /source-map-loader/0.2.4: dependencies: - async: 2.6.1 + async: 2.6.2 loader-utils: 1.2.3 dev: false engines: @@ -11268,7 +11214,7 @@ packages: dev: false resolution: integrity: sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - /sshpk/1.16.0: + /sshpk/1.16.1: dependencies: asn1: 0.2.3 assert-plus: 1.0.0 @@ -11284,7 +11230,7 @@ packages: node: '>=0.10.0' hasBin: true resolution: - integrity: sha512-Zhev35/y7hRMcID/upReIvRse+I9SVhyVre/KTJSJQWMz3C3+G+HpO7m1wK/yckEtujKZ7dS4hkVxAnmHaIGVQ== + integrity: sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== /ssri/5.3.0: dependencies: safe-buffer: 5.1.2 @@ -11301,12 +11247,12 @@ packages: dev: false resolution: integrity: sha512-Qe8QntFrrpWTnHwvwj2FZTgv+PKIsp0B9VxLzLLbSpPXWOgRgc5LVj/aTiSfK1RqIeF9jeC1UeOH8Q8y60A7og== - /static-eval/2.0.0: + /static-eval/2.0.2: dependencies: - escodegen: 1.11.0 + escodegen: 1.11.1 dev: false resolution: - integrity: sha512-6flshd3F1Gwm+Ksxq463LtFd1liC77N/PX1FVVc3OzL3hAmo2fwHFbuArkcfi7s9rTNsLEhcRmXGFZhlgy40uw== + integrity: sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg== /static-extend/0.1.2: dependencies: define-property: 0.2.5 @@ -11340,13 +11286,13 @@ packages: node: '>=0.10.0' resolution: integrity: sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= - /stream-browserify/2.0.1: + /stream-browserify/2.0.2: dependencies: inherits: 2.0.3 readable-stream: 2.3.6 dev: false resolution: - integrity: sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds= + integrity: sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== /stream-combiner/0.2.2: dependencies: duplexer: 0.1.1 @@ -11585,6 +11531,14 @@ packages: node: '>=4' resolution: integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + /supports-color/6.1.0: + dependencies: + has-flag: 3.0.0 + dev: false + engines: + node: '>=6' + resolution: + integrity: sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== /svg-baker-runtime/1.4.0: dependencies: deepmerge: 1.3.2 @@ -11603,7 +11557,7 @@ packages: merge-options: 1.0.1 micromatch: 3.1.0 postcss: 5.2.18 - postcss-prefix-selector: 1.6.0 + postcss-prefix-selector: 1.7.1 posthtml-rename-id: 1.0.11 posthtml-svg-mode: 1.0.3 query-string: 4.3.4 @@ -11637,6 +11591,10 @@ packages: node: '>=6' resolution: integrity: sha512-tnL7qj5ArgSYjXePzx+pZpDDzz2rMhjYdzaTjiuBz6nbPPgf2uOvO8mWj8wf/0Iv6Szd406fUYMDIyT3XnwEww== + /svg.js/2.7.1: + dev: false + resolution: + integrity: sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA== /svgo/0.7.2: dependencies: coa: 1.0.4 @@ -11664,12 +11622,12 @@ packages: webpack: ^1 || ^2 || ^2.1.0-beta || ^2.2.0-beta || ^3 || ^4 resolution: integrity: sha512-K6E52DbYyzGNXGyv2LhI2Duomr3t/2FFMmnGdHZ1Ruk3ulFHDMASJtg3WpA3CXlWODZx189tTaOIO5mWkSKyVg== - /sw-precache-webpack-plugin/0.11.5/webpack@4.28.4: + /sw-precache-webpack-plugin/0.11.5/webpack@4.29.6: dependencies: del: 3.0.0 sw-precache: 5.2.1 uglify-es: 3.3.9 - webpack: 4.28.4 + webpack: 4.29.6 dev: false engines: node: '>=4.0.0' @@ -11681,7 +11639,7 @@ packages: /sw-precache/5.2.1: dependencies: dom-urls: 1.1.0 - es6-promise: 4.2.5 + es6-promise: 4.2.6 glob: 7.1.3 lodash.defaults: 4.2.0 lodash.template: 4.4.0 @@ -11741,13 +11699,6 @@ packages: dev: false resolution: integrity: sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE= - /tcp-port-used/1.0.1: - dependencies: - debug: 4.1.0 - is2: 2.0.1 - dev: false - resolution: - integrity: sha512-rwi5xJeU6utXoEIiMvVBMc9eJ2/ofzB+7nLOdnZuFTmNCLqRiQh2sMG9MqCxHU/69VC/Fwp5dV9306Qd54ll1Q== /term-size/1.2.0: dependencies: execa: 0.7.0 @@ -11756,14 +11707,14 @@ packages: node: '>=4' resolution: integrity: sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk= - /terser-webpack-plugin/1.2.1: + /terser-webpack-plugin/1.2.3: dependencies: cacache: 11.3.2 find-cache-dir: 2.0.0 schema-utils: 1.0.0 serialize-javascript: 1.6.1 source-map: 0.6.1 - terser: 3.14.1 + terser: 3.16.1 webpack-sources: 1.3.0 worker-farm: 1.6.0 dev: false @@ -11772,23 +11723,18 @@ packages: peerDependencies: webpack: ^4.0.0 resolution: - integrity: sha512-GGSt+gbT0oKcMDmPx4SRSfJPE1XaN3kQRWG4ghxKQw9cn5G9x6aCKSsgYdvyM0na9NJ4Drv0RG6jbBByZ5CMjw== - /terser/3.14.1: + integrity: sha512-GOK7q85oAb/5kE12fMuLdn2btOS9OBZn4VsecpHDywoUC/jLhSAKOiYo0ezx7ss2EXPMzyEWFoE0s1WLE+4+oA== + /terser/3.16.1: dependencies: commander: 2.17.1 source-map: 0.6.1 source-map-support: 0.5.10 dev: false engines: - node: '>=4.0.0' + node: '>=6.0.0' hasBin: true resolution: - integrity: sha512-NSo3E99QDbYSMeJaEk9YW2lTg3qS9V0aKGlb+PlOrei1X02r1wSBHCNX/O+yeTRFSWPKPIGj6MqvvdqV4rnVGw== - /text-encoding/0.6.4: - deprecated: no longer maintained - dev: false - resolution: - integrity: sha1-45mpgiV6J22uQou5KEXLcb3CbRk= + integrity: sha512-JDJjgleBROeek2iBcSNzOHLKsB/MdDf+E/BOAJ0Tk9r7p9/fVobfv7LMJ/g/k3v9SXdmjZnIlFd5nfn/Rt0Xow== /text-table/0.2.0: dev: false resolution: @@ -11915,7 +11861,7 @@ packages: integrity: sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== /tooltip.js/1.3.1: dependencies: - popper.js: 1.14.6 + popper.js: 1.14.7 dev: false resolution: integrity: sha512-nZQGUM3YfjUlCVw3+RqYcCubIDj5NmtPsQ6Xj1NZvii1KSnPxuQ9d6xvNgsNmsmilwhNcBOmHLhwuSDVYINm0g== @@ -11948,16 +11894,6 @@ packages: node: '>=0.8' resolution: integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - /tough-cookie/3.0.0: - dependencies: - ip-regex: 3.0.0 - psl: 1.1.31 - punycode: 2.1.1 - dev: false - engines: - node: '>=6' - resolution: - integrity: sha512-LHMvg+RBP/mAVNqVbOX8t+iJ+tqhBA/t49DuI7+IDAWHrASnesqSu1vWbKB7UrE2yk+HMFUBMadRGMkB4VCfog== /tr46/1.0.1: dependencies: punycode: 2.1.1 @@ -12041,7 +11977,7 @@ packages: hasBin: true resolution: integrity: sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw== - /tsconfig-paths/3.7.0: + /tsconfig-paths/3.8.0: dependencies: '@types/json5': 0.0.29 deepmerge: 2.2.1 @@ -12050,7 +11986,7 @@ packages: strip-bom: 3.0.0 dev: false resolution: - integrity: sha512-7iE+Q/2E1lgvxD+c0Ot+GFFmgmfIjt/zCayyruXkXQ84BLT85gHXy0WSoQSiuFX9+d+keE/jiON7notV74ZY+A== + integrity: sha512-zZEYFo4sjORK8W58ENkRn9s+HmQFkkwydDG7My5s/fnfr2YYCaiyXe/HBUcIgU8epEKOXwiahOO+KZYjiXlWyQ== /tslib/1.9.3: dev: false resolution: @@ -12066,13 +12002,13 @@ packages: typescript: '>=2.1.4 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >=3.1.0-dev || >=3.2.0-dev || >=3.3.0-dev || >=3.4.0-dev' resolution: integrity: sha512-6BNDBbZh2K0ibRXe70Mkl9gfVttxQ3t3hqV1BRDfpIcjrUoOgD946iH4SrXp+IggDgeMs3dJORjD5tqL5j4jXg== - /tslint-consistent-codestyle/1.15.0/tslint@5.12.1+typescript@3.1.6: + /tslint-consistent-codestyle/1.15.0/tslint@5.13.0+typescript@3.2.4: dependencies: - '@fimbul/bifrost': /@fimbul/bifrost/0.17.0/tslint@5.12.1+typescript@3.1.6 + '@fimbul/bifrost': /@fimbul/bifrost/0.17.0/tslint@5.13.0+typescript@3.2.4 tslib: 1.9.3 - tslint: /tslint/5.12.1/typescript@3.1.6 - tsutils: /tsutils/2.29.0/typescript@3.1.6 - typescript: 3.1.6 + tslint: /tslint/5.13.0/typescript@3.2.4 + tsutils: /tsutils/2.29.0/typescript@3.2.4 + typescript: 3.2.4 dev: false id: registry.npmjs.org/tslint-consistent-codestyle/1.15.0 peerDependencies: @@ -12092,21 +12028,21 @@ packages: tslint: '>=4.0.0' resolution: integrity: sha512-Me9Qf/87BOfCY8uJJw+J7VMF4U8WiMXKLhKKKugMydF0xMhMOt9wo2mjYTNhwbF9H7SHh8PAIwRG8roisTNekQ== - /tslint-loader/3.6.0/tslint@5.12.1: + /tslint-loader/3.6.0/tslint@5.13.0: dependencies: loader-utils: 1.2.3 mkdirp: 0.5.1 object-assign: 4.1.1 rimraf: 2.6.3 semver: 5.6.0 - tslint: /tslint/5.12.1/typescript@3.1.6 + tslint: /tslint/5.13.0/typescript@3.2.4 dev: false id: registry.npmjs.org/tslint-loader/3.6.0 peerDependencies: tslint: '>=4.0.0' resolution: integrity: sha512-Me9Qf/87BOfCY8uJJw+J7VMF4U8WiMXKLhKKKugMydF0xMhMOt9wo2mjYTNhwbF9H7SHh8PAIwRG8roisTNekQ== - /tslint/5.12.1: + /tslint/5.13.0: dependencies: babel-code-frame: 6.26.0 builtin-modules: 1.1.1 @@ -12114,9 +12050,10 @@ packages: commander: 2.19.0 diff: 3.5.0 glob: 7.1.3 - js-yaml: 3.12.1 + js-yaml: 3.12.2 minimatch: 3.0.4 - resolve: 1.9.0 + mkdirp: 0.5.1 + resolve: 1.10.0 semver: 5.6.0 tslib: 1.9.3 tsutils: 2.29.0 @@ -12125,10 +12062,10 @@ packages: node: '>=4.8.0' hasBin: true peerDependencies: - typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev' + typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev' resolution: - integrity: sha512-sfodBHOucFg6egff8d1BvuofoOQ/nOeYNfbp7LDlKBcLNrL3lmS5zoiDGyOMdT7YsEXAwWpTdAHwOGOc8eRZAw== - /tslint/5.12.1/typescript@3.1.6: + integrity: sha512-ECOOQRxXCYnUUePG5h/+Z1Zouobk3KFpIHA9aKBB/nnMxs97S1JJPDGt5J4cGm1y9U9VmVlfboOxA8n1kSNzGw== + /tslint/5.13.0/typescript@3.2.4: dependencies: babel-code-frame: 6.26.0 builtin-modules: 1.1.1 @@ -12136,22 +12073,23 @@ packages: commander: 2.19.0 diff: 3.5.0 glob: 7.1.3 - js-yaml: 3.12.1 + js-yaml: 3.12.2 minimatch: 3.0.4 - resolve: 1.9.0 + mkdirp: 0.5.1 + resolve: 1.10.0 semver: 5.6.0 tslib: 1.9.3 - tsutils: /tsutils/2.29.0/typescript@3.1.6 - typescript: 3.1.6 + tsutils: /tsutils/2.29.0/typescript@3.2.4 + typescript: 3.2.4 dev: false engines: node: '>=4.8.0' hasBin: true - id: registry.npmjs.org/tslint/5.12.1 + id: registry.npmjs.org/tslint/5.13.0 peerDependencies: - typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev' + typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev' resolution: - integrity: sha512-sfodBHOucFg6egff8d1BvuofoOQ/nOeYNfbp7LDlKBcLNrL3lmS5zoiDGyOMdT7YsEXAwWpTdAHwOGOc8eRZAw== + integrity: sha512-ECOOQRxXCYnUUePG5h/+Z1Zouobk3KFpIHA9aKBB/nnMxs97S1JJPDGt5J4cGm1y9U9VmVlfboOxA8n1kSNzGw== /tsutils/2.29.0: dependencies: tslib: 1.9.3 @@ -12160,17 +12098,17 @@ packages: typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev' resolution: integrity: sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== - /tsutils/2.29.0/typescript@3.1.6: + /tsutils/2.29.0/typescript@3.2.4: dependencies: tslib: 1.9.3 - typescript: 3.1.6 + typescript: 3.2.4 dev: false id: registry.npmjs.org/tsutils/2.29.0 peerDependencies: typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev' resolution: integrity: sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== - /tsutils/3.7.0: + /tsutils/3.8.0: dependencies: tslib: 1.9.3 dev: false @@ -12179,19 +12117,19 @@ packages: peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev' resolution: - integrity: sha512-n+e+3q7Jx2kfZw7tjfI9axEIWBY0sFMOlC+1K70X0SeXpO/UYSB+PN+E9tIJNqViB7oiXQdqD7dNchnvoneZew== - /tsutils/3.7.0/typescript@3.1.6: + integrity: sha512-XQdPhgcoTbCD8baXC38PQ0vpTZ8T3YrE+vR66YIj/xvDt1//8iAhafpIT/4DmvzzC1QFapEImERu48Pa01dIUA== + /tsutils/3.8.0/typescript@3.2.4: dependencies: tslib: 1.9.3 - typescript: 3.1.6 + typescript: 3.2.4 dev: false engines: node: '>= 6' - id: registry.npmjs.org/tsutils/3.7.0 + id: registry.npmjs.org/tsutils/3.8.0 peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev' resolution: - integrity: sha512-n+e+3q7Jx2kfZw7tjfI9axEIWBY0sFMOlC+1K70X0SeXpO/UYSB+PN+E9tIJNqViB7oiXQdqD7dNchnvoneZew== + integrity: sha512-XQdPhgcoTbCD8baXC38PQ0vpTZ8T3YrE+vR66YIj/xvDt1//8iAhafpIT/4DmvzzC1QFapEImERu48Pa01dIUA== /tty-browserify/0.0.0: dev: false resolution: @@ -12223,7 +12161,7 @@ packages: /type-is/1.6.16: dependencies: media-typer: 0.3.0 - mime-types: 2.1.21 + mime-types: 2.1.22 dev: false engines: node: '>= 0.6' @@ -12239,21 +12177,36 @@ packages: node: '>= 4' resolution: integrity: sha1-bcJDPnjti+qOiHo6zeLzF4W9Yic= - /typedoc-plugin-external-module-name/1.1.3: + /typedoc-plugin-external-module-name/1.1.1: dev: false peerDependencies: typedoc: '>=0.7 <1.0' resolution: - integrity: sha512-/VMawTW4NnUUsgq0o8O37y9MmXFaOCDrH1dvDg7SZUS5ZSpUPSILVWwGJP+7g4I8vKZ5bBKZKHfPIEA4xUC+PQ== - /typedoc-plugin-external-module-name/1.1.3/typedoc@0.11.1: + integrity: sha512-Erc0MyKDGYIN7kSlIjgifVieZEogg8YwOH8bjW6RXb0y44hVeTER0UoW6Rh+n8leg6rvlPdnH092aFfcd8kRlA== + /typedoc-plugin-external-module-name/1.1.1/typedoc@0.11.1: dependencies: typedoc: 0.11.1 dev: false - id: registry.npmjs.org/typedoc-plugin-external-module-name/1.1.3 + id: registry.npmjs.org/typedoc-plugin-external-module-name/1.1.1 peerDependencies: typedoc: '>=0.7 <1.0' resolution: - integrity: sha512-/VMawTW4NnUUsgq0o8O37y9MmXFaOCDrH1dvDg7SZUS5ZSpUPSILVWwGJP+7g4I8vKZ5bBKZKHfPIEA4xUC+PQ== + integrity: sha512-Erc0MyKDGYIN7kSlIjgifVieZEogg8YwOH8bjW6RXb0y44hVeTER0UoW6Rh+n8leg6rvlPdnH092aFfcd8kRlA== + /typedoc-plugin-internal-external/1.0.10: + dev: false + peerDependencies: + typedoc: ^0.8.0 + resolution: + integrity: sha512-RxNVuG53s1XjRU9AureonuWvVmkeANXtfM0rnmqrLlmrWJXUVPDJs9L3Ds87YX9fvVVDc9evT8iSQquP8/tNNQ== + /typedoc-plugin-internal-external/1.0.10/typedoc@0.11.1: + dependencies: + typedoc: 0.11.1 + dev: false + id: registry.npmjs.org/typedoc-plugin-internal-external/1.0.10 + peerDependencies: + typedoc: ^0.8.0 + resolution: + integrity: sha512-RxNVuG53s1XjRU9AureonuWvVmkeANXtfM0rnmqrLlmrWJXUVPDJs9L3Ds87YX9fvVVDc9evT8iSQquP8/tNNQ== /typedoc/0.11.1: dependencies: '@types/fs-extra': 5.0.1 @@ -12264,8 +12217,8 @@ packages: '@types/minimatch': 3.0.3 '@types/shelljs': 0.7.8 fs-extra: 5.0.0 - handlebars: 4.0.12 - highlight.js: 9.13.1 + handlebars: 4.1.0 + highlight.js: 9.14.2 lodash: 4.17.11 marked: 0.3.19 minimatch: 3.0.4 @@ -12321,6 +12274,13 @@ packages: hasBin: true resolution: integrity: sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA== + /typescript/3.2.4: + dev: false + engines: + node: '>=4.2.0' + hasBin: true + resolution: + integrity: sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg== /ua-parser-js/0.7.19: dev: false resolution: @@ -12362,7 +12322,7 @@ packages: webpack: ^2.0.0 || ^3.0.0 || ^4.0.0 resolution: integrity: sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw== - /uglifyjs-webpack-plugin/1.3.0/webpack@4.28.4: + /uglifyjs-webpack-plugin/1.3.0/webpack@4.29.6: dependencies: cacache: 10.0.4 find-cache-dir: 1.0.0 @@ -12370,7 +12330,7 @@ packages: serialize-javascript: 1.6.1 source-map: 0.6.1 uglify-es: 3.3.9 - webpack: 4.28.4 + webpack: 4.29.6 webpack-sources: 1.3.0 worker-farm: 1.6.0 dev: false @@ -12576,12 +12536,12 @@ packages: webpack: ^3.0.0 || ^4.0.0 resolution: integrity: sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg== - /url-loader/1.1.2/webpack@4.28.4: + /url-loader/1.1.2/webpack@4.29.6: dependencies: loader-utils: 1.2.3 mime: 2.4.0 schema-utils: 1.0.0 - webpack: 4.28.4 + webpack: 4.29.6 dev: false engines: node: '>= 6.9.0' @@ -12674,6 +12634,12 @@ packages: dev: false resolution: integrity: sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== + /util/0.11.1: + dependencies: + inherits: 2.0.3 + dev: false + resolution: + integrity: sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== /utila/0.4.0: dev: false resolution: @@ -12700,6 +12666,12 @@ packages: dev: false resolution: integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + /validator/8.2.0: + dev: false + engines: + node: '>= 0.10' + resolution: + integrity: sha512-Yw5wW34fSv5spzTXNkokD6S6/Oq92d8q/t14TqsS3fAiA1RYnxSFSIZ+CY3n6PGGRCq5HhJTSepQvFUS2QUDxA== /value-equal/0.4.0: dev: false resolution: @@ -12765,15 +12737,15 @@ packages: dev: false resolution: integrity: sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w= - /warning/4.0.2: + /warning/4.0.3: dependencies: loose-envify: 1.4.0 dev: false resolution: - integrity: sha512-wbTp09q/9C+jJn4KKJfJfoS6VleK/Dti0yqWSm6KMvJ4MRCXFQNapHuJXutJIrWV0Cf4AhTdeIe4qdKHR1+Hug== + integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== /watchpack/1.6.0: dependencies: - chokidar: 2.0.4 + chokidar: 2.1.2 graceful-fs: 4.1.15 neo-async: 2.6.0 dev: false @@ -12793,23 +12765,13 @@ packages: chromedriver: ^2.31.0 resolution: integrity: sha512-/b9rVXwXnXJKSLqcNpW/tC2U7BVM3Lzj2NuT2MWPLnGmzHZ6JZpTlSLoRSW5bVsaZcolEb22m0pAPLxwy6reBQ== - /wdio-chromedriver-service/0.1.5/chromedriver@2.45.0: - dependencies: - chromedriver: 2.45.0 - fs-extra: 0.30.0 - dev: false - id: registry.npmjs.org/wdio-chromedriver-service/0.1.5 - peerDependencies: - chromedriver: ^2.31.0 - resolution: - integrity: sha512-/b9rVXwXnXJKSLqcNpW/tC2U7BVM3Lzj2NuT2MWPLnGmzHZ6JZpTlSLoRSW5bVsaZcolEb22m0pAPLxwy6reBQ== /wdio-dot-reporter/0.0.10: dev: false resolution: integrity: sha512-A0TCk2JdZEn3M1DSG9YYbNRcGdx/YRw19lTiRpgwzH4qqWkO/oRDZRmi3Snn4L2j54KKTfPalBhlOtc8fojVgg== /wdio-junit-reporter/0.4.4: dependencies: - junit-report-builder: 1.3.1 + junit-report-builder: 1.3.2 lodash.get: 4.4.2 mkdirp: 0.5.1 dev: false @@ -12943,17 +12905,15 @@ packages: dev: false resolution: integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== - /webpack-cli/3.2.1: + /webpack-cli/3.2.3: dependencies: chalk: 2.4.2 cross-spawn: 6.0.5 enhanced-resolve: 4.1.0 findup-sync: 2.0.0 global-modules: 1.0.0 - global-modules-path: 2.3.1 import-local: 2.0.0 interpret: 1.2.0 - lightercollective: 0.1.0 loader-utils: 1.2.3 supports-color: 5.5.0 v8-compile-cache: 2.0.2 @@ -12964,36 +12924,32 @@ packages: hasBin: true peerDependencies: webpack: 4.x.x - requiresBuild: true resolution: - integrity: sha512-jeJveHwz/vwpJ3B8bxEL5a/rVKIpRNJDsKggfKnxuYeohNDW4Y/wB9N/XHJA093qZyS0r6mYL+/crLsIol4WKA== - /webpack-cli/3.2.1/webpack@4.28.4: + integrity: sha512-Ik3SjV6uJtWIAN5jp5ZuBMWEAaP5E4V78XJ2nI+paFPh8v4HPSwo/myN0r29Xc/6ZKnd2IdrAlpSgNOu2CDQ6Q== + /webpack-cli/3.2.3/webpack@4.29.6: dependencies: chalk: 2.4.2 cross-spawn: 6.0.5 enhanced-resolve: 4.1.0 findup-sync: 2.0.0 global-modules: 1.0.0 - global-modules-path: 2.3.1 import-local: 2.0.0 interpret: 1.2.0 - lightercollective: 0.1.0 loader-utils: 1.2.3 supports-color: 5.5.0 v8-compile-cache: 2.0.2 - webpack: 4.28.4 + webpack: 4.29.6 yargs: 12.0.5 dev: false engines: node: '>=6.11.5' hasBin: true - id: registry.npmjs.org/webpack-cli/3.2.1 + id: registry.npmjs.org/webpack-cli/3.2.3 peerDependencies: webpack: 4.x.x - requiresBuild: true resolution: - integrity: sha512-jeJveHwz/vwpJ3B8bxEL5a/rVKIpRNJDsKggfKnxuYeohNDW4Y/wB9N/XHJA093qZyS0r6mYL+/crLsIol4WKA== - /webpack-dev-middleware/3.4.0: + integrity: sha512-Ik3SjV6uJtWIAN5jp5ZuBMWEAaP5E4V78XJ2nI+paFPh8v4HPSwo/myN0r29Xc/6ZKnd2IdrAlpSgNOu2CDQ6Q== + /webpack-dev-middleware/3.6.0: dependencies: memory-fs: 0.4.1 mime: 2.4.0 @@ -13005,36 +12961,36 @@ packages: peerDependencies: webpack: ^4.0.0 resolution: - integrity: sha512-Q9Iyc0X9dP9bAsYskAVJ/hmIZZQwf/3Sy4xCAZgL5cUkjZmUZLt4l5HpbST/Pdgjn3u6pE7u5OdGd1apgzRujA== - /webpack-dev-middleware/3.4.0/webpack@4.28.4: + integrity: sha512-oeXA3m+5gbYbDBGo4SvKpAHJJEGMoekUbHgo1RK7CP1sz7/WOSeu/dWJtSTk+rzDCLkPwQhGocgIq6lQqOyOwg== + /webpack-dev-middleware/3.6.0/webpack@4.29.6: dependencies: memory-fs: 0.4.1 mime: 2.4.0 range-parser: 1.2.0 - webpack: 4.28.4 + webpack: 4.29.6 webpack-log: 2.0.0 dev: false engines: node: '>= 6' - id: registry.npmjs.org/webpack-dev-middleware/3.4.0 + id: registry.npmjs.org/webpack-dev-middleware/3.6.0 peerDependencies: webpack: ^4.0.0 resolution: - integrity: sha512-Q9Iyc0X9dP9bAsYskAVJ/hmIZZQwf/3Sy4xCAZgL5cUkjZmUZLt4l5HpbST/Pdgjn3u6pE7u5OdGd1apgzRujA== - /webpack-dev-server/3.1.14: + integrity: sha512-oeXA3m+5gbYbDBGo4SvKpAHJJEGMoekUbHgo1RK7CP1sz7/WOSeu/dWJtSTk+rzDCLkPwQhGocgIq6lQqOyOwg== + /webpack-dev-server/3.2.1: dependencies: ansi-html: 0.0.7 bonjour: 3.5.0 - chokidar: 2.0.4 + chokidar: 2.1.2 compression: 1.7.3 connect-history-api-fallback: 1.6.0 - debug: 3.2.6 + debug: 4.1.1 del: 3.0.0 express: 4.16.4 html-entities: 1.2.1 - http-proxy-middleware: 0.18.0 + http-proxy-middleware: 0.19.1 import-local: 2.0.0 - internal-ip: 3.0.1 + internal-ip: 4.2.0 ip: 1.1.5 killable: 1.0.1 loglevel: 1.6.1 @@ -13048,9 +13004,9 @@ packages: sockjs-client: 1.3.0 spdy: 4.0.0 strip-ansi: 3.0.1 - supports-color: 5.5.0 + supports-color: 6.1.0 url: 0.11.0 - webpack-dev-middleware: 3.4.0 + webpack-dev-middleware: 3.6.0 webpack-log: 2.0.0 yargs: 12.0.2 dev: false @@ -13060,21 +13016,21 @@ packages: peerDependencies: webpack: ^4.0.0 resolution: - integrity: sha512-mGXDgz5SlTxcF3hUpfC8hrQ11yhAttuUQWf1Wmb+6zo3x6rb7b9mIfuQvAPLdfDRCGRGvakBWHdHOa0I9p/EVQ== - /webpack-dev-server/3.1.14/webpack@4.28.4: + integrity: sha512-sjuE4mnmx6JOh9kvSbPYw3u/6uxCLHNWfhWaIPwcXWsvWOPN+nc5baq4i9jui3oOBRXGonK9+OI0jVkaz6/rCw== + /webpack-dev-server/3.2.1/webpack@4.29.6: dependencies: ansi-html: 0.0.7 bonjour: 3.5.0 - chokidar: 2.0.4 + chokidar: 2.1.2 compression: 1.7.3 connect-history-api-fallback: 1.6.0 - debug: 3.2.6 + debug: 4.1.1 del: 3.0.0 express: 4.16.4 html-entities: 1.2.1 - http-proxy-middleware: 0.18.0 + http-proxy-middleware: 0.19.1 import-local: 2.0.0 - internal-ip: 3.0.1 + internal-ip: 4.2.0 ip: 1.1.5 killable: 1.0.1 loglevel: 1.6.1 @@ -13088,21 +13044,21 @@ packages: sockjs-client: 1.3.0 spdy: 4.0.0 strip-ansi: 3.0.1 - supports-color: 5.5.0 + supports-color: 6.1.0 url: 0.11.0 - webpack: 4.28.4 - webpack-dev-middleware: /webpack-dev-middleware/3.4.0/webpack@4.28.4 + webpack: 4.29.6 + webpack-dev-middleware: /webpack-dev-middleware/3.6.0/webpack@4.29.6 webpack-log: 2.0.0 yargs: 12.0.2 dev: false engines: node: '>= 6.11.5' hasBin: true - id: registry.npmjs.org/webpack-dev-server/3.1.14 + id: registry.npmjs.org/webpack-dev-server/3.2.1 peerDependencies: webpack: ^4.0.0 resolution: - integrity: sha512-mGXDgz5SlTxcF3hUpfC8hrQ11yhAttuUQWf1Wmb+6zo3x6rb7b9mIfuQvAPLdfDRCGRGvakBWHdHOa0I9p/EVQ== + integrity: sha512-sjuE4mnmx6JOh9kvSbPYw3u/6uxCLHNWfhWaIPwcXWsvWOPN+nc5baq4i9jui3oOBRXGonK9+OI0jVkaz6/rCw== /webpack-log/2.0.0: dependencies: ansi-colors: 3.2.3 @@ -13124,12 +13080,12 @@ packages: webpack: 2 || 3 || 4 resolution: integrity: sha512-FZcnB3MMQ0CT0aU1+LItwywXWAixLTGUEAtN0fw15dScf2LudQwheLPUCj+QMhDlwZT+9ysfKqUFTcfUGc8bXg== - /webpack-manifest-plugin/2.0.3/webpack@4.28.4: + /webpack-manifest-plugin/2.0.3/webpack@4.29.6: dependencies: fs-extra: 0.30.0 lodash: 4.17.11 tapable: 1.1.1 - webpack: 4.28.4 + webpack: 4.29.6 dev: false engines: node: '>=6.11.5' @@ -13155,16 +13111,16 @@ packages: dev: false resolution: integrity: sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA== - /webpack/4.28.4: - dependencies: - '@webassemblyjs/ast': 1.7.11 - '@webassemblyjs/helper-module-context': 1.7.11 - '@webassemblyjs/wasm-edit': 1.7.11 - '@webassemblyjs/wasm-parser': 1.7.11 - acorn: 5.7.3 - acorn-dynamic-import: 3.0.0 - ajv: 6.7.0 - ajv-keywords: /ajv-keywords/3.2.0/ajv@6.7.0 + /webpack/4.29.6: + dependencies: + '@webassemblyjs/ast': 1.8.5 + '@webassemblyjs/helper-module-context': 1.8.5 + '@webassemblyjs/wasm-edit': 1.8.5 + '@webassemblyjs/wasm-parser': 1.8.5 + acorn: 6.1.1 + acorn-dynamic-import: /acorn-dynamic-import/4.0.0/acorn@6.1.1 + ajv: 6.9.2 + ajv-keywords: /ajv-keywords/3.4.0/ajv@6.9.2 chrome-trace-event: 1.0.0 enhanced-resolve: 4.1.0 eslint-scope: 4.0.0 @@ -13175,10 +13131,10 @@ packages: micromatch: 3.1.10 mkdirp: 0.5.1 neo-async: 2.6.0 - node-libs-browser: 2.1.0 - schema-utils: 0.4.7 + node-libs-browser: 2.2.0 + schema-utils: 1.0.0 tapable: 1.1.1 - terser-webpack-plugin: 1.2.1 + terser-webpack-plugin: 1.2.3 watchpack: 1.6.0 webpack-sources: 1.3.0 dev: false @@ -13186,7 +13142,7 @@ packages: node: '>=6.11.5' hasBin: true resolution: - integrity: sha512-NxjD61WsK/a3JIdwWjtIpimmvE6UrRi3yG54/74Hk9rwNj5FPkA4DJCf1z4ByDWLkvZhTZE+P3C/eh6UD5lDcw== + integrity: sha512-MwBwpiE1BQpMDkbnUUaW6K8RFZjljJHArC6tWQJoFm0oQtfoSebtg4Y7/QHnJ/SddtjYLHaKGX64CFjG5rehJw== /websocket-driver/0.7.0: dependencies: http-parser-js: 0.5.0 @@ -13300,26 +13256,26 @@ packages: dev: false resolution: integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - /write-file-atomic/2.3.0: + /write-file-atomic/2.4.2: dependencies: graceful-fs: 4.1.15 imurmurhash: 0.1.4 signal-exit: 3.0.2 dev: false resolution: - integrity: sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA== + integrity: sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g== /ws/5.2.2: dependencies: async-limiter: 1.0.0 dev: false resolution: integrity: sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== - /ws/6.1.2: + /ws/6.1.4: dependencies: async-limiter: 1.0.0 dev: false resolution: - integrity: sha512-rfUqzvz0WxmSXtJpPMX2EeASXabOrSMk1ruMOV3JBTBjo4ac2lDjGGsbQSyxj8Odhw5fBib8ZKEjDNvgouNKYw== + integrity: sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA== /x-is-string/0.1.0: dev: false resolution: @@ -13334,7 +13290,7 @@ packages: dependencies: global: 4.3.2 is-function: 1.0.1 - parse-headers: 2.0.1 + parse-headers: 2.0.2 xtend: 4.0.1 dev: false resolution: @@ -13551,6 +13507,17 @@ packages: node: '>=4' resolution: integrity: sha1-5a2ryKz0CPY4X8dklWhMiOavaJo= + /z-schema/3.18.4: + dependencies: + lodash.get: 4.4.2 + lodash.isequal: 4.5.0 + validator: 8.2.0 + dev: false + hasBin: true + optionalDependencies: + commander: 2.19.0 + resolution: + integrity: sha512-DUOKC/IhbkdLKKiV89gw9DUauTV8U/8yJl1sjf6MtDmzevLKOF2duNJ495S3MFVjqZarr+qNGCPbkg4mu4PpLw== /zip-stream/1.2.0: dependencies: archiver-utils: 1.3.0 @@ -13566,16 +13533,15 @@ packages: dependencies: '@types/body-parser': 1.17.0 '@types/chai': 4.1.7 - '@types/express': 4.16.0 - '@types/express-session': 1.15.11 + '@types/express': 4.16.1 + '@types/express-session': 1.15.12 '@types/minimist': 1.2.0 - '@types/mocha': 5.2.5 - '@types/node': 10.10.3 + '@types/mocha': 5.2.6 + '@types/node': 10.12.18 '@types/passport': 0.4.7 body-parser: 1.18.3 chai: 4.2.0 - cookie-parser: 1.4.3 - cpx: 1.5.0 + cookie-parser: 1.4.4 debug: 2.6.9 delay-cli: 1.1.0 express: 4.16.4 @@ -13583,57 +13549,52 @@ packages: minimist: 1.2.0 mocha: 5.2.0 npm-run-all: 4.1.5 - nyc: 13.1.0 + nyc: 13.3.0 openid-client: 2.4.5 passport: 0.4.0 path: 0.12.7 - react: 16.7.0 - tslint: /tslint/5.12.1/typescript@3.1.6 + react: 16.8.3 + tslint: /tslint/5.13.0/typescript@3.2.4 typemoq: 2.1.0 - typescript: 3.1.6 + typescript: 3.2.4 dev: false name: '@rush-temp/agent-test-app' resolution: - integrity: sha512-kk/efSB+lvV/BkiDYBPnM0DXGaNOWW8/JClTyJFZ0UCRDftvqiIA7Gvm67u79zMgsHEif1hIeN2PEwhqVDDTJQ== + integrity: sha512-w5m3H94Z7b3lY2/oyBhU1GbFVW6ypnOcZk03UIIEw8fwDmdKrPXYC72NiFV65lv5wnNe7gI9wbtqt3pnf+VxaQ== tarball: 'file:projects/agent-test-app.tgz' version: 0.0.0 'file:projects/bentleyjs-core.tgz': dependencies: '@types/chai': 4.1.7 - '@types/mocha': 5.2.5 - '@types/node': 10.10.3 + '@types/mocha': 5.2.6 + '@types/node': 10.12.18 chai: 4.2.0 - make-dir-cli: 1.0.0 mocha: 5.2.0 - nyc: 13.1.0 + nyc: 13.3.0 rimraf: 2.6.3 - source-map-loader: 0.2.4 - source-map-support: 0.5.10 ts-node: 7.0.1 - tslint: /tslint/5.12.1/typescript@3.1.6 + tslint: /tslint/5.13.0/typescript@3.2.4 typedoc: 0.11.1 - typedoc-plugin-external-module-name: /typedoc-plugin-external-module-name/1.1.3/typedoc@0.11.1 - typescript: 3.1.6 - webpack: 4.28.4 - webpack-cli: /webpack-cli/3.2.1/webpack@4.28.4 + typescript: 3.2.4 dev: false name: '@rush-temp/bentleyjs-core' resolution: - integrity: sha512-8OTca5iEUCTnVer9PaKc5fVfFRqyFJmbO3uNX8Wvw4JF/GwXau1z4aABNvv4K6rgbqE7VICEj7M49OxZyFZ3Kg== + integrity: sha512-V8xmcGg10YatNkjbY8sds0NiGytdBZyilv+KFob3tBlEzHJau8v3qu1qaWVK7lFi4GQHiN6nlJ/8IibVWb6V6g== tarball: 'file:projects/bentleyjs-core.tgz' version: 0.0.0 'file:projects/build-tools.tgz': dependencies: + '@microsoft/api-extractor': 6.3.0 cache-require-paths: 0.3.0 chai: 4.2.0 chalk: 2.4.2 - chokidar: 2.0.4 + chokidar: 2.1.2 commander: 2.19.0 comment-json: 1.1.3 cpx: 1.5.0 cross-spawn: 6.0.5 css-loader: 0.28.11 - enzyme-adapter-react-16: 1.7.1 + enzyme-adapter-react-16: 1.10.0 enzyme-to-json: 3.3.5 file-loader: 1.1.11 fs-extra: 6.0.1 @@ -13644,7 +13605,7 @@ packages: merge-json: 0.1.0-b.3 mocha: 5.2.0 mocha-junit-reporter: /mocha-junit-reporter/1.18.0/mocha@5.2.0 - nyc: 13.1.0 + nyc: 13.3.0 obj-traverse: 1.0.0 recursive-readdir: 2.2.2 rimraf: 2.6.3 @@ -13652,13 +13613,15 @@ packages: style-loader: 0.21.0 svg-sprite-loader: 3.9.2 ts-node: 7.0.1 - tsconfig-paths: 3.7.0 + tsconfig-paths: 3.8.0 tslib: 1.9.3 - tslint: /tslint/5.12.1/typescript@3.1.6 - tslint-consistent-codestyle: /tslint-consistent-codestyle/1.15.0/tslint@5.12.1+typescript@3.1.6 - tsutils: /tsutils/3.7.0/typescript@3.1.6 + tslint: /tslint/5.13.0/typescript@3.2.4 + tslint-consistent-codestyle: /tslint-consistent-codestyle/1.15.0/tslint@5.13.0+typescript@3.2.4 + tsutils: /tsutils/3.8.0/typescript@3.2.4 typedoc: 0.11.1 - typescript: 3.1.6 + typedoc-plugin-external-module-name: /typedoc-plugin-external-module-name/1.1.1/typedoc@0.11.1 + typedoc-plugin-internal-external: /typedoc-plugin-internal-external/1.0.10/typedoc@0.11.1 + typescript: 3.2.4 uglifyjs-webpack-plugin: 1.3.0 url-loader: 1.1.2 webpack-node-externals: 1.7.2 @@ -13666,73 +13629,71 @@ packages: dev: false name: '@rush-temp/build-tools' resolution: - integrity: sha512-qzQnCd/PnjjGUkcSeiYDqxYoJyeCObE8UVugD5VRqOuRDXtwlLdzYFQFyE6WNdppx/O86ze/T6O9R5TnlU/ATw== + integrity: sha512-RqRNFP4o6Fx6LuzIdwCZNiQbsXF0BBoiRQr37Z22PvYGVL681iu3NxKeRYA10zUJ3Dt1WiQJuaqDoogrgBcTzw== tarball: 'file:projects/build-tools.tgz' version: 0.0.0 'file:projects/config-loader.tgz': dependencies: '@types/json5': 0.0.30 - '@types/node': 10.10.3 + '@types/node': 10.12.18 chalk: 2.4.2 json5: 2.1.0 rimraf: 2.6.3 - tslint: /tslint/5.12.1/typescript@3.1.6 - typescript: 3.1.6 + tslint: /tslint/5.13.0/typescript@3.2.4 + typescript: 3.2.4 dev: false name: '@rush-temp/config-loader' resolution: - integrity: sha512-sonyDzydHs0xi8ErD0nS8rnlfGf7Y87uWPKgC4MYpZBT87Kqon/YCvmvhmDl+4pqYICUkpmtu6MYtPMcYJt/8w== + integrity: sha512-EXiYhQhWQ4hZ6TwrSHQEBHuCo31duHtnNRa/1/vJtTCKfoTmmmXxCHRjuSITdjesQ99xBwLXm9tvm0kQqQpqNQ== tarball: 'file:projects/config-loader.tgz' version: 0.0.0 'file:projects/display-performance-test-app.tgz': dependencies: '@types/body-parser': 1.17.0 - '@types/express': 4.16.0 - '@types/node': 10.10.3 + '@types/express': 4.16.1 + '@types/node': 10.12.18 body-parser: 1.18.3 child_process: 1.0.2 - cpx: 1.5.0 - electron: 4.0.1 + chrome-launcher: 0.10.5 + electron: 4.0.6 express: 4.16.4 node-glob: 1.2.0 + npm-run-all: 4.1.5 null-loader: 0.1.1 - popper.js: 1.14.6 + popper.js: 1.14.7 rimraf: 2.6.3 - source-map-loader: 0.2.4 tooltip.js: 1.3.1 - tslint: /tslint/5.12.1/typescript@3.1.6 - typescript: 3.1.6 - webpack: 4.28.4 + tslint: /tslint/5.13.0/typescript@3.2.4 + typescript: 3.2.4 dev: false name: '@rush-temp/display-performance-test-app' resolution: - integrity: sha512-ybhTxfoeeBQAPvOCNCZhxZ+LAY+l0Ajaa0S+gqjtlTvfKWixVOJxBxA4Ix/l10c8o67YrEETW2bUeVUlP+yDYA== + integrity: sha512-mgJ3jWEfF/Noghat/kI+tzV6Xk/9NBn+wjuHLOp8rFUHr9B1zgZPFNbt/xTstaxyGnt17SjKf/mKaOVfqssUaA== tarball: 'file:projects/display-performance-test-app.tgz' version: 0.0.0 'file:projects/display-test-app.tgz': dependencies: '@types/body-parser': 1.17.0 - '@types/express': 4.16.0 - '@types/node': 10.10.3 + '@types/express': 4.16.1 + '@types/node': 10.12.18 body-parser: 1.18.3 child_process: 1.0.2 cpx: 1.5.0 - electron: 4.0.1 + electron: 4.0.6 express: 4.16.4 + fs-extra: 6.0.1 node-glob: 1.2.0 npm-run-all: 4.1.5 null-loader: 0.1.1 - popper.js: 1.14.6 + popper.js: 1.14.7 rimraf: 2.6.3 - source-map-loader: 0.2.4 tooltip.js: 1.3.1 - tslint: /tslint/5.12.1/typescript@3.1.6 - typescript: 3.1.6 - webpack: 4.28.4 + tslint: /tslint/5.13.0/typescript@3.2.4 + typescript: 3.2.4 dev: false name: '@rush-temp/display-test-app' resolution: - integrity: sha512-EtcUeH465fDGGhK/7pRa26Mr5g/l7F5spj1cj5VUk+vdV5KN03oku0ClA4eljuv22q6sZ/D+2bNWWOIUpvv9Pg== + integrity: sha512-hGu/kQLBT2ni5pCtaSNC7NDOfmR6OPQRx0IzdgwSIJ2wm4r6615hizcdRAIHgAf3ShaJ+TtyWEDN8OnjYzOg2w== tarball: 'file:projects/display-test-app.tgz' version: 0.0.0 'file:projects/ecschema-metadata.tgz': @@ -13740,200 +13701,189 @@ packages: '@types/chai': 4.1.7 '@types/chai-as-promised': 7.1.0 '@types/glob': 5.0.36 - '@types/i18next-node-fs-backend': 0.0.30 - '@types/mocha': 5.2.5 - '@types/node': 10.10.3 + '@types/mocha': 5.2.6 + '@types/node': 10.12.18 '@types/sinon': 5.0.7 bunyan: 1.8.12 bunyan-seq: 0.2.0 chai: 4.2.0 chai-as-promised: /chai-as-promised/7.1.1/chai@4.2.0 glob: 7.1.3 - i18next-node-fs-backend: 2.1.0 mocha: 5.2.0 - nyc: 13.1.0 + nyc: 13.3.0 rimraf: 2.6.3 - sinon: 7.2.2 + sinon: 7.2.5 ts-node: 7.0.1 - tslint: /tslint/5.12.1/typescript@3.1.6 + tslint: /tslint/5.13.0/typescript@3.2.4 typedoc: 0.11.1 - typescript: 3.1.6 + typescript: 3.2.4 dev: false name: '@rush-temp/ecschema-metadata' resolution: - integrity: sha512-+tiX2CcgVrLjLjUQ2J1iROUsTkDABl/WBFx/6eRC/jknbWdQxUZN54VvfCNAHp3MG/2NrdNXRL1NiO8/HMmMng== + integrity: sha512-oQ9i5XkNYoDQa3NeuYchAXgt/wB0UkLaABHzscua5JnoXujPaHx83rXAHys95Fn1A7J9wK0kZUSWCfsyHZfuzw== tarball: 'file:projects/ecschema-metadata.tgz' version: 0.0.0 'file:projects/electron-manager.tgz': dependencies: - '@types/node': 10.10.3 - electron: 4.0.1 + '@types/node': 10.12.18 + electron: 4.0.6 rimraf: 2.6.3 - tslint: /tslint/5.12.1/typescript@3.1.6 + tslint: /tslint/5.13.0/typescript@3.2.4 typedoc: 0.11.1 - typedoc-plugin-external-module-name: /typedoc-plugin-external-module-name/1.1.3/typedoc@0.11.1 - typescript: 3.1.6 + typescript: 3.2.4 dev: false name: '@rush-temp/electron-manager' resolution: - integrity: sha512-A7ZFGNsgzjAH3yp7UbMiV3W5vzfkIGey7mrLSgd4Zb1u/Kyo45k96JM5AvTn+Um9uaidtMOpZcqjqWxGsK0a/w== + integrity: sha512-XNsLTDh+kqvzVjlqUHH2m22MZ9lEJb7CoOdoY5+1xSpmYosmWVVHlyAGTX/UJXDAcG21eJKFxJCeLM2S0uTHUA== tarball: 'file:projects/electron-manager.tgz' version: 0.0.0 'file:projects/example-code-app.tgz': dependencies: '@types/body-parser': 1.17.0 '@types/chai': 4.1.7 - '@types/express': 4.16.0 + '@types/express': 4.16.1 '@types/fs-extra': 4.0.8 '@types/i18next': 8.4.6 - '@types/i18next-browser-languagedetector': 2.0.1 - '@types/i18next-xhr-backend': 1.4.1 + '@types/i18next-browser-languagedetector': 2.0.2 '@types/js-base64': 2.3.1 - '@types/mocha': 5.2.5 - '@types/node': 10.10.3 + '@types/mocha': 5.2.6 + '@types/node': 10.12.18 '@types/webpack': 3.8.17 body-parser: 1.18.3 chai: 4.2.0 commander: 2.19.0 cpx: 1.5.0 - electron: 4.0.1 + electron: 4.0.6 express: 4.16.4 fs-extra: 6.0.1 - fuse.js: 3.3.0 + fuse.js: 3.4.2 i18next: 10.6.0 i18next-browser-languagedetector: 2.2.4 - i18next-xhr-backend: 1.5.1 - js-base64: 2.5.0 + i18next-xhr-backend: 2.0.1 + js-base64: 2.5.1 jsdom: 11.12.0 jsdom-global: /jsdom-global/3.0.2/jsdom@11.12.0 mocha: 5.2.0 rimraf: 2.6.3 save: 2.3.3 - tslint: /tslint/5.12.1/typescript@3.1.6 - typescript: 3.1.6 - webpack: 4.28.4 + tslint: /tslint/5.13.0/typescript@3.2.4 + typescript: 3.2.4 + webpack: 4.29.6 xmlhttprequest: 1.8.0 dev: false name: '@rush-temp/example-code-app' resolution: - integrity: sha512-ziQoE1wLLkbag1Z6B2bHburN2BLl04RZeosp0NgqN2Om8MxV1SjxKQEafRNR8RTtVB4dyxH5/fJOWcRXWwhOUg== + integrity: sha512-jKV6FdAp5BaaxqTNOuCSQQsPFNdyBwsyBIDOu91MVZRNMjc4sL5vwyNBGk5D4l2PLgv4g5jsish7a9veHEjvRQ== tarball: 'file:projects/example-code-app.tgz' version: 0.0.0 'file:projects/example-code-snippets.tgz': dependencies: '@types/body-parser': 1.17.0 '@types/chai': 4.1.7 - '@types/express': 4.16.0 + '@types/express': 4.16.1 '@types/fs-extra': 4.0.8 '@types/i18next': 8.4.6 - '@types/i18next-browser-languagedetector': 2.0.1 - '@types/i18next-xhr-backend': 1.4.1 + '@types/i18next-browser-languagedetector': 2.0.2 '@types/js-base64': 2.3.1 - '@types/mocha': 5.2.5 - '@types/node': 10.10.3 + '@types/mocha': 5.2.6 + '@types/node': 10.12.18 '@types/webpack': 3.8.17 body-parser: 1.18.3 chai: 4.2.0 commander: 2.19.0 cpx: 1.5.0 - electron: 4.0.1 + electron: 4.0.6 express: 4.16.4 fs-extra: 6.0.1 - fuse.js: 3.3.0 + fuse.js: 3.4.2 i18next: 10.6.0 i18next-browser-languagedetector: 2.2.4 - i18next-xhr-backend: 1.5.1 - js-base64: 2.5.0 + i18next-xhr-backend: 2.0.1 + js-base64: 2.5.1 mocha: 5.2.0 rimraf: 2.6.3 save: 2.3.3 - tslint: /tslint/5.12.1/typescript@3.1.6 - typescript: 3.1.6 - webpack: 4.28.4 + tslint: /tslint/5.13.0/typescript@3.2.4 + typescript: 3.2.4 + webpack: 4.29.6 dev: false name: '@rush-temp/example-code-snippets' resolution: - integrity: sha512-G1blAaApvOOoBxbq5cEemopz5a1uXikZhG7EuEuWgPPNE7VFxve+Klc21trTAIhbn5xRlLf76V99gQx/TuuszA== + integrity: sha512-8F1qCJA8sPg3OsAqGT8v8Gx1VTY3oDos2aIYasknVL4TVyzaCsRDEP44MTR/OxduVI8tHB95iXDf4U5jZsdetg== tarball: 'file:projects/example-code-snippets.tgz' version: 0.0.0 'file:projects/geometry-core.tgz': dependencies: '@types/chai': 4.1.7 - '@types/mocha': 5.2.5 - '@types/node': 10.10.3 + '@types/mocha': 5.2.6 + '@types/node': 10.12.18 chai: 4.2.0 cpx: 1.5.0 debug: 2.6.9 - make-dir-cli: 1.0.0 mocha: 5.2.0 - nyc: 13.1.0 + nyc: 13.3.0 rimraf: 2.6.3 semver: 5.6.0 - source-map-loader: 0.2.4 - source-map-support: 0.5.10 ts-node: 7.0.1 - tslint: /tslint/5.12.1/typescript@3.1.6 + tslint: /tslint/5.13.0/typescript@3.2.4 typedoc: 0.11.1 - typedoc-plugin-external-module-name: /typedoc-plugin-external-module-name/1.1.3/typedoc@0.11.1 - typescript: 3.1.6 - webpack: 4.28.4 - webpack-cli: /webpack-cli/3.2.1/webpack@4.28.4 + typescript: 3.2.4 dev: false name: '@rush-temp/geometry-core' resolution: - integrity: sha512-XuW295G0X2Nve/LwtbTXlpvaMD1j6S4gXz7OxKJ4XxJRqJRcGsmqcyW4EnqsXeLhQZsM2tSdbKNe/TrifSEFGg== + integrity: sha512-OYrXYRXphy6MzVk/elmU1dIU7xrxWamUJ/IDJlsFde4/hu7atcDxPkksoQaL9WDRcubNTW85UL32CduHEQQXBQ== tarball: 'file:projects/geometry-core.tgz' version: 0.0.0 'file:projects/imodel-from-geojson.tgz': dependencies: '@types/fs-extra': 4.0.8 - '@types/lodash': 4.14.119 - '@types/node': 10.10.3 - '@types/yargs': 12.0.5 + '@types/lodash': 4.14.121 + '@types/node': 10.12.18 + '@types/yargs': 12.0.9 fs-extra: 6.0.1 rimraf: 2.6.3 - tslint: /tslint/5.12.1/typescript@3.1.6 - typescript: 3.1.6 + tslint: /tslint/5.13.0/typescript@3.2.4 + typescript: 3.2.4 yargs: 12.0.5 dev: false name: '@rush-temp/imodel-from-geojson' resolution: - integrity: sha512-uwCHK/oYOleZe7rWnRh2/J5YnmpKk//t1PsFk3Lx1VPkqLmvM+tWgXGQwAudqCAEdM7Cyn/R4cP3uY3UIRNjxQ== + integrity: sha512-Jm16ak5DoZ1tL2LrHXWkRoHY8cST9sHhtQmdpthcyfHvLKOGgAom3Iu/KFQ6MdL3TuExZFxa0FEUgagZ50+oVQ== tarball: 'file:projects/imodel-from-geojson.tgz' version: 0.0.0 'file:projects/imodel-from-reality-model.tgz': dependencies: '@types/fs-extra': 4.0.8 - '@types/lodash': 4.14.119 - '@types/node': 10.10.3 + '@types/lodash': 4.14.121 + '@types/node': 10.12.18 '@types/request-promise-native': 1.0.15 - '@types/yargs': 12.0.5 + '@types/yargs': 12.0.9 fs-extra: 6.0.1 request: 2.88.0 - request-promise-native: /request-promise-native/1.0.5/request@2.88.0 + request-promise-native: /request-promise-native/1.0.7/request@2.88.0 rimraf: 2.6.3 - tslint: /tslint/5.12.1/typescript@3.1.6 - typescript: 3.1.6 + tslint: /tslint/5.13.0/typescript@3.2.4 + typescript: 3.2.4 yargs: 12.0.5 dev: false name: '@rush-temp/imodel-from-reality-model' resolution: - integrity: sha512-SKcLuHwCqM591QyavnOokUO2TFfAdtHMWuyiG8UuE+g+bKZjXMB+l5jqkzmawtL1v8Q6oDIyft+jd6d/DuSTRA== + integrity: sha512-saKQPjEkL4FWKtEmO8LFr50iJ+7lqLArjEwCwO+BJRTuFYuNTJlQnLKNfEv+61n9M9oYYvZR/fKORoP8fSKjmg== tarball: 'file:projects/imodel-from-reality-model.tgz' version: 0.0.0 'file:projects/imodeljs-backend.tgz': dependencies: - '@bentley/imodeljs-native': 0.86.0 + '@bentley/imodeljs-native': npm.bentley.com/@bentley/imodeljs-native/0.99.0 '@types/body-parser': 1.17.0 '@types/chai': 4.1.7 - '@types/express': 4.16.0 + '@types/express': 4.16.1 '@types/form-data': 2.2.1 '@types/fs-extra': 4.0.8 '@types/glob': 5.0.36 '@types/js-base64': 2.3.1 - '@types/mocha': 5.2.5 + '@types/mocha': 5.2.6 '@types/multiparty': 0.0.31 - '@types/node': 10.10.3 + '@types/node': 10.12.18 '@types/semver': 5.5.0 asn1: 0.2.3 body-parser: 1.18.3 @@ -13942,168 +13892,149 @@ packages: form-data: 2.3.2 fs-extra: 6.0.1 glob: 7.1.3 - js-base64: 2.5.0 + js-base64: 2.5.1 mocha: 5.2.0 multiparty: 4.2.1 - nyc: 13.1.0 + nyc: 13.3.0 rimraf: 2.6.3 semver: 5.6.0 source-map-support: 0.5.10 ts-node: 7.0.1 - tslint: /tslint/5.12.1/typescript@3.1.6 + tslint: /tslint/5.13.0/typescript@3.2.4 typedoc: 0.11.1 - typedoc-plugin-external-module-name: /typedoc-plugin-external-module-name/1.1.3/typedoc@0.11.1 typemoq: 2.1.0 - typescript: 3.1.6 - webpack: 4.28.4 + typescript: 3.2.4 + webpack: 4.29.6 dev: false name: '@rush-temp/imodeljs-backend' resolution: - integrity: sha512-c76krplE801xr/94mbHXIV+LJwaK+o8Ycfm1Bt04dpj1mLqKmmLHOJ1GtZ6wg9o64iuHuU2miktE6OGmXji7rg== + integrity: sha512-FY9gPXA2+JYhZtiHPnrY1ogFNe4h83mb9ZOpqmB6G9xdqD4HLDQLP2ut62YwPvY021AUSp4Rqwb4dyO+Ty8IMQ== tarball: 'file:projects/imodeljs-backend.tgz' version: 0.0.0 'file:projects/imodeljs-clients-backend.tgz': dependencies: - '@openid/appauth': 1.2.0 + '@openid/appauth': 1.2.1 '@types/chai': 4.1.7 - '@types/mocha': 5.2.5 - '@types/nock': 9.3.0 - '@types/node': 10.10.3 + '@types/deep-assign': 0.1.1 + '@types/fs-extra': 4.0.8 + '@types/js-base64': 2.3.1 + '@types/mocha': 5.2.6 + '@types/nock': 9.3.1 + '@types/node': 10.12.18 chai: 4.2.0 cpx: 1.5.0 + deep-assign: 2.0.0 + fs-extra: 6.0.1 + fs-write-stream-atomic: 1.0.10 https-proxy-agent: 2.2.1 + js-base64: 2.5.1 mocha: 5.2.0 nock: 9.6.1 - nyc: 13.1.0 + nyc: 13.3.0 openid-client: 2.4.5 + rimraf: 2.6.3 source-map-support: 0.5.10 ts-node: 7.0.1 - tslint: /tslint/5.12.1/typescript@3.1.6 + tslint: /tslint/5.13.0/typescript@3.2.4 typedoc: 0.11.1 - typedoc-plugin-external-module-name: /typedoc-plugin-external-module-name/1.1.3/typedoc@0.11.1 - typescript: 3.1.6 + typescript: 3.2.4 dev: false name: '@rush-temp/imodeljs-clients-backend' resolution: - integrity: sha512-rJFh1cSi5XiXXbSxpCacFmSTFSncY3mMJhI5Tgh3fjUnvKAX+rpJTBkWhj1XndmqkZHidn1VvqNZN3xfoaEYGw== + integrity: sha512-G8OFPwgCw/FfE43irTVmmEGJeDQ/d31mw0OnikugeKdbzq0Svim5JuaimyHJCfYiLRnLGh0GOR0usqeLB2jpeg== tarball: 'file:projects/imodeljs-clients-backend.tgz' version: 0.0.0 'file:projects/imodeljs-clients.tgz': dependencies: '@types/chai': 4.1.7 '@types/deep-assign': 0.1.1 - '@types/fs-extra': 4.0.8 '@types/js-base64': 2.3.1 - '@types/mocha': 5.2.5 - '@types/nock': 9.3.0 - '@types/node': 10.10.3 + '@types/mocha': 5.2.6 + '@types/nock': 9.3.1 + '@types/node': 10.12.18 '@types/qs': 6.5.1 - '@types/superagent': 3.8.6 + '@types/superagent': 3.8.7 '@types/xmldom': 0.1.29 chai: 4.2.0 cpx: 1.5.0 deep-assign: 2.0.0 - fs-extra: 6.0.1 - js-base64: 2.5.0 - make-dir-cli: 1.0.0 + js-base64: 2.5.1 mocha: 5.2.0 nock: 9.6.1 - nyc: 13.1.0 + nyc: 13.3.0 oidc-client: 1.6.1 qs: 6.6.0 rimraf: 2.6.3 - source-map-loader: 0.2.4 - source-map-support: 0.5.10 superagent: 3.8.3 ts-node: 7.0.1 - tslint: /tslint/5.12.1/typescript@3.1.6 + tslint: /tslint/5.13.0/typescript@3.2.4 typedoc: 0.11.1 - typedoc-plugin-external-module-name: /typedoc-plugin-external-module-name/1.1.3/typedoc@0.11.1 - typescript: 3.1.6 - webpack: 4.28.4 - webpack-cli: /webpack-cli/3.2.1/webpack@4.28.4 + typescript: 3.2.4 xmldom: 0.1.27 xpath: 0.0.27 dev: false name: '@rush-temp/imodeljs-clients' resolution: - integrity: sha512-A6OTrLUO8F+pT9Nl/TzLUkPDfyFdZjRMgkbeKcYlVyjE+cJEEvb8rclGpQ+DtFVnZ5KTyUEJtUpJaIB8sud/vw== + integrity: sha512-YSH2VMUmijnb/eI+giGvTNr4wfw6Gs5Vq+FeA/KNshruaimTe4civ1ypsixaA2ZvVJSFrZsCDDSeq723ngn4rA== tarball: 'file:projects/imodeljs-clients.tgz' version: 0.0.0 'file:projects/imodeljs-common.tgz': dependencies: '@types/chai': 4.1.7 - '@types/mocha': 5.2.5 - '@types/node': 10.10.3 + '@types/mocha': 5.2.6 + '@types/node': 10.12.18 '@types/semver': 5.5.0 '@types/url-search-params': 0.10.2 chai: 4.2.0 - cpx: 1.5.0 - make-dir-cli: 1.0.0 mocha: 5.2.0 rimraf: 2.6.3 semver: 5.6.0 - source-map-loader: 0.2.4 - source-map-support: 0.5.10 ts-node: 7.0.1 - tslint: /tslint/5.12.1/typescript@3.1.6 + tslint: /tslint/5.13.0/typescript@3.2.4 typedoc: 0.11.1 - typedoc-plugin-external-module-name: /typedoc-plugin-external-module-name/1.1.3/typedoc@0.11.1 - typescript: 3.1.6 + typescript: 3.2.4 url-search-params: 1.1.0 - webpack: 4.28.4 - webpack-cli: /webpack-cli/3.2.1/webpack@4.28.4 dev: false name: '@rush-temp/imodeljs-common' resolution: - integrity: sha512-jFUY7ghY8J8uw3EH8CmVVyDGo4Ne4H5zV32A1HkN83ZTAeLv5/QGqjUNWYwIsQz4XtIAbpY7vCxfCb682kTcqQ== + integrity: sha512-hGf2fHddozC8GbkJpx4RlI6FRnFD3wVsjJ2R4SBgu9MHkNq3qh8PjEbuMvbQbGJLTII4iBJS/ZHzY9G3t61THg== tarball: 'file:projects/imodeljs-common.tgz' version: 0.0.0 'file:projects/imodeljs-frontend.tgz': dependencies: '@types/js-base64': 2.3.1 - '@types/node': 10.10.3 - cpx: 1.5.0 - fuse.js: 3.3.0 - js-base64: 2.5.0 - make-dir-cli: 1.0.0 + '@types/node': 10.12.18 + '@types/semver': 5.5.0 + fuse.js: 3.4.2 + js-base64: 2.5.1 oidc-client: 1.6.1 rimraf: 2.6.3 - source-map-loader: 0.2.4 - tslint: /tslint/5.12.1/typescript@3.1.6 + semver: 5.6.0 + tslint: /tslint/5.13.0/typescript@3.2.4 typedoc: 0.11.1 - typedoc-plugin-external-module-name: /typedoc-plugin-external-module-name/1.1.3/typedoc@0.11.1 - typescript: 3.1.6 - webpack: 4.28.4 - webpack-cli: /webpack-cli/3.2.1/webpack@4.28.4 + typescript: 3.2.4 dev: false name: '@rush-temp/imodeljs-frontend' resolution: - integrity: sha512-ej+DowY5MQEH9hH8Nsdg6cS1IhDXyFF3wRN0DNrupCBP8/a3R2dJoO0d+YWhxJJrT0POd5KBk6HgJtZZS77YSg== + integrity: sha512-s2akIztBE1ogoZmfFVfMQvfislE+ujmSlQ/GNiESjOujWuVz6Srs6QhmbtiC55AdnOAx3INouoZZfzbh52IUnw== tarball: 'file:projects/imodeljs-frontend.tgz' version: 0.0.0 'file:projects/imodeljs-i18n.tgz': dependencies: '@types/i18next': 8.4.6 - '@types/i18next-browser-languagedetector': 2.0.1 - '@types/i18next-xhr-backend': 1.4.1 - '@types/node': 10.10.3 + '@types/i18next-browser-languagedetector': 2.0.2 + '@types/node': 10.12.18 i18next: 10.6.0 i18next-browser-languagedetector: 2.2.4 - i18next-xhr-backend: 1.5.1 - make-dir-cli: 1.0.0 + i18next-xhr-backend: 2.0.1 rimraf: 2.6.3 - source-map-loader: 0.2.4 - tslint: /tslint/5.12.1/typescript@3.1.6 + tslint: /tslint/5.13.0/typescript@3.2.4 typedoc: 0.11.1 - typedoc-plugin-external-module-name: /typedoc-plugin-external-module-name/1.1.3/typedoc@0.11.1 - typescript: 3.1.6 - webpack: 4.28.4 - webpack-cli: /webpack-cli/3.2.1/webpack@4.28.4 + typescript: 3.2.4 dev: false name: '@rush-temp/imodeljs-i18n' resolution: - integrity: sha512-CpP8g1KOvKlXf3b99QUPH+yNCyQpffNcEToTpaue62y9Jk6y6xLzswpn8Xa37MW4b72/1Q865dDiT953JtYW0w== + integrity: sha512-xpemO9G/b88XWB5LxT9lCi0YJ+u7+B9nti6IKJRPqn+upG2Xxsow5sPsQL2AxcYAG53Fa5BZTL7fatomrvtH4Q== tarball: 'file:projects/imodeljs-i18n.tgz' version: 0.0.0 'file:projects/imodeljs-quantity.tgz': @@ -14111,58 +14042,89 @@ packages: '@types/chai': 4.1.7 '@types/chai-as-promised': 7.1.0 '@types/glob': 5.0.36 - '@types/mocha': 5.2.5 - '@types/node': 10.10.3 + '@types/mocha': 5.2.6 + '@types/node': 10.12.18 '@types/sinon': 5.0.7 chai: 4.2.0 chai-as-promised: /chai-as-promised/7.1.1/chai@4.2.0 - make-dir-cli: 1.0.0 mocha: 5.2.0 - nyc: 13.1.0 + nyc: 13.3.0 rimraf: 2.6.3 - sinon: 7.2.2 - source-map-loader: 0.2.4 + sinon: 7.2.5 ts-node: 7.0.1 - tslint: /tslint/5.12.1/typescript@3.1.6 + tslint: /tslint/5.13.0/typescript@3.2.4 typedoc: 0.11.1 - typescript: 3.1.6 - webpack: 4.28.4 - webpack-cli: /webpack-cli/3.2.1/webpack@4.28.4 + typescript: 3.2.4 dev: false name: '@rush-temp/imodeljs-quantity' resolution: - integrity: sha512-bb4/sdU5/1m6t/uqJYeq2pDdtymQcGu0Qcw7n9gHtpL67XFDKPLPISGHQ0w5lMxKshtoh9Zg9+WmELntnMBXzg== + integrity: sha512-RaSYyW83SLDRjsg87rWBQ2VUipS/RCyvMAUcX8+Gi0u+YOZN4ogHS3jo+G7+Hxr88h/CGUoKbfulK2hHqTZVsw== tarball: 'file:projects/imodeljs-quantity.tgz' version: 0.0.0 'file:projects/imodeljs-webserver.tgz': dependencies: - '@bentley/dev-cors-proxy-server': 0.0.9 - '@types/express': 4.16.0 - '@types/node': 10.10.3 - '@types/yargs': 12.0.5 + '@bentley/dev-cors-proxy-server': npm.bentley.com/@bentley/dev-cors-proxy-server/0.0.9 + '@types/express': 4.16.1 + '@types/node': 10.12.18 + '@types/yargs': 12.0.9 express: 4.16.4 rimraf: 2.6.3 tree-kill: 1.2.1 - tslint: /tslint/5.12.1/typescript@3.1.6 + tslint: /tslint/5.13.0/typescript@3.2.4 typedoc: 0.11.1 - typedoc-plugin-external-module-name: /typedoc-plugin-external-module-name/1.1.3/typedoc@0.11.1 - typescript: 3.1.6 + typescript: 3.2.4 yargs: 12.0.5 dev: false name: '@rush-temp/imodeljs-webserver' resolution: - integrity: sha512-+bT1CN7YIYFbnxb+qFSllQ/W05YC+Gvtsp5ZHiR1fGBuA4IvUnlOlclqMh261Loai1FB0zV0ufDITzRW+Fi6mw== + integrity: sha512-2f4jAp2eAWXrXWy6iQk4ccpFaJ87ByCnn7HarSwVJhzPlwWKwyehcDVG6aVQpMrNeXv0Hf7hoB5sh4xTPNFigQ== tarball: 'file:projects/imodeljs-webserver.tgz' version: 0.0.0 + 'file:projects/logger-config.tgz': + dependencies: + '@types/bunyan': 1.8.5 + '@types/bunyan-seq': 0.2.1 + '@types/node': 10.12.18 + bunyan: 1.8.12 + bunyan-seq: 0.2.0 + request: 2.88.0 + request-promise: /request-promise/4.2.4/request@2.88.0 + rimraf: 2.6.3 + tslint: /tslint/5.13.0/typescript@3.2.4 + typedoc: 0.11.1 + typescript: 3.2.4 + dev: false + name: '@rush-temp/logger-config' + resolution: + integrity: sha512-vEdPPTGyOtcVd5b71bHJkdJqnq6p6YTpkVS3RWpFGRSfG8L9yTmu+5HHOIwsN2Cd//KuqFmaqIGFDonQQSKKUA== + tarball: 'file:projects/logger-config.tgz' + version: 0.0.0 + 'file:projects/plugin-markup.tgz': + dependencies: + '@types/node': 10.12.18 + chai: 4.2.0 + mocha: 5.2.0 + rimraf: 2.6.3 + svg.js: 2.7.1 + ts-node: 7.0.1 + tslint: /tslint/5.13.0/typescript@3.2.4 + typedoc: 0.11.1 + typescript: 3.2.4 + dev: false + name: '@rush-temp/plugin-markup' + resolution: + integrity: sha512-sSTlA0pXiCjw/LO+1xTt7O1uy5hXS025JbYuwwj1thvl2Ep8agu1ynMVq5Pr8Q/U9tAzZFu/XKQvZV+ag/PICQ== + tarball: 'file:projects/plugin-markup.tgz' + version: 0.0.0 'file:projects/presentation-backend.tgz': dependencies: '@types/chai': 4.1.7 '@types/chai-as-promised': 7.1.0 '@types/chai-jest-snapshot': 1.3.4 '@types/deep-equal': 1.0.1 - '@types/faker': 4.1.4 + '@types/faker': 4.1.5 '@types/lolex': 2.1.3 - '@types/mocha': 5.2.5 + '@types/mocha': 5.2.6 '@types/sinon': 5.0.7 '@types/sinon-chai': 3.2.2 chai: 4.2.0 @@ -14174,19 +14136,18 @@ packages: faker: 4.1.0 lolex: 2.7.5 mocha: 5.2.0 - nyc: 13.1.0 + nyc: 13.3.0 rimraf: 2.6.3 - sinon: 7.2.2 - sinon-chai: /sinon-chai/3.3.0/chai@4.2.0+sinon@7.2.2 - tslint: /tslint/5.12.1/typescript@3.1.6 + sinon: 7.2.5 + sinon-chai: /sinon-chai/3.3.0/chai@4.2.0+sinon@7.2.5 + tslint: /tslint/5.13.0/typescript@3.2.4 typedoc: 0.11.1 - typedoc-plugin-external-module-name: /typedoc-plugin-external-module-name/1.1.3/typedoc@0.11.1 typemoq: 2.1.0 - typescript: 3.1.6 + typescript: 3.2.4 dev: false name: '@rush-temp/presentation-backend' resolution: - integrity: sha512-xdO7ZkcguScsgDStRwG5D5GJYRV47uCO6eRLyyUmROJXtzjs27zcxoPXaI4X0eiUoP5AU/U3mhk+0PttpphE2g== + integrity: sha512-PJsXVufnM7gypxRyTyWGTZbCmzoDBC2WfYZSkr8OfnyGtDrnjcUKnT+baEH6KYVjFtHcCIIPA/F5QHd5v4RLNA== tarball: 'file:projects/presentation-backend.tgz' version: 0.0.0 'file:projects/presentation-common.tgz': @@ -14195,11 +14156,11 @@ packages: '@types/chai-as-promised': 7.1.0 '@types/chai-jest-snapshot': 1.3.4 '@types/deep-equal': 1.0.1 - '@types/faker': 4.1.4 - '@types/mocha': 5.2.5 + '@types/faker': 4.1.5 + '@types/mocha': 5.2.6 '@types/sinon': 5.0.7 '@types/sinon-chai': 3.2.2 - '@types/source-map-support': 0.4.1 + '@types/source-map-support': 0.4.2 chai: 4.2.0 chai-as-promised: /chai-as-promised/7.1.1/chai@4.2.0 chai-jest-snapshot: /chai-jest-snapshot/2.0.0/chai@4.2.0 @@ -14208,27 +14169,22 @@ packages: deep-equal: 1.0.1 faker: 4.1.0 json-schema-faker: 0.5.0-rc16 - make-dir-cli: 1.0.0 mocha: 5.2.0 - nyc: 13.1.0 + nyc: 13.3.0 rimraf: 2.6.3 - sinon: 7.2.2 - sinon-chai: /sinon-chai/3.3.0/chai@4.2.0+sinon@7.2.2 - source-map-loader: 0.2.4 - tslint: /tslint/5.12.1/typescript@3.1.6 - tslint-consistent-codestyle: /tslint-consistent-codestyle/1.15.0/tslint@5.12.1+typescript@3.1.6 + sinon: 7.2.5 + sinon-chai: /sinon-chai/3.3.0/chai@4.2.0+sinon@7.2.5 + tslint: /tslint/5.13.0/typescript@3.2.4 + tslint-consistent-codestyle: /tslint-consistent-codestyle/1.15.0/tslint@5.13.0+typescript@3.2.4 typedoc: 0.11.1 - typedoc-plugin-external-module-name: /typedoc-plugin-external-module-name/1.1.3/typedoc@0.11.1 typemoq: 2.1.0 - typescript: 3.1.6 + typescript: 3.2.4 typescript-json-schema: 0.28.0 - webpack: 4.28.4 - webpack-cli: /webpack-cli/3.2.1/webpack@4.28.4 yargs: 12.0.5 dev: false name: '@rush-temp/presentation-common' resolution: - integrity: sha512-XQs6xNYBmKrMl4GA17Yz+wQjIw3+73ZAA7GNOB0J2GkeW6VDyu5NrFAfHA31DGQn4mN6KORgox95oC77VFGRYQ== + integrity: sha512-8MbKaN0rk2axSWR3gsWvP+UnMK6bB0dMejE5HUvN9xDVCC0vAWY9iluKFeZKK3APtVgokS6d8/g16k4JwzSOSA== tarball: 'file:projects/presentation-common.tgz' version: 0.0.0 'file:projects/presentation-components.tgz': @@ -14236,12 +14192,12 @@ packages: '@types/chai': 4.1.7 '@types/chai-as-promised': 7.1.0 '@types/chai-jest-snapshot': 1.3.4 - '@types/enzyme': 3.1.15 - '@types/faker': 4.1.4 - '@types/lodash': 4.14.119 - '@types/mocha': 5.2.5 - '@types/react': 16.7.18 - '@types/react-dom': 16.0.7 + '@types/enzyme': 3.9.0 + '@types/faker': 4.1.5 + '@types/lodash': 4.14.121 + '@types/mocha': 5.2.6 + '@types/react': 16.7.22 + '@types/react-dom': 16.0.11 '@types/sinon': 5.0.7 '@types/sinon-chai': 3.2.2 chai: 4.2.0 @@ -14249,33 +14205,28 @@ packages: chai-jest-snapshot: /chai-jest-snapshot/2.0.0/chai@4.2.0 cpx: 1.5.0 cross-env: 5.2.0 - enzyme: 3.8.0 - enzyme-adapter-react-16: /enzyme-adapter-react-16/1.7.1/84ec9ac6a98d71b6c46b2abae79a4882 - enzyme-to-json: /enzyme-to-json/3.3.5/enzyme@3.8.0 + enzyme: 3.9.0 + enzyme-adapter-react-16: /enzyme-adapter-react-16/1.10.0/ca330bebd2626bd9cb8e393ae8a11715 + enzyme-to-json: /enzyme-to-json/3.3.5/enzyme@3.9.0 faker: 4.1.0 ignore-styles: 5.0.1 lodash: 4.17.11 - make-dir-cli: 1.0.0 mocha: 5.2.0 - nyc: 13.1.0 - react: 16.7.0 - react-dom: /react-dom/16.7.0/react@16.7.0 + nyc: 13.3.0 + react: 16.8.3 + react-dom: /react-dom/16.8.3/react@16.8.3 rimraf: 2.6.3 - sinon: 7.2.2 - sinon-chai: /sinon-chai/3.3.0/chai@4.2.0+sinon@7.2.2 - source-map-loader: 0.2.4 - tslint: /tslint/5.12.1/typescript@3.1.6 + sinon: 7.2.5 + sinon-chai: /sinon-chai/3.3.0/chai@4.2.0+sinon@7.2.5 + tslint: /tslint/5.13.0/typescript@3.2.4 typedoc: 0.11.1 - typedoc-plugin-external-module-name: /typedoc-plugin-external-module-name/1.1.3/typedoc@0.11.1 typemoq: 2.1.0 - typescript: 3.1.6 - webpack: 4.28.4 - webpack-cli: /webpack-cli/3.2.1/webpack@4.28.4 + typescript: 3.2.4 xmlhttprequest: 1.8.0 dev: false name: '@rush-temp/presentation-components' resolution: - integrity: sha512-HWdszZQBY6DcIQCUzzYVhAYTmzTiwW2YkAiwnAH74Sv2ZMsbP5sFuCJcMleMQFxZc1Msh6OHlsS8ZHf50F2AGQ== + integrity: sha512-Qk5EFk4uVcHFHQQVtW4TNyscHkZ/W2FvPvkIbpJWlo/coBysXyj7fXNroXU0N+Mo9b2ApQvyUU2WOvo5DYkIQA== tarball: 'file:projects/presentation-components.tgz' version: 0.0.0 'file:projects/presentation-frontend.tgz': @@ -14284,8 +14235,8 @@ packages: '@types/chai-as-promised': 7.1.0 '@types/chai-jest-snapshot': 1.3.4 '@types/deep-equal': 1.0.1 - '@types/faker': 4.1.4 - '@types/mocha': 5.2.5 + '@types/faker': 4.1.5 + '@types/mocha': 5.2.6 '@types/sinon': 5.0.7 '@types/sinon-chai': 3.2.2 chai: 4.2.0 @@ -14296,25 +14247,20 @@ packages: deep-equal: 1.0.1 faker: 4.1.0 jsdom-global: 3.0.2 - make-dir-cli: 1.0.0 mocha: 5.2.0 - nyc: 13.1.0 + nyc: 13.3.0 rimraf: 2.6.3 - sinon: 7.2.2 - sinon-chai: /sinon-chai/3.3.0/chai@4.2.0+sinon@7.2.2 - source-map-loader: 0.2.4 - tslint: /tslint/5.12.1/typescript@3.1.6 + sinon: 7.2.5 + sinon-chai: /sinon-chai/3.3.0/chai@4.2.0+sinon@7.2.5 + tslint: /tslint/5.13.0/typescript@3.2.4 typedoc: 0.11.1 - typedoc-plugin-external-module-name: /typedoc-plugin-external-module-name/1.1.3/typedoc@0.11.1 typemoq: 2.1.0 - typescript: 3.1.6 - webpack: 4.28.4 - webpack-cli: /webpack-cli/3.2.1/webpack@4.28.4 + typescript: 3.2.4 xmlhttprequest: 1.8.0 dev: false name: '@rush-temp/presentation-frontend' resolution: - integrity: sha512-JW3rJfOnUgUutCxvF7HQXr21P3QNkcePvnLl5arSK0L5eiec6HLCMMPJwIQWpWt06+Govel0MDKDiLJ2YrPR1Q== + integrity: sha512-qqAWIB2LgNfpHo1zSEM66EfvoRvwRv1BboMJ00L3D7udAZNN3FOY9MEhT+uZ2BH0Uce4cjl2jN2Igd0QRmzrjw== tarball: 'file:projects/presentation-frontend.tgz' version: 0.0.0 'file:projects/presentation-integration-tests.tgz': @@ -14324,8 +14270,8 @@ packages: '@types/chai-jest-snapshot': 1.3.4 '@types/cpx': 1.5.0 '@types/deep-equal': 1.0.1 - '@types/faker': 4.1.4 - '@types/mocha': 5.2.5 + '@types/faker': 4.1.5 + '@types/mocha': 5.2.6 '@types/rimraf': 2.0.2 '@types/sinon': 5.0.7 '@types/sinon-chai': 3.2.2 @@ -14340,146 +14286,129 @@ packages: ignore-styles: 5.0.1 jsdom-global: 3.0.2 mocha: 5.2.0 - nyc: 13.1.0 rimraf: 2.6.3 - sinon: 7.2.2 - sinon-chai: /sinon-chai/3.3.0/chai@4.2.0+sinon@7.2.2 + sinon: 7.2.5 + sinon-chai: /sinon-chai/3.3.0/chai@4.2.0+sinon@7.2.5 ts-node: 7.0.1 - tslint: /tslint/5.12.1/typescript@3.1.6 + tslint: /tslint/5.13.0/typescript@3.2.4 typemoq: 2.1.0 - typescript: 3.1.6 + typescript: 3.2.4 xmlhttprequest: 1.8.0 dev: false name: '@rush-temp/presentation-integration-tests' resolution: - integrity: sha512-J7YZRarmRdEEcEh8qrDAB2BAi6LD3CO0q76+FPZ2Mr3+jQzMGNDi5Y2H48ora82xuVs/+ahOM0fy4iTbV9rvBQ== + integrity: sha512-hzLZREJ8h4az8l7GzFMeD+bw3yy9FDBRKtbr1YdU8vxB4ZwUQfNdq1WdeThl7RoG1n1HwlpdKaYVOTnv24PUvA== tarball: 'file:projects/presentation-integration-tests.tgz' version: 0.0.0 'file:projects/presentation-test-app.tgz': dependencies: - '@bentley/bwc': /@bentley/bwc/7.1.0/205ff1ecb36db98d7643dc5de57c3f08 - '@bentley/icons-generic-webfont': 0.0.6 + '@bentley/icons-generic-webfont': npm.bentley.com/@bentley/icons-generic-webfont/0.0.9 '@types/body-parser': 1.17.0 '@types/bunyan': 1.8.5 - '@types/express': 4.16.0 - '@types/react': 16.7.18 - '@types/react-dom': 16.0.7 + '@types/express': 4.16.1 + '@types/react': 16.7.22 + '@types/react-dom': 16.0.11 autoprefixer: 8.6.5 body-parser: 1.18.3 - cpx: 1.5.0 - css-loader: 0.28.11 - electron: 4.0.1 + electron: 4.0.6 electron-chromedriver: 2.0.0 express: 4.16.4 - file-loader: /file-loader/1.1.11/webpack@4.28.4 immutable: 3.8.2 - inspire-tree: 5.0.2 - lodash: 4.17.11 - mobx: 5.8.0 - mobx-react: /mobx-react/5.4.3/mobx@5.8.0+react@16.7.0 npm-run-all: 4.1.5 postcss-flexbugs-fixes: 3.3.1 postcss-loader: 2.1.6 - react: 16.7.0 - react-dnd: /react-dnd/5.0.0/react@16.7.0 - react-dnd-html5-backend: 5.0.1 - react-dom: /react-dom/16.7.0/react@16.7.0 - react-redux: /react-redux/5.1.1/react@16.7.0+redux@4.0.1 - redux: 4.0.1 + react: 16.8.3 + react-dom: /react-dom/16.8.3/react@16.8.3 rimraf: 2.6.3 - sass-loader: /sass-loader/7.1.0/webpack@4.28.4 semver: 5.6.0 - source-map-loader: 0.2.4 - style-loader: 0.21.0 - svg-sprite-loader: 3.9.2 - tslint: /tslint/5.12.1/typescript@3.1.6 - typescript: 3.1.6 - uglifyjs-webpack-plugin: /uglifyjs-webpack-plugin/1.3.0/webpack@4.28.4 - url-loader: /url-loader/1.1.2/webpack@4.28.4 - webpack: 4.28.4 + tslint: /tslint/5.13.0/typescript@3.2.4 + typescript: 3.2.4 dev: false name: '@rush-temp/presentation-test-app' resolution: - integrity: sha512-WS2vwi48I8q+7j64mkAu16u9zrA+9ivcAtKt7hkJA4VzXzZpZJe0iYBdzu0X7urRQNNtI8TSZ6ITGp5506mp6g== + integrity: sha512-J6rwSVzNg1dgAPyaONCJpkuc/9en0QpOXjpdreOvLZ+7kX7jcG2IzsbZxm7d0mH2bId+y5LcALylDjrrf2fIng== tarball: 'file:projects/presentation-test-app.tgz' version: 0.0.0 'file:projects/presentation-testing.tgz': dependencies: '@types/chai': 4.1.7 + '@types/chai-as-promised': 7.1.0 '@types/chai-jest-snapshot': 1.3.4 - '@types/jsdom': 12.2.1 - '@types/mocha': 5.2.5 + '@types/jsdom': 12.2.2 + '@types/mocha': 5.2.6 '@types/rimraf': 2.0.2 + '@types/sinon': 5.0.7 chai: 4.2.0 + chai-as-promised: /chai-as-promised/7.1.1/chai@4.2.0 chai-jest-snapshot: /chai-jest-snapshot/2.0.0/chai@4.2.0 cross-env: 5.2.0 ignore-styles: 5.0.1 jsdom-global: 3.0.2 make-dir: 1.3.0 mocha: 5.2.0 - nyc: 13.1.0 + nyc: 13.3.0 rimraf: 2.6.3 - tslint: /tslint/5.12.1/typescript@3.1.6 + sinon: 7.2.5 + tslint: /tslint/5.13.0/typescript@3.2.4 typedoc: 0.11.1 typemoq: 2.1.0 - typescript: 3.1.6 + typescript: 3.2.4 dev: false name: '@rush-temp/presentation-testing' resolution: - integrity: sha512-5pYiFf68kP3YZc9TYyXFyRT07OR3mr5ZzCPXBi0DT/aWwiWH3cxOk+gsA/sPU8IdWOx3kt1hvAnFTrr8YOVwOg== + integrity: sha512-WjtCmBZlMQpuyEwteLNLWMjPT1oqROjPsIDbREDYHQQnGX1RZj4ga9JW7/BRk9xew0dfBt64M4slHktq+URIbQ== tarball: 'file:projects/presentation-testing.tgz' version: 0.0.0 'file:projects/test-apps-analysis-importer.tgz': dependencies: '@types/body-parser': 1.17.0 - '@types/express': 4.16.0 - '@types/node': 10.10.3 + '@types/express': 4.16.1 + '@types/node': 10.12.18 body-parser: 1.18.3 child_process: 1.0.2 cpx: 1.5.0 express: 4.16.4 node-glob: 1.2.0 null-loader: 0.1.1 - popper.js: 1.14.6 + popper.js: 1.14.7 rimraf: 2.6.3 - tslint: /tslint/5.12.1/typescript@3.1.6 - typescript: 3.1.6 + tslint: /tslint/5.13.0/typescript@3.2.4 + typescript: 3.2.4 dev: false name: '@rush-temp/test-apps-analysis-importer' resolution: - integrity: sha512-Gc1j7bF2wFdsdTfMBWKQBk7n1+HpwHo4Hf0BEFogRufyo29oWThHLToNs+P1+a1Ch9O8xs6AujiKcmCt/Qdbzw== + integrity: sha512-pDqFWqJvn4UAxwN29lA77zTwKUoyrI1Dj85iAE2jzkRPYex3Tm42vgRKn1G8N22ab/lN+eJZBIXiiPTX2QjiGg== tarball: 'file:projects/test-apps-analysis-importer.tgz' version: 0.0.0 'file:projects/test-apps-synchro-schedule-importer.tgz': dependencies: '@types/fs-extra': 4.0.8 - '@types/lodash': 4.14.119 - '@types/node': 10.10.3 - '@types/yargs': 12.0.5 + '@types/lodash': 4.14.121 + '@types/node': 10.12.18 + '@types/yargs': 12.0.9 cpx: 1.5.0 fs-extra: 6.0.1 rimraf: 2.6.3 - tslint: /tslint/5.12.1/typescript@3.1.6 - typescript: 3.1.6 + tslint: /tslint/5.13.0/typescript@3.2.4 + typescript: 3.2.4 yargs: 12.0.5 dev: false name: '@rush-temp/test-apps-synchro-schedule-importer' resolution: - integrity: sha512-3/Si6Q86jbop2yNHPyfeUoiG0uCeptELQnfvjSTJrUkXGJ8BiLphirSnSA9f0kClgYdRc+5myksVYeR+wQt2bA== + integrity: sha512-O6iev7poymV7smXRLiHthQbrADIR2Jsohf68i4q4v88BQaVVLn4nGmPzxXDHe9+AxRvD0RE225LtsJtqLWpVQQ== tarball: 'file:projects/test-apps-synchro-schedule-importer.tgz' version: 0.0.0 'file:projects/testbed.tgz': dependencies: '@types/body-parser': 1.17.0 '@types/chai': 4.1.7 - '@types/express': 4.16.0 + '@types/express': 4.16.1 '@types/fs-extra': 4.0.8 '@types/i18next': 8.4.6 - '@types/i18next-browser-languagedetector': 2.0.1 - '@types/i18next-xhr-backend': 1.4.1 + '@types/i18next-browser-languagedetector': 2.0.2 '@types/js-base64': 2.3.1 - '@types/mocha': 5.2.5 - '@types/node': 10.10.3 + '@types/mocha': 5.2.6 + '@types/node': 10.12.18 '@types/semver': 5.5.0 '@types/webpack': 3.8.17 '@types/ws': 6.0.1 @@ -14487,57 +14416,56 @@ packages: chai: 4.2.0 commander: 2.19.0 cpx: 1.5.0 - electron: 4.0.1 + electron: 4.0.6 express: 4.16.4 find-root: 1.1.0 fs-extra: 6.0.1 - fuse.js: 3.3.0 + fuse.js: 3.4.2 glob: 7.1.3 i18next: 10.6.0 i18next-browser-languagedetector: 2.2.4 - i18next-xhr-backend: 1.5.1 + i18next-xhr-backend: 2.0.1 istanbul-lib-hook: 1.2.2 istanbul-lib-instrument: 2.3.2 - js-base64: 2.5.0 + js-base64: 2.5.1 mocha: 5.2.0 node-glob: 1.2.0 null-loader: 0.1.1 - nyc: 13.1.0 + nyc: 13.3.0 object-assign: 4.1.1 - resolve: 1.9.0 + resolve: 1.10.0 rimraf: 2.6.3 save: 2.3.3 semver: 5.6.0 source-map-loader: 0.2.4 - tslint: /tslint/5.12.1/typescript@3.1.6 - typescript: 3.1.6 - webpack: 4.28.4 - ws: 6.1.2 + tslint: /tslint/5.13.0/typescript@3.2.4 + typescript: 3.2.4 + webpack: 4.29.6 + ws: 6.1.4 dev: false name: '@rush-temp/testbed' resolution: - integrity: sha512-QLd8k+maA/X0CjnuF85DszbjNFOYNdVIvHagRZ+7P/Y1ljyFYGByvtUE0avY7XvPI423p5vmNb76ZA6DWebrFA== + integrity: sha512-j9RVEvFp0653rAmWPnND9TY6l3hZQHFNQq+NyPNez+7RY9EuGHvMHvMqW4/AVD4TlspLrHbJdXN3fsjWGuNAyw== tarball: 'file:projects/testbed.tgz' version: 0.0.0 'file:projects/ui-components.tgz': dependencies: - '@bentley/bwc': /@bentley/bwc/7.1.0/205ff1ecb36db98d7643dc5de57c3f08 - '@bentley/icons-generic-webfont': 0.0.6 + '@bentley/icons-generic-webfont': npm.bentley.com/@bentley/icons-generic-webfont/0.0.9 '@types/chai': 4.1.7 '@types/chai-as-promised': 7.1.0 '@types/chai-jest-snapshot': 1.3.4 '@types/chai-spies': 1.0.0 '@types/chai-string': 1.4.1 '@types/classnames': 2.2.7 - '@types/enzyme': 3.1.15 - '@types/faker': 4.1.4 - '@types/lodash': 4.14.119 - '@types/mocha': 5.2.5 - '@types/react': 16.7.18 - '@types/react-data-grid': 4.0.1 - '@types/react-dom': 16.0.7 + '@types/enzyme': 3.9.0 + '@types/faker': 4.1.5 + '@types/lodash': 4.14.121 + '@types/mocha': 5.2.6 + '@types/react': 16.7.22 + '@types/react-data-grid': 4.0.2 + '@types/react-dom': 16.0.11 '@types/react-highlight-words': 0.11.1 - '@types/react-resize-detector': 3.1.0 + '@types/react-resize-detector': 3.1.1 '@types/react-virtualized': 9.18.12 '@types/sinon': 5.0.7 '@types/sinon-chai': 3.2.2 @@ -14548,11 +14476,10 @@ packages: chai-spies: /chai-spies/1.0.0/chai@4.2.0 chai-string: /chai-string/1.5.0/chai@4.2.0 classnames: 2.2.6 - cpx: 1.5.0 cross-env: 5.2.0 - enzyme: 3.8.0 - enzyme-adapter-react-16: /enzyme-adapter-react-16/1.7.1/84ec9ac6a98d71b6c46b2abae79a4882 - enzyme-to-json: /enzyme-to-json/3.3.5/enzyme@3.8.0 + enzyme: 3.9.0 + enzyme-adapter-react-16: /enzyme-adapter-react-16/1.10.0/ca330bebd2626bd9cb8e393ae8a11715 + enzyme-to-json: /enzyme-to-json/3.3.5/enzyme@3.9.0 eventemitter2: 5.0.1 faker: 4.1.0 ignore-styles: 5.0.1 @@ -14560,56 +14487,50 @@ packages: jsdom: 11.12.0 jsdom-global: /jsdom-global/3.0.2/jsdom@11.12.0 lodash: 4.17.11 - make-dir-cli: 1.0.0 mocha: 5.2.0 - nyc: 13.1.0 + nyc: 13.3.0 raf: 3.4.1 - react: 16.7.0 - react-data-grid: /react-data-grid/6.0.1/react-dom@16.7.0+react@16.7.0 - react-dnd: /react-dnd/5.0.0/react@16.7.0 + react: 16.8.3 + react-data-grid: /react-data-grid/6.0.1/react-dom@16.8.3+react@16.8.3 + react-dnd: /react-dnd/5.0.0/react@16.8.3 react-dnd-html5-backend: 5.0.1 react-dnd-test-backend: 5.0.1 - react-dom: /react-dom/16.7.0/react@16.7.0 - react-highlight-words: /react-highlight-words/0.14.0/react@16.7.0 - react-resize-detector: /react-resize-detector/3.4.0/react@16.7.0 - react-testing-library: /react-testing-library/5.4.4/react-dom@16.7.0 - react-virtualized: /react-virtualized/9.21.0/react-dom@16.7.0+react@16.7.0 + react-dom: /react-dom/16.8.3/react@16.8.3 + react-highlight-words: /react-highlight-words/0.14.0/react@16.8.3 + react-resize-detector: /react-resize-detector/3.4.0/react@16.8.3 + react-testing-library: /react-testing-library/5.9.0/react-dom@16.8.3+react@16.8.3 + react-virtualized: /react-virtualized/9.21.0/react-dom@16.8.3+react@16.8.3 resize-observer-polyfill: 1.5.1 rimraf: 2.6.3 - sinon: 7.2.2 - sinon-chai: /sinon-chai/3.3.0/chai@4.2.0+sinon@7.2.2 - source-map-loader: 0.2.4 + sinon: 7.2.5 + sinon-chai: /sinon-chai/3.3.0/chai@4.2.0+sinon@7.2.5 ts-key-enum: 2.0.0 ts-node: 7.0.1 - tsconfig-paths: 3.7.0 - tslint: /tslint/5.12.1/typescript@3.1.6 + tsconfig-paths: 3.8.0 + tslint: /tslint/5.13.0/typescript@3.2.4 typedoc: 0.11.1 - typedoc-plugin-external-module-name: /typedoc-plugin-external-module-name/1.1.3/typedoc@0.11.1 typemoq: 2.1.0 - typescript: 3.1.6 - webpack: 4.28.4 - webpack-cli: /webpack-cli/3.2.1/webpack@4.28.4 + typescript: 3.2.4 xmlhttprequest: 1.8.0 dev: false name: '@rush-temp/ui-components' resolution: - integrity: sha512-2MxoUTrtjaD9hf03pPNnlTC0KOoHW72c5M6Ktv5PE0KAXX+7KC55wKRaL9NNQcm1zL2MfqTraRC3b3t2zvp+WA== + integrity: sha512-jejtULJs2WE62Y4VIak2xVskUZDfb2Tfq28TveifSoSfBdBfBULon4NTbazfJkte2YlqvROjXa1+kZL2/ZyfUQ== tarball: 'file:projects/ui-components.tgz' version: 0.0.0 'file:projects/ui-core.tgz': dependencies: - '@bentley/bwc': /@bentley/bwc/7.1.0/205ff1ecb36db98d7643dc5de57c3f08 - '@bentley/icons-generic-webfont': 0.0.6 + '@bentley/icons-generic-webfont': npm.bentley.com/@bentley/icons-generic-webfont/0.0.9 '@types/chai': 4.1.7 '@types/chai-as-promised': 7.1.0 '@types/chai-jest-snapshot': 1.3.4 '@types/chai-spies': 1.0.0 '@types/classnames': 2.2.7 - '@types/enzyme': 3.1.15 - '@types/mocha': 5.2.5 - '@types/node': 10.10.3 - '@types/react': 16.7.18 - '@types/react-dom': 16.0.7 + '@types/enzyme': 3.9.0 + '@types/mocha': 5.2.6 + '@types/node': 10.12.18 + '@types/react': 16.7.22 + '@types/react-dom': 16.0.11 '@types/sinon': 5.0.7 '@types/sinon-chai': 3.2.2 chai: 4.2.0 @@ -14617,56 +14538,49 @@ packages: chai-jest-snapshot: /chai-jest-snapshot/2.0.0/chai@4.2.0 chai-spies: /chai-spies/1.0.0/chai@4.2.0 classnames: 2.2.6 - cpx: 1.5.0 cross-env: 5.2.0 - csstype: 2.6.0 - enzyme: 3.8.0 - enzyme-adapter-react-16: /enzyme-adapter-react-16/1.7.1/84ec9ac6a98d71b6c46b2abae79a4882 - enzyme-to-json: /enzyme-to-json/3.3.5/enzyme@3.8.0 + csstype: 2.6.2 + enzyme: 3.9.0 + enzyme-adapter-react-16: /enzyme-adapter-react-16/1.10.0/ca330bebd2626bd9cb8e393ae8a11715 + enzyme-to-json: /enzyme-to-json/3.3.5/enzyme@3.9.0 ignore-styles: 5.0.1 jsdom: 11.12.0 jsdom-global: /jsdom-global/3.0.2/jsdom@11.12.0 - make-dir-cli: 1.0.0 mocha: 5.2.0 - nyc: 13.1.0 + nyc: 13.3.0 raf: 3.4.1 - react: 16.7.0 - react-dom: /react-dom/16.7.0/react@16.7.0 - react-testing-library: /react-testing-library/5.4.4/react-dom@16.7.0 + react: 16.8.3 + react-dom: /react-dom/16.8.3/react@16.8.3 + react-testing-library: /react-testing-library/5.9.0/react-dom@16.8.3+react@16.8.3 rimraf: 2.6.3 - sinon: 7.2.2 - sinon-chai: /sinon-chai/3.3.0/chai@4.2.0+sinon@7.2.2 - source-map-loader: 0.2.4 + sinon: 7.2.5 + sinon-chai: /sinon-chai/3.3.0/chai@4.2.0+sinon@7.2.5 ts-node: 7.0.1 - tsconfig-paths: 3.7.0 - tslint: /tslint/5.12.1/typescript@3.1.6 + tsconfig-paths: 3.8.0 + tslint: /tslint/5.13.0/typescript@3.2.4 typedoc: 0.11.1 - typedoc-plugin-external-module-name: /typedoc-plugin-external-module-name/1.1.3/typedoc@0.11.1 - typescript: 3.1.6 - webpack: 4.28.4 - webpack-cli: /webpack-cli/3.2.1/webpack@4.28.4 + typescript: 3.2.4 xmlhttprequest: 1.8.0 dev: false name: '@rush-temp/ui-core' resolution: - integrity: sha512-e6PDRp56KfhtlOt5j1JpMsYHEbNiuoXw4X1YlOruGfHPJ4hOpw1lkqrqCJOvB3uFWO4eBt7MMHWXPAJS8u6YGQ== + integrity: sha512-Jn/bwlB2RA2wa6HGsPMVD/lDKQvSiYMOzrrM51+QH5bGw3zKXUr5x50ceqcPjzo2OBSdlVhVx4v8u4ikNZGMIA== tarball: 'file:projects/ui-core.tgz' version: 0.0.0 'file:projects/ui-framework.tgz': dependencies: - '@bentley/bwc': /@bentley/bwc/7.1.0/205ff1ecb36db98d7643dc5de57c3f08 - '@bentley/icons-generic-webfont': 0.0.6 + '@bentley/icons-generic-webfont': npm.bentley.com/@bentley/icons-generic-webfont/0.0.9 '@types/chai': 4.1.7 '@types/chai-as-promised': 7.1.0 '@types/chai-jest-snapshot': 1.3.4 '@types/chai-spies': 1.0.0 '@types/classnames': 2.2.7 - '@types/enzyme': 3.1.15 - '@types/lodash': 4.14.119 - '@types/mocha': 5.2.5 - '@types/react': 16.7.18 - '@types/react-dom': 16.0.7 - '@types/react-redux': 5.0.21 + '@types/enzyme': 3.9.0 + '@types/lodash': 4.14.121 + '@types/mocha': 5.2.6 + '@types/react': 16.7.22 + '@types/react-dom': 16.0.11 + '@types/react-redux': 7.0.1 '@types/rimraf': 2.0.2 '@types/sinon': 5.0.7 '@types/sinon-chai': 3.2.2 @@ -14675,62 +14589,57 @@ packages: chai-jest-snapshot: /chai-jest-snapshot/2.0.0/chai@4.2.0 chai-spies: /chai-spies/1.0.0/chai@4.2.0 classnames: 2.2.6 - cpx: 1.5.0 cross-env: 5.2.0 - enzyme: 3.8.0 - enzyme-adapter-react-16: /enzyme-adapter-react-16/1.7.1/84ec9ac6a98d71b6c46b2abae79a4882 - enzyme-to-json: /enzyme-to-json/3.3.5/enzyme@3.8.0 + enzyme: 3.9.0 + enzyme-adapter-react-16: /enzyme-adapter-react-16/1.10.0/ca330bebd2626bd9cb8e393ae8a11715 + enzyme-to-json: /enzyme-to-json/3.3.5/enzyme@3.9.0 ignore-styles: 5.0.1 immutable: 3.8.2 jsdom: 11.12.0 jsdom-global: /jsdom-global/3.0.2/jsdom@11.12.0 lodash: 4.17.11 - make-dir-cli: 1.0.0 mocha: 5.2.0 - nyc: 13.1.0 + nyc: 13.3.0 oidc-client: 1.6.1 raf: 3.4.1 - react: 16.7.0 - react-dnd: /react-dnd/5.0.0/react@16.7.0 + react: 16.8.3 + react-dnd: /react-dnd/5.0.0/react@16.8.3 react-dnd-html5-backend: 5.0.1 - react-dom: /react-dom/16.7.0/react@16.7.0 - react-redux: /react-redux/5.1.1/react@16.7.0+redux@4.0.1 + react-dom: /react-dom/16.8.3/react@16.8.3 + react-redux: /react-redux/5.1.1/react@16.8.3+redux@4.0.1 react-split-pane: 0.1.77 - react-testing-library: /react-testing-library/5.4.4/react-dom@16.7.0 + react-testing-library: /react-testing-library/5.9.0/react-dom@16.8.3+react@16.8.3 redux: 4.0.1 rimraf: 2.6.3 - sinon: 7.2.2 - sinon-chai: /sinon-chai/3.3.0/chai@4.2.0+sinon@7.2.2 - source-map-loader: 0.2.4 + sinon: 7.2.5 + sinon-chai: /sinon-chai/3.3.0/chai@4.2.0+sinon@7.2.5 ts-node: 7.0.1 - tsconfig-paths: 3.7.0 - tslint: /tslint/5.12.1/typescript@3.1.6 + tsconfig-paths: 3.8.0 + tslint: /tslint/5.13.0/typescript@3.2.4 typedoc: 0.11.1 - typedoc-plugin-external-module-name: /typedoc-plugin-external-module-name/1.1.3/typedoc@0.11.1 typemoq: 2.1.0 - typescript: 3.1.6 - webpack: 4.28.4 - webpack-cli: /webpack-cli/3.2.1/webpack@4.28.4 + typescript: 3.2.4 xmlhttprequest: 1.8.0 dev: false name: '@rush-temp/ui-framework' resolution: - integrity: sha512-u4QBxojtwoaujPz9JCMsvxwBCZvdfm1pX7HixTXjaJxs+r7SikuNC7UvibzAGQJcEWQClot/rRls3TSdImUrTQ== + integrity: sha512-eycJxMRlrTpdEHGftnctelz3VGlOQVBaTUUL3Dzq1CDoy+Z5BoQlkcxgqAmijhzeXnLkBW1QQp9tbE8gF2uCDQ== tarball: 'file:projects/ui-framework.tgz' version: 0.0.0 'file:projects/ui-ninezone.tgz': dependencies: - '@bentley/bwc': /@bentley/bwc/7.1.0/205ff1ecb36db98d7643dc5de57c3f08 - '@bentley/icons-generic-webfont': 0.0.6 + '@bentley/icons-generic-webfont': npm.bentley.com/@bentley/icons-generic-webfont/0.0.9 '@types/chai': 4.1.7 '@types/chai-as-promised': 7.1.0 '@types/chai-jest-snapshot': 1.3.4 '@types/chai-spies': 1.0.0 '@types/classnames': 2.2.7 - '@types/enzyme': 3.1.15 - '@types/mocha': 5.2.5 - '@types/react': 16.7.18 - '@types/react-dom': 16.0.7 + '@types/enzyme': 3.9.0 + '@types/highlight.js': 9.12.3 + '@types/mocha': 5.2.6 + '@types/node': 10.12.18 + '@types/react': 16.7.22 + '@types/react-dom': 16.0.11 '@types/react-router-dom': 4.3.1 '@types/sinon': 5.0.7 chai: 4.2.0 @@ -14738,181 +14647,174 @@ packages: chai-jest-snapshot: /chai-jest-snapshot/2.0.0/chai@4.2.0 chai-spies: /chai-spies/1.0.0/chai@4.2.0 classnames: 2.2.6 - cpx: 1.5.0 cross-env: 5.2.0 css-loader: 0.28.11 - enzyme: 3.8.0 - enzyme-adapter-react-16: /enzyme-adapter-react-16/1.7.1/84ec9ac6a98d71b6c46b2abae79a4882 - enzyme-to-json: /enzyme-to-json/3.3.5/enzyme@3.8.0 - html-webpack-plugin: /html-webpack-plugin/4.0.0-alpha.2/webpack@4.28.4 + enzyme: 3.9.0 + enzyme-adapter-react-16: /enzyme-adapter-react-16/1.10.0/ca330bebd2626bd9cb8e393ae8a11715 + enzyme-to-json: /enzyme-to-json/3.3.5/enzyme@3.9.0 + highlight.js: 9.14.2 + html-webpack-plugin: /html-webpack-plugin/4.0.0-alpha.2/webpack@4.29.6 ignore-styles: 5.0.1 jsdom: 11.12.0 jsdom-global: /jsdom-global/3.0.2/jsdom@11.12.0 - make-dir-cli: 1.0.0 mocha: 5.2.0 - nyc: 13.1.0 + nyc: 13.3.0 raf: 3.4.1 raf-schd: 4.0.0 raw-loader: 0.5.1 - react: 16.7.0 - react-dom: /react-dom/16.7.0/react@16.7.0 - react-markdown: /react-markdown/3.6.0/react@16.7.0 - react-router-dom: /react-router-dom/4.3.1/react@16.7.0 + react: 16.8.3 + react-dom: /react-dom/16.8.3/react@16.8.3 + react-markdown: /react-markdown/3.6.0/react@16.8.3 + react-router-dom: /react-router-dom/4.3.1/react@16.8.3 rimraf: 2.6.3 - sass-loader: /sass-loader/7.1.0/webpack@4.28.4 - sinon: 7.2.2 - sinon-chai: /sinon-chai/3.3.0/chai@4.2.0+sinon@7.2.2 + sass-loader: /sass-loader/7.1.0/webpack@4.29.6 + sinon: 7.2.5 + sinon-chai: /sinon-chai/3.3.0/chai@4.2.0+sinon@7.2.5 source-map-loader: 0.2.4 style-loader: 0.21.0 svg-react-loader: 0.4.6 ts-loader: 4.5.0 ts-node: 7.0.1 - tsconfig-paths: 3.7.0 - tslint: /tslint/5.12.1/typescript@3.1.6 - tslint-loader: /tslint-loader/3.6.0/tslint@5.12.1 + tsconfig-paths: 3.8.0 + tslint: /tslint/5.13.0/typescript@3.2.4 + tslint-loader: /tslint-loader/3.6.0/tslint@5.13.0 typedoc: 0.11.1 - typedoc-plugin-external-module-name: /typedoc-plugin-external-module-name/1.1.3/typedoc@0.11.1 typemoq: 2.1.0 - typescript: 3.1.6 - url-loader: /url-loader/1.1.2/webpack@4.28.4 - webpack: 4.28.4 - webpack-cli: /webpack-cli/3.2.1/webpack@4.28.4 - webpack-dev-server: /webpack-dev-server/3.1.14/webpack@4.28.4 + typescript: 3.2.4 + url-loader: /url-loader/1.1.2/webpack@4.29.6 + webpack: 4.29.6 + webpack-cli: /webpack-cli/3.2.3/webpack@4.29.6 + webpack-dev-server: /webpack-dev-server/3.2.1/webpack@4.29.6 + webpack-merge: 4.2.1 dev: false name: '@rush-temp/ui-ninezone' resolution: - integrity: sha512-6TuuHNIXZ2BvkenOSOjnPJAgRI3fW/bswpvXkgamNQ8raKxfb3PKxLDEBel28HkiuAUTvLZZYRLKhSUawcfCEw== + integrity: sha512-PhoZFUqDLcQEbN0MzEPolKxnHB2lIVvfEJZQhRwGXVVBjVHa7ei05aeBCizR8UcXlW+35LTt9b8XX1E6roRHgw== tarball: 'file:projects/ui-ninezone.tgz' version: 0.0.0 'file:projects/ui-test-app.tgz': dependencies: - '@bentley/bwc': /@bentley/bwc/7.1.0/205ff1ecb36db98d7643dc5de57c3f08 - '@bentley/icons-generic-webfont': 0.0.6 + '@bentley/icons-generic-webfont': npm.bentley.com/@bentley/icons-generic-webfont/0.0.9 '@types/body-parser': 1.17.0 - '@types/express': 4.16.0 - '@types/react': 16.7.18 - '@types/react-dom': 16.0.7 - '@types/react-redux': 5.0.21 + '@types/express': 4.16.1 + '@types/react': 16.7.22 + '@types/react-dom': 16.0.11 + '@types/react-redux': 7.0.1 '@types/semver': 5.5.0 autoprefixer: 8.6.5 body-parser: 1.18.3 - cpx: 1.5.0 css-loader: 0.28.11 - electron: 4.0.1 + electron: 4.0.6 electron-chromedriver: 2.0.0 express: 4.16.4 - file-loader: /file-loader/1.1.11/webpack@4.28.4 + file-loader: /file-loader/1.1.11/webpack@4.29.6 immutable: 3.8.2 - inspire-tree: 5.0.2 - lodash: 4.17.11 - mobx: 5.8.0 - mobx-react: /mobx-react/5.4.3/mobx@5.8.0+react@16.7.0 + mobx: 5.9.0 + mobx-react: /mobx-react/5.4.3/mobx@5.9.0+react@16.8.3 npm-run-all: 4.1.5 postcss-flexbugs-fixes: 3.3.1 postcss-loader: 2.1.6 - react: 16.7.0 - react-dnd: /react-dnd/5.0.0/react@16.7.0 - react-dnd-html5-backend: 5.0.1 - react-dom: /react-dom/16.7.0/react@16.7.0 - react-redux: /react-redux/5.1.1/react@16.7.0+redux@4.0.1 - react-test-renderer: /react-test-renderer/16.7.0/react@16.7.0 + react: 16.8.3 + react-dom: /react-dom/16.8.3/react@16.8.3 + react-test-renderer: /react-test-renderer/16.8.3/react@16.8.3 redux: 4.0.1 rimraf: 2.6.3 - sass-loader: /sass-loader/7.1.0/webpack@4.28.4 + sass-loader: /sass-loader/7.1.0/webpack@4.29.6 semver: 5.6.0 - source-map-loader: 0.2.4 style-loader: 0.21.0 svg-sprite-loader: 3.9.2 - tsconfig-paths: 3.7.0 - tslint: /tslint/5.12.1/typescript@3.1.6 - typescript: 3.1.6 - uglifyjs-webpack-plugin: /uglifyjs-webpack-plugin/1.3.0/webpack@4.28.4 - url-loader: /url-loader/1.1.2/webpack@4.28.4 - webpack: 4.28.4 + tsconfig-paths: 3.8.0 + tslint: /tslint/5.13.0/typescript@3.2.4 + typescript: 3.2.4 + uglifyjs-webpack-plugin: /uglifyjs-webpack-plugin/1.3.0/webpack@4.29.6 + url-loader: /url-loader/1.1.2/webpack@4.29.6 + webpack: 4.29.6 dev: false name: '@rush-temp/ui-test-app' resolution: - integrity: sha512-T1qGdbVjSTZRWTrU6sK27G70LOidPHNvYesbrFiZ3mJULiIy1m8tp7JCG3E9ESGweSgUmVbIsGSNdsjuLuy0Lw== + integrity: sha512-qGq9WcNpm2ZIKFtoijlHK7ToJMN8Er3MSZMCT6GikWze4Wl58hgyb+C3SEGcax0u/ofrcfnBvrgOZgs539yY8A== tarball: 'file:projects/ui-test-app.tgz' version: 0.0.0 'file:projects/webpack-tools.tgz': dependencies: '@types/chai': 4.1.7 - '@types/enzyme': 3.1.15 - '@types/mocha': 5.2.5 - '@types/node': 10.10.3 - '@types/react': 16.7.18 - '@types/react-dom': 16.0.7 + '@types/enzyme': 3.9.0 + '@types/glob': 5.0.36 + '@types/mocha': 5.2.6 + '@types/node': 10.12.18 + '@types/react': 16.7.22 + '@types/react-dom': 16.0.11 '@types/sinon': 5.0.7 - '@types/webdriverio': 4.13.1 + '@types/webdriverio': 4.13.3 + '@types/yargs': 12.0.9 app-root-path: 2.1.0 autoprefixer: 8.6.5 - case-sensitive-paths-webpack-plugin: 2.1.2 + case-sensitive-paths-webpack-plugin: 2.2.0 chai: 4.2.0 chai-jest-snapshot: /chai-jest-snapshot/2.0.0/chai@4.2.0 chalk: 2.4.2 - chromedriver: 2.45.0 cli-highlight: 2.0.0 concurrently: 3.6.1 + cpx: 1.5.0 css-loader: 0.28.11 dotenv: 6.2.0 - electron-chromedriver: 2.0.0 - enzyme: 3.8.0 - enzyme-adapter-react-16: /enzyme-adapter-react-16/1.7.1/84ec9ac6a98d71b6c46b2abae79a4882 - enzyme-to-json: /enzyme-to-json/3.3.5/enzyme@3.8.0 + enzyme: 3.9.0 + enzyme-adapter-react-16: /enzyme-adapter-react-16/1.10.0/ca330bebd2626bd9cb8e393ae8a11715 + enzyme-to-json: /enzyme-to-json/3.3.5/enzyme@3.9.0 escape-string-regexp: 1.0.5 exports-loader: 0.7.0 - file-loader: /file-loader/1.1.11/webpack@4.28.4 - fork-ts-checker-webpack-plugin: /fork-ts-checker-webpack-plugin/0.4.15/3d5b451f3b58df190ea699c3e34587f4 + file-loader: /file-loader/1.1.11/webpack@4.29.6 + fork-ts-checker-webpack-plugin: /fork-ts-checker-webpack-plugin/0.4.15/7d74fab92916d4959c8fedd113bb5af2 fs-extra: 6.0.1 glob: 7.1.3 - html-webpack-plugin: /html-webpack-plugin/4.0.0-alpha.2/webpack@4.28.4 + html-webpack-plugin: /html-webpack-plugin/4.0.0-alpha.2/webpack@4.29.6 imports-loader: 0.8.0 - istanbul-instrumenter-loader: /istanbul-instrumenter-loader/3.0.1/webpack@4.28.4 + istanbul-instrumenter-loader: /istanbul-instrumenter-loader/3.0.1/webpack@4.29.6 jsdom: 11.12.0 license-webpack-plugin: 1.5.0 lodash: 4.17.11 - mini-css-extract-plugin: /mini-css-extract-plugin/0.4.5/webpack@4.28.4 + mini-css-extract-plugin: /mini-css-extract-plugin/0.4.5/webpack@4.29.6 mocha: 5.2.0 mocha-junit-reporter: /mocha-junit-reporter/1.18.0/mocha@5.2.0 - mocha-webpack: /mocha-webpack/2.0.0-beta.0/mocha@5.2.0+webpack@4.28.4 + mocha-webpack: /mocha-webpack/2.0.0-beta.0/mocha@5.2.0+webpack@4.29.6 node-gyp: 3.6.2 node-sass: 4.11.0 - nodemon: 1.18.9 + nodemon: 1.18.10 null-loader: 0.1.1 - nyc: 13.1.0 + nyc: 13.3.0 object-assign: 4.1.1 postcss-flexbugs-fixes: 3.3.1 postcss-loader: 2.1.6 promise: 8.0.2 - react: 16.7.0 + react: 16.8.3 react-dev-utils: 6.1.1 - react-dom: /react-dom/16.7.0/react@16.7.0 - react-test-renderer: /react-test-renderer/16.7.0/react@16.7.0 + react-dom: /react-dom/16.8.3/react@16.8.3 + react-test-renderer: /react-test-renderer/16.8.3/react@16.8.3 readline: 1.3.0 - sass-loader: /sass-loader/7.1.0/webpack@4.28.4 - sinon: 7.2.2 + rimraf: 2.6.3 + sass-loader: /sass-loader/7.1.0/webpack@4.29.6 + sinon: 7.2.5 source-map-loader: 0.2.4 style-loader: 0.21.0 svg-sprite-loader: 3.9.2 - sw-precache-webpack-plugin: /sw-precache-webpack-plugin/0.11.5/webpack@4.28.4 + sw-precache-webpack-plugin: /sw-precache-webpack-plugin/0.11.5/webpack@4.29.6 tree-kill: 1.2.1 ts-loader: 4.5.0 - tslint: /tslint/5.12.1/typescript@3.1.6 - tslint-loader: /tslint-loader/3.6.0/tslint@5.12.1 - typescript: 3.1.6 - uglifyjs-webpack-plugin: /uglifyjs-webpack-plugin/1.3.0/webpack@4.28.4 - url-loader: /url-loader/1.1.2/webpack@4.28.4 - wdio-chromedriver-service: /wdio-chromedriver-service/0.1.5/chromedriver@2.45.0 + tslint: /tslint/5.13.0/typescript@3.2.4 + tslint-loader: /tslint-loader/3.6.0/tslint@5.13.0 + typescript: 3.2.4 + uglifyjs-webpack-plugin: /uglifyjs-webpack-plugin/1.3.0/webpack@4.29.6 + url-loader: /url-loader/1.1.2/webpack@4.29.6 + wdio-chromedriver-service: 0.1.5 wdio-junit-reporter: 0.4.4 wdio-mocha-framework: 0.6.4 wdio-screenshot: /wdio-screenshot/0.6.0/webdriverio@4.14.2 wdio-spec-reporter: 0.1.5 wdio-visual-regression-service: /wdio-visual-regression-service/0.9.0/webdriverio@4.14.2 webdriverio: 4.14.2 - webpack: 4.28.4 - webpack-dev-server: /webpack-dev-server/3.1.14/webpack@4.28.4 - webpack-manifest-plugin: /webpack-manifest-plugin/2.0.3/webpack@4.28.4 + webpack: 4.29.6 + webpack-dev-server: /webpack-dev-server/3.2.1/webpack@4.29.6 + webpack-manifest-plugin: /webpack-manifest-plugin/2.0.3/webpack@4.29.6 webpack-merge: 4.2.1 webpack-node-externals: 1.7.2 whatwg-fetch: 2.0.4 @@ -14921,17 +14823,61 @@ packages: dev: false name: '@rush-temp/webpack-tools' resolution: - integrity: sha512-urc2Tv+dkwuf0/VnubKZYEOuUy/IMxHg+bmD4g7quWTjcmRBW1IpUow+0BfUXGzTgqLWisNgAJ3pfMKeSTNgIg== + integrity: sha512-NLqqZ70QuCXtDwn7Zo8buFsafYoaMjME/OSGYGmKUS20g+s2ZX6qth+z287KeHcFTkGZ+j68nqw2oS4Lx+fqxw== tarball: 'file:projects/webpack-tools.tgz' version: 0.0.0 + 'file:projects/webworker-test-app.tgz': + dependencies: + rimraf: 2.6.3 + tsconfig-paths: 3.8.0 + tslint: /tslint/5.13.0/typescript@3.2.4 + typescript: 3.2.4 + dev: false + name: '@rush-temp/webworker-test-app' + resolution: + integrity: sha512-6Qn1G1Zp/OPmdPRSaOxiOg8C+P2dXI53UDC7k9lMeL/y3CWEFGTVZDUtaozHKuRpw0E5d/5V46FWtD2QSGREpw== + tarball: 'file:projects/webworker-test-app.tgz' + version: 0.0.0 + npm.bentley.com/@bentley/dev-cors-proxy-server/0.0.9: + dependencies: + http-proxy: 1.11.1 + https-proxy-agent: 2.2.1 + proxy-from-env: 0.0.1 + dev: false + engines: + node: '>=0.10.0' + npm: '>=1.1.0' + name: '@bentley/dev-cors-proxy-server' + resolution: + integrity: sha1-OpkRx4NAa0xoTPpQ/HzBEEGyA7c= + registry: 'https://npm.bentley.com/npm/npm/' + tarball: 'https://npm.bentley.com/npm/npm/@bentley/dev-cors-proxy-server/-/dev-cors-proxy-server-0.0.9.tgz' + version: 0.0.9 + npm.bentley.com/@bentley/icons-generic-webfont/0.0.9: + dev: false + name: '@bentley/icons-generic-webfont' + resolution: + integrity: sha1-7y3D0hEmWcXtLOnlfenkgdGTvAQ= + registry: 'https://npm.bentley.com/npm/npm/' + tarball: 'https://npm.bentley.com/npm/npm/@bentley/icons-generic-webfont/-/icons-generic-webfont-0.0.9.tgz' + version: 0.0.9 + npm.bentley.com/@bentley/imodeljs-native/0.99.0: + dev: false + name: '@bentley/imodeljs-native' + requiresBuild: true + resolution: + integrity: sha1-2ZZdlq9Hdoe+gTESP8lq5ymoqRQ= + registry: 'https://npm.bentley.com/npm/npm/' + tarball: 'https://npm.bentley.com/npm/npm/@bentley/imodeljs-native/-/imodeljs-native-0.99.0.tgz' + version: 0.99.0 registry: 'https://registry.npmjs.org/' shrinkwrapMinorVersion: 9 shrinkwrapVersion: 3 specifiers: - '@bentley/bwc': ^7.1.0 '@bentley/dev-cors-proxy-server': 0.0.9 - '@bentley/icons-generic-webfont': ^0.0.6 - '@bentley/imodeljs-native': ~0.86.0 + '@bentley/icons-generic-webfont': ^0.0.9 + '@bentley/imodeljs-native': ~0.99.0 + '@microsoft/api-extractor': ^6.3.0 '@openid/appauth': ^1.1.1 '@rush-temp/agent-test-app': 'file:./projects/agent-test-app.tgz' '@rush-temp/bentleyjs-core': 'file:./projects/bentleyjs-core.tgz' @@ -14954,6 +14900,8 @@ specifiers: '@rush-temp/imodeljs-i18n': 'file:./projects/imodeljs-i18n.tgz' '@rush-temp/imodeljs-quantity': 'file:./projects/imodeljs-quantity.tgz' '@rush-temp/imodeljs-webserver': 'file:./projects/imodeljs-webserver.tgz' + '@rush-temp/logger-config': 'file:./projects/logger-config.tgz' + '@rush-temp/plugin-markup': 'file:./projects/plugin-markup.tgz' '@rush-temp/presentation-backend': 'file:./projects/presentation-backend.tgz' '@rush-temp/presentation-common': 'file:./projects/presentation-common.tgz' '@rush-temp/presentation-components': 'file:./projects/presentation-components.tgz' @@ -14970,8 +14918,10 @@ specifiers: '@rush-temp/ui-ninezone': 'file:./projects/ui-ninezone.tgz' '@rush-temp/ui-test-app': 'file:./projects/ui-test-app.tgz' '@rush-temp/webpack-tools': 'file:./projects/webpack-tools.tgz' + '@rush-temp/webworker-test-app': 'file:./projects/webworker-test-app.tgz' '@types/body-parser': ^1.17.0 '@types/bunyan': ^1.8.4 + '@types/bunyan-seq': ^0.2.0 '@types/chai': ^4.1.4 '@types/chai-as-promised': ^7 '@types/chai-jest-snapshot': ^1.3.0 @@ -14982,16 +14932,15 @@ specifiers: '@types/deep-assign': ^0.1.0 '@types/deep-equal': ^1 '@types/enzyme': ^3.1.12 - '@types/express': ^4.11.1 + '@types/express': ^4.16.1 '@types/express-session': ^1.15.11 '@types/faker': ^4.1.0 '@types/form-data': ^2.2.1 '@types/fs-extra': ^4.0.7 '@types/glob': ^5.0.35 + '@types/highlight.js': ^9.12.3 '@types/i18next': ^8.4.2 '@types/i18next-browser-languagedetector': ^2.0.1 - '@types/i18next-node-fs-backend': 0.0.30 - '@types/i18next-xhr-backend': ^1.4.1 '@types/js-base64': ^2.3.1 '@types/jsdom': ^12.2.0 '@types/json5': 0.0.30 @@ -15001,14 +14950,14 @@ specifiers: '@types/mocha': ^5.2.5 '@types/multiparty': ^0.0.31 '@types/nock': ^9.1.2 - '@types/node': 10.10.3 + '@types/node': 10.12.18 '@types/passport': ^0.4.6 '@types/qs': ^6.5.0 - '@types/react': ^16.4.14 + '@types/react': 16.7.22 '@types/react-data-grid': ^4.0.0 - '@types/react-dom': 16.0.7 + '@types/react-dom': 16.0.11 '@types/react-highlight-words': ^0.11.1 - '@types/react-redux': ^5.0.15 + '@types/react-redux': ^7.0.1 '@types/react-resize-detector': ^3.1.0 '@types/react-router-dom': ^4.2.7 '@types/react-virtualized': ^9.18.7 @@ -15042,6 +14991,7 @@ specifiers: chalk: ^2.4.1 child_process: ^1.0.2 chokidar: ^2.0.0 + chrome-launcher: ^0.10.5 classnames: ^2.2.5 cli-highlight: ^2.0.0 commander: ^2.14.1 @@ -15075,14 +15025,15 @@ specifiers: fork-ts-checker-webpack-plugin: ^0.4.9 form-data: 2.3.2 fs-extra: ^6.0.1 + fs-write-stream-atomic: ^1.0.10 fuse.js: ^3.3.0 glob: ^7.1.2 + highlight.js: ~9.14.2 html-webpack-plugin: 4.0.0-alpha.2 https-proxy-agent: ^2.2.1 i18next: ^10.2.2 i18next-browser-languagedetector: ^2.1.0 - i18next-node-fs-backend: 2.1.0 - i18next-xhr-backend: ^1.5.1 + i18next-xhr-backend: ^2.0.1 ignore-styles: ^5.0.1 immutable: ^3.8.2 imports-loader: ^0.8.0 @@ -15099,7 +15050,6 @@ specifiers: lodash: ^4.17.10 lolex: ^2.7.1 make-dir: ^1.3.0 - make-dir-cli: ^1.0.0 merge-json: 0.1.0-b.3 mini-css-extract-plugin: ^0.4.0 minimist: ^1.2.0 @@ -15151,6 +15101,7 @@ specifiers: recursive-readdir: ^2.2.2 redux: ^4.0.0 request: ^2.88.0 + request-promise: ^4.2.0 request-promise-native: ^1.0.5 resize-observer-polyfill: ^1.5.0 resolve: ^1.1.7 @@ -15167,6 +15118,7 @@ specifiers: superagent: ^3.7.0 svg-react-loader: ^0.4.5 svg-sprite-loader: ^3.8.0 + svg.js: ~2.7.1 sw-precache-webpack-plugin: ^0.11.5 tooltip.js: ^1.2.0 tree-kill: ^1.2.0 @@ -15180,9 +15132,10 @@ specifiers: tslint-loader: ^3.6.0 tsutils: ^3.6.0 typedoc: ^0.11.1 - typedoc-plugin-external-module-name: ^1.1.1 + typedoc-plugin-external-module-name: 1.1.1 + typedoc-plugin-internal-external: 1.0.10 typemoq: ^2.1.0 - typescript: ~3.1.0 + typescript: ~3.2.2 typescript-json-schema: ^0.28.0 uglifyjs-webpack-plugin: ^1.2.5 url-loader: ^1.0.1 diff --git a/common/config/rush/version-policies.json b/common/config/rush/version-policies.json index 01fe835..37408ce 100644 --- a/common/config/rush/version-policies.json +++ b/common/config/rush/version-policies.json @@ -2,7 +2,7 @@ { "policyName": "prerelease-monorepo-lockStep", "definitionName": "lockStepVersion", - "version": "0.187.0", - "nextBump": "minor" + "version": "0.189.0", + "nextBump": "prerelease" } ] diff --git a/core/backend/.npmignore b/core/backend/.npmignore index 9bf93fb..7a352e6 100644 --- a/core/backend/.npmignore +++ b/core/backend/.npmignore @@ -7,5 +7,4 @@ assets/** test perftest !public/** -!package.json !*.md diff --git a/core/backend/CHANGELOG.json b/core/backend/CHANGELOG.json index a431ef3..54ecbc5 100644 --- a/core/backend/CHANGELOG.json +++ b/core/backend/CHANGELOG.json @@ -1,6 +1,120 @@ { "name": "@bentley/imodeljs-backend", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/imodeljs-backend_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "Changes package.json to include api-extractor and adds api-extractor.json" + }, + { + "comment": "Use new buildIModelJsBuild script" + }, + { + "comment": "AxisAlignedBox and ElementAlignedBox are now typed to Range3d rather than classes" + }, + { + "comment": "Moved AzureFileHandler, IOSAzureFileHandler, UrlFileHandler and the iModelHub tests to the imodeljs-clients-backend package. This removes the dependency of imodeljs-clients on the \"fs\" module, and turns it into a browser only package. " + }, + { + "comment": "clone methods are no longer generic" + }, + { + "comment": "Remove uneeded typedoc plugin depedency" + }, + { + "comment": "Added spatial <-> cartographic methods that check/use the geographic coordinate system before using ecef location." + }, + { + "comment": "Added async method for ECSqlStatement and SqliteStatement for step and stepAndInsert" + }, + { + "comment": "Create iModel from empty template if seed file path not defined." + }, + { + "comment": "Add IModelImporter for importing data between iModels" + }, + { + "comment": "Enable IModelWriteTest create/delete iModels on per user-machine basis" + }, + { + "comment": "Enable IModelWriteTest create/delete iModels on per user-machine basis" + }, + { + "comment": "Validated size of change sets before applying them. " + }, + { + "comment": "codespec lock example" + }, + { + "comment": "Add backend Material API" + }, + { + "comment": "Validated version of Node.js in IModelHost.startup()" + }, + { + "comment": "Save BUILD_SEMVER to globally accessible map" + }, + { + "comment": "Fixed resolution of queryable promises. " + }, + { + "comment": "added queryModelRange " + }, + { + "comment": "IModelConnection.close() always disposes the briefcase held at the backend in the case of ReadWrite connections. " + }, + { + "comment": "Move the IModelUnitTestRpcImpl into the testbed and out of the public API and marked nativeDb as hidden" + }, + { + "comment": "Remove loadNativeAsset and formatElements RPC calls from the IModelReadRpcInterface" + }, + { + "comment": "debugging aid" + }, + { + "comment": "Removed IModelConnection.connectionId, added IModelApp.sessionId" + }, + { + "comment": "Tile requests can optionally specify a retryInterval." + }, + { + "comment": "Improve tile request logging and make timeout configurable." + }, + { + "comment": "Prevent tile generation from interfering with other asynchronous requests." + }, + { + "comment": "Handled error with fetching host information on deployed machines." + }, + { + "comment": "Quick fix to ULAS failures. " + }, + { + "comment": "WIP fixes to Usage Logging. " + }, + { + "comment": "upgrade to TypeScript 3.2.2" + } + ] + } + }, + { + "version": "0.188.0", + "tag": "@bentley/imodeljs-backend_v0.188.0", + "date": "Wed, 16 Jan 2019 16:36:09 GMT", + "comments": { + "none": [ + { + "comment": "Changed Elements Db class for backend processing" + } + ] + } + }, { "version": "0.187.0", "tag": "@bentley/imodeljs-backend_v0.187.0", diff --git a/core/backend/CHANGELOG.md b/core/backend/CHANGELOG.md index 1189af7..1107cce 100644 --- a/core/backend/CHANGELOG.md +++ b/core/backend/CHANGELOG.md @@ -1,6 +1,50 @@ # Change Log - @bentley/imodeljs-backend -This log was last generated on Tue, 15 Jan 2019 15:18:59 GMT and should not be manually modified. +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- Changes package.json to include api-extractor and adds api-extractor.json +- Use new buildIModelJsBuild script +- AxisAlignedBox and ElementAlignedBox are now typed to Range3d rather than classes +- Moved AzureFileHandler, IOSAzureFileHandler, UrlFileHandler and the iModelHub tests to the imodeljs-clients-backend package. This removes the dependency of imodeljs-clients on the "fs" module, and turns it into a browser only package. +- clone methods are no longer generic +- Remove uneeded typedoc plugin depedency +- Added spatial <-> cartographic methods that check/use the geographic coordinate system before using ecef location. +- Added async method for ECSqlStatement and SqliteStatement for step and stepAndInsert +- Create iModel from empty template if seed file path not defined. +- Add IModelImporter for importing data between iModels +- Enable IModelWriteTest create/delete iModels on per user-machine basis +- Enable IModelWriteTest create/delete iModels on per user-machine basis +- Validated size of change sets before applying them. +- codespec lock example +- Add backend Material API +- Validated version of Node.js in IModelHost.startup() +- Save BUILD_SEMVER to globally accessible map +- Fixed resolution of queryable promises. +- added queryModelRange +- IModelConnection.close() always disposes the briefcase held at the backend in the case of ReadWrite connections. +- Move the IModelUnitTestRpcImpl into the testbed and out of the public API and marked nativeDb as hidden +- Remove loadNativeAsset and formatElements RPC calls from the IModelReadRpcInterface +- debugging aid +- Removed IModelConnection.connectionId, added IModelApp.sessionId +- Tile requests can optionally specify a retryInterval. +- Improve tile request logging and make timeout configurable. +- Prevent tile generation from interfering with other asynchronous requests. +- Handled error with fetching host information on deployed machines. +- Quick fix to ULAS failures. +- WIP fixes to Usage Logging. +- upgrade to TypeScript 3.2.2 + +## 0.188.0 +Wed, 16 Jan 2019 16:36:09 GMT + +### Updates + +- Changed Elements Db class for backend processing ## 0.187.0 Tue, 15 Jan 2019 15:18:59 GMT diff --git a/core/backend/package.json b/core/backend/package.json index e52fcfb..3ec61be 100644 --- a/core/backend/package.json +++ b/core/backend/package.json @@ -1,21 +1,22 @@ { "name": "@bentley/imodeljs-backend", - "version": "0.187.0", + "version": "0.189.0", "description": "iModel.js backend components", "main": "lib/imodeljs-backend.js", "typings": "lib/imodeljs-backend", "license": "MIT", "engines": { - "node": ">=10.14.0 <11.0" + "node": ">=10.6.0 <11.0" }, "scripts": { - "build": "tsc 1>&2 && npm run copy:assets && npm run copy:test-assets", + "build": "tsc 1>&2 && npm run copy:assets && npm run copy:test-assets && node ./node_modules/@bentley/build-tools/scripts/ignoreargs.js 1>&2", "clean": "rimraf lib package-deps.json", "docs": "node ./node_modules/@bentley/build-tools/scripts/docs.js --source=./src --includes=../../generated-docs/extract --json=../../generated-docs/core/imodeljs-backend/file.json --tsIndexFile=./imodeljs-backend.ts --onlyJson %TYPEDOC_THEME%", "copy:assets": "cpx \"./src/assets/**/*\" ./lib/assets", "copy:test-assets": "cpx \"./src/test/assets/**/*\" ./lib/test/assets", "cover": "nyc npm test", "cover:integration": "nyc npm run test-integration", + "extract-api": "node ./node_modules/@bentley/build-tools/scripts/extract-api.js --entry=imodeljs-backend", "lint": "tslint --project . 1>&2", "pretest": "cpx \"./src/test/logging.config.json\" ./lib/test", "test": "node ./node_modules/@bentley/build-tools/scripts/test.js --offline=\"mock\" --grep \"#integration|#WebGLPerformance\" --invert", @@ -38,34 +39,35 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/geometry-core": "0.187.0", - "@bentley/imodeljs-clients": "0.187.0", - "@bentley/imodeljs-common": "0.187.0" + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/imodeljs-clients": "0.189.0", + "@bentley/imodeljs-clients-backend": "0.189.0", + "@bentley/imodeljs-common": "0.189.0" }, "//devDependencies": [ "NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install", "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/config-loader": "0.187.0", - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/build-tools": "0.187.0", - "@bentley/geometry-core": "0.187.0", - "@bentley/imodeljs-clients": "0.187.0", - "@bentley/imodeljs-clients-backend": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", + "@bentley/config-loader": "0.189.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/build-tools": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/imodeljs-clients": "0.189.0", + "@bentley/imodeljs-clients-backend": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@types/body-parser": "^1.17.0", "@types/chai": "^4.1.4", - "@types/express": "^4.11.1", + "@types/express": "^4.16.1", "@types/fs-extra": "^4.0.7", "@types/glob": "^5.0.35", "@types/js-base64": "^2.3.1", "@types/mocha": "^5.2.5", - "@types/node": "10.10.3", + "@types/node": "10.12.18", "@types/semver": "^5.5.0", "@types/form-data": "^2.2.1", "@types/multiparty": "^0.0.31", - "@types/body-parser": "^1.17.0", "chai": "^4.1.2", "cpx": "^1.5.0", "mocha": "^5.2.0", @@ -75,15 +77,13 @@ "ts-node": "^7.0.1", "tslint": "^5.11.0", "typedoc": "^0.11.1", - "typedoc-plugin-external-module-name": "^1.1.1", "typemoq": "^2.1.0", - "typescript": "~3.1.0", + "typescript": "~3.2.2", "webpack": "^4.20.2", "asn1": "0.2.3" }, "dependencies": { - "@bentley/imodeljs-native": "~0.86.0", - "@types/body-parser": "^1.17.0", + "@bentley/imodeljs-native": "~0.99.0", "body-parser": "^1.18.2", "fs-extra": "^6.0.1", "glob": "^7.1.2", diff --git a/core/backend/src/BisCore.ts b/core/backend/src/BisCore.ts index a4fb4c8..e707554 100644 --- a/core/backend/src/BisCore.ts +++ b/core/backend/src/BisCore.ts @@ -14,6 +14,7 @@ import * as categoryMod from "./Category"; import * as viewMod from "./ViewDefinition"; import * as linkMod from "./Relationship"; import * as textureMod from "./Texture"; +import * as materialMod from "./Material"; /** * The [BisCore]($docs/bis/intro/schemas-domains.md) schema is the lowest level Schema in an iModel. @@ -46,5 +47,6 @@ export class BisCore extends Schema { ClassRegistry.registerModule(viewMod, this); ClassRegistry.registerModule(linkMod, this); ClassRegistry.registerModule(textureMod, this); + ClassRegistry.registerModule(materialMod, this); } } diff --git a/core/backend/src/BriefcaseManager.ts b/core/backend/src/BriefcaseManager.ts index 8dcb068..0220cdc 100644 --- a/core/backend/src/BriefcaseManager.ts +++ b/core/backend/src/BriefcaseManager.ts @@ -10,9 +10,8 @@ import { BriefcaseQuery, ChangeSetQuery, IModelQuery, ConflictingCodesError, IModelClient, HubIModel, IncludePrefix, } from "@bentley/imodeljs-clients"; import { IModelBankClient } from "@bentley/imodeljs-clients/lib/IModelBank/IModelBankClient"; -import { AzureFileHandler } from "@bentley/imodeljs-clients/lib/imodelhub/AzureFileHandler"; -import { IOSAzureFileHandler } from "@bentley/imodeljs-clients/lib/imodelhub/IOSAzureFileHandler"; -import { ChangeSetApplyOption, BeEvent, DbResult, OpenMode, assert, Logger, LogLevel, ChangeSetStatus, BentleyStatus, IModelHubStatus, PerfLogger, ActivityLoggingContext, GuidString, Id64 } from "@bentley/bentleyjs-core"; +import { AzureFileHandler, IOSAzureFileHandler } from "@bentley/imodeljs-clients-backend"; +import { ChangeSetApplyOption, BeEvent, DbResult, OpenMode, assert, Logger, ChangeSetStatus, BentleyStatus, IModelHubStatus, PerfLogger, ActivityLoggingContext, GuidString, Id64, IModelStatus } from "@bentley/bentleyjs-core"; import { BriefcaseStatus, IModelError, IModelVersion, IModelToken, CreateIModelProps } from "@bentley/imodeljs-common"; import { IModelJsNative } from "./IModelJsNative"; import { IModelDb, OpenParams, SyncMode, AccessMode, ExclusiveAccessOption } from "./IModelDb"; @@ -45,7 +44,7 @@ export const enum KeepBriefcase { /** A token that represents a ChangeSet */ export class ChangeSetToken { - constructor(public id: string, public parentId: string, public index: number, public pathname: string, public containsSchemaChanges: boolean) { } + constructor(public id: string, public parentId: string, public index: number, public pathname: string, public containsSchemaChanges: boolean, public pushDate?: string) { } } /** Entry in the briefcase cache */ @@ -192,10 +191,8 @@ class BriefcaseCache { public addBriefcase(briefcase: BriefcaseEntry) { const key = briefcase.getKey(); - if (this._briefcases.get(key)) { - Logger.logError(loggingCategory, "Briefcase already exists in the cache.", () => briefcase.getDebugInfo()); - throw new IModelError(DbResult.BE_SQLITE_ERROR, `Briefcase ${key} already exists in the cache.`); - } + if (this._briefcases.get(key)) + throw new IModelError(DbResult.BE_SQLITE_ERROR, `Briefcase ${key} already exists in the cache.`, Logger.logError, loggingCategory, () => briefcase.getDebugInfo()); Logger.logTrace(loggingCategory, "Added briefcase to the cache", () => briefcase.getDebugInfo()); this._briefcases.set(key, briefcase); @@ -207,11 +204,8 @@ class BriefcaseCache { /** Delete a briefcase from the cache by key */ public deleteBriefcaseByKey(key: string) { const briefcase = this._briefcases.get(key); - if (!briefcase) { - const msg = `Briefcase ${key} not found in cache`; - Logger.logError(loggingCategory, msg); - throw new IModelError(DbResult.BE_SQLITE_ERROR, msg); - } + if (!briefcase) + throw new IModelError(DbResult.BE_SQLITE_ERROR, "Briefcase not found in cache", Logger.logError, loggingCategory, () => ({ key })); this._briefcases.delete(key); Logger.logTrace(loggingCategory, "Deleted briefcase from the cache", () => briefcase.getDebugInfo()); @@ -275,6 +269,7 @@ export class BriefcaseManager { public static getChangeSetsPath(iModelId: GuidString): string { return path.join(BriefcaseManager.getIModelPath(iModelId), "csets"); } public static getChangeCachePathName(iModelId: GuidString): string { return path.join(BriefcaseManager.getIModelPath(iModelId), iModelId.concat(".bim.ecchanges")); } + public static getChangedElementsPathName(iModelId: GuidString): string { return path.join(BriefcaseManager.getIModelPath(iModelId), iModelId.concat(".bim.elems")); } private static getBriefcasesPath(iModelId: GuidString) { return path.join(BriefcaseManager.getIModelPath(iModelId), "bc"); @@ -300,8 +295,6 @@ export class BriefcaseManager { return path.join(pathBaseName, briefcaseId.toString(), iModelName.concat(".bim")); } - private static buildScratchPath(): string { return path.join(BriefcaseManager.cacheDir, "scratch"); } - /** Clear the briefcase manager cache */ private static clearCache() { BriefcaseManager._cache.clear(); @@ -339,7 +332,7 @@ export class BriefcaseManager { actx.enter(); const fileNames = IModelJsFs.readdirSync(briefcaseDir); if (fileNames.length !== 1) - throw new IModelError(DbResult.BE_SQLITE_ERROR, `Briefcase directory ${briefcaseDir} must contain exactly one briefcase`); + throw new IModelError(DbResult.BE_SQLITE_ERROR, `Briefcase directory ${briefcaseDir} must contain exactly one briefcase`, Logger.logError, loggingCategory); const pathname = path.join(briefcaseDir, fileNames[0]); // Open the briefcase to populate the briefcase entry with briefcaseid changesetid and reversedchangesetid @@ -351,7 +344,7 @@ export class BriefcaseManager { const nativeDb = new IModelHost.platform.DgnDb(); const res: DbResult = nativeDb.openIModelFile(briefcase.pathname, OpenMode.Readonly); if (DbResult.BE_SQLITE_OK !== res) - throw new IModelError(DbResult.BE_SQLITE_ERROR, `Unable to open briefcase at ${briefcase.pathname}`); + throw new IModelError(res, "Unable to open briefcase", Logger.logError, loggingCategory, () => ({ iModelId: briefcase.iModelId, pathname: briefcase.pathname, result: res })); briefcase.briefcaseId = nativeDb.getBriefcaseId(); briefcase.changeSetId = nativeDb.getParentChangeSetId(); @@ -368,7 +361,7 @@ export class BriefcaseManager { if (briefcase.briefcaseId !== BriefcaseId.Standalone) { const hubBriefcases: HubBriefcase[] = await BriefcaseManager.imodelClient.briefcases.get(actx, accessToken, iModelId, new BriefcaseQuery().byId(briefcase.briefcaseId)); if (hubBriefcases.length === 0) - throw new IModelError(DbResult.BE_SQLITE_ERROR, `Unable to find briefcase ${briefcase.briefcaseId}:${briefcase.pathname} on the Hub (for the current user)`); + throw new IModelError(DbResult.BE_SQLITE_ERROR, `Unable to find briefcase ${briefcase.briefcaseId}:${briefcase.pathname} on the Hub (for the current user)`, Logger.logError, loggingCategory, () => briefcase.getDebugInfo()); briefcase.userId = hubBriefcases[0].userId; briefcase.fileId = hubBriefcases[0].fileId!.toString(); } @@ -464,7 +457,7 @@ export class BriefcaseManager { return; if (!IModelHost.configuration) - throw new IModelError(DbResult.BE_SQLITE_ERROR, "IModelHost.startup() should be called before any backend operations"); + throw new IModelError(DbResult.BE_SQLITE_ERROR, "IModelHost.startup() should be called before any backend operations", Logger.logError, loggingCategory); Logger.logTrace(loggingCategory, "Initializing briefcase cache"); IModelHost.onBeforeShutdown.addListener(BriefcaseManager.onIModelHostShutdown); @@ -502,8 +495,7 @@ export class BriefcaseManager { const changeSet: ChangeSet = (await BriefcaseManager.imodelClient.changeSets.get(actx, accessToken, iModelId, new ChangeSetQuery().byId(changeSetId)))[0]; return +changeSet.index!; } catch (err) { - assert(false, "Could not determine index of change set"); - return -1; + throw new IModelError(ChangeSetStatus.InvalidId, "Could not determine index of change set", Logger.logError, loggingCategory, () => ({ iModelId, changeSetId })); } } @@ -553,10 +545,10 @@ export class BriefcaseManager { assert(!!briefcase); if (changeSetIndex < briefcase.changeSetIndex! && openParams.syncMode === SyncMode.PullAndPush) { - Logger.logError(loggingCategory, "No support to open an older version when opening an IModel to push changes (SyncMode.PullAndPush). Cannot open briefcase", () => briefcase!.getDebugInfo()); + Logger.logError(loggingCategory, "Cannot open an older version of an IModel to push changes (SyncMode.PullAndPush)", () => briefcase!.getDebugInfo()); await BriefcaseManager.deleteBriefcase(actx, accessToken, briefcase); actx.enter(); - throw new IModelError(BriefcaseStatus.CannotApplyChanges, "Cannot merge when there are reversed changes"); + throw new IModelError(BriefcaseStatus.CannotApplyChanges, "Cannot open an older version of an IModel to push changes (SyncMode.PullAndPush)"); } // Apply the required change sets @@ -564,8 +556,14 @@ export class BriefcaseManager { await BriefcaseManager.processChangeSets(actx, accessToken, briefcase, version); } catch (error) { actx.enter(); - Logger.logWarning(loggingCategory, "Error applying changes to briefcase. Closing and then deleting it so that it can be re-fetched again.", () => briefcase!.getDebugInfo()); + // Clean up the cache for a retry + Logger.logError(loggingCategory, "Error applying changes to briefcase. Deleting the briefcase from cache to enable retries", () => briefcase!.getDebugInfo()); await BriefcaseManager.deleteBriefcase(actx, accessToken, briefcase); + if (error.errorNumber === ChangeSetStatus.CorruptedChangeStream || error.errorNumber === ChangeSetStatus.InvalidId || error.errorNumber === ChangeSetStatus.InvalidVersion) { + Logger.logError(loggingCategory, "Detected potential corruption of change sets. Deleting them from cache to enable retries", () => ({ iModelId })); + BriefcaseManager.deleteChangeSetsFromLocalDisk(iModelId); + } + actx.enter(); throw error; } @@ -577,7 +575,7 @@ export class BriefcaseManager { briefcase.nativeDb.closeIModel(); const res: DbResult = briefcase.nativeDb.openIModelFile(briefcase.pathname, openParams.openMode); if (DbResult.BE_SQLITE_OK !== res) - throw new IModelError(res, briefcase.pathname); + throw new IModelError(res, `Unable to reopen briefcase at ${briefcase.pathname}`, Logger.logError, loggingCategory, () => briefcase!.getDebugInfo()); briefcase.openParams = openParams; } @@ -720,20 +718,20 @@ export class BriefcaseManager { const nativeDb: IModelJsNative.DgnDb = new IModelHost.platform.DgnDb(); let res: DbResult = BriefcaseManager.openDb(nativeDb, actx, accessToken, contextId, briefcase.pathname, openParams.openMode); if (DbResult.BE_SQLITE_OK !== res) { - Logger.logError(loggingCategory, `Unable to open briefcase at ${briefcase.pathname}`); - Logger.logWarning(loggingCategory, `Unable to create briefcase ${briefcase.pathname}. Deleting any remnants of it`); + const msg = `Unable to open Db at ${briefcase.pathname} when creating a briefcase`; + Logger.logError(loggingCategory, msg, () => ({ ...briefcase.getDebugInfo(), result: res })); await BriefcaseManager.deleteBriefcase(actx, accessToken, briefcase); actx.enter(); - throw new IModelError(res, briefcase.pathname); + throw new IModelError(res, msg); } res = nativeDb.setBriefcaseId(briefcase.briefcaseId); if (DbResult.BE_SQLITE_OK !== res) { - Logger.logError(loggingCategory, `Unable to setup briefcase id for ${briefcase.pathname}`); - Logger.logWarning(loggingCategory, `Unable to create briefcase ${briefcase.pathname}. Deleting any remnants of it`); + const msg = `Unable to setup briefcase id for Db at ${briefcase.pathname} when creating a briefcase`; + Logger.logError(loggingCategory, msg, () => ({ ...briefcase.getDebugInfo(), result: res })); await BriefcaseManager.deleteBriefcase(actx, accessToken, briefcase); actx.enter(); - throw new IModelError(res, briefcase.pathname); + throw new IModelError(res, msg); } assert(nativeDb.getParentChangeSetId() === briefcase.changeSetId); @@ -802,10 +800,15 @@ export class BriefcaseManager { private static deleteBriefcaseFromLocalDisk(briefcase: BriefcaseEntry) { assert(!briefcase.isOpen); const dirName = path.dirname(briefcase.pathname); - BriefcaseManager.deleteFolderRecursive(dirName); - if (Logger.isEnabled(loggingCategory, LogLevel.Trace) && !IModelJsFs.existsSync(briefcase.pathname)) { + if (BriefcaseManager.deleteFolderRecursive(dirName)) Logger.logTrace(loggingCategory, "Deleted briefcase from local disk", () => briefcase.getDebugInfo()); - } + } + + /** Deletes change sets of an iModel from local disk */ + private static deleteChangeSetsFromLocalDisk(iModelId: string) { + const changeSetsPath: string = BriefcaseManager.getChangeSetsPath(iModelId); + if (BriefcaseManager.deleteFolderRecursive(changeSetsPath)) + Logger.logTrace(loggingCategory, "Deleted change sets from local disk", () => ({ iModelId, changeSetsPath })); } /** Deletes a briefcase from the IModelServer (if it exists) */ @@ -844,12 +847,15 @@ export class BriefcaseManager { /** Deletes a briefcase, and releases its references in iModelHub if necessary */ private static async deleteBriefcase(actx: ActivityLoggingContext, accessToken: AccessToken, briefcase: BriefcaseEntry): Promise { actx.enter(); - Logger.logTrace(loggingCategory, "Attempting to delete briefcase", () => briefcase.getDebugInfo()); - assert(!briefcase.isOpen); + Logger.logTrace(loggingCategory, "Attempting to delete briefcase", () => briefcase.getDebugInfo()); + if (briefcase.isOpen) + BriefcaseManager.closeBriefcase(briefcase, false); BriefcaseManager.deleteBriefcaseFromCache(briefcase); + await BriefcaseManager.deleteBriefcaseFromServer(actx, accessToken, briefcase); actx.enter(); + BriefcaseManager.deleteBriefcaseFromLocalDisk(briefcase); } @@ -880,14 +886,35 @@ export class BriefcaseManager { return Promise.reject(new IModelError(BriefcaseStatus.VersionNotFound, "Version not found", Logger.logWarning, loggingCategory)); } + private static wasChangeSetDownloaded(changeSet: ChangeSet, changeSetsPath: string): boolean { + const pathname = path.join(changeSetsPath, changeSet.fileName!); + + // Was the file downloaded? + if (!IModelJsFs.existsSync(pathname)) + return false; + + // Was the download complete? + const actualFileSize: number = IModelJsFs.lstatSync(pathname)!.size; + const expectedFileSize: number = +changeSet.fileSize!; + if (actualFileSize === expectedFileSize) + return true; + + Logger.logError(loggingCategory, `ChangeSet size ${actualFileSize} does not match the expected size ${expectedFileSize}. Deleting it so that it can be refetched`, () => (changeSet)); + try { + IModelJsFs.unlinkSync(pathname); + } catch (error) { + Logger.logError(loggingCategory, `Cannot delete ChangeSet file at ${pathname}`); + } + return false; + } + private static async downloadChangeSetsInternal(actx: ActivityLoggingContext, iModelId: GuidString, changeSets: ChangeSet[]) { actx.enter(); const changeSetsPath: string = BriefcaseManager.getChangeSetsPath(iModelId); const changeSetsToDownload = new Array(); for (const changeSet of changeSets) { - const changeSetPathname = path.join(changeSetsPath, changeSet.fileName!); - if (!IModelJsFs.existsSync(changeSetPathname)) + if (!BriefcaseManager.wasChangeSetDownloaded(changeSet, changeSetsPath)) changeSetsToDownload.push(changeSet); } @@ -920,13 +947,13 @@ export class BriefcaseManager { /** Open a standalone iModel from the local disk */ public static openStandalone(pathname: string, openMode: OpenMode, enableTransactions: boolean): BriefcaseEntry { if (BriefcaseManager._cache.findBriefcaseByToken(new IModelToken(pathname, undefined, undefined, undefined, openMode))) - throw new IModelError(DbResult.BE_SQLITE_CANTOPEN, `Cannot open ${pathname} again - it has already been opened once`); + throw new IModelError(DbResult.BE_SQLITE_CANTOPEN, `Cannot open standalone iModel at ${pathname} again - it has already been opened once`, Logger.logError, loggingCategory); const nativeDb = new IModelHost.platform.DgnDb(); const res = nativeDb.openIModelFile(pathname, openMode); if (DbResult.BE_SQLITE_OK !== res) - throw new IModelError(res, pathname); + throw new IModelError(res, `Cannot open standalone iModel at ${pathname}`, Logger.logError, loggingCategory); let briefcaseId: number = nativeDb.getBriefcaseId(); if (enableTransactions) { @@ -960,7 +987,7 @@ export class BriefcaseManager { const res: DbResult = nativeDb.createStandaloneIModel(fileName, JSON.stringify(args)); if (DbResult.BE_SQLITE_OK !== res) - throw new IModelError(res, fileName); + throw new IModelError(res, `Cannot open standalone iModel at ${fileName}`, Logger.logError, loggingCategory); nativeDb.setBriefcaseId(BriefcaseId.Standalone); @@ -996,9 +1023,12 @@ export class BriefcaseManager { } } - private static deleteFolderRecursive(folderPath: string) { + /** Deletes a folder on disk. Does not throw any errors, but returns true + * if the delete was successful. + */ + private static deleteFolderRecursive(folderPath: string): boolean { if (!IModelJsFs.existsSync(folderPath)) - return; + return true; try { const files = IModelJsFs.readdirSync(folderPath); @@ -1023,6 +1053,8 @@ export class BriefcaseManager { } } catch (error) { } + + return !IModelJsFs.existsSync(folderPath); } /** Purges the cache of briefcases - closes any open briefcases, @@ -1064,13 +1096,13 @@ export class BriefcaseManager { private static openBriefcase(actx: ActivityLoggingContext, accessToken: AccessToken, contextId: string, briefcase: BriefcaseEntry, openParams: OpenParams): void { if (briefcase.isOpen) - throw new Error(`Briefcase ${briefcase.pathname} is already open.`); + throw new IModelError(IModelStatus.AlreadyOpen, `Briefcase ${briefcase.pathname} is already open.`, Logger.logError, loggingCategory, () => briefcase.getDebugInfo()); briefcase.nativeDb = briefcase.nativeDb || new IModelHost.platform.DgnDb(); const res: DbResult = BriefcaseManager.openDb(briefcase.nativeDb!, actx, accessToken, contextId, briefcase.pathname, openParams.openMode); if (DbResult.BE_SQLITE_OK !== res) - throw new IModelError(res, briefcase.pathname); + throw new IModelError(res, `Cannot open briefcase at ${briefcase.pathname}`, Logger.logError, loggingCategory, () => briefcase.getDebugInfo()); briefcase.openParams = openParams; briefcase.userId = accessToken.getUserInfo()!.id; @@ -1082,18 +1114,18 @@ export class BriefcaseManager { const perfLogger = new PerfLogger("BriefcaseManager.processChangeSets"); if (!briefcase.nativeDb || !briefcase.isOpen) - return Promise.reject(new IModelError(ChangeSetStatus.ApplyError, "Briefcase must be open to process change sets")); + return Promise.reject(new IModelError(ChangeSetStatus.ApplyError, "Briefcase must be open to process change sets", Logger.logError, loggingCategory, () => briefcase.getDebugInfo())); if (briefcase.openParams!.openMode !== OpenMode.ReadWrite) - return Promise.reject(new IModelError(ChangeSetStatus.ApplyError, "Briefcase must be open ReadWrite to process change sets")); + return Promise.reject(new IModelError(ChangeSetStatus.ApplyError, "Briefcase must be open ReadWrite to process change sets", Logger.logError, loggingCategory, () => briefcase.getDebugInfo())); assert(briefcase.nativeDb.getParentChangeSetId() === briefcase.changeSetId, "Mismatch between briefcase and the native Db"); if (briefcase.changeSetIndex === undefined) - return Promise.reject(new IModelError(ChangeSetStatus.ApplyError, "Cannot apply changes to a standalone file")); + return Promise.reject(new IModelError(ChangeSetStatus.ApplyError, "Cannot apply changes to a standalone file", Logger.logError, loggingCategory, () => briefcase.getDebugInfo())); const targetChangeSetId: string = await targetVersion.evaluateChangeSet(actx, accessToken, briefcase.iModelId, BriefcaseManager.imodelClient); const targetChangeSetIndex: number = await BriefcaseManager.getChangeSetIndexFromId(actx, accessToken, briefcase.iModelId, targetChangeSetId); actx.enter(); if (typeof targetChangeSetIndex === "undefined") - return Promise.reject(new IModelError(ChangeSetStatus.ApplyError, "Could not determine change set information from the Hub")); + return Promise.reject(new IModelError(ChangeSetStatus.ApplyError, "Could not determine change set information from the Hub", Logger.logError, loggingCategory, () => briefcase.getDebugInfo())); // Error check the requested change set option if (requestedChangeSetOption) { @@ -1101,19 +1133,19 @@ export class BriefcaseManager { switch (requestedChangeSetOption) { case ChangeSetApplyOption.Merge: if (targetChangeSetIndex < currentChangeSetIndex) - return Promise.reject(new IModelError(ChangeSetStatus.NothingToMerge, "Nothing to merge")); + return Promise.reject(new IModelError(ChangeSetStatus.NothingToMerge, "Nothing to merge", Logger.logError, loggingCategory, () => ({ ...briefcase.getDebugInfo(), targetChangeSetId, targetChangeSetIndex }))); break; case ChangeSetApplyOption.Reinstate: if (targetChangeSetIndex < currentChangeSetIndex) - return Promise.reject(new IModelError(ChangeSetStatus.ApplyError, "Cannot reinstate to an earlier version")); + return Promise.reject(new IModelError(ChangeSetStatus.ApplyError, "Cannot reinstate to an earlier version", Logger.logError, loggingCategory, () => ({ ...briefcase.getDebugInfo(), targetChangeSetId, targetChangeSetIndex }))); break; case ChangeSetApplyOption.Reverse: if (targetChangeSetIndex >= currentChangeSetIndex) - return Promise.reject(new IModelError(ChangeSetStatus.ApplyError, "Cannot reverse to a later version")); + return Promise.reject(new IModelError(ChangeSetStatus.ApplyError, "Cannot reverse to a later version", Logger.logError, loggingCategory, () => ({ ...briefcase.getDebugInfo(), targetChangeSetId, targetChangeSetIndex }))); break; default: assert(false, "Unknown change set process option"); - return Promise.reject(new IModelError(ChangeSetStatus.ApplyError, "Unknown ChangeSet process option")); + return Promise.reject(new IModelError(ChangeSetStatus.ApplyError, "Unknown ChangeSet process option", Logger.logError, loggingCategory, () => ({ ...briefcase.getDebugInfo(), targetChangeSetId, targetChangeSetIndex }))); } } @@ -1192,7 +1224,7 @@ export class BriefcaseManager { // Apply the changes const status: ChangeSetStatus = briefcase.nativeDb!.applyChangeSets(JSON.stringify(changeSetTokens), processOption); if (ChangeSetStatus.Success !== status) - return Promise.reject(new IModelError(status, "Error applying changesets", Logger.logError, loggingCategory)); + return Promise.reject(new IModelError(status, "Error applying changesets", Logger.logError, loggingCategory, () => ({ ...briefcase.getDebugInfo(), targetChangeSetId, targetChangeSetIndex }))); // Mark Db as reopened after merge (if there are schema changes) if (containsSchemaChanges) @@ -1222,7 +1254,7 @@ export class BriefcaseManager { break; default: assert(false, "Unknown change set process option"); - return Promise.reject(new IModelError(BriefcaseStatus.CannotApplyChanges, "Unknown ChangeSet process option")); + return Promise.reject(new IModelError(BriefcaseStatus.CannotApplyChanges, "Unknown ChangeSet process option", Logger.logError, loggingCategory, () => ({ ...briefcase.getDebugInfo(), targetChangeSetId, targetChangeSetIndex }))); } // Update cache if necessary @@ -1236,13 +1268,13 @@ export class BriefcaseManager { public static async reverseChanges(actx: ActivityLoggingContext, accessToken: AccessToken, briefcase: BriefcaseEntry, reverseToVersion: IModelVersion): Promise { if (briefcase.openParams!.accessMode === AccessMode.Shared) - return Promise.reject(new IModelError(ChangeSetStatus.ApplyError, "Cannot reverse changes when the Db allows shared access - open with AccessMode.Exclusive")); + return Promise.reject(new IModelError(ChangeSetStatus.ApplyError, "Cannot reverse changes when the Db allows shared access - open with AccessMode.Exclusive", Logger.logError, loggingCategory, () => briefcase.getDebugInfo())); return BriefcaseManager.processChangeSets(actx, accessToken, briefcase, reverseToVersion); } public static async reinstateChanges(actx: ActivityLoggingContext, accessToken: AccessToken, briefcase: BriefcaseEntry, reinstateToVersion?: IModelVersion): Promise { if (briefcase.openParams!.accessMode === AccessMode.Shared) - return Promise.reject(new IModelError(ChangeSetStatus.ApplyError, "Cannot reinstate (or reverse) changes when the Db allows shared access - open with AccessMode.Exclusive")); + return Promise.reject(new IModelError(ChangeSetStatus.ApplyError, "Cannot reinstate (or reverse) changes when the Db allows shared access - open with AccessMode.Exclusive", Logger.logError, loggingCategory, () => briefcase.getDebugInfo())); const targetVersion: IModelVersion = reinstateToVersion || IModelVersion.asOfChangeSet(briefcase.changeSetId); return BriefcaseManager.processChangeSets(actx, accessToken, briefcase, targetVersion); } @@ -1262,14 +1294,14 @@ export class BriefcaseManager { private static startCreateChangeSet(briefcase: BriefcaseEntry): ChangeSetToken { const res: IModelJsNative.ErrorStatusOrResult = briefcase.nativeDb!.startCreateChangeSet(); if (res.error) - throw new IModelError(res.error.status, "Error in startCreateChangeSet", Logger.logError, loggingCategory); + throw new IModelError(res.error.status, "Error in startCreateChangeSet", Logger.logError, loggingCategory, () => briefcase.getDebugInfo()); return JSON.parse(res.result!); } private static finishCreateChangeSet(briefcase: BriefcaseEntry) { const status = briefcase.nativeDb!.finishCreateChangeSet(); if (ChangeSetStatus.Success !== status) - throw new IModelError(status, "Error in finishCreateChangeSet", Logger.logError, loggingCategory); + throw new IModelError(status, "Error in finishCreateChangeSet", Logger.logError, loggingCategory, () => briefcase.getDebugInfo()); } private static abandonCreateChangeSet(briefcase: BriefcaseEntry) { @@ -1280,7 +1312,7 @@ export class BriefcaseManager { private static getPendingChangeSets(briefcase: BriefcaseEntry): string[] { const res: IModelJsNative.ErrorStatusOrResult = briefcase.nativeDb!.getPendingChangeSets(); if (res.error) - throw new IModelError(res.error.status, "Error in getPendingChangeSets", Logger.logWarning, loggingCategory); + throw new IModelError(res.error.status, "Error in getPendingChangeSets", Logger.logWarning, loggingCategory, () => briefcase.getDebugInfo()); return JSON.parse(res.result!) as string[]; } @@ -1288,14 +1320,14 @@ export class BriefcaseManager { private static addPendingChangeSet(briefcase: BriefcaseEntry, changeSetId: string): void { const result = briefcase.nativeDb!.addPendingChangeSet(changeSetId); if (DbResult.BE_SQLITE_OK !== result) - throw new IModelError(result, "Error in addPendingChangeSet", Logger.logError, loggingCategory); + throw new IModelError(result, "Error in addPendingChangeSet", Logger.logError, loggingCategory, () => briefcase.getDebugInfo()); } /** Remove a pending ChangeSet after its codes have been updated */ private static removePendingChangeSet(briefcase: BriefcaseEntry, changeSetId: string): void { const result = briefcase.nativeDb!.removePendingChangeSet(changeSetId); if (DbResult.BE_SQLITE_OK !== result) - throw new IModelError(result, "Error in removePendingChangeSet", Logger.logError, loggingCategory); + throw new IModelError(result, "Error in removePendingChangeSet", Logger.logError, loggingCategory, () => briefcase.getDebugInfo()); } /** Update codes for all pending ChangeSets */ @@ -1353,7 +1385,7 @@ export class BriefcaseManager { private static extractCodes(briefcase: BriefcaseEntry): HubCode[] { const res: IModelJsNative.ErrorStatusOrResult = briefcase.nativeDb!.extractCodes(); if (res.error) - throw new IModelError(res.error.status, "Error in extractCodes", Logger.logError, loggingCategory); + throw new IModelError(res.error.status, "Error in extractCodes", Logger.logError, loggingCategory, () => briefcase.getDebugInfo()); return BriefcaseManager.parseCodesFromJson(briefcase, res.result!); } @@ -1361,7 +1393,7 @@ export class BriefcaseManager { private static extractCodesFromFile(briefcase: BriefcaseEntry, changeSetTokens: ChangeSetToken[]): HubCode[] { const res: IModelJsNative.ErrorStatusOrResult = briefcase.nativeDb!.extractCodesFromFile(JSON.stringify(changeSetTokens)); if (res.error) - throw new IModelError(res.error.status, "Error in extractCodesFromFile", Logger.logError, loggingCategory); + throw new IModelError(res.error.status, "Error in extractCodesFromFile", Logger.logError, loggingCategory, () => briefcase.getDebugInfo()); return BriefcaseManager.parseCodesFromJson(briefcase, res.result!); } @@ -1394,7 +1426,7 @@ export class BriefcaseManager { } } catch (error) { actx.enter(); - Logger.logError(loggingCategory, "`Relinquishing codes or locks has failed with: ${error}`", () => briefcase.getDebugInfo()); + Logger.logError(loggingCategory, "`Relinquishing codes or locks has failed with: ${ error } `", () => briefcase.getDebugInfo()); } // Remove ChangeSet id if it succeeded or failed with conflicts @@ -1408,7 +1440,7 @@ export class BriefcaseManager { */ public static createStandaloneChangeSet(briefcase: BriefcaseEntry): ChangeSetToken { if (!briefcase.isStandalone) - throw new IModelError(BentleyStatus.ERROR, "Error in createStandaloneChangeSet", Logger.logError, loggingCategory); + throw new IModelError(BentleyStatus.ERROR, "Cannot call createStandaloneChangeSet() when the briefcase is not standalone", Logger.logError, loggingCategory, () => briefcase.getDebugInfo()); const changeSetToken: ChangeSetToken = BriefcaseManager.startCreateChangeSet(briefcase); BriefcaseManager.finishCreateChangeSet(briefcase); @@ -1419,7 +1451,7 @@ export class BriefcaseManager { /** Applies a change set to a standalone iModel */ public static applyStandaloneChangeSets(briefcase: BriefcaseEntry, changeSetTokens: ChangeSetToken[], processOption: ChangeSetApplyOption): ChangeSetStatus { if (!briefcase.isStandalone) - throw new IModelError(BentleyStatus.ERROR, "Error in applyStandaloneChangeSet", Logger.logError, loggingCategory); + throw new IModelError(BentleyStatus.ERROR, "Cannot call applyStandaloneChangeSets() when the briefcase is not standalone", Logger.logError, loggingCategory, () => briefcase.getDebugInfo()); return briefcase.nativeDb!.applyChangeSets(JSON.stringify(changeSetTokens), processOption); } @@ -1433,7 +1465,7 @@ export class BriefcaseManager { private static async pushChangeSet(actx: ActivityLoggingContext, accessToken: AccessToken, briefcase: BriefcaseEntry, description: string, relinquishCodesLocks: boolean): Promise { actx.enter(); if (briefcase.openParams!.syncMode !== SyncMode.PullAndPush) { - throw new IModelError(BriefcaseStatus.CannotUpload, "Cannot push from an IModelDb that's opened PullOnly"); + throw new IModelError(BriefcaseStatus.CannotUpload, "Cannot push from an IModelDb that's opened PullOnly", Logger.logError, loggingCategory, () => briefcase.getDebugInfo()); } const changeSetToken: ChangeSetToken = BriefcaseManager.startCreateChangeSet(briefcase); @@ -1530,55 +1562,20 @@ export class BriefcaseManager { return (this._imodelClient === undefined) || (this._imodelClient instanceof IModelBankClient); } - /** Create an iModel on iModelHub */ - public static async create(actx: ActivityLoggingContext, accessToken: AccessToken, projectId: string, hubName: string, args: CreateIModelProps): Promise { - assert(!this.isUsingIModelBankClient(), "This is a Hub-only operation"); - - await BriefcaseManager.memoizedInitCache(actx, accessToken); - assert(!!BriefcaseManager.imodelClient); - + /** + * Create an iModel on iModelHub + * @hidden + */ + public static async create(actx: ActivityLoggingContext, accessToken: AccessToken, contextId: string, iModelName: string, args: CreateIModelProps): Promise { actx.enter(); - - const nativeDb = new IModelHost.platform.DgnDb(); - - const scratchDir = BriefcaseManager.buildScratchPath(); - if (!IModelJsFs.existsSync(scratchDir)) - IModelJsFs.mkdirSync(scratchDir); - - const fileName = path.join(scratchDir, hubName + ".bim"); - if (IModelJsFs.existsSync(fileName)) - IModelJsFs.unlinkSync(fileName); // Note: Cannot create two files with the same name at the same time with multiple async calls. - - let res: DbResult = nativeDb.createIModel(accessToken.toTokenString(), IModelHost.backendVersion, projectId, fileName, JSON.stringify(args)); - if (res !== DbResult.BE_SQLITE_OK) { - if (res === -100) { - // The addon returns -100 if usage tracking failed. For now we don't yet fail, as - // apps need to switch to OIDC authentication first. - Logger.logWarning(loggingCategory, "Usage tracking failed.", () => ({ userId: !accessToken.getUserInfo() ? undefined : accessToken.getUserInfo()!.id, projectId })); - } else - throw new IModelError(res, fileName); + if (this.isUsingIModelBankClient()) { + throw new IModelError(IModelStatus.BadRequest, "Cannot create an iModel in iModelBank. This is a iModelHub only operation", Logger.logError, loggingCategory, () => ({ contextId, iModelName })); } - res = nativeDb.saveChanges(); - if (DbResult.BE_SQLITE_OK !== res) - throw new IModelError(res, "Error saving changes", Logger.logError, loggingCategory); - - nativeDb.closeIModel(); - - const iModelId: GuidString = await BriefcaseManager.upload(actx, accessToken, projectId, fileName, hubName, args.rootSubject.description); - return iModelId; - } - - /** Pushes a new iModel to the Hub */ - private static async upload(actx: ActivityLoggingContext, accessToken: AccessToken, projectId: string, pathname: string, hubName?: string, hubDescription?: string, timeOutInMilliseconds: number = 2 * 60 * 1000): Promise { - assert(!this.isUsingIModelBankClient(), "This is a Hub-only operation"); - - hubName = hubName || path.basename(pathname, ".bim"); - + const hubIModel: HubIModel = await BriefcaseManager.imodelClient.iModels.create(actx, accessToken, contextId, iModelName, undefined, args.rootSubject.description, undefined, 2 * 60 * 1000); actx.enter(); - const iModel: HubIModel = await BriefcaseManager.imodelClient.iModels.create(actx, accessToken, projectId, hubName, pathname, hubDescription, undefined, timeOutInMilliseconds); - return iModel.wsgId; + return hubIModel.wsgId; } /** @hidden */ diff --git a/core/backend/src/ChangeSummaryManager.ts b/core/backend/src/ChangeSummaryManager.ts index 70b4b4d..83063a1 100644 --- a/core/backend/src/ChangeSummaryManager.ts +++ b/core/backend/src/ChangeSummaryManager.ts @@ -57,7 +57,7 @@ export interface ChangeSummaryExtractOptions { currentVersionOnly?: boolean; } -class ChangeSummaryExtractContext { +export class ChangeSummaryExtractContext { public constructor(public readonly accessToken: AccessToken, public readonly iModel: IModelDb) { } public get iModelId(): GuidString { assert(!!this.iModel.briefcase); return this.iModel.briefcase!.iModelId; } @@ -239,7 +239,7 @@ export class ChangeSummaryManager { } } - private static async downloadChangeSets(actx: ActivityLoggingContext, ctx: ChangeSummaryExtractContext, startChangeSetId: GuidString, endChangeSetId: GuidString): Promise { + public static async downloadChangeSets(actx: ActivityLoggingContext, ctx: ChangeSummaryExtractContext, startChangeSetId: GuidString, endChangeSetId: GuidString): Promise { actx.enter(); // Get the change set before the startChangeSet so that startChangeSet is included in the download and processing let beforeStartChangeSetId: GuidString; diff --git a/core/backend/src/ChangedElementsDb.ts b/core/backend/src/ChangedElementsDb.ts new file mode 100644 index 0000000..95523f3 --- /dev/null +++ b/core/backend/src/ChangedElementsDb.ts @@ -0,0 +1,136 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module ChangedElementsDb */ + +import { IModelError, IModelStatus, ChangedElements } from "@bentley/imodeljs-common"; +import { DbResult, OpenMode, IDisposable, ActivityLoggingContext } from "@bentley/bentleyjs-core"; +import { IModelJsNative } from "./IModelJsNative"; +import { IModelDb, ChangeSetToken, ECDbOpenMode, BriefcaseManager, ChangeSummaryManager, ChangeSummaryExtractContext } from "./imodeljs-backend"; +import { ChangeSet, ChangesType, AccessToken } from "@bentley/imodeljs-clients"; +import * as path from "path"; +import { IModelHost } from "./IModelHost"; + +/** An ChangedElementsDb file */ +export class ChangedElementsDb implements IDisposable { + private _nativeDb: IModelJsNative.ChangedElementsECDb | undefined; + + constructor() { + this._nativeDb = new IModelHost.platform.ChangedElementsECDb(); + } + + public dispose(): void { + if (!this._nativeDb) + return; + + this.closeDb(); + this._nativeDb!.dispose(); + this._nativeDb = undefined; + } + + private static buildChangeSetTokens(changeSets: ChangeSet[], changeSetsPath: string): ChangeSetToken[] { + const changeSetTokens = new Array(); + changeSets.forEach((changeSet: ChangeSet) => { + const changeSetPathname = path.join(changeSetsPath, changeSet.fileName!); + changeSetTokens.push(new ChangeSetToken(changeSet.wsgId, changeSet.parentId!, +changeSet.index!, changeSetPathname, changeSet.changesType === ChangesType.Schema, changeSet.pushDate)); + }); + return changeSetTokens; + } + + /** Create a ChangedElementsDb + * @param pathName The path to the ECDb file to create. + * @throws [IModelError]($common) if the operation failed. + */ + private _createDb(briefcase: IModelDb, pathName: string): void { + const status: DbResult = this.nativeDb.createDb(briefcase.nativeDb, pathName); + if (status !== DbResult.BE_SQLITE_OK) + throw new IModelError(status, "Failed to created ECDb"); + } + + /** Open the Changed Elements Db. + * @param pathName The path to the ECDb file to open + * @param openMode Open mode + * @throws [IModelError]($common) if the operation failed. + */ + private _openDb(pathName: string, openMode: ECDbOpenMode = ECDbOpenMode.Readonly): void { + const nativeOpenMode: OpenMode = openMode === ECDbOpenMode.Readonly ? OpenMode.Readonly : OpenMode.ReadWrite; + const tryUpgrade: boolean = openMode === ECDbOpenMode.FileUpgrade; + const status: DbResult = this.nativeDb.openDb(pathName, nativeOpenMode, tryUpgrade); + if (status !== DbResult.BE_SQLITE_OK) + throw new IModelError(status, "Failed to open ECDb"); + } + + /** Open the Changed Elements Db. + * @param pathName The path to the ECDb file to open + * @param openMode Open mode + * @returns ChangedElementsDb + */ + public static openDb(pathName: string, openMode: ECDbOpenMode = ECDbOpenMode.Readonly): ChangedElementsDb { + const cacheDb: ChangedElementsDb = new ChangedElementsDb(); + cacheDb._openDb(pathName, openMode); + return cacheDb; + } + + /** Create the changed elements cache db + * @param briefcase IModelDb to use + * @param pathName The path to the ECDb file to create. + * @returns The new cache db + */ + public static createDb(briefcase: IModelDb, pathName: string): ChangedElementsDb { + const cacheDb: ChangedElementsDb = new ChangedElementsDb(); + cacheDb._createDb(briefcase, pathName); + return cacheDb; + } + + /** Processes a range of changesets and adds it to the changed elements cache + * @param accessToken Access token to download changesets + * @param briefcase iModel briefcase to use + * @param startChangesetId Start Changeset Id + * @param endChangesetId End Changeset Id + */ + public async processChangesets(accessToken: AccessToken, briefcase: IModelDb, rulesetId: string, startChangesetId: string, endChangesetId: string, filterSpatial?: boolean): Promise { + const ctx = new ChangeSummaryExtractContext(accessToken, briefcase); + const changesets = await ChangeSummaryManager.downloadChangeSets(new ActivityLoggingContext(""), ctx, startChangesetId, endChangesetId); + const tokens = ChangedElementsDb.buildChangeSetTokens(changesets, BriefcaseManager.getChangeSetsPath(briefcase.iModelToken.iModelId!)); + // ChangeSets need to be processed from newest to oldest + tokens.reverse(); + const status: DbResult = this.nativeDb.processChangesets(briefcase.nativeDb, JSON.stringify(tokens), rulesetId, !!filterSpatial ? filterSpatial : false); + if (status !== DbResult.BE_SQLITE_OK) + throw new IModelError(status, "Failed to process changesets"); + return status; + } + + /** Get changed elements between two changesets + * @param startChangesetId Start Changeset Id + * @param endChangesetId End Changeset Id + * @returns Returns the changed elements between the changesets provided + * @throws [IModelError]($common) if the operation failed. + */ + public getChangedElements(startChangesetId: string, endChangesetId: string): ChangedElements | undefined { + const result: IModelJsNative.ErrorStatusOrResult = this.nativeDb.getChangedElements(startChangesetId, endChangesetId); + if (result.error) + throw new IModelError(result.error.status, result.error.message); + return result.result; + } + + /** Returns true if the Changed Elements Db is open */ + public get isOpen(): boolean { return this.nativeDb.isOpen(); } + + /** Returns true if the cache already contains this changeset Id */ + public isProcessed(changesetId: string): boolean { return this.nativeDb.isProcessed(changesetId); } + + /** Close the Db after saving any uncommitted changes. + * @throws [IModelError]($common) if the database is not open. + */ + public closeDb(): void { + this.nativeDb.closeDb(); + } + + public get nativeDb(): IModelJsNative.ChangedElementsECDb { + if (!this._nativeDb) + throw new IModelError(IModelStatus.BadRequest, "ChangedElementsDb object has already been disposed."); + + return this._nativeDb!; + } +} diff --git a/core/backend/src/ChangedElementsManager.ts b/core/backend/src/ChangedElementsManager.ts new file mode 100644 index 0000000..5ba6b2c --- /dev/null +++ b/core/backend/src/ChangedElementsManager.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { BriefcaseManager } from "./BriefcaseManager"; +import { GuidString } from "@bentley/bentleyjs-core"; +import { ChangedElements } from "@bentley/imodeljs-common"; +import { ChangedElementsDb } from "./ChangedElementsDb"; +import { IModelJsFs } from "./IModelJsFs"; + +interface ChangedElementsDbCacheEntry { + iModelId: GuidString; + db: ChangedElementsDb; +} + +/** Utilities for querying changed elements caches */ +export class ChangedElementsManager { + /** Maintains a single entry since we will only have a cache per iModel, which means a ChangedElementsDb per backend instance */ + private static _entry: ChangedElementsDbCacheEntry | undefined; + + public static getChangedElementsPathName(iModelId: GuidString): string { return BriefcaseManager.getChangedElementsPathName(iModelId); } + + /** Get changed elements Db */ + private static getChangedElementsDb(iModelId: GuidString): ChangedElementsDb | undefined { + if (this._entry && this._entry.iModelId === iModelId) + return this._entry.db; + if (this._entry && this._entry.iModelId !== iModelId) { + this._entry.db.closeDb(); + this._entry = undefined; + } + if (!this._entry) { + const path = ChangedElementsManager.getChangedElementsPathName(iModelId); + if (!IModelJsFs.existsSync(path)) + return undefined; + + const db: ChangedElementsDb = ChangedElementsDb.openDb(path); + this._entry = { + iModelId, + db, + }; + + return db; + } + + return undefined; + } + + /** Gets the changed elements from the cache if found + * @param iModelId Id of the iModel + * @param startChangesetId Start changeset Id + * @param endChangesetId End changeset Id + * @returns Changed elements if found + */ + public static getChangedElements(iModelId: GuidString, startChangesetId: string, endChangesetId: string): ChangedElements | undefined { + const db: ChangedElementsDb | undefined = ChangedElementsManager.getChangedElementsDb(iModelId); + if (!db) + return undefined; + + return db.getChangedElements(startChangesetId, endChangesetId); + } + + /** Checks if the cache contains information about the changeset + * @param iModelId Id of the iModel + * @param changesetId Changeset to check for + * @returns true if the changeset has been processed and exists in the cache + */ + public static isProcessed(iModelId: GuidString, changesetId: string): boolean { + const db: ChangedElementsDb | undefined = ChangedElementsManager.getChangedElementsDb(iModelId); + if (!db) + return false; + + return db.isProcessed(changesetId); + } +} diff --git a/core/backend/src/ConcurrencyControl.ts b/core/backend/src/ConcurrencyControl.ts index 51c6e76..244b39a 100644 --- a/core/backend/src/ConcurrencyControl.ts +++ b/core/backend/src/ConcurrencyControl.ts @@ -218,6 +218,27 @@ export class ConcurrencyControl { return res; } + /** Obtain the CodeSpec lock. This is always an immediate request, never deferred. */ + public async lockCodeSpecs(actx: ActivityLoggingContext, accessToken: AccessToken): Promise { + actx.enter(); + const locks: Lock[] = [ + { + wsgId: "what-is-this", + ecId: "and-what-is-this", + lockLevel: LockLevel.Exclusive, + lockType: LockType.CodeSpecs, + objectId: Id64.fromString("0x1"), + briefcaseId: this._iModel.briefcase.briefcaseId, + seedFileId: this._iModel.briefcase.fileId, + releasedWithChangeSet: this._iModel.briefcase.changeSetId, + }, + ]; + assert(this.inBulkOperation(), "should always be in bulk mode"); + const res = BriefcaseManager.imodelClient.locks.update(actx, accessToken, this._iModel.iModelToken.iModelId!, locks); + assert(this.inBulkOperation(), "should always be in bulk mode"); + return res; + } + private buildLockRequests(briefcaseInfo: BriefcaseEntry, req: ConcurrencyControl.Request): Lock[] | undefined { const reqAny: any = ConcurrencyControl.convertRequestToAny(req); diff --git a/core/backend/src/ECDb.ts b/core/backend/src/ECDb.ts index 7755262..756686c 100644 --- a/core/backend/src/ECDb.ts +++ b/core/backend/src/ECDb.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ /** @module ECDb */ -import { IModelError, IModelStatus } from "@bentley/imodeljs-common"; +import { IModelError, IModelStatus, PageOptions, kPagingDefaultOptions, PagableECSql } from "@bentley/imodeljs-common"; import { IModelJsNative } from "./IModelJsNative"; import { ECSqlStatement, ECSqlStatementCache } from "./ECSqlStatement"; import { SqliteStatement, SqliteStatementCache, CachedSqliteStatement } from "./SqliteStatement"; @@ -12,7 +12,6 @@ import { DbResult, OpenMode, IDisposable, Logger, assert } from "@bentley/bentle import { IModelHost } from "./IModelHost"; const loggingCategory = "imodeljs-backend.ECDb"; - /** Modes for how to open [ECDb]($backend) files. */ export enum ECDbOpenMode { Readonly, @@ -22,7 +21,7 @@ export enum ECDbOpenMode { } /** An ECDb file */ -export class ECDb implements IDisposable { +export class ECDb implements IDisposable, PagableECSql { private _nativeDb?: IModelJsNative.ECDb; private readonly _statementCache: ECSqlStatementCache = new ECSqlStatementCache(); private readonly _sqliteStatementCache: SqliteStatementCache = new SqliteStatementCache(); @@ -30,6 +29,130 @@ export class ECDb implements IDisposable { constructor() { this._nativeDb = new IModelHost.platform.ECDb(); } + /** Compute number of rows that would be returned by the ECSQL. + * + * See also: + * - [ECSQL Overview]($docs/learning/backend/ExecutingECSQL) + * - [Code Examples]($docs/learning/backend/ECSQLCodeExamples) + * + * @param ecsql The ECSQL statement to execute + * @param bindings The values to bind to the parameters (if the ECSQL has any). + * Pass an *array* of values if the parameters are *positional*. + * Pass an *object of the values keyed on the parameter name* for *named parameters*. + * The values in either the array or object must match the respective types of the parameters. + * See "[iModel.js Types used in ECSQL Parameter Bindings]($docs/learning/ECSQLParameterTypes)" for details. + * @returns Return row count. + * @throws [IModelError]($common) If the statement is invalid + */ + public async queryRowCount(ecsql: string, bindings?: any[] | object): Promise { + return this.withPreparedStatement(`select count(*) from (${ecsql})`, async (stmt: ECSqlStatement) => { + if (bindings) + stmt.bindValues(bindings); + const ret = await stmt.stepAsync(); + if (ret === DbResult.BE_SQLITE_ROW) { + return stmt.getValue(0).getInteger(); + } + throw new IModelError(ret, "Fail to compute row count"); + }); + } + + /** Execute a query agaisnt this ECDb + * The result of the query is returned as an array of JavaScript objects where every array element represents an + * [ECSQL row]($docs/learning/ECSQLRowFormat). + * + * See also: + * - [ECSQL Overview]($docs/learning/backend/ExecutingECSQL) + * - [Code Examples]($docs/learning/backend/ECSQLCodeExamples) + * + * @param ecsql The ECSQL statement to execute + * @param bindings The values to bind to the parameters (if the ECSQL has any). + * Pass an *array* of values if the parameters are *positional*. + * Pass an *object of the values keyed on the parameter name* for *named parameters*. + * The values in either the array or object must match the respective types of the parameters. + * See "[iModel.js Types used in ECSQL Parameter Bindings]($docs/learning/ECSQLParameterTypes)" for details. + * @param options Provide paging option. This allow set page size and page number from which to grab rows from. + * @returns Returns the query result as an array of the resulting rows or an empty array if the query has returned no rows. + * See [ECSQL row format]($docs/learning/ECSQLRowFormat) for details about the format of the returned rows. + * @throws [IModelError]($common) If the statement is invalid + */ + public async queryPage(ecsql: string, bindings?: any[] | object, options?: PageOptions): Promise { + if (!options) { + options = kPagingDefaultOptions; + } + + const pageNo = options.start || kPagingDefaultOptions.start; + const pageSize = options.size || kPagingDefaultOptions.size; + + // verify if correct options was provided. + if (pageNo! < 0) + throw new IModelError(DbResult.BE_SQLITE_ERROR, "options.start must be positive integer"); + + if (pageSize! < 0) + throw new IModelError(DbResult.BE_SQLITE_ERROR, "options.size must be positive integer starting from 1"); + + const pageParams = { sys_page_size: pageSize!, sys_page_offset: pageNo! * pageSize! }; + return this.withPreparedStatement(`select * from (${ecsql}) limit :sys_page_size offset :sys_page_offset`, async (stmt: ECSqlStatement) => { + if (bindings) + stmt.bindValues(bindings); + + stmt.bindValues(pageParams); + const rows: any[] = []; + + let ret = await stmt.stepAsync(); + while (ret === DbResult.BE_SQLITE_ROW) { + rows.push(stmt.getRow()); + ret = await stmt.stepAsync(); + } + return rows; + }); + } + + /** Execute a pagable query. + * The result of the query is async iterator over the rows. The iterator will get next page automatically once rows in current page has been read. + * [ECSQL row]($docs/learning/ECSQLRowFormat). + * + * See also: + * - [ECSQL Overview]($docs/learning/backend/ExecutingECSQL) + * - [Code Examples]($docs/learning/backend/ECSQLCodeExamples) + * + * @param ecsql The ECSQL statement to execute + * @param bindings The values to bind to the parameters (if the ECSQL has any). + * Pass an *array* of values if the parameters are *positional*. + * Pass an *object of the values keyed on the parameter name* for *named parameters*. + * The values in either the array or object must match the respective types of the parameters. + * See "[iModel.js Types used in ECSQL Parameter Bindings]($docs/learning/ECSQLParameterTypes)" for details. + * @param options Provide paging option. Which allow page to start iterating from and also size of the page to use. + * @returns Returns the query result as an array of the resulting rows or an empty array if the query has returned no rows. + * See [ECSQL row format]($docs/learning/ECSQLRowFormat) for details about the format of the returned rows. + * @throws [IModelError]($common) If the statement is invalid + */ + public async * query(ecsql: string, bindings?: any[] | object, options?: PageOptions): AsyncIterableIterator { + if (!options) { + options = kPagingDefaultOptions; + } + + let pageNo = options.start || kPagingDefaultOptions.start!; + const pageSize = options.size || kPagingDefaultOptions.size!; + + // verify if correct options was provided. + if (pageNo < 0) + throw new IModelError(DbResult.BE_SQLITE_ERROR, "options.start must be positive integer"); + + if (pageSize < 0) + throw new IModelError(DbResult.BE_SQLITE_ERROR, "options.size must be positive integer starting from 1"); + + do { + const page = await this.queryPage(ecsql, bindings, { start: pageNo, size: pageSize }); + if (page.length > 0) { + for (const row of page) { + yield row; + } + pageNo = pageNo + 1; + } else { + pageNo = -1; + } + } while (pageNo >= 0); + } /** Call this function when finished with this ECDb object. This releases the native resources held by the * ECDb object. @@ -78,6 +201,16 @@ export class ECDb implements IDisposable { this.nativeDb.closeDb(); } + /** @private use to test statement caching */ + public clearStatementCache() { + this._statementCache.clear(); + } + + /** @private use to test statement caching */ + public getCachedStatementCount() { + return this._statementCache.getCount(); + } + /** Commit the outermost transaction, writing changes to the file. Then, restart the transaction. * @param changeSetName The name of the operation that generated these changes. * @throws [IModelError]($common) if the database is not open or if the operation failed. @@ -126,12 +259,23 @@ export class ECDb implements IDisposable { */ public withPreparedStatement(ecsql: string, cb: (stmt: ECSqlStatement) => T): T { const stmt = this.getPreparedStatement(ecsql); + const release = () => { + if (stmt.isShared) + this._statementCache.release(stmt); + else + stmt.dispose(); + }; + try { const val: T = cb(stmt); - this._statementCache.release(stmt); + if (val instanceof Promise) { + val.then(release, release); + } else { + release(); + } return val; } catch (err) { - this._statementCache.release(stmt); + release(); Logger.logError(loggingCategory, err.toString()); throw err; } @@ -153,9 +297,12 @@ export class ECDb implements IDisposable { } this._statementCache.removeUnusedStatementsIfNecessary(); - const stmt = this.prepareStatement(ecsql); - this._statementCache.add(ecsql, stmt); + if (cachedStmt) + this._statementCache.replace(ecsql, stmt); + else + this._statementCache.add(ecsql, stmt); + return stmt; } @@ -180,12 +327,22 @@ export class ECDb implements IDisposable { */ public withPreparedSqliteStatement(sql: string, cb: (stmt: SqliteStatement) => T): T { const stmt = this.getPreparedSqliteStatement(sql); + const release = () => { + if (stmt.isShared) + this._sqliteStatementCache.release(stmt); + else + stmt.dispose(); + }; try { const val: T = cb(stmt); - this._sqliteStatementCache.release(stmt); + if (val instanceof Promise) { + val.then(release, release); + } else { + release(); + } return val; } catch (err) { - this._sqliteStatementCache.release(stmt); + release(); Logger.logError(loggingCategory, err.toString()); throw err; } @@ -205,11 +362,12 @@ export class ECDb implements IDisposable { cachedStmt.useCount++; return cachedStmt.statement; } - this._sqliteStatementCache.removeUnusedStatementsIfNecessary(); - const stmt: SqliteStatement = this.prepareSqliteStatement(sql); - this._sqliteStatementCache.add(sql, stmt); + if (cachedStmt) + this._sqliteStatementCache.replace(sql, stmt); + else + this._sqliteStatementCache.add(sql, stmt); return stmt; } /** Prepare an SQLite SQL statement. diff --git a/core/backend/src/ECSqlStatement.ts b/core/backend/src/ECSqlStatement.ts index 823cec3..a267cb1 100644 --- a/core/backend/src/ECSqlStatement.ts +++ b/core/backend/src/ECSqlStatement.ts @@ -10,6 +10,7 @@ import { XAndY, XYAndZ, XYZ, LowAndHighXYZ, Range3d } from "@bentley/geometry-co import { ECDb } from "./ECDb"; import { IModelJsNative } from "./IModelJsNative"; import { IModelHost } from "./IModelHost"; +import { Config } from "@bentley/imodeljs-clients"; /** @hidden */ const loggingCategory = "imodeljs-backend.ECSqlStatement"; @@ -266,6 +267,30 @@ export class ECSqlStatement implements IterableIterator, IDisposable { */ public step(): DbResult { return this._stmt!.step(); } + /** Asynchronous version of Step method. + * + * For **ECSQL SELECT** statements the method returns + * - [DbResult.BE_SQLITE_ROW]($bentleyjs-core) if the statement now points successfully to the next row. + * - [DbResult.BE_SQLITE_DONE]($bentleyjs-core) if the statement has no more rows. + * - Error status in case of errors. + * + * For **ECSQL INSERT, UPDATE, DELETE** statements the method returns + * - [DbResult.BE_SQLITE_DONE]($bentleyjs-core) if the statement has been executed successfully. + * - Error status in case of errors. + * + * > Insert statements can be used with ECDb only, not with IModelDb. + * + * See also: [Code Samples]($docs/learning/backend/ECSQLCodeExamples) + */ + public async stepAsync(): Promise { + return new Promise((resolve, reject) => { + if (!this._stmt) + reject(); + else + this._stmt.stepAsync(resolve); + }); + } + /** Step this INSERT statement and returns status and the ECInstanceId of the newly * created instance. * @@ -282,6 +307,29 @@ export class ECSqlStatement implements IterableIterator, IDisposable { return new ECSqlInsertResult(r.status); } + /** Asynchronous version of stepForInsert method + * created instance. + * + * > Insert statements can be used with ECDb only, not with IModelDb. + * + * @returns Returns the generated ECInstanceId in case of success and the status of the step + * call. In case of error, the respective error code is returned. + */ + public async stepForInsertAsync(): Promise { + return new Promise((resolve, reject) => { + if (!this._stmt) + reject(); + else { + this._stmt.stepForInsertAsync((r: { status: DbResult, id: string }) => { + if (r.status === DbResult.BE_SQLITE_DONE) + resolve(new ECSqlInsertResult(r.status, r.id)); + else + resolve(new ECSqlInsertResult(r.status)); + }); + } + }); + } + /** Get the query result's column count (only for ECSQL SELECT statements). */ public getColumnCount(): number { return this._stmt!.getColumnCount(); } @@ -942,7 +990,6 @@ export class CachedECSqlStatement { this.useCount = 1; } } - /** A cache for ECSqlStatements. * * Preparing [ECSqlStatement]($backend)s can be costly. This class provides a way to @@ -955,7 +1002,7 @@ export class ECSqlStatementCache { private readonly _statements: Map = new Map(); public readonly maxCount: number; - public constructor(maxCount = 20) { this.maxCount = maxCount; } + public constructor(maxCount = Config.App.getNumber("imjs_ecsql_cache_size", 40)) { this.maxCount = maxCount; } public add(str: string, stmt: ECSqlStatement): void { const existing = this._statements.get(str); @@ -973,6 +1020,20 @@ export class ECSqlStatementCache { return this._statements.get(str); } + public replace(str: string, stmt: ECSqlStatement) { + if (stmt.isShared) { + throw new Error("expecting a unshared statement"); + } + const existingCS = this.find(str); + if (existingCS) { + existingCS.statement.setIsShared(false); + this._statements.delete(str); + } + const newCS = new CachedECSqlStatement(stmt); + newCS.statement.setIsShared(true); + this._statements.set(str, newCS); + } + public release(stmt: ECSqlStatement): void { for (const cs of this._statements) { const css = cs[1]; diff --git a/core/backend/src/Element.ts b/core/backend/src/Element.ts index 3cecd82..4c7c533 100644 --- a/core/backend/src/Element.ts +++ b/core/backend/src/Element.ts @@ -5,7 +5,7 @@ /** @module Elements */ import { Id64String, Id64, GuidString, DbOpcode, JsonUtils, IModelStatus } from "@bentley/bentleyjs-core"; -import { Transform } from "@bentley/geometry-core"; +import { Transform, Range3d } from "@bentley/geometry-core"; import { DrawingModel } from "./Model"; import { Entity } from "./Entity"; import { IModelDb } from "./IModelDb"; @@ -746,7 +746,7 @@ export class GeometryPart extends DefinitionElement implements GeometryPartProps public constructor(props: GeometryPartProps, iModel: IModelDb) { super(props, iModel); this.geom = props.geom; - this.bbox = ElementAlignedBox3d.fromJSON(props.bbox); + this.bbox = Range3d.fromJSON(props.bbox); } /** convert this geometry part to a JSON object. diff --git a/core/backend/src/Entity.ts b/core/backend/src/Entity.ts index ec099e9..4077048 100644 --- a/core/backend/src/Entity.ts +++ b/core/backend/src/Entity.ts @@ -64,5 +64,5 @@ export class Entity implements EntityProps { public get className(): string { return this.constructor.name; } /** Make a deep copy of this Entity */ - public clone(): T { return new (this.constructor as any)(this, this.iModel) as T; } + public clone(): this { return new (this.constructor as any)(this, this.iModel); } } diff --git a/core/backend/src/IModelDb.ts b/core/backend/src/IModelDb.ts index 8834fac..90ffcfb 100644 --- a/core/backend/src/IModelDb.ts +++ b/core/backend/src/IModelDb.ts @@ -4,15 +4,16 @@ *--------------------------------------------------------------------------------------------*/ /** @module iModels */ import { ActivityLoggingContext, BeEvent, BentleyStatus, DbResult, AuthStatus, GuidString, Id64, Id64Arg, Id64Set, Id64String, JsonUtils, Logger, OpenMode } from "@bentley/bentleyjs-core"; -import { AccessToken } from "@bentley/imodeljs-clients"; +import { AccessToken, UlasClient, UsageLogEntry, UsageType, LogPostingResponse } from "@bentley/imodeljs-clients"; import { AxisAlignedBox3d, CategorySelectorProps, Code, CodeSpec, CreateIModelProps, DisplayStyleProps, EcefLocation, ElementAspectProps, ElementLoadProps, ElementProps, EntityMetaData, EntityProps, EntityQueryParams, FilePropertyProps, FontMap, FontMapProps, FontProps, IModel, IModelError, IModelNotFoundResponse, IModelProps, IModelStatus, IModelToken, IModelVersion, ModelProps, ModelSelectorProps, PropertyCallback, SheetProps, SnapRequestProps, SnapResponseProps, ThumbnailProps, TileTreeProps, ViewDefinitionProps, ViewQueryParams, - ViewStateProps, IModelCoordinatesResponseProps, GeoCoordinatesResponseProps, + ViewStateProps, IModelCoordinatesResponseProps, GeoCoordinatesResponseProps, PageOptions, kPagingDefaultOptions, PagableECSql, } from "@bentley/imodeljs-common"; import * as path from "path"; +import * as os from "os"; import { BriefcaseEntry, BriefcaseId, BriefcaseManager, KeepBriefcase } from "./BriefcaseManager"; import { ClassRegistry, MetaDataRegistry } from "./ClassRegistry"; import { CodeSpecs } from "./CodeSpecs"; @@ -22,12 +23,11 @@ import { Element, Subject } from "./Element"; import { ElementAspect } from "./ElementAspect"; import { Entity } from "./Entity"; import { IModelJsNative } from "./IModelJsNative"; -import { IModelJsFs } from "./IModelJsFs"; import { Model } from "./Model"; import { Relationship, RelationshipProps, Relationships } from "./Relationship"; import { CachedSqliteStatement, SqliteStatement, SqliteStatementCache } from "./SqliteStatement"; import { SheetViewDefinition, ViewDefinition } from "./ViewDefinition"; -import { IModelHost, KnownLocations } from "./IModelHost"; +import { IModelHost } from "./IModelHost"; /** @hidden */ const loggingCategory = "imodeljs-backend.IModelDb"; @@ -114,7 +114,7 @@ export class OpenParams { * IModelDb raises a set of events to allow apps and subsystems to track IModelDb object life cycle, including [[onOpen]] and [[onOpened]]. * @see [learning about IModelDb]($docs/learning/backend/IModelDb.md) */ -export class IModelDb extends IModel { +export class IModelDb extends IModel implements PagableECSql { public static readonly defaultLimit = 1000; // default limit for batching queries public static readonly maxLimit = 10000; // maximum limit for batching queries /** Event called after a changeset is applied to this IModelDb. */ @@ -203,10 +203,10 @@ export class IModelDb extends IModel { } /** Create an iModel on iModelHub */ - public static async create(activity: ActivityLoggingContext, accessToken: AccessToken, contextId: string, fileName: string, args: CreateIModelProps): Promise { + public static async create(activity: ActivityLoggingContext, accessToken: AccessToken, contextId: string, iModelName: string, args: CreateIModelProps): Promise { activity.enter(); IModelDb.onCreate.raiseEvent(accessToken, contextId, args); - const iModelId: string = await BriefcaseManager.create(activity, accessToken, contextId, fileName, args); + const iModelId: string = await BriefcaseManager.create(activity, accessToken, contextId, iModelName, args); return IModelDb.open(activity, accessToken, contextId, iModelId); } @@ -236,12 +236,48 @@ export class IModelDb extends IModel { IModelDb.onOpen.raiseEvent(accessToken, contextId, iModelId, openParams, version, activity); const briefcaseEntry: BriefcaseEntry = await BriefcaseManager.open(activity, accessToken, contextId, iModelId, openParams, version); activity.enter(); + const imodelDb = IModelDb.constructIModelDb(briefcaseEntry, openParams, contextId); + await imodelDb.logUsage(activity, accessToken, contextId); IModelDb.onOpened.raiseEvent(imodelDb, activity); Logger.logTrace(loggingCategory, "IModelDb.open", () => ({ ...imodelDb._token, ...openParams })); return imodelDb; } + private static createUsageLogEntry(accessToken: AccessToken, contextId: GuidString): UsageLogEntry { + const entry: UsageLogEntry = new UsageLogEntry(os.hostname(), UsageType.Trial); + const userInfo = accessToken.getUserInfo(); + const featureTrackingInfo = userInfo ? userInfo.featureTracking : undefined; + + entry.userInfo = { + imsId: userInfo ? userInfo.id : "", + ultimateSite: !featureTrackingInfo ? 0 : parseInt(featureTrackingInfo.ultimateSite, 10), + usageCountryIso: !featureTrackingInfo ? "" : featureTrackingInfo.usageCountryIso, + }; + + entry.projectId = contextId; + entry.productId = 2686; // todo: needs to be passed in from frontend + entry.productVersion = { major: 1, minor: 0 }; // todo: needs to be passed in from frontend + + return entry; + } + + private async logUsage(actx: ActivityLoggingContext, accessToken: AccessToken, contextId: GuidString): Promise { + const client = new UlasClient(); + let status: BentleyStatus; + try { + const entry: UsageLogEntry = IModelDb.createUsageLogEntry(accessToken, contextId); + const resp: LogPostingResponse = await client.logUsage(actx, accessToken, entry); + status = resp ? resp.status : BentleyStatus.ERROR; + } catch (error) { + status = BentleyStatus.ERROR; + } + + if (status !== BentleyStatus.SUCCESS) { + Logger.logError(loggingCategory, "Could not log usage information", () => this.iModelToken); + } + } + /** * Close this standalone iModel, if it is currently open * @throws IModelError if the iModel is not open, or is not standalone @@ -313,7 +349,9 @@ export class IModelDb extends IModel { /** Event called when the iModel is about to be closed */ public readonly onBeforeClose = new BeEvent<() => void>(); - /** Get the in-memory handle of the native Db */ + /** Get the in-memory handle of the native Db + * @hidden + */ public get nativeDb(): IModelJsNative.DgnDb { return this.briefcase.nativeDb; } /** Get the briefcase Id of this iModel */ @@ -340,7 +378,11 @@ export class IModelDb extends IModel { this._statementCache.removeUnusedStatementsIfNecessary(); const stmt = this.prepareStatement(ecsql); - this._statementCache.add(ecsql, stmt); + if (cachedStatement) + this._statementCache.replace(ecsql, stmt); + else + this._statementCache.add(ecsql, stmt); + return stmt; } @@ -359,18 +401,154 @@ export class IModelDb extends IModel { */ public withPreparedStatement(ecsql: string, callback: (stmt: ECSqlStatement) => T): T { const stmt = this.getPreparedStatement(ecsql); + const release = () => { + if (stmt.isShared) + this._statementCache.release(stmt); + else + stmt.dispose(); + }; + try { - const val = callback(stmt); - this._statementCache.release(stmt); + const val: T = callback(stmt); + if (val instanceof Promise) { + val.then(release, release); + } else { + release(); + } return val; } catch (err) { - this._statementCache.release(stmt); // always release statement + release(); Logger.logError(loggingCategory, err.toString()); throw err; } } + /** Compute number of rows that would be returned by the ECSQL. + * + * See also: + * - [ECSQL Overview]($docs/learning/backend/ExecutingECSQL) + * - [Code Examples]($docs/learning/backend/ECSQLCodeExamples) + * + * @param ecsql The ECSQL statement to execute + * @param bindings The values to bind to the parameters (if the ECSQL has any). + * Pass an *array* of values if the parameters are *positional*. + * Pass an *object of the values keyed on the parameter name* for *named parameters*. + * The values in either the array or object must match the respective types of the parameters. + * See "[iModel.js Types used in ECSQL Parameter Bindings]($docs/learning/ECSQLParameterTypes)" for details. + * @returns Return row count. + * @throws [IModelError]($common) If the statement is invalid + */ + public async queryRowCount(ecsql: string, bindings?: any[] | object): Promise { + return this.withPreparedStatement(`select count(*) from (${ecsql})`, async (stmt: ECSqlStatement) => { + if (bindings) + stmt.bindValues(bindings); + const ret = await stmt.stepAsync(); + if (ret === DbResult.BE_SQLITE_ROW) { + return stmt.getValue(0).getInteger(); + } + throw new IModelError(ret, "Fail to compute row count"); + }); + } + + /** Execute a query agaisnt this ECDb + * The result of the query is returned as an array of JavaScript objects where every array element represents an + * [ECSQL row]($docs/learning/ECSQLRowFormat). + * + * See also: + * - [ECSQL Overview]($docs/learning/backend/ExecutingECSQL) + * - [Code Examples]($docs/learning/backend/ECSQLCodeExamples) + * + * @param ecsql The ECSQL statement to execute + * @param bindings The values to bind to the parameters (if the ECSQL has any). + * Pass an *array* of values if the parameters are *positional*. + * Pass an *object of the values keyed on the parameter name* for *named parameters*. + * The values in either the array or object must match the respective types of the parameters. + * See "[iModel.js Types used in ECSQL Parameter Bindings]($docs/learning/ECSQLParameterTypes)" for details. + * @param options Provide paging option. This allow set page size and page number from which to grab rows from. + * @returns Returns the query result as an array of the resulting rows or an empty array if the query has returned no rows. + * See [ECSQL row format]($docs/learning/ECSQLRowFormat) for details about the format of the returned rows. + * @throws [IModelError]($common) If the statement is invalid + */ + public async queryPage(ecsql: string, bindings?: any[] | object, options?: PageOptions): Promise { + if (!options) { + options = kPagingDefaultOptions; + } + + const pageNo = options.start || kPagingDefaultOptions.start; + const pageSize = options.size || kPagingDefaultOptions.size; + + // verify if correct options was provided. + if (pageNo! < 0) + throw new IModelError(DbResult.BE_SQLITE_ERROR, "options.start must be positive integer"); + + if (pageSize! < 0) + throw new IModelError(DbResult.BE_SQLITE_ERROR, "options.size must be positive integer starting from 1"); + + const pageParams = { sys_page_size: pageSize!, sys_page_offset: pageNo! * pageSize! }; + return this.withPreparedStatement(`select * from (${ecsql}) limit :sys_page_size offset :sys_page_offset`, async (stmt: ECSqlStatement) => { + if (bindings) + stmt.bindValues(bindings); + + stmt.bindValues(pageParams); + const rows: any[] = []; + + let ret = await stmt.stepAsync(); + while (ret === DbResult.BE_SQLITE_ROW) { + rows.push(stmt.getRow()); + ret = await stmt.stepAsync(); + } + return rows; + }); + } + + /** Execute a pagable query. + * The result of the query is async iterator over the rows. The iterator will get next page automatically once rows in current page has been read. + * [ECSQL row]($docs/learning/ECSQLRowFormat). + * + * See also: + * - [ECSQL Overview]($docs/learning/backend/ExecutingECSQL) + * - [Code Examples]($docs/learning/backend/ECSQLCodeExamples) + * + * @param ecsql The ECSQL statement to execute + * @param bindings The values to bind to the parameters (if the ECSQL has any). + * Pass an *array* of values if the parameters are *positional*. + * Pass an *object of the values keyed on the parameter name* for *named parameters*. + * The values in either the array or object must match the respective types of the parameters. + * See "[iModel.js Types used in ECSQL Parameter Bindings]($docs/learning/ECSQLParameterTypes)" for details. + * @param options Provide paging option. Which allow page to start iterating from and also size of the page to use. + * @returns Returns the query result as an array of the resulting rows or an empty array if the query has returned no rows. + * See [ECSQL row format]($docs/learning/ECSQLRowFormat) for details about the format of the returned rows. + * @throws [IModelError]($common) If the statement is invalid + */ + public async * query(ecsql: string, bindings?: any[] | object, options?: PageOptions): AsyncIterableIterator { + if (!options) { + options = kPagingDefaultOptions; + } + + let pageNo = options.start || kPagingDefaultOptions.start!; + const pageSize = options.size || kPagingDefaultOptions.size!; + + // verify if correct options was provided. + if (pageNo < 0) + throw new IModelError(DbResult.BE_SQLITE_ERROR, "options.start must be positive integer"); + + if (pageSize < 0) + throw new IModelError(DbResult.BE_SQLITE_ERROR, "options.size must be positive integer starting from 1"); + + do { + const page = await this.queryPage(ecsql, bindings, { start: pageNo, size: pageSize }); + if (page.length > 0) { + for (const row of page) { + yield row; + } + pageNo = pageNo + 1; + } else { + pageNo = -1; + } + } while (pageNo >= 0); + } /** Execute a query against this IModelDb. + * @deprecated use withPreparedStatement or query or queryPage instead * The result of the query is returned as an array of JavaScript objects where every array element represents an * [ECSQL row]($docs/learning/ECSQLRowFormat). * @@ -413,12 +591,22 @@ export class IModelDb extends IModel { */ public withPreparedSqliteStatement(sql: string, callback: (stmt: SqliteStatement) => T): T { const stmt = this.getPreparedSqlStatement(sql); + const release = () => { + if (stmt.isShared) + this._sqliteStatementCache.release(stmt); + else + stmt.dispose(); + }; try { - const val = callback(stmt); - this._sqliteStatementCache.release(stmt); + const val: T = callback(stmt); + if (val instanceof Promise) { + val.then(release, release); + } else { + release(); + } return val; } catch (err) { - this._sqliteStatementCache.release(stmt); // always release statement + release(); Logger.logError(loggingCategory, err.toString()); throw err; } @@ -446,9 +634,12 @@ export class IModelDb extends IModel { return cachedStatement.statement; } - this._statementCache.removeUnusedStatementsIfNecessary(); + this._sqliteStatementCache.removeUnusedStatementsIfNecessary(); const stmt: SqliteStatement = this.prepareSqliteStatement(sql); - this._sqliteStatementCache.add(sql, stmt); + if (cachedStatement) + this._sqliteStatementCache.replace(sql, stmt); + else + this._sqliteStatementCache.add(sql, stmt); return stmt; } @@ -825,18 +1016,18 @@ export class IModelDb extends IModel { */ public queryNextAvailableFileProperty(prop: FilePropertyProps) { return this.nativeDb.queryNextAvailableFileProperty(JSON.stringify(prop)); } - public async requestSnap(activity: ActivityLoggingContext, connectionId: string, props: SnapRequestProps): Promise { + public async requestSnap(activity: ActivityLoggingContext, sessionId: string, props: SnapRequestProps): Promise { activity.enter(); - let request = this._snaps.get(connectionId); + let request = this._snaps.get(sessionId); if (undefined === request) { request = new IModelHost.platform.SnapRequest(); - this._snaps.set(connectionId, request); + this._snaps.set(sessionId, request); } else request.cancelSnap(); return new Promise((resolve, reject) => { request!.doSnap(this.nativeDb, JsonUtils.toObject(props), (ret: IModelJsNative.ErrorStatusOrResult) => { - this._snaps.delete(connectionId); + this._snaps.delete(sessionId); if (ret.error !== undefined) reject(new Error(ret.error.message)); else @@ -846,29 +1037,14 @@ export class IModelDb extends IModel { } /** Cancel a previously requested snap. */ - public cancelSnap(connectionId: string): void { - const request = this._snaps.get(connectionId); + public cancelSnap(sessionId: string): void { + const request = this._snaps.get(sessionId); if (undefined !== request) { request.cancelSnap(); - this._snaps.delete(connectionId); + this._snaps.delete(sessionId); } } - /** Load a file from the *Assets* directory of imodeljs-native - * @param assetName The asset file name with path relative to the *Assets* directory. - */ - public static loadNativeAsset(assetName: string): Uint8Array { - const fileName = path.join(KnownLocations.nativeAssetsDir, assetName); - return IModelJsFs.readFileSync(fileName) as Buffer; - } - - /** Execute a test from native code - * @param testName The name of the test - * @param params parameters for the test - * @hidden - */ - public executeTest(testName: string, params: any): any { return JSON.parse(this.nativeDb.executeTest(testName, JSON.stringify(params))); } - /** Get the IModel coordinate corresponding to each GeoCoordinate point in the input */ public async getIModelCoordinatesFromGeoCoordinates(activity: ActivityLoggingContext, props: string): Promise { activity.enter(); @@ -1350,6 +1526,16 @@ export namespace IModelDb { } } + /** Represents the current state of a pollable tile content request. + * Note: lack of a "completed" state because polling a completed request returns the content as a Uint8Array. + * @hidden + */ + export const enum TileContentState { + New, // Request was just created and enqueued. + Pending, // Request is enqueued but not yet being processed. + Loading, // Request is being actively processed. + } + /** @hidden */ export class Tiles { /** @hidden */ @@ -1372,21 +1558,45 @@ export namespace IModelDb { }); } + private pollTileContent(resolve: (arg0: Uint8Array) => void, reject: (err: Error) => void, treeId: string, tileId: string, activity: ActivityLoggingContext) { + activity.enter(); + if (!this._iModel.briefcase) { + reject(this._iModel.newNotOpenError()); + return; + } + + const ret = this._iModel.nativeDb.pollTileContent(treeId, tileId); + if (undefined !== ret.error) { + reject(new IModelError(ret.error.status, "TreeId=" + treeId + " TileId=" + tileId)); + } else if (ret.result instanceof Uint8Array) { + resolve(ret.result); + } else { + // ###TODO: Decide appropriate timeout interval. May want to switch on state (new vs loading vs pending) + setTimeout(() => this.pollTileContent(resolve, reject, treeId, tileId, activity), 10); + } + } + /** @hidden */ public async requestTileContent(activity: ActivityLoggingContext, treeId: string, tileId: string): Promise { activity.enter(); if (!this._iModel.briefcase) throw this._iModel.newNotOpenError(); - return new Promise((resolve, reject) => { - activity.enter(); - this._iModel.nativeDb.getTileContent(treeId, tileId, (ret: IModelJsNative.ErrorStatusOrResult) => { - if (undefined !== ret.error) - reject(new IModelError(ret.error.status, "TreeId=" + treeId + " TileId=" + tileId)); - else - resolve(ret.result!); + if (IModelHost.useTileContentThreadPool) { + return new Promise((resolve, reject) => { + this.pollTileContent(resolve, reject, treeId, tileId, activity); }); - }); + } else { + return new Promise((resolve, reject) => { + activity.enter(); + this._iModel.nativeDb.getTileContent(treeId, tileId, (ret: IModelJsNative.ErrorStatusOrResult) => { + if (undefined !== ret.error) + reject(new IModelError(ret.error.status, "TreeId=" + treeId + " TileId=" + tileId)); + else + resolve(ret.result!); + }); + }); + } } } } diff --git a/core/backend/src/IModelHost.ts b/core/backend/src/IModelHost.ts index df87dc1..4a580b7 100644 --- a/core/backend/src/IModelHost.ts +++ b/core/backend/src/IModelHost.ts @@ -16,7 +16,6 @@ import { IModelJsFs } from "./IModelJsFs"; import { IModelJsNative } from "./IModelJsNative"; import { IModelReadRpcImpl } from "./rpc-impl/IModelReadRpcImpl"; import { IModelTileRpcImpl } from "./rpc-impl/IModelTileRpcImpl"; -import { IModelUnitTestRpcImpl } from "./rpc-impl/IModelUnitTestRpcImpl"; import { IModelWriteRpcImpl } from "./rpc-impl/IModelWriteRpcImpl"; import { StandaloneIModelRpcImpl } from "./rpc-impl/StandaloneIModelRpcImpl"; import { WipRpcImpl } from "./rpc-impl/WipRpcImpl"; @@ -24,6 +23,9 @@ import { initializeRpcBackend } from "./RpcBackend"; import * as os from "os"; import * as semver from "semver"; +/** @hidden */ +const loggingCategory = "imodeljs-backend.IModelHost"; + /** * Configuration of imodeljs-backend. */ @@ -42,6 +44,15 @@ export class IModelHostConfiguration { /** The kind of iModel server to use. Defaults to iModelHubClient */ public imodelClient?: IModelClient; + + /** The time, in milliseconds, for which [IModelTileRpcInterface.requestTileTreeProps]($common) should wait before returning a "pending" status. */ + public tileTreeRequestTimeout = IModelHostConfiguration.defaultTileRequestTimeout; + /** The time, in milliseconds, for which [IModelTileRpcInterface.requestTileContent]($common) should wait before returning a "pending" status. */ + public tileContentRequestTimeout = IModelHostConfiguration.defaultTileRequestTimeout; + /** The default time, in milliseconds, used for [[tileTreeRequestTimeout]] and [[tileContentRequestTimeout]]. To change this, override one or both of those properties. */ + public static defaultTileRequestTimeout = 20 * 1000; + /** If true, requests for tile content will execute on a separate thread pool in order to avoid blocking other, less expensive asynchronous requests such as ECSql queries. */ + public useTileContentThreadPool = false; } /** @@ -68,22 +79,31 @@ export class IModelHost { return; if (!Platform.isMobile) - this.checkVersion(); + this.validateNativePlatformVersion(); platform.logger = Logger; platform.initializeRegion(region); } - private static checkVersion(): void { + private static validateNativePlatformVersion(): void { const requiredVersion = require("../package.json").dependencies["@bentley/imodeljs-native"]; - if (semver.satisfies(this.platform.version, requiredVersion)) + const thisVersion = this.platform.version; + if (semver.satisfies(thisVersion, requiredVersion)) return; if (IModelJsFs.existsSync(path.join(__dirname, "DevBuild.txt"))) { console.log("Bypassing version checks for development build"); // tslint:disable-line:no-console return; } this._platform = undefined; - throw new IModelError(IModelStatus.BadRequest, "imodeljs-native version is (" + this.platform.version + "). imodeljs-backend requires version (" + requiredVersion + ")"); + throw new IModelError(IModelStatus.BadRequest, "imodeljs-native version is (" + thisVersion + "). imodeljs-backend requires version (" + requiredVersion + ")"); + } + + private static validateNodeJsVersion(): void { + const requiredVersion = require("../package.json").engines.node; + if (!semver.satisfies(process.version, requiredVersion)) { + throw new IModelError(IModelStatus.BadRequest, `Node.js version ${process.version} is not within the range acceptable to imodeljs-backend: (${requiredVersion})`); + } + return; } /** @hidden */ @@ -96,17 +116,24 @@ export class IModelHost { */ public static startup(configuration: IModelHostConfiguration = new IModelHostConfiguration()) { if (IModelHost.configuration) - throw new IModelError(BentleyStatus.ERROR, "startup may only be called once"); + throw new IModelError(BentleyStatus.ERROR, "startup may only be called once", Logger.logError, loggingCategory, () => (configuration)); + + this.validateNodeJsVersion(); this.backendVersion = require("../package.json").version; initializeRpcBackend(); const region: number = Config.App.getNumber(UrlDiscoveryClient.configResolveUrlUsingRegion, 0); if (!this._isNativePlatformLoaded) { - if (configuration.nativePlatform !== undefined) - this.registerPlatform(configuration.nativePlatform, region); - else - this.loadNative(region); + try { + if (configuration.nativePlatform !== undefined) + this.registerPlatform(configuration.nativePlatform, region); + else + this.loadNative(region); + } catch (error) { + Logger.logError(loggingCategory, "Error registering/loading the native platform API", () => (configuration)); + throw error; + } } if (configuration.imodelClient) @@ -116,7 +143,6 @@ export class IModelHost { IModelTileRpcImpl.register(); IModelWriteRpcImpl.register(); StandaloneIModelRpcImpl.register(); - IModelUnitTestRpcImpl.register(); WipRpcImpl.register(); BisCore.registerSchema(); @@ -139,6 +165,18 @@ export class IModelHost { public static get appAssetsDir(): string | undefined { return (IModelHost.configuration === undefined) ? undefined : IModelHost.configuration.appAssetsDir; } + + /** The time, in milliseconds, for which [IModelTileRpcInterface.requestTileTreeProps]($common) should wait before returning a "pending" status. */ + public static get tileTreeRequestTimeout(): number { + return undefined !== IModelHost.configuration ? IModelHost.configuration.tileTreeRequestTimeout : IModelHostConfiguration.defaultTileRequestTimeout; + } + /** The time, in milliseconds, for which [IModelTileRpcInterface.requestTileContent]($common) should wait before returning a "pending" status. */ + public static get tileContentRequestTimeout(): number { + return undefined !== IModelHost.configuration ? IModelHost.configuration.tileContentRequestTimeout : IModelHostConfiguration.defaultTileRequestTimeout; + } + + /** If true, requests for tile content will execute on a separate thread pool in order to avoid blocking other, less expensive asynchronous requests such as ECSql queries. */ + public static get useTileContentThreadPool(): boolean { return undefined !== IModelHost.configuration && IModelHost.configuration.useTileContentThreadPool; } } /** Information about the platform on which the app is running. Also see [[KnownLocations]] and [[IModelJsFs]]. */ diff --git a/core/backend/src/IModelImporter.ts b/core/backend/src/IModelImporter.ts new file mode 100644 index 0000000..e87298a --- /dev/null +++ b/core/backend/src/IModelImporter.ts @@ -0,0 +1,243 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { DbResult, Id64, Id64Array, Id64String, IModelStatus, Logger } from "@bentley/bentleyjs-core"; +import { Code, CodeSpec, ElementProps, IModel, IModelError } from "@bentley/imodeljs-common"; +import { ECSqlStatement } from "./ECSqlStatement"; +import { DefinitionPartition, Drawing, Element, InformationPartitionElement, Sheet, Subject } from "./Element"; +import { IModelDb } from "./IModelDb"; +import { IModelJsNative } from "./IModelJsNative"; +import { IModelHost } from "./IModelHost"; +import { ElementRefersToElements, RelationshipProps } from "./Relationship"; + +const logCategory = "IModelImporter"; + +export class IModelImporter { + private _sourceDb: IModelDb; + private _targetDb: IModelDb; + private _importContext: IModelJsNative.ImportContext; + + protected _excludedCodeSpecNames = new Set(); + protected _excludedCodeSpecIds = new Set(); + protected _excludedElementIds = new Set(); + protected _excludedElementClassNames = new Set(); + + /** */ + public constructor(sourceDb: IModelDb, targetDb: IModelDb) { + this._sourceDb = sourceDb; + this._targetDb = targetDb; + this._importContext = new IModelHost.platform.ImportContext(this._sourceDb.nativeDb, this._targetDb.nativeDb); + this._importContext.addElementId(IModel.rootSubjectId, IModel.rootSubjectId); + } + /** */ + public dispose(): void { + this._importContext.dispose(); + } + /** */ + public addCodeSpecId(sourceId: Id64String, targetId: Id64String): void { + this._importContext.addCodeSpecId(sourceId, targetId); + } + /** */ + public findCodeSpecId(sourceId: Id64String): Id64String { + return this._importContext.findCodeSpecId(sourceId); + } + /** */ + public excludeCodeSpec(codeSpecName: string): void { + this._excludedCodeSpecNames.add(codeSpecName); + } + /** */ + public importCodeSpecs(): void { + const sql = `SELECT ECInstanceId AS id FROM BisCore:CodeSpec`; + this._sourceDb.withPreparedStatement(sql, (statement: ECSqlStatement) => { + while (DbResult.BE_SQLITE_ROW === statement.step()) { + const sourceCodeSpecId = statement.getRow().id; + const codeSpec: CodeSpec = this._sourceDb.codeSpecs.getById(sourceCodeSpecId); + if (this._excludedCodeSpecNames.has(codeSpec.name)) { + Logger.logInfo(logCategory, `Excluding CodeSpec: ${codeSpec.name}`); + this._excludedCodeSpecIds.add(codeSpec.id); + continue; + } + const targetCodeSpecId = this._importContext.importCodeSpec(sourceCodeSpecId); + if (!Id64.isValidId64(targetCodeSpecId)) { + throw new IModelError(IModelStatus.InvalidCodeSpec, `Error importing CodeSpec: ${codeSpec.name}`, Logger.logError, logCategory); + } + } + }); + } + /** */ + public importCodeSpec(sourceId: Id64String): Id64String { + return this._importContext.importCodeSpec(sourceId); + } + /** */ + public importFonts(): void { + for (const font of this._sourceDb.fontMap.fonts.values()) { + this._importContext.importFont(font.id); + } + } + /** */ + public excludeElementId(elementId: Id64String): void { + this._excludedElementIds.add(elementId); + } + /** */ + public excludeElementClass(classFullName: string): void { + this._excludedElementClassNames.add(classFullName); + } + /** */ + public addElementId(sourceId: Id64String, targetId: Id64String): void { + this._importContext.addElementId(sourceId, targetId); + } + /** */ + public findElementId(sourceId: Id64String): Id64String { + return this._importContext.findElementId(sourceId); + } + /** */ + public static resolveSubjectId(iModelDb: IModelDb, subjectPath: string): Id64String | undefined { + let subjectId: Id64String | undefined = IModel.rootSubjectId; + const subjectNames: string[] = subjectPath.split("/"); + for (const subjectName of subjectNames) { + if ("" === subjectName) { + continue; + } + const subjectCode: Code = Subject.createCode(iModelDb, subjectId!, subjectName); + subjectId = iModelDb.elements.queryElementIdByCode(subjectCode); + if (undefined === subjectId) { + break; + } + } + return subjectId; + } + /** */ + public excludeSubject(subjectPath: string): void { + const subjectId: Id64String | undefined = IModelImporter.resolveSubjectId(this._sourceDb, subjectPath); + if (subjectId && Id64.isValidId64(subjectId)) { + this._excludedElementIds.add(subjectId); + } + } + /** */ + public import(): void { + this.importCodeSpecs(); + this.importFonts(); + this.importElement(IModel.rootSubjectId); + this.importModels(DefinitionPartition.classFullName); + this.importModels(InformationPartitionElement.classFullName); + this.importModels(Drawing.classFullName); + this.importModels(Sheet.classFullName); + this.importRelationships(); + } + /** */ + public importElement(sourceElementId: Id64String): Id64String { + let targetElementId: Id64String | undefined = this._importContext.findElementId(sourceElementId); + if (!Id64.isValidId64(targetElementId)) { + if (this._excludedElementIds.has(sourceElementId)) { + Logger.logInfo(logCategory, `Excluding Element(${sourceElementId})`); + return Id64.invalid; // already excluded + } + const sourceElementProps = this._sourceDb.elements.getElementProps({ id: sourceElementId, wantGeometry: false }); + if (this._excludedElementClassNames.has(sourceElementProps.classFullName)) { // WIP: handle subclasses + Logger.logInfo(logCategory, `Excluding Element(${sourceElementId}) by Class(${sourceElementProps.classFullName})`); + this.excludeElementId(sourceElementId); + return Id64.invalid; // excluded by class + } + if (this._excludedCodeSpecIds.has(sourceElementProps.code.spec)) { + Logger.logInfo(logCategory, `Excluding Element(${sourceElementId}) by CodeSpec(${sourceElementProps.code.spec})`); + this.excludeElementId(sourceElementId); + return Id64.invalid; // excluded by CodeSpec + } + if (sourceElementProps.category) { + if (this._excludedElementIds.has(sourceElementProps.category)) { + Logger.logInfo(logCategory, `Excluding Element(${sourceElementId}) by Category(${sourceElementProps.category})`); + this.excludeElementId(sourceElementId); + return Id64.invalid; // excluded by Category + } + } + const targetElementProps: ElementProps = this._importContext.cloneElement(sourceElementId); + targetElementId = this._targetDb.elements.queryElementIdByCode(new Code(targetElementProps.code)); + if (targetElementId === undefined) { + try { + targetElementId = this._targetDb.elements.insertElement(targetElementProps); // insert from TypeScript so TypeScript handlers are called + this.addElementId(sourceElementId, targetElementId); + Logger.logInfo(logCategory, `Inserted ${targetElementProps.classFullName}-${targetElementProps.code.value}-${targetElementId}`); + } catch (error) { + Logger.logError(logCategory, "Error inserting Element into target iModel"); + } + } else { + try { + targetElementProps.id = targetElementId; + this._targetDb.elements.updateElement(targetElementProps); + this.addElementId(sourceElementId, targetElementId); + } catch (error) { + Logger.logError(logCategory, "Error updating Element within target iModel"); + } + } + } + this._importChildElements(sourceElementId); + return targetElementId!; + } + /** */ + private _importChildElements(elementId: Id64String): void { + const childElementIds: Id64Array = this._sourceDb.elements.queryChildren(elementId); + for (const childElementId of childElementIds) { + this.importElement(childElementId); + } + } + /** */ + public importModels(modeledElementClass: string): void { + const sql = `SELECT ECInstanceId AS id FROM ${modeledElementClass}`; + this._sourceDb.withPreparedStatement(sql, (statement: ECSqlStatement) => { + while (DbResult.BE_SQLITE_ROW === statement.step()) { + const modeledElementId = statement.getRow().id; + this.importModel(modeledElementId); + this.importModelContents(modeledElementId); + } + }); + } + /** */ + public importModel(sourceModeledElementId: Id64String): void { + const targetModeledElementId = this._importContext.findElementId(sourceModeledElementId); + try { + if (this._targetDb.models.getModelProps(targetModeledElementId)) { + return; // already imported + } + } catch (error) { + // catch NotFound error and insertModel + const modelProps = this._sourceDb.models.getModelProps(sourceModeledElementId); + modelProps.modeledElement.id = targetModeledElementId; + modelProps.id = targetModeledElementId; + modelProps.parentModel = this._importContext.findElementId(modelProps.parentModel!); + this._targetDb.models.insertModel(modelProps); + } + } + /** */ + public importModelContents(modelId: Id64String): void { + const sql = `SELECT ECInstanceId AS id FROM ${Element.classFullName} WHERE Parent.Id IS NULL AND Model.Id=:modelId`; + this._sourceDb.withPreparedStatement(sql, (statement: ECSqlStatement) => { + statement.bindId("modelId", modelId); + while (DbResult.BE_SQLITE_ROW === statement.step()) { + const row = statement.getRow(); + this.importElement(row.id); + } + }); + } + /** */ + public importRelationships(): void { + const sql = `SELECT ECInstanceId AS id FROM ${ElementRefersToElements.classFullName}`; + this._sourceDb.withPreparedStatement(sql, (statement: ECSqlStatement) => { + while (DbResult.BE_SQLITE_ROW === statement.step()) { + const row = statement.getRow(); + const relationshipProps = this._sourceDb.relationships.getInstanceProps(ElementRefersToElements.classFullName, row.id); + relationshipProps.sourceId = this._importContext.findElementId(relationshipProps.sourceId); + relationshipProps.targetId = this._importContext.findElementId(relationshipProps.targetId); + if (Id64.isValidId64(relationshipProps.sourceId) && Id64.isValidId64(relationshipProps.targetId)) { + try { + // check for an existing relationship + this._targetDb.relationships.getInstanceProps(relationshipProps.classFullName, { sourceId: relationshipProps.sourceId, targetId: relationshipProps.targetId }); + } catch (error) { + // catch NotFound error and insert relationship + this._targetDb.relationships.insertInstance(relationshipProps); + } + } + } + }); + } +} diff --git a/core/backend/src/IModelJsNative.ts b/core/backend/src/IModelJsNative.ts index 5be8151..45283e4 100644 --- a/core/backend/src/IModelJsNative.ts +++ b/core/backend/src/IModelJsNative.ts @@ -6,7 +6,7 @@ import { BentleyStatus, ChangeSetApplyOption, ChangeSetStatus, DbOpcode, DbResult, GuidString, Id64String, IDisposable, IModelStatus, Logger, OpenMode, RepositoryStatus, StatusCodeWithMessage, } from "@bentley/bentleyjs-core"; -import { ElementProps } from "@bentley/imodeljs-common"; +import { ElementProps, ChangedElements } from "@bentley/imodeljs-common"; import { IModelDb } from "./IModelDb"; // tslint:disable:prefer-get @@ -108,6 +108,7 @@ export declare namespace IModelJsNative { public getSchema(name: string): ErrorStatusOrResult; public getSchemaItem(schemaName: string, itemName: string): ErrorStatusOrResult; public getTileContent(treeId: string, tileId: string, callback: (result: ErrorStatusOrResult) => void): void; + public pollTileContent(treeId: string, tileId: string): ErrorStatusOrResult; public getTileTree(id: string, callback: (result: ErrorStatusOrResult) => void): void; public getTxnDescription(txnId: TxnIdString): string; public getUndoString(): string; @@ -170,6 +171,18 @@ export declare namespace IModelJsNative { public importSchema(schemaPathName: string): DbResult; } + export class ChangedElementsECDb implements IDisposable { + constructor(); + public dispose(): void; + public createDb(db: DgnDb, dbName: string): DbResult; + public openDb(dbName: string, mode: OpenMode, upgradeProfiles?: boolean): DbResult; + public isOpen(): boolean; + public closeDb(): void; + public processChangesets(db: DgnDb, changesets: string, rulesetId: string, filterSpatial: boolean): DbResult; + public getChangedElements(startChangesetId: string, endChangesetId: string): ErrorStatusOrResult; + public isProcessed(changesetId: string): boolean; + } + export class ECSqlStatement implements IDisposable { constructor(); public prepare(db: DgnDb | ECDb, ecsql: string): StatusCodeWithMessage; @@ -178,7 +191,9 @@ export declare namespace IModelJsNative { public getBinder(param: number | string): ECSqlBinder; public clearBindings(): DbResult; public step(): DbResult; + public stepAsync(callback: (result: DbResult) => void): void; public stepForInsert(): { status: DbResult, id: string }; + public stepForInsertAsync(callback: (result: { status: DbResult, id: string }) => void): void; public getValue(columnIndex: number): ECSqlValue; public getColumnCount(): number; } @@ -258,6 +273,7 @@ export declare namespace IModelJsNative { public bindGuid(param: number | string, guidStr: GuidString): DbResult; public clearBindings(): DbResult; public step(): DbResult; + public stepAsync(callback: (result: DbResult) => void): void; public getColumnCount(): number; public getColumnType(columnIndex: number): number; public getColumnName(columnIndex: number): string; @@ -326,4 +342,17 @@ export declare namespace IModelJsNative { constructor(); public dispose(): void; } + + /** @hidden */ + export class ImportContext implements IDisposable { + constructor(sourceDb: DgnDb, targetDb: DgnDb); + public dispose(): void; + public addCodeSpecId(sourceId: Id64String, targetId: Id64String): BentleyStatus; + public addElementId(sourceId: Id64String, targetId: Id64String): BentleyStatus; + public findCodeSpecId(sourceId: Id64String): Id64String; + public findElementId(sourceId: Id64String): Id64String; + public cloneElement(sourceId: Id64String): ElementProps; + public importCodeSpec(sourceId: Id64String): Id64String; + public importFont(sourceId: number): number; + } } diff --git a/core/backend/src/Material.ts b/core/backend/src/Material.ts new file mode 100644 index 0000000..2328c71 --- /dev/null +++ b/core/backend/src/Material.ts @@ -0,0 +1,135 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module Elements */ + +import { Id64String } from "@bentley/bentleyjs-core"; +import { + BisCodeSpec, + Code, + CodeSpec, + CodeScopeProps, + TextureMapProps, + RenderMaterialProps, +} from "@bentley/imodeljs-common"; +import { DefinitionElement } from "./Element"; +import { IModelDb } from "./IModelDb"; + +/** Defines a rendering material. */ +export class RenderMaterial extends DefinitionElement implements RenderMaterialProps { + public paletteName: string; + public description?: string; + /** @hidden */ + constructor(props: RenderMaterialProps, iModel: IModelDb) { + super(props, iModel); + this.paletteName = props.paletteName; + this.description = props.description; + } + /** @hidden */ + public toJSON(): RenderMaterialProps { + const val = super.toJSON() as RenderMaterialProps; + val.paletteName = this.paletteName; + val.description = this.description; + return val; + } + /** Create a Code for a RenderMaterial given a name that is meant to be unique within the scope of the specified DefinitionModel. + * @param iModel The IModelDb + * @param scopeModelId The Id of the DefinitionModel that contains the RenderMaterial and provides the scope for its name. + * @param name The RenderMaterial name + */ + public static createCode(iModel: IModelDb, scopeModelId: CodeScopeProps, name: string): Code { + const codeSpec: CodeSpec = iModel.codeSpecs.getByName(BisCodeSpec.texture); + return 0 === name.length ? Code.createEmpty() : new Code({ spec: codeSpec.id, scope: scopeModelId, value: name }); + } + /** + * Create a RenderMaterial with given parameters. + * @param iModelDb The iModel + * @param definitionModelId The [[DefinitionModel]] + * @param materialName The name/CodeValue of the RenderMaterial + * @param params Parameters object which describes how to construct the RenderMaterial + * @returns The newly constructed RenderMaterial element. + * @throws [[IModelError]] if unable to create the element. + */ + public static create(iModelDb: IModelDb, definitionModelId: Id64String, materialName: string, params: RenderMaterial.Params): RenderMaterial { + const map = undefined !== params.patternMap ? { Pattern: params.patternMap } : undefined; + const renderMaterialProps: RenderMaterialProps = { + classFullName: this.classFullName, + code: this.createCode(iModelDb, definitionModelId, materialName), + paletteName: params.paletteName, + description: params.description, + jsonProperties: { + materialAssets: { + renderMaterial: { + HasBaseColor: params.color !== undefined, + color: params.color, + HasSpecularColor: params.specularColor !== undefined, + specular_color: params.specularColor, + HasFinish: params.finish !== undefined, + finish: params.finish, + HasTransmit: params.transmit !== undefined, + transmit: params.transmit, + HasDiffuse: params.diffuse !== undefined, + diffuse: params.diffuse, + HasSpecular: params.specular !== undefined, + specular: params.specular, + HasReflect: params.reflect !== undefined, + reflect: params.reflect, + HasReflectColor: params.reflectColor !== undefined, + reflect_color: params.reflectColor, + Map: map, + }, + }, + }, + model: definitionModelId, + isPrivate: false, + }; + return new RenderMaterial(renderMaterialProps, iModelDb); + } + /** + * Insert a new RenderMaterial into a model. + * @param iModelDb Insert into this iModel + * @param definitionModelId Insert the new Texture into this DefinitionModel + * @param materialName The name/CodeValue of the RenderMaterial + * @param params Parameters object which describes how to construct the RenderMaterial + * @returns The Id of the newly inserted RenderMaterial element. + * @throws [[IModelError]] if unable to insert the element. + */ + public static insert(iModelDb: IModelDb, definitionModelId: Id64String, materialName: string, params: RenderMaterial.Params): Id64String { + const renderMaterial = this.create(iModelDb, definitionModelId, materialName, params); + return iModelDb.elements.insertElement(renderMaterial); + } +} + +export namespace RenderMaterial { + /** Parameters used to construct a [[RenderMaterial]]. */ + export class Params { + /** The palette name which categorizes this RenderMaterial */ + public paletteName: string; + /** The optional description for this RenderMaterial */ + public description?: string; + /** If defined, use this color for surface fill or diffuse illumination; if undefined, defaults to black */ + public color?: number[]; + /** If defined, use this color for surface specular illumination; if undefined, defaults to black */ + public specularColor?: number[]; + /** If defined, apply this specular exponent(surface shininess); range is 0 to 128; if undefined, defaults to 15.0 * 0.9 */ + public finish?: number; + /** If defined, apply this surface transparency; if undefined, defaults to 0.0 */ + public transmit?: number; + /** If defined, apply this surface diffuse reflectivity; if undefined, defaults to 0.6 */ + public diffuse?: number; + /** If defined, apply this surface specular reflectivity; if undefined, defaults to 0.0. NOTE: The actual JSON allows a HasSpecular property to be true and the specular value itself undefined, in which case the value 0.4 would be used. This API does not allow this case. */ + public specular?: number; + /** If defined, apply this surface environmental reflectivity; stored as fraction of specular in V8 material settings; if undefined, defaults to 0.0 */ + public reflect?: number; + /** If defined, apply this surface reflectance color; if undefined, defaults to whatever the specularColor is */ + public reflectColor?: number[]; + /** If defined, specifies the pattern mapping. */ + public patternMap?: TextureMapProps; + + /** Construct a new RenderMaterial.Params object with the specified paletteName. Alter the public members on that object to specify settings. */ + public constructor(paletteName: string) { + this.paletteName = paletteName; + } + } +} diff --git a/core/backend/src/Model.ts b/core/backend/src/Model.ts index b95fb04..0b4aa6d 100644 --- a/core/backend/src/Model.ts +++ b/core/backend/src/Model.ts @@ -6,7 +6,7 @@ import { Id64String, Id64, DbOpcode, JsonUtils, IModelStatus } from "@bentley/bentleyjs-core"; import { AxisAlignedBox3d, GeometricModel2dProps, IModel, IModelError, InformationPartitionElementProps, ModelProps, RelatedElement } from "@bentley/imodeljs-common"; -import { Point2d } from "@bentley/geometry-core"; +import { Point2d, Range3d } from "@bentley/geometry-core"; import { DefinitionPartition, DocumentPartition, PhysicalPartition } from "./Element"; import { Entity } from "./Entity"; import { IModelDb } from "./IModelDb"; @@ -92,7 +92,7 @@ export class GeometricModel extends Model { const { error, result } = this.iModel.nativeDb.queryModelExtents(JSON.stringify({ id: this.id.toString() })); if (error) throw new IModelError(error.status, "Error querying model extents"); - return AxisAlignedBox3d.fromJSON(JSON.parse(result!).modelExtents); + return Range3d.fromJSON(JSON.parse(result!).modelExtents); } } diff --git a/core/backend/src/PromiseMemoizer.ts b/core/backend/src/PromiseMemoizer.ts index fe8252b..cea2ef5 100644 --- a/core/backend/src/PromiseMemoizer.ts +++ b/core/backend/src/PromiseMemoizer.ts @@ -12,8 +12,8 @@ export class QueryablePromise { public get isPending(): boolean { return !this.isFulfilled && !this.isRejected; } public get isFulfilled(): boolean { return !!this.result; } public get isRejected(): boolean { return !!this.error; } - public constructor(private readonly _promise: Promise) { - this._promise + public constructor(public readonly promise: Promise) { + this.promise .then((res: T) => this.result = res) .catch((err: any) => this.error = err); } diff --git a/core/backend/src/SqliteStatement.ts b/core/backend/src/SqliteStatement.ts index b7e85f5..7aefb7c 100644 --- a/core/backend/src/SqliteStatement.ts +++ b/core/backend/src/SqliteStatement.ts @@ -8,6 +8,7 @@ import { Id64String, GuidString, DbResult, IDisposable, StatusCodeWithMessage } import { IModelError, ECJsNames } from "@bentley/imodeljs-common"; import { IModelJsNative } from "./IModelJsNative"; import { IModelHost } from "./IModelHost"; +import { Config } from "@bentley/imodeljs-clients"; /** Marks a string as either an [Id64String]($bentleyjs-core) or [GuidString]($bentleyjs-core), so * that it can be passed to the [bindValue]($backend.SqliteStatement) or [bindValues]($backend.SqliteStatement) @@ -196,6 +197,26 @@ export class SqliteStatement implements IterableIterator, IDisposable { */ public step(): DbResult { return this._stmt!.step(); } + /** Asynchronous version of Step method. + * + * For **SQL SELECT** statements the method returns + * - [DbResult.BE_SQLITE_ROW]($bentleyjs-core) if the statement now points successfully to the next row. + * - [DbResult.BE_SQLITE_DONE]($bentleyjs-core) if the statement has no more rows. + * - Error status in case of errors. + * + * For **SQL INSERT, UPDATE, DELETE** statements the method returns + * - [DbResult.BE_SQLITE_DONE]($bentleyjs-core) if the statement has been executed successfully. + * - Error status in case of errors. + */ + public async stepAsync(): Promise { + return new Promise((resolve, reject) => { + if (!this._stmt) + reject(); + else + this._stmt!.stepAsync(resolve); + }); + } + /** Get the query result's column count (only for SQL SELECT statements). */ public getColumnCount(): number { return this._stmt!.getColumnCount(); } @@ -393,7 +414,7 @@ export class SqliteStatementCache { private readonly _statements: Map = new Map(); public readonly maxCount: number; - public constructor(maxCount = 20) { this.maxCount = maxCount; } + public constructor(maxCount = Config.App.getNumber("imjs_sqlite_cache_size", 40)) { this.maxCount = maxCount; } public add(str: string, stmt: SqliteStatement): void { const existing = this._statements.get(str); @@ -431,6 +452,19 @@ export class SqliteStatementCache { } } } + public replace(str: string, stmt: SqliteStatement) { + if (stmt.isShared) { + throw new Error("expecting a unshared statement"); + } + const existingCS = this.find(str); + if (existingCS) { + existingCS.statement.setIsShared(false); + this._statements.delete(str); + } + const newCS = new CachedSqliteStatement(stmt); + newCS.statement.setIsShared(true); + this._statements.set(str, newCS); + } public removeUnusedStatementsIfNecessary(): void { if (this.getCount() <= this.maxCount) diff --git a/core/backend/src/imodeljs-backend.ts b/core/backend/src/imodeljs-backend.ts index 6dcdcea..81250dd 100644 --- a/core/backend/src/imodeljs-backend.ts +++ b/core/backend/src/imodeljs-backend.ts @@ -22,16 +22,19 @@ export * from "./IModelHost"; export * from "./Relationship"; export * from "./Texture"; export * from "./LineStyle"; +export * from "./Material"; export * from "./Model"; export * from "./NavigationRelationship"; export * from "./Schema"; export * from "./SqliteStatement"; export * from "./ViewDefinition"; export * from "./BisCore"; +export * from "./ChangedElementsDb"; export * from "./domains/Functional"; export * from "./domains/FunctionalElements"; export * from "./domains/Generic"; export * from "./domains/GenericElements"; +export * from "./IModelImporter"; export * from "./IModelJsNative"; export * from "./IModelDb"; // must be last diff --git a/core/backend/src/perftest/changesetPerformance.test.ts b/core/backend/src/perftest/changesetPerformance.test.ts index 1ace0a3..8df0bdf 100644 --- a/core/backend/src/perftest/changesetPerformance.test.ts +++ b/core/backend/src/perftest/changesetPerformance.test.ts @@ -2,42 +2,23 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { IModelDb, OpenParams, IModelJsFs, KeepBriefcase } from "../imodeljs-backend"; -import { Config, IModelHubClient, ImsActiveSecureTokenClient, AuthorizationToken, AccessToken, ChangeSet } from "@bentley/imodeljs-clients"; -import { ActivityLoggingContext, Guid } from "@bentley/bentleyjs-core"; -import { IModelVersion } from "@bentley/imodeljs-common"; +import { IModelDb, OpenParams, IModelJsFs, KeepBriefcase, ConcurrencyControl, DictionaryModel, SpatialCategory, BriefcaseManager } from "../imodeljs-backend"; +import { Config, IModelHubClient, ImsActiveSecureTokenClient, AuthorizationToken, AccessToken, ChangeSet, HubIModel, IModelQuery } from "@bentley/imodeljs-clients"; +import { ActivityLoggingContext, Guid, Id64String } from "@bentley/bentleyjs-core"; +import { IModelVersion, IModel, SubCategoryAppearance } from "@bentley/imodeljs-common"; import { KnownTestLocations } from "../test/KnownTestLocations"; import * as path from "path"; import * as fs from "fs"; import { assert } from "chai"; import { Element } from "../Element"; import { IModelHost } from "../IModelHost"; +import { IModelTestUtils, TestIModelInfo } from "../test/IModelTestUtils"; -async function getImodelAfterApplyingCS(csvPath: string) { +async function getImodelAfterApplyingCS(csvPath: string, projectId: string, imodelId: string, actLogCtx: ActivityLoggingContext, accessToken: AccessToken, client: IModelHubClient) { csvPath = csvPath; - const fs1 = require("fs"); - const configData = JSON.parse(fs1.readFileSync("src/perftest/CSPerfConfig.json")); - const uname = configData.username; - const pass = configData.password; - const projectId = configData.projectId; - const imodelId = configData.imodelId; - const myAppConfig = { - imjs_buddi_resolve_url_using_region: 102, - imjs_default_relying_party_uri: "https://connect-wsg20.bentley.com", - }; - Config.App.merge(myAppConfig); - const client: IModelHubClient = new IModelHubClient(); - IModelHost.loadNative(myAppConfig.imjs_buddi_resolve_url_using_region); - process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; - const actLogCtx = new ActivityLoggingContext(Guid.createValue()); - const imsClient: ImsActiveSecureTokenClient = new ImsActiveSecureTokenClient(); - const authToken: AuthorizationToken = await imsClient.getToken(actLogCtx, uname, pass); - const accessToken: AccessToken = await client.getAccessToken(actLogCtx, authToken); - const changeSets: ChangeSet[] = await client.changeSets.get(actLogCtx, accessToken, imodelId); const firstChangeSetId = changeSets[0].wsgId; const secondChangeSetId = changeSets[1].wsgId; - const thirdChangeSetId = changeSets[2].wsgId; // open imodel first time from imodel-hub with first revision const startTime = new Date().getTime(); @@ -58,39 +39,10 @@ async function getImodelAfterApplyingCS(csvPath: string) { assert.strictEqual(imodeldb1.briefcase.currentChangeSetId, secondChangeSetId); imodeldb1.close(actLogCtx, accessToken).catch(); fs.appendFileSync(csvPath, "Open, From Cache second cs," + elapsedTime1 + "\n"); - - // open imodel from local cache with third revision - const startTime2 = new Date().getTime(); - const imodeldb2: IModelDb = await IModelDb.open(actLogCtx, accessToken, projectId, imodelId, OpenParams.pullOnly(), IModelVersion.asOfChangeSet(thirdChangeSetId)); - const endTime2 = new Date().getTime(); - assert.exists(imodeldb2); - const elapsedTime2 = (endTime2 - startTime2) / 1000.0; - assert.strictEqual(imodeldb2.briefcase.currentChangeSetId, thirdChangeSetId); - imodeldb2.close(actLogCtx, accessToken).catch(); - fs.appendFileSync(csvPath, "Open, From Cache third cs," + elapsedTime2 + "\n"); } -async function pushImodelAfterMetaChanges(csvPath: string) { +async function pushImodelAfterMetaChanges(csvPath: string, projectId: string, imodelPushId: string, actLogCtx: ActivityLoggingContext, accessToken: AccessToken) { csvPath = csvPath; - const fs1 = require("fs"); - const configData = JSON.parse(fs1.readFileSync("src/perftest/CSPerfConfig.json")); - const uname = configData.username; - const pass = configData.password; - const projectId = configData.projectId; - const imodelPushId = configData.imodelPushId; - const myAppConfig = { - imjs_buddi_resolve_url_using_region: 102, - imjs_default_relying_party_uri: "https://connect-wsg20.bentley.com", - }; - Config.App.merge(myAppConfig); - const client: IModelHubClient = new IModelHubClient(); - IModelHost.loadNative(myAppConfig.imjs_buddi_resolve_url_using_region); - process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; - const actLogCtx = new ActivityLoggingContext(Guid.createValue()); - const imsClient: ImsActiveSecureTokenClient = new ImsActiveSecureTokenClient(); - const authToken: AuthorizationToken = await imsClient.getToken(actLogCtx, uname, pass); - const accessToken: AccessToken = await client.getAccessToken(actLogCtx, authToken); - const iModelPullAndPush: IModelDb = await IModelDb.open(actLogCtx, accessToken, projectId, imodelPushId, OpenParams.pullAndPush(), IModelVersion.latest()); assert.exists(iModelPullAndPush); @@ -99,7 +51,7 @@ async function pushImodelAfterMetaChanges(csvPath: string) { const rootEl: Element = iModelPullAndPush.elements.getRootSubject(); rootEl.userLabel = rootEl.userLabel + "changed"; iModelPullAndPush.elements.updateElement(rootEl); - iModelPullAndPush.saveChanges(); + iModelPullAndPush.saveChanges("user changes root subject of the imodel"); const endTime = new Date().getTime(); const elapsedTime = (endTime - startTime) / 1000.0; fs.appendFileSync(csvPath, "Update, Make Meta Changes," + elapsedTime + "\n"); @@ -115,6 +67,200 @@ async function pushImodelAfterMetaChanges(csvPath: string) { await iModelPullAndPush.close(actLogCtx, accessToken, KeepBriefcase.No); } +export async function createNewModelAndCategory(rwIModel: IModelDb, accessToken: AccessToken, actx: ActivityLoggingContext) { + // Create a new physical model. + let modelId: Id64String; + [, modelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(rwIModel, IModelTestUtils.getUniqueModelCode(rwIModel, "newPhysicalModel"), true); + + // Find or create a SpatialCategory. + const dictionary: DictionaryModel = rwIModel.models.getModel(IModel.dictionaryId) as DictionaryModel; + const newCategoryCode = IModelTestUtils.getUniqueSpatialCategoryCode(dictionary, "ThisTestSpatialCategory"); + const spatialCategoryId: Id64String = SpatialCategory.insert(rwIModel, IModel.dictionaryId, newCategoryCode.value!, new SubCategoryAppearance({ color: 0xff0000 })); + + // Reserve all of the codes that are required by the new model and category. + try { + await rwIModel.concurrencyControl.request(actx, accessToken); + } catch (err) { + if (err instanceof ConcurrencyControl.RequestError) { + assert.fail(JSON.stringify(err.unavailableCodes) + ", " + JSON.stringify(err.unavailableLocks)); + } + } + + return { modelId, spatialCategoryId }; +} + +async function pushImodelAfterDataChanges(csvPath: string, projectId: string, actLogCtx: ActivityLoggingContext, accessToken: AccessToken) { + csvPath = csvPath; + const iModelName = "CodesPushTest"; + // delete any existing imodel with given name + const iModels: HubIModel[] = await BriefcaseManager.imodelClient.iModels.get(actLogCtx, accessToken, projectId, new IModelQuery().byName(iModelName)); + for (const iModelTemp of iModels) { + await BriefcaseManager.imodelClient.iModels.delete(actLogCtx, accessToken, projectId, iModelTemp.id!); + } + // create new imodel with given name + const rwIModel: IModelDb = await IModelDb.create(actLogCtx, accessToken, projectId, iModelName, { rootSubject: { name: "TestSubject" } }); + const rwIModelId = rwIModel.iModelToken.iModelId; + assert.isNotEmpty(rwIModelId); + + // create new model, category and physical element, and insert in imodel + rwIModel.concurrencyControl.setPolicy(new ConcurrencyControl.OptimisticPolicy()); + const r: { modelId: Id64String, spatialCategoryId: Id64String } = await createNewModelAndCategory(rwIModel, accessToken, actLogCtx); + rwIModel.elements.insertElement(IModelTestUtils.createPhysicalObject(rwIModel, r.modelId, r.spatialCategoryId)); + rwIModel.saveChanges("User created model, category and one physical element"); + + // get the time to push a data change of an imodel to imodel hub + const startTime1 = new Date().getTime(); + await rwIModel.pushChanges(actLogCtx, accessToken).catch(); + const endTime1 = new Date().getTime(); + const elapsedTime1 = (endTime1 - startTime1) / 1000.0; + fs.appendFileSync(csvPath, "Push, Data Changes to Hub," + elapsedTime1 + "\n"); + await rwIModel.close(actLogCtx, accessToken, KeepBriefcase.No); +} + +async function pushImodelAfterSchemaChanges(csvPath: string, projectId: string, actLogCtx: ActivityLoggingContext, accessToken: AccessToken) { + csvPath = csvPath; + const iModelName = "SchemaPushTest"; + // delete any existing imodel with given name + const iModels: HubIModel[] = await BriefcaseManager.imodelClient.iModels.get(actLogCtx, accessToken, projectId, new IModelQuery().byName(iModelName)); + for (const iModelTemp of iModels) { + await BriefcaseManager.imodelClient.iModels.delete(actLogCtx, accessToken, projectId, iModelTemp.id!); + } + // create new imodel with given name + const rwIModel: IModelDb = await IModelDb.create(actLogCtx, accessToken, projectId, iModelName, { rootSubject: { name: "TestSubject" } }); + const rwIModelId = rwIModel.iModelToken.iModelId; + assert.isNotEmpty(rwIModelId); + // import schema and push change to hub + const schemaPathname = path.join(KnownTestLocations.assetsDir, "PerfTestDomain.ecschema.xml"); + rwIModel.concurrencyControl.setPolicy(new ConcurrencyControl.OptimisticPolicy()); + await rwIModel.importSchema(actLogCtx, schemaPathname, accessToken).catch(); + assert.isDefined(rwIModel.getMetaData("PerfTestDomain:" + "PerfElement"), "PerfElement" + "is present in iModel."); + await rwIModel.concurrencyControl.request(actLogCtx, accessToken); + rwIModel.saveChanges("schema change pushed"); + await rwIModel.pullAndMergeChanges(actLogCtx, accessToken); + const startTime1 = new Date().getTime(); + await rwIModel.pushChanges(actLogCtx, accessToken); + const endTime1 = new Date().getTime(); + const elapsedTime1 = (endTime1 - startTime1) / 1000.0; + fs.appendFileSync(csvPath, "Push, Schema Changes to Hub," + elapsedTime1 + "\n"); + await rwIModel.close(actLogCtx, accessToken, KeepBriefcase.No); +} + +const getElementCount = (iModel: IModelDb): number => { + const rows: any[] = iModel.executeQuery("SELECT COUNT(*) AS cnt FROM bis.Element"); + const count = +(rows[0].cnt); + return count; +}; + +async function executeQueryTime(csvPath: string, projectId: string, imodelId: string, actLogCtx: ActivityLoggingContext, accessToken: AccessToken) { + csvPath = csvPath; + const imodeldb: IModelDb = await IModelDb.open(actLogCtx, accessToken, projectId, imodelId, OpenParams.pullOnly(), IModelVersion.latest()); + assert.exists(imodeldb); + const startTime = new Date().getTime(); + const stat = imodeldb.executeQuery("SELECT * FROM BisCore.LineStyle"); + const endTime = new Date().getTime(); + const elapsedTime1 = (endTime - startTime) / 1000.0; + assert.equal(7, stat.length); + fs.appendFileSync(csvPath, "ExecuteQuery, Execute a simple ECSQL query," + elapsedTime1 + "\n"); + imodeldb.close(actLogCtx, accessToken).catch(); +} + +async function reverseChanges(csvPath: string, projectId: string, actLogCtx: ActivityLoggingContext, accessToken: AccessToken) { + csvPath = csvPath; + const iModelName = "reverseChangeTest"; + // delete any existing imodel with given name + const iModels: HubIModel[] = await BriefcaseManager.imodelClient.iModels.get(actLogCtx, accessToken, projectId, new IModelQuery().byName(iModelName)); + for (const iModelTemp of iModels) { + await BriefcaseManager.imodelClient.iModels.delete(actLogCtx, accessToken, projectId, iModelTemp.id!); + } + // create new imodel with given name + const rwIModel: IModelDb = await IModelDb.create(actLogCtx, accessToken, projectId, iModelName, { rootSubject: { name: "TestSubject" } }); + const rwIModelId = rwIModel.iModelToken.iModelId; + assert.isNotEmpty(rwIModelId); + + // create new model, category and physical element, and insert in imodel, and push these changes + rwIModel.concurrencyControl.setPolicy(new ConcurrencyControl.OptimisticPolicy()); + const r: { modelId: Id64String, spatialCategoryId: Id64String } = await createNewModelAndCategory(rwIModel, accessToken, actLogCtx); + rwIModel.elements.insertElement(IModelTestUtils.createPhysicalObject(rwIModel, r.modelId, r.spatialCategoryId)); + rwIModel.saveChanges("User created model, category and one physical element"); + await rwIModel.pushChanges(actLogCtx, accessToken).catch(); + const firstCount = getElementCount(rwIModel); + assert.equal(firstCount, 7); + + let i = 0; + while (i < 4) { + rwIModel.elements.insertElement(IModelTestUtils.createPhysicalObject(rwIModel, r.modelId, r.spatialCategoryId)); + i = i + 1; + } + rwIModel.saveChanges("added more elements to imodel"); + await rwIModel.pushChanges(actLogCtx, accessToken).catch(); + const secondCount = getElementCount(rwIModel); + assert.equal(secondCount, 11); + + let imodelInfo: TestIModelInfo; + imodelInfo = await IModelTestUtils.getTestModelInfo(accessToken, projectId, "reverseChangeTest"); + const firstChangeSetId = imodelInfo.changeSets[0].wsgId; + const startTime = new Date().getTime(); + await rwIModel.reverseChanges(actLogCtx, accessToken, IModelVersion.asOfChangeSet(firstChangeSetId)); + const endTime = new Date().getTime(); + const elapsedTime1 = (endTime - startTime) / 1000.0; + + const reverseCount = getElementCount(rwIModel); + assert.equal(reverseCount, firstCount); + + fs.appendFileSync(csvPath, "ReverseChanges, Reverse the imodel to first CS from latest," + elapsedTime1 + "\n"); + await rwIModel.close(actLogCtx, accessToken, KeepBriefcase.No); +} + +async function reinstateChanges(csvPath: string, projectId: string, actLogCtx: ActivityLoggingContext, accessToken: AccessToken) { + csvPath = csvPath; + const iModelName = "reinstateChangeTest"; + // delete any existing imodel with given name + const iModels: HubIModel[] = await BriefcaseManager.imodelClient.iModels.get(actLogCtx, accessToken, projectId, new IModelQuery().byName(iModelName)); + for (const iModelTemp of iModels) { + await BriefcaseManager.imodelClient.iModels.delete(actLogCtx, accessToken, projectId, iModelTemp.id!); + } + // create new imodel with given name + const rwIModel: IModelDb = await IModelDb.create(actLogCtx, accessToken, projectId, iModelName, { rootSubject: { name: "TestSubject" } }); + const rwIModelId = rwIModel.iModelToken.iModelId; + assert.isNotEmpty(rwIModelId); + + // create new model, category and physical element, and insert in imodel, and push these changes + rwIModel.concurrencyControl.setPolicy(new ConcurrencyControl.OptimisticPolicy()); + const r: { modelId: Id64String, spatialCategoryId: Id64String } = await createNewModelAndCategory(rwIModel, accessToken, actLogCtx); + rwIModel.elements.insertElement(IModelTestUtils.createPhysicalObject(rwIModel, r.modelId, r.spatialCategoryId)); + rwIModel.saveChanges("User created model, category and one physical element"); + await rwIModel.pushChanges(actLogCtx, accessToken).catch(); + const firstCount = getElementCount(rwIModel); + assert.equal(firstCount, 7); + + let i = 0; + while (i < 4) { + rwIModel.elements.insertElement(IModelTestUtils.createPhysicalObject(rwIModel, r.modelId, r.spatialCategoryId)); + i = i + 1; + } + rwIModel.saveChanges("added more elements to imodel"); + await rwIModel.pushChanges(actLogCtx, accessToken).catch(); + const secondCount = getElementCount(rwIModel); + assert.equal(secondCount, 11); + + let imodelInfo: TestIModelInfo; + imodelInfo = await IModelTestUtils.getTestModelInfo(accessToken, projectId, iModelName); + const firstChangeSetId = imodelInfo.changeSets[0].wsgId; + await rwIModel.reverseChanges(actLogCtx, accessToken, IModelVersion.asOfChangeSet(firstChangeSetId)); + const reverseCount = getElementCount(rwIModel); + assert.equal(reverseCount, firstCount); + + const startTime = new Date().getTime(); + await rwIModel.reinstateChanges(actLogCtx, accessToken, IModelVersion.latest()); + const endTime = new Date().getTime(); + const elapsedTime1 = (endTime - startTime) / 1000.0; + const reinstateCount = getElementCount(rwIModel); + assert.equal(reinstateCount, secondCount); + + fs.appendFileSync(csvPath, "ReinstateChanges, Reinstate the imodel to latest CS from first," + elapsedTime1 + "\n"); + await rwIModel.close(actLogCtx, accessToken, KeepBriefcase.No); +} + describe("ImodelChangesetPerformance", async () => { if (!IModelJsFs.existsSync(KnownTestLocations.outputDir)) IModelJsFs.mkdirSync(KnownTestLocations.outputDir); @@ -122,16 +268,62 @@ describe("ImodelChangesetPerformance", async () => { if (!IModelJsFs.existsSync(csvPath)) { fs.appendFileSync(csvPath, "Operation,Description,ExecutionTime\n"); } + let projectId: string; + let imodelId: string; + let imodelPushId: string; + let actLogCtx: ActivityLoggingContext; + let accessToken: AccessToken; + let client: IModelHubClient; before(async () => { + const fs1 = require("fs"); + const configData = JSON.parse(fs1.readFileSync("src/perftest/CSPerfConfig.json")); + const uname = configData.username; + const pass = configData.password; + projectId = configData.projectId; + imodelId = configData.imodelId; + imodelPushId = configData.imodelPushId; + const myAppConfig = { + imjs_buddi_resolve_url_using_region: 102, + imjs_default_relying_party_uri: "https://connect-wsg20.bentley.com", + }; + Config.App.merge(myAppConfig); + client = new IModelHubClient(); + IModelHost.loadNative(myAppConfig.imjs_buddi_resolve_url_using_region); + process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; + actLogCtx = new ActivityLoggingContext(Guid.createValue()); + const imsClient: ImsActiveSecureTokenClient = new ImsActiveSecureTokenClient(); + const authToken: AuthorizationToken = await imsClient.getToken(actLogCtx, uname, pass); + accessToken = await client.getAccessToken(actLogCtx, authToken); }); it("GetImodelFromHubAFterCSApplied", async () => { - await getImodelAfterApplyingCS(csvPath); + await getImodelAfterApplyingCS(csvPath, projectId, imodelId, actLogCtx, accessToken, client); }); it("PushImodelMetaChangeToImodelHUb", async () => { - pushImodelAfterMetaChanges(csvPath).catch(); + pushImodelAfterMetaChanges(csvPath, projectId, imodelPushId, actLogCtx, accessToken).catch(); + }); + + it("PushImodelDataChangeToImodelHUb", async () => { + pushImodelAfterDataChanges(csvPath, projectId, actLogCtx, accessToken).catch(); }); + + it("pushImodelAfterSchemaChanges", async () => { + pushImodelAfterSchemaChanges(csvPath, projectId, actLogCtx, accessToken).catch(); + }); + + it("executeQuery", async () => { + executeQueryTime(csvPath, projectId, imodelId, actLogCtx, accessToken).catch(); + }); + + it("reverseChanges", async () => { + reverseChanges(csvPath, projectId, actLogCtx, accessToken).catch(); + }); + + it("reinstateChanges", async () => { + reinstateChanges(csvPath, projectId, actLogCtx, accessToken).catch(); + }); + }); diff --git a/core/backend/src/rpc-impl/IModelReadRpcImpl.ts b/core/backend/src/rpc-impl/IModelReadRpcImpl.ts index 474b323..7483c50 100644 --- a/core/backend/src/rpc-impl/IModelReadRpcImpl.ts +++ b/core/backend/src/rpc-impl/IModelReadRpcImpl.ts @@ -4,17 +4,19 @@ *--------------------------------------------------------------------------------------------*/ /** @module RpcInterface */ -import { Logger, Id64String, Id64Set, Id64, assert, ActivityLoggingContext } from "@bentley/bentleyjs-core"; +import { ActivityLoggingContext, assert, Id64, Id64Set, Id64String, Logger, OpenMode, IModelStatus } from "@bentley/bentleyjs-core"; +import { Range3dProps, Range3d } from "@bentley/geometry-core"; import { AccessToken } from "@bentley/imodeljs-clients"; import { - EntityQueryParams, RpcInterface, RpcManager, IModel, IModelReadRpcInterface, IModelToken, - ModelProps, ElementProps, SnapRequestProps, SnapResponseProps, EntityMetaData, ViewStateProps, ImageSourceFormat, - IModelCoordinatesResponseProps, GeoCoordinatesResponseProps, + ElementProps, EntityMetaData, EntityQueryParams, GeoCoordinatesResponseProps, ImageSourceFormat, IModel, + IModelCoordinatesResponseProps, IModelReadRpcInterface, IModelToken, ModelProps, PageOptions, RpcInterface, RpcManager, + SnapRequestProps, SnapResponseProps, ViewStateProps, } from "@bentley/imodeljs-common"; -import { IModelDb, OpenParams } from "../IModelDb"; -import { OpenIModelDbMemoizer } from "./OpenIModelDbMemoizer"; +import { KeepBriefcase } from "../BriefcaseManager"; import { SpatialCategory } from "../Category"; +import { IModelDb, OpenParams } from "../IModelDb"; import { DictionaryModel } from "../Model"; +import { OpenIModelDbMemoizer } from "./OpenIModelDbMemoizer"; const loggingCategory = "imodeljs-backend.IModelReadRpcImpl"; @@ -32,18 +34,49 @@ export class IModelReadRpcImpl extends RpcInterface implements IModelReadRpcInte public async close(accessToken: AccessToken, iModelToken: IModelToken): Promise { const activityContext = ActivityLoggingContext.current; activityContext.enter(); - await IModelDb.find(iModelToken).close(activityContext, AccessToken.fromJson(accessToken)!); + await IModelDb.find(iModelToken).close(activityContext, AccessToken.fromJson(accessToken)!, iModelToken.openMode === OpenMode.Readonly ? KeepBriefcase.Yes : KeepBriefcase.No); return Promise.resolve(true); } - public async executeQuery(iModelToken: IModelToken, sql: string, bindings?: any[] | object): Promise { + public async queryPage(iModelToken: IModelToken, ecsql: string, bindings?: any[] | object, options?: PageOptions): Promise { const activityContext = ActivityLoggingContext.current; activityContext.enter(); const iModelDb: IModelDb = IModelDb.find(iModelToken); - const rows: any[] = iModelDb.executeQuery(sql, bindings); - Logger.logTrace(loggingCategory, "IModelDbRemoting.executeQuery", () => ({ sql, numRows: rows.length })); + const rows = iModelDb.queryPage(ecsql, bindings, options); + Logger.logTrace(loggingCategory, "IModelDbRemoting.getRows", () => ({ ecsql })); return rows; } + public async queryRowCount(iModelToken: IModelToken, ecsql: string, bindings?: any[] | object): Promise { + const activityContext = ActivityLoggingContext.current; activityContext.enter(); + const iModelDb: IModelDb = IModelDb.find(iModelToken); + const rowCount: number = await iModelDb.queryRowCount(ecsql, bindings); + Logger.logTrace(loggingCategory, "IModelDbRemoting.getRowCount", () => ({ ecsql, count: rowCount })); + return rowCount; + } + + public async queryModelRanges(iModelToken: IModelToken, modelIds: Id64Set): Promise { + const activityContext = ActivityLoggingContext.current; activityContext.enter(); + const iModelDb: IModelDb = IModelDb.find(iModelToken); + const ranges: Range3dProps[] = []; + for (const id of modelIds) { + const val = iModelDb.nativeDb.queryModelExtents(JSON.stringify({ id: id.toString() })); + if (val.error) { + if (val.error.status === IModelStatus.NoGeometry) { // if there was no geometry, just return null range + ranges.push(new Range3d()); + continue; + } + + if (modelIds.size === 1) + throw val.error; // if they're asking for more than one model, don't throw on error. + } + const range = JSON.parse(val.result!); + if (range.modelExtents) { + ranges.push(range.modelExtents); + } + } + return ranges; + } + public async getModelProps(iModelToken: IModelToken, modelIds: Id64Set): Promise { const activityContext = ActivityLoggingContext.current; activityContext.enter(); const iModelDb: IModelDb = IModelDb.find(iModelToken); @@ -98,17 +131,6 @@ export class IModelReadRpcImpl extends RpcInterface implements IModelReadRpcInte return res; } - public async formatElements(iModelToken: IModelToken, elementIds: Id64Set): Promise { - const activityContext = ActivityLoggingContext.current; activityContext.enter(); - const iModelDb: IModelDb = IModelDb.find(iModelToken); - const formatArray: any[] = []; - for (const elementId of elementIds) { - const formatString: string = iModelDb.getElementPropertiesForDisplay(elementId); - formatArray.push(JSON.parse(formatString)); - } - return formatArray; - } - public async getClassHierarchy(iModelToken: IModelToken, classFullName: string): Promise { const activityContext = ActivityLoggingContext.current; activityContext.enter(); const iModelDb: IModelDb = IModelDb.find(iModelToken); @@ -146,19 +168,14 @@ export class IModelReadRpcImpl extends RpcInterface implements IModelReadRpcInte return IModelDb.find(iModelToken).readFontJson(); } - public async requestSnap(iModelToken: IModelToken, connectionId: string, props: SnapRequestProps): Promise { - const activityContext = ActivityLoggingContext.current; activityContext.enter(); - return IModelDb.find(iModelToken).requestSnap(activityContext, connectionId, props); - } - - public async cancelSnap(iModelToken: IModelToken, connectionId: string): Promise { + public async requestSnap(iModelToken: IModelToken, sessionId: string, props: SnapRequestProps): Promise { const activityContext = ActivityLoggingContext.current; activityContext.enter(); - return IModelDb.find(iModelToken).cancelSnap(connectionId); + return IModelDb.find(iModelToken).requestSnap(activityContext, sessionId, props); } - public async loadNativeAsset(_iModelToken: IModelToken, assetName: string): Promise { + public async cancelSnap(iModelToken: IModelToken, sessionId: string): Promise { const activityContext = ActivityLoggingContext.current; activityContext.enter(); - return IModelDb.loadNativeAsset(assetName); + return IModelDb.find(iModelToken).cancelSnap(sessionId); } public async getToolTipMessage(iModelToken: IModelToken, id: string): Promise { diff --git a/core/backend/src/rpc-impl/IModelTileRpcImpl.ts b/core/backend/src/rpc-impl/IModelTileRpcImpl.ts index 2bf4146..0d530da 100644 --- a/core/backend/src/rpc-impl/IModelTileRpcImpl.ts +++ b/core/backend/src/rpc-impl/IModelTileRpcImpl.ts @@ -4,15 +4,156 @@ *--------------------------------------------------------------------------------------------*/ /** @module RpcInterface */ -import { IModelDb } from "../IModelDb"; import { IModelTileRpcInterface, IModelToken, - TileTreeProps, RpcInterface, RpcManager, + RpcPendingResponse, + TileTreeProps, } from "@bentley/imodeljs-common"; -import { ActivityLoggingContext } from "@bentley/bentleyjs-core"; +import { + assert, + ActivityLoggingContext, + BeDuration, + Logger, +} from "@bentley/bentleyjs-core"; +import { IModelDb } from "../IModelDb"; +import { IModelHost } from "../IModelHost"; +import { PromiseMemoizer, QueryablePromise } from "../PromiseMemoizer"; + +interface TileRequestProps { + actx: ActivityLoggingContext; + iModelToken: IModelToken; + treeId: string; +} + +function generateTileRequestKey(props: TileRequestProps): string { + return `${JSON.stringify(props.iModelToken)}:${props.treeId}`; +} + +abstract class TileRequestMemoizer extends PromiseMemoizer { + private readonly _loggingCategory = "imodeljs-backend.IModelTileRequestRpc"; + protected abstract get _operationName(): string; + protected abstract addMetadata(metadata: any, props: Props): void; + protected abstract stringify(props: Props): string; + protected abstract get _timeoutMilliseconds(): number; + + private makeMetadata(props: Props): any { + const meta = { ...props.iModelToken }; + this.addMetadata(meta, props); + return meta; + } + + protected constructor(memoizeFn: (props: Props) => Promise, generateKeyFn: (props: Props) => string) { + super(memoizeFn, generateKeyFn); + } + + private _superMemoize = this.memoize; + public memoize = (props: Props): QueryablePromise => { + return this._superMemoize(props); + } + + private _superDeleteMemoized = this.deleteMemoized; + public deleteMemoized = (props: Props) => { + this._superDeleteMemoized(props); + } + + private log(status: string, props: Props): void { + const descr = this._operationName + "(" + this.stringify(props) + ")"; + Logger.logTrace(this._loggingCategory, "Backend " + status + " " + descr, () => this.makeMetadata(props)); + } + + protected async perform(props: Props): Promise { + props.actx.enter(); + this.log("received", props); + + const tileQP = this.memoize(props); + const waitPromise = BeDuration.wait(this._timeoutMilliseconds); + await Promise.race([tileQP.promise, waitPromise]); + + props.actx.enter(); + + if (tileQP.isPending) { + this.log("issuing pending status for", props); + throw new RpcPendingResponse(); + } + + this.deleteMemoized(props); + + if (tileQP.isFulfilled) { + this.log("completed", props); + return tileQP.result!; + } + + assert(tileQP.isRejected); + this.log("rejected", props); + throw tileQP.error!; + } +} + +async function getTileTreeProps(props: TileRequestProps): Promise { + const db = IModelDb.find(props.iModelToken); + return db.tiles.requestTileTreeProps(props.actx, props.treeId); +} + +class RequestTileTreePropsMemoizer extends TileRequestMemoizer { + protected get _timeoutMilliseconds() { return IModelHost.tileTreeRequestTimeout; } + protected get _operationName() { return "requestTileTreeProps"; } + protected stringify(props: TileRequestProps): string { return props.treeId; } + protected addMetadata(meta: any, props: TileRequestProps): void { + meta.treeId = props.treeId; + } + + private static _instance?: RequestTileTreePropsMemoizer; + + private constructor() { + super(getTileTreeProps, generateTileRequestKey); + } + + public static async perform(props: TileRequestProps): Promise { + if (undefined === this._instance) + this._instance = new RequestTileTreePropsMemoizer(); + + return this._instance.perform(props); + } +} + +interface TileContentRequestProps extends TileRequestProps { + contentId: string; +} + +async function getTileContent(props: TileContentRequestProps): Promise { + const db = IModelDb.find(props.iModelToken); + return db.tiles.requestTileContent(props.actx, props.treeId, props.contentId); +} + +function generateTileContentKey(props: TileContentRequestProps): string { + return generateTileRequestKey(props) + `:${props.contentId}`; +} + +class RequestTileContentMemoizer extends TileRequestMemoizer { + protected get _timeoutMilliseconds() { return IModelHost.tileContentRequestTimeout; } + protected get _operationName() { return "requestTileContent"; } + protected stringify(props: TileContentRequestProps): string { return props.treeId + ":" + props.contentId; } + protected addMetadata(meta: any, props: TileContentRequestProps): void { + meta.treeId = props.treeId; + meta.contentId = props.contentId; + } + + private static _instance?: RequestTileContentMemoizer; + + private constructor() { + super(getTileContent, generateTileContentKey); + } + + public static async perform(props: TileContentRequestProps): Promise { + if (undefined === this._instance) + this._instance = new RequestTileContentMemoizer(); + + return this._instance.perform(props); + } +} /** @hidden */ export class IModelTileRpcImpl extends RpcInterface implements IModelTileRpcInterface { @@ -29,4 +170,14 @@ export class IModelTileRpcImpl extends RpcInterface implements IModelTileRpcInte const db = IModelDb.find(iModelToken); return db.tiles.requestTileContent(actx, treeId, contentId); } + + public async requestTileTreeProps(iModelToken: IModelToken, treeId: string): Promise { + const actx = ActivityLoggingContext.current; actx.enter(); + return RequestTileTreePropsMemoizer.perform({ actx, iModelToken, treeId }); + } + + public async requestTileContent(iModelToken: IModelToken, treeId: string, contentId: string): Promise { + const actx = ActivityLoggingContext.current; actx.enter(); + return RequestTileContentMemoizer.perform({ actx, iModelToken, treeId, contentId }); + } } diff --git a/core/backend/src/rpc-impl/OpenIModelDbMemoizer.ts b/core/backend/src/rpc-impl/OpenIModelDbMemoizer.ts index d4689ae..4e364a5 100644 --- a/core/backend/src/rpc-impl/OpenIModelDbMemoizer.ts +++ b/core/backend/src/rpc-impl/OpenIModelDbMemoizer.ts @@ -47,25 +47,25 @@ export class OpenIModelDbMemoizer extends PromiseMemoizer { OpenIModelDbMemoizer._openIModelDbMemoizer = new OpenIModelDbMemoizer(); const { memoize: memoizeOpenIModelDb, deleteMemoized: deleteMemoizedOpenIModelDb } = OpenIModelDbMemoizer._openIModelDbMemoizer; - const openPromise = memoizeOpenIModelDb(actx, accessTokenObj!, iModelToken.contextId!, iModelToken.iModelId!, openParams, iModelVersion); + const openQP = memoizeOpenIModelDb(actx, accessTokenObj!, iModelToken.contextId!, iModelToken.iModelId!, openParams, iModelVersion); const waitPromise = BeDuration.wait(100); // Wait a little before issuing a pending response - this avoids a potentially expensive round trip for the case a briefcase was already downloaded. - await Promise.race([openPromise, waitPromise]); // This resolves as soon as either the open is completed or the wait time has expired. Prevents waiting un-necessarily if the open has already completed. + await Promise.race([openQP.promise, waitPromise]); // This resolves as soon as either the open is completed or the wait time has expired. Prevents waiting un-necessarily if the open has already completed. - if (openPromise.isPending) { + if (openQP.isPending) { Logger.logTrace(loggingCategory, "Issuing pending status in OpenIModelDbMemoizer.openIModelDb", () => (iModelToken)); throw new RpcPendingResponse(); } deleteMemoizedOpenIModelDb(actx, accessTokenObj!, iModelToken.contextId!, iModelToken.iModelId!, openParams, iModelVersion); - if (openPromise.isFulfilled) { + if (openQP.isFulfilled) { Logger.logTrace(loggingCategory, "Completed open request in OpenIModelDbMemoizer.openIModelDb", () => (iModelToken)); - return openPromise.result!; + return openQP.result!; } - assert(openPromise.isRejected); + assert(openQP.isRejected); Logger.logTrace(loggingCategory, "Rejected open request in OpenIModelDbMemoizer.openIModelDb", () => (iModelToken)); - throw openPromise.error!; + throw openQP.error!; } } diff --git a/core/backend/src/rpc-impl/WipRpcImpl.ts b/core/backend/src/rpc-impl/WipRpcImpl.ts index fb85877..18349df 100644 --- a/core/backend/src/rpc-impl/WipRpcImpl.ts +++ b/core/backend/src/rpc-impl/WipRpcImpl.ts @@ -5,10 +5,11 @@ /** @module RpcInterface */ import { ActivityLoggingContext } from "@bentley/bentleyjs-core"; -import { IModelToken, RpcInterface, RpcManager } from "@bentley/imodeljs-common"; +import { IModelToken, RpcInterface, RpcManager, ChangedElements } from "@bentley/imodeljs-common"; import { WipRpcInterface } from "@bentley/imodeljs-common/lib/rpc/WipRpcInterface"; // not part of the "barrel" import { IModelDb } from "../IModelDb"; import { ChangeSummaryManager } from "../ChangeSummaryManager"; +import { ChangedElementsManager } from "../ChangedElementsManager"; /** * The backend implementation of WipRpcInterface. @@ -36,4 +37,12 @@ export class WipRpcImpl extends RpcInterface implements WipRpcInterface { if (ChangeSummaryManager.isChangeCacheAttached(iModel)) ChangeSummaryManager.detachChangeCache(iModel); } + + public async getChangedElements(iModelToken: IModelToken, startChangesetId: string, endChangesetId: string): Promise { + return ChangedElementsManager.getChangedElements(iModelToken.iModelId!, startChangesetId, endChangesetId); + } + + public async isChangesetProcessed(iModelToken: IModelToken, changesetId: string): Promise { + return ChangedElementsManager.isProcessed(iModelToken.iModelId!, changesetId); + } } diff --git a/core/backend/src/test/IModelTestUtils.ts b/core/backend/src/test/IModelTestUtils.ts index 8800d72..77fd1d5 100644 --- a/core/backend/src/test/IModelTestUtils.ts +++ b/core/backend/src/test/IModelTestUtils.ts @@ -328,6 +328,7 @@ export class IModelTestUtils { public static startBackend() { IModelJsConfig.init(true /* suppress exception */, false /* suppress error message */, Config.App); const config = new IModelHostConfiguration(); + config.useTileContentThreadPool = true; IModelHost.startup(config); } @@ -374,6 +375,8 @@ export class IModelTestUtils { Logger.setLevel("imodeljs-clients.Url", LogLevel.Trace); Logger.setLevel("DgnCore", LogLevel.Error); Logger.setLevel("BeSQLite", LogLevel.Error); + Logger.setLevel("Bentley.LICENSING", LogLevel.Error); + Logger.setLevel("imodeljs-addon", LogLevel.Error); } } diff --git a/core/backend/src/test/assets/test2.bim b/core/backend/src/test/assets/test2.bim new file mode 100644 index 0000000..d69ebbf Binary files /dev/null and b/core/backend/src/test/assets/test2.bim differ diff --git a/core/backend/src/test/integration/BriefcaseManager.test.ts b/core/backend/src/test/integration/BriefcaseManager.test.ts index 0b8ec81..515f8ad 100644 --- a/core/backend/src/test/integration/BriefcaseManager.test.ts +++ b/core/backend/src/test/integration/BriefcaseManager.test.ts @@ -5,7 +5,7 @@ import { assert } from "chai"; import { IModelJsFs } from "../../IModelJsFs"; -import { OpenMode, ActivityLoggingContext, GuidString } from "@bentley/bentleyjs-core"; +import { OpenMode, ActivityLoggingContext, GuidString, Logger, LogLevel } from "@bentley/bentleyjs-core"; import { IModelVersion } from "@bentley/imodeljs-common"; import { IModelTestUtils, TestUsers, TestIModelInfo } from "../IModelTestUtils"; import { KeepBriefcase, IModelDb, OpenParams, AccessMode, ExclusiveAccessOption, Element, IModelHost, IModelHostConfiguration, BriefcaseManager, BriefcaseEntry } from "../../imodeljs-backend"; @@ -46,6 +46,9 @@ describe("BriefcaseManager (#integration)", () => { }; before(async () => { + IModelTestUtils.setupLogging(); + // IModelTestUtils.setupDebugLogLevels(); + accessToken = await HubUtility.login(TestUsers.regular); testProjectId = await HubUtility.queryProjectIdByName(accessToken, "iModelJsIntegrationTest"); @@ -267,6 +270,8 @@ describe("BriefcaseManager (#integration)", () => { await iModelPullOnly.pullAndMergeChanges(actx, accessToken, IModelVersion.asOfChangeSet(thirdChangeSetId)); assert.strictEqual(iModelPullOnly.briefcase.currentChangeSetId, thirdChangeSetId); + const prevLogLevel: LogLevel | undefined = Logger.getLevel("imodeljs-backend.BriefcaseManager"); + Logger.setLevel("imodeljs-backend.BriefcaseManager", LogLevel.None); let exceptionThrown = false; try { await iModelFixed.pullAndMergeChanges(actx, accessToken, IModelVersion.asOfChangeSet(thirdChangeSetId)); @@ -283,6 +288,7 @@ describe("BriefcaseManager (#integration)", () => { exceptionThrown = true; } assert.isTrue(exceptionThrown); + Logger.setLevel("imodeljs-backend.BriefcaseManager", prevLogLevel || LogLevel.None); await iModelPullOnly.close(actx, accessToken, KeepBriefcase.No); await iModelFixed.close(actx, accessToken, KeepBriefcase.No); @@ -304,6 +310,8 @@ describe("BriefcaseManager (#integration)", () => { iModelPullOnly.elements.updateElement(rootEl); iModelPullOnly.saveChanges(); + const prevLogLevel: LogLevel | undefined = Logger.getLevel("imodeljs-backend.BriefcaseManager"); + Logger.setLevel("imodeljs-backend.BriefcaseManager", LogLevel.None); let exceptionThrown = false; try { await iModelPullOnly.pushChanges(actx, accessToken); @@ -311,6 +319,7 @@ describe("BriefcaseManager (#integration)", () => { exceptionThrown = true; } assert.isTrue(exceptionThrown); + Logger.setLevel("imodeljs-backend.BriefcaseManager", prevLogLevel || LogLevel.None); const iModelPullAndPush: IModelDb = await IModelDb.open(actx, accessToken, testProjectId, readWriteTestIModel.id, OpenParams.pullAndPush(), IModelVersion.latest()); assert.exists(iModelPullAndPush); diff --git a/core/backend/src/test/integration/ChangeSummary.test.ts b/core/backend/src/test/integration/ChangeSummary.test.ts index dd4bf9e..2b9cc2b 100644 --- a/core/backend/src/test/integration/ChangeSummary.test.ts +++ b/core/backend/src/test/integration/ChangeSummary.test.ts @@ -376,7 +376,7 @@ describe("ChangeSummary (#integration)", () => { let iModel: IModelDb = await IModelDb.open(actx, accessToken, testProjectId, readOnlyTestIModel.id, OpenParams.fixedVersion(AccessMode.Exclusive)); try { assert.exists(iModel); - await using(new DisableNativeAssertions(), async () => { + await using(new DisableNativeAssertions(), async (_r) => { await ChangeSummaryManager.extractChangeSummaries(actx, accessToken, iModel); }); } catch (e) { @@ -390,7 +390,7 @@ describe("ChangeSummary (#integration)", () => { iModel = await IModelDb.open(actx, accessToken, testProjectId, readOnlyTestIModel.id, OpenParams.fixedVersion(AccessMode.Shared)); try { assert.exists(iModel); - await using(new DisableNativeAssertions(), async () => { + await using(new DisableNativeAssertions(), async (_r) => { await ChangeSummaryManager.extractChangeSummaries(actx, accessToken, iModel); }); } catch (e) { @@ -405,7 +405,7 @@ describe("ChangeSummary (#integration)", () => { try { assert.exists(iModel); await iModel.close(actx, accessToken); - await using(new DisableNativeAssertions(), async () => { + await using(new DisableNativeAssertions(), async (_r) => { await ChangeSummaryManager.extractChangeSummaries(actx, accessToken, iModel); }); } catch (e) { @@ -419,7 +419,7 @@ describe("ChangeSummary (#integration)", () => { assert.exists(iModel.briefcase); assert.isTrue(iModel.briefcase!.isStandalone); try { - await using(new DisableNativeAssertions(), async () => { + await using(new DisableNativeAssertions(), async (_r) => { await ChangeSummaryManager.extractChangeSummaries(actx, accessToken, iModel); }); } catch (e) { diff --git a/core/backend/src/test/integration/ChangedElements.test.ts b/core/backend/src/test/integration/ChangedElements.test.ts new file mode 100644 index 0000000..8c724ac --- /dev/null +++ b/core/backend/src/test/integration/ChangedElements.test.ts @@ -0,0 +1,106 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { assert } from "chai"; +import { DbResult, ActivityLoggingContext } from "@bentley/bentleyjs-core"; +import { AccessToken, ChangeSet } from "@bentley/imodeljs-clients"; +import { IModelVersion, ChangedElements } from "@bentley/imodeljs-common"; +import { BriefcaseManager } from "../../BriefcaseManager"; +import { IModelDb, OpenParams } from "../../IModelDb"; +import { IModelTestUtils, TestUsers, TestIModelInfo } from "../IModelTestUtils"; +import { IModelJsFs } from "../../IModelJsFs"; +import { HubUtility } from "./HubUtility"; +import { ChangedElementsDb } from "../../imodeljs-backend"; +import { ChangedElementsManager } from "../../ChangedElementsManager"; + +function setupTest(iModelId: string): void { + const cacheFilePath: string = BriefcaseManager.getChangeCachePathName(iModelId); + if (IModelJsFs.existsSync(cacheFilePath)) + IModelJsFs.removeSync(cacheFilePath); +} + +describe("ChangedElements (#integration)", () => { + let accessToken: AccessToken; + let testProjectId: string; + + let testIModel: TestIModelInfo; + + const actx = new ActivityLoggingContext(""); + + before(async () => { + accessToken = await HubUtility.login(TestUsers.regular); + testProjectId = await HubUtility.queryProjectIdByName(accessToken, "iModelJsIntegrationTest"); + testIModel = await IModelTestUtils.getTestModelInfo(accessToken, testProjectId, "ReadOnlyTest"); + + // Purge briefcases that are close to reaching the acquire limit + const managerAccessToken: AccessToken = await HubUtility.login(TestUsers.manager); + await HubUtility.purgeAcquiredBriefcases(managerAccessToken, "iModelJsIntegrationTest", "ReadOnlyTest"); + }); + + it("Create ChangedElements Cache and process changesets", async () => { + setupTest(testIModel.id); + + const iModel: IModelDb = await IModelDb.open(actx, accessToken, testProjectId, testIModel.id, OpenParams.fixedVersion(), IModelVersion.latest()); + const changeSets: ChangeSet[] = await BriefcaseManager.imodelClient.changeSets.get(actx, accessToken, testIModel.id); + assert.exists(iModel); + + const filePath = ChangedElementsManager.getChangedElementsPathName(iModel.iModelToken.iModelId!); + if (IModelJsFs.existsSync(filePath)) + IModelJsFs.removeSync(filePath); + + let cache: ChangedElementsDb | undefined = ChangedElementsDb.createDb(iModel, filePath); + const startChangesetId = changeSets[0].id!; + const endChangesetId = changeSets[changeSets.length - 1].id!; + // Check that the changesets have not been processed yet + assert.isFalse(cache.isProcessed(startChangesetId)); + assert.isFalse(cache.isProcessed(endChangesetId)); + // Try getting changed elements, should fail because we haven't processed the changesets + let changes: ChangedElements | undefined; + try { + changes = cache.getChangedElements(startChangesetId, endChangesetId); + assert.isTrue(false); + } catch { + // Expected to fail + } + assert.isTrue(changes === undefined); + // Process changesets with "Items" presentation rules + const result: DbResult = await cache.processChangesets(accessToken, iModel, "Items", startChangesetId, endChangesetId); + assert.equal(result, DbResult.BE_SQLITE_OK); + // Check that the changesets should have been processed now + assert.isTrue(cache.isProcessed(startChangesetId)); + assert.isTrue(cache.isProcessed(endChangesetId)); + // Try getting changed elements, it should work this time + changes = cache.getChangedElements(startChangesetId, endChangesetId); + assert.isTrue(changes !== undefined); + assert.isTrue(changes!.elements.length !== 0); + assert.isTrue(changes!.elements.length === changes!.classIds.length && changes!.elements.length === changes!.opcodes.length); + + // Destroy the cache + cache = undefined; + changes = undefined; + // Open the db using the manager and try to get changed elements again to test the cached processed elements + cache = ChangedElementsDb.openDb(filePath); + assert.isTrue(cache !== undefined); + // Check that the changesets should still be in the cache + assert.isTrue(cache.isProcessed(startChangesetId)); + assert.isTrue(cache.isProcessed(endChangesetId)); + // Try getting changed elements again + changes = cache.getChangedElements(startChangesetId, endChangesetId); + assert.isTrue(changes !== undefined); + assert.isTrue(changes!.elements.length !== 0); + assert.isTrue(changes!.elements.length === changes!.classIds.length && changes!.elements.length === changes!.opcodes.length); + + // Test the ChangedElementsManager + cache = undefined; + changes = undefined; + // Check that the changesets should still be in the cache + assert.isTrue(ChangedElementsManager.isProcessed(iModel.iModelToken.iModelId!, startChangesetId)); + assert.isTrue(ChangedElementsManager.isProcessed(iModel.iModelToken.iModelId!, endChangesetId)); + // Check that we can get elements + changes = ChangedElementsManager.getChangedElements(iModel.iModelToken.iModelId!, startChangesetId, endChangesetId); + assert.isTrue(changes !== undefined); + assert.isTrue(changes!.elements.length !== 0); + assert.isTrue(changes!.elements.length === changes!.classIds.length && changes!.elements.length === changes!.opcodes.length); + }); +}); diff --git a/core/backend/src/test/integration/DebugHubIssues.test.ts b/core/backend/src/test/integration/DebugHubIssues.test.ts index ae19f12..2d4b776 100644 --- a/core/backend/src/test/integration/DebugHubIssues.test.ts +++ b/core/backend/src/test/integration/DebugHubIssues.test.ts @@ -73,6 +73,10 @@ describe.skip("DebugHubIssues (#integration)", () => { iModelName = "ConnectionReadTest"; iModelDir = path.join(iModelRootDir, iModelName); await HubUtility.downloadIModelByName(accessToken, projectName, iModelName, iModelDir); + + iModelName = "PushTest"; + iModelDir = path.join(iModelRootDir, iModelName); + await HubUtility.downloadIModelByName(accessToken, projectName, iModelName, iModelDir); }); it.skip("should be able to open ReadOnlyTest model", async () => { diff --git a/core/backend/src/test/integration/HubUtility.ts b/core/backend/src/test/integration/HubUtility.ts index 2ed0be7..3348898 100644 --- a/core/backend/src/test/integration/HubUtility.ts +++ b/core/backend/src/test/integration/HubUtility.ts @@ -205,6 +205,19 @@ export class HubUtility { await BriefcaseManager.imodelClient.iModels.delete(actx, accessToken, projectId, iModelId); } + public static dumpChangeSetFile(iModel: IModelDb, dir: string, whichCs: string): void { + const changeSets: ChangeSetToken[] = HubUtility.readChangeSets(dir); + + changeSets.forEach((changeSet) => { + if (changeSet.id === whichCs) { + BriefcaseManager.dumpChangeSet(iModel.briefcase, changeSet); + return; + } + }); + + throw new Error(whichCs + " - .cs file not found in directory " + dir); + } + /** Validate all change set operations by downloading seed files & change sets, creating a standalone iModel, * merging the change sets, reversing them, and finally reinstating them. The method also logs the necessary performance * metrics with these operations. diff --git a/core/backend/src/test/integration/IModelWrite.test.ts b/core/backend/src/test/integration/IModelWrite.test.ts index 92bc357..3539ab2 100644 --- a/core/backend/src/test/integration/IModelWrite.test.ts +++ b/core/backend/src/test/integration/IModelWrite.test.ts @@ -1,551 +1,607 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ - -import { expect, assert } from "chai"; -import { Id64String, DbOpcode, DbResult, ActivityLoggingContext } from "@bentley/bentleyjs-core"; -import { IModelVersion, SubCategoryAppearance, IModel } from "@bentley/imodeljs-common"; -import { IModelTestUtils, TestUsers, Timer, TestIModelInfo } from "../IModelTestUtils"; -import { IModelJsFs } from "../../IModelJsFs"; -import { KeepBriefcase, IModelDb, OpenParams, Element, DictionaryModel, BriefcaseManager, SpatialCategory, SqliteStatement, SqliteValue, SqliteValueType, BriefcaseEntry } from "../../imodeljs-backend"; -import { ConcurrencyControl } from "../../ConcurrencyControl"; -import { AccessToken, CodeState, HubIModel, HubCode, IModelQuery, MultiCode } from "@bentley/imodeljs-clients"; -import { HubUtility } from "./HubUtility"; - -const actx = new ActivityLoggingContext(""); - -export async function createNewModelAndCategory(rwIModel: IModelDb, accessToken: AccessToken) { - // Create a new physical model. - let modelId: Id64String; - [, modelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(rwIModel, IModelTestUtils.getUniqueModelCode(rwIModel, "newPhysicalModel"), true); - - // Find or create a SpatialCategory. - const dictionary: DictionaryModel = rwIModel.models.getModel(IModel.dictionaryId) as DictionaryModel; - const newCategoryCode = IModelTestUtils.getUniqueSpatialCategoryCode(dictionary, "ThisTestSpatialCategory"); - const spatialCategoryId: Id64String = SpatialCategory.insert(rwIModel, IModel.dictionaryId, newCategoryCode.value!, new SubCategoryAppearance({ color: 0xff0000 })); - - // Reserve all of the codes that are required by the new model and category. - try { - await rwIModel.concurrencyControl.request(actx, accessToken); - } catch (err) { - if (err instanceof ConcurrencyControl.RequestError) { - assert.fail(JSON.stringify(err.unavailableCodes) + ", " + JSON.stringify(err.unavailableLocks)); - } - } - - return { modelId, spatialCategoryId }; -} - -describe("IModelWriteTest (#integration)", () => { - let accessToken: AccessToken; - let testProjectId: string; - let writeTestProjectId: string; - let readOnlyTestIModel: TestIModelInfo; - let readWriteTestIModel: TestIModelInfo; - - const validateBriefcaseCache = () => { - const paths = new Array(); - (BriefcaseManager as any)._cache._briefcases.forEach((briefcase: BriefcaseEntry, key: string) => { - assert.isTrue(IModelJsFs.existsSync(briefcase.pathname), `File corresponding to briefcase cache entry not found: ${briefcase.pathname}`); - assert.strictEqual(briefcase.getKey(), key, `Cached key ${key} doesn't match the current generated key ${briefcase.getKey()}`); - assert.isFalse(paths.includes(briefcase.pathname), `Briefcase with path: ${briefcase.pathname} (key: ${key}) has a duplicate in the cache`); - paths.push(briefcase.pathname); - }); - }; - - before(async () => { - accessToken = await HubUtility.login(TestUsers.manager); - - testProjectId = await HubUtility.queryProjectIdByName(accessToken, "iModelJsIntegrationTest"); - readOnlyTestIModel = await IModelTestUtils.getTestModelInfo(accessToken, testProjectId, "ReadOnlyTest"); - readWriteTestIModel = await IModelTestUtils.getTestModelInfo(accessToken, testProjectId, "ReadWriteTest"); - - writeTestProjectId = await HubUtility.queryProjectIdByName(accessToken, "iModelJsTest"); - - // Purge briefcases that are close to reaching the acquire limit - const managerAccessToken: AccessToken = await HubUtility.login(TestUsers.manager); - await HubUtility.purgeAcquiredBriefcases(managerAccessToken, "iModelJsIntegrationTest", "ReadOnlyTest"); - await HubUtility.purgeAcquiredBriefcases(managerAccessToken, "iModelJsIntegrationTest", "ReadWriteTest"); - }); - - afterEach(() => { - validateBriefcaseCache(); - }); - - it("test change-merging scenarios in optimistic concurrency mode (#integration)", async () => { - const firstUser = await IModelTestUtils.getTestUserAccessToken(TestUsers.super); - const secondUser = await IModelTestUtils.getTestUserAccessToken(TestUsers.superManager); - const neutralObserverUser = await IModelTestUtils.getTestUserAccessToken(TestUsers.manager); - - const firstIModel: IModelDb = await IModelDb.open(actx, firstUser, testProjectId, readWriteTestIModel.id, OpenParams.pullAndPush()); - const secondIModel: IModelDb = await IModelDb.open(actx, secondUser, testProjectId, readWriteTestIModel.id, OpenParams.pullAndPush()); - const neutralObserverIModel: IModelDb = await IModelDb.open(actx, neutralObserverUser, testProjectId, readWriteTestIModel.id, OpenParams.pullOnly()); - assert.notEqual(firstIModel, secondIModel); - - // Set up optimistic concurrency. Note the defaults are: - firstIModel.concurrencyControl.setPolicy(new ConcurrencyControl.OptimisticPolicy()); - secondIModel.concurrencyControl.setPolicy(new ConcurrencyControl.OptimisticPolicy()); - // Note: neutralObserver's IModel does not need to be configured for optimistic concurrency. He just pulls changes. - - // firstUser: create model, category, and element el1 - const r: { modelId: Id64String, spatialCategoryId: Id64String } = await createNewModelAndCategory(firstIModel, firstUser); - const el1 = firstIModel.elements.insertElement(IModelTestUtils.createPhysicalObject(firstIModel, r.modelId, r.spatialCategoryId)); - // const el2 = firstIModel.elements.insertElement(IModelTestUtils.createPhysicalObject(firstIModel, r.modelId, r.spatialCategoryId)); - firstIModel.saveChanges("firstUser created model, category, and two elements"); - await firstIModel.pushChanges(actx, firstUser); - - // secondUser: pull and merge - await secondIModel.pullAndMergeChanges(actx, secondUser); - - // --- Test 1: Overlapping changes that really are conflicts => conflict-resolution policy is applied --- - - // firstUser: modify el1.userLabel - if (true) { - const el1cc: Element = firstIModel.elements.getElement(el1); - el1cc.userLabel = el1cc.userLabel + " -> changed by firstUser"; - firstIModel.elements.updateElement(el1cc); - firstIModel.saveChanges("firstUser modified el1.userLabel"); - await firstIModel.pushChanges(actx, firstUser); - } - - // secondUser: modify el1.userLabel - let expectedValueofEl1UserLabel: string; - if (true) { - const el1before: Element = (secondIModel.elements.getElement(el1)); - expectedValueofEl1UserLabel = el1before.userLabel + " -> changed by secondUser"; - el1before.userLabel = expectedValueofEl1UserLabel; - secondIModel.elements.updateElement(el1before); - secondIModel.saveChanges("secondUser modified el1.userLabel"); - - // pull + merge => take secondUser's change (RejectIncomingChange). That's because the default updateVsUpdate settting is RejectIncomingChange - await secondIModel.pullAndMergeChanges(actx, secondUser); - const el1after = secondIModel.elements.getElement(el1); - assert.equal(el1after.userLabel, expectedValueofEl1UserLabel); - - await secondIModel.pushChanges(actx, secondUser); - } - - // Make sure a neutral observer sees secondUser's change. - if (true) { - await neutralObserverIModel.pullAndMergeChanges(actx, neutralObserverUser); - const elobj = neutralObserverIModel.elements.getElement(el1); - assert.equal(elobj.userLabel, expectedValueofEl1UserLabel); - } - - // firstUser: pull and see that secondUser has overridden my change - if (true) { - await firstIModel.pullAndMergeChanges(actx, firstUser); - const elobj = firstIModel.elements.getElement(el1); - assert.equal(elobj.userLabel, expectedValueofEl1UserLabel); - } - - // --- Test 2: Overlapping changes that are not conflicts --- - /* **************** No. We do not support property-level change-merging. - - // firstUser: modify el1.userLabel - const wasExpectedValueofEl1UserLabel = expectedValueofEl1UserLabel; - if (true) { - const el1cc: Element = firstIModel.elements.getElement(el1); - assert.equal(el1cc.userLabel, wasExpectedValueofEl1UserLabel); - expectedValueofEl1UserLabel = el1cc.userLabel + " -> changed again by firstUser"; - el1cc.userLabel = expectedValueofEl1UserLabel; - firstIModel.elements.updateElement(el1cc); - firstIModel.saveChanges("firstUser modified el1.userLabel"); - await firstIModel.pushChanges(actx, firstUser); - } - - // Make sure a neutral observer sees firstUser's changes. - if (true) { - await neutralObserverIModel.pullAndMergeChanges(actx, neutralObserverUser); - const elobj = neutralObserverIModel.elements.getElement(el1); - assert.equal(elobj.userLabel, expectedValueofEl1UserLabel); - } - - // secondUser: modify el1.userProperties - const secondUserPropNs = "secondUser"; - const secondUserPropName = "property"; - const expectedValueOfSecondUserProp: string = "x"; - if (true) { - const el1before: Element = secondIModel.elements.getElement(el1); - assert.equal(el1before.userLabel, wasExpectedValueofEl1UserLabel); - - el1before.setUserProperties(secondUserPropNs, { property: expectedValueOfSecondUserProp }); // secondUser changes userProperties - secondIModel.elements.updateElement(el1before); - secondIModel.saveChanges("secondUser modified el1.userProperties"); - assert.equal(el1before.userLabel, wasExpectedValueofEl1UserLabel, "secondUser does not change userLabel"); - - // pull + merge => no conflict + both changes should be intact - await secondIModel.pullAndMergeChanges(actx, secondUser); - const el1after = secondIModel.elements.getElement(el1); - assert.equal(el1after.userLabel, expectedValueofEl1UserLabel); - assert.equal(el1after.getUserProperties(secondUserPropNs)[secondUserPropName], expectedValueOfSecondUserProp); - - await secondIModel.pushChanges(actx, secondUser); - } - - // firstUser: pull and see both changes - if (true) { - await firstIModel.pullAndMergeChanges(actx, firstUser); - const elobj = firstIModel.elements.getElement(el1); - assert.equal(elobj.userLabel, expectedValueofEl1UserLabel); - assert.equal(elobj.getUserProperties(secondUserPropNs)[secondUserPropName], expectedValueOfSecondUserProp); - } - - // Make sure a neutral observer sees both changes. - if (true) { - await neutralObserverIModel.pullAndMergeChanges(actx, neutralObserverUser); - const elobj = neutralObserverIModel.elements.getElement(el1); - assert.equal(elobj.userLabel, expectedValueofEl1UserLabel); - assert.equal(elobj.getUserProperties(secondUserPropNs)[secondUserPropName], expectedValueOfSecondUserProp); - } -*/ - // --- Test 3: Non-overlapping changes --- - - }); - - // Does not work with mocks - it.skip("should build concurrency control request", async () => { - const iModel: IModelDb = await IModelDb.open(actx, accessToken, testProjectId, readWriteTestIModel.id, OpenParams.pullAndPush()); - - const el: Element = iModel.elements.getRootSubject(); - el.buildConcurrencyControlRequest(DbOpcode.Update); // make a list of the locks, etc. that will be needed to update this element - const reqAsAny: any = ConcurrencyControl.convertRequestToAny(iModel.concurrencyControl.pendingRequest); - assert.isDefined(reqAsAny); - assert.isArray(reqAsAny.Locks); - assert.equal(reqAsAny.Locks.length, 3, " we expect to need a lock on the element (exclusive), its model (shared), and the db itself (shared)"); - assert.isArray(reqAsAny.Codes); - assert.equal(reqAsAny.Codes.length, 0, " since we didn't add or change the element's code, we don't expect to need a code reservation"); - - await iModel.close(actx, accessToken); - }); - - it("should push changes with codes (#integration)", async () => { - const adminAccessToken = await IModelTestUtils.getTestUserAccessToken(TestUsers.superManager); - let timer = new Timer("delete iModels"); - // Delete any existing iModels with the same name as the read-write test iModel - const iModelName = "CodesPushTest"; - const iModels: HubIModel[] = await BriefcaseManager.imodelClient.iModels.get(actx, adminAccessToken, writeTestProjectId, new IModelQuery().byName(iModelName)); - for (const iModelTemp of iModels) { - await BriefcaseManager.imodelClient.iModels.delete(actx, adminAccessToken, writeTestProjectId, iModelTemp.id!); - } - timer.end(); - - // Create a new iModel on the Hub (by uploading a seed file) - timer = new Timer("create iModel"); - const rwIModel: IModelDb = await IModelDb.create(actx, adminAccessToken, writeTestProjectId, iModelName, { rootSubject: { name: "TestSubject" } }); - const rwIModelId = rwIModel.iModelToken.iModelId; - assert.isNotEmpty(rwIModelId); - timer.end(); - - timer = new Timer("querying codes"); - const initialCodes = await BriefcaseManager.imodelClient.codes.get(actx, adminAccessToken, rwIModelId!); - timer.end(); - - timer = new Timer("make local changes"); - const code = IModelTestUtils.getUniqueModelCode(rwIModel, "newPhysicalModel"); - IModelTestUtils.createAndInsertPhysicalPartitionAndModel(rwIModel, code, true); - - rwIModel.saveChanges("inserted generic objects"); - timer.end(); - - timer = new Timer("push changes"); - - // Push the changes to the hub - const prePushChangeSetId = rwIModel.iModelToken.changeSetId; - await rwIModel.pushChanges(actx, adminAccessToken); - const postPushChangeSetId = rwIModel.iModelToken.changeSetId; - assert(!!postPushChangeSetId); - expect(prePushChangeSetId !== postPushChangeSetId); - - timer.end(); - - timer = new Timer("querying codes"); - const codes = await BriefcaseManager.imodelClient.codes.get(actx, adminAccessToken, rwIModelId!); - timer.end(); - expect(codes.length > initialCodes.length); - }); - - it("should push changes with code conflicts (#integration)", async () => { - const adminAccessToken = await IModelTestUtils.getTestUserAccessToken(TestUsers.superManager); - let timer = new Timer("delete iModels"); - // Delete any existing iModels with the same name as the read-write test iModel - const iModelName = "CodesConflictTest"; - const iModels: HubIModel[] = await BriefcaseManager.imodelClient.iModels.get(actx, adminAccessToken, writeTestProjectId, new IModelQuery().byName(iModelName)); - for (const iModelTemp of iModels) { - await BriefcaseManager.imodelClient.iModels.delete(actx, adminAccessToken, writeTestProjectId, iModelTemp.id!); - } - timer.end(); - - // Create a new iModel on the Hub (by uploading a seed file) - timer = new Timer("create iModel"); - const rwIModel: IModelDb = await IModelDb.create(actx, adminAccessToken, writeTestProjectId, iModelName, { rootSubject: { name: "TestSubject" } }); - const rwIModelId = rwIModel.iModelToken.iModelId; - assert.isNotEmpty(rwIModelId); - timer.end(); - - const code = IModelTestUtils.getUniqueModelCode(rwIModel, "newPhysicalModel"); - const otherBriefcase = await BriefcaseManager.imodelClient.briefcases.create(actx, adminAccessToken, rwIModelId!); - const hubCode = new HubCode(); - hubCode.value = code.value; - hubCode.codeSpecId = code.spec; - hubCode.codeScope = code.scope; - hubCode.briefcaseId = otherBriefcase.briefcaseId; - hubCode.state = CodeState.Reserved; - await BriefcaseManager.imodelClient.codes.update(actx, adminAccessToken, rwIModelId!, [hubCode]); - - timer = new Timer("querying codes"); - const initialCodes = await BriefcaseManager.imodelClient.codes.get(actx, adminAccessToken, rwIModelId!); - timer.end(); - - timer = new Timer("make local changes"); - IModelTestUtils.createAndInsertPhysicalPartitionAndModel(rwIModel, code, true); - - rwIModel.saveChanges("inserted generic objects"); - timer.end(); - - timer = new Timer("push changes"); - - // Push the changes to the hub - const prePushChangeSetId = rwIModel.iModelToken.changeSetId; - await rwIModel.pushChanges(actx, adminAccessToken); - const postPushChangeSetId = rwIModel.iModelToken.changeSetId; - assert(!!postPushChangeSetId); - expect(prePushChangeSetId !== postPushChangeSetId); - - timer.end(); - - timer = new Timer("querying codes"); - const codes = await BriefcaseManager.imodelClient.codes.get(actx, accessToken, rwIModelId!); - timer.end(); - expect(codes.length === initialCodes.length); - expect(codes[0].state === CodeState.Reserved); - }); - - it("should write to briefcase with optimistic concurrency (#integration)", async () => { - const adminAccessToken = await IModelTestUtils.getTestUserAccessToken(TestUsers.superManager); - - let timer = new Timer("delete iModels"); - // Delete any existing iModels with the same name as the OptimisticConcurrencyTest iModel - const iModelName = "OptimisticConcurrencyTest"; - const iModels: HubIModel[] = await BriefcaseManager.imodelClient.iModels.get(actx, adminAccessToken, writeTestProjectId, new IModelQuery().byName(iModelName)); - for (const iModelTemp of iModels) { - await BriefcaseManager.imodelClient.iModels.delete(actx, adminAccessToken, writeTestProjectId, iModelTemp.id!); - } - timer.end(); - - // Create a new iModel on the Hub (by uploading a seed file) - timer = new Timer("create iModel"); - const rwIModel: IModelDb = await IModelDb.create(actx, adminAccessToken, writeTestProjectId, iModelName, { rootSubject: { name: "TestSubject" } }); - const rwIModelId = rwIModel.iModelToken.iModelId; - assert.isNotEmpty(rwIModelId); - timer.end(); - - timer = new Timer("make local changes"); - - // Turn on optimistic concurrency control. This allows the app to modify elements, models, etc. without first acquiring locks. - // Later, when the app downloads and merges changeSets from the Hub into the briefcase, BriefcaseManager will merge changes and handle conflicts. - // The app still has to reserve codes. - rwIModel.concurrencyControl.setPolicy(new ConcurrencyControl.OptimisticPolicy()); - - // Show that we can modify the properties of an element. In this case, we modify the root element itself. - const rootEl: Element = rwIModel.elements.getRootSubject(); - rootEl.userLabel = rootEl.userLabel + "changed"; - rwIModel.elements.updateElement(rootEl); - - assert.isFalse(rwIModel.concurrencyControl.hasPendingRequests); - - rwIModel.saveChanges(JSON.stringify({ userid: "user1", description: "changed a userLabel" })); // save it, to show that saveChanges will accumulate local txn descriptions - - // Create a new physical model. - let newModelId: Id64String; - [, newModelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(rwIModel, IModelTestUtils.getUniqueModelCode(rwIModel, "newPhysicalModel"), true); - - // Find or create a SpatialCategory. - const dictionary: DictionaryModel = rwIModel.models.getModel(IModel.dictionaryId) as DictionaryModel; - const newCategoryCode = IModelTestUtils.getUniqueSpatialCategoryCode(dictionary, "ThisTestSpatialCategory"); - const spatialCategoryId: Id64String = SpatialCategory.insert(rwIModel, IModel.dictionaryId, newCategoryCode.value!, new SubCategoryAppearance({ color: 0xff0000 })); - - timer.end(); - - timer = new Timer("query Codes I"); - - // iModel.concurrencyControl should have recorded the codes that are required by the new elements. - assert.isTrue(rwIModel.concurrencyControl.hasPendingRequests); - assert.isTrue(await rwIModel.concurrencyControl.areAvailable(actx, adminAccessToken)); - - timer.end(); - timer = new Timer("reserve Codes"); - - // Reserve all of the codes that are required by the new model and category. - try { - await rwIModel.concurrencyControl.request(actx, adminAccessToken); - } catch (err) { - if (err instanceof ConcurrencyControl.RequestError) { - assert.fail(JSON.stringify(err.unavailableCodes) + ", " + JSON.stringify(err.unavailableLocks)); - } - } - - timer.end(); - timer = new Timer("query Codes II"); - - // Verify that the codes are reserved. - const category = rwIModel.elements.getElement(spatialCategoryId); - assert.isTrue(category.code.value !== undefined); - const codeStates: MultiCode[] = await rwIModel.concurrencyControl.codes.query(actx, adminAccessToken, category.code.spec, category.code.scope); - const foundCode: MultiCode[] = codeStates.filter((cs) => (cs.value === category.code.value!) && (cs.state === CodeState.Reserved)); - assert.equal(foundCode.length, 1); - - /* NEEDS WORK - query just this one code - assert.isTrue(category.code.value !== undefined); - const codeStates2 = await iModel.concurrencyControl.codes.query(adminAccessToken, category.code.spec, category.code.scope, category.code.value!); - assert.equal(codeStates2.length, 1); - assert.equal(codeStates2[0].values.length, 1); - assert.equal(codeStates2[0].values[0], category.code.value!); - */ - - timer.end(); - - timer = new Timer("make more local changes"); - - // Create a couple of physical elements. - const elid1 = rwIModel.elements.insertElement(IModelTestUtils.createPhysicalObject(rwIModel, newModelId, spatialCategoryId)); - rwIModel.elements.insertElement(IModelTestUtils.createPhysicalObject(rwIModel, newModelId, spatialCategoryId)); - - // Commit the local changes to a local transaction in the briefcase. - rwIModel.saveChanges(JSON.stringify({ userid: "user1", description: "inserted generic objects" })); - - rwIModel.elements.getElement(elid1); // throws if elid1 is not found - rwIModel.elements.getElement(spatialCategoryId); // throws if spatialCategoryId is not found - - timer.end(); - - timer = new Timer("pullmergepush"); - - // Push the changes to the hub - const prePushChangeSetId = rwIModel.iModelToken.changeSetId; - await rwIModel.pushChanges(actx, adminAccessToken); - const postPushChangeSetId = rwIModel.iModelToken.changeSetId; - assert(!!postPushChangeSetId); - expect(prePushChangeSetId !== postPushChangeSetId); - - timer.end(); - - // Open a readonly copy of the iModel - const roIModel: IModelDb = await IModelDb.open(actx, adminAccessToken, writeTestProjectId, rwIModelId!, OpenParams.fixedVersion(), IModelVersion.latest()); - assert.exists(roIModel); - - await rwIModel.close(actx, adminAccessToken, KeepBriefcase.No); - await roIModel.close(actx, adminAccessToken); - }); - - it("Run plain SQL against pull-only connection", async () => { - const iModel: IModelDb = await IModelDb.open(actx, accessToken, testProjectId, readOnlyTestIModel.id, OpenParams.pullOnly()); - try { - iModel.withPreparedSqliteStatement("CREATE TABLE Test(Id INTEGER PRIMARY KEY, Name TEXT NOT NULL, Code INTEGER)", (stmt: SqliteStatement) => { - assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE); - }); - - iModel.withPreparedSqliteStatement("INSERT INTO Test(Name,Code) VALUES(?,?)", (stmt: SqliteStatement) => { - stmt.bindValue(1, "Dummy 1"); - stmt.bindValue(2, 100); - assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE); - }); - - iModel.withPreparedSqliteStatement("INSERT INTO Test(Name,Code) VALUES(?,?)", (stmt: SqliteStatement) => { - stmt.bindValues(["Dummy 2", 200]); - assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE); - }); - - iModel.withPreparedSqliteStatement("INSERT INTO Test(Name,Code) VALUES(:p1,:p2)", (stmt: SqliteStatement) => { - stmt.bindValue(":p1", "Dummy 3"); - stmt.bindValue(":p2", 300); - assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE); - }); - - iModel.withPreparedSqliteStatement("INSERT INTO Test(Name,Code) VALUES(:p1,:p2)", (stmt: SqliteStatement) => { - stmt.bindValues({ ":p1": "Dummy 4", ":p2": 400 }); - assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE); - }); - - iModel.saveChanges(); - - iModel.withPreparedSqliteStatement("SELECT Id,Name,Code FROM Test ORDER BY Id", (stmt: SqliteStatement) => { - for (let i: number = 1; i <= 4; i++) { - assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); - assert.equal(stmt.getColumnCount(), 3); - const val0: SqliteValue = stmt.getValue(0); - assert.equal(val0.columnName, "Id"); - assert.equal(val0.type, SqliteValueType.Integer); - assert.isFalse(val0.isNull); - assert.equal(val0.getInteger(), i); - - const val1: SqliteValue = stmt.getValue(1); - assert.equal(val1.columnName, "Name"); - assert.equal(val1.type, SqliteValueType.String); - assert.isFalse(val1.isNull); - assert.equal(val1.getString(), `Dummy ${i}`); - - const val2: SqliteValue = stmt.getValue(2); - assert.equal(val2.columnName, "Code"); - assert.equal(val2.type, SqliteValueType.Integer); - assert.isFalse(val2.isNull); - assert.equal(val2.getInteger(), i * 100); - - const row: any = stmt.getRow(); - assert.equal(row.id, i); - assert.equal(row.name, `Dummy ${i}`); - assert.equal(row.code, i * 100); - } - assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE); - }); - } finally { - // delete the briefcase as the test modified it locally. - let briefcasePath: string | undefined; - if (!!iModel.briefcase) - briefcasePath = iModel.briefcase.pathname; - - await iModel.close(actx, accessToken, KeepBriefcase.No); - if (!!briefcasePath && IModelJsFs.existsSync(briefcasePath)) - IModelJsFs.unlinkSync(briefcasePath); - } - }); - - it("Run plain SQL against readonly connection", async () => { - const iModel: IModelDb = await IModelDb.open(actx, accessToken, testProjectId, readOnlyTestIModel.id, OpenParams.fixedVersion()); - - iModel.withPreparedSqliteStatement("SELECT Name,StrData FROM be_Prop WHERE Namespace='ec_Db'", (stmt: SqliteStatement) => { - let rowCount: number = 0; - while (stmt.step() === DbResult.BE_SQLITE_ROW) { - rowCount++; - assert.equal(stmt.getColumnCount(), 2); - const nameVal: SqliteValue = stmt.getValue(0); - assert.equal(nameVal.columnName, "Name"); - assert.equal(nameVal.type, SqliteValueType.String); - assert.isFalse(nameVal.isNull); - const name: string = nameVal.getString(); - - const versionVal: SqliteValue = stmt.getValue(1); - assert.equal(versionVal.columnName, "StrData"); - assert.equal(versionVal.type, SqliteValueType.String); - assert.isFalse(versionVal.isNull); - const profileVersion: any = JSON.parse(versionVal.getString()); - - assert.isTrue(name === "SchemaVersion" || name === "InitialSchemaVersion"); - if (name === "SchemaVersion") { - assert.equal(profileVersion.major, 4); - assert.equal(profileVersion.minor, 0); - assert.equal(profileVersion.sub1, 0); - assert.isAtLeast(profileVersion.sub2, 1); - } else if (name === "InitialSchemaVersion") { - assert.equal(profileVersion.major, 4); - assert.equal(profileVersion.minor, 0); - assert.equal(profileVersion.sub1, 0); - assert.isAtLeast(profileVersion.sub2, 1); - } - } - assert.equal(rowCount, 2); - }); - await iModel.close(actx, accessToken); - }); -}); +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { expect, assert } from "chai"; +import { Id64String, DbOpcode, DbResult, ActivityLoggingContext, Id64 } from "@bentley/bentleyjs-core"; +import { IModelVersion, SubCategoryAppearance, IModel, CodeSpec, CodeScopeSpec } from "@bentley/imodeljs-common"; +import { IModelTestUtils, TestUsers, Timer, TestIModelInfo } from "../IModelTestUtils"; +import { IModelJsFs } from "../../IModelJsFs"; +import { KeepBriefcase, IModelDb, OpenParams, Element, DictionaryModel, BriefcaseManager, SpatialCategory, SqliteStatement, SqliteValue, SqliteValueType, BriefcaseEntry } from "../../imodeljs-backend"; +import { ConcurrencyControl } from "../../ConcurrencyControl"; +import { AccessToken, CodeState, HubIModel, HubCode, IModelQuery, MultiCode, Lock, LockType, LockLevel } from "@bentley/imodeljs-clients"; +import { HubUtility } from "./HubUtility"; +import * as os from "os"; + +const actx = new ActivityLoggingContext(""); + +export async function createNewModelAndCategory(rwIModel: IModelDb, accessToken: AccessToken) { + // Create a new physical model. + let modelId: Id64String; + [, modelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(rwIModel, IModelTestUtils.getUniqueModelCode(rwIModel, "newPhysicalModel"), true); + + // Find or create a SpatialCategory. + const dictionary: DictionaryModel = rwIModel.models.getModel(IModel.dictionaryId) as DictionaryModel; + const newCategoryCode = IModelTestUtils.getUniqueSpatialCategoryCode(dictionary, "ThisTestSpatialCategory"); + const spatialCategoryId: Id64String = SpatialCategory.insert(rwIModel, IModel.dictionaryId, newCategoryCode.value!, new SubCategoryAppearance({ color: 0xff0000 })); + + // Reserve all of the codes that are required by the new model and category. + try { + await rwIModel.concurrencyControl.request(actx, accessToken); + } catch (err) { + if (err instanceof ConcurrencyControl.RequestError) { + assert.fail(JSON.stringify(err.unavailableCodes) + ", " + JSON.stringify(err.unavailableLocks)); + } + } + + return { modelId, spatialCategoryId }; +} + +describe("IModelWriteTest (#integration)", () => { + let accessToken: AccessToken; + let testProjectId: string; + let writeTestProjectId: string; + let readOnlyTestIModel: TestIModelInfo; + let readWriteTestIModel: TestIModelInfo; + + const validateBriefcaseCache = () => { + const paths = new Array(); + (BriefcaseManager as any)._cache._briefcases.forEach((briefcase: BriefcaseEntry, key: string) => { + assert.isTrue(IModelJsFs.existsSync(briefcase.pathname), `File corresponding to briefcase cache entry not found: ${briefcase.pathname}`); + assert.strictEqual(briefcase.getKey(), key, `Cached key ${key} doesn't match the current generated key ${briefcase.getKey()}`); + assert.isFalse(paths.includes(briefcase.pathname), `Briefcase with path: ${briefcase.pathname} (key: ${key}) has a duplicate in the cache`); + paths.push(briefcase.pathname); + }); + }; + + let readWriteTestIModelName: string; + + before(async () => { + accessToken = await HubUtility.login(TestUsers.manager); + + testProjectId = await HubUtility.queryProjectIdByName(accessToken, "iModelJsIntegrationTest"); + readOnlyTestIModel = await IModelTestUtils.getTestModelInfo(accessToken, testProjectId, "ReadOnlyTest"); + + let username = ""; + try { + username = os.userInfo().username; + } catch (err) { + } + readWriteTestIModelName = "ReadWriteTest".concat("_", username, "_", os.hostname() || ""); + + try { + await HubUtility.deleteIModel(accessToken, "iModelJsIntegrationTest", readWriteTestIModelName); + } catch (err) { + } + await BriefcaseManager.imodelClient.iModels.create(actx, accessToken, testProjectId, readWriteTestIModelName, undefined, "TestSubject", undefined, 2 * 60 * 1000); + readWriteTestIModel = await IModelTestUtils.getTestModelInfo(accessToken, testProjectId, readWriteTestIModelName); + + writeTestProjectId = await HubUtility.queryProjectIdByName(accessToken, "iModelJsTest"); + + // Purge briefcases that are close to reaching the acquire limit + const managerAccessToken: AccessToken = await HubUtility.login(TestUsers.manager); + await HubUtility.purgeAcquiredBriefcases(managerAccessToken, "iModelJsIntegrationTest", "ReadOnlyTest"); + }); + + afterEach(() => { + validateBriefcaseCache(); + }); + + after(async () => { + try { + await HubUtility.deleteIModel(accessToken, "iModelJsIntegrationTest", readWriteTestIModelName); + } catch (err) { + } + }); + + it("acquire codespec lock", async () => { + const loggingContext = new ActivityLoggingContext(""); + const userAccessToken = await IModelTestUtils.getTestUserAccessToken(TestUsers.super); + const iModel: IModelDb = await IModelDb.open(actx, userAccessToken, testProjectId, readWriteTestIModel.id, OpenParams.pullAndPush()); + const code1 = new CodeSpec(iModel, Id64.invalid, "MyCode", CodeScopeSpec.Type.Model); + + iModel.concurrencyControl.setPolicy(new ConcurrencyControl.OptimisticPolicy()); + const locks = await iModel.concurrencyControl.lockCodeSpecs(actx, userAccessToken); + assert.equal(locks.length, 1); + iModel.insertCodeSpec(code1); + await iModel.close(loggingContext, userAccessToken, KeepBriefcase.No); + }); + + it("acquire codespec lock - example", async () => { + const loggingContext = new ActivityLoggingContext(""); + const userAccessToken = await IModelTestUtils.getTestUserAccessToken(TestUsers.super); + const model: IModelDb = await IModelDb.open(actx, userAccessToken, testProjectId, readWriteTestIModel.id, OpenParams.pullAndPush()); + const code1 = new CodeSpec(model, Id64.invalid, "MyCode", CodeScopeSpec.Type.Model); + + model.concurrencyControl.setPolicy(new ConcurrencyControl.OptimisticPolicy()); // needed for writing to iModels + + const codeSpecsLock = new Lock(); + codeSpecsLock.briefcaseId = model.briefcase.briefcaseId; + codeSpecsLock.lockLevel = LockLevel.Exclusive; + codeSpecsLock.lockType = LockType.CodeSpecs; + codeSpecsLock.objectId = "0x1"; + codeSpecsLock.seedFileId = model.briefcase.fileId; + + const locks = await BriefcaseManager.imodelClient.locks.update(loggingContext, userAccessToken, model.briefcase.iModelId, [codeSpecsLock]); + assert.equal(locks.length, 1); + model.insertCodeSpec(code1); + await model.close(loggingContext, userAccessToken, KeepBriefcase.No); + }); + + it("test change-merging scenarios in optimistic concurrency mode (#integration)", async () => { + const firstUser = await IModelTestUtils.getTestUserAccessToken(TestUsers.super); + const secondUser = await IModelTestUtils.getTestUserAccessToken(TestUsers.superManager); + const neutralObserverUser = await IModelTestUtils.getTestUserAccessToken(TestUsers.manager); + + const firstIModel: IModelDb = await IModelDb.open(actx, firstUser, testProjectId, readWriteTestIModel.id, OpenParams.pullAndPush()); + const secondIModel: IModelDb = await IModelDb.open(actx, secondUser, testProjectId, readWriteTestIModel.id, OpenParams.pullAndPush()); + const neutralObserverIModel: IModelDb = await IModelDb.open(actx, neutralObserverUser, testProjectId, readWriteTestIModel.id, OpenParams.pullOnly()); + assert.notEqual(firstIModel, secondIModel); + + // Set up optimistic concurrency. Note the defaults are: + firstIModel.concurrencyControl.setPolicy(new ConcurrencyControl.OptimisticPolicy()); + secondIModel.concurrencyControl.setPolicy(new ConcurrencyControl.OptimisticPolicy()); + // Note: neutralObserver's IModel does not need to be configured for optimistic concurrency. He just pulls changes. + + // firstUser: create model, category, and element el1 + const r: { modelId: Id64String, spatialCategoryId: Id64String } = await createNewModelAndCategory(firstIModel, firstUser); + const el1 = firstIModel.elements.insertElement(IModelTestUtils.createPhysicalObject(firstIModel, r.modelId, r.spatialCategoryId)); + // const el2 = firstIModel.elements.insertElement(IModelTestUtils.createPhysicalObject(firstIModel, r.modelId, r.spatialCategoryId)); + firstIModel.saveChanges("firstUser created model, category, and two elements"); + await firstIModel.pushChanges(actx, firstUser); + + // secondUser: pull and merge + await secondIModel.pullAndMergeChanges(actx, secondUser); + + // --- Test 1: Overlapping changes that really are conflicts => conflict-resolution policy is applied --- + + // firstUser: modify el1.userLabel + if (true) { + const el1cc: Element = firstIModel.elements.getElement(el1); + el1cc.userLabel = el1cc.userLabel + " -> changed by firstUser"; + firstIModel.elements.updateElement(el1cc); + firstIModel.saveChanges("firstUser modified el1.userLabel"); + await firstIModel.pushChanges(actx, firstUser); + } + + // secondUser: modify el1.userLabel + let expectedValueofEl1UserLabel: string; + if (true) { + const el1before: Element = (secondIModel.elements.getElement(el1)); + expectedValueofEl1UserLabel = el1before.userLabel + " -> changed by secondUser"; + el1before.userLabel = expectedValueofEl1UserLabel; + secondIModel.elements.updateElement(el1before); + secondIModel.saveChanges("secondUser modified el1.userLabel"); + + // pull + merge => take secondUser's change (RejectIncomingChange). That's because the default updateVsUpdate settting is RejectIncomingChange + await secondIModel.pullAndMergeChanges(actx, secondUser); + const el1after = secondIModel.elements.getElement(el1); + assert.equal(el1after.userLabel, expectedValueofEl1UserLabel); + + await secondIModel.pushChanges(actx, secondUser); + } + + // Make sure a neutral observer sees secondUser's change. + if (true) { + await neutralObserverIModel.pullAndMergeChanges(actx, neutralObserverUser); + const elobj = neutralObserverIModel.elements.getElement(el1); + assert.equal(elobj.userLabel, expectedValueofEl1UserLabel); + } + + // firstUser: pull and see that secondUser has overridden my change + if (true) { + await firstIModel.pullAndMergeChanges(actx, firstUser); + const elobj = firstIModel.elements.getElement(el1); + assert.equal(elobj.userLabel, expectedValueofEl1UserLabel); + } + + // --- Test 2: Overlapping changes that are not conflicts --- + /* **************** No. We do not support property-level change-merging. + + // firstUser: modify el1.userLabel + const wasExpectedValueofEl1UserLabel = expectedValueofEl1UserLabel; + if (true) { + const el1cc: Element = firstIModel.elements.getElement(el1); + assert.equal(el1cc.userLabel, wasExpectedValueofEl1UserLabel); + expectedValueofEl1UserLabel = el1cc.userLabel + " -> changed again by firstUser"; + el1cc.userLabel = expectedValueofEl1UserLabel; + firstIModel.elements.updateElement(el1cc); + firstIModel.saveChanges("firstUser modified el1.userLabel"); + await firstIModel.pushChanges(actx, firstUser); + } + + // Make sure a neutral observer sees firstUser's changes. + if (true) { + await neutralObserverIModel.pullAndMergeChanges(actx, neutralObserverUser); + const elobj = neutralObserverIModel.elements.getElement(el1); + assert.equal(elobj.userLabel, expectedValueofEl1UserLabel); + } + + // secondUser: modify el1.userProperties + const secondUserPropNs = "secondUser"; + const secondUserPropName = "property"; + const expectedValueOfSecondUserProp: string = "x"; + if (true) { + const el1before: Element = secondIModel.elements.getElement(el1); + assert.equal(el1before.userLabel, wasExpectedValueofEl1UserLabel); + + el1before.setUserProperties(secondUserPropNs, { property: expectedValueOfSecondUserProp }); // secondUser changes userProperties + secondIModel.elements.updateElement(el1before); + secondIModel.saveChanges("secondUser modified el1.userProperties"); + assert.equal(el1before.userLabel, wasExpectedValueofEl1UserLabel, "secondUser does not change userLabel"); + + // pull + merge => no conflict + both changes should be intact + await secondIModel.pullAndMergeChanges(actx, secondUser); + const el1after = secondIModel.elements.getElement(el1); + assert.equal(el1after.userLabel, expectedValueofEl1UserLabel); + assert.equal(el1after.getUserProperties(secondUserPropNs)[secondUserPropName], expectedValueOfSecondUserProp); + + await secondIModel.pushChanges(actx, secondUser); + } + + // firstUser: pull and see both changes + if (true) { + await firstIModel.pullAndMergeChanges(actx, firstUser); + const elobj = firstIModel.elements.getElement(el1); + assert.equal(elobj.userLabel, expectedValueofEl1UserLabel); + assert.equal(elobj.getUserProperties(secondUserPropNs)[secondUserPropName], expectedValueOfSecondUserProp); + } + + // Make sure a neutral observer sees both changes. + if (true) { + await neutralObserverIModel.pullAndMergeChanges(actx, neutralObserverUser); + const elobj = neutralObserverIModel.elements.getElement(el1); + assert.equal(elobj.userLabel, expectedValueofEl1UserLabel); + assert.equal(elobj.getUserProperties(secondUserPropNs)[secondUserPropName], expectedValueOfSecondUserProp); + } + */ + // --- Test 3: Non-overlapping changes --- + + }); + + // Does not work with mocks + it.skip("should build concurrency control request", async () => { + const iModel: IModelDb = await IModelDb.open(actx, accessToken, testProjectId, readWriteTestIModel.id, OpenParams.pullAndPush()); + + const el: Element = iModel.elements.getRootSubject(); + el.buildConcurrencyControlRequest(DbOpcode.Update); // make a list of the locks, etc. that will be needed to update this element + const reqAsAny: any = ConcurrencyControl.convertRequestToAny(iModel.concurrencyControl.pendingRequest); + assert.isDefined(reqAsAny); + assert.isArray(reqAsAny.Locks); + assert.equal(reqAsAny.Locks.length, 3, " we expect to need a lock on the element (exclusive), its model (shared), and the db itself (shared)"); + assert.isArray(reqAsAny.Codes); + assert.equal(reqAsAny.Codes.length, 0, " since we didn't add or change the element's code, we don't expect to need a code reservation"); + + await iModel.close(actx, accessToken); + }); + + it("should push changes with codes (#integration)", async () => { + const adminAccessToken = await IModelTestUtils.getTestUserAccessToken(TestUsers.superManager); + let timer = new Timer("delete iModels"); + // Delete any existing iModels with the same name as the read-write test iModel + const iModelName = "CodesPushTest"; + const iModels: HubIModel[] = await BriefcaseManager.imodelClient.iModels.get(actx, adminAccessToken, writeTestProjectId, new IModelQuery().byName(iModelName)); + for (const iModelTemp of iModels) { + await BriefcaseManager.imodelClient.iModels.delete(actx, adminAccessToken, writeTestProjectId, iModelTemp.id!); + } + timer.end(); + + // Create a new empty iModel on the Hub & obtain a briefcase + timer = new Timer("create iModel"); + const rwIModel: IModelDb = await IModelDb.create(actx, adminAccessToken, writeTestProjectId, iModelName, { rootSubject: { name: "TestSubject" } }); + const rwIModelId = rwIModel.iModelToken.iModelId; + assert.isNotEmpty(rwIModelId); + timer.end(); + + timer = new Timer("querying codes"); + const initialCodes = await BriefcaseManager.imodelClient.codes.get(actx, adminAccessToken, rwIModelId!); + timer.end(); + + timer = new Timer("make local changes"); + const code = IModelTestUtils.getUniqueModelCode(rwIModel, "newPhysicalModel"); + IModelTestUtils.createAndInsertPhysicalPartitionAndModel(rwIModel, code, true); + + rwIModel.saveChanges("inserted generic objects"); + timer.end(); + + timer = new Timer("push changes"); + + // Push the changes to the hub + const prePushChangeSetId = rwIModel.iModelToken.changeSetId; + await rwIModel.pushChanges(actx, adminAccessToken); + const postPushChangeSetId = rwIModel.iModelToken.changeSetId; + assert(!!postPushChangeSetId); + expect(prePushChangeSetId !== postPushChangeSetId); + + timer.end(); + + timer = new Timer("querying codes"); + const codes = await BriefcaseManager.imodelClient.codes.get(actx, adminAccessToken, rwIModelId!); + timer.end(); + expect(codes.length > initialCodes.length); + }); + + it("should push changes with code conflicts (#integration)", async () => { + const adminAccessToken = await IModelTestUtils.getTestUserAccessToken(TestUsers.superManager); + let timer = new Timer("delete iModels"); + // Delete any existing iModels with the same name as the read-write test iModel + const iModelName = "CodesConflictTest"; + const iModels: HubIModel[] = await BriefcaseManager.imodelClient.iModels.get(actx, adminAccessToken, writeTestProjectId, new IModelQuery().byName(iModelName)); + for (const iModelTemp of iModels) { + await BriefcaseManager.imodelClient.iModels.delete(actx, adminAccessToken, writeTestProjectId, iModelTemp.id!); + } + timer.end(); + + // Create a new empty iModel on the Hub & obtain a briefcase + timer = new Timer("create iModel"); + const rwIModel: IModelDb = await IModelDb.create(actx, adminAccessToken, writeTestProjectId, iModelName, { rootSubject: { name: "TestSubject" } }); + const rwIModelId = rwIModel.iModelToken.iModelId; + assert.isNotEmpty(rwIModelId); + timer.end(); + + const code = IModelTestUtils.getUniqueModelCode(rwIModel, "newPhysicalModel"); + const otherBriefcase = await BriefcaseManager.imodelClient.briefcases.create(actx, adminAccessToken, rwIModelId!); + const hubCode = new HubCode(); + hubCode.value = code.value; + hubCode.codeSpecId = code.spec; + hubCode.codeScope = code.scope; + hubCode.briefcaseId = otherBriefcase.briefcaseId; + hubCode.state = CodeState.Reserved; + await BriefcaseManager.imodelClient.codes.update(actx, adminAccessToken, rwIModelId!, [hubCode]); + + timer = new Timer("querying codes"); + const initialCodes = await BriefcaseManager.imodelClient.codes.get(actx, adminAccessToken, rwIModelId!); + timer.end(); + + timer = new Timer("make local changes"); + IModelTestUtils.createAndInsertPhysicalPartitionAndModel(rwIModel, code, true); + + rwIModel.saveChanges("inserted generic objects"); + timer.end(); + + timer = new Timer("push changes"); + + // Push the changes to the hub + const prePushChangeSetId = rwIModel.iModelToken.changeSetId; + await rwIModel.pushChanges(actx, adminAccessToken); + const postPushChangeSetId = rwIModel.iModelToken.changeSetId; + assert(!!postPushChangeSetId); + expect(prePushChangeSetId !== postPushChangeSetId); + + timer.end(); + + timer = new Timer("querying codes"); + const codes = await BriefcaseManager.imodelClient.codes.get(actx, accessToken, rwIModelId!); + timer.end(); + expect(codes.length === initialCodes.length); + expect(codes[0].state === CodeState.Reserved); + }); + + it("should write to briefcase with optimistic concurrency (#integration)", async () => { + const adminAccessToken = await IModelTestUtils.getTestUserAccessToken(TestUsers.superManager); + + let timer = new Timer("delete iModels"); + // Delete any existing iModels with the same name as the OptimisticConcurrencyTest iModel + const iModelName = "OptimisticConcurrencyTest"; + const iModels: HubIModel[] = await BriefcaseManager.imodelClient.iModels.get(actx, adminAccessToken, writeTestProjectId, new IModelQuery().byName(iModelName)); + for (const iModelTemp of iModels) { + await BriefcaseManager.imodelClient.iModels.delete(actx, adminAccessToken, writeTestProjectId, iModelTemp.id!); + } + timer.end(); + + // Create a new empty iModel on the Hub & obtain a briefcase + timer = new Timer("create iModel"); + const rwIModel: IModelDb = await IModelDb.create(actx, adminAccessToken, writeTestProjectId, iModelName, { rootSubject: { name: "TestSubject" } }); + const rwIModelId = rwIModel.iModelToken.iModelId; + assert.isNotEmpty(rwIModelId); + timer.end(); + + timer = new Timer("make local changes"); + + // Turn on optimistic concurrency control. This allows the app to modify elements, models, etc. without first acquiring locks. + // Later, when the app downloads and merges changeSets from the Hub into the briefcase, BriefcaseManager will merge changes and handle conflicts. + // The app still has to reserve codes. + rwIModel.concurrencyControl.setPolicy(new ConcurrencyControl.OptimisticPolicy()); + + // Show that we can modify the properties of an element. In this case, we modify the root element itself. + const rootEl: Element = rwIModel.elements.getRootSubject(); + rootEl.userLabel = rootEl.userLabel + "changed"; + rwIModel.elements.updateElement(rootEl); + + assert.isFalse(rwIModel.concurrencyControl.hasPendingRequests); + + rwIModel.saveChanges(JSON.stringify({ userid: "user1", description: "changed a userLabel" })); // save it, to show that saveChanges will accumulate local txn descriptions + + // Create a new physical model. + let newModelId: Id64String; + [, newModelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(rwIModel, IModelTestUtils.getUniqueModelCode(rwIModel, "newPhysicalModel"), true); + + // Find or create a SpatialCategory. + const dictionary: DictionaryModel = rwIModel.models.getModel(IModel.dictionaryId) as DictionaryModel; + const newCategoryCode = IModelTestUtils.getUniqueSpatialCategoryCode(dictionary, "ThisTestSpatialCategory"); + const spatialCategoryId: Id64String = SpatialCategory.insert(rwIModel, IModel.dictionaryId, newCategoryCode.value!, new SubCategoryAppearance({ color: 0xff0000 })); + + timer.end(); + + timer = new Timer("query Codes I"); + + // iModel.concurrencyControl should have recorded the codes that are required by the new elements. + assert.isTrue(rwIModel.concurrencyControl.hasPendingRequests); + assert.isTrue(await rwIModel.concurrencyControl.areAvailable(actx, adminAccessToken)); + + timer.end(); + timer = new Timer("reserve Codes"); + + // Reserve all of the codes that are required by the new model and category. + try { + await rwIModel.concurrencyControl.request(actx, adminAccessToken); + } catch (err) { + if (err instanceof ConcurrencyControl.RequestError) { + assert.fail(JSON.stringify(err.unavailableCodes) + ", " + JSON.stringify(err.unavailableLocks)); + } + } + + timer.end(); + timer = new Timer("query Codes II"); + + // Verify that the codes are reserved. + const category = rwIModel.elements.getElement(spatialCategoryId); + assert.isTrue(category.code.value !== undefined); + const codeStates: MultiCode[] = await rwIModel.concurrencyControl.codes.query(actx, adminAccessToken, category.code.spec, category.code.scope); + const foundCode: MultiCode[] = codeStates.filter((cs) => (cs.value === category.code.value!) && (cs.state === CodeState.Reserved)); + assert.equal(foundCode.length, 1); + + /* NEEDS WORK - query just this one code + assert.isTrue(category.code.value !== undefined); + const codeStates2 = await iModel.concurrencyControl.codes.query(adminAccessToken, category.code.spec, category.code.scope, category.code.value!); + assert.equal(codeStates2.length, 1); + assert.equal(codeStates2[0].values.length, 1); + assert.equal(codeStates2[0].values[0], category.code.value!); + */ + + timer.end(); + + timer = new Timer("make more local changes"); + + // Create a couple of physical elements. + const elid1 = rwIModel.elements.insertElement(IModelTestUtils.createPhysicalObject(rwIModel, newModelId, spatialCategoryId)); + rwIModel.elements.insertElement(IModelTestUtils.createPhysicalObject(rwIModel, newModelId, spatialCategoryId)); + + // Commit the local changes to a local transaction in the briefcase. + rwIModel.saveChanges(JSON.stringify({ userid: "user1", description: "inserted generic objects" })); + + rwIModel.elements.getElement(elid1); // throws if elid1 is not found + rwIModel.elements.getElement(spatialCategoryId); // throws if spatialCategoryId is not found + + timer.end(); + + timer = new Timer("pullmergepush"); + + // Push the changes to the hub + const prePushChangeSetId = rwIModel.iModelToken.changeSetId; + await rwIModel.pushChanges(actx, adminAccessToken); + const postPushChangeSetId = rwIModel.iModelToken.changeSetId; + assert(!!postPushChangeSetId); + expect(prePushChangeSetId !== postPushChangeSetId); + + timer.end(); + + // Open a readonly copy of the iModel + const roIModel: IModelDb = await IModelDb.open(actx, adminAccessToken, writeTestProjectId, rwIModelId!, OpenParams.fixedVersion(), IModelVersion.latest()); + assert.exists(roIModel); + + await rwIModel.close(actx, adminAccessToken, KeepBriefcase.No); + await roIModel.close(actx, adminAccessToken); + }); + + it("Run plain SQL against pull-only connection", async () => { + const iModel: IModelDb = await IModelDb.open(actx, accessToken, testProjectId, readOnlyTestIModel.id, OpenParams.pullOnly()); + try { + iModel.withPreparedSqliteStatement("CREATE TABLE Test(Id INTEGER PRIMARY KEY, Name TEXT NOT NULL, Code INTEGER)", (stmt: SqliteStatement) => { + assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE); + }); + + iModel.withPreparedSqliteStatement("INSERT INTO Test(Name,Code) VALUES(?,?)", (stmt: SqliteStatement) => { + stmt.bindValue(1, "Dummy 1"); + stmt.bindValue(2, 100); + assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE); + }); + + iModel.withPreparedSqliteStatement("INSERT INTO Test(Name,Code) VALUES(?,?)", (stmt: SqliteStatement) => { + stmt.bindValues(["Dummy 2", 200]); + assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE); + }); + + iModel.withPreparedSqliteStatement("INSERT INTO Test(Name,Code) VALUES(:p1,:p2)", (stmt: SqliteStatement) => { + stmt.bindValue(":p1", "Dummy 3"); + stmt.bindValue(":p2", 300); + assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE); + }); + + iModel.withPreparedSqliteStatement("INSERT INTO Test(Name,Code) VALUES(:p1,:p2)", (stmt: SqliteStatement) => { + stmt.bindValues({ ":p1": "Dummy 4", ":p2": 400 }); + assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE); + }); + + iModel.saveChanges(); + + iModel.withPreparedSqliteStatement("SELECT Id,Name,Code FROM Test ORDER BY Id", (stmt: SqliteStatement) => { + for (let i: number = 1; i <= 4; i++) { + assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); + assert.equal(stmt.getColumnCount(), 3); + const val0: SqliteValue = stmt.getValue(0); + assert.equal(val0.columnName, "Id"); + assert.equal(val0.type, SqliteValueType.Integer); + assert.isFalse(val0.isNull); + assert.equal(val0.getInteger(), i); + + const val1: SqliteValue = stmt.getValue(1); + assert.equal(val1.columnName, "Name"); + assert.equal(val1.type, SqliteValueType.String); + assert.isFalse(val1.isNull); + assert.equal(val1.getString(), `Dummy ${i}`); + + const val2: SqliteValue = stmt.getValue(2); + assert.equal(val2.columnName, "Code"); + assert.equal(val2.type, SqliteValueType.Integer); + assert.isFalse(val2.isNull); + assert.equal(val2.getInteger(), i * 100); + + const row: any = stmt.getRow(); + assert.equal(row.id, i); + assert.equal(row.name, `Dummy ${i}`); + assert.equal(row.code, i * 100); + } + assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE); + }); + } finally { + // delete the briefcase as the test modified it locally. + let briefcasePath: string | undefined; + if (!!iModel.briefcase) + briefcasePath = iModel.briefcase.pathname; + + await iModel.close(actx, accessToken, KeepBriefcase.No); + if (!!briefcasePath && IModelJsFs.existsSync(briefcasePath)) + IModelJsFs.unlinkSync(briefcasePath); + } + }); + + it("Run plain SQL against readonly connection", async () => { + const iModel: IModelDb = await IModelDb.open(actx, accessToken, testProjectId, readOnlyTestIModel.id, OpenParams.fixedVersion()); + + iModel.withPreparedSqliteStatement("SELECT Name,StrData FROM be_Prop WHERE Namespace='ec_Db'", (stmt: SqliteStatement) => { + let rowCount: number = 0; + while (stmt.step() === DbResult.BE_SQLITE_ROW) { + rowCount++; + assert.equal(stmt.getColumnCount(), 2); + const nameVal: SqliteValue = stmt.getValue(0); + assert.equal(nameVal.columnName, "Name"); + assert.equal(nameVal.type, SqliteValueType.String); + assert.isFalse(nameVal.isNull); + const name: string = nameVal.getString(); + + const versionVal: SqliteValue = stmt.getValue(1); + assert.equal(versionVal.columnName, "StrData"); + assert.equal(versionVal.type, SqliteValueType.String); + assert.isFalse(versionVal.isNull); + const profileVersion: any = JSON.parse(versionVal.getString()); + + assert.isTrue(name === "SchemaVersion" || name === "InitialSchemaVersion"); + if (name === "SchemaVersion") { + assert.equal(profileVersion.major, 4); + assert.equal(profileVersion.minor, 0); + assert.equal(profileVersion.sub1, 0); + assert.isAtLeast(profileVersion.sub2, 1); + } else if (name === "InitialSchemaVersion") { + assert.equal(profileVersion.major, 4); + assert.equal(profileVersion.minor, 0); + assert.equal(profileVersion.sub1, 0); + assert.isAtLeast(profileVersion.sub2, 1); + } + } + assert.equal(rowCount, 2); + }); + await iModel.close(actx, accessToken); + }); +}); diff --git a/core/backend/src/test/integration/OpenMemoizer.test.ts b/core/backend/src/test/integration/OpenMemoizer.test.ts index e25365d..71def90 100644 --- a/core/backend/src/test/integration/OpenMemoizer.test.ts +++ b/core/backend/src/test/integration/OpenMemoizer.test.ts @@ -39,7 +39,7 @@ describe("OpenIModelDbMemoizer (#integration)", () => { assert.isTrue(qp1.isPending); assert.notStrictEqual(qp3, qp1); - await pause(5000); // Hopefully it won't take more than 5 seconds to re-establish the cache from scratch (if necessary) + await pause(20000); // Hopefully it won't take more than 20 seconds to re-establish the cache from scratch (if necessary) assert.isTrue(qp1.isFulfilled); assert.isTrue(qp3.isFulfilled); assert.exists(qp1.result); diff --git a/core/backend/src/test/integration/PushRetry.test.ts b/core/backend/src/test/integration/PushRetry.test.ts index 8731f4e..e220628 100644 --- a/core/backend/src/test/integration/PushRetry.test.ts +++ b/core/backend/src/test/integration/PushRetry.test.ts @@ -10,8 +10,8 @@ import { IModelDb, OpenParams, BriefcaseManager, ChangeSummaryManager, ECSqlStat import { ConcurrencyControl } from "../../ConcurrencyControl"; import { HubIModel, IModelQuery, AccessToken, ChangeSetPostPushEvent, NamedVersionCreatedEvent } from "@bentley/imodeljs-clients"; import { HubUtility } from "./HubUtility"; -import * as utils from "./../../../../clients/lib/test/imodelhub/TestUtils"; -import { ResponseBuilder, RequestType, ScopeType } from "./../../../../clients/lib/test/ResponseBuilder"; +import * as utils from "./../../../../clients-backend/lib/test/imodelhub/TestUtils"; +import { ResponseBuilder, RequestType, ScopeType } from "./../../../../clients-backend/lib/test/ResponseBuilder"; import { createNewModelAndCategory } from "./IModelWrite.test"; import { TestConfig } from "../TestConfig"; import { TestPushUtility } from "./TestPushUtility"; diff --git a/core/backend/src/test/integration/TestPushUtility.ts b/core/backend/src/test/integration/TestPushUtility.ts index 6eb2d84..9214c7f 100644 --- a/core/backend/src/test/integration/TestPushUtility.ts +++ b/core/backend/src/test/integration/TestPushUtility.ts @@ -6,7 +6,7 @@ import { Id64String, ActivityLoggingContext, GuidString } from "@bentley/bentleyjs-core"; import { Point3d, Range3d, YawPitchRollAngles } from "@bentley/geometry-core"; import { AccessToken } from "@bentley/imodeljs-clients"; -import { IModelVersion, CodeScopeSpec, Code, ColorDef, IModel, GeometricElement3dProps, AxisAlignedBox3d } from "@bentley/imodeljs-common"; +import { IModelVersion, CodeScopeSpec, Code, ColorDef, IModel, GeometricElement3dProps } from "@bentley/imodeljs-common"; import { IModelDb, OpenParams, BriefcaseManager, CategorySelector, DisplayStyle3d, ModelSelector, OrthographicViewDefinition, PhysicalModel, SpatialCategory } from "../../imodeljs-backend"; import * as path from "path"; import * as fs from "fs"; @@ -78,7 +78,7 @@ export class TestPushUtility { const viewRange = new Range3d(0, 0, 0, 50, 50, 50); OrthographicViewDefinition.insert(this._iModelDb, definitionModelId, viewName, modelSelectorId, categorySelectorId, displayStyleId, viewRange); - this._iModelDb.updateProjectExtents(new AxisAlignedBox3d(new Point3d(-1000, -1000, -1000), new Point3d(1000, 1000, 1000))); + this._iModelDb.updateProjectExtents(new Range3d(-1000, -1000, -1000, 1000, 1000, 1000)); this.insertTestElement(this._currentLevel, 0); this.insertTestElement(this._currentLevel, 1); diff --git a/core/backend/src/test/standalone/DisableNativeAssertions.test.ts b/core/backend/src/test/standalone/DisableNativeAssertions.test.ts index bb8e1bd..3241d77 100644 --- a/core/backend/src/test/standalone/DisableNativeAssertions.test.ts +++ b/core/backend/src/test/standalone/DisableNativeAssertions.test.ts @@ -43,7 +43,7 @@ describe("DisableNativeAssertions", () => { using(ECDbTestHelper.createECDb(_outDir, "create.ecdb"), (ecdb: ECDb) => { assert.isTrue(ecdb.isOpen); - using(new DisableNativeAssertions(), () => { + using(new DisableNativeAssertions(), (_r) => { let hasThrown: boolean = false; try { // An invalid SQL is expected to fire an assertion in native code BeSQLite during preparation diff --git a/core/backend/src/test/standalone/ECSqlQuery.test.ts b/core/backend/src/test/standalone/ECSqlQuery.test.ts new file mode 100644 index 0000000..983618d --- /dev/null +++ b/core/backend/src/test/standalone/ECSqlQuery.test.ts @@ -0,0 +1,86 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { assert } from "chai"; +import { IModelTestUtils } from "../IModelTestUtils"; +import { IModelDb } from "../../IModelDb"; + +describe("ECSql Query", () => { + let imodel1: IModelDb; + let imodel2: IModelDb; + let imodel3: IModelDb; + let imodel4: IModelDb; + let imodel5: IModelDb; + + before(async () => { + imodel1 = IModelTestUtils.openIModel("test.bim"); + imodel2 = IModelTestUtils.openIModel("CompatibilityTestSeed.bim"); + imodel3 = IModelTestUtils.openIModel("GetSetAutoHandledStructProperties.bim"); + imodel4 = IModelTestUtils.openIModel("GetSetAutoHandledArrayProperties.bim"); + imodel5 = IModelTestUtils.openIModel("mirukuru.ibim"); + }); + + after(async () => { + IModelTestUtils.closeIModel(imodel1); + IModelTestUtils.closeIModel(imodel2); + IModelTestUtils.closeIModel(imodel3); + IModelTestUtils.closeIModel(imodel4); + IModelTestUtils.closeIModel(imodel5); + }); + + it("Paging Results", async () => { + const getRowPerPage = (nPageSize: number, nRowCount: number) => { + const nRowPerPage = nRowCount / nPageSize; + const nPages = Math.ceil(nRowPerPage); + const nRowOnLastPage = nRowCount - (Math.floor(nRowPerPage) * pageSize); + const pages = new Array(nPages).fill(pageSize); + if (nRowPerPage) { + pages[nPages - 1] = nRowOnLastPage; + } + return pages; + }; + + const pageSize = 5; + const query = "SELECT ECInstanceId as Id, Parent.Id as ParentId FROM BisCore.element"; + const dbs = [imodel1, imodel2, imodel3, imodel4, imodel5]; + const pendingRowCount = []; + for (const db of dbs) { + pendingRowCount.push(db.queryRowCount(query)); + } + + const rowCounts = await Promise.all(pendingRowCount); + const expected = [46, 62, 7, 7, 28]; + assert.equal(rowCounts.length, expected.length); + for (let i = 0; i < expected.length; i++) { + assert.equal(rowCounts[i], expected[i]); + } + // verify row per page + for (const db of dbs) { + const i = dbs.indexOf(db); + const rowPerPage = getRowPerPage(pageSize, expected[i]); + for (let k = 0; k < rowPerPage.length; k++) { + const row = await db.queryPage(query, undefined, { size: pageSize, start: k }); + assert.equal(row.length, rowPerPage[k]); + } + } + + // verify async iterator + for (const db of dbs) { + const resultSet = []; + for await (const row of db.query(query, undefined, { size: pageSize })) { + resultSet.push(row); + assert.isTrue(Reflect.has(row, "id")); + if (Reflect.ownKeys(row).length > 1) { + assert.isTrue(Reflect.has(row, "parentId")); + const parentId: string = row.parentId; + assert.isTrue(parentId.startsWith("0x")); + } + const id: string = row.id; + assert.isTrue(id.startsWith("0x")); + } + const entry = dbs.indexOf(db); + assert.equal(rowCounts[entry], resultSet.length); + } + }); +}); diff --git a/core/backend/src/test/standalone/ECSqlStatement.test.ts b/core/backend/src/test/standalone/ECSqlStatement.test.ts index 7264ab9..24c9efe 100644 --- a/core/backend/src/test/standalone/ECSqlStatement.test.ts +++ b/core/backend/src/test/standalone/ECSqlStatement.test.ts @@ -18,6 +18,129 @@ describe("ECSqlStatement", () => { const testRange = new Range3d(1.2, 2.3, 3.4, 4.5, 5.6, 6.7); const blobVal = new Uint8Array(testRange.toFloat64Array().buffer); + it("Asynchronous step and stepForInsert Methods", async () => { + await using(ECDbTestHelper.createECDb(_outDir, "asyncmethodtest.ecdb", + ` + + + + + + `), async (ecdb: ECDb) => { + assert.isTrue(ecdb.isOpen); + + const r: ECSqlInsertResult = await ecdb.withPreparedStatement("INSERT INTO ts.Foo(n,dt,fooId) VALUES(20,TIMESTAMP '2018-10-18T12:00:00Z',20)", async (stmt: ECSqlStatement) => { + return stmt.stepForInsertAsync(); + }); + assert.equal(r.status, DbResult.BE_SQLITE_DONE); + assert.equal(r.id, "0x1"); + + const s: DbResult = await ecdb.withPreparedStatement("SELECT n, dt, fooId FROM ts.Foo", async (stmt: ECSqlStatement) => { + return stmt.stepAsync(); + }); + assert.equal(s, DbResult.BE_SQLITE_ROW); + }); + }); + it("Primary Key Binding through array", async () => { + await using(ECDbTestHelper.createECDb(_outDir, "bindingTest.ecdb", + ` + + + + `), async (ecdb: ECDb) => { + assert.isTrue(ecdb.isOpen); + const rowIds = ["0x1000000004c", "0x100000000ea", "0x200000000ff", "0x31", "0xffffffffffffff01"]; + for (const rowId of rowIds) { + const r: ECSqlInsertResult = await ecdb.withPreparedStatement(`insert into ts.Foo(ECInstanceId) values(?)`, async (stmt: ECSqlStatement) => { + stmt.bindId(1, rowId); + return stmt.stepForInsertAsync(); + }); + assert.equal(r.status, DbResult.BE_SQLITE_DONE); + } + const args = new Array(rowIds.length).fill("?").join(","); + let row = await ecdb.queryPage(`SELECT * FROM ts.Foo WHERE ECInstanceId IN (${args})`, rowIds); + assert.equal(row.length, rowIds.length); + assert.isTrue(Reflect.has(row[0], "id")); + row = await ecdb.queryPage(`SELECT * FROM (SELECT ECInstanceId AS id FROM ts.Foo) WHERE id IN (${args})`, rowIds); + assert.equal(row.length, rowIds.length); + assert.isTrue(Reflect.has(row[0], "id")); + assert.isTrue(String(row[0].id).startsWith("0x")); + row = await ecdb.queryPage(`SELECT * FROM (SELECT ECInstanceId AS sap FROM ts.Foo) WHERE sap IN (${args})`, rowIds); + assert.isTrue(Reflect.has(row[0], "sap")); + assert.equal(row.length, rowIds.length); + assert.isTrue(String(row[0].sap).startsWith("0x")); + + }); + }); + it("Paging Resultset", async () => { + await using(ECDbTestHelper.createECDb(_outDir, "pagingresultset.ecdb", + ` + + + + `), async (ecdb: ECDb) => { + assert.isTrue(ecdb.isOpen); + const ROW_COUNT = 27; + // insert test rows + for (let i = 1; i <= ROW_COUNT; i++) { + const r: ECSqlInsertResult = await ecdb.withPreparedStatement(`insert into ts.Foo(n) values(${i})`, async (stmt: ECSqlStatement) => { + return stmt.stepForInsertAsync(); + }); + assert.equal(r.status, DbResult.BE_SQLITE_DONE); + } + for (let i = 1; i < ROW_COUNT; i++) { + const rowCount = await ecdb.queryRowCount("SELECT ECInstanceId, ECClassId, n FROM ts.Foo WHERE n <= ?", [i]); + assert.equal(rowCount, i); + } + + const temp = await ecdb.queryPage("SELECT ECInstanceId FROM ONLY ts.Foo"); + assert.equal(temp.length, ROW_COUNT); + // query page by page + const PAGE_SIZE = 5; + const QUERY = "SELECT n FROM ts.Foo"; + const EXPECTED_ROW_COUNT = [5, 5, 5, 5, 5, 2, 0]; + const ready = []; + for (let i = 0; i < EXPECTED_ROW_COUNT.length; i++) { + ready.push(ecdb.queryPage(QUERY, undefined, { start: i, size: PAGE_SIZE })); + } + // verify if each page has right count of rows + const results = await Promise.all(ready); + for (let i = 0; i < EXPECTED_ROW_COUNT.length; i++) { + assert.equal(results[i].length, EXPECTED_ROW_COUNT[i]); + } + }); + }); + it("Paging use cache statement", async () => { + await using(ECDbTestHelper.createECDb(_outDir, "pagingresultset.ecdb", + ` + + + + `), async (ecdb: ECDb) => { + assert.isTrue(ecdb.isOpen); + const ROW_COUNT = 27; + // insert test rows + for (let i = 1; i <= ROW_COUNT; i++) { + const r: ECSqlInsertResult = await ecdb.withPreparedStatement(`insert into ts.Foo(n) values(${i})`, async (stmt: ECSqlStatement) => { + return stmt.stepForInsertAsync(); + }); + assert.equal(r.status, DbResult.BE_SQLITE_DONE); + } + + // check if varying page number does not require prepare new statements + ecdb.clearStatementCache(); + for (const testPageSize of [1, 2, 4, 5, 6, 7, 10, ROW_COUNT]) { + let rowNo = 1; + for await (const row of ecdb.query("SELECT n FROM ts.Foo WHERE n != ? and ECInstanceId < ?", [123, 30], { size: testPageSize })) { + assert.equal(row.n, rowNo); + rowNo = rowNo + 1; + } + assert.equal(rowNo, 28); // expect all rows + assert.equal(1, ecdb.getCachedStatementCount()); // there must be single cached statement used with differetn size pages. + } + }); + }); + it("Bind Ids", () => { using(ECDbTestHelper.createECDb(_outDir, "bindids.ecdb"), (ecdb: ECDb) => { diff --git a/core/backend/src/test/standalone/IModel.test.ts b/core/backend/src/test/standalone/IModel.test.ts index 691a573..7fcff0f 100644 --- a/core/backend/src/test/standalone/IModel.test.ts +++ b/core/backend/src/test/standalone/IModel.test.ts @@ -2,13 +2,37 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { ActivityLoggingContext, BeEvent, DbResult, Guid, Id64, Id64String, OpenMode } from "@bentley/bentleyjs-core"; -import { Angle, Matrix4d, Point3d, Range3d, Transform } from "@bentley/geometry-core"; +import { + ActivityLoggingContext, + BeEvent, + DbResult, + Guid, + Id64, + Id64String, + OpenMode, + LogLevel, + Logger, + using, +} from "@bentley/bentleyjs-core"; +import { + Angle, + GeometryQuery, + LineString3d, + Loop, + Matrix4d, + Point3d, + Range3d, + Transform, + StrokeOptions, + PolyfaceBuilder, + YawPitchRollAngles, +} from "@bentley/geometry-core"; import { AccessToken, IAccessTokenManager } from "@bentley/imodeljs-clients"; import { AxisAlignedBox3d, Code, CodeScopeSpec, CodeSpec, ColorByName, EntityMetaData, EntityProps, FilePropertyProps, FontMap, FontType, GeometricElementProps, IModel, IModelError, IModelStatus, PrimitiveTypeCode, RelatedElement, SubCategoryAppearance, - ViewDefinitionProps, DisplayStyleSettingsProps, ColorDef, ViewFlags, RenderMode, DisplayStyleProps, BisCodeSpec, ImageSourceFormat, TextureFlags, + ViewDefinitionProps, DisplayStyleSettingsProps, ColorDef, ViewFlags, RenderMode, DisplayStyleProps, BisCodeSpec, ImageSourceFormat, + TextureFlags, TextureMapping, TextureMapProps, TextureMapUnits, GeometryStreamBuilder, GeometricElement3dProps, GeometryParams, } from "@bentley/imodeljs-common"; import { assert, expect } from "chai"; import * as path from "path"; @@ -16,17 +40,33 @@ import { AutoPush, AutoPushParams, AutoPushEventHandler, AutoPushEventType, AutoPushState, BisCore, Category, ClassRegistry, DefinitionPartition, DictionaryModel, DocumentPartition, ECSqlStatement, Element, ElementGroupsMembers, ElementPropertyFormatter, Entity, GeometricElement2d, GeometricElement3d, GeometricModel, GroupInformationPartition, IModelDb, InformationPartitionElement, - LightLocation, LinkPartition, Model, PhysicalModel, PhysicalPartition, SpatialCategory, SqliteStatement, SqliteValue, + LightLocation, LinkPartition, Model, PhysicalModel, PhysicalPartition, RenderMaterial, SpatialCategory, SqliteStatement, SqliteValue, SqliteValueType, SubCategory, Subject, Texture, ViewDefinition, DisplayStyle3d, ElementDrivesElement, PhysicalObject, } from "../../imodeljs-backend"; -import { IModelTestUtils } from "../IModelTestUtils"; +import { DisableNativeAssertions, IModelTestUtils } from "../IModelTestUtils"; import { KnownTestLocations } from "../KnownTestLocations"; +import { HubUtility } from "../integration/HubUtility"; let lastPushTimeMillis = 0; let lastAutoPushEventType: AutoPushEventType | undefined; // spell-checker: disable +async function getIModelError(promise: Promise): Promise { + try { + await promise; + return undefined; + } catch (err) { + return err instanceof IModelError ? err : undefined; + } +} + +function expectIModelError(expectedErrorNumber: IModelStatus, error: IModelError | undefined): void { + expect(error).not.to.be.undefined; + expect(error).instanceof(IModelError); + expect(error!.errorNumber).to.equal(expectedErrorNumber); +} + describe("iModel", () => { let imodel1: IModelDb; let imodel2: IModelDb; @@ -68,6 +108,13 @@ describe("iModel", () => { assert.equal(s1, s2); }; + it.skip("dump cs file", () => { + Logger.setLevel("DgnCore", LogLevel.Trace); + Logger.setLevel("Changeset", LogLevel.Trace); + const db = IModelDb.openStandalone("D:\\dgn\\problem\\83927\\EAP_TT_001\\seed\\EAP_TT_001.bim"); + HubUtility.dumpChangeSetFile(db, "D:\\dgn\\problem\\83927\\EAP_TT_001", "9fd0e30f88e93bec72532f6f1e05688e2c2408cd"); + }); + 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: AxisAlignedBox3d = imodel1.projectExtents; @@ -206,11 +253,11 @@ describe("iModel", () => { // This is an encoded png containing a 3x3 square with white in top left pixel, blue in middle pixel, and green in // bottom right pixel. The rest of the square is red. - const pngData: Uint8Array = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 3, 0, 0, 0, 3, 8, 2, 0, 0, 0, 217, 74, 34, 232, 0, 0, 0, 1, 115, 82, 71, 66, 0, 174, 206, 28, 233, 0, 0, 0, 4, 103, 65, 77, 65, 0, 0, 177, 143, 11, 252, 97, 5, 0, 0, 0, 9, 112, 72, 89, 115, 0, 0, 14, 195, 0, 0, 14, 195, 1, 199, 111, 168, 100, 0, 0, 0, 24, 73, 68, 65, 84, 24, 87, 99, 248, 15, 4, 12, 12, 64, 4, 198, 64, 46, 132, 5, 162, 254, 51, 0, 0, 195, 90, 10, 246, 127, 175, 154, 145, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130]); + const pngData = [137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 3, 0, 0, 0, 3, 8, 2, 0, 0, 0, 217, 74, 34, 232, 0, 0, 0, 1, 115, 82, 71, 66, 0, 174, 206, 28, 233, 0, 0, 0, 4, 103, 65, 77, 65, 0, 0, 177, 143, 11, 252, 97, 5, 0, 0, 0, 9, 112, 72, 89, 115, 0, 0, 14, 195, 0, 0, 14, 195, 1, 199, 111, 168, 100, 0, 0, 0, 24, 73, 68, 65, 84, 24, 87, 99, 248, 15, 4, 12, 12, 64, 4, 198, 64, 46, 132, 5, 162, 254, 51, 0, 0, 195, 90, 10, 246, 127, 175, 154, 145, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130]; const testTextureName = "fake texture name"; const testTextureFormat = ImageSourceFormat.Png; - const testTextureData = Base64.btoa(String.fromCharCode.apply(null, pngData)); + const testTextureData = Base64.btoa(String.fromCharCode(...pngData)); const testTextureWidth = 3; const testTextureHeight = 3; const testTextureDescription = "empty description"; @@ -228,6 +275,162 @@ describe("iModel", () => { expect(texture.flags).to.equal(testTextureFlags); }); + it("should insert a RenderMaterial", () => { + const model = imodel2.models.getModel(IModel.dictionaryId) as DictionaryModel; + expect(model).not.to.be.undefined; + + const testMaterialName = "test material name"; + const testPaletteName = "test palette name"; + const testDescription = "test description"; + const color = [25, 32, 9]; + const specularColor = [99, 255, 1]; + const finish = 0.4; + const transmit = 0.1; + const diffuse = 0.24; + const specular = 0.9; + const reflect = 0.3; + const reflectColor = [255, 0, 127]; + const textureMapProps: 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", + }; + + const renderMaterialParams = new RenderMaterial.Params(testPaletteName); + renderMaterialParams.description = testDescription; + renderMaterialParams.color = color; + renderMaterialParams.specularColor = specularColor; + renderMaterialParams.finish = finish; + renderMaterialParams.transmit = transmit; + renderMaterialParams.diffuse = diffuse; + renderMaterialParams.specular = specular; + renderMaterialParams.reflect = reflect; + renderMaterialParams.reflectColor = reflectColor; + renderMaterialParams.patternMap = textureMapProps; + const renderMaterialId = RenderMaterial.insert(imodel2, IModel.dictionaryId, testMaterialName, renderMaterialParams); + + const renderMaterial = imodel2.elements.getElement(renderMaterialId); + assert((renderMaterial instanceof RenderMaterial) === 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.skip("attempt to apply material to new element in imodel5", () => { + // This is an encoded png containing a 3x3 square with white in top left pixel, blue in middle pixel, and green in + // bottom right pixel. The rest of the square is red. + const pngData = [137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 3, 0, 0, 0, 3, 8, 2, 0, 0, 0, 217, 74, 34, 232, 0, 0, 0, 1, 115, 82, 71, 66, 0, 174, 206, 28, 233, 0, 0, 0, 4, 103, 65, 77, 65, 0, 0, 177, 143, 11, 252, 97, 5, 0, 0, 0, 9, 112, 72, 89, 115, 0, 0, 14, 195, 0, 0, 14, 195, 1, 199, 111, 168, 100, 0, 0, 0, 24, 73, 68, 65, 84, 24, 87, 99, 248, 15, 4, 12, 12, 64, 4, 198, 64, 46, 132, 5, 162, 254, 51, 0, 0, 195, 90, 10, 246, 127, 175, 154, 145, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130]; + + const testTextureName = "fake texture name"; + const testTextureFormat = ImageSourceFormat.Png; + const testTextureData = Base64.btoa(String.fromCharCode(...pngData)); + const testTextureWidth = 3; + const testTextureHeight = 3; + const testTextureDescription = "empty description"; + const testTextureFlags = TextureFlags.None; + + const texId = Texture.insert(imodel5, IModel.dictionaryId, testTextureName, testTextureFormat, testTextureData, testTextureWidth, testTextureHeight, testTextureDescription, testTextureFlags); + + const matId = RenderMaterial.insert(imodel5, IModel.dictionaryId, "test material name", + { + paletteName: "TestPaletteName", + patternMap: { + TextureId: texId, + pattern_offset: [0, 0], + pattern_scale: [1, 1], + pattern_scalemode: TextureMapUnits.Relative, + }, + }); + + /** Create a simple flat mesh with 4 points (2x2) */ + const width = imodel5.projectExtents.width * 0.2; + const height = imodel5.projectExtents.depth * 0.2; + let shape: GeometryQuery; + 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 }); + + /** 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 }; + + /** The [[GeometricElement3dProps]] */ + const props: GeometricElement3dProps = { + placement: { origin: imodel5.projectExtents.center, angles: new YawPitchRollAngles() }, + model: modelId, + code: Code.createEmpty(), + classFullName: "Generic:PhysicalObject", + category: categoryId, + geom: geometry, + }; + imodel5.elements.insertElement(props); + imodel5.saveChanges(); + }); + it("should insert a DisplayStyle", () => { const model = imodel2.models.getModel(IModel.dictionaryId) as DictionaryModel; expect(model).not.to.be.undefined; @@ -377,6 +580,29 @@ describe("iModel", () => { expect(tree.rootTile.contentRange).to.be.undefined; }); + it("should throw on invalid tile requests", async () => { + const logCtx = new ActivityLoggingContext("invalidTileRequests"); + await using(new DisableNativeAssertions(), async (_r) => { + let error = await getIModelError(imodel1.tiles.requestTileTreeProps(logCtx, "0x12345")); + expectIModelError(IModelStatus.MissingId, error); + + error = await getIModelError(imodel1.tiles.requestTileTreeProps(logCtx, "NotAValidId")); + expectIModelError(IModelStatus.InvalidId, error); + + error = await getIModelError(imodel1.tiles.requestTileContent(logCtx, "0x1c", "0/0/0/0")); + expectIModelError(IModelStatus.InvalidId, error); + + error = await getIModelError(imodel1.tiles.requestTileContent(logCtx, "0x12345", "0/0/0/0/1")); + expectIModelError(IModelStatus.MissingId, error); + + error = await getIModelError(imodel1.tiles.requestTileContent(logCtx, "0x1c", "V/W/X/Y/Z")); + expectIModelError(IModelStatus.InvalidId, error); + + error = await getIModelError(imodel1.tiles.requestTileContent(logCtx, "0x1c", "NotAValidId")); + expectIModelError(IModelStatus.InvalidId, error); + }); + }); + it("should produce an array of rows", () => { const rows: any[] = imodel1.executeQuery(`SELECT * FROM ${Category.classFullName}`); assert.exists(rows); @@ -562,7 +788,7 @@ describe("iModel", () => { assert.equal(testElem.classFullName, "DgnPlatformTest:TestElementWithNoHandler"); assert.isUndefined(testElem.integerProperty1); - const newTestElem = testElem.clone(); + const newTestElem = testElem.clone(); assert.equal(newTestElem.classFullName, testElem.classFullName); newTestElem.integerProperty1 = 999; assert.isTrue(testElem.arrayOfPoint3d[0].isAlmostEqual(newTestElem.arrayOfPoint3d[0])); @@ -607,6 +833,23 @@ describe("iModel", () => { assert.deepEqual(afterUpdateElemFetched.id, editElem.id, " the id should not have changed."); assert.deepEqual(afterUpdateElemFetched.p3d, wasp3d, " p3d property should not have changed"); + // Make array shorter + assert.equal(afterUpdateElemFetched.arrayOfInt.length, 300); + + afterUpdateElemFetched.arrayOfInt = [99, 3]; + imodel4.elements.updateElement(afterUpdateElemFetched); + + const afterShortenArray = imodel4.elements.getElement(afterUpdateElemFetched.id); + assert.equal(afterUpdateElemFetched.arrayOfInt.length, 2); + assert.deepEqual(afterShortenArray.arrayOfInt, [99, 3]); + + // Make array longer + afterShortenArray.arrayOfInt = [1, 2, 3]; + imodel4.elements.updateElement(afterShortenArray); + const afterLengthenArray = imodel4.elements.getElement(afterShortenArray.id); + assert.equal(afterLengthenArray.arrayOfInt.length, 3); + assert.deepEqual(afterLengthenArray.arrayOfInt, [1, 2, 3]); + // ------------ delete ----------------- const elid = afterUpdateElemFetched.id; imodel4.elements.deleteElement(elid); @@ -650,14 +893,14 @@ describe("iModel", () => { it("update the project extents", async () => { const originalExtents = imodel1.projectExtents; - const newExtents = new AxisAlignedBox3d(originalExtents.low, originalExtents.high); + const newExtents = Range3d.create(originalExtents.low, originalExtents.high); newExtents.low.x -= 50; newExtents.low.y -= 25; newExtents.low.z -= 189; newExtents.high.x += 1087; newExtents.high.y += 19; newExtents.high.z += .001; imodel1.updateProjectExtents(newExtents); const updatedProps = JSON.parse(imodel1.briefcase!.nativeDb.getIModelProps()); assert.isTrue(updatedProps.hasOwnProperty("projectExtents"), "Returned property JSON object has project extents"); - const updatedExtents = AxisAlignedBox3d.fromJSON(updatedProps.projectExtents); + const updatedExtents = Range3d.fromJSON(updatedProps.projectExtents); assert.isTrue(newExtents.isAlmostEqual(updatedExtents), "Project extents successfully updated in database"); }); @@ -700,13 +943,13 @@ describe("iModel", () => { const z2 = imodel5.ecefToSpatial(ecefPt); assert.isTrue(z2.isAlmostEqual(center), "ecefToSpatial"); - const carto = imodel5.spatialToCartographic(center); + const carto = imodel5.spatialToCartographicFromEcef(center); assert.approximately(Angle.radiansToDegrees(carto.longitude), 132.70599650539427, .1); // this data is in Japan assert.approximately(Angle.radiansToDegrees(carto.latitude), 34.35461328445589, .1); const c2 = { longitude: 2.316156576159219, latitude: 0.5996011150631385, height: 10 }; assert.isTrue(carto.equalsEpsilon(c2, .001), "spatialToCartographic"); - imodel5.cartographicToSpatial(carto, z2); + imodel5.cartographicToSpatialFromEcef(carto, z2); assert.isTrue(z2.isAlmostEqual(center, .001), "cartographicToSpatial"); }); diff --git a/core/backend/src/test/standalone/IModelImporter.test.ts b/core/backend/src/test/standalone/IModelImporter.test.ts index e828ce0..cb9f2e6 100644 --- a/core/backend/src/test/standalone/IModelImporter.test.ts +++ b/core/backend/src/test/standalone/IModelImporter.test.ts @@ -5,48 +5,94 @@ import * as path from "path"; import { assert } from "chai"; import { Id64, Id64String } from "@bentley/bentleyjs-core"; -import { Range2d, Range3d } from "@bentley/geometry-core"; -import { CodeScopeSpec, ColorDef, GeometricElement2dProps, IModel, SubCategoryAppearance, Code } from "@bentley/imodeljs-common"; +import { Box, LineString3d, Point3d, Range2d, Vector3d, YawPitchRollAngles, Point2d, StandardViewIndex, Range3d } from "@bentley/geometry-core"; +import { CodeScopeSpec, ColorDef, FontType, GeometricElement2dProps, GeometryStreamBuilder, GeometryStreamProps, IModel, SubCategoryAppearance, Code, GeometricElement3dProps, CategorySelectorProps, SubjectProps, SpatialViewDefinitionProps, ModelSelectorProps, AuxCoordSystem2dProps } from "@bentley/imodeljs-common"; import { - CategorySelector, DefinitionModel, DisplayStyle2d, DisplayStyle3d, DocumentListModel, Drawing, DrawingCategory, DrawingGraphic, DrawingGraphicRepresentsElement, DrawingViewDefinition, - IModelDb, IModelJsFs, ModelSelector, OrthographicViewDefinition, PhysicalModel, SpatialCategory, SubCategory, Subject, + AuxCoordSystem2d, CategorySelector, DefinitionModel, DisplayStyle2d, DisplayStyle3d, DocumentListModel, + Drawing, DrawingCategory, DrawingGraphic, DrawingGraphicRepresentsElement, DrawingViewDefinition, IModelDb, IModelImporter, IModelJsFs, InformationPartitionElement, + ModelSelector, OrthographicViewDefinition, PhysicalModel, PhysicalObject, Platform, SpatialCategory, SubCategory, Subject, } from "../../imodeljs-backend"; import { KnownTestLocations } from "../KnownTestLocations"; -class TestImporter { - public readonly iModelDb: IModelDb; +class TestDataManager { + public sourceDb: IModelDb; + public targetDb: IModelDb; public constructor() { const outputDir = KnownTestLocations.outputDir; if (!IModelJsFs.existsSync(outputDir)) IModelJsFs.mkdirSync(outputDir); - const outputFile: string = path.join(outputDir, "TestImporter.bim"); - if (IModelJsFs.existsSync(outputFile)) - IModelJsFs.removeSync(outputFile); - this.iModelDb = IModelDb.createStandalone(outputFile, { rootSubject: { name: "TestImporter" } }); - assert.isTrue(IModelJsFs.existsSync(outputFile)); + // Source IModelDb + const createdOutputFile: string = path.join(outputDir, "TestIModelImporter-Source.bim"); + if (IModelJsFs.existsSync(createdOutputFile)) + IModelJsFs.removeSync(createdOutputFile); + this.sourceDb = IModelDb.createStandalone(createdOutputFile, { rootSubject: { name: "TestIModelImporter-Source" } }); + assert.isTrue(IModelJsFs.existsSync(createdOutputFile)); + // Target IModelDb + const importedOutputFile: string = path.join(outputDir, "TestIModelImporter-Target.bim"); + if (IModelJsFs.existsSync(importedOutputFile)) + IModelJsFs.removeSync(importedOutputFile); + this.targetDb = IModelDb.createStandalone(importedOutputFile, { rootSubject: { name: "TestIModelImporter-Target" } }); + assert.isTrue(IModelJsFs.existsSync(importedOutputFile)); + // insert some elements to avoid getting same IDs for sourceDb and targetDb + const subjectId = Subject.insert(this.targetDb, IModel.rootSubjectId, "Only in Target"); + Subject.insert(this.targetDb, subjectId, "1"); + Subject.insert(this.targetDb, subjectId, "2"); + Subject.insert(this.targetDb, subjectId, "3"); + Subject.insert(this.targetDb, subjectId, "4"); } - public import(): void { - const codeSpecId: Id64String = this.iModelDb.codeSpecs.insert("CodeSpec", CodeScopeSpec.Type.Model); - assert.isTrue(Id64.isValidId64(codeSpecId)); - const subjectId: Id64String = Subject.insert(this.iModelDb, IModel.rootSubjectId, "Subject", "Subject description"); + public createSourceDb(): void { + if (Platform.platformName.startsWith("win")) { + this.sourceDb.embedFont({ id: 1, type: FontType.TrueType, name: "Arial" }); + assert.exists(this.sourceDb.fontMap.getFont("Arial")); + assert.exists(this.sourceDb.fontMap.getFont(1)); + } + const projectExtents = new Range3d(-1000, -1000, -1000, 1000, 1000, 1000); + this.sourceDb.updateProjectExtents(projectExtents); + const codeSpecId1: Id64String = this.sourceDb.codeSpecs.insert("CodeSpec1", CodeScopeSpec.Type.Model); + const codeSpecId2: Id64String = this.sourceDb.codeSpecs.insert("CodeSpec2", CodeScopeSpec.Type.ParentElement); + assert.isTrue(Id64.isValidId64(codeSpecId1)); + assert.isTrue(Id64.isValidId64(codeSpecId2)); + const subjectId = Subject.insert(this.sourceDb, IModel.rootSubjectId, "Subject", "Subject description"); assert.isTrue(Id64.isValidId64(subjectId)); - const definitionModelId: Id64String = DefinitionModel.insert(this.iModelDb, subjectId, "Definition"); + const sourceOnlySubjectId = Subject.insert(this.sourceDb, IModel.rootSubjectId, "Only in Source"); + assert.isTrue(Id64.isValidId64(sourceOnlySubjectId)); + const definitionModelId = DefinitionModel.insert(this.sourceDb, subjectId, "Definition"); assert.isTrue(Id64.isValidId64(definitionModelId)); - const physicalModelId: Id64String = PhysicalModel.insert(this.iModelDb, subjectId, "Physical"); + const physicalModelId = PhysicalModel.insert(this.sourceDb, subjectId, "Physical"); assert.isTrue(Id64.isValidId64(physicalModelId)); - const documentListModelId: Id64String = DocumentListModel.insert(this.iModelDb, subjectId, "Document"); + const documentListModelId = DocumentListModel.insert(this.sourceDb, subjectId, "Document"); assert.isTrue(Id64.isValidId64(documentListModelId)); - const drawingId: Id64String = Drawing.insert(this.iModelDb, documentListModelId, "Drawing"); + const drawingId = Drawing.insert(this.sourceDb, documentListModelId, "Drawing"); assert.isTrue(Id64.isValidId64(drawingId)); - const modelSelectorId: Id64String = ModelSelector.insert(this.iModelDb, definitionModelId, "PhysicalModels", [physicalModelId]); + const modelSelectorId = ModelSelector.insert(this.sourceDb, definitionModelId, "PhysicalModels", [physicalModelId]); assert.isTrue(Id64.isValidId64(modelSelectorId)); - const spatialCategoryId: Id64String = SpatialCategory.insert(this.iModelDb, definitionModelId, "SpatialCategory", { color: ColorDef.red }); + const defaultAppearance: SubCategoryAppearance.Props = { + color: ColorDef.green, + transp: 0, + invisible: false, + }; + defaultAppearance.color = ColorDef.green; + const spatialCategoryId = SpatialCategory.insert(this.sourceDb, definitionModelId, "SpatialCategory", defaultAppearance); assert.isTrue(Id64.isValidId64(spatialCategoryId)); - const subCategoryId: Id64String = SubCategory.insert(this.iModelDb, spatialCategoryId, "SubCategory", { color: ColorDef.blue }); + const subCategoryId = SubCategory.insert(this.sourceDb, spatialCategoryId, "SubCategory", { color: ColorDef.blue }); assert.isTrue(Id64.isValidId64(subCategoryId)); - const drawingCategoryId: Id64String = DrawingCategory.insert(this.iModelDb, definitionModelId, "DrawingCategory", new SubCategoryAppearance()); + const physicalObjectProps1: GeometricElement3dProps = { + classFullName: PhysicalObject.classFullName, + model: physicalModelId, + category: spatialCategoryId, + code: Code.createEmpty(), + userLabel: "PhysicalObject1", + geom: TestDataManager.createBox(Point3d.create(1, 1, 1)), + placement: { + origin: Point3d.create(2, 2, 2), + angles: YawPitchRollAngles.createDegrees(0, 0, 0), + }, + }; + const physicalObjectId1: Id64String = this.sourceDb.elements.insertElement(physicalObjectProps1); + assert.isTrue(Id64.isValidId64(physicalObjectId1)); + const drawingCategoryId = DrawingCategory.insert(this.sourceDb, definitionModelId, "DrawingCategory", new SubCategoryAppearance()); assert.isTrue(Id64.isValidId64(drawingCategoryId)); const drawingGraphicProps: GeometricElement2dProps = { classFullName: DrawingGraphic.classFullName, @@ -54,34 +100,156 @@ class TestImporter { category: drawingCategoryId, code: Code.createEmpty(), userLabel: "DrawingGraphic", + geom: TestDataManager.createRectangle(Point2d.create(1, 1)), + placement: { + origin: Point2d.create(2, 2), + angle: 0, + }, }; - const drawingGraphicId: Id64String = this.iModelDb.elements.insertElement(drawingGraphicProps); + const drawingGraphicId = this.sourceDb.elements.insertElement(drawingGraphicProps); assert.isTrue(Id64.isValidId64(drawingGraphicId)); - const drawingGraphicRepresentsId: Id64String = DrawingGraphicRepresentsElement.insert(this.iModelDb, drawingGraphicId, drawingId); // WIP: drawingId as targetId is bogus + const drawingGraphicRepresentsId = DrawingGraphicRepresentsElement.insert(this.sourceDb, drawingGraphicId, physicalObjectId1); assert.isTrue(Id64.isValidId64(drawingGraphicRepresentsId)); - const spatialCategorySelectorId: Id64String = CategorySelector.insert(this.iModelDb, definitionModelId, "SpatialCategories", [spatialCategoryId]); + const spatialCategorySelectorId = CategorySelector.insert(this.sourceDb, definitionModelId, "SpatialCategories", [spatialCategoryId]); assert.isTrue(Id64.isValidId64(spatialCategorySelectorId)); - const drawingCategorySelectorId: Id64String = CategorySelector.insert(this.iModelDb, definitionModelId, "DrawingCategories", [drawingCategoryId]); + const drawingCategorySelectorId = CategorySelector.insert(this.sourceDb, definitionModelId, "DrawingCategories", [drawingCategoryId]); assert.isTrue(Id64.isValidId64(drawingCategorySelectorId)); - const displayStyle2dId: Id64String = DisplayStyle2d.insert(this.iModelDb, definitionModelId, "DisplayStyle2d"); + const displayStyle2dId = DisplayStyle2d.insert(this.sourceDb, definitionModelId, "DisplayStyle2d"); assert.isTrue(Id64.isValidId64(displayStyle2dId)); - const displayStyle3dId: Id64String = DisplayStyle3d.insert(this.iModelDb, definitionModelId, "DisplayStyle3d"); + const displayStyle3dId = DisplayStyle3d.insert(this.sourceDb, definitionModelId, "DisplayStyle3d"); assert.isTrue(Id64.isValidId64(displayStyle3dId)); - const viewRange = new Range3d(0, 0, 0, 100, 100, 20); - const viewId: Id64String = OrthographicViewDefinition.insert(this.iModelDb, definitionModelId, "Orthographic View", modelSelectorId, spatialCategorySelectorId, displayStyle3dId, viewRange); + const viewId = OrthographicViewDefinition.insert(this.sourceDb, definitionModelId, "Orthographic View", modelSelectorId, spatialCategorySelectorId, displayStyle3dId, projectExtents, StandardViewIndex.Iso); assert.isTrue(Id64.isValidId64(viewId)); - this.iModelDb.views.setDefaultViewId(viewId); + this.sourceDb.views.setDefaultViewId(viewId); const drawingViewRange = new Range2d(0, 0, 100, 100); - const drawingViewId: Id64String = DrawingViewDefinition.insert(this.iModelDb, definitionModelId, "Drawing View", drawingId, drawingCategorySelectorId, displayStyle2dId, drawingViewRange); + const drawingViewId = DrawingViewDefinition.insert(this.sourceDb, definitionModelId, "Drawing View", drawingId, drawingCategorySelectorId, displayStyle2dId, drawingViewRange); assert.isTrue(Id64.isValidId64(drawingViewId)); - this.iModelDb.saveChanges(); + const auxCoordSystemProps: AuxCoordSystem2dProps = { + classFullName: AuxCoordSystem2d.classFullName, + model: definitionModelId, + code: AuxCoordSystem2d.createCode(this.sourceDb, definitionModelId, "AuxCoordSystem2d"), + }; + const auxCoordSystemId = this.sourceDb.elements.insertElement(auxCoordSystemProps); + assert.isTrue(Id64.isValidId64(auxCoordSystemId)); + this.sourceDb.saveChanges(); + } + + public static createBox(size: Point3d): GeometryStreamProps { + const geometryStreamBuilder = new GeometryStreamBuilder(); + geometryStreamBuilder.appendGeometry(Box.createDgnBox( + Point3d.createZero(), Vector3d.unitX(), Vector3d.unitY(), new Point3d(0, 0, size.z), + size.x, size.y, size.x, size.y, true, + )!); + return geometryStreamBuilder.geometryStream; + } + + public static createRectangle(size: Point2d): GeometryStreamProps { + const geometryStreamBuilder = new GeometryStreamBuilder(); + geometryStreamBuilder.appendGeometry(LineString3d.createPoints([ + new Point3d(0, 0), + new Point3d(size.x, 0), + new Point3d(size.x, size.y), + new Point3d(0, size.y), + new Point3d(0, 0), + ])); + return geometryStreamBuilder.geometryStream; + } + + public testIdMaps(): void { + const remapTester = new IModelImporter(this.sourceDb, this.targetDb); + const id1 = "0x1"; + const id2 = "0x2"; + remapTester.addCodeSpecId(id1, id2); + assert.equal(id2, remapTester.findCodeSpecId(id1)); + remapTester.addElementId(id1, id2); + assert.equal(id2, remapTester.findElementId(id1)); + remapTester.dispose(); + } + + public importIntoTargetDb(): void { + const importer = new IModelImporter(this.sourceDb, this.targetDb); + importer.excludeCodeSpec("CodeSpec2"); + importer.excludeElementClass(AuxCoordSystem2d.classFullName); + importer.excludeSubject("/Only in Source"); + importer.import(); + importer.dispose(); + this.targetDb.saveChanges(); + } + + public assertTargetDbContents(): void { + // CodeSpec + assert.isTrue(this.targetDb.codeSpecs.hasName("CodeSpec1")); + assert.isFalse(this.targetDb.codeSpecs.hasName("CodeSpec2")); + // Font + if (Platform.platformName.startsWith("win")) { + assert.exists(this.targetDb.fontMap.getFont("Arial")); + } + // Subject + const subjectId = this.targetDb.elements.queryElementIdByCode(Subject.createCode(this.targetDb, IModel.rootSubjectId, "Subject"))!; + assert.isTrue(Id64.isValidId64(subjectId)); + const subjectProps: SubjectProps = this.targetDb.elements.getElementProps(subjectId) as SubjectProps; + assert.equal(subjectProps.description, "Subject description"); // description is an auto-handled property + const sourceOnlySubjectId = this.targetDb.elements.queryElementIdByCode(Subject.createCode(this.targetDb, IModel.rootSubjectId, "Only in Source")); + assert.equal(undefined, sourceOnlySubjectId); + const targetOnlySubjectId = this.targetDb.elements.queryElementIdByCode(Subject.createCode(this.targetDb, IModel.rootSubjectId, "Only in Target"))!; + assert.isTrue(Id64.isValidId64(targetOnlySubjectId)); + // Partitions / Models + const definitionModelId = this.targetDb.elements.queryElementIdByCode(InformationPartitionElement.createCode(this.targetDb, subjectId, "Definition"))!; + const physicalModelId = this.targetDb.elements.queryElementIdByCode(InformationPartitionElement.createCode(this.targetDb, subjectId, "Physical"))!; + const documentListModelId = this.targetDb.elements.queryElementIdByCode(InformationPartitionElement.createCode(this.targetDb, subjectId, "Document"))!; + assert.isTrue(Id64.isValidId64(definitionModelId)); + assert.isTrue(Id64.isValidId64(physicalModelId)); + assert.isTrue(Id64.isValidId64(documentListModelId)); + // SpatialCategory + const spatialCategoryId = this.targetDb.elements.queryElementIdByCode(SpatialCategory.createCode(this.targetDb, definitionModelId, "SpatialCategory"))!; + assert.isTrue(Id64.isValidId64(spatialCategoryId)); + const spatialCategoryProps = this.targetDb.elements.getElementProps(spatialCategoryId); + assert.equal(definitionModelId, spatialCategoryProps.model); + assert.equal(definitionModelId, spatialCategoryProps.code.scope); + const subCategoryId = this.targetDb.elements.queryElementIdByCode(SubCategory.createCode(this.targetDb, spatialCategoryId, "SubCategory"))!; + assert.isTrue(Id64.isValidId64(subCategoryId)); + // DrawingCategory + const drawingCategoryId = this.targetDb.elements.queryElementIdByCode(DrawingCategory.createCode(this.targetDb, definitionModelId, "DrawingCategory"))!; + assert.isTrue(Id64.isValidId64(drawingCategoryId)); + const drawingCategoryProps = this.targetDb.elements.getElementProps(drawingCategoryId); + assert.equal(definitionModelId, drawingCategoryProps.model); + assert.equal(definitionModelId, drawingCategoryProps.code.scope); + // Spatial CategorySelector + const spatialCategorySelectorId = this.targetDb.elements.queryElementIdByCode(CategorySelector.createCode(this.targetDb, definitionModelId, "SpatialCategories"))!; + assert.isTrue(Id64.isValidId64(spatialCategorySelectorId)); + const spatialCategorySelectorProps = this.targetDb.elements.getElementProps(spatialCategorySelectorId); + assert.isTrue(spatialCategorySelectorProps.categories.includes(spatialCategoryId)); + // Drawing CategorySelector + const drawingCategorySelectorId = this.targetDb.elements.queryElementIdByCode(CategorySelector.createCode(this.targetDb, definitionModelId, "DrawingCategories"))!; + assert.isTrue(Id64.isValidId64(drawingCategorySelectorId)); + const drawingCategorySelectorProps = this.targetDb.elements.getElementProps(drawingCategorySelectorId); + assert.isTrue(drawingCategorySelectorProps.categories.includes(drawingCategoryId)); + // ModelSelector + const modelSelectorId = this.targetDb.elements.queryElementIdByCode(ModelSelector.createCode(this.targetDb, definitionModelId, "PhysicalModels"))!; + assert.isTrue(Id64.isValidId64(modelSelectorId)); + const modelSelectorProps = this.targetDb.elements.getElementProps(modelSelectorId); + assert.isTrue(modelSelectorProps.models.includes(physicalModelId)); + // DisplayStyle + const displayStyle3dId = this.targetDb.elements.queryElementIdByCode(DisplayStyle3d.createCode(this.targetDb, definitionModelId, "DisplayStyle3d"))!; + assert.isTrue(Id64.isValidId64(displayStyle3dId)); + // ViewDefinition + const viewId = this.targetDb.elements.queryElementIdByCode(OrthographicViewDefinition.createCode(this.targetDb, definitionModelId, "Orthographic View"))!; + assert.isTrue(Id64.isValidId64(viewId)); + const viewProps = this.targetDb.elements.getElementProps(viewId); + assert.equal(viewProps.displayStyleId, displayStyle3dId); + assert.equal(viewProps.categorySelectorId, spatialCategorySelectorId); + assert.equal(viewProps.modelSelectorId, modelSelectorId); + // AuxCoordSystem2d + assert.equal(undefined, this.targetDb.elements.queryElementIdByCode(AuxCoordSystem2d.createCode(this.targetDb, definitionModelId, "AuxCoordSystem2d"))); } } describe("IModelImporter", () => { it("should import", async () => { - const importer = new TestImporter(); - assert.isDefined(importer); - importer.import(); + const manager = new TestDataManager(); + manager.createSourceDb(); + manager.testIdMaps(); + manager.importIntoTargetDb(); + manager.assertTargetDbContents(); }); }); diff --git a/core/backend/src/test/standalone/PromiseMemoizer.test.ts b/core/backend/src/test/standalone/PromiseMemoizer.test.ts index 1fd5235..03ff81c 100644 --- a/core/backend/src/test/standalone/PromiseMemoizer.test.ts +++ b/core/backend/src/test/standalone/PromiseMemoizer.test.ts @@ -9,12 +9,9 @@ import { assert } from "chai"; import { OpenParams } from "../../imodeljs-backend"; import { PromiseMemoizer, QueryablePromise } from "../../PromiseMemoizer"; -import { IModelTestUtils, TestUsers } from "../IModelTestUtils"; - -// Require Dev envirnomet -describe.skip("PromiseMemoizer", () => { - let accessTokenRegular: AccessToken; - let accessTokenManager: AccessToken; +describe("PromiseMemoizer", () => { + let fakeAccessTokenRegular: AccessToken; + let fakeAccessTokenManager: AccessToken; const pause = async (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); @@ -32,44 +29,52 @@ describe.skip("PromiseMemoizer", () => { const { memoize: memoizeTest, deleteMemoized: deleteMemoizedTest } = new PromiseMemoizer(testFunction, generateTestFunctionKey); before(async () => { - accessTokenRegular = await IModelTestUtils.getTestUserAccessToken(TestUsers.regular); - accessTokenManager = await IModelTestUtils.getTestUserAccessToken(TestUsers.manager); + fakeAccessTokenRegular = AccessToken.fromJsonWebTokenString("Regular", new Date(), new Date()); + fakeAccessTokenManager = AccessToken.fromJsonWebTokenString("Manager", new Date(), new Date()); + }); + + it("should be able to await memoized promise", async () => { + const startTime = Date.now(); + const qp: QueryablePromise = memoizeTest(fakeAccessTokenRegular, "contextId2", "iModelId2", OpenParams.fixedVersion(), IModelVersion.latest()); + await qp.promise; + const endTime = Date.now(); + assert.isAbove(endTime - startTime, 999); // at least 1000 milliseconds }); it("should be able to memoize and deleteMemoized function calls", async () => { const qps = new Array>(6); const expectedResults = new Array(6); - qps[0] = memoizeTest(accessTokenRegular, "contextId", "iModelId1", OpenParams.fixedVersion(), IModelVersion.latest()); - expectedResults[0] = generateTestFunctionKey(accessTokenRegular, "contextId", "iModelId1", OpenParams.fixedVersion(), IModelVersion.latest()); + qps[0] = memoizeTest(fakeAccessTokenRegular, "contextId", "iModelId1", OpenParams.fixedVersion(), IModelVersion.latest()); + expectedResults[0] = generateTestFunctionKey(fakeAccessTokenRegular, "contextId", "iModelId1", OpenParams.fixedVersion(), IModelVersion.latest()); - qps[1] = memoizeTest(accessTokenRegular, "contextId", "iModelId1", OpenParams.fixedVersion(), IModelVersion.latest()); - expectedResults[1] = generateTestFunctionKey(accessTokenRegular, "contextId", "iModelId1", OpenParams.fixedVersion(), IModelVersion.latest()); - assert.strictEqual(qps[1], qps[0]); + qps[1] = memoizeTest(fakeAccessTokenRegular, "contextId", "iModelId1", OpenParams.fixedVersion(), IModelVersion.latest()); + expectedResults[1] = generateTestFunctionKey(fakeAccessTokenRegular, "contextId", "iModelId1", OpenParams.fixedVersion(), IModelVersion.latest()); + assert.strictEqual(qps[1], qps[0], "qps[1] === qps[0] fails"); - qps[2] = memoizeTest(accessTokenManager, "contextId", "iModelId1", OpenParams.fixedVersion(), IModelVersion.latest()); - expectedResults[2] = generateTestFunctionKey(accessTokenManager, "contextId", "iModelId1", OpenParams.fixedVersion(), IModelVersion.latest()); - assert.notStrictEqual(qps[2], qps[0]); + qps[2] = memoizeTest(fakeAccessTokenManager, "contextId", "iModelId1", OpenParams.fixedVersion(), IModelVersion.latest()); + expectedResults[2] = generateTestFunctionKey(fakeAccessTokenManager, "contextId", "iModelId1", OpenParams.fixedVersion(), IModelVersion.latest()); + assert.notStrictEqual(qps[2], qps[0], "qps[2] === qps[0] fails"); - qps[3] = memoizeTest(accessTokenRegular, "contextId", "iModelId2", OpenParams.fixedVersion(), IModelVersion.latest()); - expectedResults[3] = generateTestFunctionKey(accessTokenRegular, "contextId", "iModelId2", OpenParams.fixedVersion(), IModelVersion.latest()); - assert.notStrictEqual(qps[3], qps[0]); + qps[3] = memoizeTest(fakeAccessTokenRegular, "contextId", "iModelId2", OpenParams.fixedVersion(), IModelVersion.latest()); + expectedResults[3] = generateTestFunctionKey(fakeAccessTokenRegular, "contextId", "iModelId2", OpenParams.fixedVersion(), IModelVersion.latest()); + assert.notStrictEqual(qps[3], qps[0], "qps[3] === qps[0] fails"); - qps[4] = memoizeTest(accessTokenRegular, "contextId", "iModelId1", OpenParams.pullOnly(), IModelVersion.latest()); - expectedResults[4] = generateTestFunctionKey(accessTokenRegular, "contextId", "iModelId1", OpenParams.pullOnly(), IModelVersion.latest()); - assert.notStrictEqual(qps[4], qps[0]); + qps[4] = memoizeTest(fakeAccessTokenRegular, "contextId", "iModelId1", OpenParams.pullOnly(), IModelVersion.latest()); + expectedResults[4] = generateTestFunctionKey(fakeAccessTokenRegular, "contextId", "iModelId1", OpenParams.pullOnly(), IModelVersion.latest()); + assert.notStrictEqual(qps[4], qps[0], "qps[4] === qps[0] fails"); - qps[5] = memoizeTest(accessTokenRegular, "contextId", "iModelId1", OpenParams.fixedVersion(), IModelVersion.first()); - expectedResults[5] = generateTestFunctionKey(accessTokenRegular, "contextId", "iModelId1", OpenParams.fixedVersion(), IModelVersion.first()); - assert.notStrictEqual(qps[5], qps[0]); + qps[5] = memoizeTest(fakeAccessTokenRegular, "contextId", "iModelId1", OpenParams.fixedVersion(), IModelVersion.first()); + expectedResults[5] = generateTestFunctionKey(fakeAccessTokenRegular, "contextId", "iModelId1", OpenParams.fixedVersion(), IModelVersion.first()); + assert.notStrictEqual(qps[5], qps[0], "qps[5] === qps[0] fails"); - const qpRej = memoizeTest(accessTokenRegular, "TestError", "iModelId1", OpenParams.fixedVersion(), IModelVersion.first()); - assert.notStrictEqual(qps[6], qps[0]); + const qpRej = memoizeTest(fakeAccessTokenRegular, "TestError", "iModelId1", OpenParams.fixedVersion(), IModelVersion.first()); + assert.notStrictEqual(qps[6], qps[0], "qps[6] === qps[0] fails"); for (const qp of qps) { - assert.isTrue(qp.isPending); + assert.isTrue(qp.isPending, "qp.isPending check fails"); } - assert.isTrue(qpRej.isPending); + assert.isTrue(qpRej.isPending, "qpRej.isPending check fails"); await pause(1500); @@ -80,8 +85,8 @@ describe.skip("PromiseMemoizer", () => { assert.isTrue(qpRej.isRejected); assert.strictEqual(qpRej.error.message, "TestError"); - deleteMemoizedTest(accessTokenRegular, "contextId", "iModelId1", OpenParams.fixedVersion(), IModelVersion.latest()); - const qp0 = memoizeTest(accessTokenRegular, "contextId", "iModelId1", OpenParams.fixedVersion(), IModelVersion.latest()); + deleteMemoizedTest(fakeAccessTokenRegular, "contextId", "iModelId1", OpenParams.fixedVersion(), IModelVersion.latest()); + const qp0 = memoizeTest(fakeAccessTokenRegular, "contextId", "iModelId1", OpenParams.fixedVersion(), IModelVersion.latest()); assert.isTrue(qp0.isPending); }); diff --git a/core/backend/src/test/standalone/SqliteStatement.test.ts b/core/backend/src/test/standalone/SqliteStatement.test.ts index 5c155fe..3c2625b 100644 --- a/core/backend/src/test/standalone/SqliteStatement.test.ts +++ b/core/backend/src/test/standalone/SqliteStatement.test.ts @@ -17,6 +17,35 @@ describe("SqliteStatement", () => { const doubleVal: number = -2.5; const blobVal = new Uint8Array(new Range3d(1.2, 2.3, 3.4, 4.5, 5.6, 6.7).toFloat64Array().buffer); + it("Asynchronous step", async () => { + await using(ECDbTestHelper.createECDb(_outDir, "testAsync.ecdb"), async (ecdb: ECDb) => { + assert.isTrue(ecdb.isOpen); + + let r: DbResult; + r = await ecdb.withPreparedSqliteStatement("CREATE TABLE MyTable(id INTEGER PRIMARY KEY, stringcol TEXT)", async (stmt: SqliteStatement) => { + return stmt.stepAsync(); + }); + assert.equal(r, DbResult.BE_SQLITE_DONE); + + r = await ecdb.withPreparedSqliteStatement("INSERT INTO MyTable(stringcol) VALUES('test1')", async (stmt: SqliteStatement) => { + return stmt.stepAsync(); + }); + assert.equal(r, DbResult.BE_SQLITE_DONE); + + r = await ecdb.withPreparedSqliteStatement("INSERT INTO MyTable(stringcol) VALUES('test2')", async (stmt: SqliteStatement) => { + return stmt.stepAsync(); + }); + assert.equal(r, DbResult.BE_SQLITE_DONE); + + const stringCol = await ecdb.withPreparedSqliteStatement("SELECT stringcol FROM MyTable", async (stmt: SqliteStatement) => { + const rv = await stmt.stepAsync(); + assert.equal(rv, DbResult.BE_SQLITE_ROW); + return stmt.getValue(0).getString(); + }); + assert.equal(stringCol, "test1"); + }); + }); + it("Create Table, Insert, Select with ECDb", () => { using(ECDbTestHelper.createECDb(_outDir, "sqlitestatement.ecdb"), (ecdb: ECDb) => { assert.isTrue(ecdb.isOpen); diff --git a/core/bentley/.npmignore b/core/bentley/.npmignore index 146229f..214db3e 100644 --- a/core/bentley/.npmignore +++ b/core/bentley/.npmignore @@ -3,5 +3,4 @@ !lib/**/*.d.ts !lib/**/*.js test -!package.json !*.md diff --git a/core/bentley/CHANGELOG.json b/core/bentley/CHANGELOG.json index aa93cba..37fcbfd 100644 --- a/core/bentley/CHANGELOG.json +++ b/core/bentley/CHANGELOG.json @@ -1,6 +1,39 @@ { "name": "@bentley/bentleyjs-core", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/bentleyjs-core_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "Changes package.json to include api-extractor and adds api-extractor.json" + }, + { + "comment": "Use new buildIModelJsBuild script" + }, + { + "comment": "Add release tags to indicate API stability" + }, + { + "comment": "Remove uneeded typedoc plugin depedency" + }, + { + "comment": "Save BUILD_SEMVER to globally accessible map" + }, + { + "comment": "upgrade to TypeScript 3.2.2" + } + ] + } + }, + { + "version": "0.188.0", + "tag": "@bentley/bentleyjs-core_v0.188.0", + "date": "Wed, 16 Jan 2019 16:36:09 GMT", + "comments": {} + }, { "version": "0.187.0", "tag": "@bentley/bentleyjs-core_v0.187.0", diff --git a/core/bentley/CHANGELOG.md b/core/bentley/CHANGELOG.md index 96e419f..aa0b983 100644 --- a/core/bentley/CHANGELOG.md +++ b/core/bentley/CHANGELOG.md @@ -1,6 +1,23 @@ # Change Log - @bentley/bentleyjs-core -This log was last generated on Tue, 15 Jan 2019 15:18:59 GMT and should not be manually modified. +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- Changes package.json to include api-extractor and adds api-extractor.json +- Use new buildIModelJsBuild script +- Add release tags to indicate API stability +- Remove uneeded typedoc plugin depedency +- Save BUILD_SEMVER to globally accessible map +- upgrade to TypeScript 3.2.2 + +## 0.188.0 +Wed, 16 Jan 2019 16:36:09 GMT + +*Version update only* ## 0.187.0 Tue, 15 Jan 2019 15:18:59 GMT diff --git a/core/bentley/package.json b/core/bentley/package.json index 5244121..3d76961 100644 --- a/core/bentley/package.json +++ b/core/bentley/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/bentleyjs-core", - "version": "0.187.0", + "version": "0.189.0", "description": "Bentley JavaScript core components", "main": "lib/bentleyjs-core.js", "typings": "lib/bentleyjs-core", @@ -10,15 +10,24 @@ "url": "https://github.com/imodeljs/imodeljs" }, "scripts": { - "build": "tsc 1>&2 && npm run webpackModule-dev", + "build": "node ./node_modules/@bentley/webpack-tools/bin/buildIModelJsModule.js", "clean": "rimraf lib package-deps.json", + "extract-api": "node ./node_modules/@bentley/build-tools/scripts/extract-api.js --entry=bentleyjs-core", "test": "node ./node_modules/@bentley/build-tools/scripts/test.js", "docs": "node ./node_modules/@bentley/build-tools/scripts/docs.js --source=./src --json=../../generated-docs/core/bentleyjs-core/file.json --tsIndexFile=bentleyjs-core.ts --onlyJson %TYPEDOC_THEME%", "cover": "nyc npm test", "cover:docs": "node ./node_modules/@bentley/build-tools/scripts/docscoverage.js", - "lint": "tslint --project . 1>&2", - "webpackModule-dev": "make-dir ./lib/module/dev/ && webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/bentleyjs-core.js --env.bundlename=bentleyjs-core --json >./lib/module/dev/webpackStats.json", - "webpackModule-prod": "webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/bentleyjs-core.js --env.bundlename=bentleyjs-core --env.prod" + "lint": "tslint --project . 1>&2" + }, + "iModelJs": { + "buildModule": { + "type": "system", + "webpack": { + "dest": "./lib/module", + "entry": "./lib/bentleyjs-core.js", + "bundleName": "bentleyjs-core" + } + } }, "keywords": [ "Bentley", @@ -30,25 +39,19 @@ }, "dependencies": {}, "devDependencies": { - "@bentley/build-tools": "0.187.0", - "@bentley/webpack-tools": "0.187.0", + "@bentley/build-tools": "0.189.0", + "@bentley/webpack-tools": "0.189.0", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", - "@types/node": "10.10.3", + "@types/node": "10.12.18", "chai": "^4.1.2", - "make-dir-cli": "^1.0.0", "mocha": "^5.2.0", "rimraf": "^2.6.2", "tslint": "^5.11.0", "typedoc": "^0.11.1", - "typedoc-plugin-external-module-name": "^1.1.1", - "typescript": "~3.1.0", + "typescript": "~3.2.2", "ts-node": "^7.0.1", - "nyc": "^13.0.1", - "source-map-loader": "^0.2.3", - "source-map-support": "^0.5.6", - "webpack": "^4.20.2", - "webpack-cli": "^3.1.0" + "nyc": "^13.0.1" }, "nyc": { "nycrc-path": "./node_modules/@bentley/build-tools/.nycrc" diff --git a/core/bentley/src/ActivityLoggingContext.ts b/core/bentley/src/ActivityLoggingContext.ts index 8fc54be..3bd3591 100644 --- a/core/bentley/src/ActivityLoggingContext.ts +++ b/core/bentley/src/ActivityLoggingContext.ts @@ -7,6 +7,7 @@ /** A notion of the logging context in which a backend operation is performed, used to correlate * a frontend request with all of the backend operations that it requests. * See [ActivityLoggingContext rules]($docs/learning/backend/managingactivityloggingcontext.md). + * @beta ActivityLoggingContext is scheduled to be renamed before the 1.0 release. */ export class ActivityLoggingContext { /** The current activity context. */ @@ -33,5 +34,4 @@ export class ActivityLoggingContext { ActivityLoggingContext._current = this; return this; } - } diff --git a/core/bentley/src/Assert.ts b/core/bentley/src/Assert.ts index 915c2f5..96eac49 100644 --- a/core/bentley/src/Assert.ts +++ b/core/bentley/src/Assert.ts @@ -12,6 +12,7 @@ * @throws Error containing the specified message if condition is false. * @note This function should be used to validate conditions that should never realistically occur, or * which indicate a misuse of the API which should be eliminated during development. + * @beta Need strategy for removing assert in production builds */ export function assert(condition: boolean, msg?: string): void { if (!condition) diff --git a/core/bentley/src/BeEvent.ts b/core/bentley/src/BeEvent.ts index f30ead7..cab0386 100644 --- a/core/bentley/src/BeEvent.ts +++ b/core/bentley/src/BeEvent.ts @@ -4,7 +4,9 @@ *--------------------------------------------------------------------------------------------*/ /** @module Events */ -/** A function invoked when a BeEvent is raised. */ +/** A function invoked when a BeEvent is raised. + * @public + */ export type Listener = (...arg: any[]) => void; class EventContext { @@ -15,6 +17,7 @@ class EventContext { * Manages a set of *listeners* for a particular event and notifies them when the event is raised. * This class is usually instantiated inside of a container class and * exposed as a property for others to *subscribe* via [[BeEvent.addListener]]. + * @public */ export class BeEvent { private _listeners: EventContext[] = []; @@ -78,7 +81,7 @@ export class BeEvent { * @param args This method takes any number of parameters and passes them through to the listeners. * @see [[BeEvent.removeListener]], [[BeEvent.addListener]] */ - public raiseEvent(..._args: any[]) { + public raiseEvent(...args: any[]) { this._insideRaiseEvent = true; const listeners = this._listeners; @@ -90,7 +93,7 @@ export class BeEvent { if (!context.listener) { dropped = true; } else { - context.listener.apply(context.scope, arguments); + context.listener.apply(context.scope, args); if (context.once) { context.listener = undefined; dropped = true; @@ -123,7 +126,9 @@ export class BeEvent { public clear(): void { this._listeners.length = 0; } } -/** Specialization of BeEvent for Ui events that take a single strongly typed argument. */ +/** Specialization of BeEvent for Ui events that take a single strongly typed argument. + * @beta Right name? Right package? + */ export class BeUiEvent extends BeEvent<(args: TEventArgs) => void> { /** Raises event with single strongly typed argument. */ @@ -133,6 +138,7 @@ export class BeUiEvent extends BeEvent<(args: TEventArgs) => void> { /** * A list of BeEvent objects, accessible by an event name. * This class may be used instead of explicitly declaring each BeEvent as a member of a containing class. + * @beta Is this class used? */ export class BeEventList { private _events: { [name: string]: BeEvent | undefined; } = {}; diff --git a/core/bentley/src/BeSQLite.ts b/core/bentley/src/BeSQLite.ts index 9c30d16..30429c6 100644 --- a/core/bentley/src/BeSQLite.ts +++ b/core/bentley/src/BeSQLite.ts @@ -4,13 +4,17 @@ *--------------------------------------------------------------------------------------------*/ /** @module BeSQLite */ -/** Whether to open a database readonly or writeable. */ +/** Whether to open a database readonly or writeable. + * @public + */ export const enum OpenMode { Readonly = 0x00000001, ReadWrite = 0x00000002, } -/** Values, stored in changesets, that indicate what operation was performed on the database. */ +/** Values, stored in changesets, that indicate what operation was performed on the database. + * @public + */ export const enum DbOpcode { /** A row was deleted. */ Delete = 9, @@ -20,7 +24,9 @@ export const enum DbOpcode { Update = 23, } -/** Values for return codes from BeSQLite functions. Consult SQLite documentation for further explanations. */ +/** Values for return codes from BeSQLite functions. Consult SQLite documentation for further explanations. + * @public + */ export const enum DbResult { /** Success */ BE_SQLITE_OK = 0, @@ -169,8 +175,8 @@ export const enum DbResult { BE_SQLITE_CONSTRAINT_VTAB = (BE_SQLITE_CONSTRAINT_BASE | (9 << 8)), } -/** - * Options that specify how to apply ChangeSets. +/** Options that specify how to apply ChangeSets. + * @public */ export enum ChangeSetApplyOption { /** ChangeSet won't be used. */ diff --git a/core/bentley/src/BentleyError.ts b/core/bentley/src/BentleyError.ts index 836d268..4bcd65c 100644 --- a/core/bentley/src/BentleyError.ts +++ b/core/bentley/src/BentleyError.ts @@ -11,6 +11,7 @@ import { LogFunction, Logger } from "./Logger"; * This status code should be rarely used. * Prefer to throw an exception to indicate an error, rather than returning a special status code. * If a status code is to be returned, prefer to return a more specific error status type such as IModelStatus or DbResult. + * @public */ export const enum BentleyStatus { SUCCESS = 0x0000, @@ -19,6 +20,7 @@ export const enum BentleyStatus { /** Status codes that are used in conjunction with [[BentleyError]]. * Error status codes are divided into separate ranges for different kinds of errors. All known ranges at least should be defined here, to avoid collisions. + * @public */ export const enum IModelStatus { IMODEL_ERROR_BASE = 0x10000, @@ -91,7 +93,9 @@ export const enum IModelStatus { NoGeoLocation = IMODEL_ERROR_BASE + 66, } -/** Error status from various briefcase operations */ +/** Error status from various briefcase operations + * @beta Should these be internal? + */ export const enum BriefcaseStatus { CannotAcquire = 0x20000, CannotDownload = 0x20001, @@ -102,7 +106,9 @@ export const enum BriefcaseStatus { CannotApplyChanges = 0x20006, } -/** RpcInterface status codes */ +/** RpcInterface status codes + * @beta Should these be internal? + */ export enum RpcInterfaceStatus { Success = 0, RPC_INTERFACE_ERROR_BASE = 0x21000, @@ -110,7 +116,9 @@ export enum RpcInterfaceStatus { IncompatibleVersion = RPC_INTERFACE_ERROR_BASE, } -/** Error status from various ChangeSet operations */ +/** Error status from various ChangeSet operations + * @beta Should these be internal? + */ export const enum ChangeSetStatus { // Note: Values must be kept in sync with ChangeSetStatus in DgnPlatform Success = 0, CHANGESET_ERROR_BASE = 0x16000, @@ -166,7 +174,9 @@ export const enum ChangeSetStatus { // Note: Values must be kept in sync with Ch CannotMergeIntoReversed = CHANGESET_ERROR_BASE + 25, } -/** Return codes for methods which perform repository management operations */ +/** Return codes for methods which perform repository management operations + * @beta Should these be internal? + */ export const enum RepositoryStatus { Success = 0, /** The repository server did not respond to a request */ @@ -199,7 +209,9 @@ export const enum RepositoryStatus { RepositoryIsLocked = 0x1500E, } -/** Status from returned HTTP status code */ +/** Status from returned HTTP status code + * @beta Should these be internal? + */ export const enum HttpStatus { /** 2xx Success */ Success = 0, @@ -213,7 +225,9 @@ export const enum HttpStatus { ServerError = 0x17004, } -/** Server returned WSG errors */ +/** Server returned WSG errors + * @beta Right name? Right package? + */ export const enum WSStatus { Success = 0, WSERROR_BASE = 0x18000, @@ -234,7 +248,9 @@ export const enum WSStatus { LoginRequired = WSERROR_BASE + 15, } -// iModelHub Services Errors +/** iModelHub Services Errors + * @beta Right package? + */ export enum IModelHubStatus { Success = 0, IMODELHUBERROR_BASE = 0x19000, @@ -294,14 +310,18 @@ export enum IModelHubStatus { FileNotFound = IMODELHUBERROR_REQUESTERRORBASE + 6, } -// Authentication Errors +/** Authentication Errors + * @beta Internal? Right package? + */ export enum AuthStatus { Success = 0, AUTHSTATUS_BASE = 0x20000, Error = AUTHSTATUS_BASE, } -/** When you want to associate an explanatory message with an error status value. */ +/** When you want to associate an explanatory message with an error status value. + * @beta Internal? + */ export interface StatusCodeWithMessage { status: ErrorCodeType; message: string; @@ -309,10 +329,13 @@ export interface StatusCodeWithMessage { /** Defines the *signature* for a function that returns meta-data related to an error. * Declared as a function so that the expense of creating the meta-data is only paid when it is needed. + * @public */ export type GetMetaDataFunction = () => any; -/** The error type thrown by this module. `BentleyError` subclasses `Error` to add an `errorNumber` member. See [[IModelStatus]] for `errorNumber` values. */ +/** The error type thrown by this module. `BentleyError` subclasses `Error` to add an `errorNumber` member. See [[IModelStatus]] for `errorNumber` values. + * @public + */ export class BentleyError extends Error { private readonly _getMetaData: GetMetaDataFunction | undefined; public errorNumber: number; diff --git a/core/bentley/src/Compare.ts b/core/bentley/src/Compare.ts index 7db1edc..5363c7d 100644 --- a/core/bentley/src/Compare.ts +++ b/core/bentley/src/Compare.ts @@ -22,12 +22,13 @@ * @see Dictionary * @see IndexMap * @see PriorityQueue + * @public */ export type OrderedComparator = (lhs: T, rhs: U) => number; /** * An [[OrderedComparator]] for numbers that treats two numbers as equal if the absolute value of their difference is less than a specified tolerance. - * @hidden + * @public */ export function compareWithTolerance(a: number, b: number, tolerance = 0.1): number { if (a < b - tolerance) @@ -38,16 +39,16 @@ export function compareWithTolerance(a: number, b: number, tolerance = 0.1): num return 0; } -/** @hidden */ +/** @public */ export function compareNumbers(a: number, b: number): number { return a - b; } -/** @hidden */ +/** @public */ export function compareBooleans(a: boolean, b: boolean): number { return a !== b ? (a < b ? -1 : 1) : 0; } -/** @hidden */ +/** @public */ export function compareStrings(a: string, b: string): number { return a === b ? 0 : (a < b ? -1 : 1); } -/** @hidden */ +/** @public */ export function comparePossiblyUndefined(compareDefined: (lhs: T, rhs: T) => number, lhs?: T, rhs?: T): number { if (undefined === lhs) return undefined === rhs ? 0 : -1; @@ -57,5 +58,5 @@ export function comparePossiblyUndefined(compareDefined: (lhs: T, rhs: T) => return compareDefined(lhs, rhs); } -/** @hidden */ +/** @public */ export function compareStringsOrUndefined(lhs?: string, rhs?: string): number { return comparePossiblyUndefined(compareStrings, lhs, rhs); } diff --git a/core/bentley/src/Dictionary.ts b/core/bentley/src/Dictionary.ts index 44295f0..db9e290 100644 --- a/core/bentley/src/Dictionary.ts +++ b/core/bentley/src/Dictionary.ts @@ -32,6 +32,7 @@ class DictionaryIterator implements Iterator> { /** * Represents an entry in a [[Dictionary]]. + * @public */ export interface DictionaryEntry { /** The key used for lookup in the Dictionary. */ @@ -52,6 +53,7 @@ export interface DictionaryEntry { * * Modifying a key in a way that affects the comparison function will produce unpredictable results, the * most likely of which is that keys will cease to map to the values with which they were initially inserted. + * @public */ export class Dictionary implements Iterable> { protected _keys: K[] = []; diff --git a/core/bentley/src/Disposable.ts b/core/bentley/src/Disposable.ts index eedc390..69ff067 100644 --- a/core/bentley/src/Disposable.ts +++ b/core/bentley/src/Disposable.ts @@ -17,6 +17,7 @@ * * Implementations of IDisposable tend to be more "low-level" types. The disposal of such types is often handled on your behalf by imodel.js. * However, always consult the documentation for an IDisposable type to determine under what circumstances you are expected to explicitly dispose of it. + * @public */ export interface IDisposable { /** Disposes of any resources owned by this object. @@ -41,6 +42,7 @@ export interface IDisposable { * ``` * @param disposable The object to be disposed of. * @returns undefined + * @public */ export function dispose(disposable?: IDisposable): undefined { if (undefined !== disposable) @@ -51,6 +53,7 @@ export function dispose(disposable?: IDisposable): undefined { /** Disposes of and empties a list of disposable objects. * @param list The list of disposable obejcts. * @returns undefined + * @public */ export function disposeArray(list?: IDisposable[]): undefined { if (undefined === list) @@ -67,8 +70,9 @@ export function disposeArray(list?: IDisposable[]): undefined { * is called on the resource no matter if the func returns or throws. If func returns, the return value * of this function is equal to return value of func. If func throws, this function also throws (after * disposing the resource). + * @public */ -export function using(resources: TDisposable | TDisposable[], func: (...resources: TArg[]) => TResult): TResult { +export function using(resources: T | T[], func: (...r: T[]) => TResult): TResult { if (!Array.isArray(resources)) return using([resources], func); @@ -76,8 +80,8 @@ export function using void; class FuncDisposable implements IDisposable { @@ -97,7 +103,9 @@ class FuncDisposable implements IDisposable { public dispose() { this._disposeFunc(); } } -/** A disposable container of disposable objects. */ +/** A disposable container of disposable objects. + * @public + */ export class DisposableList implements IDisposable { private _disposables: IDisposable[]; diff --git a/core/bentley/src/FluentdBunyanLoggerConfig.ts b/core/bentley/src/FluentdBunyanLoggerConfig.ts deleted file mode 100644 index 309614c..0000000 --- a/core/bentley/src/FluentdBunyanLoggerConfig.ts +++ /dev/null @@ -1,55 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ -/** @module Logging */ - -// tslint:disable-next-line:no-var-requires -const bunyan = require("bunyan"); -import { FluentdLoggerStream, IFluentdConfig } from "./FluentdLoggerStream"; -import { BentleyError, IModelStatus } from "./BentleyError"; - -/** Helps to configure the bentleyjs-core Logger to use fluentd and seq. - * Note: The app must depend on the bunyan, request and request-promise packages. - */ -export class FluentdBunyanLoggerConfig { - /** Create a bunyan logger that streams to fluentd - * ``` - * BunyanLoggerConfig.logToBunyan(FluentdBunyanLoggerConfig.createBunyanFluentdLogger(fluentdConfig)); - * ``` - * See [[BunyanLoggerConfig.logToBunyan]] - */ - public static createBunyanFluentdLogger(fluentdConfig: IFluentdConfig, loggerName: string): any { - if (fluentdConfig === undefined) { - fluentdConfig = {}; - } - const params: IFluentdConfig = {}; - params.fluentdHost = (fluentdConfig.fluentdHost || "http://localhost"); - params.fluentdPort = (fluentdConfig.fluentdPort || 9880); - params.fluentdTimeout = (fluentdConfig.fluentdTimeout || 1500); - params.seqServerUrl = (fluentdConfig.seqServerUrl || "http://localhost"); - params.seqServerPort = (fluentdConfig.seqServerPort || 5341); - params.seqApiKey = (fluentdConfig.seqApiKey || "InvalidApiKey"); - - // nb: Define only one bunyan stream! Otherwise, we will get logging messages coming out multiple times, once for each stream. (https://github.com/trentm/node-bunyan/issues/334) - // this one stream must accept messages at all levels. That is why we set it to "trace". That is just its lower limit. - // const tracelevel: any = "trace"; - - return bunyan.createLogger({ - name: loggerName, - streams: [ - { stream: new FluentdLoggerStream(params), level: 10 }, - ], - }); - } - - /** Check that the specified object is a valid SeqConfig. This is useful when reading a config from a .json file. */ - public static validateProps(fluentdConfig: any): void { - const validProps: string[] = ["host", "port"]; - for (const prop of Object.keys(fluentdConfig)) { - if (!validProps.includes(prop)) { - throw new BentleyError(IModelStatus.BadArg, "unrecognized fluentdConfig property: " + prop); - } - } - } -} diff --git a/core/bentley/src/FluentdLoggerStream.ts b/core/bentley/src/FluentdLoggerStream.ts deleted file mode 100644 index 174ffe2..0000000 --- a/core/bentley/src/FluentdLoggerStream.ts +++ /dev/null @@ -1,136 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ -/** @module Logging */ - -import { Writable } from "stream"; -import * as domain from "domain"; - -// tslint:disable-next-line:no-var-requires -const bunyan = require("bunyan"); -// tslint:disable-next-line:no-var-requires -const post = require("request-promise"); - -export interface GenericPost { - postasync(config: any, jsonbody: any): Promise; -} - -/** fluentd logging server configuration. */ -export interface IFluentdConfig { - /** The URL of the fluentd server to connect to. Defaults to localhost. */ - fluentdHost?: string; - /** The port of the fluentd server. Defaults to 9880. */ - fluentdPort?: number; - /** fluentd server request timeout. Defaults to 1500. */ - fluentdTimeout?: number; - /** The URL of the seq server to send logs. Defaults to localhost. */ - seqServerUrl?: string; - /** The port of the seq server. Defaults to 5341. */ - seqServerPort?: number; - /** The API Key to use when sending logs to Seq */ - seqApiKey?: string; -} - -export class PostFluentd implements GenericPost { - private generateOptions(config: IFluentdConfig, jsonbody: any): any { - const customHeaders: any = {}; - customHeaders["content-type"] = "application/json"; - customHeaders["seq-server"] = config.seqServerUrl; - customHeaders["seq-apikey"] = config.seqApiKey; - customHeaders["seq-port"] = config.seqServerPort; - // TODO: Handle SEQ_PORT (on fluentd side as well) and use kabab case instead of snake case. - return { - uri: config.fluentdHost + ":" + config.fluentdPort + "/seqlogging", - body: jsonbody, - headers: JSON.parse(JSON.stringify(customHeaders)), - resolveWithFullResponse: true, - timeout: config.fluentdTimeout, - }; - } - public async postasync(config: any, jsonbody: any): Promise { - const response = await post(this.generateOptions(config, jsonbody)); - return response.statusCode || -1; - } -} - -export class FluentdLoggerStream extends Writable { - private _fluentdParams: IFluentdConfig; - constructor(fluentdParams: IFluentdConfig) { - super(); - this._fluentdParams = fluentdParams; - } - - private mapLevelToString(level: any): any { - let response: string; - switch (level) { - case bunyan.TRACE: { - response = "Trace"; - break; - } - case bunyan.DEBUG: { - response = "Debug"; - break; - } - case bunyan.INFO: { - response = "Information"; - break; - } - case bunyan.WARN: { - response = "Warning"; - break; - } - case bunyan.ERROR: { - response = "Error"; - break; - } - case bunyan.FATAL: { - response = "Fatal"; - break; - } - default: { - response = "Information"; - } - } - return response; - } - - // tslint:disable-next-line:naming-convention - public _writev(chunks: Array<{ chunk: any, encoding: string }>, callback: (err?: Error) => void): void { - for (const entry of chunks) { - this._write(entry.chunk, entry.encoding, callback); - } - } - - // tslint:disable-next-line:naming-convention - public _write(chunk: any, encoding: string, callback: (err?: Error) => void): void { - // we create a domain to catch errors from the socket. Major errors like CONNECTION not made is sent to bunyan - const fluentdDomain: domain.Domain = domain.create(); - fluentdDomain.on("error", (errEvent: Error) => { - this.emit("error", new Error(`Fluentd domain error -- , ${errEvent.message}`)); - callback(errEvent); - }); - - fluentdDomain.run(() => { - // generate a valid json as body - let packet: any; - try { - packet = JSON.parse(chunk); - if (packet.hasOwnProperty("level")) { - packet.level = this.mapLevelToString(chunk.level); - } - packet = JSON.stringify(packet); - } catch (error) { - this.emit("error", new Error(`Error: ${error}, Encoding: ${encoding}`)); - packet = JSON.stringify(chunk); - } - - // Post to fluentd -- async - const poster = new PostFluentd(); - Promise.resolve(poster.postasync(this._fluentdParams, packet)) - .then((res) => { if (res === -1 || res !== 200) { throw new Error("invalid response from fluentd"); } }) - .catch((err) => { this.emit("error", new Error(`Fluentd post error -- ${err.message}`)); }); - callback(); - }); - } -} diff --git a/core/bentley/src/Id.ts b/core/bentley/src/Id.ts index 44b900d..c8d2df3 100644 --- a/core/bentley/src/Id.ts +++ b/core/bentley/src/Id.ts @@ -4,25 +4,30 @@ *--------------------------------------------------------------------------------------------*/ /** @module Ids */ -/** - * A string containing a well-formed string representation of an [Id64]($bentleyjs-core). - * +/** A string containing a well-formed string representation of an [Id64]($bentleyjs-core). * See [Working with Ids]($docs/learning/common/Id64.md). + * @public */ export type Id64String = string; -/** - * A string containing a well-formed string representation of a [Guid]($bentleyjs-core). +/** A string containing a well-formed string representation of a [Guid]($bentleyjs-core). + * @public */ export type GuidString = string; -/** A set of [[Id64String]]s. */ +/** A set of [[Id64String]]s. + * @public + */ export type Id64Set = Set; -/** An array of [[Id64String]]s. */ +/** An array of [[Id64String]]s. + * @public + */ export type Id64Array = Id64String[]; -/** Used as an argument to a function that can accept one or more [[Id64String]]s. */ +/** Used as an argument to a function that can accept one or more [[Id64String]]s. + * @public + */ export type Id64Arg = Id64String | Id64Set | Id64Array; function toHex(str: string): number { @@ -63,6 +68,7 @@ function isValidHexString(id: string, startIndex: number, len: number) { * The [[Id64String]] type alias is used to indicate function arguments, return types, and variables which are known to contain a well-formed representation of a 64-bit Id. * * See [Working with Ids]($docs/learning/common/Id64.md) for a detailed description and code examples. + * @public */ export namespace Id64 { /** Extract the "local" Id portion of an Id64String, contained in the lower 40 bits of the 64-bit value. */ @@ -417,6 +423,7 @@ export namespace Id64 { * - The inputs are unsigned 32-bit integers; * - The inputs represent a valid Id64String (e.g., local ID is not zero). * @see [[Id64.Uint32Map]] for a similarly-optimized replacement for Map + * @public */ export class Uint32Set { protected readonly _map = new Map>(); @@ -479,6 +486,7 @@ export namespace Id64 { /** A specialized replacement for Map optimized for performance-critical code. * @see [[Id64.Uint32Set]] for implementation details. + * @public */ export class Uint32Map { protected readonly _map = new Map>(); @@ -524,6 +532,7 @@ export namespace Id64 { * Generates unique [[Id64String]] values in sequence, which are guaranteed not to conflict with Ids associated with persistent elements or models. * This is useful for associating stable, non-persistent identifiers with things like [Decorator]($frontend)s. * A TransientIdSequence can generate a maximum of (2^40)-2 unique Ids. + * @public */ export class TransientIdSequence { private _localId: number = 0; @@ -537,6 +546,7 @@ export class TransientIdSequence { * * The [[GuidString]] type alias is used to indicate function arguments, return types, and variables which are known to * be in the GUID format. + * @public */ export namespace Guid { const uuidPattern = new RegExp("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"); diff --git a/core/bentley/src/IndexMap.ts b/core/bentley/src/IndexMap.ts index 0a1d0a0..b17993c 100644 --- a/core/bentley/src/IndexMap.ts +++ b/core/bentley/src/IndexMap.ts @@ -7,7 +7,9 @@ import { CloneFunction, shallowClone, lowerBound } from "./SortedArray"; import { OrderedComparator } from "./Compare"; -/** Associates a value of type T with an index representing its insertion order in an IndexMap */ +/** Associates a value of type T with an index representing its insertion order in an IndexMap + * @public + */ export class IndexedValue { public readonly value: T; public readonly index: number; @@ -22,6 +24,7 @@ export class IndexedValue { * Maintains a set of unique elements in sorted order and retains the insertion order of each. * The uniqueness of the elements is determined by a comparison routine supplied by the user. * The user may also supply a maximum size, beyond which insertions will fail. + * @public */ export class IndexMap { protected _array: Array> = []; diff --git a/core/bentley/src/JsonUtils.ts b/core/bentley/src/JsonUtils.ts index 5501654..19ee21f 100644 --- a/core/bentley/src/JsonUtils.ts +++ b/core/bentley/src/JsonUtils.ts @@ -4,7 +4,9 @@ *--------------------------------------------------------------------------------------------*/ /** @module Utils */ -/** Utility functions for converting from JSON objects, with default values. */ +/** Utility functions for converting from JSON objects, with default values. + * @public + */ export namespace JsonUtils { /** Get a value as a boolean. * @param json the input JSON object diff --git a/core/bentley/src/LRUMap.ts b/core/bentley/src/LRUMap.ts index 4c7fbef..a33a4af 100644 --- a/core/bentley/src/LRUMap.ts +++ b/core/bentley/src/LRUMap.ts @@ -10,7 +10,9 @@ * See README.md at https://github.com/rsms/js-lru for details. */ -/** An entry holds the key and value, and pointers to any older and newer entries. */ +/** An entry holds the key and value, and pointers to any older and newer entries. + * @public + */ export class Entry { public newer?: Entry; public older?: Entry; @@ -78,6 +80,7 @@ class ValueIterator implements Iterator { * * removed <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- added * ``` + * @public */ export class LRUMap { private _keymap: Map>; diff --git a/core/bentley/src/Logger.ts b/core/bentley/src/Logger.ts index f00fd60..ed6ea59 100644 --- a/core/bentley/src/Logger.ts +++ b/core/bentley/src/Logger.ts @@ -8,9 +8,14 @@ import { GetMetaDataFunction, IModelStatus, BentleyError } from "./BentleyError" import { IDisposable } from "./Disposable"; import { ActivityLoggingContext } from "./ActivityLoggingContext"; -/** Defines the *signature* for a log function. */ +/** Defines the *signature* for a log function. + * @public + */ export type LogFunction = (category: string, message: string, metaData?: GetMetaDataFunction) => void; +/** Use to categorize logging messages by severity. + * @public + */ export enum LogLevel { /** Tracing and debugging - low level */ Trace, @@ -24,13 +29,17 @@ export enum LogLevel { None, } -/** Identifies a logging category and the LogLevel that should be used for it. The LogLevel is specified by its string name. */ +/** Identifies a logging category and the LogLevel that should be used for it. The LogLevel is specified by its string name. + * @public + */ export interface LoggerCategoryAndLevel { category: string; logLevel: string; } -/** Specifies logging levels, including the default logging level and a set of categories and levels for them. */ +/** Specifies logging levels, including the default logging level and a set of categories and levels for them. + * @public + */ export interface LoggerLevelsConfig { defaultLevel?: string; categoryLevels?: LoggerCategoryAndLevel[]; @@ -38,6 +47,7 @@ export interface LoggerLevelsConfig { /** Logger allows libraries and apps to report potentially useful information about operations, and it allows apps and users to control * how or if the logged information is displayed or collected. See [Learning about Logging]($docs/learning/common/Logging.md). + * @public */ export class Logger { private static _logError: LogFunction | undefined; @@ -277,6 +287,7 @@ export class Logger { * * The timings are logged using the log category **Performance** and log severity [[LogLevel.INFO]]. * Enable those, if you want to capture timings. + * @public */ export class PerfLogger implements IDisposable { private static _loggerName: string = "Performance"; @@ -306,7 +317,9 @@ export class PerfLogger implements IDisposable { } } -/** Helps with macro-substitution */ +/** Helps with macro-substitution + * @alpha Better name? + */ export class EnvMacroSubst { /** Replace macros delimited by ${} assuming that they refer to environment variables. */ public static replace(str: string, defaultValues?: any): string { diff --git a/core/bentley/src/PriorityQueue.ts b/core/bentley/src/PriorityQueue.ts index 7895a78..f0a9051 100644 --- a/core/bentley/src/PriorityQueue.ts +++ b/core/bentley/src/PriorityQueue.ts @@ -7,11 +7,13 @@ import { CloneFunction, shallowClone } from "./SortedArray"; import { OrderedComparator } from "./Compare"; +/** @public */ export type ComputePriorityFunction = (value: T) => number; /** * A [priority queue](https://en.wikipedia.org/wiki/Priority_queue) implemented as a heap array. * The queue is ordered by an [[OrderedComparator]] function supplied by the user such that the value in the queue that compares less than all other values is always located at the front of the queue. + * @public */ export class PriorityQueue implements Iterable { protected _array: T[] = []; diff --git a/core/bentley/src/SortedArray.ts b/core/bentley/src/SortedArray.ts index 06d9aca..e5c5f11 100644 --- a/core/bentley/src/SortedArray.ts +++ b/core/bentley/src/SortedArray.ts @@ -9,6 +9,7 @@ import { OrderedComparator } from "./Compare"; /** * A function that, given a value of type T, returns a copy of that value. Such functions are used by various collection classes in the iModel.js library. * It is up to the function to decide how deeply or shallowly the value is cloned. For example, [[shallowClone]] simply returns the input. + * @public */ export type CloneFunction = (value: T) => T; @@ -17,6 +18,7 @@ export type CloneFunction = (value: T) => T; * Useful as a default argument for functions that can alternatively accept custom logic for cloning values of object type. * @param value The value to clone. * @returns the input value. + * @public */ export function shallowClone(value: T) { return value; } @@ -26,6 +28,7 @@ export function shallowClone(value: T) { return value; } * @param list An array of U already sorted according to the comparison criterion. * @param compare The function used to compare the value with elements in `list`. * @returns an object with 'index' corresponding to the computed position and 'equal' set to true if an equivalent element already exists at that index. + * @public */ export function lowerBound(value: T, list: U[], compare: OrderedComparator): { index: number, equal: boolean } { let low = 0; @@ -67,6 +70,7 @@ export function lowerBound(value: T, list: U[], compare: OrderedCompar * * Modifying an element in a way that affects the comparison function will produce unpredictable results, the * most likely of which is that the array will cease to be sorted. + * @public */ export class SortedArray implements Iterable { protected _array: T[] = []; diff --git a/core/bentley/src/StringUtils.ts b/core/bentley/src/StringUtils.ts index e33536f..88fca4e 100644 --- a/core/bentley/src/StringUtils.ts +++ b/core/bentley/src/StringUtils.ts @@ -8,7 +8,7 @@ // ###TODO use TextDecoder where available // From https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/Core/getStringFromTypedArray.js // which is itself inspired by https://github.com/inexorabletash/text-encoding -// @hidden +/** @internal */ namespace Utf8ToString { function inRange(a: number, min: number, max: number): boolean { return min <= a && a <= max; } @@ -117,6 +117,7 @@ namespace Utf8ToString { /** Given an array of bytes containing a utf-8 string, convert to a string. * @param utf8: An array of utf-8 characters as a byte array * @returns An equivalent string, or undefined if the array does not contain a valid utf-8 string. + * @public */ export function utf8ToString(utf8: Uint8Array): string | undefined { // ###TODO: if available: const decoder = new TextDecoder("utf-8"); @@ -128,6 +129,7 @@ export function utf8ToString(utf8: Uint8Array): string | undefined { * @param base64 The base-64-encoded string. * @returns the decoded byte array. * @throws DOMException if the length of the input string is not a multiple of 4. + * @public */ export function base64StringToUint8Array(base64: string): Uint8Array { return new Uint8Array(atob(base64).split("").map((c) => c.charCodeAt(0))); diff --git a/core/bentley/src/Time.ts b/core/bentley/src/Time.ts index 2e372eb..b4c9dcc 100644 --- a/core/bentley/src/Time.ts +++ b/core/bentley/src/Time.ts @@ -6,6 +6,7 @@ /** A duration of time. Can be either positive (towards future) or negative (in the past). * BeDurations are immutable. + * @public */ export class BeDuration { private readonly _milliseconds: number; @@ -46,6 +47,7 @@ export class BeDuration { /** A specific point in time relative to the current time. * BeTimePoints are used for timing operations. They are created from a BeDuration relative to the "now". * BeTimePoints are immutable. + * @public */ export class BeTimePoint { private readonly _milliseconds: number; @@ -93,7 +95,9 @@ export class BeTimePoint { public plus(duration: BeDuration) { return new BeTimePoint(this._milliseconds + duration.milliseconds); } } -/** A StopWatch for timing operations. */ +/** A StopWatch for timing operations. + * @public + */ export class StopWatch { private _start?: BeTimePoint; private _stop?: BeTimePoint; diff --git a/core/bentley/src/bentleyjs-core.ts b/core/bentley/src/bentleyjs-core.ts index 4a4d9ca..6c710b7 100644 --- a/core/bentley/src/bentleyjs-core.ts +++ b/core/bentley/src/bentleyjs-core.ts @@ -20,6 +20,14 @@ export * from "./StringUtils"; export * from "./Time"; export * from "./PriorityQueue"; +// Set the version number so it can be found at runtime. BUILD_SEMVER is replaced at build time by the webpack DefinePlugin. +declare var BUILD_SEMVER: string; +if ((typeof (BUILD_SEMVER) !== "undefined") && (typeof window !== "undefined") && window) { + if (!(window as any).iModelJsVersions) + (window as any).iModelJsVersions = new Map(); + (window as any).iModelJsVersions.set("bentleyjs-core", BUILD_SEMVER); +} + /** @module Utils */ /** @docs-package-description diff --git a/core/bentley/src/test/Disposable.test.ts b/core/bentley/src/test/Disposable.test.ts index 2f9fadb..9c9d627 100644 --- a/core/bentley/src/test/Disposable.test.ts +++ b/core/bentley/src/test/Disposable.test.ts @@ -51,7 +51,7 @@ describe("Disposable", () => { const disposable = new CallbackDisposable(() => { disposed = true; }); - await using(disposable, async () => { + await using(disposable, async (_r) => { return new Promise((resolve: () => void, _reject: () => void) => { setTimeout(() => { resolve(); @@ -67,7 +67,7 @@ describe("Disposable", () => { const disposable = new CallbackDisposable(() => { disposed = true; }); - const result = using(disposable, async () => { + const result = using(disposable, async (_r) => { return new Promise((_resolve: () => void, reject: () => void) => { setTimeout(() => { reject(); @@ -90,7 +90,7 @@ describe("Disposable", () => { const disposable2 = new CallbackDisposable(() => { disposed2 = true; }); - const result = using([disposable1, disposable2], (resource1: CallbackDisposable, resource2: CallbackDisposable) => { + const result = using([disposable1, disposable2], (resource1, resource2) => { assert.equal(resource1, disposable1); assert.equal(resource2, disposable2); return 123; diff --git a/core/bentley/src/test/Logger.test.ts b/core/bentley/src/test/Logger.test.ts index 362eda8..588c955 100644 --- a/core/bentley/src/test/Logger.test.ts +++ b/core/bentley/src/test/Logger.test.ts @@ -406,7 +406,7 @@ describe("Logger", () => { perfMessages.push(message); }, undefined); - using(new PerfLogger("mytestroutine"), () => { + using(new PerfLogger("mytestroutine"), (_r) => { for (let i = 0; i < 1000; i++) { if (i % 2 === 0) continue; @@ -416,7 +416,7 @@ describe("Logger", () => { Logger.setLevel("Performance", LogLevel.Info); - using(new PerfLogger("mytestroutine2"), () => { + using(new PerfLogger("mytestroutine2"), (_r) => { for (let i = 0; i < 1000; i++) { if (i % 2 === 0) continue; diff --git a/core/clients-backend/CHANGELOG.json b/core/clients-backend/CHANGELOG.json index 83c946a..48b034b 100644 --- a/core/clients-backend/CHANGELOG.json +++ b/core/clients-backend/CHANGELOG.json @@ -1,6 +1,51 @@ { "name": "@bentley/imodeljs-clients-backend", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/imodeljs-clients-backend_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "Added OidcAgentClientV2. This will replace OidcAgentClient after some fixes from IMS+Connect. " + }, + { + "comment": "ChangeSet and Briefcase downloads are atomic (i.e., will not be partially downloaded) and can simultaneously happen in multiple machines. " + }, + { + "comment": "Changes package.json to include api-extractor and adds api-extractor.json" + }, + { + "comment": "Use new buildIModelJsBuild script" + }, + { + "comment": "Moved AzureFileHandler, IOSAzureFileHandler, UrlFileHandler and the iModelHub tests to the imodeljs-clients-backend package. This removes the dependency of imodeljs-clients on the \"fs\" module, and turns it into a browser only package. " + }, + { + "comment": "Fixes to OidcDelegationClient-s. " + }, + { + "comment": "Remove uneeded typedoc plugin depedency" + }, + { + "comment": "Create iModel from empty template if seed file path not defined." + }, + { + "comment": "Removed RBAC client - the RBAC service is considered internal. " + }, + { + "comment": "upgrade to TypeScript 3.2.2" + } + ] + } + }, + { + "version": "0.188.0", + "tag": "@bentley/imodeljs-clients-backend_v0.188.0", + "date": "Wed, 16 Jan 2019 16:36:09 GMT", + "comments": {} + }, { "version": "0.187.0", "tag": "@bentley/imodeljs-clients-backend_v0.187.0", diff --git a/core/clients-backend/CHANGELOG.md b/core/clients-backend/CHANGELOG.md index 02b3731..aa0b308 100644 --- a/core/clients-backend/CHANGELOG.md +++ b/core/clients-backend/CHANGELOG.md @@ -1,6 +1,27 @@ # Change Log - @bentley/imodeljs-clients-backend -This log was last generated on Tue, 15 Jan 2019 15:18:59 GMT and should not be manually modified. +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- Added OidcAgentClientV2. This will replace OidcAgentClient after some fixes from IMS+Connect. +- ChangeSet and Briefcase downloads are atomic (i.e., will not be partially downloaded) and can simultaneously happen in multiple machines. +- Changes package.json to include api-extractor and adds api-extractor.json +- Use new buildIModelJsBuild script +- Moved AzureFileHandler, IOSAzureFileHandler, UrlFileHandler and the iModelHub tests to the imodeljs-clients-backend package. This removes the dependency of imodeljs-clients on the "fs" module, and turns it into a browser only package. +- Fixes to OidcDelegationClient-s. +- Remove uneeded typedoc plugin depedency +- Create iModel from empty template if seed file path not defined. +- Removed RBAC client - the RBAC service is considered internal. +- upgrade to TypeScript 3.2.2 + +## 0.188.0 +Wed, 16 Jan 2019 16:36:09 GMT + +*Version update only* ## 0.187.0 Tue, 15 Jan 2019 15:18:59 GMT diff --git a/core/clients-backend/package.json b/core/clients-backend/package.json index bf051aa..c8d8f68 100644 --- a/core/clients-backend/package.json +++ b/core/clients-backend/package.json @@ -1,23 +1,26 @@ { "name": "@bentley/imodeljs-clients-backend", - "version": "0.187.0", + "version": "0.189.0", "description": "Clients for various Bentley Services used by iModel.js at the backend", "main": "lib/imodeljs-clients-backend.js", "typings": "lib/imodeljs-clients-backend", "license": "MIT", "engines": { - "node": ">=8.9.0 <9.0" + "node": ">=10.14.0 <11.0" }, "scripts": { - "build": "tsc 1>&2 && npm run copy:typedefs", + "build": "tsc 1>&2 && npm run copy:typedefs && node ./node_modules/@bentley/build-tools/scripts/ignoreargs.js 1>&2", "copy:typedefs": "cpx \"./src/openid-client.d.ts\" ./lib/", + "copy:test-assets": "cpx \"./src/test/assets/**/*\" ./lib/test/assets", "clean": "rimraf lib package-deps.json", - "cover": "nyc npm test", - "cover:integration": "nyc --report-dir ./lib/test/coverage/integration npm run test-integration", + "cover": "npm run copy:test-assets && nyc npm test", + "cover:integration": "npm run copy:test-assets && nyc --report-dir ./lib/test/coverage/integration npm run test-integration", "docs": "node ./node_modules/@bentley/build-tools/scripts/docs.js --source=./src --includes=../../generated-docs/extract --json=../../generated-docs/core/imodeljs-clients-backend/file.json --tsIndexFile=imodeljs-clients-backend.ts --onlyJson %TYPEDOC_THEME%", + "extract-api": "node ./node_modules/@bentley/build-tools/scripts/extract-api.js --entry=imodeljs-clients-backend", "lint": "tslint --project . 1>&2", - "test": "node ./node_modules/@bentley/build-tools/scripts/test.js --grep=\"integration\" --invert", - "test-integration": "node ./node_modules/@bentley/build-tools/scripts/test.js", + "pretest": "rimraf \"./lib/test/*.log\" && npm run copy:test-assets", + "test": "npm run pretest && node ./node_modules/@bentley/build-tools/scripts/test.js --offline=\"mock\" --grep=\"#integration|iModelHub URL Whitelist Validator\" --invert", + "test-integration": "npm run pretest && node ./node_modules/@bentley/build-tools/scripts/test.js", "watch": "npm run docs && bmsWatch --src ./lib/docs/json --destination ./public" }, "repository": { @@ -32,37 +35,44 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "0.187.0" + "@bentley/bentleyjs-core": "0.189.0" }, "//devDependencies": [ "NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install", "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/config-loader": "0.187.0", - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/build-tools": "0.187.0", + "@bentley/config-loader": "0.189.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/build-tools": "0.189.0", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", "@types/nock": "^9.1.2", - "@types/node": "10.10.3", + "@types/node": "10.12.18", + "@types/deep-assign": "^0.1.0", + "@types/fs-extra": "^4.0.7", + "@types/js-base64": "^2.3.1", "chai": "^4.1.2", "cpx": "^1.5.0", "nock": "^9.2.3", "nyc": "^13.0.1", "mocha": "^5.2.0", + "rimraf": "^2.6.2", "source-map-support": "^0.5.6", "ts-node": "^7.0.1", "tslint": "^5.11.0", "typedoc": "^0.11.1", - "typedoc-plugin-external-module-name": "^1.1.1", - "typescript": "~3.1.0" + "typescript": "~3.2.2" }, "dependencies": { - "@bentley/imodeljs-clients": "0.187.0", + "@bentley/imodeljs-clients": "0.189.0", "openid-client": "^2.3.1", "@openid/appauth": "^1.1.1", - "https-proxy-agent": "^2.2.1" + "https-proxy-agent": "^2.2.1", + "deep-assign": "^2.0.0", + "fs-extra": "^6.0.1", + "js-base64": "^2.4.5", + "fs-write-stream-atomic": "^1.0.10" }, "nyc": { "nycrc-path": "./node_modules/@bentley/build-tools/.nycrc" diff --git a/core/clients-backend/src/@types/fs-write-stream-atomic/index.d.ts b/core/clients-backend/src/@types/fs-write-stream-atomic/index.d.ts new file mode 100644 index 0000000..895d75d --- /dev/null +++ b/core/clients-backend/src/@types/fs-write-stream-atomic/index.d.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/// + +declare module 'fs-write-stream-atomic' { + + import stream = require('stream'); + + class WriteStreamAtomic extends stream.Writable { + public constructor(path: string, options?: any); + } + + export = WriteStreamAtomic; +} diff --git a/core/clients/src/UrlFileHandler.ts b/core/clients-backend/src/UrlFileHandler.ts similarity index 92% rename from core/clients/src/UrlFileHandler.ts rename to core/clients-backend/src/UrlFileHandler.ts index 212a831..8734abd 100644 --- a/core/clients/src/UrlFileHandler.ts +++ b/core/clients-backend/src/UrlFileHandler.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ /** @module Utils */ -import { ProgressInfo } from "./Request"; -import { FileHandler } from "./FileHandler"; +import { ActivityLoggingContext } from "@bentley/bentleyjs-core"; +import { ProgressInfo, FileHandler } from "@bentley/imodeljs-clients"; import * as fs from "fs-extra"; import * as path from "path"; import * as https from "https"; import { URL } from "url"; -import { ActivityLoggingContext } from "@bentley/bentleyjs-core"; +import WriteStreamAtomic = require("fs-write-stream-atomic"); /** * Provides methods to upload and download files from the Internet @@ -41,7 +41,7 @@ export class UrlFileHandler implements FileHandler { if (response.statusCode !== 200) { reject(); } else { - const target = fs.createWriteStream(downloadToPathname); + const target = new WriteStreamAtomic(downloadToPathname); target.on("error", (err) => { reject(err); }); diff --git a/core/clients/src/imodelhub/AzureFileHandler.ts b/core/clients-backend/src/imodelhub/AzureFileHandler.ts similarity index 92% rename from core/clients/src/imodelhub/AzureFileHandler.ts rename to core/clients-backend/src/imodelhub/AzureFileHandler.ts index 6d68ea2..cf8555f 100644 --- a/core/clients/src/imodelhub/AzureFileHandler.ts +++ b/core/clients-backend/src/imodelhub/AzureFileHandler.ts @@ -4,15 +4,13 @@ *--------------------------------------------------------------------------------------------*/ /** @module iModelHub */ -import { request, RequestOptions, ProgressInfo, ResponseError } from "../Request"; import { Logger, ActivityLoggingContext } from "@bentley/bentleyjs-core"; -import { FileHandler } from "../FileHandler"; -import { ArgumentCheck } from "./Errors"; +import { request, RequestOptions, ProgressInfo, ResponseError, FileHandler, ArgumentCheck } from "@bentley/imodeljs-clients"; import * as fs from "fs"; import * as path from "path"; import * as https from "https"; import { Transform, TransformCallback } from "stream"; - +import WriteStreamAtomic = require("fs-write-stream-atomic"); const loggingCategory = "imodeljs-clients.imodelhub"; /** @@ -99,33 +97,35 @@ export class AzureFileHandler implements FileHandler { AzureFileHandler.makeDirectoryRecursive(path.dirname(downloadToPathname)); const bufferedStream = new BufferedStream(this._threshold); - const fileStream = fs.createWriteStream(downloadToPathname, "binary"); - const bytesWritten: () => number = () => fileStream.bytesWritten; + const fileStream = new WriteStreamAtomic(downloadToPathname, { encoding: "binary" }); + let bytesWritten: number = 0; + if (progressCallback) { fileStream.on("drain", () => { - progressCallback({ loaded: bytesWritten(), total: fileSize, percent: fileSize ? bytesWritten() / fileSize : 0 }); + progressCallback({ loaded: bytesWritten, total: fileSize, percent: fileSize ? bytesWritten / fileSize : 0 }); }); fileStream.on("finish", () => { - progressCallback({ loaded: bytesWritten(), total: fileSize, percent: fileSize ? bytesWritten() / fileSize : 0 }); + progressCallback({ loaded: bytesWritten, total: fileSize, percent: fileSize ? bytesWritten / fileSize : 0 }); }); } try { await new Promise((resolve, reject) => { https.get(downloadUrl, ((res) => { - res.pipe(bufferedStream).pipe(fileStream) + res.pipe(bufferedStream) + .on("data", (chunk: any) => { + bytesWritten += chunk.length; + }) + .pipe(fileStream) .on("error", (error: any) => { - fileStream.close(); const parsedError = ResponseError.parse(error); reject(parsedError); }) .on("finish", () => { - fileStream.close(); resolve(); }); })) .on("error", (error: any) => { - fileStream.close(); const parsedError = ResponseError.parse(error); reject(parsedError); }); diff --git a/core/clients/src/imodelhub/IOSAzureFileHandler.ts b/core/clients-backend/src/imodelhub/IOSAzureFileHandler.ts similarity index 95% rename from core/clients/src/imodelhub/IOSAzureFileHandler.ts rename to core/clients-backend/src/imodelhub/IOSAzureFileHandler.ts index 511f280..2513ee3 100644 --- a/core/clients/src/imodelhub/IOSAzureFileHandler.ts +++ b/core/clients-backend/src/imodelhub/IOSAzureFileHandler.ts @@ -3,11 +3,8 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ /** @module iModelHub */ - -import { request, RequestOptions } from "../Request"; import { Logger, ActivityLoggingContext } from "@bentley/bentleyjs-core"; -import { FileHandler } from "../FileHandler"; -import { ArgumentCheck } from "./Errors"; +import { request, RequestOptions, FileHandler, ArgumentCheck } from "@bentley/imodeljs-clients"; import * as fs from "fs"; import * as path from "path"; const loggingCategory = "imodeljs-clients.imodelhub"; diff --git a/core/clients-backend/src/imodeljs-clients-backend.ts b/core/clients-backend/src/imodeljs-clients-backend.ts index f7a822e..dc21abf 100644 --- a/core/clients-backend/src/imodeljs-clients-backend.ts +++ b/core/clients-backend/src/imodeljs-clients-backend.ts @@ -4,3 +4,6 @@ *--------------------------------------------------------------------------------------------*/ export * from "./oidc"; export * from "./RequestHost"; +export * from "./imodelhub/AzureFileHandler"; +export * from "./imodelhub/IOSAzureFileHandler"; +export * from "./UrlFileHandler"; diff --git a/core/clients-backend/src/oidc/OidcAgentClient.ts b/core/clients-backend/src/oidc/OidcAgentClient.ts index bc4d51c..ec48ead 100644 --- a/core/clients-backend/src/oidc/OidcAgentClient.ts +++ b/core/clients-backend/src/oidc/OidcAgentClient.ts @@ -1,45 +1,93 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ - -import { OidcBackendClientConfiguration, OidcBackendClient } from "./OidcBackendClient"; -import { OidcDelegationClient } from "./OidcDelegationClient"; -import { AuthorizationToken, AccessToken, ImsActiveSecureTokenClient, ImsDelegationSecureTokenClient } from "@bentley/imodeljs-clients"; -import { ActivityLoggingContext, BentleyStatus, BentleyError } from "@bentley/bentleyjs-core"; - -/** Client configuration to create OIDC/OAuth tokens for agent applications */ -export interface OidcAgentClientConfiguration extends OidcBackendClientConfiguration { - serviceUserEmail: string; - serviceUserPassword: string; -} - -/** Utility to generate OIDC/OAuth tokens for agent or service applications */ -export class OidcAgentClient extends OidcBackendClient { - constructor(private _agentConfiguration: OidcAgentClientConfiguration) { - super(_agentConfiguration as OidcBackendClientConfiguration); - } - - public async getToken(actx: ActivityLoggingContext): Promise { - // Note: for now we start with an IMS saml token, and use OIDC delegation to get a JWT token - const authToken: AuthorizationToken = await (new ImsActiveSecureTokenClient()).getToken(actx, this._agentConfiguration.serviceUserEmail, this._agentConfiguration.serviceUserPassword, this._configuration.clientId); - const samlToken: AccessToken = await (new ImsDelegationSecureTokenClient()).getToken(actx, authToken); - - const delegationClient = new OidcDelegationClient(this._configuration); - const jwt: AccessToken = await delegationClient.getJwtFromSaml(actx, samlToken); - return jwt; - } - - public async refreshToken(actx: ActivityLoggingContext, jwt: AccessToken): Promise { - actx.enter(); - - // Refresh 1 minute before expiry - const expiresAt = jwt.getExpiresAt(); - if (!expiresAt) - throw new BentleyError(BentleyStatus.ERROR, "Invalid JWT passed to refresh"); - if ((expiresAt.getTime() - Date.now()) > 1 * 60 * 1000) - return jwt; - - return this.getToken(actx); - } -} +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { GrantParams, TokenSet } from "openid-client"; +import { AuthorizationToken, AccessToken, ImsActiveSecureTokenClient, ImsDelegationSecureTokenClient, IncludePrefix } from "@bentley/imodeljs-clients"; +import { ActivityLoggingContext, BentleyStatus, BentleyError } from "@bentley/bentleyjs-core"; +import { OidcBackendClientConfiguration, OidcBackendClient } from "./OidcBackendClient"; +import { OidcDelegationClient } from "./OidcDelegationClient"; + +// @todo: We are using the V1 version of this API for now. +// Migrate to V2 after the Connect + IMS team can support - +// * Setting up a way for the agent client's "user" ({client_id}@apps.imsoidc.bentley.com) +// to access Connect projects without the need to accept EULA agreements. +// * Provide a friendly name for this "user" - it currently shows up in Connect +// with the first and last names as the above email instead of the client's name + +/** Client configuration to create OIDC/OAuth tokens for agent applications */ +export interface OidcAgentClientConfiguration extends OidcBackendClientConfiguration { + serviceUserEmail: string; + serviceUserPassword: string; +} + +/** Utility to generate OIDC/OAuth tokens for agent or service applications */ +export class OidcAgentClient extends OidcBackendClient { + constructor(private _agentConfiguration: OidcAgentClientConfiguration) { + super(_agentConfiguration as OidcBackendClientConfiguration); + } + + public async getToken(actx: ActivityLoggingContext): Promise { + // Note: for now we start with an IMS saml token, and use OIDC delegation to get a JWT token + const authToken: AuthorizationToken = await (new ImsActiveSecureTokenClient()).getToken(actx, this._agentConfiguration.serviceUserEmail, this._agentConfiguration.serviceUserPassword, this._configuration.clientId); + const samlToken: AccessToken = await (new ImsDelegationSecureTokenClient()).getToken(actx, authToken); + + const delegationClient = new OidcDelegationClient(this._configuration); + const jwt: AccessToken = await delegationClient.getJwtFromSaml(actx, samlToken); + return jwt; + } + + public async refreshToken(actx: ActivityLoggingContext, jwt: AccessToken): Promise { + actx.enter(); + + // Refresh 1 minute before expiry + const expiresAt = jwt.getExpiresAt(); + if (!expiresAt) + throw new BentleyError(BentleyStatus.ERROR, "Invalid JWT passed to refresh"); + if ((expiresAt.getTime() - Date.now()) > 1 * 60 * 1000) + return jwt; + + return this.getToken(actx); + } +} + +export type OidcAgentClientConfigurationV2 = OidcBackendClientConfiguration; + +/** Utility to generate OIDC/OAuth tokens for agent or service applications */ +export class OidcAgentClientV2 extends OidcBackendClient { + constructor(agentConfiguration: OidcAgentClientConfigurationV2) { + super(agentConfiguration); + } + + public async getToken(actx: ActivityLoggingContext): Promise { + const scope = this._configuration.scope; + if (scope.includes("openid") || scope.includes("email") || scope.includes("profile") || scope.includes("organization")) + throw new BentleyError(BentleyStatus.ERROR, "Scopes for an Agent cannot include 'openid email profile organization'"); + + const grantParams: GrantParams = { + grant_type: "client_credentials", + scope, + }; + + const client = await this.getClient(actx); + const tokenSet: TokenSet = await client.grant(grantParams); + return this.createToken(tokenSet); + } + + /** Refresh the supplied JSON Web Token (assuming the client was registered for offline access) */ + public async refreshToken(actx: ActivityLoggingContext, jwt: AccessToken): Promise { + actx.enter(); + + // Refresh 1 minute before expiry + const expiresAt = jwt.getExpiresAt(); + if (!expiresAt) + throw new BentleyError(BentleyStatus.ERROR, "Invalid JWT passed to refresh"); + if ((expiresAt.getTime() - Date.now()) > 1 * 60 * 1000) + return jwt; + + const client = await this.getClient(actx); + const tokenSet: TokenSet = await client.refresh(jwt.toTokenString(IncludePrefix.No)!); + return this.createToken(tokenSet); + } +} diff --git a/core/clients-backend/src/oidc/OidcBackendClient.ts b/core/clients-backend/src/oidc/OidcBackendClient.ts index 102ed3b..c0005b4 100644 --- a/core/clients-backend/src/oidc/OidcBackendClient.ts +++ b/core/clients-backend/src/oidc/OidcBackendClient.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { OidcClient, AccessToken, IncludePrefix, UserInfo } from "@bentley/imodeljs-clients"; -import { Issuer, Client as OpenIdClient, ClientConfiguration, TokenSet, UserInfo as OpenIdUserInfo } from "openid-client"; -import { ActivityLoggingContext, BentleyStatus, BentleyError } from "@bentley/bentleyjs-core"; +import { OidcClient, AccessToken, UserInfo } from "@bentley/imodeljs-clients"; +import { Issuer, Client as OpenIdClient, ClientConfiguration, TokenSet } from "openid-client"; +import { ActivityLoggingContext } from "@bentley/bentleyjs-core"; /** Client configuration to create OIDC/OAuth tokens for backend applications */ export interface OidcBackendClientConfiguration { @@ -66,27 +66,10 @@ export abstract class OidcBackendClient extends OidcClient { return this._client; } - protected createToken(tokenSet: TokenSet, openIdUserInfo: OpenIdUserInfo): AccessToken { + protected createToken(tokenSet: TokenSet, userInfo?: UserInfo): AccessToken { const startsAt: Date = new Date(tokenSet.expires_at - tokenSet.expires_in); const expiresAt: Date = new Date(tokenSet.expires_at); - const userInfo = UserInfo.fromJson(openIdUserInfo); return AccessToken.fromJsonWebTokenString(tokenSet.access_token, startsAt, expiresAt, userInfo); } - /** Refresh the supplied JSON Web Token (assuming the client was registered for offline access) */ - public async refreshToken(actx: ActivityLoggingContext, jwt: AccessToken): Promise { - actx.enter(); - - // Refresh 1 minute before expiry - const expiresAt = jwt.getExpiresAt(); - if (!expiresAt) - throw new BentleyError(BentleyStatus.ERROR, "Invalid JWT passed to refresh"); - if ((expiresAt.getTime() - Date.now()) > 1 * 60 * 1000) - return jwt; - - const client = await this.getClient(actx); - const tokenSet: TokenSet = await client.refresh(jwt.toTokenString(IncludePrefix.No)!); - const userInfo: OpenIdUserInfo = await client.userinfo(tokenSet.access_token); - return this.createToken(tokenSet, userInfo); - } } diff --git a/core/clients-backend/src/oidc/OidcDelegationClient.ts b/core/clients-backend/src/oidc/OidcDelegationClient.ts index d320148..cca3e8a 100644 --- a/core/clients-backend/src/oidc/OidcDelegationClient.ts +++ b/core/clients-backend/src/oidc/OidcDelegationClient.ts @@ -4,37 +4,34 @@ *--------------------------------------------------------------------------------------------*/ import { AccessToken, IncludePrefix } from "@bentley/imodeljs-clients"; -import { GrantParams, TokenSet, UserInfo as OpenIdUserInfo } from "openid-client"; +import { GrantParams, TokenSet } from "openid-client"; import { ActivityLoggingContext, BentleyStatus, BentleyError } from "@bentley/bentleyjs-core"; import { OidcBackendClientConfiguration, OidcBackendClient } from "./OidcBackendClient"; +export type OidcDelegationClientConfiguration = OidcBackendClientConfiguration; + /** Utility to generate delegation OAuth or legacy SAML tokens for backend applications */ export class OidcDelegationClient extends OidcBackendClient { /** * Creates an instance of OidcBackendClient. * @param deploymentEnv Deployment environment. */ - public constructor(configuration: OidcBackendClientConfiguration) { + public constructor(configuration: OidcDelegationClientConfiguration) { super(configuration); } private async exchangeToJwtToken(actx: ActivityLoggingContext, accessToken: AccessToken, grantType: string): Promise { actx.enter(); - const scope = this._configuration.scope; - if (!scope.includes("openid") || !scope.includes("email") || !scope.includes("profile") || !scope.includes("organization")) - throw new BentleyError(BentleyStatus.ERROR, "Scopes when fetching a JWT token must include 'openid email profile organization'"); - const grantParams: GrantParams = { grant_type: grantType, - scope, + scope: this._configuration.scope, assertion: accessToken.toTokenString(IncludePrefix.No), }; const client = await this.getClient(actx); const tokenSet: TokenSet = await client.grant(grantParams); - const userInfo: OpenIdUserInfo = await client.userinfo(tokenSet.access_token); - return this.createToken(tokenSet, userInfo); + return this.createToken(tokenSet, accessToken.getUserInfo()); } /** Get a JWT for the specified scope from a SAML token */ diff --git a/core/clients-backend/src/oidc/index.ts b/core/clients-backend/src/oidc/index.ts index 0700baa..ce837ee 100644 --- a/core/clients-backend/src/oidc/index.ts +++ b/core/clients-backend/src/oidc/index.ts @@ -3,6 +3,6 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ export * from "./OidcBackendClient"; -export * from "./OidcAgentClient"; export * from "./OidcDelegationClient"; export * from "./OidcDeviceClient"; +export * from "./OidcAgentClient"; diff --git a/core/clients-backend/src/test/HubAccessTestValidator.ts b/core/clients-backend/src/test/HubAccessTestValidator.ts new file mode 100644 index 0000000..1df10a6 --- /dev/null +++ b/core/clients-backend/src/test/HubAccessTestValidator.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import * as chai from "chai"; +import { AccessToken } from "@bentley/imodeljs-clients"; +import { TestConfig, TestUsers } from "./TestConfig"; + +chai.should(); + +/** Utility to test basic access to Connect, RBAC and iModelHub */ +export class HubAccessTestValidator { + private static _singletonInstance: HubAccessTestValidator; + + private constructor(private _testProjectName: string, private _testProjectId: string, private _testIModelName: string, private _testIModelId: string) { + } + + public static async getInstance(): Promise { + if (HubAccessTestValidator._singletonInstance) + return HubAccessTestValidator._singletonInstance; + + const accessToken: AccessToken = await TestConfig.getAccessToken(TestUsers.regular); + + const testProjectName = "iModelJsIntegrationTest"; + const testIModelName = "ReadOnlyTest"; + const testProjectId: string = await TestConfig.queryProjectId(accessToken, testProjectName); + const testIModelId: string = await TestConfig.queryIModelId(accessToken, testIModelName, testProjectId); + + HubAccessTestValidator._singletonInstance = new HubAccessTestValidator(testProjectName, testProjectId, testIModelName, testIModelId); + return HubAccessTestValidator._singletonInstance; + } + + public async validateConnectAccess(accessToken: AccessToken) { + const projectId = await TestConfig.queryProjectId(accessToken, this._testProjectName); + chai.expect(projectId).to.be.equal(this._testProjectId); + } + + public async validateIModelHubAccess(accessToken: AccessToken) { + const iModelId = await TestConfig.queryIModelId(accessToken, this._testIModelName, this._testProjectId); + chai.expect(iModelId).to.be.equal(this._testIModelId); + } +} diff --git a/core/clients-backend/src/test/OidcAgentClient.test.ts b/core/clients-backend/src/test/OidcAgentClient.test.ts new file mode 100644 index 0000000..50a1dff --- /dev/null +++ b/core/clients-backend/src/test/OidcAgentClient.test.ts @@ -0,0 +1,92 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import * as chai from "chai"; +import { Issuer } from "openid-client"; +import { ActivityLoggingContext } from "@bentley/bentleyjs-core"; +import { AccessToken, Config } from "@bentley/imodeljs-clients"; +import { IModelJsConfig } from "@bentley/config-loader/lib/IModelJsConfig"; +import { OidcAgentClient, OidcAgentClientConfiguration, OidcAgentClientV2, OidcAgentClientConfigurationV2 } from "../imodeljs-clients-backend"; +import { HubAccessTestValidator } from "./HubAccessTestValidator"; + +IModelJsConfig.init(true /* suppress exception */, false /* suppress error message */, Config.App); + +chai.should(); + +// @todo: We are using the V1 version of this API for now. +// Migrate to V2 after the Connect + IMS team can support - +// * Setting up a way for the agent client's "user" ({client_id}@apps.imsoidc.bentley.com) +// to access Connect projects without the need to accept EULA agreements. +// * Provide a friendly name for this "user" - it currently shows up in Connect +// with the first and last names as the above email instead of the client's name + +describe("OidcAgentClient (#integration)", () => { + + let validator: HubAccessTestValidator; + const actx = new ActivityLoggingContext(""); + + let agentConfiguration: OidcAgentClientConfiguration; + let agentConfigurationV2: OidcAgentClientConfigurationV2; + + before(async () => { + validator = await HubAccessTestValidator.getInstance(); + + agentConfiguration = { + clientId: Config.App.getString("imjs_agent_test_client_id"), + clientSecret: Config.App.getString("imjs_agent_test_client_secret"), + serviceUserEmail: Config.App.getString("imjs_agent_test_service_user_email"), + serviceUserPassword: Config.App.getString("imjs_agent_test_service_user_password"), + scope: "openid email profile organization context-registry-service imodelhub", + }; + + agentConfigurationV2 = { + clientId: Config.App.getString("imjs_agent_test_client_id_v2"), + clientSecret: Config.App.getString("imjs_agent_test_client_secret_v2"), + scope: "context-registry-service imodelhub", + }; + + }); + + it("should discover token end points correctly", async () => { + const client = new OidcAgentClient(agentConfiguration); + const url: string = await client.getUrl(actx); + + const issuer: Issuer = await client.discoverEndpoints(actx); + chai.expect(issuer.token_endpoint).equals(`${url}/connect/token`); + chai.expect(issuer.authorization_endpoint).equals(`${url}/connect/authorize`); + chai.expect(issuer.introspection_endpoint).equals(`${url}/connect/introspect`); + chai.expect(issuer.userinfo_endpoint).equals(`${url}/connect/userinfo`); + }); + + it("should get valid OIDC tokens for agent applications", async () => { + const agentClient = new OidcAgentClient(agentConfiguration); + const jwt: AccessToken = await agentClient.getToken(actx); + await validator.validateConnectAccess(jwt); + await validator.validateIModelHubAccess(jwt); + }); + + it("should discover token end points correctly (V2)", async () => { + const client = new OidcAgentClientV2(agentConfigurationV2); + const url: string = await client.getUrl(actx); + + const issuer: Issuer = await client.discoverEndpoints(actx); + chai.expect(issuer.token_endpoint).equals(`${url}/connect/token`); + chai.expect(issuer.authorization_endpoint).equals(`${url}/connect/authorize`); + chai.expect(issuer.introspection_endpoint).equals(`${url}/connect/introspect`); + }); + + // @todo: see note above. waiting for Connect support + it.skip("should get valid OIDC tokens for agent applications (V2)", async () => { + const agentClient = new OidcAgentClientV2(agentConfigurationV2); + const jwt: AccessToken = await agentClient.getToken(actx); + await validator.validateConnectAccess(jwt); + await validator.validateIModelHubAccess(jwt); + + const refreshJwt: AccessToken = await agentClient.refreshToken(actx, jwt); + await validator.validateConnectAccess(refreshJwt); + await validator.validateIModelHubAccess(refreshJwt); + }); + +}); diff --git a/core/clients-backend/src/test/OidcBackendClient.test.ts b/core/clients-backend/src/test/OidcBackendClient.test.ts deleted file mode 100644 index 6bdad77..0000000 --- a/core/clients-backend/src/test/OidcBackendClient.test.ts +++ /dev/null @@ -1,137 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ - -import * as chai from "chai"; -import { Issuer } from "openid-client"; -import { ActivityLoggingContext } from "@bentley/bentleyjs-core"; -import { - ImsActiveSecureTokenClient, AuthorizationToken, AccessToken, ImsDelegationSecureTokenClient, - ConnectClient, RbacClient, Project, ConnectRequestQueryOptions, RbacUser, - IModelHubClient, HubIModel, IModelQuery, Config, -} from "@bentley/imodeljs-clients"; -import { IModelJsConfig } from "@bentley/config-loader/lib/IModelJsConfig"; -import { OidcBackendClientConfiguration, OidcAgentClient, OidcAgentClientConfiguration, OidcDelegationClient } from "../imodeljs-clients-backend"; - -IModelJsConfig.init(true /* suppress exception */, false /* suppress error message */, Config.App); - -chai.should(); - -describe("OidcBackendClient (#integration)", () => { - const actx = new ActivityLoggingContext(""); - - let testUser: { email: string, password: string }; - let agentConfiguration: OidcAgentClientConfiguration; - - const testProjectName = "iModelJsIntegrationTest"; - const testIModelName = "ReadOnlyTest"; - let testProjectId: string; - let testIModelId: string; - - const getProjectId = async (accessToken: AccessToken, projectName: string): Promise => { - const connectClient = new ConnectClient(); - const queryOptions: ConnectRequestQueryOptions = { - $filter: "Name+eq+'" + projectName + "'", - }; - const project: Project = await connectClient.getProject(actx, accessToken, queryOptions); - chai.expect(!!project); - return project.wsgId; - }; - - const getIModelId = async (accessToken: AccessToken, iModelName: string, projectId: string): Promise => { - const hubClient = new IModelHubClient(); - const imodel: HubIModel = (await hubClient.iModels.get(actx, accessToken, projectId, new IModelQuery().byName(testIModelName)))[0]; - chai.expect(imodel.name).to.be.equal(iModelName); - return imodel.wsgId; - }; - - const validateConnectAccess = async (accessToken: AccessToken) => { - const projectId = await getProjectId(accessToken, testProjectName); - chai.expect(projectId).to.be.equal(testProjectId); - }; - - const validateRbacAccess = async (accessToken: AccessToken) => { - const rbacClient = new RbacClient(); - const users: RbacUser[] = await rbacClient.getUsers(actx, accessToken, testProjectId); - chai.expect(users.length !== 0); - }; - - const validateIModelHubAccess = async (accessToken: AccessToken) => { - const iModelId = await getIModelId(accessToken, testIModelName, testProjectId); - chai.expect(iModelId).to.be.equal(testIModelId); - }; - - before(async () => { - - testUser = { - email: Config.App.getString("imjs_test_regular_user_name"), - password: Config.App.getString("imjs_test_regular_user_password"), - }; - - agentConfiguration = { - clientId: Config.App.getString("imjs_agent_test_client_id"), - clientSecret: Config.App.getString("imjs_agent_test_client_secret"), - serviceUserEmail: Config.App.getString("imjs_agent_test_service_user_email"), - serviceUserPassword: Config.App.getString("imjs_agent_test_service_user_password"), - scope: "openid email profile organization", - }; - - const authToken: AuthorizationToken = await (new ImsActiveSecureTokenClient()).getToken(actx, testUser.email, testUser.password); - const samlToken: AccessToken = await (new ImsDelegationSecureTokenClient()).getToken(actx, authToken); - testProjectId = await getProjectId(samlToken, testProjectName); - testIModelId = await getIModelId(samlToken, testIModelName, testProjectId); - }); - - it("should discover token end points correctly", async () => { - const client = new OidcAgentClient(agentConfiguration); - const url: string = await client.getUrl(actx); - - const issuer: Issuer = await client.discoverEndpoints(actx); - chai.expect(issuer.token_endpoint).equals(`${url}/connect/token`); - chai.expect(issuer.authorization_endpoint).equals(`${url}/connect/authorize`); - chai.expect(issuer.introspection_endpoint).equals(`${url}/connect/introspect`); - chai.expect(issuer.userinfo_endpoint).equals(`${url}/connect/userinfo`); - }); - - it("should get valid OIDC tokens for agent applications", async () => { - const agentClient = new OidcAgentClient(agentConfiguration); - const jwt: AccessToken = await agentClient.getToken(actx); - await validateConnectAccess(jwt); - await validateIModelHubAccess(jwt); - }); - - it.skip("should get valid SAML delegation tokens", async () => { - const agentClient = new OidcAgentClient(agentConfiguration); - const jwt = await agentClient.getToken(actx); - - const delegationConfiguration: OidcBackendClientConfiguration = { - clientId: Config.App.getString("imjs_delegation_test_client_id"), - clientSecret: Config.App.getString("imjs_delegation_test_client_secret"), - scope: Config.App.getString("imjs_default_relying_party_uri"), - }; - - const delegationClient = new OidcDelegationClient(delegationConfiguration); - const saml = await delegationClient.getSamlFromJwt(actx, jwt); - await validateConnectAccess(saml); - await validateRbacAccess(saml); - await validateIModelHubAccess(saml); - }); - - it.skip("should get valid OIDC delegation tokens", async () => { - const agentClient = new OidcAgentClient(agentConfiguration); - const jwt = await agentClient.getToken(actx); - - const delegationConfiguration: OidcBackendClientConfiguration = { - clientId: Config.App.getString("imjs_delegation_test_client_id"), - clientSecret: Config.App.getString("imjs_delegation_test_client_secret"), - scope: "context-registry-service imodelhub rbac-service", - }; - const delegationClient = new OidcDelegationClient(delegationConfiguration); - const delegationJwt = await delegationClient.getJwtFromJwt(actx, jwt); - await validateConnectAccess(delegationJwt); - await validateRbacAccess(delegationJwt); - await validateIModelHubAccess(delegationJwt); - }); - -}); diff --git a/core/clients-backend/src/test/OidcDelegationClient.test.ts b/core/clients-backend/src/test/OidcDelegationClient.test.ts new file mode 100644 index 0000000..a4ad4b0 --- /dev/null +++ b/core/clients-backend/src/test/OidcDelegationClient.test.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import * as chai from "chai"; +import { ActivityLoggingContext } from "@bentley/bentleyjs-core"; +import { Config, AccessToken } from "@bentley/imodeljs-clients"; +import { IModelJsConfig } from "@bentley/config-loader/lib/IModelJsConfig"; +import { OidcDelegationClient, OidcDelegationClientConfiguration, OidcAgentClient, OidcAgentClientConfiguration } from "../imodeljs-clients-backend"; +import { HubAccessTestValidator } from "./HubAccessTestValidator"; + +IModelJsConfig.init(true /* suppress exception */, false /* suppress error message */, Config.App); + +chai.should(); + +// @todo: Work with OIDC team to get these tests working +describe("OidcDelegationClient (#integration)", () => { + + let validator: HubAccessTestValidator; + let jwt: AccessToken; + const actx = new ActivityLoggingContext(""); + + before(async () => { + validator = await HubAccessTestValidator.getInstance(); + + const agentConfiguration: OidcAgentClientConfiguration = { + clientId: Config.App.getString("imjs_agent_test_client_id"), + clientSecret: Config.App.getString("imjs_agent_test_client_secret"), + serviceUserEmail: Config.App.getString("imjs_agent_test_service_user_email"), + serviceUserPassword: Config.App.getString("imjs_agent_test_service_user_password"), + scope: "openid email profile organization imodeljs-backend-2686", + }; + + const agentClient = new OidcAgentClient(agentConfiguration); + jwt = await agentClient.getToken(actx); + }); + + it("should get valid SAML delegation tokens", async () => { + + const delegationConfiguration: OidcDelegationClientConfiguration = { + clientId: Config.App.getString("imjs_delegation_test_client_id"), + clientSecret: Config.App.getString("imjs_delegation_test_client_secret"), + scope: Config.App.getString("imjs_default_relying_party_uri"), + }; + + const delegationClient = new OidcDelegationClient(delegationConfiguration); + const saml = await delegationClient.getSamlFromJwt(actx, jwt); + await validator.validateConnectAccess(saml); + await validator.validateIModelHubAccess(saml); + }); + + it("should get valid OIDC delegation tokens", async () => { + const delegationConfiguration: OidcDelegationClientConfiguration = { + clientId: Config.App.getString("imjs_delegation_test_client_id"), + clientSecret: Config.App.getString("imjs_delegation_test_client_secret"), + scope: "context-registry-service imodelhub rbac-service", + }; + + const delegationClient = new OidcDelegationClient(delegationConfiguration); + const delegationJwt = await delegationClient.getJwtFromJwt(actx, jwt); + await validator.validateConnectAccess(delegationJwt); + await validator.validateIModelHubAccess(delegationJwt); + }); + +}); diff --git a/core/clients/src/test/ResponseBuilder.ts b/core/clients-backend/src/test/ResponseBuilder.ts similarity index 98% rename from core/clients/src/test/ResponseBuilder.ts rename to core/clients-backend/src/test/ResponseBuilder.ts index 7438502..f6aa9b6 100644 --- a/core/clients/src/test/ResponseBuilder.ts +++ b/core/clients-backend/src/test/ResponseBuilder.ts @@ -2,10 +2,9 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { ECJsonTypeMap, ECInstance, WsgInstance, ChangeState } from "../imodeljs-clients"; +import { ECJsonTypeMap, ECInstance, WsgInstance, ChangeState, Config } from "@bentley/imodeljs-clients"; import nock = require("nock"); import { TestConfig } from "./TestConfig"; -import { Config } from "../Config"; export enum RequestType { Get, diff --git a/core/clients-backend/src/test/TestConfig.ts b/core/clients-backend/src/test/TestConfig.ts new file mode 100644 index 0000000..00424e0 --- /dev/null +++ b/core/clients-backend/src/test/TestConfig.ts @@ -0,0 +1,151 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { expect } from "chai"; +import * as fs from "fs"; +import * as path from "path"; +import { Logger, LogLevel, ActivityLoggingContext, GuidString, Guid } from "@bentley/bentleyjs-core"; +import { IModelJsConfig } from "@bentley/config-loader/lib/IModelJsConfig"; +import { ImsActiveSecureTokenClient, ImsDelegationSecureTokenClient, AuthorizationToken, AccessToken, HubIModel, IModelHubClient, IModelClient, ConnectClient, Project, Config, IModelQuery } from "@bentley/imodeljs-clients"; +import { loggingCategoryFullUrl } from "@bentley/imodeljs-clients/lib/Request"; + +IModelJsConfig.init(true /* suppress exception */, false /* suppress error message */, Config.App); + +const actx = new ActivityLoggingContext(Guid.createValue()); + +const logFileStream = fs.createWriteStream(path.join(__dirname, "./iModelClientsTests.log"), { flags: "a" }); + +// The Request URLs are captured separate. The log file is used by the Hub URL whitelist validation. +export const urlLogPath = path.join(__dirname, "./requesturls.log"); +const urlLogFileStream = fs.createWriteStream(urlLogPath, { flags: "a" }); +console.log("URL Log file created at: " + urlLogPath); + +function logFunction(logLevel: string, category: string, message: string) { + if (category === loggingCategoryFullUrl) + urlLogFileStream.write(message + "\n"); + else + logFileStream.write(logLevel + "|" + category + "|" + message + "\n"); +} + +// Initialize logger to file +Logger.initialize( + (category: string, message: string): void => { logFunction("Error", category, message); }, + (category: string, message: string): void => { logFunction("Warning", category, message); }, + (category: string, message: string): void => { logFunction("Info", category, message); }, + (category: string, message: string): void => { logFunction("Trace", category, message); }); + +// Note: Turn this off unless really necessary - it causes Error messages on the +// console with the existing suite of tests, and this is quite misleading, +// especially when diagnosing CI job failures. +const loggingConfigFile: string | undefined = process.env.imjs_test_logging_config; +if (!!loggingConfigFile) { + // tslint:disable-next-line:no-var-requires + Logger.configureLevels(require(loggingConfigFile)); +} + +// log all request URLs as this will be the input to the Hub URL whitelist test +Logger.setLevel(loggingCategoryFullUrl, LogLevel.Trace); + +/** Credentials for test users */ +export interface UserCredentials { + email: string; + password: string; +} + +function isOfflineSet(): boolean { + const index = process.argv.indexOf("--offline"); + return process.argv[index + 1] === "mock"; +} + +/** Basic configuration used by all tests + */ +export class TestConfig { + /** Name of project used by most tests */ + public static readonly projectName: string = "iModelJsTest"; + public static readonly enableMocks: boolean = isOfflineSet(); + + /** Login the specified user and return the AuthorizationToken */ + public static async login(user: UserCredentials = TestUsers.regular): Promise { + if (Config.App.getNumber("imjs_buddi_resolve_url_using_region") !== 0) + process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; // Dev requires that SSL certificate checks be bypassed + + const authToken: AuthorizationToken | undefined = await (new ImsActiveSecureTokenClient()).getToken(actx, user.email, user.password); + expect(authToken); + + return authToken; + } + + /** Login the specified user and return the AccessToken */ + public static async getAccessToken(user: UserCredentials = TestUsers.regular): Promise { + const authToken: AuthorizationToken | undefined = await TestConfig.login(user); + + const accessToken: AccessToken = await (new ImsDelegationSecureTokenClient()).getToken(actx, authToken); + expect(accessToken); + + return accessToken; + } + + /** Query for the specified project */ + public static async queryProjectId(accessToken: AccessToken, projectName: string): Promise { + const connectClient = new ConnectClient(); + const project: Project | undefined = await connectClient.getProject(actx, accessToken, { + $select: "*", + $filter: `Name+eq+'${projectName}'`, + }); + if (!project || !project.wsgId) + throw new Error(`Project ${projectName} not found for user ${!accessToken.getUserInfo() ? "n/a" : accessToken.getUserInfo()!.email}.`); + return project.wsgId; + } + + /** Query for the specified iModel */ + public static async queryIModelId(accessToken: AccessToken, iModelName: string, projectId: GuidString): Promise { + const imodelHubClient: IModelClient = new IModelHubClient(); + const iModel: HubIModel = (await imodelHubClient.iModels.get(actx, accessToken, projectId, new IModelQuery().byName(iModelName)))[0]; + if (!iModel || !iModel.wsgId || iModel.name !== iModelName) + throw new Error(`iModel ${iModelName} not found for project ${projectId} for user ${!accessToken.getUserInfo() ? "n/a" : accessToken.getUserInfo()!.email}.`); + return iModel.wsgId; + } +} + +/** Test users with various permissions */ +export class TestUsers { + /** User with the typical permissions of the regular/average user - Co-Admin: No, Connect-Services-Admin: No */ + public static get regular(): UserCredentials { + return { + email: Config.App.getString("imjs_test_regular_user_name"), + password: Config.App.getString("imjs_test_regular_user_password"), + }; + } + + /** User with typical permissions of the project administrator - Co-Admin: Yes, Connect-Services-Admin: No */ + public static get manager(): UserCredentials { + return { + email: Config.App.getString("imjs_test_manager_user_name"), + password: Config.App.getString("imjs_test_manager_user_password"), + }; + } + + /** User with the typical permissions of the connected services administrator - Co-Admin: No, Connect-Services-Admin: Yes */ + public static get super(): UserCredentials { + return { + email: Config.App.getString("imjs_test_super_user_name"), + password: Config.App.getString("imjs_test_super_user_password"), + }; + } + + /** User with the typical permissions of the connected services administrator - Co-Admin: Yes, Connect-Services-Admin: Yes */ + public static get superManager(): UserCredentials { + return { + email: Config.App.getString("imjs_test_super_manager_user_name"), + password: Config.App.getString("imjs_test_super_manager_user_password"), + }; + } + public static get serviceAccount1(): UserCredentials { + return { + email: Config.App.getString("imjs_test_serviceAccount1_user_name"), + password: Config.App.getString("imjs_test_serviceAccount1_user_password"), + }; + } + +} diff --git a/core/clients/src/test/assets/LargerSeedFile.bim b/core/clients-backend/src/test/assets/LargerSeedFile.bim similarity index 100% rename from core/clients/src/test/assets/LargerSeedFile.bim rename to core/clients-backend/src/test/assets/LargerSeedFile.bim diff --git a/core/clients/src/test/assets/SeedFile/0_5b851177bf8f984364cbbf5401974fc6e07ac60d.cs b/core/clients-backend/src/test/assets/SeedFile/0_5b851177bf8f984364cbbf5401974fc6e07ac60d.cs similarity index 100% rename from core/clients/src/test/assets/SeedFile/0_5b851177bf8f984364cbbf5401974fc6e07ac60d.cs rename to core/clients-backend/src/test/assets/SeedFile/0_5b851177bf8f984364cbbf5401974fc6e07ac60d.cs diff --git a/core/clients/src/test/assets/SeedFile/1_4d66e2551816b0ae6b4d6c373b4708fb3700b49f.cs b/core/clients-backend/src/test/assets/SeedFile/1_4d66e2551816b0ae6b4d6c373b4708fb3700b49f.cs similarity index 100% rename from core/clients/src/test/assets/SeedFile/1_4d66e2551816b0ae6b4d6c373b4708fb3700b49f.cs rename to core/clients-backend/src/test/assets/SeedFile/1_4d66e2551816b0ae6b4d6c373b4708fb3700b49f.cs diff --git a/core/clients/src/test/assets/SeedFile/2_29519e2eb4a7234d20eacd9101a6d89b080bcaa4.cs b/core/clients-backend/src/test/assets/SeedFile/2_29519e2eb4a7234d20eacd9101a6d89b080bcaa4.cs similarity index 100% rename from core/clients/src/test/assets/SeedFile/2_29519e2eb4a7234d20eacd9101a6d89b080bcaa4.cs rename to core/clients-backend/src/test/assets/SeedFile/2_29519e2eb4a7234d20eacd9101a6d89b080bcaa4.cs diff --git a/core/clients/src/test/assets/SeedFile/3_ab161985348aeae375f83879f2b85de17201970c.cs b/core/clients-backend/src/test/assets/SeedFile/3_ab161985348aeae375f83879f2b85de17201970c.cs similarity index 100% rename from core/clients/src/test/assets/SeedFile/3_ab161985348aeae375f83879f2b85de17201970c.cs rename to core/clients-backend/src/test/assets/SeedFile/3_ab161985348aeae375f83879f2b85de17201970c.cs diff --git a/core/clients/src/test/assets/SeedFile/4_b5e6056dad772628cb08ac99cbf09443aeb33c7f.cs b/core/clients-backend/src/test/assets/SeedFile/4_b5e6056dad772628cb08ac99cbf09443aeb33c7f.cs similarity index 100% rename from core/clients/src/test/assets/SeedFile/4_b5e6056dad772628cb08ac99cbf09443aeb33c7f.cs rename to core/clients-backend/src/test/assets/SeedFile/4_b5e6056dad772628cb08ac99cbf09443aeb33c7f.cs diff --git a/core/clients/src/test/assets/SeedFile/5_41a76328c255fac6a3d9ef67cc3a1fe571eca639.cs b/core/clients-backend/src/test/assets/SeedFile/5_41a76328c255fac6a3d9ef67cc3a1fe571eca639.cs similarity index 100% rename from core/clients/src/test/assets/SeedFile/5_41a76328c255fac6a3d9ef67cc3a1fe571eca639.cs rename to core/clients-backend/src/test/assets/SeedFile/5_41a76328c255fac6a3d9ef67cc3a1fe571eca639.cs diff --git a/core/clients/src/test/assets/SeedFile/5d04b1852b434f97395f3e7bf7afb193603e2219.cs b/core/clients-backend/src/test/assets/SeedFile/5d04b1852b434f97395f3e7bf7afb193603e2219.cs similarity index 100% rename from core/clients/src/test/assets/SeedFile/5d04b1852b434f97395f3e7bf7afb193603e2219.cs rename to core/clients-backend/src/test/assets/SeedFile/5d04b1852b434f97395f3e7bf7afb193603e2219.cs diff --git a/core/clients/src/test/assets/SeedFile/6_5550add603b58986e58a7e359e27d73ecc048f29.cs b/core/clients-backend/src/test/assets/SeedFile/6_5550add603b58986e58a7e359e27d73ecc048f29.cs similarity index 100% rename from core/clients/src/test/assets/SeedFile/6_5550add603b58986e58a7e359e27d73ecc048f29.cs rename to core/clients-backend/src/test/assets/SeedFile/6_5550add603b58986e58a7e359e27d73ecc048f29.cs diff --git a/core/clients/src/test/assets/SeedFile/6ec2ce4097a5b80446598c195b5b0ed572eab68d.cs b/core/clients-backend/src/test/assets/SeedFile/6ec2ce4097a5b80446598c195b5b0ed572eab68d.cs similarity index 100% rename from core/clients/src/test/assets/SeedFile/6ec2ce4097a5b80446598c195b5b0ed572eab68d.cs rename to core/clients-backend/src/test/assets/SeedFile/6ec2ce4097a5b80446598c195b5b0ed572eab68d.cs diff --git a/core/clients/src/test/assets/SeedFile/782ef159-0918-40bc-be40-1d5575130ee8.bim b/core/clients-backend/src/test/assets/SeedFile/782ef159-0918-40bc-be40-1d5575130ee8.bim similarity index 100% rename from core/clients/src/test/assets/SeedFile/782ef159-0918-40bc-be40-1d5575130ee8.bim rename to core/clients-backend/src/test/assets/SeedFile/782ef159-0918-40bc-be40-1d5575130ee8.bim diff --git a/core/clients/src/test/assets/SeedFile/7_36bbcc58feaae6ee6437ff043a0f3bcb224dff4d.cs b/core/clients-backend/src/test/assets/SeedFile/7_36bbcc58feaae6ee6437ff043a0f3bcb224dff4d.cs similarity index 100% rename from core/clients/src/test/assets/SeedFile/7_36bbcc58feaae6ee6437ff043a0f3bcb224dff4d.cs rename to core/clients-backend/src/test/assets/SeedFile/7_36bbcc58feaae6ee6437ff043a0f3bcb224dff4d.cs diff --git a/core/clients/src/test/assets/SeedFile/8_eb6b54d5072c620457edae5de9fc62b116adc393.cs b/core/clients-backend/src/test/assets/SeedFile/8_eb6b54d5072c620457edae5de9fc62b116adc393.cs similarity index 100% rename from core/clients/src/test/assets/SeedFile/8_eb6b54d5072c620457edae5de9fc62b116adc393.cs rename to core/clients-backend/src/test/assets/SeedFile/8_eb6b54d5072c620457edae5de9fc62b116adc393.cs diff --git a/core/clients/src/test/assets/SeedFile/9_71788410b752b1955c86df4af7f3ab766a54ba52.cs b/core/clients-backend/src/test/assets/SeedFile/9_71788410b752b1955c86df4af7f3ab766a54ba52.cs similarity index 100% rename from core/clients/src/test/assets/SeedFile/9_71788410b752b1955c86df4af7f3ab766a54ba52.cs rename to core/clients-backend/src/test/assets/SeedFile/9_71788410b752b1955c86df4af7f3ab766a54ba52.cs diff --git a/core/clients/src/test/assets/whitelist.txt b/core/clients-backend/src/test/assets/whitelist.txt similarity index 98% rename from core/clients/src/test/assets/whitelist.txt rename to core/clients-backend/src/test/assets/whitelist.txt index 39c5616..0b6e97f 100644 --- a/core/clients/src/test/assets/whitelist.txt +++ b/core/clients-backend/src/test/assets/whitelist.txt @@ -1,79 +1,80 @@ -$changeset -GlobalScope/GlobalEventSAS/ -GlobalScope/GlobalEventSubscription/ -GlobalScope/GlobalEventSubscription/********-****-****-****-************ -GlobalScope/Subscriptions/********-****-****-****-************/messages/*/********-****-****-****-************ -GlobalScope/Subscriptions/********-****-****-****-************/messages/head -GlobalScope/Subscriptions/********-****-****-****-************/messages/head?timeout=* -iModelScope/Briefcase/ -iModelScope/Briefcase/* -iModelScope/Briefcase/*?$select=*,FileAccessKey-forward-AccessKey.DownloadURL -iModelScope/ChangeSet/ -iModelScope/ChangeSet/**************************************** -iModelScope/ChangeSet/?$filter=(CumulativeChangeSet-backward-ChangeSet.Id+eq+'****************************************'+and+FollowingChangeSet-backward-ChangeSet.Id+eq+'****************************************')+or+(CumulativeChangeSet-backward-ChangeSet.Id+eq+'****************************************'+and+FollowingChangeSet-backward-ChangeSet.Id+eq+'****************************************') -iModelScope/ChangeSet/?$filter=(CumulativeChangeSet-backward-Version.Id+eq+'********-****-****-****-************'+and+FollowingChangeSet-backward-ChangeSet.Id+eq+'****************************************')+or+(FollowingChangeSet-backward-Version.Id+eq+'********-****-****-****-************'+and+CumulativeChangeSet-backward-ChangeSet.Id+eq+'****************************************') -iModelScope/ChangeSet/?$filter=(FollowingChangeSet-backward-Version.Id+eq+'********-****-****-****-************'+and+CumulativeChangeSet-backward-Version.Id+eq+'********-****-****-****-************')+or+(FollowingChangeSet-backward-Version.Id+eq+'********-****-****-****-************'+and+CumulativeChangeSet-backward-Version.Id+eq+'********-****-****-****-************') -iModelScope/ChangeSet/?$filter=CumulativeChangeSet-backward-ChangeSet.Id+eq+'****************************************' -iModelScope/ChangeSet/?$filter=CumulativeChangeSet-backward-Version.Id+eq+'********-****-****-****-************' -iModelScope/ChangeSet/?$filter=FollowingChangeSet-backward-ChangeSet.Id+eq+'****************************************' -iModelScope/ChangeSet/?$filter=FollowingChangeSet-backward-Version.Id+eq+'********-****-****-****-************' -iModelScope/ChangeSet/?$filter=SeedFileId+eq+'********-****-****-****-************' -iModelScope/ChangeSet/?$orderby=Index+desc&$top=* -iModelScope/ChangeSet/?$select=*,FileAccessKey-forward-AccessKey.DownloadURL -iModelScope/ChangeSet/?$skip=* -iModelScope/Code/$query -iModelScope/Code/DiscardReservedCodes-* -iModelScope/CodeSequence/ -iModelScope/EventSAS/ -iModelScope/EventSubscription/ -iModelScope/EventSubscription/********-****-****-****-************ -iModelScope/LargeThumbnail/ -iModelScope/LargeThumbnail/********-****-****-****-************ -iModelScope/LargeThumbnail/********-****-****-****-************/$file -iModelScope/LargeThumbnail/?$filter=HasThumbnail-backward-Version.Id+eq+'********-****-****-****-************' -iModelScope/Lock/$query -iModelScope/Lock/DeleteAll-* -iModelScope/MultiCode/ -iModelScope/MultiCode/?$filter=BriefcaseId+eq+* -iModelScope/MultiCode/?$filter=BriefcaseId+eq+*+and+CodeSpecId+eq+'*' -iModelScope/MultiCode/?$filter=BriefcaseId+ne+* -iModelScope/MultiCode/?$filter=CodeScope+eq+'*' -iModelScope/MultiCode/?$filter=CodeSpecId+eq+'*' -iModelScope/MultiCode/?$select=Values -iModelScope/MultiLock/ -iModelScope/MultiLock/?$filter=BriefcaseId+eq+* -iModelScope/MultiLock/?$filter=BriefcaseId+ne+*+and+(LockLevel+gt+*+or+ReleasedWithChangeSetIndex+gt+*) -iModelScope/MultiLock/?$filter=LockLevel+eq+*+and+LockType+eq+* -iModelScope/MultiLock/?$filter=ReleasedWithChangeSet+eq+'****************************************' -iModelScope/MultiLock/?$filter=ReleasedWithChangeSetIndex+eq+* -iModelScope/SeedFile/ -iModelScope/SeedFile/********-****-****-****-************ -iModelScope/SeedFile/?$orderby=Index+desc -iModelScope/SeedFile/?$select=*,FileAccessKey-forward-AccessKey.DownloadURL&$orderby=Index+desc -iModelScope/SmallThumbnail/ -iModelScope/SmallThumbnail/********-****-****-****-************ -iModelScope/SmallThumbnail/********-****-****-****-************/$file -iModelScope/SmallThumbnail/?$filter=HasThumbnail-backward-Version.Id+eq+'********-****-****-****-************' -iModelScope/Subscriptions/********-****-****-****-************/messages/head -iModelScope/Subscriptions/********-****-****-****-************/messages/head?timeout=* -iModelScope/UserInfo/$query -iModelScope/UserInfo/********-****-****-****-************ -iModelScope/UserInfo/********-****-****-****-************?$select=*,HasStatistics-forward-Statistics.BriefcasesCount -iModelScope/UserInfo/********-****-****-****-************?$select=*,HasStatistics-forward-Statistics.BriefcasesCount,HasStatistics-forward-Statistics.OwnedLocksCount -iModelScope/UserInfo/********-****-****-****-************?$select=*,HasStatistics-forward-Statistics.LastChangeSetPushDate -iModelScope/UserInfo/********-****-****-****-************?$select=*,HasStatistics-forward-Statistics.OwnedLocksCount -iModelScope/UserInfo/********-****-****-****-************?$select=*,HasStatistics-forward-Statistics.PushedChangeSetsCount -iModelScope/UserInfo/********-****-****-****-************?$select=*,HasStatistics-forward-Statistics.PushedChangeSetsCount,HasStatistics-forward-Statistics.LastChangeSetPushDate -iModelScope/UserInfo/?$select=*,HasStatistics-forward-Statistics.* -iModelScope/UserInfo/?$select=*,HasStatistics-forward-Statistics.BriefcasesCount -iModelScope/Version/ -iModelScope/Version/********-****-****-****-************ -iModelScope/Version/********-****-****-****-************?$select=*,HasThumbnail-forward-SmallThumbnail.*,HasThumbnail-forward-LargeThumbnail.* -iModelScope/Version/?$filter=ChangeSetId+eq+'****************************************' -iModelScope/Version/?$filter=Name+eq+'*'&$select=*,HasThumbnail-forward-LargeThumbnail.* -ProjectScope/LargeThumbnail/********-****-****-****-************/$file -ProjectScope/SmallThumbnail/********-****-****-****-************/$file -ProjectScope/iModel/ -ProjectScope/iModel/********-****-****-****-************ -ProjectScope/iModel/?$filter=Name+eq+'*' +$changeset +GlobalScope/GlobalEventSAS/ +GlobalScope/GlobalEventSubscription/ +GlobalScope/GlobalEventSubscription/********-****-****-****-************ +GlobalScope/Subscriptions/********-****-****-****-************/messages/*/********-****-****-****-************ +GlobalScope/Subscriptions/********-****-****-****-************/messages/head +GlobalScope/Subscriptions/********-****-****-****-************/messages/head?timeout=* +iModelScope/Briefcase/ +iModelScope/Briefcase/* +iModelScope/Briefcase/*?$select=*,FileAccessKey-forward-AccessKey.DownloadURL +iModelScope/ChangeSet/ +iModelScope/ChangeSet/**************************************** +iModelScope/ChangeSet/?$filter=(CumulativeChangeSet-backward-ChangeSet.Id+eq+'****************************************'+and+FollowingChangeSet-backward-ChangeSet.Id+eq+'****************************************')+or+(CumulativeChangeSet-backward-ChangeSet.Id+eq+'****************************************'+and+FollowingChangeSet-backward-ChangeSet.Id+eq+'****************************************') +iModelScope/ChangeSet/?$filter=(CumulativeChangeSet-backward-Version.Id+eq+'********-****-****-****-************'+and+FollowingChangeSet-backward-ChangeSet.Id+eq+'****************************************')+or+(FollowingChangeSet-backward-Version.Id+eq+'********-****-****-****-************'+and+CumulativeChangeSet-backward-ChangeSet.Id+eq+'****************************************') +iModelScope/ChangeSet/?$filter=(FollowingChangeSet-backward-Version.Id+eq+'********-****-****-****-************'+and+CumulativeChangeSet-backward-Version.Id+eq+'********-****-****-****-************')+or+(FollowingChangeSet-backward-Version.Id+eq+'********-****-****-****-************'+and+CumulativeChangeSet-backward-Version.Id+eq+'********-****-****-****-************') +iModelScope/ChangeSet/?$filter=CumulativeChangeSet-backward-ChangeSet.Id+eq+'****************************************' +iModelScope/ChangeSet/?$filter=CumulativeChangeSet-backward-Version.Id+eq+'********-****-****-****-************' +iModelScope/ChangeSet/?$filter=FollowingChangeSet-backward-ChangeSet.Id+eq+'****************************************' +iModelScope/ChangeSet/?$filter=FollowingChangeSet-backward-Version.Id+eq+'********-****-****-****-************' +iModelScope/ChangeSet/?$filter=SeedFileId+eq+'********-****-****-****-************' +iModelScope/ChangeSet/?$orderby=Index+desc&$top=* +iModelScope/ChangeSet/?$select=*,FileAccessKey-forward-AccessKey.DownloadURL +iModelScope/ChangeSet/?$skip=* +iModelScope/Code/$query +iModelScope/Code/DiscardReservedCodes-* +iModelScope/CodeSequence/ +iModelScope/EventSAS/ +iModelScope/EventSubscription/ +iModelScope/EventSubscription/********-****-****-****-************ +iModelScope/LargeThumbnail/ +iModelScope/LargeThumbnail/********-****-****-****-************ +iModelScope/LargeThumbnail/********-****-****-****-************/$file +iModelScope/LargeThumbnail/?$filter=HasThumbnail-backward-Version.Id+eq+'********-****-****-****-************' +iModelScope/Lock/$query +iModelScope/Lock/DeleteAll-* +iModelScope/MultiCode/ +iModelScope/MultiCode/?$filter=BriefcaseId+eq+* +iModelScope/MultiCode/?$filter=BriefcaseId+eq+*+and+CodeSpecId+eq+'*' +iModelScope/MultiCode/?$filter=BriefcaseId+ne+* +iModelScope/MultiCode/?$filter=CodeScope+eq+'*' +iModelScope/MultiCode/?$filter=CodeSpecId+eq+'*' +iModelScope/MultiCode/?$select=Values +iModelScope/MultiLock/ +iModelScope/MultiLock/?$filter=BriefcaseId+eq+* +iModelScope/MultiLock/?$filter=BriefcaseId+ne+*+and+(LockLevel+gt+*+or+ReleasedWithChangeSetIndex+gt+*) +iModelScope/MultiLock/?$filter=LockLevel+eq+*+and+LockType+eq+* +iModelScope/MultiLock/?$filter=ReleasedWithChangeSet+eq+'****************************************' +iModelScope/MultiLock/?$filter=ReleasedWithChangeSetIndex+eq+* +iModelScope/SeedFile/ +iModelScope/SeedFile/********-****-****-****-************ +iModelScope/SeedFile/?$orderby=Index+desc +iModelScope/SeedFile/?$select=*,FileAccessKey-forward-AccessKey.DownloadURL&$orderby=Index+desc +iModelScope/SmallThumbnail/ +iModelScope/SmallThumbnail/********-****-****-****-************ +iModelScope/SmallThumbnail/********-****-****-****-************/$file +iModelScope/SmallThumbnail/?$filter=HasThumbnail-backward-Version.Id+eq+'********-****-****-****-************' +iModelScope/Subscriptions/********-****-****-****-************/messages/head +iModelScope/Subscriptions/********-****-****-****-************/messages/head?timeout=* +iModelScope/UserInfo/$query +iModelScope/UserInfo/********-****-****-****-************ +iModelScope/UserInfo/********-****-****-****-************?$select=*,HasStatistics-forward-Statistics.BriefcasesCount +iModelScope/UserInfo/********-****-****-****-************?$select=*,HasStatistics-forward-Statistics.BriefcasesCount,HasStatistics-forward-Statistics.OwnedLocksCount +iModelScope/UserInfo/********-****-****-****-************?$select=*,HasStatistics-forward-Statistics.LastChangeSetPushDate +iModelScope/UserInfo/********-****-****-****-************?$select=*,HasStatistics-forward-Statistics.OwnedLocksCount +iModelScope/UserInfo/********-****-****-****-************?$select=*,HasStatistics-forward-Statistics.PushedChangeSetsCount +iModelScope/UserInfo/********-****-****-****-************?$select=*,HasStatistics-forward-Statistics.PushedChangeSetsCount,HasStatistics-forward-Statistics.LastChangeSetPushDate +iModelScope/UserInfo/?$select=*,HasStatistics-forward-Statistics.* +iModelScope/UserInfo/?$select=*,HasStatistics-forward-Statistics.BriefcasesCount +iModelScope/Version/ +iModelScope/Version/********-****-****-****-************ +iModelScope/Version/********-****-****-****-************?$select=*,HasThumbnail-forward-SmallThumbnail.*,HasThumbnail-forward-LargeThumbnail.* +iModelScope/Version/?$filter=ChangeSetId+eq+'****************************************' +iModelScope/Version/?$filter=Name+eq+'*'&$select=*,HasThumbnail-forward-LargeThumbnail.* +iModelScope/Version/?$filter=Name+eq+'*' +ProjectScope/LargeThumbnail/********-****-****-****-************/$file +ProjectScope/SmallThumbnail/********-****-****-****-************/$file +ProjectScope/iModel/ +ProjectScope/iModel/********-****-****-****-************ +ProjectScope/iModel/?$filter=Name+eq+'*' ProjectScope/iModel/?$orderby=CreatedDate+asc&$top=* \ No newline at end of file diff --git a/core/clients/src/test/imodelhub/Briefcases.test.ts b/core/clients-backend/src/test/imodelhub/Briefcases.test.ts similarity index 96% rename from core/clients/src/test/imodelhub/Briefcases.test.ts rename to core/clients-backend/src/test/imodelhub/Briefcases.test.ts index f28e08d..3c91192 100644 --- a/core/clients/src/test/imodelhub/Briefcases.test.ts +++ b/core/clients-backend/src/test/imodelhub/Briefcases.test.ts @@ -6,14 +6,11 @@ import * as chai from "chai"; import * as fs from "fs"; import * as path from "path"; -import { - AccessToken, IModelHubClient, Briefcase, BriefcaseQuery, IModelHubClientError, -} from "../../imodeljs-clients"; +import { IModelHubStatus, ActivityLoggingContext, GuidString } from "@bentley/bentleyjs-core"; +import { AccessToken, IModelHubClient, Briefcase, BriefcaseQuery, IModelHubClientError, IModelClient } from "@bentley/imodeljs-clients"; import { TestConfig, TestUsers } from "../TestConfig"; import { ResponseBuilder, RequestType, ScopeType } from "../ResponseBuilder"; import * as utils from "./TestUtils"; -import { IModelHubStatus, ActivityLoggingContext, GuidString } from "@bentley/bentleyjs-core"; -import { IModelClient } from "../../IModelClient"; function mockGetBriefcaseById(imodelId: GuidString, briefcase: Briefcase) { if (!TestConfig.enableMocks) diff --git a/core/clients/src/test/imodelhub/ChangeSet.test.ts b/core/clients-backend/src/test/imodelhub/ChangeSet.test.ts similarity index 97% rename from core/clients/src/test/imodelhub/ChangeSet.test.ts rename to core/clients-backend/src/test/imodelhub/ChangeSet.test.ts index d30600a..7e7ea02 100644 --- a/core/clients/src/test/imodelhub/ChangeSet.test.ts +++ b/core/clients-backend/src/test/imodelhub/ChangeSet.test.ts @@ -6,16 +6,11 @@ import * as chai from "chai"; import * as fs from "fs"; import * as path from "path"; import * as deepAssign from "deep-assign"; - -import { TestConfig, TestUsers } from "../TestConfig"; - -import { - AccessToken, IModelClient, IModelHubClient, Briefcase, ChangeSet, ChangeSetQuery, IModelHubClientError, Version, -} from "../../imodeljs-clients"; - +import { IModelHubStatus, ActivityLoggingContext, GuidString } from "@bentley/bentleyjs-core"; +import { AccessToken, IModelClient, IModelHubClient, Briefcase, ChangeSet, ChangeSetQuery, IModelHubClientError, Version } from "@bentley/imodeljs-clients"; import { ResponseBuilder, RequestType, ScopeType } from "../ResponseBuilder"; +import { TestConfig, TestUsers } from "../TestConfig"; import * as utils from "./TestUtils"; -import { IModelHubStatus, ActivityLoggingContext, GuidString } from "@bentley/bentleyjs-core"; chai.should(); diff --git a/core/clients/src/test/imodelhub/Codes.test.ts b/core/clients-backend/src/test/imodelhub/Codes.test.ts similarity index 97% rename from core/clients/src/test/imodelhub/Codes.test.ts rename to core/clients-backend/src/test/imodelhub/Codes.test.ts index 1275f7b..90b0485 100644 --- a/core/clients/src/test/imodelhub/Codes.test.ts +++ b/core/clients-backend/src/test/imodelhub/Codes.test.ts @@ -5,14 +5,13 @@ import * as chai from "chai"; import * as utils from "./TestUtils"; +import { IModelHubStatus, ActivityLoggingContext, Id64, GuidString } from "@bentley/bentleyjs-core"; import { AccessToken, IModelClient, CodeState, HubCode, AggregateResponseError, ConflictingCodesError, CodeQuery, IModelHubClientError, CodeSequence, CodeSequenceType, -} from "../../imodeljs-clients"; - +} from "@bentley/imodeljs-clients"; import { ResponseBuilder } from "../ResponseBuilder"; import { TestConfig, TestUsers } from "../TestConfig"; -import { IModelHubStatus, ActivityLoggingContext, Id64, GuidString } from "@bentley/bentleyjs-core"; chai.should(); diff --git a/core/clients/src/test/imodelhub/Events.test.ts b/core/clients-backend/src/test/imodelhub/Events.test.ts similarity index 97% rename from core/clients/src/test/imodelhub/Events.test.ts rename to core/clients-backend/src/test/imodelhub/Events.test.ts index 0365ac2..77e65ea 100644 --- a/core/clients/src/test/imodelhub/Events.test.ts +++ b/core/clients-backend/src/test/imodelhub/Events.test.ts @@ -6,16 +6,13 @@ import * as chai from "chai"; import * as utils from "./TestUtils"; import { GuidString, Guid, IModelHubStatus, ActivityLoggingContext, Id64 } from "@bentley/bentleyjs-core"; - import { AccessToken, IModelClient, LockEvent, AllLocksDeletedEvent, ChangeSetPostPushEvent, ChangeSetPrePushEvent, CodeEvent, AllCodesDeletedEvent, BriefcaseDeletedEvent, IModelDeletedEvent, VersionEvent, - EventSubscription, EventSAS, EventType, IModelHubEvent, -} from "../../imodeljs-clients"; - + EventSubscription, EventSAS, EventType, IModelHubEvent, LockLevel, LockType, +} from "@bentley/imodeljs-clients"; import { TestConfig, TestUsers } from "../TestConfig"; import { ResponseBuilder, RequestType, ScopeType } from "../ResponseBuilder"; -import { LockLevel, LockType } from "../../imodelhub/Locks"; chai.should(); diff --git a/core/clients/src/test/imodelhub/GlobalEvents.test.ts b/core/clients-backend/src/test/imodelhub/GlobalEvents.test.ts similarity index 97% rename from core/clients/src/test/imodelhub/GlobalEvents.test.ts rename to core/clients-backend/src/test/imodelhub/GlobalEvents.test.ts index 14aa3fb..3d06637 100644 --- a/core/clients/src/test/imodelhub/GlobalEvents.test.ts +++ b/core/clients-backend/src/test/imodelhub/GlobalEvents.test.ts @@ -5,13 +5,11 @@ import * as chai from "chai"; import { Guid, ActivityLoggingContext } from "@bentley/bentleyjs-core"; - import { AccessToken, IModelClient, HubIModel, GlobalEventSubscription, GlobalEventSAS, GlobalEventType, SoftiModelDeleteEvent, HardiModelDeleteEvent, IModelCreatedEvent, ChangeSetCreatedEvent, NamedVersionCreatedEvent, IModelHubGlobalEvent, GetEventOperationType, -} from "../../imodeljs-clients"; - +} from "@bentley/imodeljs-clients"; import { TestConfig, TestUsers } from "../TestConfig"; import { ResponseBuilder, RequestType, ScopeType } from "../ResponseBuilder"; import * as utils from "./TestUtils"; diff --git a/core/clients/src/test/imodelhub/IModelBankCloudEnv.ts b/core/clients-backend/src/test/imodelhub/IModelBankCloudEnv.ts similarity index 85% rename from core/clients/src/test/imodelhub/IModelBankCloudEnv.ts rename to core/clients-backend/src/test/imodelhub/IModelBankCloudEnv.ts index e030c08..94184dc 100644 --- a/core/clients/src/test/imodelhub/IModelBankCloudEnv.ts +++ b/core/clients-backend/src/test/imodelhub/IModelBankCloudEnv.ts @@ -6,13 +6,11 @@ import * as fs from "fs"; import * as fsextra from "fs-extra"; import * as path from "path"; import * as child_process from "child_process"; -import { IModelBankDummyAuthorizationClient } from "../../IModelBank/IModelBankDummyAuthorizationClient"; -import { TestIModelHubCloudEnv } from "./IModelHubCloudEnv"; -import { IModelClient } from "../../IModelClient"; +import { IModelClient, IModelBankClient, IModelBankFileSystemContextClient } from "@bentley/imodeljs-clients"; +import { IModelBankDummyAuthorizationClient } from "@bentley/imodeljs-clients/lib/IModelBank/IModelBankDummyAuthorizationClient"; import { UrlFileHandler } from "../../UrlFileHandler"; -import { IModelBankClient } from "../../IModelBank/IModelBankClient"; +import { TestIModelHubCloudEnv } from "./IModelHubCloudEnv"; import { workDir } from "./TestUtils"; -import { IModelBankFileSystemContextClient } from "../../IModelBank/IModelBankFileSystemContextClient"; export function getIModelBankCloudEnv(): [TestIModelHubCloudEnv, IModelClient] { diff --git a/core/clients/src/test/imodelhub/IModelHubCloudEnv.ts b/core/clients-backend/src/test/imodelhub/IModelHubCloudEnv.ts similarity index 91% rename from core/clients/src/test/imodelhub/IModelHubCloudEnv.ts rename to core/clients-backend/src/test/imodelhub/IModelHubCloudEnv.ts index d5ec77e..6f9dd71 100644 --- a/core/clients/src/test/imodelhub/IModelHubCloudEnv.ts +++ b/core/clients-backend/src/test/imodelhub/IModelHubCloudEnv.ts @@ -2,11 +2,11 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { AccessToken, UserInfo, ConnectClient, Project, IModelHubClient } from "../../imodeljs-clients"; +import { ActivityLoggingContext } from "@bentley/bentleyjs-core"; +import { AccessToken, UserInfo, ConnectClient, Project, IModelHubClient } from "@bentley/imodeljs-clients"; +import { ContextManagerClient, IModelAuthorizationClient, IModelCloudEnvironment } from "@bentley/imodeljs-clients/lib/IModelCloudEnvironment"; import { TestConfig } from "../TestConfig"; -import { ContextManagerClient, IModelAuthorizationClient, IModelCloudEnvironment } from "../../IModelCloudEnvironment"; import { getDefaultClient } from "./TestUtils"; -import { ActivityLoggingContext } from "@bentley/bentleyjs-core"; /** An implementation of IModelProjectAbstraction backed by a iModelHub/Connect project */ class TestConnectClient implements ContextManagerClient { diff --git a/core/clients/src/test/imodelhub/Locks.test.ts b/core/clients-backend/src/test/imodelhub/Locks.test.ts similarity index 97% rename from core/clients/src/test/imodelhub/Locks.test.ts rename to core/clients-backend/src/test/imodelhub/Locks.test.ts index 6a2091d..f1e0559 100644 --- a/core/clients/src/test/imodelhub/Locks.test.ts +++ b/core/clients-backend/src/test/imodelhub/Locks.test.ts @@ -3,17 +3,15 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import * as chai from "chai"; - +import { GuidString, Guid, IModelHubStatus, ActivityLoggingContext, Id64, Id64String } from "@bentley/bentleyjs-core"; import { AccessToken, IModelClient, Lock, Briefcase, ChangeSet, LockType, LockLevel, LockQuery, AggregateResponseError, ConflictingLocksError, IModelHubClientError, -} from "../../imodeljs-clients"; - +} from "@bentley/imodeljs-clients"; import { TestConfig, TestUsers } from "../TestConfig"; import { ResponseBuilder, RequestType, ScopeType } from "../ResponseBuilder"; import * as utils from "./TestUtils"; -import { GuidString, Guid, IModelHubStatus, ActivityLoggingContext, Id64, Id64String } from "@bentley/bentleyjs-core"; chai.should(); diff --git a/core/clients/src/test/imodelhub/Performance.test.ts b/core/clients-backend/src/test/imodelhub/Performance.test.ts similarity index 97% rename from core/clients/src/test/imodelhub/Performance.test.ts rename to core/clients-backend/src/test/imodelhub/Performance.test.ts index 069cfe6..a56ff6d 100644 --- a/core/clients/src/test/imodelhub/Performance.test.ts +++ b/core/clients-backend/src/test/imodelhub/Performance.test.ts @@ -2,13 +2,12 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ +import { Logger, ActivityLoggingContext, Id64, GuidString } from "@bentley/bentleyjs-core"; import { AccessToken, ResponseError, AuthenticationError, IModelClient, Briefcase, HubCode, CodeState, CodeQuery, Lock, LockLevel, LockType, LockQuery, -} from "../../imodeljs-clients"; - +} from "@bentley/imodeljs-clients"; import * as utils from "./TestUtils"; -import { Logger, ActivityLoggingContext, Id64, GuidString } from "@bentley/bentleyjs-core"; describe.skip("iModelHub Performance tests", function (this: Mocha.ISuiteCallbackContext) { let accessToken: AccessToken; diff --git a/core/clients/src/test/imodelhub/TestUtils.ts b/core/clients-backend/src/test/imodelhub/TestUtils.ts similarity index 94% rename from core/clients/src/test/imodelhub/TestUtils.ts rename to core/clients-backend/src/test/imodelhub/TestUtils.ts index d52e792..d371594 100644 --- a/core/clients/src/test/imodelhub/TestUtils.ts +++ b/core/clients-backend/src/test/imodelhub/TestUtils.ts @@ -5,25 +5,21 @@ import * as fs from "fs"; import * as path from "path"; import * as chai from "chai"; +import { Base64 } from "js-base64"; import { GuidString, Guid, ActivityLoggingContext, Id64, Id64String } from "@bentley/bentleyjs-core"; - import { ECJsonTypeMap, AccessToken, UserInfo, Project, ProgressInfo, IModelHubClient, HubCode, CodeState, MultiCode, Briefcase, ChangeSet, Version, Thumbnail, SmallThumbnail, LargeThumbnail, IModelQuery, LockType, LockLevel, - MultiLock, Lock, VersionQuery, -} from "../../imodeljs-clients"; + MultiLock, Lock, VersionQuery, Config, IModelBaseHandler, + IModelBankClient, IModelBankFileSystemContextClient, +} from "@bentley/imodeljs-clients"; import { AzureFileHandler } from "../../imodelhub/AzureFileHandler"; - +import { IModelCloudEnvironment } from "@bentley/imodeljs-clients/lib/IModelCloudEnvironment"; import { ResponseBuilder, RequestType, ScopeType, UrlDiscoveryMock } from "../ResponseBuilder"; import { TestConfig, UserCredentials, TestUsers } from "../TestConfig"; -import { IModelCloudEnvironment } from "../../IModelCloudEnvironment"; import { TestIModelHubCloudEnv } from "./IModelHubCloudEnv"; -import { IModelBankClient } from "../../IModelBank/IModelBankClient"; import { getIModelBankCloudEnv } from "./IModelBankCloudEnv"; -import { IModelBankFileSystemContextClient } from "../../IModelBank/IModelBankFileSystemContextClient"; -import { Config } from "../../Config"; -import { IModelBaseHandler } from "../../imodelhub/BaseHandler"; const bankProjects: string[] = []; @@ -627,7 +623,7 @@ export function getMockSeedFilePath() { return path.join(dir, fs.readdirSync(dir).find((value) => value.endsWith(".bim"))!); } -export async function createIModel(accessToken: AccessToken, name: string, projectId?: string, deleteIfExists = false) { +export async function createIModel(accessToken: AccessToken, name: string, projectId?: string, deleteIfExists = false, fromSeedFile = false) { if (TestConfig.enableMocks) return; @@ -645,7 +641,8 @@ export async function createIModel(accessToken: AccessToken, name: string, proje } } - return client.iModels.create(actx, accessToken, projectId, name, getMockSeedFilePath(), ""); + const pathName = fromSeedFile ? getMockSeedFilePath() : undefined; + return client.iModels.create(actx, accessToken, projectId, name, pathName, ""); } export function getMockChangeSets(briefcase: Briefcase): ChangeSet[] { @@ -742,10 +739,16 @@ export class ProgressTracker { }; } - public check() { - chai.expect(this._count).to.be.greaterThan(0); - chai.expect(this._loaded).to.be.greaterThan(0); - chai.expect(this._loaded).to.be.equal(this._total); + public check(expectCalled: boolean = true) { + if (expectCalled) { + chai.expect(this._count).to.be.greaterThan(0); + chai.expect(this._loaded).to.be.greaterThan(0); + chai.expect(this._loaded).to.be.equal(this._total); + } else { + chai.expect(this._count).to.be.eq(0); + chai.expect(this._loaded).to.be.eq(0); + chai.expect(this._loaded).to.be.eq(0); + } } } diff --git a/core/clients/src/test/imodelhub/Thumbnails.test.ts b/core/clients-backend/src/test/imodelhub/Thumbnails.test.ts similarity index 95% rename from core/clients/src/test/imodelhub/Thumbnails.test.ts rename to core/clients-backend/src/test/imodelhub/Thumbnails.test.ts index ff8b9c1..4314cf2 100644 --- a/core/clients/src/test/imodelhub/Thumbnails.test.ts +++ b/core/clients-backend/src/test/imodelhub/Thumbnails.test.ts @@ -3,9 +3,7 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import * as chai from "chai"; - -import { AccessToken, IModelClient, ChangeSet, Version, Thumbnail, ThumbnailSize, ThumbnailQuery } from "../../imodeljs-clients"; - +import { AccessToken, IModelClient, ChangeSet, Version, Thumbnail, ThumbnailSize, ThumbnailQuery } from "@bentley/imodeljs-clients"; import { TestConfig, TestUsers } from "../TestConfig"; import { ResponseBuilder, RequestType, ScopeType } from "../ResponseBuilder"; import * as utils from "./TestUtils"; @@ -44,7 +42,8 @@ async function getIModelId(accessToken: AccessToken, name: string): Promise { +// todo: tests fail because the imodel has been deleted from the project +describe.skip("iModelHub ThumbnailHandler", () => { const test: TestParameters[] = [{ size: "Small", thumbnails: [] }, { size: "Large", thumbnails: [] }]; let accessToken: AccessToken; let _projectId: string; diff --git a/core/clients/src/test/imodelhub/UrlValidator.test.ts b/core/clients-backend/src/test/imodelhub/UrlValidator.test.ts similarity index 97% rename from core/clients/src/test/imodelhub/UrlValidator.test.ts rename to core/clients-backend/src/test/imodelhub/UrlValidator.test.ts index 3a39d4a..57784d8 100644 --- a/core/clients/src/test/imodelhub/UrlValidator.test.ts +++ b/core/clients-backend/src/test/imodelhub/UrlValidator.test.ts @@ -2,14 +2,12 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { assert, should } from "chai"; - import * as fs from "fs"; import * as path from "path"; -import { urlLogPath, TestConfig } from "../TestConfig"; -import { IModelBaseHandler } from "../../imodelhub/BaseHandler"; -import { UrlDiscoveryClient } from "../../Client"; +import { assert, should } from "chai"; import { ActivityLoggingContext, Guid } from "@bentley/bentleyjs-core"; +import { IModelBaseHandler, UrlDiscoveryClient } from "@bentley/imodeljs-clients"; +import { urlLogPath, TestConfig } from "../TestConfig"; export const whitelistRelPath: string = "../assets/whitelist.txt"; diff --git a/core/clients/src/test/imodelhub/UserInfo.test.ts b/core/clients-backend/src/test/imodelhub/UserInfo.test.ts similarity index 96% rename from core/clients/src/test/imodelhub/UserInfo.test.ts rename to core/clients-backend/src/test/imodelhub/UserInfo.test.ts index edcaefe..db9c6a0 100644 --- a/core/clients/src/test/imodelhub/UserInfo.test.ts +++ b/core/clients-backend/src/test/imodelhub/UserInfo.test.ts @@ -3,14 +3,11 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import * as chai from "chai"; - -import { TestUsers, TestConfig } from "../TestConfig"; - -import { AccessToken } from "../../Token"; +import { IModelHubStatus, ActivityLoggingContext, GuidString } from "@bentley/bentleyjs-core"; +import { AccessToken, UserInfoQuery, HubUserInfo, UserInfo, IModelHubClientError, IModelClient } from "@bentley/imodeljs-clients"; import { ResponseBuilder, ScopeType, RequestType } from "../ResponseBuilder"; +import { TestUsers, TestConfig } from "../TestConfig"; import * as utils from "./TestUtils"; -import { UserInfoQuery, HubUserInfo, UserInfo, IModelHubClientError, IModelClient } from "../../imodeljs-clients"; -import { IModelHubStatus, ActivityLoggingContext, GuidString } from "@bentley/bentleyjs-core"; function mockGetUserInfo(imodelId: GuidString, userInfo: HubUserInfo[], query?: string) { if (!TestConfig.enableMocks) diff --git a/core/clients/src/test/imodelhub/UserStatistics.test.ts b/core/clients-backend/src/test/imodelhub/UserStatistics.test.ts similarity index 98% rename from core/clients/src/test/imodelhub/UserStatistics.test.ts rename to core/clients-backend/src/test/imodelhub/UserStatistics.test.ts index 4a31a3d..6a7c2cd 100644 --- a/core/clients/src/test/imodelhub/UserStatistics.test.ts +++ b/core/clients-backend/src/test/imodelhub/UserStatistics.test.ts @@ -3,15 +3,11 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import * as chai from "chai"; - -import { TestConfig, TestUsers } from "../TestConfig"; - -import { IModelHubClientError, IModelClient } from "../../imodeljs-clients"; -import { AccessToken } from "../../Token"; +import { IModelHubStatus, ActivityLoggingContext, GuidString } from "@bentley/bentleyjs-core"; +import { IModelHubClientError, IModelClient, UserStatisticsQuery, UserStatistics, AccessToken } from "@bentley/imodeljs-clients"; import { ResponseBuilder, RequestType, ScopeType } from "../ResponseBuilder"; import * as utils from "./TestUtils"; -import { UserStatisticsQuery, UserStatistics } from "../../imodelhub/Users"; -import { IModelHubStatus, ActivityLoggingContext, GuidString } from "@bentley/bentleyjs-core"; +import { TestConfig, TestUsers } from "../TestConfig"; chai.should(); diff --git a/core/clients/src/test/imodelhub/Versions.test.ts b/core/clients-backend/src/test/imodelhub/Versions.test.ts similarity index 96% rename from core/clients/src/test/imodelhub/Versions.test.ts rename to core/clients-backend/src/test/imodelhub/Versions.test.ts index d50eaff..3a6382d 100644 --- a/core/clients/src/test/imodelhub/Versions.test.ts +++ b/core/clients-backend/src/test/imodelhub/Versions.test.ts @@ -3,17 +3,14 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import * as chai from "chai"; - +import { ActivityLoggingContext, GuidString } from "@bentley/bentleyjs-core"; import { AccessToken, Version, VersionQuery, Briefcase, ChangeSet, Thumbnail, - ThumbnailQuery, ThumbnailSize, -} from "../../imodeljs-clients"; - + ThumbnailQuery, ThumbnailSize, IModelClient, +} from "@bentley/imodeljs-clients"; import { TestConfig, TestUsers } from "../TestConfig"; import { ResponseBuilder, RequestType, ScopeType } from "../ResponseBuilder"; import * as utils from "./TestUtils"; -import { ActivityLoggingContext, GuidString } from "@bentley/bentleyjs-core"; -import { IModelClient } from "../../IModelClient"; function getSelectStatement(thumbnailSizes: ThumbnailSize[]) { let selectStatement: string = "*"; @@ -81,8 +78,8 @@ describe("iModelHub VersionHandler", () => { } accessToken = TestConfig.enableMocks ? new utils.MockAccessToken() : await utils.login(TestUsers.super); - await utils.createIModel(accessToken, imodelName); - await utils.createIModel(accessToken, imodelName2); + await utils.createIModel(accessToken, imodelName, undefined, false, true); + await utils.createIModel(accessToken, imodelName2, undefined, false, true); imodelId = await utils.getIModelId(accessToken, imodelName); imodelId2 = await utils.getIModelId(accessToken, imodelName2); iModelClient = utils.getDefaultClient(); @@ -93,7 +90,7 @@ describe("iModelHub VersionHandler", () => { const changeSetCount = (await iModelClient.changeSets.get(actx, accessToken, imodelId)).length; if (changeSetCount > 9) { // Recreate iModel if can't create any new changesets - await utils.createIModel(accessToken, imodelName, undefined, true); + await utils.createIModel(accessToken, imodelName, undefined, true, true); imodelId = await utils.getIModelId(accessToken, imodelName); briefcase = (await utils.getBriefcases(accessToken, imodelId, 1))[0]; } diff --git a/core/clients/src/test/imodelhub/iModels.test.ts b/core/clients-backend/src/test/imodelhub/iModels.test.ts similarity index 88% rename from core/clients/src/test/imodelhub/iModels.test.ts rename to core/clients-backend/src/test/imodelhub/iModels.test.ts index b788209..586b6e3 100644 --- a/core/clients/src/test/imodelhub/iModels.test.ts +++ b/core/clients-backend/src/test/imodelhub/iModels.test.ts @@ -7,12 +7,10 @@ import * as fs from "fs"; import * as path from "path"; import { Guid, GuidString, IModelHubStatus, ActivityLoggingContext } from "@bentley/bentleyjs-core"; - import { AccessToken, WsgError, IModelQuery, IModelClient, InitializationState, IModelHubClient, HubIModel, SeedFile, IModelHubError, IModelHubClientError, -} from "../../imodeljs-clients"; - +} from "@bentley/imodeljs-clients"; import { TestConfig, TestUsers } from "../TestConfig"; import { ResponseBuilder, RequestType, ScopeType } from "../ResponseBuilder"; import * as utils from "./TestUtils"; @@ -52,20 +50,25 @@ function mockGetIModel(projectId: string, imodelName: string, imodelId: GuidStri ResponseBuilder.mockResponse(utils.IModelHubUrlMock.getUrl(), RequestType.Get, requestPath, requestResponse); } -function mockPostiModel(projectId: string, imodelId: GuidString, imodelName: string, description: string) { +function mockPostiModel(projectId: string, imodelId: GuidString, imodelName: string, description: string, iModelTemplate?: string) { const requestPath = utils.createRequestUrl(ScopeType.Project, projectId, "iModel"); + const postBodyProperties = new Map([ + ["name", imodelName], + ["description", description], + ]); + + if (!!iModelTemplate) { + postBodyProperties.set("iModelTemplate", iModelTemplate); + } + const postBody = ResponseBuilder.generatePostBody( - ResponseBuilder.generateObject(HubIModel, - new Map([ - ["name", imodelName], - ["description", description], - ]))); + ResponseBuilder.generateObject(HubIModel, postBodyProperties)); const requestResponse = ResponseBuilder.generatePostResponse(ResponseBuilder.generateObject(HubIModel, new Map([ ["wsgId", imodelId], ["name", imodelName], ["description", description], - ["initialized", false], + ["initialized", !!iModelTemplate], ]))); ResponseBuilder.mockResponse(utils.IModelHubUrlMock.getUrl(), RequestType.Post, requestPath, requestResponse, 1, postBody); } @@ -138,6 +141,13 @@ function mockCreateiModel(projectId: string, imodelId: GuidString, imodelName: s mockGetSeedFile(imodelId); } +function mockCreateEmptyiModel(projectId: string, imodelId: GuidString, imodelName: string, description: string) { + if (!TestConfig.enableMocks) + return; + + mockPostiModel(projectId, imodelId, imodelName, description, "Empty"); +} + function mockDeleteiModel(projectId: string, imodelId: GuidString) { if (!TestConfig.enableMocks) return; @@ -523,6 +533,40 @@ describe("iModelHub iModelsHandler", () => { chai.expect(error!.errorNumber).to.be.equal(IModelHubStatus.iModelAlreadyExists); }); + it("should create iModel from empty seed file", async function (this: Mocha.ITestCallbackContext) { + await utils.deleteIModelByName(accessToken, projectId, createIModelName); + + const description = "Test iModel created by imodeljs-clients tests"; + mockCreateEmptyiModel(projectId, Guid.createValue(), createIModelName, description); + const progressTracker = new utils.ProgressTracker(); + const imodel = await imodelClient.iModels.create(alctx, accessToken, projectId, createIModelName, undefined, description, progressTracker.track()); + + chai.expect(imodel.name).to.be.equal(createIModelName); + chai.expect(imodel.initialized).to.be.equal(true); + progressTracker.check(false); + + mockGetIModelByName(projectId, createIModelName, description, imodel.id); + const getiModel = (await iModelClient.iModels.get(alctx, accessToken, projectId, new IModelQuery().byName(createIModelName)))[0]; + chai.assert(!!getiModel); + chai.expect(getiModel.wsgId).to.be.equal(imodel.id!); + }); + + it("should create single iModel from empty seed file", async function (this: Mocha.ITestCallbackContext) { + if (!TestConfig.enableMocks) + this.skip(); + + const description = "Test iModel created by imodeljs-clients tests"; + imodelId = imodelId || Guid.createValue(); + mockGetIModel(projectId, createIModelName, imodelId, 0); + mockCreateEmptyiModel(projectId, Guid.createValue(), createIModelName, description); + const progressTracker = new utils.ProgressTracker(); + const imodel = await imodelClient.iModel.create(alctx, accessToken, projectId, createIModelName, undefined, description, progressTracker.track()); + + chai.expect(imodel.name).to.be.equal(createIModelName); + chai.expect(imodel.initialized).to.be.equal(true); + progressTracker.check(false); + }); + it("should update iModel", async () => { imodelId = imodelId || Guid.createValue(); mockGetIModel(projectId, imodelName, imodelId, 1); diff --git a/core/clients/CHANGELOG.json b/core/clients/CHANGELOG.json index 488f75d..d282587 100644 --- a/core/clients/CHANGELOG.json +++ b/core/clients/CHANGELOG.json @@ -1,6 +1,78 @@ { "name": "@bentley/imodeljs-clients", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/imodeljs-clients_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "Added OidcAgentClientV2. This will replace OidcAgentClient after some fixes from IMS+Connect." + }, + { + "comment": "OIDC changes needed for Angular client" + }, + { + "comment": "Changes package.json to include api-extractor and adds api-extractor.json" + }, + { + "comment": "Use new buildIModelJsBuild script" + }, + { + "comment": "Moved AzureFileHandler, IOSAzureFileHandler, UrlFileHandler and the iModelHub tests to the imodeljs-clients-backend package. This removes the dependency of imodeljs-clients on the \"fs\" module, and turns it into a browser only package. " + }, + { + "comment": "Fixed expansion of config variables. " + }, + { + "comment": "Remove uneeded typedoc plugin depedency" + }, + { + "comment": "Fix error parsing" + }, + { + "comment": "Documentation improvements" + }, + { + "comment": "Create iModel from empty template if seed file path not defined." + }, + { + "comment": "Save BUILD_SEMVER to globally accessible map" + }, + { + "comment": "fix for cache member mix and preserve full root document" + }, + { + "comment": "Added creatorId, new method to list RD per project, identified numerous area for changes WIP" + }, + { + "comment": "Implemented spatial criterai when searching through all reality data associated to a project." + }, + { + "comment": "Threading issue accessing Reality Data, RealityData class was transformed to be the main data access object instead of the client that was used by most/all reality data causing cache data clash and mix between many reality data." + }, + { + "comment": "Removed RBAC client - the RBAC service is considered internal. " + }, + { + "comment": "Handled error with fetching host information on deployed machines." + }, + { + "comment": "WIP fixes to Usage Logging. " + }, + { + "comment": "upgrade to TypeScript 3.2.2" + } + ] + } + }, + { + "version": "0.188.0", + "tag": "@bentley/imodeljs-clients_v0.188.0", + "date": "Wed, 16 Jan 2019 16:36:09 GMT", + "comments": {} + }, { "version": "0.187.0", "tag": "@bentley/imodeljs-clients_v0.187.0", diff --git a/core/clients/CHANGELOG.md b/core/clients/CHANGELOG.md index 6aa24ec..2967385 100644 --- a/core/clients/CHANGELOG.md +++ b/core/clients/CHANGELOG.md @@ -1,6 +1,36 @@ # Change Log - @bentley/imodeljs-clients -This log was last generated on Tue, 15 Jan 2019 15:18:59 GMT and should not be manually modified. +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- Added OidcAgentClientV2. This will replace OidcAgentClient after some fixes from IMS+Connect. +- OIDC changes needed for Angular client +- Changes package.json to include api-extractor and adds api-extractor.json +- Use new buildIModelJsBuild script +- Moved AzureFileHandler, IOSAzureFileHandler, UrlFileHandler and the iModelHub tests to the imodeljs-clients-backend package. This removes the dependency of imodeljs-clients on the "fs" module, and turns it into a browser only package. +- Fixed expansion of config variables. +- Remove uneeded typedoc plugin depedency +- Fix error parsing +- Documentation improvements +- Create iModel from empty template if seed file path not defined. +- Save BUILD_SEMVER to globally accessible map +- fix for cache member mix and preserve full root document +- Added creatorId, new method to list RD per project, identified numerous area for changes WIP +- Implemented spatial criterai when searching through all reality data associated to a project. +- Threading issue accessing Reality Data, RealityData class was transformed to be the main data access object instead of the client that was used by most/all reality data causing cache data clash and mix between many reality data. +- Removed RBAC client - the RBAC service is considered internal. +- Handled error with fetching host information on deployed machines. +- WIP fixes to Usage Logging. +- upgrade to TypeScript 3.2.2 + +## 0.188.0 +Wed, 16 Jan 2019 16:36:09 GMT + +*Version update only* ## 0.187.0 Tue, 15 Jan 2019 15:18:59 GMT diff --git a/core/clients/package.json b/core/clients/package.json index 878efa1..083d533 100644 --- a/core/clients/package.json +++ b/core/clients/package.json @@ -1,28 +1,37 @@ { "name": "@bentley/imodeljs-clients", - "version": "0.187.0", + "version": "0.189.0", "description": "Clients for various Bentley Services used by iModel.js", "main": "lib/imodeljs-clients.js", "typings": "lib/imodeljs-clients", "license": "MIT", "engines": { - "node": ">=8.9.0 <9.0" + "node": ">=10.14.0 <11.0" }, "scripts": { - "build": "tsc 1>&2 && npm run webpackModule-dev", + "build": "node ./node_modules/@bentley/webpack-tools/bin/buildIModelJsModule.js", "clean": "rimraf lib package-deps.json", "copy:test-assets": "cpx \"./src/test/assets/**/*\" ./lib/test/assets", "cover": "npm run copy:test-assets && nyc npm test", "cover:integration": "npm run copy:test-assets && nyc --report-dir ./lib/test/coverage/integration npm run test-integration", + "extract-api": "node ./node_modules/@bentley/build-tools/scripts/extract-api.js --entry=imodeljs-clients", "docs": "node ./node_modules/@bentley/build-tools/scripts/docs.js --source=./src --includes=../../generated-docs/extract --json=../../generated-docs/core/imodeljs-clients/file.json --tsIndexFile=imodeljs-clients.ts --onlyJson %TYPEDOC_THEME%", "lint": "tslint --project . 1>&2", "pretest": "rimraf \"./lib/test/*.log\" && npm run copy:test-assets", - "test": "npm run pretest && node ./node_modules/@bentley/build-tools/scripts/test.js --offline=\"mock\" --grep=\"#integration|iModelHub URL Whitelist Validator\" --invert", + "test": "npm run pretest && node ./node_modules/@bentley/build-tools/scripts/test.js --offline=\"mock\" --grep=\"#integration\" --invert", "test-integration": "npm run pretest && node ./node_modules/@bentley/build-tools/scripts/test.js", "test:settings": "npm run pretest && node ./node_modules/@bentley/build-tools/scripts/test.js --grep Setting", - "watch": "npm run docs && bmsWatch --src ./lib/docs/json --destination ./public", - "webpackModule-dev": "make-dir ./lib/module/dev/ && webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/imodeljs-clients.js --env.bundlename=imodeljs-clients --json >./lib/module/dev/webpackStats.json", - "webpackModule-prod": "webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/imodeljs-clients.js --env.bundlename=imodeljs-clients --env.prod" + "watch": "npm run docs && bmsWatch --src ./lib/docs/json --destination ./public" + }, + "iModelJs": { + "buildModule": { + "type": "system", + "webpack": { + "dest": "./lib/module", + "entry": "./lib/imodeljs-clients.js", + "bundleName": "imodeljs-clients" + } + } }, "repository": { "type": "git", @@ -39,47 +48,40 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "0.187.0" + "@bentley/bentleyjs-core": "0.189.0" }, "//devDependencies": [ "NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install", "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/config-loader": "0.187.0", - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/build-tools": "0.187.0", - "@bentley/webpack-tools": "0.187.0", + "@bentley/config-loader": "0.189.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/build-tools": "0.189.0", + "@bentley/webpack-tools": "0.189.0", + "@bentley/geometry-core": "0.189.0", "@types/chai": "^4.1.4", "@types/deep-assign": "^0.1.0", - "@types/fs-extra": "^4.0.7", "@types/js-base64": "^2.3.1", "@types/mocha": "^5.2.5", "@types/nock": "^9.1.2", - "@types/node": "10.10.3", + "@types/node": "10.12.18", "@types/qs": "^6.5.0", "@types/superagent": "^3.5.6", "@types/xmldom": "^0.1.29", "chai": "^4.1.2", "cpx": "^1.5.0", - "make-dir-cli": "^1.0.0", "mocha": "^5.2.0", "nock": "^9.2.3", "nyc": "^13.0.1", "rimraf": "^2.6.2", - "source-map-loader": "^0.2.3", - "source-map-support": "^0.5.6", "ts-node": "^7.0.1", "tslint": "^5.11.0", "typedoc": "^0.11.1", - "typedoc-plugin-external-module-name": "^1.1.1", - "typescript": "~3.1.0", - "webpack": "^4.20.2", - "webpack-cli": "^3.1.0" + "typescript": "~3.2.2" }, "dependencies": { "deep-assign": "^2.0.0", - "fs-extra": "^6.0.1", "js-base64": "^2.4.5", "qs": "^6.5.1", "superagent": "^3.7.0", diff --git a/core/clients/src/Config.ts b/core/clients/src/Config.ts index 56e103b..b3e283f 100644 --- a/core/clients/src/Config.ts +++ b/core/clients/src/Config.ts @@ -90,6 +90,7 @@ export class Config { } }); + this._expanded[name] = value; return value; } /** Get a property value. Throws exception if property name is not found */ diff --git a/core/clients/src/ConnectClients.ts b/core/clients/src/ConnectClients.ts index e473748..4b722ab 100644 --- a/core/clients/src/ConnectClients.ts +++ b/core/clients/src/ConnectClients.ts @@ -5,7 +5,7 @@ /** @module ConnectServices */ import { WsgClient } from "./WsgClient"; import { AccessToken } from "./Token"; -import { request, RequestQueryOptions, Response, RequestOptions } from "./Request"; +import { RequestQueryOptions, RequestOptions } from "./Request"; import { ECJsonTypeMap, WsgInstance } from "./ECJsonTypeMap"; import { ActivityLoggingContext } from "@bentley/bentleyjs-core"; import { Config } from "./Config"; @@ -107,140 +107,6 @@ export interface RbacRequestQueryOptions extends RequestQueryOptions { rbacOnly?: boolean; } -export enum IModelHubPermissions { - None = 0, - CreateIModel = 1 << 0, - ReadIModel = 1 << 1, - ModifyIModel = 1 << 2, - ManageResources = 1 << 3, - ManageVersions = 1 << 4, -} - -/** Client API to access the connect services. */ -export class RbacClient extends WsgClient { - public static readonly searchKey: string = "RBAC.URL"; - public static readonly configRelyingPartyUri = "imjs_rbac_relying_party_uri"; - - public constructor() { - super("v2.4"); - } - - /** - * Gets name/key to query the service URLs from the URL Discovery Service ("Buddi") - * @returns Search key for the URL. - */ - protected getUrlSearchKey(): string { - return RbacClient.searchKey; - } - - /** - * Gets theRelyingPartyUrl for the service. - * @returns RelyingPartyUrl for the service. - */ - protected getRelyingPartyUrl(): string { - if (Config.App.has(RbacClient.configRelyingPartyUri)) - return Config.App.get(RbacClient.configRelyingPartyUri) + "/"; - - if (Config.App.getBoolean(WsgClient.configUseHostRelyingPartyUriAsFallback, true)) { - if (Config.App.has(WsgClient.configHostRelyingPartyUri)) - return Config.App.get(WsgClient.configHostRelyingPartyUri) + "/"; - } - - throw new Error(`RelyingPartyUrl not set. Set it in Config.App using key ${RbacClient.configRelyingPartyUri}`); - } - - /** - * Gets connect projects accessible to the authorized user. - * @param token Delegation token of the authorized user. - * @param queryOptions Query options. Use the mapped EC property names in the query strings and not the TypeScript property names. - * @returns Resolves to an array of projects. - */ - public async getProjects(alctx: ActivityLoggingContext, token: AccessToken, queryOptions?: RbacRequestQueryOptions): Promise { - const userInfo = token.getUserInfo(); - if (!userInfo) - return Promise.reject(new Error("Invalid access token")); - - const url: string = "/Repositories/BentleyCONNECT--Main/RBAC/User/" + userInfo.id + "/Project"; - return this.getInstances(alctx, RbacProject, token, url, queryOptions); - } - - /** - * Gets all users in a project - * @param token Delegation token of the authorized user - * @param projectId Id of the project we want to get users for - * @param queryOptions Query options. Use the mapped EC property names in the query strings and not TypeScript property names. - * @returns Resolves to an array of users - */ - public async getUsers(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, queryOptions?: RbacRequestQueryOptions) { - const url: string = "/Repositories/BentleyCONNECT--Main/RBAC/Project/" + projectId + "/User"; - return this.getInstances(alctx, RbacUser, token, url, queryOptions); - } - - /** - * Get the permissions relevant to the iModelHubService for a specified project - * @param token Delegation token of the authorized user. - * @param projectId Id of the specified project. - */ - public async getIModelHubPermissions(alctx: ActivityLoggingContext, token: AccessToken, projectId: string): Promise { - alctx.enter(); - const userInfo = token.getUserInfo(); - if (!userInfo) - return Promise.reject(new Error("Invalid access token")); - - const relativeUrlPath: string = "/Repositories/BentleyCONNECT--Main/RBAC/User/" + userInfo.id + "/Project"; - const url: string = await this.getUrl(alctx) + relativeUrlPath; - alctx.enter(); - - const iModelHubServiceGPRId = 2485; - const filterStr = `$id+eq+'${projectId}'+and+Permission.ServiceGPRId+eq+${iModelHubServiceGPRId}`; - const options: any = { - method: "GET", - headers: { authorization: token.toTokenString() }, - qs: { - $select: "Permission.$id", - $filter: filterStr, - }, - }; - - await this.setupOptionDefaults(options); - - const res: Response = await request(alctx, url, options); - alctx.enter(); - - if (!res.body || !res.body.hasOwnProperty("instances")) - return Promise.reject(new Error("Expected an array of instances to be returned")); - - const instances = res.body.instances; - if (!instances || instances.length !== 1) - return Promise.reject(new Error("Project with specified id was not found")); - - let permissions: IModelHubPermissions = IModelHubPermissions.None; - for (const relationshipInstance of instances[0].relationshipInstances) { - switch (relationshipInstance.relatedInstance.instanceId) { - case "IMHS_Create_iModel": - permissions = permissions | IModelHubPermissions.CreateIModel; - break; - case "IMHS_Read_iModel": - permissions = permissions | IModelHubPermissions.ReadIModel; - break; - case "IMHS_Modify_iModel": - permissions = permissions | IModelHubPermissions.ModifyIModel; - break; - case "IMHS_ManageResources": - permissions = permissions | IModelHubPermissions.ManageResources; - break; - case "IMHS_Manage_Versions": - permissions = permissions | IModelHubPermissions.ManageVersions; - break; - default: - } - } - - return permissions; - } - -} - /** Client API to access the connect services. */ export class ConnectClient extends WsgClient { public static readonly searchKey: string = "CONNECTEDContextService.URL"; diff --git a/core/clients/src/FileHandler.ts b/core/clients/src/FileHandler.ts index 55b1d00..a6828b7 100644 --- a/core/clients/src/FileHandler.ts +++ b/core/clients/src/FileHandler.ts @@ -15,20 +15,20 @@ export interface FileHandler { * Download a file. * @param alctx Activity logging context * @param downloadUrl URL to download file from. - * @param downloadToPathname Pathname to download the file to. + * @param path Path to download the file to, including file name. * @param fileSize Size of the file that's being downloaded. * @param progressCallback Callback for tracking progress. */ - downloadFile(alctx: ActivityLoggingContext, downloadUrl: string, downloadToPathname: string, fileSize?: number, progress?: (progress: ProgressInfo) => void): Promise; + downloadFile(alctx: ActivityLoggingContext, downloadUrl: string, path: string, fileSize?: number, progress?: (progress: ProgressInfo) => void): Promise; /** * Upload a file. * @param alctx Activity logging context * @param uploadUrl URL to upload the file to. - * @param uploadFromPathname Pathname to upload the file from. + * @param path Path of the file to be uploaded. * @param progressCallback Callback for tracking progress. */ - uploadFile(alctx: ActivityLoggingContext, uploadUrlString: string, uploadFromPathname: string, progress?: (progress: ProgressInfo) => void): Promise; + uploadFile(alctx: ActivityLoggingContext, uploadUrlString: string, path: string, progress?: (progress: ProgressInfo) => void): Promise; /** * Get size of a file. diff --git a/core/clients/src/IModelClient.ts b/core/clients/src/IModelClient.ts index e0189dc..258e0e8 100644 --- a/core/clients/src/IModelClient.ts +++ b/core/clients/src/IModelClient.ts @@ -25,7 +25,7 @@ export abstract class IModelClient { private _fileHandler?: FileHandler; /** * Creates an instance of [[IModelClient]]. - * @param fileHandler File handler to handle file upload/download and file system operations. See [[AzureFileHandler]]. + * @param fileHandler File handler to handle file upload/download and file system operations. */ public constructor(baseHandler: IModelBaseHandler, fileHandler?: FileHandler) { this._handler = baseHandler; @@ -36,7 +36,7 @@ export abstract class IModelClient { /** * Sets file handler for file upload/download. - * @param fileHandler File handler to handle file upload/download and file system operations. See [[AzureFileHandler]]. + * @param fileHandler File handler to handle file upload/download and file system operations. */ public setFileHandler(fileHandler: FileHandler) { this._fileHandler = fileHandler; diff --git a/core/clients/src/RealityDataServicesClient.ts b/core/clients/src/RealityDataServicesClient.ts index 0aeea29..96a42b9 100644 --- a/core/clients/src/RealityDataServicesClient.ts +++ b/core/clients/src/RealityDataServicesClient.ts @@ -11,8 +11,18 @@ import { URL } from "url"; import { request, RequestOptions } from "./Request"; import { Config } from "./Config"; import { ActivityLoggingContext } from "@bentley/bentleyjs-core"; - -/** RealityData */ +import { Angle, Range2d } from "@bentley/geometry-core"; + +/** RealityData + * This class implements a Reality Data stored in ProjectWise Context Share (Reality Data Service) + * Data is accessed directly through methods of the reality data instance. + * Access to the data required a properly entitled token though the access to the blob is controled through + * an Azure blob URL, the token may be required to obtain this Azure blob URL or refresh it. + * The Azure blob URL is considered valid for an hour and is refreshed after 50 minutes. + * In addition to the reality data properties, and Azure blob URL and internal states, a reality data also contains + * the identification of the CONNECT project to identify the context(used for access permissions resolution) and + * may contain a RealityDataClient to obtain the WSG client specialisation to communicate with ProjectWise Context Share (to obtain the Azure blob URL). + */ @ECJsonTypeMap.classToJson("wsg", "S3MX.RealityData", { schemaPropertyName: "schemaName", classPropertyName: "className" }) export class RealityData extends WsgInstance { @ECJsonTypeMap.propertyToJson("wsg", "properties.Id") @@ -94,11 +104,149 @@ export class RealityData extends WsgInstance { @ECJsonTypeMap.propertyToJson("wsg", "properties.OwnedBy") public ownedBy?: string; + @ECJsonTypeMap.propertyToJson("wsg", "properties.CreatorId") + public creatorId?: string; + @ECJsonTypeMap.propertyToJson("wsg", "properties.Version") public version?: string; + + // Cache parameters for reality data access. Contains the blob url, the timestamp to refresh (every 50 minutes) the url and the root document path. + private _blobUrl: any; + private _blobTimeStamp: Date; + private _blobRooDocumentPath: undefined | string; // Path relative to blob root of root document. It is slash terminated if not empty + + // Link to client to fetch the blob url + public client: undefined | RealityDataServicesClient; + + // project id used when using the client. If defined must contain the GUID of the CONNECT + // project or "Server" to indicate access is performed out of context (for accessing PUBLIC or ENTERPRISE data). + // If undefined when accessing reality data tiles then it will automatically be set to "Server" + public projectId: undefined | string; + + /** + * Gets string url to fetch blob data from + * @param token Delegation token of the authorized user issued for this service. + * @param name name or path of tile + * @param nameRelativeToRootDocumentPath (optional default is false) Indicates if the given name is relative to the root document path. + * @returns string url for blob data + */ + public async getBlobStringUrl(alctx: ActivityLoggingContext, token: AccessToken, name: string, nameRelativeToRootDocumentPath: boolean = false): Promise { + const url = await this.getBlobUrl(alctx, token); + + let host: string = ""; + if (nameRelativeToRootDocumentPath && this._blobRooDocumentPath && this._blobRooDocumentPath !== "") + host = url.origin + url.pathname + "/" + this._blobRooDocumentPath; // _blobRootDocumentPath is always '/' terminated if not empty + else + host = url.origin + url.pathname + "/"; + + const query = url.search; + + return `${host}${name}${query}`; + } + + /** + * Gets a tileset's tile data + * @param token Delegation token of the authorized user issued for this service. + * @param name name or path of tile + * @param nameRelativeToRootDocumentPath (optional default is false) Indicates if the given name is relative to the root document path. + * @returns tile data json + */ + public async getModelData(alctx: ActivityLoggingContext, token: AccessToken, name: string, nameRelativeToRootDocumentPath: boolean = false): Promise { + return this.getTileJson(alctx, token, name, nameRelativeToRootDocumentPath); + } + + /** + * Gets a tile access url URL object + * @param token Delegation token of the authorized user issued for this service. + * @returns app URL object for blob url + */ + public async getBlobUrl(alctx: ActivityLoggingContext, token: AccessToken): Promise { + // Normally the client is set when the reality data is extracted for the client but it could be undefined + // if the reality data instance is created manually. + if (!this.client) + this.client = new RealityDataServicesClient(); + + if (!this.projectId) + this.projectId = "Server"; + + if (!this.id) + return Promise.reject(new Error("id not set")); + + if (undefined === this._blobUrl || this._blobTimeStamp.valueOf() - Date.now() > 3000000) { // 3 million milliseconds or 50 minutes + const fileAccess: FileAccessKey[] = await this.client.getFileAccessKey(alctx, token, this.projectId as string, this.id); + if (fileAccess.length !== 1) + return Promise.reject(new Error("Could not obtain blob file access key for reality data: " + this.id)); + const urlString = fileAccess[0].url!; + this._blobUrl = (typeof window !== "undefined") ? new window.URL(urlString) : new URL(urlString); + this._blobTimeStamp = new Date(Date.now()); + if (!this._blobRooDocumentPath && this.rootDocument) { + const urlParts = this.rootDocument.split("/"); + urlParts.pop(); + if (urlParts.length === 0) + this._blobRooDocumentPath = ""; + else + this._blobRooDocumentPath = urlParts.join("/") + "/"; + } + } + + return Promise.resolve(this._blobUrl); + } + + /** + * Gets a tileset's app data json + * @param token Delegation token of the authorized user issued for this service. + * @param name name or path of tile + * @param nameRelativeToRootDocumentPath (optional default is false) Indicates if the given name is relative to the root document path. + * @returns app data json object + */ + public async getTileJson(alctx: ActivityLoggingContext, token: AccessToken, name: string, nameRelativeToRootDocumentPath: boolean = false): Promise { + const stringUrl = await this.getBlobStringUrl(alctx, token, name, nameRelativeToRootDocumentPath); + const options: RequestOptions = { + method: "GET", + responseType: "json", + }; + const data = await request(alctx, stringUrl, options); + return data.body; + } + + /** + * Gets tile content + * @param token Delegation token of the authorized user issued for this service. + * @param name name or path of tile + * @param nameRelativeToRootDocumentPath (optional default is false) Indicates if the given name is relative to the root document path. + * @returns array buffer of tile content + */ + public async getTileContent(alctx: ActivityLoggingContext, token: AccessToken, name: string, nameRelativeToRootDocumentPath: boolean = false): Promise { + const stringUrl = await this.getBlobStringUrl(alctx, token, name, nameRelativeToRootDocumentPath); + const options: RequestOptions = { + method: "GET", + responseType: "arraybuffer", + }; + const data = await request(alctx, stringUrl, options); + return data.body; + } + + /** + * Gets a reality data root document json + * @param token Delegation token of the authorized user issued for this service. + * @returns tile data json + */ + public async getRootDocumentJson(alctx: ActivityLoggingContext, token: AccessToken): Promise { + alctx.enter(); + + if (!this.rootDocument) + return Promise.reject(new Error("Root document not defined for reality data: " + this.id)); + + const root = this.rootDocument!; + + return this.getModelData(alctx, token, root, false); + } + } -/** Document */ +/** File Access Key + * This class is used by the RealityDataServicesClient to extract an Azure blob URL + */ @ECJsonTypeMap.classToJson("wsg", "FileAccess.FileAccessKey", { schemaPropertyName: "schemaName", classPropertyName: "className" }) export class FileAccessKey extends WsgInstance { @ECJsonTypeMap.propertyToJson("wsg", "properties.Url") @@ -115,15 +263,16 @@ export class FileAccessKey extends WsgInstance { } /** - * Client wrapper to Reality Data Service + * Client wrapper to Reality Data Service. + * An instance of this class is used to extract reality data from the ProjectWise Context Share (Reality Data Service) + * Most important methods enable to obtain a specific reality data, fetch all reality data associated to a project and + * all reality data of a project within a provided spatial extent. + * This class also implements extraction of the Azure blob address. */ export class RealityDataServicesClient extends WsgClient { public static readonly searchKey: string = "RealityDataServices"; public static readonly configRelyingPartyUri = "imjs_reality_data_service_relying_party_uri"; - private _blobUrl: any; - private _blobRoot: undefined | string; - /** * Creates an instance of RealityDataServicesClient. */ @@ -131,24 +280,6 @@ export class RealityDataServicesClient extends WsgClient { super("v2.5"); } - /** - * Potentially a model name will be of this format: {{root}}/{{name}} - * When this occurs, the tile content requests will be made without the root portion prefixed, which results in 404's - * As a workaround, when the root document json is fetched, we can determine if a root name exists and if so, - * save it so we can conditionally prefix it the child tile names when they are requested - * - * This method is where we conditionally prefix that root into the name field - * @param name - */ - private updateModelName(name: string): string { - // a compound name implies that it includes a forward slash or equivalent character - const isCompound = name.includes("/") || name.includes("~2F"); - - // if the name is already compound or the blobRoot is not set, then we do not prefix the model name as it would be redundant - // otherwise, we return the name prefixed with that value - return (!isCompound && !name.includes("%2F") && undefined !== this._blobRoot) ? this._blobRoot + "/" + name : name; - } - /** * Gets name/key to query the service URLs from the URL Discovery Service ("Buddi") * @returns Search key for the URL. @@ -174,226 +305,82 @@ export class RealityDataServicesClient extends WsgClient { } /** - * Gets reality data properties + * This method returns the URL to obtain the Reality Data details from PW Context Share. + * Technically it should never be required as the RealityData object returned should have all the information to obtain the + * data. * @param token Delegation token of the authorized user issued for this service. * @param projectId id of associated connect project * @param tilesId realityDataInstance id, called tilesId when returned from tile generator job - * @returns an array of RealityData + * @returns string containing the URL to reality data for indicated tile. */ - public async getRealityData(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string): Promise { - return this.getInstances(alctx, RealityData, token, `/Repositories/S3MXECPlugin--${projectId}/S3MX/RealityData/${tilesId}`); - } + public async getRealityDataUrl(alctx: ActivityLoggingContext, projectId: string, tilesId: string): Promise { + const serverUrl: string = await this.getUrl(alctx); - /** - * Gets a tileset's app data json file access key - * @param token Delegation token of the authorized user issued for this service. - * @param projectId id of associated connect project - * @param tilesId realityDataInstance id, called tilesId when returned from tile generator job - * @returns an array of FileAccessKey - */ - public async getAppDataFileAccessKey(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string): Promise { - return this.getFileAccessKey(alctx, token, projectId, tilesId, "Bim_AppData.json"); + return serverUrl + `/Repositories/S3MXECPlugin--${projectId}/S3MX/RealityData/${tilesId}`; } /** - * Gets a tile file access key + * Gets reality data with all of its properties * @param token Delegation token of the authorized user issued for this service. * @param projectId id of associated connect project * @param tilesId realityDataInstance id, called tilesId when returned from tile generator job - * @param name name or path of tile - * @returns a string url + * @returns The requested reality data. */ - public async getFileAccessKey(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string, name: string): Promise { - const path = encodeURIComponent(tilesId + "/" + this.updateModelName(name)).split("%").join("~"); - return this.getInstances(alctx, FileAccessKey, token, `/Repositories/S3MXECPlugin--${projectId}/S3MX/Document/${path}/FileAccess.FileAccessKey?$filter=Permissions+eq+%27Read%27`); + public async getRealityData(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string): Promise { + const realityDatas: RealityData[] = await this.getInstances(alctx, RealityData, token, `/Repositories/S3MXECPlugin--${projectId}/S3MX/RealityData/${tilesId}`); + if (realityDatas.length !== 1) + return Promise.reject(new Error("Could not fetch reality data: " + tilesId)); + + realityDatas[0].client = this; + realityDatas[0].projectId = projectId; + return realityDatas[0]; } /** - * Gets a tileset's tile data blob key url + * Gets all reality data associated to the project. Consider using getRealityDataInProjectOverlapping() if spatial extent is known. * @param token Delegation token of the authorized user issued for this service. * @param projectId id of associated connect project - * @param tilesId realityDataInstance id, called tilesId when returned from tile generator job - * @param name name or path of tile - * @returns a string url + * @returns an array of RealityData that are associated to the project. */ - public async getTileDataBlobUrl(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string, name: string): Promise { - const keys: FileAccessKey[] = await this.getFileAccessKey(alctx, token, projectId, tilesId, name); - return keys[0].url!; + public async getRealityDataInProject(alctx: ActivityLoggingContext, token: AccessToken, projectId: string): Promise { + const realityDatas: RealityData[] = await this.getInstances(alctx, RealityData, token, `/Repositories/S3MXECPlugin--${projectId}/S3MX/RealityData?project=${projectId}&$filter=Type+eq+'RealityMesh3DTiles'`); + realityDatas.forEach((realityData) => { realityData.client = this; realityData.projectId = projectId; }); + return realityDatas; } /** - * Gets a tileset's app data json blob url + * Gets all reality data that has a footprint defined that overlaps the given area and that are associated with the project. Reality Data returned must be accessible by user + * as public, enterprise data, private or accessible through context RBAC rights attributed to user. * @param token Delegation token of the authorized user issued for this service. * @param projectId id of associated connect project - * @param tilesId realityDataInstance id, called tilesId when returned from tile generator job - * @param name name or path of tile - * @returns a string url - */ - public async getAppDataBlobUrl(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string): Promise { - const keys: FileAccessKey[] = await this.getFileAccessKey(alctx, token, projectId, tilesId, "Bim_AppData.json"); - return keys[0].url!; - } - - /** - * Gets a tileset's app data json - * @param token Delegation token of the authorized user issued for this service. - * @param projectId id of associated connect project - * @param tilesId realityDataInstance id, called tilesId when returned from tile generator job - * @param name name or path of tile - * @returns app data json object - */ - public async getAppData(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string): Promise { - return this.getTileJson(alctx, token, projectId, tilesId, "Bim_AppData.json"); - } - - /** - * trims off the TileSets segment from a url - * @param url string url to cleanup - * @returns clean url string - */ - public cleanTilesetUrl(url: string): string { - const path = url.split("//").filter((v) => v !== "TileSets" && v !== "Bim").join("/"); - return path.includes("?") ? path.slice(0, path.indexOf("?")) : path; - } - - /** - * Gets a tileset's tile data - * @param token Delegation token of the authorized user issued for this service. - * @param projectId id of associated connect project - * @param tilesId realityDataInstance id, called tilesId when returned from tile generator job - * @param name name or path of tile - * @returns tile data json - */ - public async getModelData(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string, name: string): Promise { - return this.getTileJson(alctx, token, projectId, tilesId, this.cleanTilesetUrl(name)); - } - - /** - * Gets a tile access url URL object - * @param token Delegation token of the authorized user issued for this service. - * @param projectId id of associated connect project - * @param tilesId realityDataInstance id, called tilesId when returned from tile generator job - * @param name name or path of tile - * @returns app URL object for blob url - */ - public async getBlobUrl(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string, name: string): Promise { - const urlString = await this.getTileDataBlobUrl(alctx, token, projectId, tilesId, name); - if (typeof this._blobUrl === "undefined") - this._blobUrl = (typeof window !== "undefined") ? new window.URL(urlString) : new URL(urlString); - return Promise.resolve(this._blobUrl); - } - - /** - * Gets string url to fetch blob data from - * @param token Delegation token of the authorized user issued for this service. - * @param projectId id of associated connect project - * @param tilesId realityDataInstance id, called tilesId when returned from tile generator job - * @param name name or path of tile - * @returns string url for blob data - */ - public async getBlobStringUrl(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string, name: string): Promise { - const url = undefined === this._blobUrl ? await this.getBlobUrl(alctx, token, projectId, tilesId, name) : this._blobUrl; - const host = url.origin + url.pathname; - const query = url.search; - return `${host}/${this.updateModelName(name)}${query}`; - } - - /** - * Gets a tileset's app data json - * @param token Delegation token of the authorized user issued for this service. - * @param projectId id of associated connect project - * @param tilesId realityDataInstance id, called tilesId when returned from tile generator job - * @param name name or path of tile - * @returns app data json object - */ - public async getTileJson(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string, name: string): Promise { - const stringUrl = await this.getBlobStringUrl(alctx, token, projectId, tilesId, name); - const options: RequestOptions = { - method: "GET", - responseType: "json", - }; - const data = await request(alctx, stringUrl, options); - return data.body; - } - - /** - * Gets tile content - * @param token Delegation token of the authorized user issued for this service. - * @param projectId id of associated connect project - * @param tilesId realityDataInstance id, called tilesId when returned from tile generator job - * @param name name or path of tile - * @returns array buffer of tile content + * @param range The range to search for given as a range 2d where X repesentents the longitude in radians and Y the latitude in radians + * longitude can be in the range -2P to 2PI but the minimum value must be smaller numerically to the maximum. + * Note that the longitudes are usually by convention in the range of -PI to PI except + * for ranges that overlap the -PI/+PI frontier in which case either representation is acceptable. + * @returns an array of RealityData */ - public async getTileContent(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string, name: string): Promise { - const stringUrl = await this.getBlobStringUrl(alctx, token, projectId, tilesId, this.cleanTilesetUrl(name)); - const options: RequestOptions = { - method: "GET", - responseType: "arraybuffer", - }; - const data = await request(alctx, stringUrl, options); - return data.body; + public async getRealityDataInProjectOverlapping(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, range: Range2d): Promise { + const minLongDeg = Angle.radiansToDegrees(range.low.x); + const maxLongDeg = Angle.radiansToDegrees(range.high.x); + const minLatDeg = Angle.radiansToDegrees(range.low.y); + const maxLatDeg = Angle.radiansToDegrees(range.high.y); + const polygonString = `{\"points\":[[${minLongDeg},${minLatDeg}],[${maxLongDeg},${minLatDeg}],[${maxLongDeg},${maxLatDeg}],[${minLongDeg},${maxLatDeg}],[${minLongDeg},${minLatDeg}]], \"coordinate_system\":\"4326\"}`; + + const realityDatas: RealityData[] = await this.getInstances(alctx, RealityData, token, `/Repositories/S3MXECPlugin--${projectId}/S3MX/RealityData?project=${projectId}&polygon=${polygonString}&$filter=Type+eq+'RealityMesh3DTiles'`); + realityDatas.forEach((realityData) => { realityData.client = this; realityData.projectId = projectId; }); + return realityDatas; } /** - * Gets a reality data root document json + * Gets a tile file access key * @param token Delegation token of the authorized user issued for this service. * @param projectId id of associated connect project * @param tilesId realityDataInstance id, called tilesId when returned from tile generator job - * @returns tile data json + * @returns a FileAccessKey object containing the Azure blob address. */ - public async getRootDocumentJson(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string): Promise { - const realityData: RealityData[] = await this.getRealityData(alctx, token, projectId, tilesId); - alctx.enter(); - let root = realityData[0].rootDocument!; - - // reset the blob url when a root document is requested to ensure the previous blob storage key isn't reused - this._blobUrl = undefined; - - // if the RootDocument is ClarkSimple/RootTile.json, then only use RootTile.json, - // so we need to only use the last part of the RootDocument path - if (root.includes("/")) - root = root.split("/")[root.split("/").length - 1]; - - return this.getModelData(alctx, token, projectId, tilesId, root); + public async getFileAccessKey(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, tilesId: string): Promise { + const path = encodeURIComponent(tilesId); + return this.getInstances(alctx, FileAccessKey, token, `/Repositories/S3MXECPlugin--${projectId}/S3MX/RealityData/${path}/FileAccess.FileAccessKey?$filter=Permissions+eq+%27Read%27`); } - /** - * Gets reality data corresponding to given url - * @param token Delegation token of the authorized user issued for this service. - * @param url expected to be similar to this: https://...realitydataservices.bentley.com//Repositories/S3MXECPlugin--/S3MX/RealityData//.json - * @param tileRequest method to fetch tile data from (either getTileJson or getTileContent) - * @returns tile data json - */ - private async getTileDataFromUrl(token: AccessToken, url: string, tileRequest: (token: AccessToken, projectId: string, tilesId: string, name: string) => Promise): Promise { - try { - const urlParts = url.split("/"); - const projectId = urlParts.find((val: string) => val.includes("--"))!.split("--")[1]; - const tilesId = urlParts.find((val: string) => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(val)); - const modelName = url.split(tilesId + "/")[1]; - - return tileRequest(token, projectId, tilesId!, modelName); - } catch (ex) { - throw new Error(ex); - } - } - - /** - * Gets a reality data tile json corresponding to given url - * @param token Delegation token of the authorized user issued for this service. - * @param url expected to be similar to this: https://...realitydataservices.bentley.com//Repositories/S3MXECPlugin--/S3MX/RealityData//.json - * @returns tile data json - */ - public async getTileJsonFromUrl(token: AccessToken, url: string): Promise { - return this.getTileDataFromUrl(token, url, this.getTileJson.bind(this)); - } - - /** - * Gets a reality data tile content corresponding to given url - * @param token Delegation token of the authorized user issued for this service. - * @param url expected to be similar to this: https://...realitydataservices.bentley.com//Repositories/S3MXECPlugin--/S3MX/RealityData//.json - * @returns tile data content - */ - public async getTileContentFromUrl(token: AccessToken, url: string): Promise { - return this.getTileDataFromUrl(token, url, this.getTileContent.bind(this)); - } } diff --git a/core/clients/src/Request.ts b/core/clients/src/Request.ts index 71db485..1d01f95 100644 --- a/core/clients/src/Request.ts +++ b/core/clients/src/Request.ts @@ -115,7 +115,7 @@ export class ResponseError extends BentleyError { * @returns Parsed error. */ public static parse(response: any, log = true): ResponseError { - const error = new ResponseError(ResponseError.parseHttpStatus(response.status / 100)); + const error = new ResponseError(ResponseError.parseHttpStatus(response.statusType)); if (!response) { error.message = "Couldn't get response object."; return error; @@ -161,8 +161,8 @@ export class ResponseError extends BentleyError { return (response !== undefined && response.statusType === HttpStatus.ServerError); } - public static parseHttpStatus(status: number): HttpStatus { - switch (status) { + public static parseHttpStatus(statusType: number): HttpStatus { + switch (statusType) { case 1: return HttpStatus.Info; case 2: @@ -203,8 +203,8 @@ const logRequest = (req: sarequest.SuperAgentRequest) => { }; // @todo The purpose of this wrapper is to allow us to easily replace this with another -// module that will rid us of NodeJs dependency.The alternate HTTP module is currently -// being written and allow working in desktop environments also. +// module that will rid us of NodeJs dependency. + /** * Wrapper around HTTP request utility * @param url Server URL to address the request diff --git a/core/clients/src/SettingsAdmin.ts b/core/clients/src/SettingsAdmin.ts index 296c723..cd9b7ac 100644 --- a/core/clients/src/SettingsAdmin.ts +++ b/core/clients/src/SettingsAdmin.ts @@ -32,7 +32,8 @@ export const enum SettingsStatus { * These are constructed by the SettingsAdmin methods and examined by applications. */ export class SettingsResult { - /** Construct a new SettingsResult + /** Construct a new SettingsResult. SettingsResult objects are created by the SettingsAdmin methods. + * @internal * @param status The result of the settings method. * @param errorMessage An error message that is sometimes returned by the server. * @param setting The object returned by the Settings method. Used only in the "get" methods. diff --git a/core/clients/src/SettingsClient.ts b/core/clients/src/SettingsClient.ts index f06048b..4bb15e7 100644 --- a/core/clients/src/SettingsClient.ts +++ b/core/clients/src/SettingsClient.ts @@ -11,7 +11,11 @@ import { SettingsAdmin, SettingsStatus, SettingsResult } from "./SettingsAdmin"; import { BentleyError, BentleyStatus, ActivityLoggingContext } from "@bentley/bentleyjs-core"; import { ImsDelegationSecureTokenClient } from "./ImsClients"; -/** Client API for the CONNECT ProductSettingsService - implements the SettingsAdmin interface when settings are stored by CONNECT. */ +/** + * Client API for the CONNECT ProductSettingsService - implements the SettingsAdmin interface when settings are stored by CONNECT. + * This class is not accessed directly from applications, they should use IModelApp.SettingsAdmin. + * @internal + */ export class ConnectSettingsClient extends Client implements SettingsAdmin { public static readonly searchKey: string = "ProductSettingsService.RP"; /** @@ -32,10 +36,6 @@ export class ConnectSettingsClient extends Client implements SettingsAdmin { return imsClient.getToken(alctx, authSamlToken, baseUrl); } - /** - * Gets name/key to query the service URLs from the URL Discovery Service ("Buddi") - * @returns Search key for the URL. - */ protected getUrlSearchKey(): string { return ConnectSettingsClient.searchKey; } // gets the portion of the Url that encapsulates the type of setting requested. @@ -165,7 +165,6 @@ export class ConnectSettingsClient extends Client implements SettingsAdmin { }); } - // Saves user settings public async saveUserSetting(alctx: ActivityLoggingContext, settings: any, settingNamespace: string, settingName: string, accessToken: AccessToken, applicationSpecific: boolean, projectId?: string, iModelId?: string): Promise { return this.saveAnySetting(alctx, true, settings, settingNamespace, settingName, accessToken, applicationSpecific, projectId, iModelId); } diff --git a/core/clients/src/Token.ts b/core/clients/src/Token.ts index ae94af3..23645c0 100644 --- a/core/clients/src/Token.ts +++ b/core/clients/src/Token.ts @@ -162,7 +162,7 @@ export class AccessToken extends Token { } /** Create an AccessToken from a JWT token for OIDC workflows */ - public static fromJsonWebTokenString(jwt: string, startsAt: Date, expiresAt: Date, userInfo: UserInfo): AccessToken { + public static fromJsonWebTokenString(jwt: string, startsAt: Date, expiresAt: Date, userInfo?: UserInfo): AccessToken { const token = new AccessToken(""); token._jwt = jwt; token._startsAt = startsAt; diff --git a/core/clients/src/imodelhub/Briefcases.ts b/core/clients/src/imodelhub/Briefcases.ts index 427609a..066623b 100644 --- a/core/clients/src/imodelhub/Briefcases.ts +++ b/core/clients/src/imodelhub/Briefcases.ts @@ -230,18 +230,18 @@ export class BriefcaseHandler { * * This method does not work on the browser. Directory containing the Briefcase file is created if it does not exist. If there is an error during download, any partially downloaded file is deleted from disk. * @param briefcase Briefcase to download. This needs to include a download link. See [[BriefcaseQuery.selectDownloadUrl]]. - * @param downloadToPathname Directory where the Briefcase should be downloaded. + * @param path Path where briefcase file should be downloaded, including filename. * @param progressCallback Callback for tracking progress. * @throws [[IModelHubClientError]] with [IModelHubStatus.UndefinedArgumentError]($bentley) or [IModelHubStatus.InvalidArgumentError]($bentley) if one of the arguments is undefined or has an invalid value. * @throws [[IModelHubClientError]] with [IModelHubStatus.NotSupportedInBrowser]($bentley) if called in a browser. * @throws [[IModelHubClientError]] with [IModelHubStatus.FileHandlerNotSet]($bentley) if [[FileHandler]] instance was not set for [[IModelClient]]. * @throws [[ResponseError]] if the briefcase cannot be downloaded. */ - public async download(alctx: ActivityLoggingContext, briefcase: Briefcase, downloadToPathname: string, progressCallback?: (progress: ProgressInfo) => void): Promise { + public async download(alctx: ActivityLoggingContext, briefcase: Briefcase, path: string, progressCallback?: (progress: ProgressInfo) => void): Promise { alctx.enter(); Logger.logInfo(loggingCategory, `Downloading briefcase ${briefcase.wsgId} for iModel ${briefcase.iModelId}`); ArgumentCheck.defined("briefcase", briefcase); - ArgumentCheck.defined("downloadToPathname", downloadToPathname); + ArgumentCheck.defined("path", path); if (typeof window !== "undefined") return Promise.reject(IModelHubClientError.browser()); @@ -252,7 +252,7 @@ export class BriefcaseHandler { if (!briefcase.downloadUrl) return Promise.reject(IModelHubClientError.missingDownloadUrl("briefcase")); - await this._fileHandler.downloadFile(alctx, briefcase.downloadUrl, downloadToPathname, parseInt(briefcase.fileSize!, 10), progressCallback); + await this._fileHandler.downloadFile(alctx, briefcase.downloadUrl, path, parseInt(briefcase.fileSize!, 10), progressCallback); alctx.enter(); Logger.logTrace(loggingCategory, `Downloading briefcase ${briefcase.wsgId} for iModel ${briefcase.iModelId}`); } diff --git a/core/clients/src/imodelhub/ChangeSets.ts b/core/clients/src/imodelhub/ChangeSets.ts index 5c3ce3b..f608fb6 100644 --- a/core/clients/src/imodelhub/ChangeSets.ts +++ b/core/clients/src/imodelhub/ChangeSets.ts @@ -320,16 +320,16 @@ export class ChangeSetHandler { * This method creates the directory containing the ChangeSets if necessary. If there is an error in downloading some of the ChangeSets, all partially downloaded ChangeSets are deleted from disk. * @param alctx The activity logging context * @param changeSets ChangeSets to download. These need to include a download link. See [[ChangeSetQuery.selectDownloadUrl]]. - * @param downloadToPath Directory where the ChangeSets should be downloaded. + * @param path Path of directory where the ChangeSets should be downloaded. * @throws [[IModelHubClientError]] with [IModelHubStatus.UndefinedArgumentError]($bentley), if one of the required arguments is undefined or empty. * @param progressCallback Callback for tracking progress. * @throws [[ResponseError]] if the download fails. */ - public async download(alctx: ActivityLoggingContext, changeSets: ChangeSet[], downloadToPath: string, progressCallback?: (progress: ProgressInfo) => void): Promise { + public async download(alctx: ActivityLoggingContext, changeSets: ChangeSet[], path: string, progressCallback?: (progress: ProgressInfo) => void): Promise { alctx.enter(); Logger.logInfo(loggingCategory, `Downloading ${changeSets.length} changesets`); ArgumentCheck.nonEmptyArray("changeSets", changeSets); - ArgumentCheck.defined("downloadToPath", downloadToPath); + ArgumentCheck.defined("path", path); if (typeof window !== "undefined") return Promise.reject(IModelHubClientError.browser()); @@ -351,7 +351,7 @@ export class ChangeSetHandler { changeSets.forEach((changeSet) => queue.push(async () => { const downloadUrl: string = changeSet.downloadUrl!; - const downloadToPathname: string = fileHandler.join(downloadToPath, changeSet.fileName!); + const downloadPath: string = fileHandler.join(path, changeSet.fileName!); let previouslyDownloaded = 0; const callback = (progress: ProgressInfo) => { @@ -359,7 +359,7 @@ export class ChangeSetHandler { previouslyDownloaded = progress.loaded; progressCallback!({ loaded: downloadedSize, total: totalSize, percent: downloadedSize / totalSize }); }; - return fileHandler.downloadFile(alctx, downloadUrl, downloadToPathname, parseInt(changeSet.fileSize!, 10), progressCallback ? callback : undefined); + return fileHandler.downloadFile(alctx, downloadUrl, downloadPath, parseInt(changeSet.fileSize!, 10), progressCallback ? callback : undefined); })); await queue.waitAll(); @@ -374,7 +374,7 @@ export class ChangeSetHandler { * @param token Delegation token of the authorized user. * @param imodelId Id of the iModel. See [[HubIModel]]. * @param changeSet Information of the ChangeSet to be uploaded. - * @param changeSetPathname Pathname of the ChangeSet file to be uploaded. + * @param path Path of the ChangeSet file to be uploaded. * @param progressCallback Callback for tracking upload progress. * @throws [IModelHubStatus.BriefcaseDoesNotBelongToUser]($bentley) if Briefcase specified by changeSet.briefcaseId belongs to another user. * @throws [IModelHubStatus.AnotherUserPushing]($bentley) if another user is currently uploading a ChangeSet. @@ -383,13 +383,13 @@ export class ChangeSetHandler { * @throws [IModelHubStatus.ChangeSetPointsToBadSeed]($bentley) if changeSet.seedFileId is not set to the correct file id. That file id should match to the value written to the Briefcase file. See [IModelDb.setGuid]($backend). * @throws [Common iModelHub errors]($docs/learning/iModelHub/CommonErrors) */ - public async create(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, changeSet: ChangeSet, changeSetPathname: string, progressCallback?: (progress: ProgressInfo) => void): Promise { + public async create(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, changeSet: ChangeSet, path: string, progressCallback?: (progress: ProgressInfo) => void): Promise { alctx.enter(); Logger.logInfo(loggingCategory, `Uploading changeset ${changeSet.id} to iModel ${imodelId}`); ArgumentCheck.defined("token", token); ArgumentCheck.validGuid("imodelId", imodelId); ArgumentCheck.defined("changeSet", changeSet); - ArgumentCheck.defined("changeSetPathname", changeSetPathname); + ArgumentCheck.defined("path", path); if (typeof window !== "undefined") return Promise.reject(IModelHubClientError.browser()); @@ -397,12 +397,12 @@ export class ChangeSetHandler { if (!this._fileHandler) return Promise.reject(IModelHubClientError.fileHandler()); - if (!this._fileHandler.exists(changeSetPathname) || this._fileHandler.isDirectory(changeSetPathname)) + if (!this._fileHandler.exists(path) || this._fileHandler.isDirectory(path)) return Promise.reject(IModelHubClientError.fileNotFound()); const postChangeSet = await this._handler.postInstance(alctx, ChangeSet, token, this.getRelativeUrl(imodelId), changeSet); - await this._fileHandler.uploadFile(alctx, postChangeSet.uploadUrl!, changeSetPathname, progressCallback); + await this._fileHandler.uploadFile(alctx, postChangeSet.uploadUrl!, path, progressCallback); alctx.enter(); postChangeSet.uploadUrl = undefined; diff --git a/core/clients/src/imodelhub/Client.ts b/core/clients/src/imodelhub/Client.ts index e8c7563..d2bfdf3 100644 --- a/core/clients/src/imodelhub/Client.ts +++ b/core/clients/src/imodelhub/Client.ts @@ -17,7 +17,7 @@ import { ActivityLoggingContext } from "@bentley/bentleyjs-core"; export class IModelHubClient extends IModelClient { /** * Create an instance of IModelHubClient. - * @param fileHandler File handler to handle file upload/download and file system operations. See [[AzureFileHandler]]. + * @param fileHandler File handler to handle file upload/download and file system operations. * @param iModelBaseHandler WSG Client for iModel Hub operations. See [[IModelBaseHandler]] */ public constructor(fileHandler?: FileHandler, iModelBaseHandler: IModelBaseHandler = new IModelBaseHandler()) { diff --git a/core/clients/src/imodelhub/Thumbnails.ts b/core/clients/src/imodelhub/Thumbnails.ts index afef145..122662c 100644 --- a/core/clients/src/imodelhub/Thumbnails.ts +++ b/core/clients/src/imodelhub/Thumbnails.ts @@ -126,7 +126,7 @@ export class ThumbnailHandler { return Promise.reject(new Error("Expected an image to be returned from the query")); } - const base64Data = Base64.btoa(String.fromCharCode.apply(null, byteArray)); + const base64Data = Base64.btoa(String.fromCharCode(...byteArray)); return "data:image/png;base64," + base64Data; } diff --git a/core/clients/src/imodelhub/iModels.ts b/core/clients/src/imodelhub/iModels.ts index e9d286e..2c9c7fc 100644 --- a/core/clients/src/imodelhub/iModels.ts +++ b/core/clients/src/imodelhub/iModels.ts @@ -14,6 +14,7 @@ import { ProgressInfo } from "../Request"; import { IModelBaseHandler } from "./BaseHandler"; const loggingCategory = "imodeljs-clients.imodelhub"; +const iModelTemplateEmpty = "Empty"; /** * HubIModel represents an iModel on iModelHub. Getting a valid HubIModel instance from iModelHub is required for majority of iModelHub method calls, as wsgId of this object needs to be passed as imodelId argument to those methods. @@ -45,6 +46,10 @@ export class HubIModel extends WsgInstance { /** Set to true, when iModel is ready to be used. See [[IModelHandler.create]]. */ @ECJsonTypeMap.propertyToJson("wsg", "properties.Initialized") public initialized?: boolean; + + /** @hidden - internal property, set when creating iModel from empty seed file */ + @ECJsonTypeMap.propertyToJson("wsg", "properties.iModelTemplate") + public iModelTemplate?: string; } /** Initialization state of seed file. Can be queried with [[IModelHandler.getInitializationState]]. See [iModel creation]($docs/learning/iModelHub/iModels/CreateiModel.md). */ @@ -193,22 +198,22 @@ class SeedFileHandler { * @param token Delegation token of the authorized user. * @param imodelId Id of the iModel. See [[HubIModel]]. * @param seedFile Information of the SeedFile to be uploaded. - * @param seedPathname Pathname of the SeedFile to be uploaded. + * @param seedPath Path of the SeedFile to be uploaded. * @param progressCallback Callback for tracking progress. */ - public async uploadSeedFile(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, seedPathname: string, seedFileDescription?: string, progressCallback?: (progress: ProgressInfo) => void): Promise { + public async uploadSeedFile(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, seedPath: string, seedFileDescription?: string, progressCallback?: (progress: ProgressInfo) => void): Promise { alctx.enter(); Logger.logInfo(loggingCategory, `Uploading seed file to iModel ${imodelId}`); const seedFile = new SeedFile(); - seedFile.fileName = this._fileHandler!.basename(seedPathname); - seedFile.fileSize = this._fileHandler!.getFileSize(seedPathname).toString(); + seedFile.fileName = this._fileHandler!.basename(seedPath); + seedFile.fileSize = this._fileHandler!.getFileSize(seedPath).toString(); if (seedFileDescription) seedFile.fileDescription = seedFileDescription; const createdSeedFile: SeedFile = await this._handler.postInstance(alctx, SeedFile, token, this.getRelativeUrl(imodelId), seedFile); alctx.enter(); - await this._fileHandler!.uploadFile(alctx, createdSeedFile.uploadUrl!, seedPathname, progressCallback); + await this._fileHandler!.uploadFile(alctx, createdSeedFile.uploadUrl!, seedPath, progressCallback); alctx.enter(); createdSeedFile.uploadUrl = undefined; createdSeedFile.downloadUrl = undefined; @@ -328,8 +333,9 @@ export class IModelsHandler { * @param projectId Id of the connect [[Project]]. * @param iModelName Name of the iModel on the Hub. * @param description Description of the iModel on the Hub. + * @param iModelTemplate iModel template. */ - private async createIModelInstance(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, iModelName: string, description?: string): Promise { + private async createIModelInstance(alctx: ActivityLoggingContext, token: AccessToken, projectId: string, iModelName: string, description?: string, iModelTemplate?: string): Promise { alctx.enter(); Logger.logInfo(loggingCategory, `Creating iModel with name ${iModelName} in project ${projectId}`); @@ -338,6 +344,8 @@ export class IModelsHandler { iModel.name = iModelName; if (description) iModel.description = description; + if (iModelTemplate) + iModel.iModelTemplate = iModelTemplate; try { imodel = await this._handler.postInstance(alctx, HubIModel, token, this.getRelativeUrl(projectId), iModel); @@ -400,13 +408,14 @@ export class IModelsHandler { * @param token Delegation token of the authorized user. * @param contextId Id for the iModel's context. For iModelHub it should be the id of the connect [[Project]]. * @param name Name of the iModel on the Hub. + * @param path iModel seed file path. If not defined, iModel will be created from an empty file. * @param description Description of the iModel on the Hub. * @param progressCallback Callback for tracking progress. * @param timeOutInMiliseconds Time to wait for iModel initialization. * @throws [[IModelHubError]] with [IModelHubStatus.UserDoesNotHavePermission]($bentley) if the user does not have CreateiModel permission. * @throws [Common iModelHub errors]($docs/learning/iModelHub/CommonErrors) */ - public async create(alctx: ActivityLoggingContext, token: AccessToken, contextId: string, name: string, pathName: string, + public async create(alctx: ActivityLoggingContext, token: AccessToken, contextId: string, name: string, path?: string, description?: string, progressCallback?: (progress: ProgressInfo) => void, timeOutInMilliseconds: number = 120000): Promise { alctx.enter(); @@ -414,7 +423,8 @@ export class IModelsHandler { ArgumentCheck.defined("token", token); ArgumentCheck.validGuid("contextId", contextId); ArgumentCheck.defined("name", name); - ArgumentCheck.defined("pathName", pathName); + + const imodelFromTemplate = !path; if (typeof window !== "undefined") return Promise.reject(IModelHubClientError.browser()); @@ -422,21 +432,26 @@ export class IModelsHandler { if (!this._fileHandler) return Promise.reject(IModelHubClientError.fileHandler()); - if (!this._fileHandler.exists(pathName) || this._fileHandler.isDirectory(pathName)) + if (!!path && (!this._fileHandler.exists(path) || this._fileHandler.isDirectory(path))) return Promise.reject(IModelHubClientError.fileNotFound()); - const imodel = await this.createIModelInstance(alctx, token, contextId, name, description); + const imodelTemplate = imodelFromTemplate ? iModelTemplateEmpty : undefined; + const imodel = await this.createIModelInstance(alctx, token, contextId, name, description, imodelTemplate); alctx.enter(); + if (imodelFromTemplate) { + return imodel; + } + try { - await this._seedFileHandler.uploadSeedFile(alctx, token, imodel.id!, pathName, description, progressCallback); + await this._seedFileHandler.uploadSeedFile(alctx, token, imodel.id!, path!, description, progressCallback); } catch (err) { await this.delete(alctx, token, contextId, imodel.id!); return Promise.reject(err); } alctx.enter(); - const errorMessage = "Cannot upload SeedFile " + pathName; + const errorMessage = "Cannot upload SeedFile " + path; const retryDelay = timeOutInMilliseconds / 10; for (let retries = 10; retries > 0; --retries) { try { @@ -495,16 +510,16 @@ export class IModelsHandler { * Method to download the seed file for iModel. This will download the original seed file, that was uploaded when creating iModel. To download a file that was updated with ChangeSets on iModelHub, see [[BriefcaseHandler.download]]. * @param token Delegation token of the authorized user. * @param imodelId Id of the iModel. See [[HubIModel]]. - * @param downloadToPathname Directory where the seed file should be downloaded. + * @param path Path to download the seed file to, including file name. * @param progressCallback Callback for tracking progress. * @throws [Common iModelHub errors]($docs/learning/iModelHub/CommonErrors) */ - public async download(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, downloadToPathname: string, progressCallback?: (progress: ProgressInfo) => void): Promise { + public async download(alctx: ActivityLoggingContext, token: AccessToken, imodelId: GuidString, path: string, progressCallback?: (progress: ProgressInfo) => void): Promise { alctx.enter(); Logger.logInfo(loggingCategory, `Downloading seed file for iModel ${imodelId}`); ArgumentCheck.defined("token", token); ArgumentCheck.validGuid("imodelId", imodelId); - ArgumentCheck.defined("downloadToPathname", downloadToPathname); + ArgumentCheck.defined("path", path); if (typeof window !== "undefined") return Promise.reject(IModelHubClientError.browser()); @@ -518,7 +533,7 @@ export class IModelsHandler { if (!seedFiles || !seedFiles[0] || !seedFiles[0].downloadUrl) return Promise.reject(IModelHubError.fromId(IModelHubStatus.FileDoesNotExist, "Failed to get seed file.")); - await this._fileHandler.downloadFile(alctx, seedFiles[0].downloadUrl!, downloadToPathname, parseInt(seedFiles[0].fileSize!, 10), progressCallback); + await this._fileHandler.downloadFile(alctx, seedFiles[0].downloadUrl!, path, parseInt(seedFiles[0].fileSize!, 10), progressCallback); alctx.enter(); Logger.logTrace(loggingCategory, `Downloading seed file for iModel ${imodelId}`); } @@ -600,13 +615,14 @@ export class IModelHandler { * @param token Delegation token of the authorized user. * @param contextId Id for the iModel's context. For iModelHub it should be the id of the connect [[Project]]. * @param name Name of the iModel on the Hub. + * @param path iModel seed file path. If not defined, iModel will be created from an empty file. * @param description Description of the iModel on the Hub. * @param progressCallback Callback for tracking progress. * @param timeOutInMiliseconds Time to wait for iModel initialization. * @throws [[IModelHubError]] with [IModelHubStatus.UserDoesNotHavePermission]($bentley) if the user does not have CreateiModel permission. * @throws [Common iModelHub errors]($docs/learning/iModelHub/CommonErrors) */ - public async create(alctx: ActivityLoggingContext, token: AccessToken, contextId: string, name: string, pathName: string, + public async create(alctx: ActivityLoggingContext, token: AccessToken, contextId: string, name: string, path?: string, description?: string, progressCallback?: (progress: ProgressInfo) => void, timeOutInMilliseconds: number = 120000): Promise { let imodelExists = true; @@ -622,7 +638,7 @@ export class IModelHandler { if (imodelExists) return Promise.reject(new IModelHubError(IModelHubStatus.iModelAlreadyExists)); - return this._handler.create(alctx, token, contextId, name, pathName, description, progressCallback, timeOutInMilliseconds); + return this._handler.create(alctx, token, contextId, name, path, description, progressCallback, timeOutInMilliseconds); } /** @@ -646,13 +662,13 @@ export class IModelHandler { * @param alctx Activity logging context * @param token Delegation token of the authorized user. * @param contextId Id for the iModel's context. For iModelHub it should be the id of the connect [[Project]]. - * @param downloadToPathname Directory where the seed file should be downloaded. + * @param path Path where seed file should be downloaded, including filename. * @param progressCallback Callback for tracking progress. * @throws [[IModelHubError]] with [IModelHubStatus.iModelDoesNotExist]$(bentley) if iModel does not exist. * @throws [Common iModelHub errors]($docs/learning/iModelHub/CommonErrors) */ - public async download(alctx: ActivityLoggingContext, token: AccessToken, contextId: string, downloadToPathname: string, progressCallback?: (progress: ProgressInfo) => void): Promise { + public async download(alctx: ActivityLoggingContext, token: AccessToken, contextId: string, path: string, progressCallback?: (progress: ProgressInfo) => void): Promise { const imodel = await this.get(alctx, token, contextId); - await this._handler.download(alctx, token, imodel.id!, downloadToPathname, progressCallback); + await this._handler.download(alctx, token, imodel.id!, path, progressCallback); } } diff --git a/core/clients/src/imodeljs-clients.ts b/core/clients/src/imodeljs-clients.ts index 58aad90..ff44227 100644 --- a/core/clients/src/imodeljs-clients.ts +++ b/core/clients/src/imodeljs-clients.ts @@ -22,9 +22,6 @@ export * from "./IModelBank/IModelBankClient"; export * from "./IModelBank/IModelBankHandler"; export * from "./IModelBank/IModelBankFileSystemContextClient"; -// NOTE: Classes with backend-specific dependencies (like "fs") must be kept out of the "barrel" to avoid unacceptable webpack trickery on the frontend. -// NOTE: Do not export UrlFileHandler - "fs" dependency - export * from "./imodelhub/BaseHandler"; export * from "./imodelhub/Client"; export * from "./imodelhub/Query"; @@ -39,13 +36,21 @@ export * from "./imodelhub/Locks"; export * from "./imodelhub/Users"; export * from "./imodelhub/Versions"; export * from "./imodelhub/Thumbnails"; -export * from "./imodelhub/AzureFileHandler"; - -// NOTE: Classes with backend-specific dependencies (like "fs") must be kept out of the "barrel" to avoid unacceptable webpack trickery on the frontend. -// NOTE: Do not export AzureFileHandler - "fs" dependency export * from "./oidc/OidcClient"; export * from "./oidc/OidcFrontendClient"; +export * from "./oidc/AngularOidcFrontendClient"; + +export * from "./ulas/LogEntryConverter"; +export * from "./ulas/UlasClient"; + +// Set the version number so it can be found at runtime. BUILD_SEMVER is replaced at build time by the webpack DefinePlugin. +declare var BUILD_SEMVER: string; +if ((typeof (BUILD_SEMVER) !== "undefined") && (typeof window !== "undefined") && window) { + if (!(window as any).iModelJsVersions) + (window as any).iModelJsVersions = new Map(); + (window as any).iModelJsVersions.set("imodeljs-clients", BUILD_SEMVER); +} /** @docs-package-description * The imodeljs-clients package allows sending requests to various CONNECT services. @@ -80,6 +85,10 @@ export * from "./oidc/OidcFrontendClient"; * @docs-group-description iModels * Classes for abstracting access to [iModelHub]($docs/learning/iModelHub/index.md). See [iModelBank]($docs/reference/imodeljs-clients/imodelbank). */ +/** + * @docs-group-description Settings + * Classes for saving and retrieving application-, project-, and iModel- specific [Settings]($docs/learning/frontend/Settings.md) + */ /** * @docs-group-description OtherServices * Classes for communicating with various other services. diff --git a/core/clients/src/oidc/AngularOidcFrontendClient.ts b/core/clients/src/oidc/AngularOidcFrontendClient.ts new file mode 100644 index 0000000..29965b5 --- /dev/null +++ b/core/clients/src/oidc/AngularOidcFrontendClient.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { IOidcFrontendClient } from "./OidcFrontendClient"; + +/** Interface for frontend client that handles redirect callback */ +export interface IAngularOidcFrontendClient extends IOidcFrontendClient { + handleRedirectCallback(): Promise; +} diff --git a/core/clients/src/test/Config.test.ts b/core/clients/src/test/Config.test.ts index 469a909..05c4465 100644 --- a/core/clients/src/test/Config.test.ts +++ b/core/clients/src/test/Config.test.ts @@ -45,6 +45,8 @@ describe("Config", () => { assert.equal(101, Config.App.get("APPLE")); assert.equal("101_101_101_101", Config.App.get("MY_APPLE")); + assert.equal("101_101_101_101", Config.App.get("MY_APPLE")); + assert.equal("202_202_202_202", Config.App.get("MY_BANANA")); assert.equal("202_202_202_202", Config.App.get("MY_BANANA")); }); diff --git a/core/clients/src/test/ConnectClients.test.ts b/core/clients/src/test/ConnectClients.test.ts index e0391fb..82d19e5 100644 --- a/core/clients/src/test/ConnectClients.test.ts +++ b/core/clients/src/test/ConnectClients.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import * as chai from "chai"; -import { ConnectClient, RbacClient, Project, ConnectRequestQueryOptions, IModelHubPermissions, RbacUser } from "../ConnectClients"; +import { ConnectClient, Project, ConnectRequestQueryOptions } from "../ConnectClients"; import { AuthorizationToken, AccessToken } from "../Token"; import { TestConfig } from "./TestConfig"; import { ActivityLoggingContext } from "@bentley/bentleyjs-core"; @@ -66,57 +66,3 @@ describe("ConnectClient (#integration)", () => { }); }); - -describe("RbacClient (#integration)", () => { - let accessToken: AccessToken; - const connectClient = new ConnectClient(); - const rbacClient = new RbacClient(); - const actx = new ActivityLoggingContext(""); - - before(async function (this: Mocha.IHookCallbackContext) { - this.enableTimeouts(false); - const authToken: AuthorizationToken = await TestConfig.login(); - accessToken = await connectClient.getAccessToken(actx, authToken); - }); - - it("should get the permissions relevant to the iModelHubService for the specified project (#integration)", async function (this: Mocha.ITestCallbackContext) { - // Get test project - const queryOptions: ConnectRequestQueryOptions = { - $filter: "Name+eq+'" + TestConfig.projectName + "'", - }; - const project: Project = await connectClient.getProject(actx, accessToken, queryOptions); - chai.expect(!!project); - - // Round trip the access token to mimic its use - const newAccessToken = AccessToken.fromSamlTokenString(accessToken.toTokenString()!); - - const permissions: IModelHubPermissions = await rbacClient.getIModelHubPermissions(actx, newAccessToken!, project.wsgId); - - chai.expect(permissions & IModelHubPermissions.CreateIModel); - chai.expect(permissions & IModelHubPermissions.ReadIModel); - chai.expect(permissions & IModelHubPermissions.ModifyIModel); - chai.expect(permissions & IModelHubPermissions.ManageResources); - chai.expect(permissions & IModelHubPermissions.ManageVersions); - }); - - it("should get the users in the specified project (#integration)", async function (this: Mocha.ITestCallbackContext) { - // Get test project - const queryOptions: ConnectRequestQueryOptions = { - $filter: "Name+eq+'" + TestConfig.projectName + "'", - }; - const project: Project = await connectClient.getProject(actx, accessToken, queryOptions); - chai.expect(!!project); - - // Get the user ID we are using that should exist in the returned users - const currentUserId = accessToken.getUserInfo()!.id; - // Get users - const users: RbacUser[] = await rbacClient.getUsers(actx, accessToken!, project.wsgId); - - // We should have some valid users - chai.expect(users.length !== 0); - // Test that the user accessing this is existent in the users returned - let foundUser = false; - users.map((user: RbacUser) => { foundUser = foundUser || (user.wsgId === currentUserId); }); - chai.expect(foundUser); - }); -}); diff --git a/core/clients/src/test/RealityDataServicesClient.test.ts b/core/clients/src/test/RealityDataServicesClient.test.ts index 0bf87e1..7799c3f 100644 --- a/core/clients/src/test/RealityDataServicesClient.test.ts +++ b/core/clients/src/test/RealityDataServicesClient.test.ts @@ -3,23 +3,27 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import * as chai from "chai"; -import { Version } from "../imodelhub/Versions"; +// import { Version } from "../imodelhub/Versions"; import { AuthorizationToken, AccessToken } from "../Token"; import { TestConfig } from "./TestConfig"; import { RealityDataServicesClient, RealityData } from "../RealityDataServicesClient"; -import { IModelHubClient } from "../imodeljs-clients"; +// import { IModelHubClient } from "../imodeljs-clients"; import { ActivityLoggingContext } from "@bentley/bentleyjs-core"; +import { Range2d } from "@bentley/geometry-core"; + chai.should(); describe.skip("RealityDataServicesClient", () => { let accessToken: AccessToken; - const imodelHubClient: IModelHubClient = new IModelHubClient(); + // const imodelHubClient: IModelHubClient = new IModelHubClient(); const realityDataServiceClient: RealityDataServicesClient = new RealityDataServicesClient(); - const projectId: string = "b2101b1a-0c1f-451e-97f2-6599bf900d36"; - const iModelId: string = "0c315eb1-d10c-4449-9c09-f36d54ad37f2"; - let versionId: string; - const tilesId: any = null; + const projectId: string = "fb1696c8-c074-4c76-a539-a5546e048cc6"; + // const iModelId: string = "0c315eb1-d10c-4449-9c09-f36d54ad37f2"; + // let versionId: string; + const tilesId: string = "593eff78-b757-4c07-84b2-a8fe31c19927"; + const tilesIdWithRootDocPath: string = "3317b4a0-0086-4f16-a979-6ceb496d785e"; + const actx = new ActivityLoggingContext(""); before(async function (this: Mocha.IHookCallbackContext) { @@ -28,11 +32,11 @@ describe.skip("RealityDataServicesClient", () => { const authToken: AuthorizationToken = await TestConfig.login(); accessToken = await realityDataServiceClient.getAccessToken(actx, authToken); - const imodelHubToken = await imodelHubClient.getAccessToken(actx, authToken); - const versions: Version[] = await imodelHubClient.versions.get(actx, imodelHubToken, iModelId); - chai.expect(versions); - versionId = versions[0].wsgId; - chai.expect(versionId); + // const imodelHubToken = await imodelHubClient.getAccessToken(actx, authToken); + // const versions: Version[] = await imodelHubClient.versions.get(actx, imodelHubToken, iModelId); + // chai.expect(versions); + // versionId = versions[0].wsgId; + // chai.expect(versionId); // const instanceId: string = `${projectId}--${iModelId}--${versionId}`; // const queryOptions: RequestQueryOptions = { @@ -44,57 +48,116 @@ describe.skip("RealityDataServicesClient", () => { }); it("should be able to retrieve reality data properties (#integration)", async function (this: Mocha.ITestCallbackContext) { - const realityData: RealityData[] = await realityDataServiceClient.getRealityData(actx, accessToken, projectId, tilesId); + const realityData: RealityData = await realityDataServiceClient.getRealityData(actx, accessToken, projectId, tilesId); + chai.assert(realityData); + chai.assert(realityData.id === tilesId); + chai.assert(realityData.client); + chai.assert(realityData.projectId === projectId); + }); + + it("should be able to retrieve reality data properties for every reality data associated to project (#integration)", async function (this: Mocha.ITestCallbackContext) { + const realityData: RealityData[] = await realityDataServiceClient.getRealityDataInProject(actx, accessToken, projectId); + + realityData.forEach((value) => { + chai.assert(value.type === "RealityMesh3DTiles"); // iModelJS only supports this type + chai.assert(value.rootDocument && value.rootDocument !== ""); // All such type require a root document to work correctly + chai.assert(value.projectId === projectId); + chai.assert(value.id); + }); chai.assert(realityData); }); - it("should be able to retrieve app data json blob url (#integration)", async function (this: Mocha.ITestCallbackContext) { - const url: string = await realityDataServiceClient.getAppDataBlobUrl(actx, accessToken, projectId, tilesId); + it("should be able to retrieve reality data properties for every reality data associated to project within an extent (#integration)", async function (this: Mocha.ITestCallbackContext) { + const theRange = Range2d.createXYXY(-80 * 3.1416 / 180, 39 * 3.1416 / 180, -74 * 3.1416 / 180, 42 * 3.1416 / 180); // Range encloses Pensylvania and should gather Shell project + const realityData: RealityData[] = await realityDataServiceClient.getRealityDataInProjectOverlapping(actx, accessToken, projectId, theRange); - chai.assert(url); + realityData.forEach((value) => { + chai.assert(value.type === "RealityMesh3DTiles"); // iModelJS only supports this type + chai.assert(value.rootDocument && value.rootDocument !== ""); // All such type require a root document to work correctly + chai.assert(value.projectId === projectId); + chai.assert(value.id); + }); + + chai.assert(realityData); }); - it("should be able to get app data json (#integration)", async function (this: Mocha.ITestCallbackContext) { - if (TestConfig.enableMocks) - this.skip(); + it("should be able to retrieve app data json blob url (#integration)", async function (this: Mocha.ITestCallbackContext) { + const realityData: RealityData = await realityDataServiceClient.getRealityData(actx, accessToken, projectId, tilesId); - const appData: any = await realityDataServiceClient.getAppData(actx, accessToken, projectId, tilesId); + const url: string = await realityData.getRootDocumentJson(actx, accessToken); - chai.assert(appData); + chai.assert(url); }); it("should be able to get model data json (#integration)", async function (this: Mocha.ITestCallbackContext) { - const appData: any = await realityDataServiceClient.getAppData(actx, accessToken, projectId, tilesId); - const appDataJson = JSON.parse(appData.toString("utf8")); + const realityData: RealityData = await realityDataServiceClient.getRealityData(actx, accessToken, projectId, tilesId); + + const rootData: any = await realityData.getRootDocumentJson(actx, accessToken); + chai.assert(rootData); - const modelName = appDataJson.models[Object.keys(appDataJson.models)[0]].tilesetUrl; + const rootDataJson = JSON.parse(rootData.toString("utf8")); - chai.assert(appData); + const modelName = rootDataJson.root.children[0].content.url; chai.assert(modelName); - const modelData: any = await realityDataServiceClient.getModelData(actx, accessToken, projectId, tilesId, modelName); + const modelData: any = await realityData.getModelData(actx, accessToken, modelName); chai.assert(modelData); }); it("should be able to get model data content (#integration)", async function (this: Mocha.ITestCallbackContext) { - const appData: any = await realityDataServiceClient.getAppData(actx, accessToken, projectId, tilesId); - const appDataJson = JSON.parse(appData.toString("utf8")); - const modelName = appDataJson.models[Object.keys(appDataJson.models)[0]].tilesetUrl; + const realityData: RealityData = await realityDataServiceClient.getRealityData(actx, accessToken, projectId, tilesId); - chai.assert(appData); + const rootData: any = await realityData.getRootDocumentJson(actx, accessToken); + const rootDataJson = JSON.parse(rootData.toString("utf8")); + + const modelName = rootDataJson.root.children[0].content.url; + + chai.assert(rootData); chai.assert(modelName); - const modelData: any = await realityDataServiceClient.getModelData(actx, accessToken, projectId, tilesId, modelName); - const modelDataJson = JSON.parse(modelData.toString("utf8")); + const modelData: any = await realityData.getModelData(actx, accessToken, modelName); + + chai.assert(modelData); + }); + + it("should be able to get model data content with root doc not at blob root (root doc path) (#integration)", async function (this: Mocha.ITestCallbackContext) { + const realityData: RealityData = await realityDataServiceClient.getRealityData(actx, accessToken, projectId, tilesIdWithRootDocPath); + + // The root document of this reality should not be at the root of the blob + const rootParts = realityData.rootDocument!.split("/"); + chai.assert(rootParts.length >= 2); + rootParts.pop(); + const rootDocPath: string = rootParts.join("/") + "/"; + + const rootData: any = await realityData.getRootDocumentJson(actx, accessToken); + const rootDataJson = JSON.parse(rootData.toString("utf8")); + + const modelName = rootDataJson.root.children[0].children[0].content.url; + + chai.assert(rootData); + chai.assert(modelName); + + let exceptionThrown: boolean = false; + try { + // Should fail as we call with an incorrect content path. + const data: any = await realityData.getTileContent(actx, accessToken, modelName); + chai.assert(!data); /// Should never be reached. + } catch { + exceptionThrown = true; + } + chai.assert(exceptionThrown); + + // Should succeed as we call with added root document path + const data2: any = await realityData.getTileContent(actx, accessToken, rootDocPath + modelName, false); - let contentPath = modelDataJson.root.content.url; - contentPath = `TileSets//Bim//${contentPath.split(".")[0]}/${contentPath}`; + chai.assert(data2); - const data: any = await realityDataServiceClient.getTileContent(actx, accessToken, projectId, tilesId, contentPath); + // Should succeed as we call with indicate that path is relative to root path + const data3: any = await realityData.getTileContent(actx, accessToken, modelName, true); - chai.assert(data); + chai.assert(data3); }); }); diff --git a/core/clients/src/test/TestConfig.ts b/core/clients/src/test/TestConfig.ts index 5119812..4990c65 100644 --- a/core/clients/src/test/TestConfig.ts +++ b/core/clients/src/test/TestConfig.ts @@ -8,39 +8,15 @@ import { HubIModel } from "../imodelhub/iModels"; import { IModelHubClient, IModelClient } from "../imodeljs-clients"; import { ConnectClient, Project } from "../ConnectClients"; import { expect } from "chai"; -import * as fs from "fs"; -import * as path from "path"; -import { Logger, LogLevel, ActivityLoggingContext, GuidString, Guid } from "@bentley/bentleyjs-core"; +import { Logger, ActivityLoggingContext, GuidString, Guid } from "@bentley/bentleyjs-core"; import { Config } from "../Config"; import { IModelJsConfig } from "@bentley/config-loader/lib/IModelJsConfig"; -import { loggingCategoryFullUrl } from "../Request"; IModelJsConfig.init(true /* suppress exception */, false /* suppress error message */, Config.App); const actx = new ActivityLoggingContext(Guid.createValue()); -const logFileStream = fs.createWriteStream(path.join(__dirname, "./iModelClientsTests.log"), { flags: "a" }); - -// The Request URLs are captured separate. The log file is used by the Hub URL whitelist validation. -export const urlLogPath = path.join(__dirname, "./requesturls.log"); -const urlLogFileStream = fs.createWriteStream(urlLogPath, { flags: "a" }); -console.log("URL Log file created at: " + urlLogPath); - -function logFunction(logLevel: string, category: string, message: string) { - if (category === loggingCategoryFullUrl) - urlLogFileStream.write(message + "\n"); - else - logFileStream.write(logLevel + "|" + category + "|" + message + "\n"); -} - -// Initialize logger to file -Logger.initialize( - (category: string, message: string): void => { logFunction("Error", category, message); }, - (category: string, message: string): void => { logFunction("Warning", category, message); }, - (category: string, message: string): void => { logFunction("Info", category, message); }, - (category: string, message: string): void => { logFunction("Trace", category, message); }); - // Note: Turn this off unless really necessary - it causes Error messages on the // console with the existing suite of tests, and this is quite misleading, // especially when diagnosing CI job failures. @@ -50,9 +26,6 @@ if (!!loggingConfigFile) { Logger.configureLevels(require(loggingConfigFile)); } -// log all request URLs as this will be the input to the Hub URL whitelist test -Logger.setLevel(loggingCategoryFullUrl, LogLevel.Trace); - /** Credentials for test users */ export interface UserCredentials { email: string; diff --git a/core/clients/src/test/UlasClient.test.ts b/core/clients/src/test/UlasClient.test.ts new file mode 100644 index 0000000..171cf08 --- /dev/null +++ b/core/clients/src/test/UlasClient.test.ts @@ -0,0 +1,380 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { assert } from "chai"; +import { GuidString, Guid, BentleyStatus, ActivityLoggingContext } from "@bentley/bentleyjs-core"; +import { AuthorizationToken, AccessToken } from "../Token"; +import { UserInfo } from "../UserInfo"; +import { UlasClient, UsageLogEntry, FeatureLogEntry, FeatureStartedLogEntry, FeatureEndedLogEntry, LogPostingResponse, UsageType } from "../ulas/UlasClient"; +import { TestConfig } from "./TestConfig"; + +import * as os from "os"; + +describe("UlasClient - SAML Token (#integration)", () => { + + let authToken: AuthorizationToken; + let accessToken: AccessToken; + const client: UlasClient = new UlasClient(); + const actx = new ActivityLoggingContext(""); + + before(async function (this: Mocha.IHookCallbackContext) { + authToken = await TestConfig.login(); + accessToken = await client.getAccessToken(actx, authToken); + }); + + function populateUserInfo(entry: UsageLogEntry | FeatureLogEntry, userInfo?: UserInfo, hostUserName?: string): void { + if (!userInfo) + return; + + const featureTrackingInfo = userInfo.featureTracking; + + const imsId: GuidString = userInfo.id; + const ultimateSite: number = !featureTrackingInfo ? 0 : parseInt(featureTrackingInfo.ultimateSite, 10); + const usageCountryIso: string = !featureTrackingInfo ? "" : featureTrackingInfo.usageCountryIso; + + entry.userInfo = { imsId, ultimateSite, usageCountryIso, hostUserName }; + } + + it("Post usage log (#integration)", async function (this: Mocha.ITestCallbackContext) { + for (const usageType of [UsageType.Beta, UsageType.HomeUse, UsageType.PreActivation, UsageType.Production, UsageType.Trial]) { + const entry: UsageLogEntry = new UsageLogEntry(os.hostname(), usageType); + populateUserInfo(entry, accessToken.getUserInfo(), os.userInfo().username); + entry.productId = 43; + entry.productVersion = { major: 3, minor: 4, sub1: 5, sub2: 99 }; + + const resp: LogPostingResponse = await client.logUsage(new ActivityLoggingContext(Guid.createValue()), accessToken, entry); + assert(resp); + assert.equal(resp.status, BentleyStatus.SUCCESS); + assert.equal(resp.message, "Accepted"); + assert.isTrue(Guid.isGuid(resp.requestId)); + assert.isAtLeast(resp.time, 0); + } + }); + + it("Post usage log with project id (#integration)", async function (this: Mocha.ITestCallbackContext) { + const entry: UsageLogEntry = new UsageLogEntry(os.hostname(), UsageType.Trial); + populateUserInfo(entry, accessToken.getUserInfo(), os.userInfo().username); + entry.projectId = Guid.createValue(); + entry.productId = 43; + entry.productVersion = { major: 0, minor: 4 }; + + const resp: LogPostingResponse = await client.logUsage(new ActivityLoggingContext(Guid.createValue()), accessToken, entry); + assert(resp); + assert.equal(resp.status, BentleyStatus.SUCCESS); + assert.equal(resp.message, "Accepted"); + assert.isTrue(Guid.isGuid(resp.requestId)); + assert.isAtLeast(resp.time, 0); + }); + + it("Post usage log without product version (#integration)", async function (this: Mocha.ITestCallbackContext) { + const entry: UsageLogEntry = new UsageLogEntry(os.hostname(), UsageType.Trial); + populateUserInfo(entry, accessToken.getUserInfo(), os.userInfo().username); + entry.projectId = Guid.createValue(); + entry.productId = 43; + + const resp: LogPostingResponse = await client.logUsage(new ActivityLoggingContext(Guid.createValue()), accessToken, entry); + assert(resp); + assert.equal(resp.status, BentleyStatus.SUCCESS); + assert.equal(resp.message, "Accepted"); + assert.isTrue(Guid.isGuid(resp.requestId)); + assert.isAtLeast(resp.time, 0); + }); + + it("Post usage log - hostName and hostUserName special cases (#integration)", async function (this: Mocha.ITestCallbackContext) { + for (const host of [{ name: "::1", user: os.userInfo().username }, { name: "127.0.0.1", user: os.userInfo().username }, { name: "localhost", user: os.userInfo().username }, + { name: os.hostname(), user: "BENTLEY\\\\me" }, { name: os.hostname(), user: "BENTLEY/me" }, { name: os.hostname(), user: "Administrator" }, + { name: os.hostname(), user: "system" }, { name: os.hostname(), user: "" }, { name: os.hostname() }]) { + const entry: UsageLogEntry = new UsageLogEntry(host.name, UsageType.Beta); + populateUserInfo(entry, accessToken.getUserInfo(), host.user); + entry.productId = 43; + entry.productVersion = { major: 3, minor: 4, sub1: 5 }; + + const resp: LogPostingResponse = await client.logUsage(new ActivityLoggingContext(Guid.createValue()), accessToken, entry); + assert(resp); + assert.equal(resp.status, BentleyStatus.SUCCESS); + assert.equal(resp.message, "Accepted"); + assert.isTrue(Guid.isGuid(resp.requestId)); + assert.isAtLeast(resp.time, 0); + } + }); + + it("Invalid usage log entry (#integration)", async function (this: Mocha.ITestCallbackContext) { + let entry: UsageLogEntry = new UsageLogEntry("", UsageType.HomeUse); + populateUserInfo(entry, accessToken.getUserInfo(), os.userInfo().username); + entry.productId = 43; + entry.productVersion = { major: 3, minor: 4, sub1: 5, sub2: 101 }; + + let hasThrown: boolean = false; + try { + await client.logUsage(new ActivityLoggingContext(Guid.createValue()), accessToken, entry); + } catch (e) { + hasThrown = true; + } + assert.isTrue(hasThrown, "UlasClient.logUsage is expected to throw if hostName is not specified."); + + entry = new UsageLogEntry(os.hostname(), 100 as UsageType); + populateUserInfo(entry, accessToken.getUserInfo(), os.userInfo().username); + entry.productId = 43; + entry.productVersion = { major: 3, minor: 4, sub1: 5, sub2: 101 }; + + hasThrown = false; + try { + await client.logUsage(new ActivityLoggingContext(Guid.createValue()), accessToken, entry); + } catch (e) { + hasThrown = true; + } + assert.isTrue(hasThrown, "UlasClient.logUsage is expected to throw if UsageType is not one of the enum values."); + }); + + it("AccessToken without feature tracking claims (#integration)", async function (this: Mocha.ITestCallbackContext) { + enum TokenMode { + Valid, + NoUserProfile, + NoUserId, + NoUltimateId, + } + + for (const mode of [TokenMode.Valid, TokenMode.NoUserProfile, TokenMode.NoUserId, TokenMode.NoUltimateId]) { + let token: AccessToken; + if (mode === TokenMode.NoUserProfile) { + // fake token that does not contain a user profile + token = AccessToken.fromForeignProjectAccessTokenJson(JSON.stringify({ ForeignProjectAccessToken: {} }))!; + } else { + // token from which some user profile information is removed. UlasClient does not examine the actual token string. + authToken = await TestConfig.login(); + token = await client.getAccessToken(actx, authToken); + switch (mode) { + case TokenMode.NoUserId: + token.getUserInfo()!.id = ""; + break; + + case TokenMode.NoUltimateId: + token.getUserInfo()!.featureTracking = { ultimateSite: "", usageCountryIso: "" }; + break; + + default: + break; + } + } + + const uEntry: UsageLogEntry = new UsageLogEntry(os.hostname(), UsageType.Trial); + populateUserInfo(uEntry, token.getUserInfo(), os.userInfo().username); + uEntry.productId = 43; + uEntry.productVersion = { major: 3, minor: 4, sub1: 5, sub2: 101 }; + + let hasThrown: boolean = false; + try { + await client.logUsage(new ActivityLoggingContext(Guid.createValue()), token, uEntry); + } catch (e) { + hasThrown = true; + } + assert.equal(hasThrown, mode !== TokenMode.Valid, "UlasClient.logUsage is expected to throw if access token does not have valid user profile info."); + + const fEntry = new FeatureLogEntry(Guid.createValue(), os.hostname(), UsageType.Trial); + populateUserInfo(fEntry, token.getUserInfo(), os.userInfo().username); + fEntry.productId = 43; + fEntry.productVersion = { major: 3, minor: 4, sub1: 99 }; + + hasThrown = false; + try { + await client.logFeature(new ActivityLoggingContext(Guid.createValue()), token, fEntry); + } catch (e) { + hasThrown = true; + } + assert.equal(hasThrown, mode !== TokenMode.Valid, "UlasClient.logFeature is expected to throw if access token does not have valid user profile info."); + } + }); + + it("Post feature log (#integration)", async function (this: Mocha.ITestCallbackContext) { + const myFeatureId: GuidString = Guid.createValue(); + + for (const usageType of [UsageType.Beta, UsageType.HomeUse, UsageType.PreActivation, UsageType.Production, UsageType.Trial]) { + const entry = new FeatureLogEntry(myFeatureId, os.hostname(), usageType); + populateUserInfo(entry, accessToken.getUserInfo(), os.userInfo().username); + entry.productId = 43; + entry.productVersion = { major: 3, minor: 4, sub1: 99 }; + + const resp: LogPostingResponse = await client.logFeature(new ActivityLoggingContext(Guid.createValue()), accessToken, entry); + assert(resp); + assert.equal(resp.status, BentleyStatus.SUCCESS); + assert.equal(resp.message, "Accepted"); + assert.isTrue(Guid.isGuid(resp.requestId)); + assert.isAtLeast(resp.time, 0); + } + }); + + it("Post feature log with project id (#integration)", async function (this: Mocha.ITestCallbackContext) { + const entry = new FeatureLogEntry(Guid.createValue(), os.hostname(), UsageType.Trial); + populateUserInfo(entry, accessToken.getUserInfo(), os.userInfo().username); + entry.productId = 43; + entry.productVersion = { major: 3, minor: 4, sub1: 99 }; + entry.projectId = Guid.createValue(); + entry.usageData.push({ name: "imodelid", value: Guid.createValue() }, { name: "imodelsize", value: 596622 }); + const resp: LogPostingResponse = await client.logFeature(new ActivityLoggingContext(Guid.createValue()), accessToken, entry); + assert(resp); + assert.equal(resp.status, BentleyStatus.SUCCESS); + assert.equal(resp.message, "Accepted"); + assert.isTrue(Guid.isGuid(resp.requestId)); + assert.isAtLeast(resp.time, 0); + }); + + it("Post feature log without product version (#integration)", async function (this: Mocha.ITestCallbackContext) { + const entry = new FeatureLogEntry(Guid.createValue(), os.hostname(), UsageType.Trial); + populateUserInfo(entry, accessToken.getUserInfo(), os.userInfo().username); + entry.productId = 43; + entry.projectId = Guid.createValue(); + entry.usageData.push({ name: "imodelid", value: Guid.createValue() }, { name: "imodelsize", value: 596622 }); + const resp: LogPostingResponse = await client.logFeature(new ActivityLoggingContext(Guid.createValue()), accessToken, entry); + assert(resp); + assert.equal(resp.status, BentleyStatus.SUCCESS); + assert.equal(resp.message, "Accepted"); + assert.isTrue(Guid.isGuid(resp.requestId)); + assert.isAtLeast(resp.time, 0); + }); + + it("Post feature log - hostName and hostUserName special cases (#integration)", async function (this: Mocha.ITestCallbackContext) { + for (const host of [{ name: "::1", user: os.userInfo().username }, { name: "127.0.0.1", user: os.userInfo().username }, { name: "localhost", user: os.userInfo().username }, + { name: os.hostname(), user: "BENTLEY\\\\me" }, { name: os.hostname(), user: "BENTLEY/me" }, { name: os.hostname(), user: "Administrator" }, + { name: os.hostname(), user: "system" }]) { + const entry = new FeatureLogEntry(Guid.createValue(), host.name, UsageType.Beta); + populateUserInfo(entry, accessToken.getUserInfo(), host.user); + entry.productId = 43; + entry.productVersion = { major: 3, minor: 4, sub1: 5, sub2: 99 }; + entry.usageData.push({ name: "imodelid", value: Guid.createValue() }, { name: "imodelsize", value: 596622 }); + const resp: LogPostingResponse = await client.logFeature(new ActivityLoggingContext(Guid.createValue()), accessToken, entry); + assert(resp); + assert.equal(resp.status, BentleyStatus.SUCCESS); + assert.equal(resp.message, "Accepted"); + assert.isTrue(Guid.isGuid(resp.requestId)); + assert.isAtLeast(resp.time, 0); + } + }); + + it("Post multiple feature logs (#integration)", async function (this: Mocha.ITestCallbackContext) { + const feature1Id: GuidString = Guid.createValue(); + const feature2Id: GuidString = Guid.createValue(); + const entry1 = new FeatureLogEntry(feature1Id, os.hostname(), UsageType.HomeUse); + populateUserInfo(entry1, accessToken.getUserInfo(), os.userInfo().username); + entry1.productId = 43; + entry1.productVersion = { major: 3, minor: 4, sub1: 99 }; + entry1.usageData.push({ name: "imodelid", value: Guid.createValue() }, { name: "imodelsize", value: 596622 }); + + // omit product version in second entry + const entry2 = new FeatureLogEntry(feature2Id, os.hostname(), UsageType.Beta); + populateUserInfo(entry2, accessToken.getUserInfo(), os.userInfo().username); + entry2.productId = 43; + entry2.usageData.push({ name: "imodelid", value: Guid.createValue() }, { name: "imodelsize", value: 400 }); + let actCtx = new ActivityLoggingContext(Guid.createValue()); + const resp: LogPostingResponse = await client.logFeature(actCtx, accessToken, entry1, entry2); + assert(resp); + assert.equal(resp.status, BentleyStatus.SUCCESS); + assert.equal(resp.message, "Accepted"); + assert.isTrue(Guid.isGuid(resp.requestId)); + assert.isAtLeast(resp.time, 0); + + // test that the method throws if no feature log entry is passed + let hasThrown: boolean = false; + try { + actCtx = new ActivityLoggingContext(Guid.createValue()); + await client.logFeature(actCtx, accessToken); + } catch (e) { + hasThrown = true; + } + assert.isTrue(hasThrown, "Passing no FeatureLogEntry to UlasClient.logFeature is expected to throw."); + }); + + it("Post duration feature log (#integration)", async function (this: Mocha.ITestCallbackContext) { + const myFeatureId: GuidString = Guid.createValue(); + let startEntry = new FeatureStartedLogEntry(myFeatureId, os.hostname(), UsageType.Beta); + populateUserInfo(startEntry, accessToken.getUserInfo(), os.userInfo().username); + startEntry.productId = 43; + startEntry.productVersion = { major: 3, minor: 4 }; + startEntry.usageData.push({ name: "imodelid", value: Guid.createValue() }, { name: "user", value: "123-123" }); + + let actCtx = new ActivityLoggingContext(Guid.createValue()); + let startResp: LogPostingResponse = await client.logFeature(actCtx, accessToken, startEntry); + assert(startResp); + assert.equal(startResp.status, BentleyStatus.SUCCESS); + assert.equal(startResp.message, "Accepted"); + assert.isAtLeast(startResp.time, 0); + + let endEntry: FeatureEndedLogEntry = FeatureEndedLogEntry.fromStartEntry(startEntry); + actCtx = new ActivityLoggingContext(Guid.createValue()); + let endResp: LogPostingResponse = await client.logFeature(actCtx, accessToken, endEntry); + assert(endResp); + assert.equal(endResp.status, BentleyStatus.SUCCESS); + assert.equal(endResp.message, "Accepted"); + assert.isAtLeast(endResp.time, 0); + + // once more, now with building end entry from scratch + startEntry = new FeatureStartedLogEntry(myFeatureId, os.hostname(), UsageType.HomeUse); + populateUserInfo(startEntry, accessToken.getUserInfo(), os.userInfo().username); + startEntry.productId = 43; + startEntry.productVersion = { major: 3, minor: 4 }; + startEntry.usageData.push({ name: "imodelid", value: Guid.createValue() }, { name: "user", value: "123-123" }); + actCtx = new ActivityLoggingContext(Guid.createValue()); + startResp = await client.logFeature(actCtx, accessToken, startEntry); + assert(startResp); + assert.equal(startResp.status, BentleyStatus.SUCCESS); + assert.equal(startResp.message, "Accepted"); + assert.isAtLeast(startResp.time, 0); + + endEntry = new FeatureEndedLogEntry(myFeatureId, startEntry.entryId, os.hostname(), UsageType.HomeUse); + populateUserInfo(endEntry, accessToken.getUserInfo(), os.userInfo().username); + endEntry.productId = 32; + endEntry.productVersion = { major: 3, minor: 4 }; + actCtx = new ActivityLoggingContext(Guid.createValue()); + endResp = await client.logFeature(actCtx, accessToken, endEntry); + assert(endResp); + assert.equal(endResp.status, BentleyStatus.SUCCESS); + assert.equal(endResp.message, "Accepted"); + assert.isAtLeast(endResp.time, 0); + }); + + it("Post duration feature log without product version (#integration)", async function (this: Mocha.ITestCallbackContext) { + const myFeatureId: GuidString = Guid.createValue(); + let startEntry = new FeatureStartedLogEntry(myFeatureId, os.hostname(), UsageType.Beta); + populateUserInfo(startEntry, accessToken.getUserInfo(), os.userInfo().username); + startEntry.productId = 43; + startEntry.usageData.push({ name: "imodelid", value: Guid.createValue() }, { name: "user", value: "123-123" }); + + let actCtx = new ActivityLoggingContext(Guid.createValue()); + let startResp: LogPostingResponse = await client.logFeature(actCtx, accessToken, startEntry); + assert(startResp); + assert.equal(startResp.status, BentleyStatus.SUCCESS); + assert.equal(startResp.message, "Accepted"); + assert.isAtLeast(startResp.time, 0); + + let endEntry: FeatureEndedLogEntry = FeatureEndedLogEntry.fromStartEntry(startEntry); + actCtx = new ActivityLoggingContext(Guid.createValue()); + let endResp: LogPostingResponse = await client.logFeature(actCtx, accessToken, endEntry); + assert(endResp); + assert.equal(endResp.status, BentleyStatus.SUCCESS); + assert.equal(endResp.message, "Accepted"); + assert.isAtLeast(endResp.time, 0); + + // once more, now with building end entry from scratch + startEntry = new FeatureStartedLogEntry(myFeatureId, os.hostname(), UsageType.HomeUse); + populateUserInfo(startEntry, accessToken.getUserInfo(), os.userInfo().username); + startEntry.productId = 43; + startEntry.usageData.push({ name: "imodelid", value: Guid.createValue() }, { name: "user", value: "123-123" }); + actCtx = new ActivityLoggingContext(Guid.createValue()); + startResp = await client.logFeature(actCtx, accessToken, startEntry); + assert(startResp); + assert.equal(startResp.status, BentleyStatus.SUCCESS); + assert.equal(startResp.message, "Accepted"); + assert.isAtLeast(startResp.time, 0); + + endEntry = new FeatureEndedLogEntry(myFeatureId, startEntry.entryId, os.hostname(), UsageType.HomeUse); + populateUserInfo(endEntry, accessToken.getUserInfo(), os.userInfo().username); + endEntry.productId = 32; + actCtx = new ActivityLoggingContext(Guid.createValue()); + endResp = await client.logFeature(actCtx, accessToken, endEntry); + assert(endResp); + assert.equal(endResp.status, BentleyStatus.SUCCESS); + assert.equal(endResp.message, "Accepted"); + assert.isAtLeast(endResp.time, 0); + }); + +}); diff --git a/core/clients/src/ulas/LogEntryConverter.ts b/core/clients/src/ulas/LogEntryConverter.ts new file mode 100644 index 0000000..a7a2d94 --- /dev/null +++ b/core/clients/src/ulas/LogEntryConverter.ts @@ -0,0 +1,222 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { Guid, GuidString } from "@bentley/bentleyjs-core"; +import { UsageLogEntry, FeatureLogEntry, FeatureStartedLogEntry, FeatureEndedLogEntry, ProductVersion, UsageType, UsageUserInfo } from "./UlasClient"; + +/** @hidden */ +/** Specifies the JSON format for a UsageLogEntry as expected by the ULAS REST API + * (see https://qa-connect-ulastm.bentley.com/Bentley.ULAS.SwaggerUI/SwaggerWebApp/?urls.primaryName=ULAS%20Posting%20Service%20v1) + */ +export interface UsageLogEntryJson { + /** Ultimate ID, i.e. company ID in SAP */ + ultID: number | undefined; + /** The ID of the Principal that was granted access to the application */ + pid: GuidString | undefined; + /** The GUID of the IMS user accessing the product, maybe the same as the Principal. */ + imsID: GuidString | undefined; + /** The client’s machine name excluding domain information. */ + hID: string; + /** The client’s login name excluding domain information */ + uID: string | undefined; + /** The GUID embedded in the policy file that allows us to track the entitlement history. */ + polID: GuidString; + /** The ID of the securable. */ + secID: string; + /** The product ID for which usage is being submitted. It is a 4-digit Product ID from the GPR. */ + prdid: number | undefined; + /** A feature string further identifying the product for which available usage is being submitted. Not to be confused with feature IDs. */ + fstr: string; + /** The version of the application producing the usage. + * Format: Pad all sections out to 4 digits padding is with zeros, e.g. 9.10.2.113 becomes 9001000020113. + */ + ver: number | undefined; + /** The GUID of the project that the usage should be associated with. + * If no project is selected, omit the field. + */ + projID: GuidString | undefined; + /** The GUID that identifies a unique usage session, used to correlate data between feature usage and usage logs. */ + corID: GuidString; + /** The UTC time of the event. */ + evTimeZ: string; + /** The version of the schema which this log entry represents. */ + lVer: number; + /** Identifies the source of the usage log entry: RealTime, Offline, Checkout */ + lSrc: string; + /** Identifies the country where the client reporting the usage belongs to. */ + country: string | undefined; + /** The type of usage that occurred on the client. It is acting as a filter to eliminate records from log processing that + * should not count towards a customer’s peak processing. One of: Production, Trial, Beta, HomeUse, PreActivation + */ + uType: string; +} + +export interface FeatureLogEntryAttributeJson { + name: string; + value: string; +} + +/** Specifies the JSON format for a FeatureLogEntry as expected by the ULAS REST API + * (see https://qa-connect-ulastm.bentley.com/Bentley.ULAS.SwaggerUI/SwaggerWebApp/?urls.primaryName=ULAS%20Posting%20Service%20v1) + */ +export interface FeatureLogEntryJson extends UsageLogEntryJson { + /** Gets the ID of the feature used (from the Global Feature Registry) */ + ftrID: GuidString; + /** The start date in UTC when feature usage has started (for duration feature log entries) */ + sDateZ: string; + /** The end date in UTC when feature usage has started (for duration feature log entries) */ + eDateZ: string; + /** Additional user-defined metadata for the feature usage */ + uData: FeatureLogEntryAttributeJson[]; +} + +export class LogEntryConverter { + // for now this is always 1 + private static readonly _logEntryVersion: number = 1; + // this is a realtime client, i.e. it sends the requests right away without caching or aggregating. + private static readonly _logPostingSource: string = "RealTime"; + // fStr argument is empty for now + private static readonly _featureString: string = ""; + private static readonly _policyFileId: GuidString = Guid.createValue(); + private static readonly _securableId: string = Guid.createValue(); + private static readonly _correlationId: GuidString = Guid.createValue(); + + public static toUsageLogJson(entry: UsageLogEntry): UsageLogEntryJson { + const userInfo: UsageUserInfo | undefined = entry.userInfo; + const imsID: GuidString | undefined = !!userInfo ? userInfo.imsId : undefined; + const ultID: number | undefined = !!userInfo ? userInfo.ultimateSite : undefined; + const usageCountry: string | undefined = !!userInfo ? userInfo.usageCountryIso : undefined; + + const hID: string = LogEntryConverter.prepareMachineName(entry.hostName); + const uID: string | undefined = !userInfo || !userInfo.hostUserName ? imsID : LogEntryConverter.prepareUserName(userInfo.hostUserName, entry.hostName); + + const ver: number | undefined = LogEntryConverter.toVersionNumber(entry.productVersion); + const uType: string = LogEntryConverter.usageTypeToString(entry.usageType); + + return { + ultID, pid: imsID, // Principal ID for now is IMS Id (eventually should be pulled from policy files) + imsID, hID, uID, polID: LogEntryConverter._policyFileId, secID: LogEntryConverter._securableId, prdid: entry.productId, + fstr: LogEntryConverter._featureString, ver, projID: entry.projectId, corID: LogEntryConverter._correlationId, + evTimeZ: entry.timestamp, lVer: LogEntryConverter._logEntryVersion, lSrc: LogEntryConverter._logPostingSource, + country: usageCountry, uType, + }; + } + + public static toFeatureLogJson(entries: FeatureLogEntry[]): FeatureLogEntryJson[] { + const json: FeatureLogEntryJson[] = []; + for (const entry of entries) { + const userInfo: UsageUserInfo | undefined = entry.userInfo; + const imsID: GuidString | undefined = !!userInfo ? userInfo.imsId : undefined; + const ultID: number | undefined = !!userInfo ? userInfo.ultimateSite : undefined; + const usageCountry: string | undefined = !!userInfo ? userInfo.usageCountryIso : undefined; + + const hID: string = LogEntryConverter.prepareMachineName(entry.hostName); + const uID: string | undefined = !userInfo || !userInfo.hostUserName ? imsID : LogEntryConverter.prepareUserName(userInfo.hostUserName, entry.hostName); + + const ver: number | undefined = LogEntryConverter.toVersionNumber(entry.productVersion); + + const evTimeZ: string = entry.timestamp; + let sDateZ: string; + let eDateZ: string; + let corID: GuidString; + const startEntry: FeatureStartedLogEntry = entry as FeatureStartedLogEntry; + const endEntry: FeatureEndedLogEntry = entry as FeatureEndedLogEntry; + const defaultDate: string = "0001-01-01T00:00:00Z"; + if (!!startEntry.entryId) { + sDateZ = evTimeZ; + eDateZ = defaultDate; + corID = startEntry.entryId; + } else if (!!endEntry.startEntryId) { + sDateZ = defaultDate; + eDateZ = evTimeZ; + corID = endEntry.startEntryId; + } else { + sDateZ = evTimeZ; + eDateZ = evTimeZ; + corID = LogEntryConverter._correlationId; + } + + const uType: string = LogEntryConverter.usageTypeToString(entry.usageType); + + const uData: FeatureLogEntryAttributeJson[] = []; + for (const att of entry.usageData) { + uData.push({ name: att.name, value: att.value.toString() }); + } + + const entryJson: FeatureLogEntryJson = { + ultID, pid: imsID, // Principal ID for now is IMS Id (eventually should be pulled from policy files) + imsID, hID, uID, polID: LogEntryConverter._policyFileId, secID: LogEntryConverter._securableId, + prdid: entry.productId, fstr: LogEntryConverter._featureString, ver, projID: entry.projectId, corID, + evTimeZ, lVer: LogEntryConverter._logEntryVersion, lSrc: LogEntryConverter._logPostingSource, + country: usageCountry, uType, ftrID: entry.featureId, sDateZ, eDateZ, uData, + }; + + json.push(entryJson); + } + return json; + } + + private static toVersionNumber(version?: ProductVersion): number | undefined { + if (!version) + return undefined; + + // version must be encoded into a single number where each version digit is padded out to 4 digits + // and the version is always considered to have 4 digits. + // Ex: 3.99.4 -> 3.99.4.0 -> 3009900040000 + let verNumber: number = !!version.sub2 ? version.sub2 : 0; + verNumber += 10000 * (!!version.sub1 ? version.sub1 : 0); + verNumber += Math.pow(10000, 2) * version.minor; + verNumber += Math.pow(10000, 3) * version.major; + return verNumber; + } + + private static prepareMachineName(machineName: string): string { + if (!machineName || machineName.length === 0) + return ""; + + if (machineName === "::1" || machineName === "127.0.0.1") + return "localhost"; + + return machineName.toLowerCase(); + } + + private static prepareUserName(userName: string, machineName: string): string { + if (!userName || userName.length === 0) + return ""; + + let preparedUserName: string = userName; + + const backslashPos: number = userName.indexOf("\\"); + if (backslashPos >= 0) + preparedUserName = userName.substr(backslashPos + 1); + else { + const slashPos: number = userName.indexOf("/"); + if (slashPos >= 0) + preparedUserName = userName.substr(slashPos + 1); + } + + preparedUserName = preparedUserName.toLowerCase(); + if (!!machineName && machineName.length > 0 && (preparedUserName.includes("administrator") || preparedUserName.includes("system"))) + preparedUserName = `${machineName.toLowerCase()}\\${preparedUserName}`; + + return preparedUserName; + } + + private static usageTypeToString(val: UsageType): string { + switch (val) { + case UsageType.Beta: + return "Beta"; + case UsageType.HomeUse: + return "HomeUse"; + case UsageType.PreActivation: + return "PreActivation"; + case UsageType.Production: + return "Production"; + case UsageType.Trial: + return "Trial"; + default: + throw new Error("Unhandled UsageType enum value"); + } + } +} diff --git a/core/clients/src/ulas/UlasClient.ts b/core/clients/src/ulas/UlasClient.ts new file mode 100644 index 0000000..e81f758 --- /dev/null +++ b/core/clients/src/ulas/UlasClient.ts @@ -0,0 +1,373 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { Logger, BentleyStatus, Guid, GuidString, LogLevel, ActivityLoggingContext } from "@bentley/bentleyjs-core"; +import { AccessToken, IncludePrefix, AuthorizationToken } from "../Token"; +import { request, Response, RequestOptions } from "../Request"; +import { Client } from "../Client"; +import { Config } from "../Config"; +import { ImsDelegationSecureTokenClient } from "../ImsClients"; +import { LogEntryConverter, UsageLogEntryJson, FeatureLogEntryJson } from "./LogEntryConverter"; + +/** + * Usage Logging and Analysis Services Client. + */ + +/** + * Logging category for the UlasClient. + */ +const loggingCategory: string = "ulasclient"; + +/** + * Represents one of the potential usage types. + * See also + * - [[UsageLogEntry]], [[FeatureLogEntry]] + * - *UsageType* entry on [ULAS Swagger](https://qa-connect-ulastm.bentley.com/Bentley.ULAS.SwaggerUI/SwaggerWebApp/?urls.primaryName=ULAS%20Posting%20Service%20v1) + * site (section *Models*) + */ +export enum UsageType { + Production, Trial, Beta, HomeUse, PreActivation, +} + +/** + * Represents the version of the product logging usage or features. + * See also [[UsageLogEntry]], [[FeatureLogEntry]]. + */ +export interface ProductVersion { + major: number; + minor: number; + sub1?: number; + sub2?: number; +} + +/** + * Information about the user for who usage is tracked with the ULAS Posting Service. + * See [[UsageLogEntry]] and [[FeatureLogEntry]] for how to use it. + * > You do not have to pass this to [[UlasClient]], if you have an OIDC access token from + * > a client registration that includes the ULAS scope in its audiences. + */ +export interface UsageUserInfo { + /** IMS User ID */ + imsId: GuidString; + /** Ultimate ID, i.e. company ID in SAP */ + ultimateSite: number; + /** Identifies the country where the client reporting the usage belongs to. */ + usageCountryIso: string; + /* The user's login name. + * > If omitted, the IMS user id will be used. + */ + hostUserName?: string; +} + +/** + * Usage log entry data that is submitted to the ULAS Posting Service. + * See also + * - [[UlasClient]] + * - *UsageLogEntry* entry on [ULAS Swagger](https://qa-connect-ulastm.bentley.com/Bentley.ULAS.SwaggerUI/SwaggerWebApp/?urls.primaryName=ULAS%20Posting%20Service%20v1) + * site (section *Models*) + */ +export class UsageLogEntry { + /** The GUID of the project that the usage should be associated with. */ + public projectId?: GuidString; + + /** Information about the user for which usage is logged. + * > This can be omitted if the OIDC access token includes the information already. + * > This means the OIDC client registration must include the ULAS scope in its audiences. + */ + public userInfo?: UsageUserInfo; + + /** Name of the client machine from which usage is logged */ + public readonly hostName: string; + + /** The product ID from the Global Product Registry (GPR) for which usage is being submitted. It is a 4-digit number. + * > It can be omitted if the access token is an OIDC token which includes the client_id. In that case, ULAS will + * > determine the GPR product ID from the client_id. + */ + public productId?: number; + + /** Version of the product for which usage is logged. */ + public productVersion?: ProductVersion; + + /** The type of usage that occurred on the client. It is acting as a filter to eliminate records from log processing that + * should not count towards a customer’s peak processing. + */ + public readonly usageType: UsageType; + + /** Timestamp against which the usage is logged. + * It is set at construction time of this object. + */ + public readonly timestamp: string; + + /** Creates a new UsageLogEntry object. + * This also sets the timestamp against which the usage will be logged. + * @param hostName Name of the client machine from which usage is logged. + * @param usageType Usage type (see [[UsageType]]) + */ + public constructor(hostName: string, usageType: UsageType) { + this.hostName = hostName; + this.usageType = usageType; + this.timestamp = new Date().toISOString(); + } +} + +/** + * Represents arbitrary metadata that can be attached to a + * [[FeatureLogEntry]] when collecting information about feature usage. + */ +export interface FeatureLogEntryAttribute { + name: string; + value: any; +} + +/** + * Feature log entry data that is submitted to the ULAS Posting Service. + * See also + * - [[UlasClient]] + * - *FeatureLogEntry* entry on [ULAS Swagger](https://qa-connect-ulastm.bentley.com/Bentley.ULAS.SwaggerUI/SwaggerWebApp/?urls.primaryName=ULAS%20Posting%20Service%20v1) + * site (section *Models*) + */ +export class FeatureLogEntry { + /** The GUID of the project that the usage should be associated with. */ + public projectId?: GuidString; + + /** ID of the feature to log (from the Global Feature Registry). */ + public readonly featureId: GuidString; + + /** Additional user-defined metadata for the feature usage. */ + public usageData: FeatureLogEntryAttribute[]; + + /** Information about the user for which usage is logged. + * > This can be omitted if the OIDC access token includes the information already. + * > This means the OIDC client registration must include the ULAS scope in its audiences. + */ + public userInfo?: UsageUserInfo; + + /** Name of the client machine from which usage is logged. */ + public readonly hostName: string; + + /** Version of the product for which the feature is logged. */ + public productVersion?: ProductVersion; + + /** The product ID from the Global Product Registry (GPR) for which usage is being submitted. It is a 4-digit number. + * > It can be omitted if the access token is an OIDC token which includes the client_id. In that case, ULAS will + * > determine the GPR product ID from the client_id. + */ + public productId?: number; + + /** The type of usage that occurred on the client. It is acting as a filter to eliminate records from log processing that + * should not count towards a customer’s peak processing. + */ + public readonly usageType: UsageType; + + /** Timestamp against which the feature is logged. + * It is set at construction time of this object. + */ + public readonly timestamp: string; + + /** Creates a new FeatureLogEntry object. + * This also sets the timestamp against which the feature will be logged. + * @param featureId Feature ID from the Global Feature Registry which is being logged. + * @param hostName Name of the client machine from which the feature is being logged. + * @param usageType Usage type (see [[UsageType]]) + */ + public constructor(featureId: GuidString, hostName: string, usageType: UsageType) { + this.featureId = featureId; + this.hostName = hostName; + this.usageType = usageType; + this.usageData = []; + this.timestamp = new Date().toISOString(); + } +} + +/** + * Start point of a duration Feature log entry that is submitted to the ULAS Posting Service. + * See also + * - [[UlasClient]] + * - [[FeatureLogEntry]] + * - *FeatureLogEntry* entry on [ULAS Swagger](https://qa-connect-ulastm.bentley.com/Bentley.ULAS.SwaggerUI/SwaggerWebApp/?urls.primaryName=ULAS%20Posting%20Service%20v1) + * site (section *Models*) + */ +export class FeatureStartedLogEntry extends FeatureLogEntry { + /** ID of this entry which must be passed to the respective [[FeatureEndedLogEntry]] to + * correlate start and end entry. + */ + public readonly entryId: GuidString; + + /** Creates a new FeatureStartedLogEntry object. + * @param featureId Feature ID from the Global Feature Registry which is being logged. + * @param hostName Name of the client machine from which the feature is being logged. + * @param usageType Usage type (see [[UsageType]]) + */ + public constructor(featureId: GuidString, hostName: string, usageType: UsageType) { + super(featureId, hostName, usageType); + this.entryId = Guid.createValue(); + } +} + +/** + * End point of a duration Feature log entry that is submitted to the ULAS Posting Service. + * See also + * - [[UlasClient]] + * - [[FeatureStartedLogEntry]] + * - *FeatureLogEntry* entry on [ULAS Swagger](https://qa-connect-ulastm.bentley.com/Bentley.ULAS.SwaggerUI/SwaggerWebApp/?urls.primaryName=ULAS%20Posting%20Service%20v1) + * site (section *Models*) + */ +export class FeatureEndedLogEntry extends FeatureLogEntry { + /* ID of the corresponding [[FeatureStartedLogEntry]]. + * See [[FeatureStartedLogEntry.entryId]] + */ + public readonly startEntryId: GuidString; + + /** Creates a new FeatureEndedLogEntry object. + * @param featureId Feature ID from the Global Feature Registry which is being logged. + * @param startEntryId ID of the corresponding [[FeatureStartedLogEntry]] + * @param hostName Name of the client machine from which the feature is being logged. + * @param usageType Usage type (see [[UsageType]]) + */ + public constructor(featureId: GuidString, startEntryId: GuidString, hostName: string, usageType: UsageType) { + super(featureId, hostName, usageType); + this.startEntryId = startEntryId; + } + + /** Creates a new FeatureEndedLogEntry from the specified FeatureStartedLogEntry. + * @param startEntry Corresponding [[FeatureStartedLogEntry]] + * @return Corresponding FeatureEndedLogEntry. + */ + public static fromStartEntry(startEntry: FeatureStartedLogEntry): FeatureEndedLogEntry { + const endEntry = new FeatureEndedLogEntry(startEntry.featureId, startEntry.entryId, + startEntry.hostName, startEntry.usageType); + + endEntry.projectId = startEntry.projectId; + endEntry.usageData = startEntry.usageData; + endEntry.productId = startEntry.productId; + endEntry.productVersion = startEntry.productVersion; + endEntry.userInfo = startEntry.userInfo; + + return endEntry; + } +} + +/** + * Response from posting a [[UsageLogEntry]] or [[FeatureLogEntry]] with the [[UlasClient]]. + * See also *LogPostingResponse* entry on [ULAS Swagger](https://qa-connect-ulastm.bentley.com/Bentley.ULAS.SwaggerUI/SwaggerWebApp/?urls.primaryName=ULAS%20Posting%20Service%20v1) + * site (section *Models*) + */ +export interface LogPostingResponse { + /* The overall status of the request. */ + status: BentleyStatus; + /* The message localized in client's language. */ + message: string; + /* The time in milliseconds it took to complete the request submitted by the client. */ + time: number; + /* The unique ID of the request assigned by the server when handling the client's request. */ + requestId: GuidString; +} + +/** + * Client for the Bentley Usage Logging & Analysis Services (ULAS). + * See also the two `POST` requests on [ULAS Swagger](https://qa-connect-ulastm.bentley.com/Bentley.ULAS.SwaggerUI/SwaggerWebApp/?urls.primaryName=ULAS%20Posting%20Service%20v1) + */ +export class UlasClient extends Client { + private static readonly _buddiSearchKey: string = "UsageLoggingServices.RealtimeLogging.Url"; + private static readonly _configRelyingPartyUri = "imjs_ulas_relying_party_uri"; + private static readonly _configDefaultRelyingPartyUri = "imjs_default_relying_party_uri"; + + /** + * Creates an instance of UlasClient. + */ + constructor() { super(); } + + /** + * Gets name/key to query the service URLs from the URL Discovery Service ("Buddi") + * @returns Search key for the URL. + */ + protected getUrlSearchKey(): string { return UlasClient._buddiSearchKey; } + + protected async setupOptionDefaults(options: RequestOptions): Promise { + await super.setupOptionDefaults(options); + options.useCorsProxy = true; + } + + /** + * Gets theRelyingPartyUrl for the service. + * @returns RelyingPartyUrl for the service. + */ + private getRelyingPartyUrl(): string { + if (Config.App.has(UlasClient._configRelyingPartyUri)) + return Config.App.get(UlasClient._configRelyingPartyUri) + "/"; + else + return Config.App.get(UlasClient._configDefaultRelyingPartyUri) + "/"; + } + + /** + * Gets the (delegation) access token to access the service + * @param authTokenInfo Access token. + * @returns Resolves to the (delegation) access token. + */ + public async getAccessToken(alctx: ActivityLoggingContext, authorizationToken: AuthorizationToken): Promise { + const imsClient = new ImsDelegationSecureTokenClient(); + return imsClient.getToken(alctx, authorizationToken, this.getRelyingPartyUrl()); + } + + /** + * Logs usage via the ULAS service + * @param alctx Activity logging context. + * @param token Access token. + * @param entry Usage log entry. + * @returns Response from the service. + */ + public async logUsage(alctx: ActivityLoggingContext, token: AccessToken, entry: UsageLogEntry): Promise { + alctx.enter(); + const entryJson: UsageLogEntryJson = LogEntryConverter.toUsageLogJson(entry); + return this.logEntry(alctx, token, entryJson, false); + } + + /** + * Logs one ore more feature entries via the ULAS service + * @param alctx Activity logging context. + * @param token Access token. + * @param entries One or more feature log entries. + * @returns Response from the service. + */ + public async logFeature(alctx: ActivityLoggingContext, token: AccessToken, ...entries: FeatureLogEntry[]): Promise { + alctx.enter(); + if (entries.length === 0) + throw new Error("At least one FeatureLogEntry must be passed to UlasClient.logFeatures."); + + const entriesJson: FeatureLogEntryJson[] = LogEntryConverter.toFeatureLogJson(entries); + return this.logEntry(alctx, token, entriesJson, true); + } + + private async logEntry(alctx: ActivityLoggingContext, token: AccessToken, entryJson: UsageLogEntryJson | FeatureLogEntryJson[], isFeatureEntry: boolean): Promise { + alctx.enter(); + let postUrl: string = (await this.getUrl(alctx)); + alctx.enter(); + if (isFeatureEntry) + postUrl += "/featureLog"; + + const authString: string = !token.getSamlAssertion() ? token.toTokenString() : "SAML " + token.toTokenString(IncludePrefix.No); + const options: RequestOptions = { + method: "POST", + headers: { authorization: authString }, + body: entryJson, + }; + + await this.setupOptionDefaults(options); + alctx.enter(); + if (Logger.isEnabled(loggingCategory, LogLevel.Trace)) + Logger.logTrace(loggingCategory, `Sending ${isFeatureEntry ? "Feature" : "Usage"} Log REST request...`, () => ({ url: postUrl, body: entryJson })); + + const resp: Response = await request(alctx, postUrl, options); + alctx.enter(); + const requestDetails = { url: postUrl, body: entryJson, response: resp }; + if (Logger.isEnabled(loggingCategory, LogLevel.Trace)) + Logger.logTrace(loggingCategory, `Sent ${isFeatureEntry ? "Feature" : "Usage"} Log REST request.`, () => requestDetails); + + const respBody: any = resp.body; + if (!respBody || !respBody.status || respBody.status.toLowerCase() !== "success") + throw new Error(`Post ${isFeatureEntry ? "Feature" : "Usage"} Log REST request failed ${!!respBody.msg ? ": " + respBody.msg : ""}. Details: ${JSON.stringify(requestDetails)}`); + + return { status: BentleyStatus.SUCCESS, message: !!respBody.msg ? respBody.msg : "", time: !!respBody.time ? respBody.time : -1, requestId: respBody.reqID }; + } +} diff --git a/core/common/CHANGELOG.json b/core/common/CHANGELOG.json index a4b7738..a66d816 100644 --- a/core/common/CHANGELOG.json +++ b/core/common/CHANGELOG.json @@ -1,6 +1,111 @@ { "name": "@bentley/imodeljs-common", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/imodeljs-common_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "allow to check if frontend is ios wkwebview" + }, + { + "comment": "allow subclasses of Range to use static methods" + }, + { + "comment": "Changes package.json to include api-extractor and adds api-extractor.json" + }, + { + "comment": "Update docs for BRepEntity.DataProps" + }, + { + "comment": "Use new buildIModelJsBuild script" + }, + { + "comment": "AxisAlignedBox and ElementAlignedBox are now typed to Range3d rather than classes" + }, + { + "comment": "clone methods are no longer generic" + }, + { + "comment": "Add release tags to indicate API stability" + }, + { + "comment": "Handle transforms on child tiles." + }, + { + "comment": "Optimize use of animation cutting planes." + }, + { + "comment": "Remove uneeded typedoc plugin depedency" + }, + { + "comment": "Add support for draco compressed meshes." + }, + { + "comment": "Consistent naming of \"get\" methods in Growable arrays." + }, + { + "comment": "Added spatial <-> cartographic methods that check/use the geographic coordinate system before using ecef location." + }, + { + "comment": "update for geometry GrowableXYArray usage" + }, + { + "comment": "Add material props classes" + }, + { + "comment": "Defer loading of edges until needed" + }, + { + "comment": "Save BUILD_SEMVER to globally accessible map" + }, + { + "comment": "Implemented spatial criterai when searching through all reality data associated to a project." + }, + { + "comment": "Optimize containment test with spheres." + }, + { + "comment": "Move the IModelUnitTestRpcInterface into the testbed and out of the public AP" + }, + { + "comment": "Renamed constructor variable in RpcConfiguration and RpcRequest" + }, + { + "comment": "Support for sending large RPC binary payloads in configurable chunks." + }, + { + "comment": "Remove loadNativeAsset and formatElements RPC calls from the IModelReadRpcInterface" + }, + { + "comment": "Removed IModelConnection.connectionId, added IModelApp.sessionId" + }, + { + "comment": "Tile requests can optionally specify a retryInterval." + }, + { + "comment": "upgrade to TypeScript 3.2.2" + } + ] + } + }, + { + "version": "0.188.0", + "tag": "@bentley/imodeljs-common_v0.188.0", + "date": "Wed, 16 Jan 2019 16:36:09 GMT", + "comments": { + "none": [ + { + "comment": "Changed Elements Db support for addon changes and generating the changed elements cache. Added WipRpcInterface methods to get changed elements list and check if a changeset is processed in the cache. Bumped WipRpcInterface version. Integration tests for changed elements db." + }, + { + "comment": "Fix error in semver parsing.\"" + } + ] + } + }, { "version": "0.187.0", "tag": "@bentley/imodeljs-common_v0.187.0", diff --git a/core/common/CHANGELOG.md b/core/common/CHANGELOG.md index 9448ff4..5fd8194 100644 --- a/core/common/CHANGELOG.md +++ b/core/common/CHANGELOG.md @@ -1,6 +1,47 @@ # Change Log - @bentley/imodeljs-common -This log was last generated on Tue, 15 Jan 2019 15:18:59 GMT and should not be manually modified. +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- allow to check if frontend is ios wkwebview +- allow subclasses of Range to use static methods +- Changes package.json to include api-extractor and adds api-extractor.json +- Update docs for BRepEntity.DataProps +- Use new buildIModelJsBuild script +- AxisAlignedBox and ElementAlignedBox are now typed to Range3d rather than classes +- clone methods are no longer generic +- Add release tags to indicate API stability +- Handle transforms on child tiles. +- Optimize use of animation cutting planes. +- Remove uneeded typedoc plugin depedency +- Add support for draco compressed meshes. +- Consistent naming of "get" methods in Growable arrays. +- Added spatial <-> cartographic methods that check/use the geographic coordinate system before using ecef location. +- update for geometry GrowableXYArray usage +- Add material props classes +- Defer loading of edges until needed +- Save BUILD_SEMVER to globally accessible map +- Implemented spatial criterai when searching through all reality data associated to a project. +- Optimize containment test with spheres. +- Move the IModelUnitTestRpcInterface into the testbed and out of the public AP +- Renamed constructor variable in RpcConfiguration and RpcRequest +- Support for sending large RPC binary payloads in configurable chunks. +- Remove loadNativeAsset and formatElements RPC calls from the IModelReadRpcInterface +- Removed IModelConnection.connectionId, added IModelApp.sessionId +- Tile requests can optionally specify a retryInterval. +- upgrade to TypeScript 3.2.2 + +## 0.188.0 +Wed, 16 Jan 2019 16:36:09 GMT + +### Updates + +- Changed Elements Db support for addon changes and generating the changed elements cache. Added WipRpcInterface methods to get changed elements list and check if a changeset is processed in the cache. Bumped WipRpcInterface version. Integration tests for changed elements db. +- Fix error in semver parsing." ## 0.187.0 Tue, 15 Jan 2019 15:18:59 GMT diff --git a/core/common/package.json b/core/common/package.json index 8dbb330..14cacec 100644 --- a/core/common/package.json +++ b/core/common/package.json @@ -1,22 +1,31 @@ { "name": "@bentley/imodeljs-common", - "version": "0.187.0", + "version": "0.189.0", "description": "iModel.js components common to frontend and backend", "license": "MIT", "main": "lib/imodeljs-common.js", "typings": "lib/imodeljs-common", "engines": { - "node": ">=8.9.0 <9.0" + "node": ">=10.14.0 <11.0" }, "scripts": { - "build": "tsc 1>&2 && npm run webpackModule-dev", + "build": "node ./node_modules/@bentley/webpack-tools/bin/buildIModelJsModule.js", "clean": "rimraf lib package-deps.json", "docs": "node ./node_modules/@bentley/build-tools/scripts/docs.js --source=./src --includes=../../generated-docs/extract --json=../../generated-docs/core/imodeljs-common/file.json --tsIndexFile=./imodeljs-common.ts --onlyJson %TYPEDOC_THEME%", + "extract-api": "node ./node_modules/@bentley/build-tools/scripts/extract-api.js --entry=imodeljs-common", "lint": "tslint --project . 1>&2", "test": "node ./node_modules/@bentley/build-tools/scripts/test.js", - "cover": "nyc npm test", - "webpackModule-dev": "make-dir ./lib/module/dev/ && webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/imodeljs-common.js --env.bundlename=imodeljs-common --json >./lib/module/dev/webpackStats.json", - "webpackModule-prod": "webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/imodeljs-common.js --env.bundlename=imodeljs-common --env.prod" + "cover": "nyc npm test" + }, + "iModelJs": { + "buildModule": { + "type": "system", + "webpack": { + "dest": "./lib/module", + "entry": "./lib/imodeljs-common.js", + "bundleName": "imodeljs-common" + } + } }, "repository": { "type": "git", @@ -43,39 +52,32 @@ "NOTE: peerDependencies are a standard way for npm to perform a module compatibility check" ], "peerDependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/geometry-core": "0.187.0", - "@bentley/imodeljs-clients": "0.187.0" + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/imodeljs-clients": "0.189.0" }, "//devDependencies": [ "NOTE: Must include modules mentioned in peerDependencies since those are not auto-installed", "NOTE: Must include modules used by the scripts section of package.json" ], "devDependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/build-tools": "0.187.0", - "@bentley/geometry-core": "0.187.0", - "@bentley/imodeljs-clients": "0.187.0", - "@bentley/webpack-tools": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/build-tools": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/imodeljs-clients": "0.189.0", + "@bentley/webpack-tools": "0.189.0", "@types/semver": "^5.5.0", "@types/url-search-params": "^0.10.2", - "cpx": "^1.5.0", - "make-dir-cli": "^1.0.0", "rimraf": "^2.6.2", - "source-map-loader": "^0.2.3", "tslint": "^5.11.0", "typedoc": "^0.11.1", - "typedoc-plugin-external-module-name": "^1.1.1", - "typescript": "~3.1.0", + "typescript": "~3.2.2", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", - "@types/node": "10.10.3", + "@types/node": "10.12.18", "chai": "^4.1.2", - "webpack": "^4.20.2", "mocha": "^5.2.0", - "ts-node": "^7.0.1", - "source-map-support": "^0.5.6", - "webpack-cli": "^3.1.0" + "ts-node": "^7.0.1" }, "//optionalDependencies": [ "NOTE: Rush (as of 4.2.5) does not support optionalDependencies!" diff --git a/core/common/src/ChangedElements.ts b/core/common/src/ChangedElements.ts new file mode 100644 index 0000000..08fdf91 --- /dev/null +++ b/core/common/src/ChangedElements.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { Id64String } from "@bentley/bentleyjs-core"; + +/** @beta */ +export interface ChangedElements { + elements: Id64String[]; + classIds: Id64String[]; + opcodes: number[]; +} diff --git a/core/common/src/Code.ts b/core/common/src/Code.ts index bc77e41..cc7b7d3 100644 --- a/core/common/src/Code.ts +++ b/core/common/src/Code.ts @@ -7,17 +7,23 @@ import { Id64, Id64String, GuidString, JsonUtils } from "@bentley/bentleyjs-core"; import { IModel } from "./IModel"; -/** The props that hold the identity of the object defining the uniqueness scope for a set of Code values. */ +/** The props that hold the identity of the object defining the uniqueness scope for a set of Code values. + * @public + */ export type CodeScopeProps = Id64String | GuidString; -/** The wire format for a Code */ +/** The wire format for a Code + * @public + */ export interface CodeProps { spec: Id64String; scope: CodeScopeProps; value?: string; } -/** A three-part structure containing information about the [Code]($docs/bis/intro/codes) of an Element */ +/** A three-part structure containing information about the [Code]($docs/bis/intro/codes) of an Element + * @public + */ export class Code implements CodeProps { /** The id of the [CodeSpec]($docs/bis/intro/codes.md#codespec) of the Element */ public spec: Id64String; @@ -39,10 +45,9 @@ export class Code implements CodeProps { public equals(other: Code): boolean { return this.spec === other.spec && this.scope === other.scope && this.value === other.value; } } -/** - * Names of the internal BIS CodeSpecs. These names match those specified by the native library. - * +/** Names of the internal BIS CodeSpecs. These names match those specified by the native library. * For other domains, the best practice is to include the domain name or alias as part of the CodeSpec name to ensure global uniqueness. + * @public */ export const enum BisCodeSpec { nullCodeSpec = "bis:NullCodeSpec", @@ -78,8 +83,13 @@ export const enum BisCodeSpec { viewDefinition = "bis:ViewDefinition", } -/** The scope of the Code. */ +/** The scope of the Code. + * @public + */ export namespace CodeScopeSpec { + /** The standard ways the CodeScope can be specified. + * @public + */ export const enum Type { /** The Code value must be unique within (at least) the iModel repository */ Repository = 1, @@ -91,7 +101,9 @@ export namespace CodeScopeSpec { RelatedElement = 4, } - /** Requirements for using a Code. */ + /** Requirements for how the CodeScope Element is identified. + * @public + */ export const enum ScopeRequirement { /** The Code is required to have a valid ElementId as its scope */ ElementId = 1, @@ -100,12 +112,12 @@ export namespace CodeScopeSpec { } } -/** - * A [Code Specification]($docs/bis/intro/glossary#codespec) captures the rules for encoding and decoding significant business information into +/** A [Code Specification]($docs/bis/intro/glossary#codespec) captures the rules for encoding and decoding significant business information into * and from a Code (string). This specification is used to generate and validate Codes. * * A CodeSpec defines the format of a Code for a certain type of Element in an IModel. * A CodeSpec can identify an external system that maintains and/or assigns Codes. + * @public */ export class CodeSpec { /** The iModel holding this CodeSpec. */ diff --git a/core/common/src/ColorDef.ts b/core/common/src/ColorDef.ts index c5429c7..3e7d906 100644 --- a/core/common/src/ColorDef.ts +++ b/core/common/src/ColorDef.ts @@ -2,14 +2,15 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -// spell-checker: disable - /** @module Symbology */ -/** - * A set of known colors by name, as a 32-bit integer in the form 0xBBGGRR (red is the low byte). +import { Geometry } from "@bentley/geometry-core"; + +// spell-checker: disable +/** A set of known colors by name, as a 32-bit integer in the form 0xBBGGRR (red is the low byte). * This is different than color values in #RRGGBB format for HTML pages (red and blue are swapped). - * If your colors don't look right, likely you're using 0xRRGGBB where ColorDef expects 0xBBGGRR. + * @note If your colors don't look right, likely you're using 0xRRGGBB where ColorDef expects 0xBBGGRR. + * @public */ export enum ColorByName { aliceBlue = 0xFFF8F0, @@ -164,6 +165,7 @@ export enum ColorByName { yellowGreen = 0x32CD9A, } +/** @public */ const enum HsvConstants { VISIBILITY_GOAL = 40, HSV_SATURATION_WEIGHT = 4, @@ -172,6 +174,7 @@ const enum HsvConstants { /** A color defined by Hue, Saturation, and Lightness. * @see [here](https://en.wikipedia.org/wiki/HSL_and_HSV) for difference between HSL and HSV + * @public */ export class HSLColor { /** Hue */ @@ -185,9 +188,9 @@ export class HSLColor { public static fromColorDef(val: ColorDef, out?: HSLColor) { return val.toHSL(out); } } -/** - * A color defined by Hue, Saturation, and Value +/** A color defined by Hue, Saturation, and Value * @see [here](https://en.wikipedia.org/wiki/HSL_and_HSV) for difference between HSL and HSV + * @public */ export class HSVColor { /** Hue */ @@ -229,11 +232,12 @@ export class HSVColor { const scratchBytes = new Uint8Array(4); const scratchUInt32 = new Uint32Array(scratchBytes.buffer); -/** A number in 0xTTBBGGRR format */ +/** A number in 0xTTBBGGRR format + * @public + */ export type ColorDefProps = number | ColorDef; -/** - * An integer representation of a color. +/** An integer representation of a color. * * Colors are stored as 4 components: Red, Blue, Green, and Transparency (0=fully opaque). Each is an 8-bit integer between 0-255. * @@ -246,6 +250,7 @@ export type ColorDefProps = number | ColorDef; * to convert to `0xRRGGBB` (see [[getRgb]]) and `0xAABBGGRR` (red in the low byte, 0==fully transparent in high byte. see [[getAbgr]]). * * The constructor also accepts strings in the common HTML formats. + * @public */ export class ColorDef { private _tbgr: number; @@ -437,12 +442,11 @@ export class ColorDef { }; const hue2rgb = (p1: number, q1: number, t: number) => Math.round(torgb(p1, q1, t) * 255); const modulo = (n: number, m: number) => ((n % m) + m) % m; - const clamp = (value: number, min: number, max: number) => Math.max(min, Math.min(max, value)); // h,s,l ranges are in 0.0 - 1.0 h = modulo(h, 1); - s = clamp(s, 0, 1); - l = clamp(l, 0, 1); + s = Geometry.clamp(s, 0, 1); + l = Geometry.clamp(l, 0, 1); if (s === 0) return ColorDef.from(l, l, l, 0, out); @@ -640,7 +644,9 @@ Object.freeze(ColorDef.red); Object.freeze(ColorDef.green); Object.freeze(ColorDef.blue); -/** An immutable representation of a color with red, green, and blue components each in the integer range [0, 255]. */ +/** An immutable representation of a color with red, green, and blue components each in the integer range [0, 255]. + * @public + */ export class RgbColor { /** Constructs from red, green, and blue components. * @param r Red diff --git a/core/common/src/ECSqlTypes.ts b/core/common/src/ECSqlTypes.ts index bd7a355..3ab18ed 100644 --- a/core/common/src/ECSqlTypes.ts +++ b/core/common/src/ECSqlTypes.ts @@ -7,8 +7,8 @@ import { assert, Id64String } from "@bentley/bentleyjs-core"; /** Describes the different data types an ECSQL value can be of. - * * See also [ECSQL]($docs/learning/ECSQL). + * @public */ export enum ECSqlValueType { // do not change the values of the enum as it must match its counterpart in the addon @@ -31,10 +31,9 @@ export enum ECSqlValueType { } /** An ECSQL Navigation value. - * * It is returned from ECSQL SELECT statements for navigation properties. - * * See also [ECSQL]($docs/learning/ECSQL). + * @public */ export interface NavigationValue { /** ECInstanceId of the related instance */ @@ -44,8 +43,8 @@ export interface NavigationValue { } /** An ECSQL Navigation value which can be bound to a navigation property ECSQL parameter - * * See also [ECSQL]($docs/learning/ECSQL). + * @public */ export interface NavigationBindingValue { /** ECInstanceId of the related instance */ @@ -60,12 +59,12 @@ export interface NavigationBindingValue { } /** Equivalent of the ECEnumeration OpCode in the **ECDbChange** ECSchema. - * * The enum can be used when programmatically binding values to the InstanceChange.OpCode property of * the ECDbChange ECSchema. * - * See also - * - [ChangeSummary Overview]($docs/learning/ChangeSummaries) + * See also + * - [ChangeSummary Overview]($docs/learning/ChangeSummaries) + * @public */ export enum ChangeOpCode { Insert = 1, @@ -73,14 +72,13 @@ export enum ChangeOpCode { Delete = 4, } -/** The enum represents the values for the ChangedValueState argument of the ECSQL function - * **Changes**. - * +/** The enum represents the values for the ChangedValueState argument of the ECSQL function **Changes**. * The enum can be used when programmatically binding values to the ChangedValueState argument * in an ECSQL using the **Changes** ECSQL function. * - * See also - * - [ChangeSummary Overview]($docs/learning/ChangeSummaries) + * See also + * - [ChangeSummary Overview]($docs/learning/ChangeSummaries) + * @public */ export enum ChangedValueState { AfterInsert = 1, @@ -90,8 +88,8 @@ export enum ChangedValueState { } /** Defines the ECSQL system properties. - * * See also [ECSQL]($docs/learning/ECSQL). + * @public */ export enum ECSqlSystemProperty { ECInstanceId, @@ -108,8 +106,8 @@ export enum ECSqlSystemProperty { } /** Utility to format ECProperty names according to the iModel.js formatting rules. - * - * See also [ECSQL Row Format]($docs/learning/ECSQLRowFormat). + * See also [ECSQL Row Format]($docs/learning/ECSQLRowFormat). + * @public */ export class ECJsNames { @@ -153,10 +151,7 @@ export class ECJsNames { if (propName === "TargetECClassId") return ECJsNames.systemPropertyToJsName(ECSqlSystemProperty.TargetECClassId); - if (propTypeUnknown) - return ECJsNames.lowerFirstChar(propName); - - throw new Error(`Property ${propName} is no ECSQL system property.`); + return ECJsNames.lowerFirstChar(propName); } return ECJsNames.lowerFirstChar(propName); diff --git a/core/common/src/ElementProps.ts b/core/common/src/ElementProps.ts index f202799..dfd87c0 100644 --- a/core/common/src/ElementProps.ts +++ b/core/common/src/ElementProps.ts @@ -12,7 +12,9 @@ import { IModelError, IModelStatus } from "./IModelError"; import { GeometryStreamProps } from "./geometry/GeometryStream"; import { Rank, SubCategoryAppearance } from "./SubCategoryAppearance"; -/** Properties of a NavigationProperty. */ +/** Properties of a NavigationProperty. + * @public + */ export interface RelatedElementProps { /** The Id of the element to which this element is related. */ id: Id64String; @@ -20,7 +22,9 @@ export interface RelatedElementProps { relClassName?: string; } -/** Properties of an [Element]($docs/bis/intro/element-fundamentals) */ +/** Properties of an [Element]($docs/bis/intro/element-fundamentals) + * @public + */ export interface ElementProps extends EntityProps { /** The Id of the [Model]($docs/bis/intro/model-fundamentals.md) containing this element */ model: Id64String; @@ -36,7 +40,9 @@ export interface ElementProps extends EntityProps { jsonProperties?: any; } -/** The Id and relationship class of an Element that is somehow related to another Element */ +/** The Id and relationship class of an Element that is somehow related to another Element + * @public + */ export class RelatedElement implements RelatedElementProps { /** The Id of the element to which this element is related. */ public readonly id: Id64String; @@ -57,75 +63,100 @@ export class RelatedElement implements RelatedElementProps { } } -/** A [RelatedElement]($common) relationship that describes the [TypeDefinitionElement]($backend) of an element. */ +/** A [RelatedElement]($common) relationship that describes the [TypeDefinitionElement]($backend) of an element. + * @public + */ export class TypeDefinition extends RelatedElement { } -/** Properties of a [GeometricElement]($backend) */ +/** Properties of a [GeometricElement]($backend) + * @public + */ export interface GeometricElementProps extends ElementProps { /** The id of the category for this geometric element. */ category: Id64String; geom?: GeometryStreamProps; } -/** Properties of a [[Placement3d]] */ +/** Properties of a [[Placement3d]] + * @public + */ export interface Placement3dProps { origin: XYZProps; angles: YawPitchRollProps; bbox?: LowAndHighXYZ; } -/** Properties of a [[Placement2d]] */ +/** Properties of a [[Placement2d]] + * @public + */ export interface Placement2dProps { origin: XYProps; angle: AngleProps; bbox?: LowAndHighXY; } +/** @public */ export type PlacementProps = Placement2dProps | Placement3dProps; -/** Properties that define a [GeometricElement3d]($backend) */ +/** Properties that define a [GeometricElement3d]($backend) + * @public + */ export interface GeometricElement3dProps extends GeometricElementProps { placement?: Placement3dProps; typeDefinition?: RelatedElementProps; } -/** Properties that define a [GeometricElement2d]($backend) */ +/** Properties that define a [GeometricElement2d]($backend) + * @public + */ export interface GeometricElement2dProps extends GeometricElementProps { placement?: Placement2dProps; typeDefinition?: RelatedElementProps; } -/** Properties of a [GeometryPart]($backend) */ +/** Properties of a [GeometryPart]($backend) + * @public + */ export interface GeometryPartProps extends ElementProps { geom?: GeometryStreamProps; bbox?: LowAndHighXYZ; } -/** Properties for a [ViewAttachment]($backend) */ +/** Properties for a [ViewAttachment]($backend) + * @public + */ export interface ViewAttachmentProps extends GeometricElement2dProps { view: RelatedElementProps; } -/** Properties of a [Subject]($backend) */ +/** Properties of a [Subject]($backend) + * @public + */ export interface SubjectProps extends ElementProps { description?: string; } -/** Properties of a [SheetBorderTemplate]($backend) */ +/** Properties of a [SheetBorderTemplate]($backend) + * @beta + */ export interface SheetBorderTemplateProps extends ElementProps { height?: number; width?: number; } -/** Properties of a [SheetTemplate]($backend) */ +/** Properties of a [SheetTemplate]($backend) + * @beta + */ export interface SheetTemplateProps extends ElementProps { height?: number; width?: number; border?: Id64String; } -/** Properties of a [Sheet]($backend) */ +/** Properties of a [Sheet]($backend) + * @beta + */ export interface SheetProps extends ElementProps { width?: number; height?: number; @@ -134,22 +165,30 @@ export interface SheetProps extends ElementProps { attachments?: Id64String[]; } -/** Properties of a [DefinitionElement]($backend) */ +/** Properties of a [DefinitionElement]($backend) + * @public + */ export interface DefinitionElementProps extends ElementProps { isPrivate?: boolean; } -/** Properties of a [TypeDefinitionElement]($backend) */ +/** Properties of a [TypeDefinitionElement]($backend) + * @public + */ export interface TypeDefinitionElementProps extends DefinitionElementProps { recipe?: RelatedElementProps; } -/** Properties of a [InformationPartitionElement]($backend) */ +/** Properties of a [InformationPartitionElement]($backend) + * @public + */ export interface InformationPartitionElementProps extends DefinitionElementProps { description?: string; } -/** Parameters to specify what element to load for [IModelDb.Elements.getElementProps]($backend). */ +/** Parameters to specify what element to load for [IModelDb.Elements.getElementProps]($backend). + * @public + */ export interface ElementLoadProps { id?: Id64String; code?: CodeProps; @@ -160,29 +199,39 @@ export interface ElementLoadProps { wantBRepData?: boolean; } -/** Properties of an [ElementAspect]($backend) */ +/** Properties of an [ElementAspect]($backend) + * @public + */ export interface ElementAspectProps extends EntityProps { element: RelatedElementProps; } -/** Properties of a [LineStyle]($backend) */ +/** Properties of a [LineStyle]($backend) + * @beta + */ export interface LineStyleProps extends ElementProps { description?: string; data: string; } -/** Properties of a [LightLocation]($backend) */ +/** Properties of a [LightLocation]($backend) + * @beta + */ export interface LightLocationProps extends GeometricElement3dProps { enabled?: boolean; } -/** Parameters of a [Category]($backend) */ +/** Parameters of a [Category]($backend) + * @public + */ export interface CategoryProps extends ElementProps { rank?: Rank; description?: string; } -/** Parameters of a [SubCategory]($backend) */ +/** Parameters of a [SubCategory]($backend) + * @public + */ export interface SubCategoryProps extends ElementProps { appearance?: SubCategoryAppearance.Props; description?: string; diff --git a/core/common/src/EntityProps.ts b/core/common/src/EntityProps.ts index cd3852a..4eb8b59 100644 --- a/core/common/src/EntityProps.ts +++ b/core/common/src/EntityProps.ts @@ -8,7 +8,9 @@ import { Id64String, Id64 } from "@bentley/bentleyjs-core"; import { Point2d, Point3d } from "@bentley/geometry-core"; import { RelatedElement } from "./ElementProps"; -/** The properties of an [Entity]($backend) as they are read/stored from/to the iModel. */ +/** The properties of an [Entity]($backend) as they are read/stored from/to the iModel. + * @public + */ export interface EntityProps { /** The full name of the [ECClass]($docs/bis/intro/glossary/#ecclass) for this entity, in the form "Schema:ClassName" */ classFullName: string; @@ -18,7 +20,9 @@ export interface EntityProps { [propName: string]: any; } -/** Parameters for performing an ECSQL SELECT query on [Entity]($backend) classes. */ +/** Parameters for performing an ECSQL SELECT query on [Entity]($backend) classes. + * @alpha Use ECSQL and IModelConnection.queryRows instead? + */ export interface EntityQueryParams { /** The sql className, in the form "Schema.ClassName", of the class to search. */ from?: string; @@ -34,7 +38,9 @@ export interface EntityQueryParams { offset?: number; } -/** The primitive types of an Entity property. */ +/** The primitive types of an Entity property. + * @beta + */ export const enum PrimitiveTypeCode { Uninitialized = 0x00, Binary = 0x101, @@ -48,10 +54,14 @@ export const enum PrimitiveTypeCode { String = 0x901, } -/** a callback function to process properties of an Entity */ +/** a callback function to process properties of an Entity + * @beta + */ export type PropertyCallback = (name: string, meta: PropertyMetaData) => void; -/** A custom attribute instance */ +/** A custom attribute instance + * @beta + */ export interface CustomAttribute { /** The class of the CustomAttribute */ ecclass: string; @@ -61,6 +71,7 @@ export interface CustomAttribute { type FactoryFunc = (jsonObj: any) => any; +/** @beta */ export interface PropertyMetaDataProps { primitiveType?: number; structName?: string; @@ -82,7 +93,9 @@ export interface PropertyMetaDataProps { customAttributes?: CustomAttribute[]; } -/** Metadata for a property. */ +/** Metadata for a property. + * @beta + */ export class PropertyMetaData implements PropertyMetaDataProps { public primitiveType?: PrimitiveTypeCode; public structName?: string; @@ -166,6 +179,8 @@ export class PropertyMetaData implements PropertyMetaDataProps { return jsonObj; } } + +/** @beta */ export interface EntityMetaDataProps { ecclass: string; description?: string; @@ -179,7 +194,9 @@ export interface EntityMetaDataProps { properties: { [propName: string]: PropertyMetaData }; } -/** Metadata for an Entity. */ +/** Metadata for an Entity. + * @beta + */ export class EntityMetaData implements EntityMetaDataProps { /** The Entity name */ public readonly ecclass: string; diff --git a/core/common/src/FeatureIndex.ts b/core/common/src/FeatureIndex.ts index b775c52..a406be9 100644 --- a/core/common/src/FeatureIndex.ts +++ b/core/common/src/FeatureIndex.ts @@ -12,9 +12,9 @@ export class NonUniformColor { public readonly indices: Uint16Array; public readonly isOpaque: boolean; - public constructor(colors: Uint32Array, indices: Uint16Array, hasAlpha: boolean) { + public constructor(colors: Uint32Array, indices: number[], hasAlpha: boolean) { this.colors = new Uint32Array(colors.buffer); - this.indices = new Uint16Array(indices.buffer); + this.indices = Uint16Array.from(indices); this.isOpaque = !hasAlpha; } } @@ -35,7 +35,7 @@ export class ColorIndex { public initUniform(color: ColorDef | number) { this._color = ("number" === typeof color) ? new ColorDef(color) : (color as ColorDef).clone(); } public get nonUniform(): NonUniformColor | undefined { return !this.isUniform ? this._color as NonUniformColor : undefined; } - public initNonUniform(colors: Uint32Array, indices: Uint16Array, hasAlpha: boolean) { + public initNonUniform(colors: Uint32Array, indices: number[], hasAlpha: boolean) { this._color = new NonUniformColor(colors, indices, hasAlpha); } } diff --git a/core/common/src/Fonts.ts b/core/common/src/Fonts.ts index 642f758..a1b654a 100644 --- a/core/common/src/Fonts.ts +++ b/core/common/src/Fonts.ts @@ -4,16 +4,24 @@ *--------------------------------------------------------------------------------------------*/ /** @module Symbology */ -/** The type of a font. */ +/** The type of a font. + * @public + */ export const enum FontType { TrueType = 1, Rsc = 2, Shx = 3 } -/** The properties of a Font. This includes a iModel local id, the font type, and the font name. */ + +/** The properties of a Font. This includes a iModel local id, the font type, and the font name. + * @public + */ export interface FontProps { id: number; type: FontType; name: string; } -/** The properties of a FontMap */ + +/** The properties of a FontMap + * @public + */ export interface FontMapProps { fonts: FontProps[]; } -/** - * A FontMap holds the table of known fonts available in an iModel. +/** A FontMap holds the table of known fonts available in an iModel. * A font is referenced by an "id" that is local to the iModel. This table maps those local ids to a FontProps. + * @public */ export class FontMap { public readonly fonts = new Map(); diff --git a/core/common/src/Frustum.ts b/core/common/src/Frustum.ts index e1058cc..3572767 100644 --- a/core/common/src/Frustum.ts +++ b/core/common/src/Frustum.ts @@ -6,7 +6,9 @@ import { Vector3d, Point3d, LowAndHighXYZ, LowAndHighXY, Range3d, Transform, Geometry, Map4d, ConvexClipPlaneSet, ClipPlane } from "@bentley/geometry-core"; -/** The 8 corners of the [Normalized Plane Coordinate]($docs/learning/glossary.md#npc) cube. */ +/** The 8 corners of the [Normalized Plane Coordinate]($docs/learning/glossary.md#npc) cube. + * @public + */ export const enum Npc { /** Left bottom rear */ _000 = 0, @@ -37,7 +39,9 @@ export const enum Npc { CORNER_COUNT = 8, } -/** The 8 corners of an [[Npc]] Frustum. */ +/** The 8 corners of an [[Npc]] Frustum. + * @public + */ // tslint:disable-next-line:variable-name export const NpcCorners = [ new Point3d(0.0, 0.0, 0.0), @@ -57,10 +61,9 @@ Object.freeze(NpcCorners); export const NpcCenter = new Point3d(.5, .5, .5); Object.freeze(NpcCenter); -/** - * The region of physical (3d) space that appears in a view. It forms the field-of-view of a camera. - * +/** The region of physical (3d) space that appears in a view. It forms the field-of-view of a camera. * It is stored as 8 points, in [[Npc]] order, that must define a truncated pyramid. + * @public */ export class Frustum { /** Array of the 8 points of this Frustum. */ diff --git a/core/common/src/GeoCoordinateServices.ts b/core/common/src/GeoCoordinateServices.ts index a1ab43e..d2bd57a 100644 --- a/core/common/src/GeoCoordinateServices.ts +++ b/core/common/src/GeoCoordinateServices.ts @@ -6,6 +6,7 @@ import { XYZProps } from "@bentley/geometry-core"; +/** @public */ export const enum GeoCoordStatus { Success = 0, NoGCSDefined = 100, @@ -17,37 +18,38 @@ export const enum GeoCoordStatus { Pending = -41556, } -/** - * Information required to request conversion of an array of Geographic coordinates (Longitude/Latitude) to iModel coordinates +/** Information required to request conversion of an array of Geographic coordinates (Longitude/Latitude) to iModel coordinates + * @beta */ export interface IModelCoordinatesRequestProps { sourceDatum: string; geoCoords: XYZProps[]; } -/** - * Information returned from a request to convert an array of Geographic coordinates (Longitude/Latitude) to iModel coordinates +/** Information returned from a request to convert an array of Geographic coordinates (Longitude/Latitude) to iModel coordinates + * @beta */ export interface PointWithStatus { p: XYZProps; s: GeoCoordStatus; } +/** @beta */ export interface IModelCoordinatesResponseProps { iModelCoords: PointWithStatus[]; fromCache: number; // the number that were read from the cache rather than calculated. } -/** - * Information required to request conversion of an array of iModel coordinates to Geographic Coordinates (longitude and latitude) +/** Information required to request conversion of an array of iModel coordinates to Geographic Coordinates (longitude and latitude) + * @beta */ export interface GeoCoordinatesRequestProps { targetDatum: string; iModelCoords: XYZProps[]; } -/** - * Information returned from a request to convert an array of iModel coordinates to Geographic Coordinates (longitude and latitude) +/** Information returned from a request to convert an array of iModel coordinates to Geographic Coordinates (longitude and latitude) + * @beta */ export interface GeoCoordinatesResponseProps { geoCoords: PointWithStatus[]; diff --git a/core/common/src/IModel.ts b/core/common/src/IModel.ts index 96ea5e4..1f29982 100644 --- a/core/common/src/IModel.ts +++ b/core/common/src/IModel.ts @@ -5,13 +5,15 @@ /** @module iModels */ import { GuidString, Id64, Id64String, IModelStatus, OpenMode } from "@bentley/bentleyjs-core"; -import { AxisOrder, Matrix3d, Point3d, Range3dProps, Transform, Vector3d, XYAndZ, XYZProps, YawPitchRollAngles, YawPitchRollProps } from "@bentley/geometry-core"; +import { AxisOrder, Matrix3d, Point3d, Range3dProps, Transform, Vector3d, XYAndZ, XYZProps, YawPitchRollAngles, YawPitchRollProps, Range3d } from "@bentley/geometry-core"; import { Cartographic } from "./geometry/Cartographic"; -import { AxisAlignedBox3d } from "./geometry/Primitives"; +import { AxisAlignedBox3d } from "./geometry/Placement"; import { IModelError } from "./IModelError"; import { ThumbnailProps } from "./Thumbnail"; -/** A token that identifies a specific instance of an iModel to be operated on */ +/** A token that identifies a specific instance of an iModel to be operated on + * @public + */ export class IModelToken { /** Constructor */ public constructor( @@ -29,7 +31,9 @@ export class IModelToken { } } -/** Properties that position an iModel on the earth via [ECEF](https://en.wikipedia.org/wiki/ECEF) (Earth Centered Earth Fixed) coordinates */ +/** Properties that position an iModel on the earth via [ECEF](https://en.wikipedia.org/wiki/ECEF) (Earth Centered Earth Fixed) coordinates + * @public + */ export interface EcefLocationProps { /** The Origin of an iModel on the earth in ECEF coordinates */ origin: XYZProps; @@ -37,7 +41,9 @@ export interface EcefLocationProps { orientation: YawPitchRollProps; } -/** The position and orientation of an iModel on the earth in [ECEF](https://en.wikipedia.org/wiki/ECEF) (Earth Centered Earth Fixed) coordinates */ +/** The position and orientation of an iModel on the earth in [ECEF](https://en.wikipedia.org/wiki/ECEF) (Earth Centered Earth Fixed) coordinates + * @public + */ export class EcefLocation implements EcefLocationProps { /** The origin of the ECEF transform. */ public readonly origin: Point3d; @@ -63,7 +69,9 @@ export class EcefLocation implements EcefLocationProps { } } -/** Properties of the [Root Subject]($docs/bis/intro/glossary#subject-root). */ +/** Properties of the [Root Subject]($docs/bis/intro/glossary#subject-root). + * @alpha Why is this not just SubjectProps? + */ export interface RootSubjectProps { /** The name of the root subject. */ name: string; @@ -71,7 +79,9 @@ export interface RootSubjectProps { description?: string; } -/** Properties that are about an iModel. */ +/** Properties that are about an iModel. + * @public + */ export interface IModelProps { /** The name and description of the root subject of this iModel */ rootSubject: RootSubjectProps; @@ -83,7 +93,9 @@ export interface IModelProps { ecefLocation?: EcefLocationProps; } -/** The properties that can be supplied when creating a *new* iModel. */ +/** The properties that can be supplied when creating a *new* iModel. + * @public + */ export interface CreateIModelProps extends IModelProps { /** The GUID of new iModel. If not present, a GUID will be generated. */ guid?: GuidString; @@ -93,6 +105,7 @@ export interface CreateIModelProps extends IModelProps { thumbnail?: ThumbnailProps; } +/** @public */ export interface FilePropertyProps { namespace: string; name: string; @@ -100,7 +113,9 @@ export interface FilePropertyProps { subId?: number | string; } -/** Represents an iModel in JavaScript. */ +/** Represents an iModel in JavaScript. + * @public + */ export abstract class IModel implements IModelProps { /** The Id of the repository model. */ public static readonly repositoryModelId: Id64String = "0x1"; @@ -166,7 +181,7 @@ export abstract class IModel implements IModelProps { protected initialize(name: string, props: IModelProps) { this.name = name; this.rootSubject = props.rootSubject; - this.projectExtents = AxisAlignedBox3d.fromJSON(props.projectExtents); + this.projectExtents = Range3d.fromJSON(props.projectExtents); this._globalOrigin = Point3d.fromJSON(props.globalOrigin); this._globalOrigin.freeze(); // cannot be modified if (props.ecefLocation) @@ -216,13 +231,13 @@ export abstract class IModel implements IModelProps { public ecefToSpatial(ecef: XYAndZ, result?: Point3d): Point3d { return this.getEcefTransform().multiplyInversePoint3d(ecef, result)!; } /** - * Convert a point in this iModel's Spatial coordinates to a [[Cartographic]] using its [[IModel.ecefLocation]]. + * Convert a point in this iModel's Spatial coordinates to a [[Cartographic]] using its [[IModel.ecefLocation]]. * @param spatial A point in the iModel's spatial coordinates * @param result If defined, use this for output * @returns A Cartographic location * @throws IModelError if [[isGeoLocated]] is false. */ - public spatialToCartographic(spatial: XYAndZ, result?: Cartographic): Cartographic { return Cartographic.fromEcef(this.spatialToEcef(spatial), result)!; } + public spatialToCartographicFromEcef(spatial: XYAndZ, result?: Cartographic): Cartographic { return Cartographic.fromEcef(this.spatialToEcef(spatial), result)!; } /** * Convert a [[Cartographic]] to a point in this iModel's Spatial coordinates using its [[IModel.ecefLocation]]. @@ -232,5 +247,5 @@ export abstract class IModel implements IModelProps { * @throws IModelError if [[isGeoLocated]] is false. * @note The resultant point will only be meaningful if the ECEF coordinate is close on the earth to the iModel. */ - public cartographicToSpatial(cartographic: Cartographic, result?: Point3d) { return this.ecefToSpatial(cartographic.toEcef(result), result); } + public cartographicToSpatialFromEcef(cartographic: Cartographic, result?: Point3d) { return this.ecefToSpatial(cartographic.toEcef(result), result); } } diff --git a/core/common/src/IModelError.ts b/core/common/src/IModelError.ts index f62cd56..5e45f0c 100644 --- a/core/common/src/IModelError.ts +++ b/core/common/src/IModelError.ts @@ -7,16 +7,26 @@ import { BentleyStatus, BentleyError, IModelStatus, BriefcaseStatus, GetMetaDataFunction, LogFunction, DbResult, AuthStatus, RepositoryStatus, ChangeSetStatus, RpcInterfaceStatus } from "@bentley/bentleyjs-core"; export { BentleyStatus, BentleyError, IModelStatus, BriefcaseStatus, GetMetaDataFunction, LogFunction, DbResult, AuthStatus, RepositoryStatus, ChangeSetStatus, RpcInterfaceStatus } from "@bentley/bentleyjs-core"; -/** The error type thrown by this module. See [[IModelStatus]] for `errorNumber` values. */ +/** The error type thrown by this module. See [[IModelStatus]] for `errorNumber` values. + * @public + */ export class IModelError extends BentleyError { public constructor(errorNumber: number | IModelStatus | DbResult | BentleyStatus | BriefcaseStatus | RepositoryStatus | ChangeSetStatus | RpcInterfaceStatus | AuthStatus, message: string, log?: LogFunction, category?: string, getMetaData?: GetMetaDataFunction) { super(errorNumber, message, log, category, getMetaData); } } +/** @public */ export class ServerError extends IModelError { public constructor(errorNumber: number, message: string, log?: LogFunction) { super(errorNumber, message, log); this.name = "Server error (" + errorNumber + ")"; } } + +export class ServerTimeoutError extends ServerError { + public constructor(errorNumber: number, message: string, log?: LogFunction) { + super(errorNumber, message, log); + this.name = "Server timeout error (" + errorNumber + ")"; + } +} diff --git a/core/common/src/IModelVersion.ts b/core/common/src/IModelVersion.ts index ec438d0..6e5f603 100644 --- a/core/common/src/IModelVersion.ts +++ b/core/common/src/IModelVersion.ts @@ -8,7 +8,9 @@ import { AccessToken, IModelClient, ChangeSet, ChangeSetQuery, VersionQuery } fr import { IModelError } from "./IModelError"; import { BentleyStatus, ActivityLoggingContext, GuidString } from "@bentley/bentleyjs-core"; -/** Option to specify the version of the iModel to be acquired and used */ +/** Option to specify the version of the iModel to be acquired and used + * @public + */ export class IModelVersion { private _first?: boolean; private _latest?: boolean; @@ -124,5 +126,4 @@ export class IModelVersion { return versions[0].changeSetId!; } - } diff --git a/core/common/src/Image.ts b/core/common/src/Image.ts index a87b2b7..f6140d5 100644 --- a/core/common/src/Image.ts +++ b/core/common/src/Image.ts @@ -7,6 +7,7 @@ /** Format of an [[ImageBuffer]]. * The format determines how many bytes are allocated for each pixel in the buffer and the semantics of each byte. * @see [[ImageBuffer.getNumBytesPerPixel]] + * @public */ export const enum ImageBufferFormat { /** RGBA format - 4 bytes per pixel. */ @@ -17,7 +18,9 @@ export const enum ImageBufferFormat { Alpha = 5, } -/** Uncompressed bitmap image data */ +/** Uncompressed bitmap image data + * @public + */ export class ImageBuffer { /** Image data in which each pixel occupies 1 or more bytes depending of the [[ImageBufferFormat]]. */ public readonly data: Uint8Array; @@ -73,11 +76,13 @@ export class ImageBuffer { /** Returns whether the input is a power of two. * @note Floating point inputs are truncated. + * @public */ export function isPowerOfTwo(num: number): boolean { return 0 === (num & (num - 1)); } /** Returns the first power-of-two value greater than or equal to the input. * @note Floating point inputs are truncated. + * @public */ export function nextHighestPowerOfTwo(num: number): number { --num; @@ -87,7 +92,9 @@ export function nextHighestPowerOfTwo(num: number): number { return num + 1; } -/** The format of an ImageSource. */ +/** The format of an ImageSource. + * @public + */ export const enum ImageSourceFormat { /** Image data is stored with JPEG compression. */ Jpeg = 0, @@ -106,7 +113,9 @@ export function isValidImageSourceFormat(format: ImageSourceFormat): boolean { } } -/** Image data encoded and compressed in either Jpeg or Png format. */ +/** Image data encoded and compressed in either Jpeg or Png format. + * @public + */ export class ImageSource { /** The content of the image, compressed */ public readonly data: Uint8Array; diff --git a/core/common/src/MaterialProps.ts b/core/common/src/MaterialProps.ts new file mode 100644 index 0000000..c812f78 --- /dev/null +++ b/core/common/src/MaterialProps.ts @@ -0,0 +1,101 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module WireFormats */ + +import { DefinitionElementProps } from "./ElementProps"; +import { TextureMapping } from "./Render"; +import { Id64String } from "@bentley/bentleyjs-core"; + +/** Contains three array entries ordered as red, green, blue containing values 0 to 1 + * @beta Is an additional type over those in ColorDef needed? + */ +export type RgbFactorProps = number[]; + +/** Contains two array entries orders X, Y containing doubles + * @beta Is an additional type over those in geometry-core needed? + */ +export type DPoint2dProps = number[]; + +/** @beta */ +export enum TextureMapUnits { Relative = 0, Meters = 3, Millimeters = 4, Feet = 5, Inches = 6 } + +/** Properties that define how a texture is mapped to a material + * @beta + */ +export interface TextureMapProps { + /** Angle in degrees to rotate texture when applying; defaults to 0.0 if undefined */ + pattern_angle?: number; + /** If true, flip the pattern map in U; if undefined, defaults to false */ + pattern_u_flip?: boolean; + /** If true, flip the pattern map in V; if undefined, defaults to false */ + pattern_flip?: boolean; + /** X, Y scale to apply to the pattern map; if undefined, defaults to {0,0}, which is almost never useful. */ + pattern_scale?: DPoint2dProps; + /** X, Y offset to apply to the pattern map; if undefined, defaults to {0,0} */ + pattern_offset?: DPoint2dProps; + /** Units to use when applying the scaling; if undefined, defaults to TextureMapUnits.Relative */ + pattern_scalemode?: TextureMapUnits; + /** Mapping mode to use for the texture application; if undefined, defaults to TextureMapping.Mode.Parametric */ + pattern_mapping?: TextureMapping.Mode; + /** Weight at which to combine diffuse image and color; if undefined, defaults to 1.0 */ + pattern_weight?: number; + /** The Id of the persistent Texture element defining the texture image. */ + TextureId: Id64String; +} + +/** Properties that define a RenderMaterial + * @beta + */ +export interface RenderMaterialProps extends DefinitionElementProps { + /** The palette name which categorizes this RenderMaterial */ + paletteName: string; + /** The optional description for this RenderMaterial */ + description?: string; + jsonProperties?: { + materialAssets?: { + renderMaterial?: { + /** If true, this material has a fill/diffuse color; if undefined, defaults to false */ + HasBaseColor?: boolean; + /** Surface color used for fill or diffuse illumination; if undefined, defaults to black */ + color?: RgbFactorProps; + /** If true, this material has a specular color; if undefined, defaults to false */ + HasSpecularColor?: boolean; + /** Surface color used for specular illumination; if undefined, defaults to black */ + specular_color?: RgbFactorProps; + /** If true, this material has a specular exponent; if undefined, defaults to false */ + HasFinish?: boolean; + /** Specular exponent (surface shininess); range is 0 to 128; if undefined, defaults to 15.0 * 0.9 */ + finish?: number; + /** If true, this material has surface transparency; if undefined, defaults to false */ + HasTransmit?: boolean; + /** Surface transparency; if undefined, defaults to 0.0 */ + transmit?: number; + /** If true, this material has a value for diffuse reflectivity; if undefined, defaults to false */ + HasDiffuse?: boolean; + /** Surface diffuse reflectivity; if undefined, defaults to 0.6 */ + diffuse?: number; + /** If true, this material has a value for specular reflectivity; if undefined, defaults to false. If false, specular value is actually set to 0.0 */ + HasSpecular?: boolean; + /** Surface specular reflectivity; if undefined, defaults to 0.4 */ + specular?: number; + /** If true, this material has a value for environmental reflectivity; if undefined, defaults to false */ + HasReflect?: boolean; + /** Surface environmental reflectivity; stored as fraction of specular in V8 material settings; if undefined defaults to 0.0 */ + reflect?: number; + /** If true, this material has a surface reflectance color; if undefined, defaults to false. If false, reflectance color is actually set to specular color */ + HasReflectColor?: boolean; + /** Surface reflectance color; if undefined, defaults to black */ + reflect_color?: RgbFactorProps; + /** An optional set of texture maps associated with this material. + * A large variety of map types may be present (e.g., bump maps, specular maps, fur, etc), but currently only the pattern map is used. + */ + Map?: { + /** Optional pattern map. */ + Pattern?: TextureMapProps; + } + }; + }; + }; +} diff --git a/core/common/src/ModelProps.ts b/core/common/src/ModelProps.ts index f3f1950..1a7e3e5 100644 --- a/core/common/src/ModelProps.ts +++ b/core/common/src/ModelProps.ts @@ -9,7 +9,9 @@ import { XYProps } from "@bentley/geometry-core"; import { Id64String } from "@bentley/bentleyjs-core"; import { RelatedElementProps } from "./ElementProps"; -/** Properties that define a [Model]($docs/bis/intro/model-fundamentals) */ +/** Properties that define a [Model]($docs/bis/intro/model-fundamentals) + * @public + */ export interface ModelProps extends EntityProps { modeledElement: RelatedElementProps; name?: string; @@ -19,13 +21,17 @@ export interface ModelProps extends EntityProps { jsonProperties?: any; } -/** Interface for querying a set of [Model]($backend)s. */ +/** Interface for querying a set of [Model]($backend)s. + * @alpha Use ECSQL and IModelConnection.queryRows instead? + */ export interface ModelQueryParams extends EntityQueryParams { wantTemplate?: boolean; wantPrivate?: boolean; } -/** Properties that define a [GeometricModel2d]($backend) */ +/** Properties that define a [GeometricModel2d]($backend) + * @public + */ export interface GeometricModel2dProps extends ModelProps { globalOrigin?: XYProps; } diff --git a/core/common/src/Paging.ts b/core/common/src/Paging.ts new file mode 100644 index 0000000..f1f8d34 --- /dev/null +++ b/core/common/src/Paging.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module iModels */ + +/** Provide paging option for the queries. */ +export interface PageOptions { + /** Zero base page number */ + start?: number; + /** Number of rows per page */ + size?: number; + /** In case some error occure number of retries */ + retries?: number; +} +/** Default option used when caller does not provide one */ +export const kPagingDefaultOptions: PageOptions = { start: 0, size: 512 }; +export interface PagableECSql { + /** Compute number of rows that would be returned by the ECSQL. */ + queryRowCount(ecsql: string, bindings?: any[] | object): Promise; + + /** Execute a query agaisnt this ECDb */ + queryPage(ecsql: string, bindings?: any[] | object, options?: PageOptions): Promise; + + /** Execute a pagable query. */ + query(ecsql: string, bindings?: any[] | object, options?: PageOptions): AsyncIterableIterator; +} diff --git a/core/common/src/QPoint.ts b/core/common/src/QPoint.ts index 11bceb3..06fa8dd 100644 --- a/core/common/src/QPoint.ts +++ b/core/common/src/QPoint.ts @@ -193,6 +193,19 @@ export class QPoint2dList { return array; } + /** Construct a QPoint2dList containing all points in the supplied list, quantized to the range of those points. */ + public static fromPoints(points: Point2d[], out?: QPoint2dList) { + let qPoints; + const qParams = QParams2d.fromRange(Range2d.createArray(points)); + if (out) { + qPoints = out; + qPoints.reset(qParams); + } else qPoints = new QPoint2dList(qParams); + for (const point of points) + qPoints.add(point); + + return qPoints; + } } /** Parameters used for quantization of 3d points. @@ -356,12 +369,17 @@ export class QPoint3dList { } /** Construct a QPoint3dList containing all points in the supplied list, quantized to the range of those points. */ - public static fromPoints(points: Point3d[]) { - const qpoints = new QPoint3dList(QParams3d.fromRange(Range3d.createArray(points))); + public static fromPoints(points: Point3d[], out?: QPoint3dList) { + let qPoints; + const qParams = QParams3d.fromRange(Range3d.createArray(points)); + if (out) { + qPoints = out; + qPoints.reset(qParams); + } else qPoints = new QPoint3dList(qParams); for (const point of points) - qpoints.add(point); + qPoints.add(point); - return qpoints; + return qPoints; } /** Clears out the contents of the list */ diff --git a/core/common/src/Render.ts b/core/common/src/Render.ts index 806b519..23312cc 100644 --- a/core/common/src/Render.ts +++ b/core/common/src/Render.ts @@ -16,7 +16,9 @@ import { AreaPattern } from "./geometry/AreaPattern"; import { Frustum } from "./Frustum"; import { ImageBuffer, ImageBufferFormat } from "./Image"; -/** Flags indicating whether and how the interiors of closed planar regions is displayed within a view. */ +/** Flags indicating whether and how the interiors of closed planar regions is displayed within a view. + * @public + */ export enum FillFlags { /** No fill */ None = 0, @@ -211,6 +213,7 @@ export class PolylineEdgeArgs { /** Represents a texture image applied to a surface during rendering. * A RenderTexture is typically - but not always - associated with a [[RenderMaterial]]. * @see [[RenderSystem]] for functions used to create RenderTextures. + * @beta */ export abstract class RenderTexture implements IDisposable { /** A string uniquely identifying this texture within the context of an [[IModelConnection]]. Typically this is the element Id of the corresponding Texture element in the [[IModelDb]]. @@ -244,6 +247,7 @@ export abstract class RenderTexture implements IDisposable { /** Represents a texture image applied to a surface during rendering. * A RenderTexture is typically - but not always - associated with a [[RenderMaterial]]. * @see [[RenderSystem]] for functions used to create RenderTextures. + * @beta */ export namespace RenderTexture { /** Enumerates the types of [[RenderTexture]]s. */ @@ -286,7 +290,9 @@ export namespace RenderTexture { } } -/** Represents a material which can be applied to a surface to control aspects of its appearance such as color, reflectivity, texture, and so on. */ +/** Represents a material which can be applied to a surface to control aspects of its appearance such as color, reflectivity, texture, and so on. + * @beta + */ export abstract class RenderMaterial { /** If the material originated from a Material element in the [[IModelDb]], the Id of that element. */ public readonly key?: string; @@ -301,7 +307,9 @@ export abstract class RenderMaterial { public get hasTexture(): boolean { return this.textureMapping !== undefined && this.textureMapping.texture !== undefined; } } -/** Represents a material which can be applied to a surface to control aspects of its appearance such as color, reflectivity, and so on. */ +/** Represents a material which can be applied to a surface to control aspects of its appearance such as color, reflectivity, and so on. + * @beta + */ export namespace RenderMaterial { /** Parameters used to construct a [[RenderMaterial]] */ export class Params { @@ -350,9 +358,9 @@ export namespace ImageLight { } } -/** - * The "cooked" material and symbology for a [[RenderGraphic]]. This determines the appearance +/** The "cooked" material and symbology for a [[RenderGraphic]]. This determines the appearance * (e.g. texture, color, width, linestyle, etc.) used to draw Geometry. + * @beta */ export class GraphicParams { public fillFlags = FillFlags.None; @@ -401,8 +409,7 @@ export class GraphicParams { export const enum AntiAliasPref { Detect = 0, On = 1, Off = 2 } -/** - * Enumerates the available rendering modes. The rendering mode chiefly controls whether and how surfaces and their edges are drawn. +/** Enumerates the available rendering modes. The rendering mode chiefly controls whether and how surfaces and their edges are drawn. * Generally speaking, * - Wireframe draws only edges. * - SmoothShade draws only surfaces. @@ -412,6 +419,7 @@ export const enum AntiAliasPref { Detect = 0, On = 1, Off = 2 } * The [[FillFlags]] associated with planar regions controls whether and how the region's interior area is displayed in Wireframe mode. * [[ViewFlags]] has options for enabling display of visible and/or hidden edges in SmoothShade mode. * [[HiddenLine.Settings]] allow aspects of edge and surface symbology to be overridden within a view. + * @public */ export const enum RenderMode { /** Render only edges, no surfaces, with exceptions for planar regions with [[FillFlags]] set up to render the surface in wireframe mode. */ @@ -424,8 +432,8 @@ export const enum RenderMode { SolidFill = 4, } -/** - * The current position (eyepoint), lens angle, and focus distance of a camera. +/** The current position (eyepoint), lens angle, and focus distance of a camera. + * @public */ export class Camera implements CameraProps { public readonly lens: Angle; @@ -469,7 +477,9 @@ export class Camera implements CameraProps { } } -/** Flags for controlling how graphics appear within a View. */ +/** Flags for controlling how graphics appear within a View. + * @public + */ export class ViewFlags { /** The [[RenderMode]] of the view. */ public renderMode: RenderMode = RenderMode.Wireframe; @@ -569,6 +579,17 @@ export class ViewFlags { } return true; } + /** @hidden */ + public edgesRequired(): boolean { + switch (this.renderMode) { + case RenderMode.SolidFill: + case RenderMode.HiddenLine: + case RenderMode.Wireframe: + return true; + case RenderMode.SmoothShade: + return this.visibleEdges; + } + } public toJSON(): ViewFlagProps { const out: ViewFlagProps = {}; @@ -702,8 +723,7 @@ export namespace ViewFlag { kBackgroundMap, } - /** - * Overrides a subset of ViewFlags. + /** Overrides a subset of ViewFlags. * @hidden */ export class Overrides { @@ -809,6 +829,21 @@ export const enum LinePixels { Invalid = -1, } +/** Represents a bounding sphere. Optional optimization for FrustumPlane containment test. + * @hidden + */ +export class BoundingSphere { + public center: Point3d; + public radius: number; + constructor(center?: Point3d, radius?: number) { this.center = center ? center : Point3d.createZero(); this.radius = undefined === radius ? 0.0 : radius; } + public init(center: Point3d, radius: number) { this.center = center; this.radius = radius; } + public transformBy(transform: Transform, result: BoundingSphere) { + transform.multiplyPoint3d(this.center, result.center); + result.radius = this.radius * Math.max(transform.matrix.columnXMagnitude(), Math.max(transform.matrix.columnYMagnitude(), (transform.matrix.columnZMagnitude()))); + return result; + } +} + /** Represents a frustum as 6 planes and provides containment and intersection testing * @hidden */ @@ -838,11 +873,11 @@ export class FrustumPlanes { FrustumPlanes.addPlaneFromPoints(this._planes, frustum.points, 4, 5, 6); // front } - public computeFrustumContainment(box: Frustum): FrustumPlanes.Containment { return this.computeContainment(box.points); } + public computeFrustumContainment(box: Frustum, sphere?: BoundingSphere): FrustumPlanes.Containment { return this.computeContainment(box.points, sphere); } public intersectsFrustum(box: Frustum): boolean { return FrustumPlanes.Containment.Outside !== this.computeFrustumContainment(box); } - public containsPoint(point: Point3d, tolerance: number = 1.0e-8): boolean { return FrustumPlanes.Containment.Outside !== this.computeContainment([point], tolerance); } + public containsPoint(point: Point3d, tolerance: number = 1.0e-8): boolean { return FrustumPlanes.Containment.Outside !== this.computeContainment([point], undefined, tolerance); } - public computeContainment(points: Point3d[], tolerance: number = 1.0e-8): FrustumPlanes.Containment { + public computeContainment(points: Point3d[], sphere?: BoundingSphere, tolerance: number = 1.0e-8): FrustumPlanes.Containment { assert(this.isValid); if (undefined === this._planes) { return FrustumPlanes.Containment.Outside; @@ -850,6 +885,14 @@ export class FrustumPlanes { let allInside = true; for (const plane of this._planes) { + if (sphere) { // if sphere provide detect total inside and outside without using corners. + const centerDistance = plane.evaluatePoint(sphere.center); + const tolerancePlusRadius = tolerance + sphere.radius; + if (centerDistance < -tolerancePlusRadius) + return FrustumPlanes.Containment.Outside; + if (centerDistance > tolerancePlusRadius) + continue; + } let nOutside = 0; for (const point of points) { if (plane.evaluatePoint(point) + tolerance < 0.0) { @@ -917,7 +960,9 @@ export namespace FrustumPlanes { } } -/** Namespace containing types controlling how ambient occlusion should be drawn. */ +/** Namespace containing types controlling how ambient occlusion should be drawn. + * @beta + */ export namespace AmbientOcclusion { /** Describes the properties with which ambient occlusion should be drawn. These properties correspond to a horizon-based ambient occlusion approach. */ export interface Props { @@ -986,7 +1031,9 @@ export namespace AmbientOcclusion { } } -/** Namespace containing types controlling how edges and surfaces should be drawn in "hidden line" and "solid fill" [[RenderMode]]s. */ +/** Namespace containing types controlling how edges and surfaces should be drawn in "hidden line" and "solid fill" [[RenderMode]]s. + * @beta + */ export namespace HiddenLine { /** Describes the symbology with which edges should be drawn. */ export interface StyleProps { @@ -1147,6 +1194,7 @@ export namespace HiddenLine { } } +/** @beta */ export namespace Gradient { /** Flags applied to a [[Gradient.Symb]]. */ export const enum Flags { @@ -1620,7 +1668,9 @@ export namespace Gradient { } } -/** Whether a closed region should be drawn for wireframe display with its internal area filled or not. */ +/** Whether a closed region should be drawn for wireframe display with its internal area filled or not. + * @public + */ export const enum FillDisplay { /** don't fill, even if fill attribute is on for the viewport */ Never = 0, @@ -1632,7 +1682,9 @@ export const enum FillDisplay { Blanking = 3, } -/** Describes how a view's background color affects the interior area of a closed region. */ +/** Describes how a view's background color affects the interior area of a closed region. + * @public + */ export const enum BackgroundFill { /** single color fill uses the fill color and line color to draw either a solid or outline fill */ None = 0, @@ -1646,6 +1698,7 @@ export const enum BackgroundFill { * within a view using [[ViewFlags]]. * @see [[GeometryStreamProps]]. * @see [[Feature]]. + * @alpha Confusion with ECClass? */ export const enum GeometryClass { /** Used to classify the "real" geometry within a model. Most geometry falls within this class. */ @@ -1670,6 +1723,7 @@ export class SceneLights { /** Describes the display properties of graphics in a persistent element's GeometryStream that aren't inherited from [[SubCategoryAppearance]]. * @see [[GeometryStreamProps]]. + * @public */ export class GeometryParams { /** Optional render material to override [[SubCategoryAppearance.materialId]]. @@ -1853,7 +1907,9 @@ export class GeometryParams { } } -/** Contains types related to display of hilited elements within a [[Viewport]]. */ +/** Contains types related to display of hilited elements within a [[Viewport]]. + * @public + */ export namespace Hilite { /** Describes the width of the outline applied to hilited geometry. The outline is drawn around the union of all hilited geometry and is visible behind non-hilited geometry. * @see [[Hilite.Settings]] @@ -1902,8 +1958,7 @@ export namespace Hilite { } } -/** - * Describes a "feature" within a batched [[RenderGraphic]]. A batched [[RenderGraphic]] can +/** Describes a "feature" within a batched [[RenderGraphic]]. A batched [[RenderGraphic]] can * contain multiple features. Each feature is associated with a unique combination of * attributes (elementId, subcategory, geometry class). This allows geometry to be * more efficiently batched on the GPU, while enabling features to be re-symbolized @@ -1916,6 +1971,7 @@ export namespace Hilite { * FeatureTable associated with the primitive. * * @see [[FeatureSymbology]] for mechanisms for controlling or overriding the symbology of individual features within a [[ViewState]]. + * @beta Name? */ export class Feature { public readonly elementId: string; @@ -1948,10 +2004,10 @@ export class Feature { } } -/** - * Describes the type of a 'batch' of graphics representing multiple [[Feature]]s. +/** Describes the type of a 'batch' of graphics representing multiple [[Feature]]s. * The most commonly-encountered batches are Tiles, which can be of either Primary or * Classifier type. + * @beta */ export const enum BatchType { /** This batch contains graphics derived from a model's visible geometry. */ @@ -1964,13 +2020,13 @@ export const enum BatchType { Classifier, } -/** - * Defines a look-up table for [[Feature]]s within a batched [[RenderGraphic]]. Consecutive 32-bit +/** Defines a look-up table for [[Feature]]s within a batched [[RenderGraphic]]. Consecutive 32-bit * indices are assigned to each unique Feature. Primitives within the [[RenderGraphic]] can * use per-vertex indices to specify the distribution of Features within the primitive.V * A FeatureTable can be shared amongst multiple primitives within a single [[RenderGraphic]], and * amongst multiple sub-Graphics of a [[RenderGraphic]]. * @see [[FeatureSymbology]] for mechanisms for resymbolizing features within a [[ViewState]]. + * @beta */ export class FeatureTable extends IndexMap { public readonly modelId: Id64String; @@ -2018,6 +2074,7 @@ export class FeatureTable extends IndexMap { /** Describes how to map a [[RenderTexture]] image onto a surface. * @see [[RenderMaterial]]. + * @beta */ export class TextureMapping { /** The texture to be mapped to the surface. */ @@ -2036,6 +2093,7 @@ export class TextureMapping { } } +/** @beta */ export namespace TextureMapping { /** Enumerates the possible texture mapping modes. */ export const enum Mode { @@ -2155,7 +2213,7 @@ export namespace TextureMapping { if (isRelativeUnits || !visitor.tryGetDistanceParameter(i, param)) { if (!visitor.tryGetNormalizedParameter(i, param)) { // If mesh does not have facetFaceData, we still want to use the texture coordinates if they are present - param = visitor.getParam(i); + param = visitor.getParam(i)!; } } @@ -2171,9 +2229,9 @@ export namespace TextureMapping { let normal: Vector3d; if (visitor.normal === undefined) - normal = points.getPoint3dAt(0).crossProductToPoints(points.getPoint3dAt(1), points.getPoint3dAt(2)); + normal = points.getPoint3dAtUncheckedPointIndex(0).crossProductToPoints(points.getPoint3dAtUncheckedPointIndex(1), points.getPoint3dAtUncheckedPointIndex(2)); else - normal = visitor.normal.atVector3dIndex(0)!; + normal = visitor.normal.getVector3dAtCheckedVectorIndex(0)!; if (!normal.normalize(normal)) return undefined; @@ -2204,7 +2262,7 @@ export namespace TextureMapping { const numEdges = visitor.numEdgesThisFacet; for (let i = 0; i < numEdges; i++) { - const vector = Vector3d.createFrom(points.getPoint3dAt(i)); + const vector = Vector3d.createFrom(points.getPoint3dAtUncheckedPointIndex(i)); params.push(Point2d.create(vector.dotProduct(sideVector), vector.dotProduct(upVector))); uvTransform.multiplyPoint2d(params[i], params[i]); @@ -2217,7 +2275,7 @@ export namespace TextureMapping { const params: Point2d[] = []; const numEdges = visitor.numEdgesThisFacet; for (let i = 0; i < numEdges; i++) { - const point = visitor.point.getPoint3dAt(i); + const point = visitor.point.getPoint3dAtUncheckedPointIndex(i); if (transformToIModel !== undefined) transformToIModel.multiplyPoint3d(point, point); @@ -2230,7 +2288,9 @@ export namespace TextureMapping { } } -/** Properties for display of analysis data */ +/** Properties for display of analysis data + * @alpha + */ export interface AnalysisStyleProps { inputName?: string; displacementChannelName?: string; @@ -2242,6 +2302,7 @@ export interface AnalysisStyleProps { inputRange?: Range1d; } +/** @alpha */ export class AnalysisStyle implements AnalysisStyleProps { public inputName?: string; public displacementChannelName?: string; @@ -2288,7 +2349,9 @@ export class AnalysisStyle implements AnalysisStyleProps { } } -/** A circle drawn at a Z elevation, whose diameter is the the XY diagonal of the project extents, used to represent the ground as a reference point within a spatial view. */ +/** A circle drawn at a Z elevation, whose diameter is the the XY diagonal of the project extents, used to represent the ground as a reference point within a spatial view. + * @public + */ export class GroundPlane implements GroundPlaneProps { /** Whether the ground plane should be displayed. */ public display: boolean = false; @@ -2318,8 +2381,7 @@ export class GroundPlane implements GroundPlaneProps { }; } - /** - * Returns and locally stores gradient symbology for the ground plane texture depending on whether we are looking from above or below. + /** Returns and locally stores gradient symbology for the ground plane texture depending on whether we are looking from above or below. * Will store the ground colors used in the optional ColorDef array provided. * @hidden */ diff --git a/core/common/src/RenderSchedule.ts b/core/common/src/RenderSchedule.ts index e1def6f..497cb20 100644 --- a/core/common/src/RenderSchedule.ts +++ b/core/common/src/RenderSchedule.ts @@ -6,46 +6,50 @@ import { Id64String } from "@bentley/bentleyjs-core"; +/** @beta Can't be public with zero documentation */ export namespace RenderSchedule { - export interface TimelineEntryProps { - time: number; - interpolation: number; - } + export interface TimelineEntryProps { + time: number; + interpolation: number; + } - export interface VisibilityEntryProps extends TimelineEntryProps { - value: number; - } + export interface VisibilityEntryProps extends TimelineEntryProps { + value: number; + } - export interface ColorEntryProps extends TimelineEntryProps { - value: { red: number, green: number, blue: number }; - } + export interface ColorEntryProps extends TimelineEntryProps { + value: { red: number, green: number, blue: number }; + } - export interface CuttingPlaneProps { - position: number[]; - direction: number[]; - } - export interface TransformProps { - position: number[]; - orientation: number[]; - pivot: number[]; - transform: number[][]; - } - export interface TransformEntryProps extends TimelineEntryProps { - value: TransformProps; - } - export interface CuttingPlaneEntryProps extends TimelineEntryProps { - value: CuttingPlaneProps; - } - export interface ElementTimelineProps { - elementIds: Id64String[]; - visibilityTimeline?: VisibilityEntryProps[]; - colorTimeline?: ColorEntryProps[]; - transformTimeline?: TransformEntryProps[]; - cuttingPlaneTimeline?: CuttingPlaneEntryProps[]; - } - export interface ModelTimelineProps { - modelId: Id64String; - elementTimelines: ElementTimelineProps[]; - } + export interface CuttingPlaneProps { + position: number[]; + direction: number[]; + visible?: boolean; + hidden?: boolean; + } + export interface TransformProps { + position: number[]; + orientation: number[]; + pivot: number[]; + transform: number[][]; + } + export interface TransformEntryProps extends TimelineEntryProps { + value: TransformProps; + } + export interface CuttingPlaneEntryProps extends TimelineEntryProps { + value: CuttingPlaneProps; + } + export interface ElementTimelineProps { + batchId: number; + elementIds: Id64String[]; + visibilityTimeline?: VisibilityEntryProps[]; + colorTimeline?: ColorEntryProps[]; + transformTimeline?: TransformEntryProps[]; + cuttingPlaneTimeline?: CuttingPlaneEntryProps[]; + } + export interface ModelTimelineProps { + modelId: Id64String; + elementTimelines: ElementTimelineProps[]; + } } diff --git a/core/common/src/RpcInterface.ts b/core/common/src/RpcInterface.ts index c9c0086..80f12b5 100644 --- a/core/common/src/RpcInterface.ts +++ b/core/common/src/RpcInterface.ts @@ -14,6 +14,7 @@ export type RpcInterfaceImplementation = /** An RPC interface is a set of operations exposed by a service that a client can call, using configurable protocols, * in a platform-independent way. TheRpcInterface class is the base class for RPC interface definitions and implementations. + * @public */ export abstract class RpcInterface { /** Determines whether the backend version of an RPC interface is compatible (according to semantic versioning) with the frontend version of the interface. */ diff --git a/core/common/src/RpcManager.ts b/core/common/src/RpcManager.ts index 9c0b32f..c9b443d 100644 --- a/core/common/src/RpcManager.ts +++ b/core/common/src/RpcManager.ts @@ -7,7 +7,9 @@ import { RpcInterface, RpcInterfaceDefinition, RpcInterfaceImplementation } from "./RpcInterface"; import { RpcRegistry } from "./rpc/core/RpcRegistry"; -/** Describes the endpoints of an RPC interface. */ +/** Describes the endpoints of an RPC interface. + * @public + */ export interface RpcInterfaceEndpoints { interfaceName: string; interfaceVersion: string; @@ -15,10 +17,11 @@ export interface RpcInterfaceEndpoints { compatible: boolean; } -/** RPC interface management is concerned with coordination of access and configuration for RPC interfaces. */ +/** RPC interface management is concerned with coordination of access and configuration for RPC interfaces. + * @public + */ export class RpcManager { - /** - * Initializes an RPC interface class. + /** Initializes an RPC interface class. * @note This function must be called on the frontend and on the backend for each RPC interface class used by an application. */ public static initializeInterface(definition: RpcInterfaceDefinition): void { @@ -50,8 +53,7 @@ export class RpcManager { RpcRegistry.instance.unregisterImpl(definition); } - /** - * Describes the RPC interfaces and endpoints that are currently available from the backend. + /** Describes the RPC interfaces and endpoints that are currently available from the backend. * @note Some endpoints may be marked incompatible if the frontend expected a different interface declaration than the backend supplied. RPC operations against an incompatible interface will fail. */ public static async describeAvailableEndpoints(): Promise { diff --git a/core/common/src/Snapping.ts b/core/common/src/Snapping.ts index 4b02f9e..3b5cd09 100644 --- a/core/common/src/Snapping.ts +++ b/core/common/src/Snapping.ts @@ -9,16 +9,16 @@ import { XYZProps, Matrix4dProps } from "@bentley/geometry-core"; import { GeometryStreamProps } from "./geometry/GeometryStream"; import { GeometryClass } from "./Render"; -/** - * Information required to request a *snap* to a pickable decoration from the front end to the back end. +/** Information required to request a *snap* to a pickable decoration from the front end to the back end. + * @beta */ export class DecorationGeometryProps { public constructor(public readonly id: Id64String, public readonly geometryStream: GeometryStreamProps) { } } -/** - * Information required to request a *snap* to an element from the front end to the back end. +/** Information required to request a *snap* to an element from the front end to the back end. * Includes the viewing parameters so that snap can be relative to the view direction, viewing mode, etc. + * @beta */ export interface SnapRequestProps { id: Id64String; @@ -35,8 +35,8 @@ export interface SnapRequestProps { decorationGeometry?: DecorationGeometryProps[]; } -/** - * Information returned from the back end to the front end holding the result of a *snap* operation. +/** Information returned from the back end to the front end holding the result of a *snap* operation. + * @beta */ export interface SnapResponseProps { status: number; diff --git a/core/common/src/SubCategoryAppearance.ts b/core/common/src/SubCategoryAppearance.ts index 1f74beb..5b18125 100644 --- a/core/common/src/SubCategoryAppearance.ts +++ b/core/common/src/SubCategoryAppearance.ts @@ -10,6 +10,7 @@ import { ColorDef, ColorDefProps } from "./ColorDef"; /** Parameters that define the way geometry on a [[SubCategory]] appears. * SubCategoryAppearance describes the intrinstic appearance of geometry belonging to that SubCategory, independent of a particular [[ViewState]]. * Aspects of a SubCategory's appearance can be overridden in the context of a particular [[ViewState]] through the use of [[SubCategoryOverride]s. + * @public */ export class SubCategoryAppearance { /** The color of the geometry. @@ -104,6 +105,7 @@ export class SubCategoryAppearance { public static defaults = new SubCategoryAppearance(); } +/** @public */ export namespace SubCategoryAppearance { /** Properties used to create a SubCategoryAppearance * @see [[SubCategoryAppearance]] @@ -139,6 +141,7 @@ export namespace SubCategoryAppearance { * 3. Any aspects of the appearance overridden by the SubCategoryOverride are replaced with the values from the SubCategoryOverride. * An aspect is overridden by virtue of not being set to "undefined" in the SubCategoryOverride. * @see [[ViewState.overrideSubCategory]] + * @public */ export class SubCategoryOverride { /** @see [[SubCategoryAppearance.color]] */ @@ -215,7 +218,9 @@ export class SubCategoryOverride { public static defaults = new SubCategoryOverride({}); } -/** The *rank* for a Category */ +/** The *rank* for a Category + * @public + */ export const enum Rank { /** This category is predefined by the system */ System = 0, diff --git a/core/common/src/TextureProps.ts b/core/common/src/TextureProps.ts index 03fdd41..bcc8d85 100644 --- a/core/common/src/TextureProps.ts +++ b/core/common/src/TextureProps.ts @@ -7,9 +7,12 @@ import { DefinitionElementProps } from "./ElementProps"; import { ImageSourceFormat } from "./Image"; +/** @beta */ export enum TextureFlags { None } -/** Properties that define a Texture */ +/** Properties that define a Texture + * @beta + */ export interface TextureProps extends DefinitionElementProps { /** Format of the image data. */ format: ImageSourceFormat; diff --git a/core/common/src/Thumbnail.ts b/core/common/src/Thumbnail.ts index 798254f..2b2580b 100644 --- a/core/common/src/Thumbnail.ts +++ b/core/common/src/Thumbnail.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ /** @module WireFormats */ -/** - * Metadata about a thumbnail. Often this is redundant with information in the image itself, but is held +/** Metadata about a thumbnail. Often this is redundant with information in the image itself, but is held * outside of the image so it can be obtained without having to decode the image data. + * @alpha Needed given types in Image.ts? */ export interface ThumbnailFormatProps { /** X size of the image, in pixels. */ @@ -17,7 +17,9 @@ export interface ThumbnailFormatProps { format: "jpeg" | "png"; } -/** Properties of a thumbnail in an iModel. */ +/** Properties of a thumbnail in an iModel. + * @alpha Needed given types in Image.ts? + */ export interface ThumbnailProps extends ThumbnailFormatProps { /** Image data */ image: Uint8Array; diff --git a/core/common/src/TileProps.ts b/core/common/src/TileProps.ts index 78b63eb..c8b8e5a 100644 --- a/core/common/src/TileProps.ts +++ b/core/common/src/TileProps.ts @@ -20,6 +20,8 @@ export interface TileProps { sizeMultiplier?: number; /** Optional boolean indicating this tile has no children. Defaults to false if undefined. */ isLeaf?: boolean; + /** Optional transform (from tile to root) */ + transformToRoot?: TransformProps; } /** @hidden */ diff --git a/core/common/src/ViewProps.ts b/core/common/src/ViewProps.ts index df433ed..3d16ab9 100644 --- a/core/common/src/ViewProps.ts +++ b/core/common/src/ViewProps.ts @@ -13,7 +13,9 @@ import { ViewFlags, AnalysisStyleProps, HiddenLine, AmbientOcclusion } from "./R import { SubCategoryAppearance, SubCategoryOverride } from "./SubCategoryAppearance"; import { RenderSchedule } from "./RenderSchedule"; -/** Returned from [IModelDb.Views.getViewStateData]($backend) */ +/** Returned from [IModelDb.Views.getViewStateData]($backend) + * @public + */ export interface ViewStateProps { viewDefinitionProps: ViewDefinitionProps; categorySelectorProps: CategorySelectorProps; @@ -23,28 +25,37 @@ export interface ViewStateProps { sheetAttachments?: Id64Array; } -/** Properties that define a ModelSelector */ +/** Properties that define a ModelSelector + * @public + */ export interface ModelSelectorProps extends DefinitionElementProps { models: Id64Array; } -/** Properties that define a CategorySelector */ +/** Properties that define a CategorySelector + * @public + */ export interface CategorySelectorProps extends DefinitionElementProps { categories: Id64Array; } +/** @alpha Use ECSQL and IModelConnection.queryRows instead? */ export interface ViewQueryParams extends EntityQueryParams { wantPrivate?: boolean; } -/** Parameters used to construct a ViewDefinition */ +/** Parameters used to construct a ViewDefinition + * @public + */ export interface ViewDefinitionProps extends DefinitionElementProps { categorySelectorId: Id64String; displayStyleId: Id64String; description?: string; } -/** JSON representation of [[ViewFlags]] */ +/** JSON representation of [[ViewFlags]] + * @public + */ export interface ViewFlagProps { /** If true, don't show construction class. */ noConstruct?: boolean; @@ -100,6 +111,7 @@ export interface ViewFlagProps { /** Describes the [[SubCategoryOverride]]s applied to a [[SubCategory]] by a [[DisplayStyle]]. * @see [[DisplayStyleSettingsProps]] + * @public */ export interface DisplayStyleSubCategoryProps extends SubCategoryAppearance.Props { /** The Id of the [[SubCategory]] whose appearance is to be overridden. */ @@ -109,6 +121,7 @@ export interface DisplayStyleSubCategoryProps extends SubCategoryAppearance.Prop /** Describes the type of background map displayed by a [[DisplayStyle]] * @see [[BackgroundMapProps]] * @see [[DisplayStyleSettingsProps]] + * @public */ export const enum BackgroundMapType { Street = 1, @@ -118,6 +131,7 @@ export const enum BackgroundMapType { /** JSON representation of the settings associated with a background map displayed by a [[DisplayStyle]]. * @see [[DisplayStyleSettingsProps]] + * @public */ export interface BackgroundMapProps { groundBias?: number; @@ -128,7 +142,9 @@ export interface BackgroundMapProps { }; } -/** JSON representation of a [[GroundPlane]]. */ +/** JSON representation of a [[GroundPlane]]. + * @public + */ export interface GroundPlaneProps { /** Whether the ground plane should be displayed. Defaults to false. */ display?: boolean; @@ -140,7 +156,9 @@ export interface GroundPlaneProps { belowColor?: ColorDefProps; } -/** Enumerates the supported types of [[SkyBox]] images. */ +/** Enumerates the supported types of [[SkyBox]] images. + * @public + */ export const enum SkyBoxImageType { None, /** A single image mapped to the surface of a sphere. @see [[SkySphere]] */ @@ -151,7 +169,9 @@ export const enum SkyBoxImageType { Cylindrical, } -/** JSON representation of a set of images used by a [[SkyCube]]. Each property specifies the element ID of a texture associated with one face of the cube. */ +/** JSON representation of a set of images used by a [[SkyCube]]. Each property specifies the element ID of a texture associated with one face of the cube. + * @public + */ export interface SkyCubeProps { front?: Id64String; back?: Id64String; @@ -161,7 +181,9 @@ export interface SkyCubeProps { left?: Id64String; } -/** JSON representation of an image or images used by a [[SkySphere]] or [[SkyCube]]. */ +/** JSON representation of an image or images used by a [[SkySphere]] or [[SkyCube]]. + * @public + */ export interface SkyBoxImageProps { /** The type of skybox image. */ type?: SkyBoxImageType; @@ -171,7 +193,9 @@ export interface SkyBoxImageProps { textures?: SkyCubeProps; } -/** JSON representation of a [[SkyBox]]. */ +/** JSON representation of a [[SkyBox]]. + * @public + */ export interface SkyBoxProps { /** Whether or not the skybox should be displayed. Defaults to false. */ display?: boolean; @@ -193,12 +217,17 @@ export interface SkyBoxProps { image?: SkyBoxImageProps; } -/** JSON representation of the environment setup of a [[DisplayStyle3d]]. */ +/** JSON representation of the environment setup of a [[DisplayStyle3d]]. + * @public + */ export interface EnvironmentProps { ground?: GroundPlaneProps; sky?: SkyBoxProps; } -/** JSON representation of a context reality model */ + +/** JSON representation of a context reality model + * @public + */ export interface ContextRealityModelProps { tilesetUrl: string; name?: string; @@ -209,6 +238,7 @@ export interface ContextRealityModelProps { * These settings are not stored directly as members of the [[DisplayStyleProps]]. Instead, they are stored * as members of `jsonProperties.styles`. * @see [[DisplayStyleSettings]]. + * @beta */ export interface DisplayStyleSettingsProps { viewflags?: ViewFlagProps; @@ -228,8 +258,9 @@ export interface DisplayStyleSettingsProps { ContextRealityModels?: ContextRealityModelProps[]; } -/** JSON representation of settings assocaited with a [[DisplayStyle3dProps]]. +/** JSON representation of settings associated with a [[DisplayStyle3dProps]]. * @see [[DisplayStyle3dSettings]]. + * @beta */ export interface DisplayStyle3dSettingsProps extends DisplayStyleSettingsProps { /** Settings controlling display of skybox and ground plane. */ @@ -240,7 +271,9 @@ export interface DisplayStyle3dSettingsProps extends DisplayStyleSettingsProps { ao?: AmbientOcclusion.Props; } -/** JSON representation of a [[DisplayStyle]] or [[DisplayStyleState]]. */ +/** JSON representation of a [[DisplayStyle]] or [[DisplayStyleState]]. + * @beta + */ export interface DisplayStyleProps extends DefinitionElementProps { /** Display styles store their settings in a `styles` property within [[ElementProps.jsonProperties]]. */ jsonProperties?: { @@ -248,7 +281,9 @@ export interface DisplayStyleProps extends DefinitionElementProps { }; } -/** JSON representation of a [[DisplayStyle3d]] or [[DisplayStyle3dState]]. */ +/** JSON representation of a [[DisplayStyle3d]] or [[DisplayStyle3dState]]. + * @beta + */ export interface DisplayStyle3dProps extends DisplayStyleProps { /** Display styles store their settings in a `styles` property within [[ElementProps.jsonProperties]]. */ jsonProperties?: { @@ -256,14 +291,18 @@ export interface DisplayStyle3dProps extends DisplayStyleProps { }; } -/** properties of a camera */ +/** properties of a camera + * @public + */ export interface CameraProps { lens: AngleProps; focusDist: number; // NOTE: this is abbreviated, do not change! eye: XYZProps; } -/** Parameters to construct a ViewDefinition3d */ +/** Parameters to construct a ViewDefinition3d + * @public + */ export interface ViewDefinition3dProps extends ViewDefinitionProps { /** if true, camera is valid. */ cameraOn: boolean; @@ -277,12 +316,16 @@ export interface ViewDefinition3dProps extends ViewDefinitionProps { camera: CameraProps; } -/** Parameters to construct a SpatialViewDefinition */ +/** Parameters to construct a SpatialViewDefinition + * @public + */ export interface SpatialViewDefinitionProps extends ViewDefinition3dProps { modelSelectorId: Id64String; } -/** Parameters used to construct a ViewDefinition2d */ +/** Parameters used to construct a ViewDefinition2d + * @public + */ export interface ViewDefinition2dProps extends ViewDefinitionProps { baseModelId: Id64String; origin: XYProps; @@ -290,12 +333,15 @@ export interface ViewDefinition2dProps extends ViewDefinitionProps { angle: AngleProps; } +/** @public */ export interface AuxCoordSystemProps extends ElementProps { type?: number; description?: string; } -/** Properties of AuxCoordSystem2d */ +/** Properties of AuxCoordSystem2d + * @public + */ export interface AuxCoordSystem2dProps extends AuxCoordSystemProps { /** Origin of the AuxCoordSystem2d */ origin?: XYProps; @@ -303,7 +349,9 @@ export interface AuxCoordSystem2dProps extends AuxCoordSystemProps { angle?: AngleProps; } -/** Properties of AuxCoordSystem3d */ +/** Properties of AuxCoordSystem3d + * @public + */ export interface AuxCoordSystem3dProps extends AuxCoordSystemProps { /** Origin of the AuxCoordSystem3d */ origin?: XYZProps; @@ -317,6 +365,7 @@ export interface AuxCoordSystem3dProps extends AuxCoordSystemProps { /** Provides access to the settings defined by a [[DisplayStyle]] or [[DisplayStyleState]], and ensures that * the style's JSON properties are kept in sync. + * @beta */ export class DisplayStyleSettings { protected readonly _json: DisplayStyleSettingsProps; @@ -460,6 +509,7 @@ export class DisplayStyleSettings { /** Provides access to the settings defined by a [[DisplayStyle3d]] or [[DisplayStyle3dState]], and ensures that * the style's JSON properties are kept in sync. + * @beta */ export class DisplayStyle3dSettings extends DisplayStyleSettings { private _hline: HiddenLine.Settings; diff --git a/core/common/src/domains/FunctionalElementProps.ts b/core/common/src/domains/FunctionalElementProps.ts index 146b54a..d21168b 100644 --- a/core/common/src/domains/FunctionalElementProps.ts +++ b/core/common/src/domains/FunctionalElementProps.ts @@ -6,6 +6,7 @@ import { ElementProps, RelatedElementProps } from "../ElementProps"; +/** @public */ export interface FunctionalElementProps extends ElementProps { typeDefinition?: RelatedElementProps; } diff --git a/core/common/src/domains/GenericElementProps.ts b/core/common/src/domains/GenericElementProps.ts index 441be9b..a9a1545 100644 --- a/core/common/src/domains/GenericElementProps.ts +++ b/core/common/src/domains/GenericElementProps.ts @@ -6,10 +6,12 @@ import { GeometricElement2dProps, RelatedElementProps } from "../ElementProps"; +/** @public */ export interface ViewAttachmentLabelProps extends GeometricElement2dProps { viewAttachment?: RelatedElementProps; } +/** @public */ export interface CalloutProps extends GeometricElement2dProps { drawingModel?: RelatedElementProps; } diff --git a/core/common/src/geometry/AreaPattern.ts b/core/common/src/geometry/AreaPattern.ts index a2eaefd..a6900a4 100644 --- a/core/common/src/geometry/AreaPattern.ts +++ b/core/common/src/geometry/AreaPattern.ts @@ -8,6 +8,7 @@ import { Point2d, Point3d, YawPitchRollAngles, Matrix3d, Transform, YawPitchRoll import { ColorDef, ColorDefProps } from "../ColorDef"; import { Id64, Id64String } from "@bentley/bentleyjs-core"; +/** @public */ export namespace AreaPattern { /** Single hatch line definition */ export interface HatchDefLineProps { diff --git a/core/common/src/geometry/Cartographic.ts b/core/common/src/geometry/Cartographic.ts index 1f479bd..00f23f8 100644 --- a/core/common/src/geometry/Cartographic.ts +++ b/core/common/src/geometry/Cartographic.ts @@ -10,7 +10,9 @@ import { Angle, Point3d, Vector3d, XYZ, XYAndZ, Range1d, Range2d, Range3d, Trans export interface LatAndLong { longitude: number; latitude: number; } export interface LatLongAndHeight extends LatAndLong { height: number; } -/** A position on the earth defined by longitude, latitude, and height above the WSG84 ellipsoid . */ +/** A position on the earth defined by longitude, latitude, and height above the WSG84 ellipsoid. + * @public + */ export class Cartographic implements LatLongAndHeight { /** * @param longitude longitude, in radians. @@ -267,10 +269,18 @@ export class Cartographic implements LatLongAndHeight { return result; } } -/** A cartographic range representing a rectangular region if low longitude/latitude > high then area crossing seam is indicated. +/** A cartographic range representing a rectangular region if low longitude/latitude > high then area crossing seam is indicated. + * @public */ export class CartographicRange { private _ranges: Range2d[]; + + // These following are used to preserve the min/max latitude and longitudes. + // The longitudes are raw values and may cross over the -PI or 2PI boundaries. + private _minLongitude: number = 0; + private _maxLongitude: number = 0; + private _minLatitude: number = 0; + private _maxLatitude: number = 0; constructor(spatialRange: Range3d, spatialToEcef: Transform) { const ecefRange = spatialToEcef.multiplyRange(spatialRange); @@ -279,25 +289,26 @@ export class CartographicRange { const high = Cartographic.fromEcef(ecefRange.high)!; const longitudeRanges = []; - const minLongitude = Math.min(low.longitude, high.longitude), maxLongitude = Math.max(low.longitude, high.longitude); - if (maxLongitude - minLongitude > Angle.piRadians) { - longitudeRanges.push(Range1d.createXX(0.0, minLongitude)); - longitudeRanges.push(Range1d.createXX(maxLongitude, Angle.pi2Radians)); + this._minLongitude = Math.min(low.longitude, high.longitude), this._maxLongitude = Math.max(low.longitude, high.longitude); + if (this._maxLongitude - this._minLongitude > Angle.piRadians) { + longitudeRanges.push(Range1d.createXX(0.0, this._minLongitude)); + longitudeRanges.push(Range1d.createXX(this._maxLongitude, Angle.pi2Radians)); } else { - longitudeRanges.push(Range1d.createXX(minLongitude, maxLongitude)); + longitudeRanges.push(Range1d.createXX(this._minLongitude, this._maxLongitude)); } this._ranges = []; for (const longitudeRange of longitudeRanges) { - const minLatitude = Math.min(low.latitude, high.latitude), maxLatitude = Math.max(low.latitude, high.latitude); - if (maxLatitude - minLatitude > Angle.piOver2Radians) { - this._ranges.push(Range2d.createXYXY(longitudeRange.low, 0.0, longitudeRange.high, minLatitude)); - this._ranges.push(Range2d.createXYXY(longitudeRange.low, maxLatitude, longitudeRange.high, Angle.piRadians)); + this._minLatitude = Math.min(low.latitude, high.latitude), this._maxLatitude = Math.max(low.latitude, high.latitude); + if (this._maxLatitude - this._minLatitude > Angle.piOver2Radians) { + this._ranges.push(Range2d.createXYXY(longitudeRange.low, 0.0, longitudeRange.high, this._minLatitude)); + this._ranges.push(Range2d.createXYXY(longitudeRange.low, this._maxLatitude, longitudeRange.high, Angle.piRadians)); } else { - this._ranges.push(Range2d.createXYXY(longitudeRange.low, minLatitude, longitudeRange.high, maxLatitude)); + this._ranges.push(Range2d.createXYXY(longitudeRange.low, this._minLatitude, longitudeRange.high, this._maxLatitude)); } } } + public intersectsRange(other: CartographicRange): boolean { for (const range of this._ranges) for (const otherRange of other._ranges) @@ -305,4 +316,17 @@ export class CartographicRange { return true; return false; } + + /** + * This method returns the raw latitude / longitude for the range in a Range2d object. + * The X value represents the longitude and the Y value the latitudes. + * Y values are kepts conscribed between -PI and +PI while + * longitude values can be expressed in any range between -2PI to +2PI + * given the minimum longitude is always smaller numerically than the maximum longitude. + * Note that usually the longitudes are usually by convention in the range of -PI to PI except + * for ranges that overlap the -PI/+PI frontier in which case either representation is acceptable. + */ + public getLongitudeLatitudeBoundingBox(): Range2d { + return Range2d.createXYXY(this._minLongitude, this._minLatitude, this._maxLongitude, this._maxLatitude); + } } diff --git a/core/common/src/geometry/GeometryStream.ts b/core/common/src/geometry/GeometryStream.ts index 077971c..e7aee90 100644 --- a/core/common/src/geometry/GeometryStream.ts +++ b/core/common/src/geometry/GeometryStream.ts @@ -21,6 +21,7 @@ import { IModelError } from "../IModelError"; /** Establish a non-default [[SubCategory]] or to override [[SubCategoryAppearance]] for the geometry that follows. * A GeometryAppearanceProps always signifies a reset to the [[SubCategoryAppearance]] for subsequent [[GeometryStreamProps]] entries for undefined values. * @see [[GeometryStreamEntryProps]] + * @public */ export interface GeometryAppearanceProps { /** Optional [[SubCategory]] id for subsequent geometry. Use to create a GeometryStream with geometry that is not on the default [[SubCategory]] for the element's [[Category]] or is has geometry on multiple subCategories. */ @@ -42,6 +43,7 @@ export interface GeometryAppearanceProps { /** Add a [[gradient]], [[backgroundFill]], or solid [[color]] fill to subsequent planar regions (or meshes). * Only one value among [[gradient]], [[backgroundFill]], and [[color]] should be set. * @see [[GeometryStreamEntryProps]] + * @public */ export interface AreaFillProps { /** Fill display type, must be set to something other than [[FillDisplay.Never]] to display fill */ @@ -58,6 +60,7 @@ export interface AreaFillProps { /** Override [[SubCategoryAppearance.materialId]] for subsequent surface and solid geometry. * @see [[GeometryStreamEntryProps]] + * @public */ export interface MaterialProps { /** Material id to use, specify an invalid [[Id64]] to override [[SubCategoryAppearance.materialId]] with no material. */ @@ -70,6 +73,7 @@ export interface MaterialProps { rotation?: YawPitchRollProps; } +/** @beta */ export namespace BRepEntity { /** Enum for type of solid kernel entity this represents */ export const enum Type { @@ -91,11 +95,11 @@ export namespace BRepEntity { materialId?: Id64String; } - /** Geometry entry representing raw brep data. Must be specifically requested using [[ElementLoadProps.wantBRepData]]. + /** Geometry entry representing raw brep data. * @see [[GeometryStreamEntryProps]] */ export interface DataProps { - /** data as Base64 encoded string */ + /** data as Base64 encoded string. Must be specifically requested using [[ElementLoadProps.wantBRepData]]. */ data?: string; /** body type, default is Solid */ type?: Type; @@ -108,6 +112,7 @@ export namespace BRepEntity { /** Add a reference to a [[GeometryPart]] from the GeometryStream of a [[GeometricElement]]. * @see [[GeometryStreamEntryProps]] + * @public */ export interface GeometryPartInstanceProps { /** GeometryPart id */ @@ -122,6 +127,7 @@ export interface GeometryPartInstanceProps { /** Allowed GeometryStream entries - should only set one value. * @see [GeometryStream]($docs/learning/common/geometrystream.md) + * @public */ export interface GeometryStreamEntryProps extends GeomJson.GeometryProps { appearance?: GeometryAppearanceProps; @@ -135,10 +141,14 @@ export interface GeometryStreamEntryProps extends GeomJson.GeometryProps { subRange?: LowAndHighXYZ; } -/** A [[GeometricElement]]'s GeometryStream is represented by an array of [[GeometryStreamEntryProps]]. */ +/** A [[GeometricElement]]'s GeometryStream is represented by an array of [[GeometryStreamEntryProps]]. + * @public + */ export type GeometryStreamProps = GeometryStreamEntryProps[]; -/** GeometryStreamBuilder is a helper class for populating the [[GeometryStreamProps]] array needed to create a [[GeometricElement]] or [[GeometryPart]]. */ +/** GeometryStreamBuilder is a helper class for populating the [[GeometryStreamProps]] array needed to create a [[GeometricElement]] or [[GeometryPart]]. + * @public + */ export class GeometryStreamBuilder { /** Current inverse placement transform, used for converting world coordinate input to be placement relative */ private _worldToLocal?: Transform; @@ -313,7 +323,9 @@ export class GeometryStreamBuilder { } } -/** Hold current state information for [[GeometryStreamIterator]] */ +/** Hold current state information for [[GeometryStreamIterator]] + * @public + */ export class GeometryStreamIteratorEntry { /** A [[GeometryParams]] representing the appearance of the current geometric entry */ public geomParams: GeometryParams; @@ -339,6 +351,7 @@ export class GeometryStreamIteratorEntry { /** GeometryStreamIterator is a helper class for iterating a [[GeometryStreamProps]]. * A [[GeometricElement]]'s GeometryStream must be specifically requested using [[ElementLoadProps.wantGeometry]]. + * @public */ export class GeometryStreamIterator implements IterableIterator { /** GeometryStream entries */ diff --git a/core/common/src/geometry/LineStyle.ts b/core/common/src/geometry/LineStyle.ts index cf339c2..a6d8619 100644 --- a/core/common/src/geometry/LineStyle.ts +++ b/core/common/src/geometry/LineStyle.ts @@ -7,6 +7,7 @@ import { Vector3d, XYZProps, YawPitchRollProps, YawPitchRollAngles, Transform } from "@bentley/geometry-core"; import { Id64String } from "@bentley/bentleyjs-core"; +/** @public */ export namespace LineStyle { /** Modify the line style appearance without changing the line style definition. diff --git a/core/common/src/geometry/Placement.ts b/core/common/src/geometry/Placement.ts new file mode 100644 index 0000000..7a1d600 --- /dev/null +++ b/core/common/src/geometry/Placement.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module Geometry */ + +import { Angle, Constant, Matrix3d, Point2d, Point3d, Range2d, Range3d, Transform, Vector3d, YawPitchRollAngles } from "@bentley/geometry-core"; +import { Placement2dProps, Placement3dProps } from "../ElementProps"; +import { Frustum } from "../Frustum"; + +/** A Range3d that is aligned with the axes of spatial coordinates. + * @public + */ +export type AxisAlignedBox3d = Range3d; + +/** A bounding box aligned to the orientation of a 3d Element + * @public + */ +export type ElementAlignedBox3d = Range3d; + +/** A bounding box aligned to the orientation of a 2d Element + * @public + */ +export type ElementAlignedBox2d = Range2d; + +/** A bounding box aligned to a local coordinate system + * @public + */ +export type LocalAlignedBox3d = Range3d; + +/** The placement of a GeometricElement3d. This includes the origin, orientation, and size (bounding box) of the element. + * All geometry of a GeometricElement are relative to its placement. + * @public + */ +export class Placement3d implements Placement3dProps { + public constructor(public origin: Point3d, public angles: YawPitchRollAngles, public bbox: ElementAlignedBox3d) { } + /** Get the rotation from local coordinates of this placement to world coordinates. */ + public get rotation(): Matrix3d { return this.angles.toMatrix3d(); } + /** Get the transform from local coordinates of this placement to world coordinates. */ + public get transform(): Transform { return Transform.createOriginAndMatrix(this.origin, this.rotation); } + + /** Create a new Placement3d from a Placement3dProps. */ + public static fromJSON(json?: Placement3dProps): Placement3d { + const props: any = json ? json : {}; + return new Placement3d(Point3d.fromJSON(props.origin), YawPitchRollAngles.fromJSON(props.angles), Range3d.fromJSON(props.bbox)); + } + + /** Get the 8 corners, in world coordinates, of this placement. */ + public getWorldCorners(out?: Frustum): Frustum { + const frust = Frustum.fromRange(this.bbox, out); + frust.multiply(this.transform); + return frust; + } + + /** Set the contents of this Placement3d from another Placement3d */ + public setFrom(other: Placement3d) { + this.origin.setFrom(other.origin); + this.angles.setFrom(other.angles); + this.bbox.setFrom(other.bbox); + } + + /** Determine whether this Placement3d is valid. */ + public get isValid(): boolean { return !this.bbox.isNull && Math.max(this.origin.maxAbs(), this.bbox.maxAbs()) < Constant.circumferenceOfEarth; } + + /** Calculate the axis-aligned bounding box for this placement. */ + public calculateRange(): AxisAlignedBox3d { + const range = new Range3d(); + if (!this.isValid) + return range; + + this.transform.multiplyRange(this.bbox, range); + + // low and high are not allowed to be equal + range.ensureMinLengths(); + return range; + } +} + +/** The placement of a GeometricElement2d. This includes the origin, rotation, and size (bounding box) of the element. + * @public + */ +export class Placement2d implements Placement2dProps { + public constructor(public origin: Point2d, public angle: Angle, public bbox: ElementAlignedBox2d) { } + /** Get the rotation from local coordinates of this placement to world coordinates. */ + public get rotation(): Matrix3d { return Matrix3d.createRotationAroundVector(Vector3d.unitZ(), this.angle)!; } + /** Get the transform from local coordinates of this placement to world coordinates. */ + public get transform(): Transform { return Transform.createOriginAndMatrix(Point3d.createFrom(this.origin), this.rotation); } + /** Create a new Placement2d from a Placement2dProps. */ + public static fromJSON(json?: Placement2dProps): Placement2d { + const props: any = json ? json : {}; + return new Placement2d(Point2d.fromJSON(props.origin), Angle.fromJSON(props.angle), Range2d.fromJSON(props.bbox)); + } + + /** Get the 8 corners, in world coordinates, of this placement. */ + public getWorldCorners(out?: Frustum): Frustum { + const frust = Frustum.fromRange(this.bbox, out); + frust.multiply(this.transform); + return frust; + } + + /** Determine whether this Placement2d is valid. */ + public get isValid(): boolean { return !this.bbox.isNull && Math.max(this.origin.maxAbs(), this.bbox.maxAbs()) < Constant.circumferenceOfEarth; } + + /** Set the contents of this Placement3d from another Placement3d */ + public setFrom(other: Placement2d) { + this.origin.setFrom(other.origin); + this.angle.setFrom(other.angle); + this.bbox.setFrom(other.bbox); + } + + /** Calculate the axis-aligned bounding box for this placement. */ + public calculateRange(): AxisAlignedBox3d { + const range = new Range3d(); + if (!this.isValid) + return range; + + this.transform.multiplyRange(Range3d.createRange2d(this.bbox, 0), range); + + // low and high are not allowed to be equal + range.ensureMinLengths(); + range.low.z = - 1.0; // is the 2dFrustumDepth, which === 1 meter + range.high.z = 1.0; + return range; + } +} diff --git a/core/common/src/geometry/Primitives.ts b/core/common/src/geometry/Primitives.ts deleted file mode 100644 index f0f59dc..0000000 --- a/core/common/src/geometry/Primitives.ts +++ /dev/null @@ -1,199 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ -/** @module Geometry */ - -import { - Point2d, Point3d, Vector3d, YawPitchRollAngles, XYAndZ, XAndY, LowAndHighXY, - Range2d, Range3d, Angle, Transform, Matrix3d, Constant, -} from "@bentley/geometry-core"; -import { Placement2dProps, Placement3dProps } from "../ElementProps"; -import { Frustum } from "../Frustum"; - -/** - * A Range3d that is aligned with the axes of a coordinate space. - */ -export class AxisAlignedBox3d extends Range3d { - constructor(low?: XYAndZ, high?: XYAndZ) { - if (low === undefined || high === undefined) - super(); // defines an empty box - else - super(low.x, low.y, low.z, high.x, high.y, high.z); - } - public clone() { return new AxisAlignedBox3d(this.low, this.high); } - - /** Construct a new AxisAlignedBox3d from a LowAndHighXY */ - public static fromRange2d(r: LowAndHighXY) { const v = new AxisAlignedBox3d(); v.low.x = r.low.x; v.low.y = r.low.y; v.high.x = r.high.x; v.high.y = r.high.y; return v; } - - /** Get the center point of this AxisAlignedBox3d */ - public get center(): Point3d { return this.low.interpolate(.5, this.high); } - - /** Ensure that the length of each dimension of this AxisAlignedBox3d is at least a minimum size. If not, expand to minimum about the center. - * @param min The minimum length for each dimension. - */ - public ensureMinLengths(min: number = .001) { - let size = (min - this.xLength()) / 2.0; - if (size > 0) { - this.low.x -= size; - this.high.x += size; - } - size = (min - this.yLength()) / 2.0; - if (size > 0) { - this.low.y -= size; - this.high.y += size; - } - size = (min - this.zLength()) / 2.0; - if (size > 0) { - this.low.z -= size; - this.high.z += size; - } - } - - /** @hidden */ - public static fromJSON(json: any): AxisAlignedBox3d { - const val = new AxisAlignedBox3d(); - val.setFromJSON(json); - return val; - } -} - -/** A bounding box aligned to the orientation of a 3d Element */ -export class ElementAlignedBox3d extends Range3d { - public static createFromPoints(low: XYAndZ, high: XYAndZ): ElementAlignedBox3d { return new ElementAlignedBox3d(low.x, low.y, low.z, high.x, high.y, high.z); } - public get left(): number { return this.low.x; } - public get bottom(): number { return this.low.y; } - public get front(): number { return this.low.z; } - public get right(): number { return this.high.x; } - public get top(): number { return this.high.y; } - public get back(): number { return this.high.z; } - public get width(): number { return this.xLength(); } - public get depth(): number { return this.yLength(); } - public get height(): number { return this.zLength(); } - public get isValid(): boolean { - const max = Constant.circumferenceOfEarth; const lo = this.low; const hi = this.high; - return !this.isNull && lo.x > -max && lo.y > -max && lo.z > -max && hi.x < max && hi.y < max && hi.z < max; - } - - public static fromJSON(json?: any): ElementAlignedBox3d { - const val = new ElementAlignedBox3d(); - if (json) - val.setFromJSON(json); - return val; - } -} - -/** A bounding box aligned to the orientation of a 2d Element */ -export class ElementAlignedBox2d extends Range2d { - public static createFromPoints(low: XAndY, high: XAndY): ElementAlignedBox2d { return new ElementAlignedBox2d(low.x, low.y, high.x, high.y); } - public get left(): number { return this.low.x; } - public get bottom(): number { return this.low.y; } - public get right(): number { return this.high.x; } - public get top(): number { return this.high.y; } - public get width(): number { return this.xLength(); } - public get height(): number { return this.yLength(); } - public static fromJSON(json?: any): ElementAlignedBox2d { - const val = new ElementAlignedBox2d(); - if (json) - val.setFromJSON(json); - return val; - } - public get isValid(): boolean { - const max = Constant.circumferenceOfEarth; const lo = this.low; const hi = this.high; - return !this.isNull && lo.x > -max && lo.y > -max && hi.x < max && hi.y < max; - } -} - -/** - * The placement of a GeometricElement3d. This includes the origin, orientation, and size (bounding box) of the element. - * All geometry of a GeometricElement are relative to its placement. - */ -export class Placement3d implements Placement3dProps { - public constructor(public origin: Point3d, public angles: YawPitchRollAngles, public bbox: ElementAlignedBox3d) { } - /** Get the rotation from local coordinates of this placement to world coordinates. */ - public get rotation(): Matrix3d { return this.angles.toMatrix3d(); } - /** Get the transform from local coordinates of this placement to world coordinates. */ - public get transform(): Transform { return Transform.createOriginAndMatrix(this.origin, this.rotation); } - - /** Create a new Placement3d from a Placement3dProps. */ - public static fromJSON(json?: Placement3dProps): Placement3d { - const props: any = json ? json : {}; - return new Placement3d(Point3d.fromJSON(props.origin), YawPitchRollAngles.fromJSON(props.angles), ElementAlignedBox3d.fromJSON(props.bbox)); - } - - /** Get the 8 corners, in world coordinates, of this placement. */ - public getWorldCorners(out?: Frustum): Frustum { - const frust = Frustum.fromRange(this.bbox, out); - frust.multiply(this.transform); - return frust; - } - - /** Set the contents of this Placement3d from another Placement3d */ - public setFrom(other: Placement3d) { - this.origin.setFrom(other.origin); - this.angles.setFrom(other.angles); - this.bbox.setFrom(other.bbox); - } - - /** Determine whether this Placement3d is valid. */ - public get isValid(): boolean { return this.bbox.isValid && this.origin.maxAbs() < Constant.circumferenceOfEarth; } - - /** Calculate the axis-aligned bounding box for this placement. */ - public calculateRange(): AxisAlignedBox3d { - const range = new AxisAlignedBox3d(); - if (!this.isValid) - return range; - - this.transform.multiplyRange(this.bbox, range); - - // low and high are not allowed to be equal - range.ensureMinLengths(); - return range; - } -} - -/** The placement of a GeometricElement2d. This includes the origin, rotation, and size (bounding box) of the element. */ -export class Placement2d implements Placement2dProps { - public constructor(public origin: Point2d, public angle: Angle, public bbox: ElementAlignedBox2d) { } - /** Get the rotation from local coordinates of this placement to world coordinates. */ - public get rotation(): Matrix3d { return Matrix3d.createRotationAroundVector(Vector3d.unitZ(), this.angle)!; } - /** Get the transform from local coordinates of this placement to world coordinates. */ - public get transform(): Transform { return Transform.createOriginAndMatrix(Point3d.createFrom(this.origin), this.rotation); } - /** Create a new Placement2d from a Placement2dProps. */ - public static fromJSON(json?: Placement2dProps): Placement2d { - const props: any = json ? json : {}; - return new Placement2d(Point2d.fromJSON(props.origin), Angle.fromJSON(props.angle), ElementAlignedBox2d.fromJSON(props.bbox)); - } - - /** Get the 8 corners, in world coordinates, of this placement. */ - public getWorldCorners(out?: Frustum): Frustum { - const frust = Frustum.fromRange(this.bbox, out); - frust.multiply(this.transform); - return frust; - } - - /** Determine whether this Placement2d is valid. */ - public get isValid(): boolean { return this.bbox.isValid && this.origin.maxAbs() < Constant.circumferenceOfEarth; } - - /** Set the contents of this Placement3d from another Placement3d */ - public setFrom(other: Placement2d) { - this.origin.setFrom(other.origin); - this.angle.setFrom(other.angle); - this.bbox.setFrom(other.bbox); - } - - /** Calculate the axis-aligned bounding box for this placement. */ - public calculateRange(): AxisAlignedBox3d { - const range = new AxisAlignedBox3d(); - if (!this.isValid) - return range; - - this.transform.multiplyRange(Range3d.createRange2d(this.bbox, 0), range); - - // low and high are not allowed to be equal - range.ensureMinLengths(); - range.low.z = - 1.0; // is the 2dFrustumDepth, which === 1 meter - range.high.z = 1.0; - return range; - } -} diff --git a/core/common/src/geometry/TextString.ts b/core/common/src/geometry/TextString.ts index 99982c4..d9a7f90 100644 --- a/core/common/src/geometry/TextString.ts +++ b/core/common/src/geometry/TextString.ts @@ -8,6 +8,7 @@ import { XYZProps, Point3d, YawPitchRollAngles, YawPitchRollProps, Transform, Ve /** Properties for a TextString class. * @see [[GeometryStreamEntryProps]] + * @public */ export interface TextStringProps { /** text string */ @@ -30,10 +31,10 @@ export interface TextStringProps { rotation?: YawPitchRollProps; } -/** - * A single line of text, all with the same font, styles (underline, bold, italic), and size. +/** A single line of text, all with the same font, styles (underline, bold, italic), and size. * This class also holds the origin and direction for the text. * A paragraph is composed of one or more instances of TextStrings. + * @public */ export class TextString { /** Text string */ diff --git a/core/common/src/imodeljs-common.ts b/core/common/src/imodeljs-common.ts index e9d14dd..ec089cb 100644 --- a/core/common/src/imodeljs-common.ts +++ b/core/common/src/imodeljs-common.ts @@ -20,6 +20,7 @@ export * from "./IModel"; export * from "./IModelError"; export * from "./IModelVersion"; export * from "./Lighting"; +export * from "./MaterialProps"; export * from "./ModelProps"; export * from "./OctEncodedNormal"; export * from "./QPoint"; @@ -29,14 +30,16 @@ export * from "./TileProps"; export * from "./Thumbnail"; export * from "./ViewProps"; export * from "./Render"; +export * from "./Paging"; export * from "./RenderSchedule"; +export * from "./ChangedElements"; export * from "./domains/FunctionalElementProps"; export * from "./domains/GenericElementProps"; export * from "./geometry/AreaPattern"; export * from "./geometry/Cartographic"; export * from "./geometry/GeometryStream"; export * from "./geometry/LineStyle"; -export * from "./geometry/Primitives"; +export * from "./geometry/Placement"; export * from "./geometry/TextString"; export * from "./rpc/TestRpcManager"; export * from "./rpc/WipRpcInterface"; @@ -61,7 +64,14 @@ export * from "./rpc/IModelReadRpcInterface"; export * from "./rpc/IModelTileRpcInterface"; export * from "./rpc/IModelWriteRpcInterface"; export * from "./rpc/StandaloneIModelRpcInterface"; -export * from "./rpc/IModelUnitTestRpcInterface"; + +// Set the version number so it can be found at runtime. BUILD_SEMVER is replaced at build time by the webpack DefinePlugin. +declare var BUILD_SEMVER: string; +if ((typeof (BUILD_SEMVER) !== "undefined") && (typeof window !== "undefined") && window) { + if (!(window as any).iModelJsVersions) + (window as any).iModelJsVersions = new Map(); + (window as any).iModelJsVersions.set("imodeljs-common", BUILD_SEMVER); +} /** @docs-package-description * The imodeljs-common package contains classes for working with iModels that can be used in both [frontend]($docs/learning/frontend/index.md) and [backend]($docs/learning/backend/index.md). diff --git a/core/common/src/rpc/IModelReadRpcInterface.ts b/core/common/src/rpc/IModelReadRpcInterface.ts index 88823ba..f65c449 100644 --- a/core/common/src/rpc/IModelReadRpcInterface.ts +++ b/core/common/src/rpc/IModelReadRpcInterface.ts @@ -6,7 +6,7 @@ import { Id64String, Id64Set } from "@bentley/bentleyjs-core"; import { AccessToken } from "@bentley/imodeljs-clients"; -import { Point2d, Point3d, Vector2d, Vector3d } from "@bentley/geometry-core"; +import { Point2d, Point3d, Vector2d, Vector3d, Range3dProps } from "@bentley/geometry-core"; import { Code } from "../Code"; import { RpcInterface } from "../RpcInterface"; import { RpcManager } from "../RpcManager"; @@ -19,17 +19,19 @@ import { ElementProps } from "../ElementProps"; import { SnapRequestProps, SnapResponseProps } from "../Snapping"; import { IModelCoordinatesResponseProps, GeoCoordinatesResponseProps } from "../GeoCoordinateServices"; import { ViewStateProps } from "../ViewProps"; +import { PageOptions } from "../Paging"; /** Response if the IModelDb was not found at the backend * (if the service has moved) + * @public */ export class IModelNotFoundResponse extends RpcNotFoundResponse { } -/** - * The RPC interface for reading from an iModel. +/** The RPC interface for reading from an iModel. * All operations only require read-only access. * This interface is not normally used directly. See IModelConnection for higher-level and more convenient API for accessing iModels from a frontend. + * @public */ export abstract class IModelReadRpcInterface extends RpcInterface { /** The types that can be marshaled by the interface. */ @@ -50,7 +52,7 @@ export abstract class IModelReadRpcInterface extends RpcInterface { public static getClient(): IModelReadRpcInterface { return RpcManager.getClientForInterface(IModelReadRpcInterface); } /** The semantic version of the interface. */ - public static version = "0.1.1"; + public static version = "0.2.1"; /*=========================================================================================== NOTE: Any add/remove/change to the methods below requires an update of the interface version. @@ -58,20 +60,20 @@ export abstract class IModelReadRpcInterface extends RpcInterface { ===========================================================================================*/ public async openForRead(_accessToken: AccessToken, _iModelToken: IModelToken): Promise { return this.forward(arguments); } public async close(_accessToken: AccessToken, _iModelToken: IModelToken): Promise { return this.forward(arguments); } - public async executeQuery(_iModelToken: IModelToken, _ecsql: string, _bindings?: any[] | object): Promise { return this.forward(arguments); } + public async queryPage(_iModelToken: IModelToken, _ecsql: string, _bindings?: any[] | object, _options?: PageOptions): Promise { return this.forward(arguments); } + public async queryRowCount(_iModelToken: IModelToken, _ecsql: string, _bindings?: any[] | object): Promise { return this.forward(arguments); } public async getModelProps(_iModelToken: IModelToken, _modelIds: Id64Set): Promise { return this.forward(arguments); } + public async queryModelRanges(_iModelToken: IModelToken, _modelIds: Id64Set): Promise { return this.forward(arguments); } public async queryModelProps(_iModelToken: IModelToken, _params: EntityQueryParams): Promise { return this.forward(arguments); } public async getElementProps(_iModelToken: IModelToken, _elementIds: Id64Set): Promise { return this.forward(arguments); } public async queryElementProps(_iModelToken: IModelToken, _params: EntityQueryParams): Promise { return this.forward(arguments); } public async queryEntityIds(_iModelToken: IModelToken, _params: EntityQueryParams): Promise { return this.forward(arguments); } - public async formatElements(_iModelToken: IModelToken, _elementIds: Id64Set): Promise { return this.forward(arguments); } public async getClassHierarchy(_iModelToken: IModelToken, _startClassName: string): Promise { return this.forward(arguments); } public async getAllCodeSpecs(_iModelToken: IModelToken): Promise { return this.forward(arguments); } public async getViewStateData(_iModelToken: IModelToken, _viewDefinitionId: string): Promise { return this.forward(arguments); } public async readFontJson(_iModelToken: IModelToken): Promise { return this.forward(arguments); } - public async requestSnap(_iModelToken: IModelToken, _connectionId: string, _props: SnapRequestProps): Promise { return this.forward(arguments); } - public async cancelSnap(_iModelToken: IModelToken, _connectionId: string): Promise { return this.forward(arguments); } - public async loadNativeAsset(_iModelToken: IModelToken, _assetName: string): Promise { return this.forward(arguments); } + public async requestSnap(_iModelToken: IModelToken, _sessionId: string, _props: SnapRequestProps): Promise { return this.forward(arguments); } + public async cancelSnap(_iModelToken: IModelToken, _sessionId: string): Promise { return this.forward(arguments); } public async getToolTipMessage(_iModelToken: IModelToken, _elementId: string): Promise { return this.forward(arguments); } public async getViewThumbnail(_iModelToken: IModelToken, _viewId: string): Promise { return this.forward(arguments); } public async getDefaultViewId(_iModelToken: IModelToken): Promise { return this.forward(arguments); } diff --git a/core/common/src/rpc/IModelTileRpcInterface.ts b/core/common/src/rpc/IModelTileRpcInterface.ts index 9841d5e..214faf5 100644 --- a/core/common/src/rpc/IModelTileRpcInterface.ts +++ b/core/common/src/rpc/IModelTileRpcInterface.ts @@ -9,6 +9,7 @@ import { RpcManager } from "../RpcManager"; import { IModelToken } from "../IModel"; import { TileTreeProps } from "../TileProps"; +/** @public */ export abstract class IModelTileRpcInterface extends RpcInterface { public static types = () => [ IModelToken, @@ -17,13 +18,18 @@ export abstract class IModelTileRpcInterface extends RpcInterface { public static getClient(): IModelTileRpcInterface { return RpcManager.getClientForInterface(IModelTileRpcInterface); } /** The semantic version of the interface. */ - public static version = "0.1.0"; + public static version = "0.1.1"; /*=========================================================================================== NOTE: Any add/remove/change to the methods below requires an update of the interface version. NOTE: Please consult the README in this folder for the semantic versioning rules. ===========================================================================================*/ - public async getTileTreeProps(_iModelToken: IModelToken, _id: string): Promise { return this.forward(arguments); } + // The following 2 functions may produce a 504 error if the response takes a long time. + public async getTileTreeProps(_iModelToken: IModelToken, _id: string): Promise { return this.forward(arguments); } public async getTileContent(_iModelToken: IModelToken, _treeId: string, _contentId: string): Promise { return this.forward(arguments); } + + // The following 2 functions use memoized promises to avoid 504 errors if the response takes a long time. + public async requestTileTreeProps(_iModelToken: IModelToken, _id: string): Promise { return this.forward(arguments); } + public async requestTileContent(_iModelToken: IModelToken, _treeId: string, _contentId: string): Promise { return this.forward(arguments); } } diff --git a/core/common/src/rpc/IModelWriteRpcInterface.ts b/core/common/src/rpc/IModelWriteRpcInterface.ts index 4b8fd60..b453144c 100644 --- a/core/common/src/rpc/IModelWriteRpcInterface.ts +++ b/core/common/src/rpc/IModelWriteRpcInterface.ts @@ -5,23 +5,23 @@ /** @module RpcInterface */ import { AccessToken } from "@bentley/imodeljs-clients"; -import { Point3d } from "@bentley/geometry-core"; +import { Point3d, Range3d } from "@bentley/geometry-core"; import { RpcInterface } from "../RpcInterface"; import { RpcManager } from "../RpcManager"; import { IModel, IModelToken } from "../IModel"; -import { AxisAlignedBox3d } from "../geometry/Primitives"; +import { AxisAlignedBox3d } from "../geometry/Placement"; import { IModelNotFoundResponse } from "./IModelReadRpcInterface"; -/** - * The RPC interface for writing to an iModel. +/** The RPC interface for writing to an iModel. * All operations require read+write access. * This interface is not normally used directly. See IModelConnection for higher-level and more convenient API for accessing iModels from a frontend. + * @alpha */ export abstract class IModelWriteRpcInterface extends RpcInterface { /** The types that can be marshaled by the interface. */ public static types = () => [ AccessToken, - AxisAlignedBox3d, + Range3d, IModelToken, Point3d, IModelNotFoundResponse, diff --git a/core/common/src/rpc/StandaloneIModelRpcInterface.ts b/core/common/src/rpc/StandaloneIModelRpcInterface.ts index c854b83..6b9a19a 100644 --- a/core/common/src/rpc/StandaloneIModelRpcInterface.ts +++ b/core/common/src/rpc/StandaloneIModelRpcInterface.ts @@ -9,9 +9,9 @@ import { RpcInterface } from "../RpcInterface"; import { RpcManager } from "../RpcManager"; import { IModel, IModelToken } from "../IModel"; -/** - * The RPC interface for working with standalone iModels. +/** The RPC interface for working with standalone iModels. * Products are generally discouraged from using standalone iModels and therefore registering this interface. + * @alpha */ export abstract class StandaloneIModelRpcInterface extends RpcInterface { /** The version of the interface. */ diff --git a/core/common/src/rpc/WipRpcInterface.ts b/core/common/src/rpc/WipRpcInterface.ts index 334c017..dd8029a 100644 --- a/core/common/src/rpc/WipRpcInterface.ts +++ b/core/common/src/rpc/WipRpcInterface.ts @@ -7,6 +7,7 @@ import { IModelToken } from "../IModel"; import { RpcInterface } from "../RpcInterface"; import { RpcManager } from "../RpcManager"; +import { ChangedElements } from "../ChangedElements"; /** * The purpose of this class is to house WIP RPC methods. For example: @@ -27,14 +28,16 @@ export abstract class WipRpcInterface extends RpcInterface { public static getClient(): WipRpcInterface { return RpcManager.getClientForInterface(WipRpcInterface); } /** The semantic version of the interface. */ - public static version = "0.2.0"; + public static version = "0.2.1"; /*=========================================================================================== NOTE: Any add/remove/change to the methods below requires an update of the interface version. NOTE: Please consult the README in this folder for the semantic versioning rules. - ===========================================================================================*/ + =======================================1===================================================*/ public async placeholder(_iModelToken: IModelToken): Promise { return this.forward(arguments); } // here to test that WipRpcInterface is configured properly public async isChangeCacheAttached(_iModelToken: IModelToken): Promise { return this.forward(arguments); } public async attachChangeCache(_iModelToken: IModelToken): Promise { return this.forward(arguments); } public async detachChangeCache(_iModelToken: IModelToken): Promise { return this.forward(arguments); } + public async getChangedElements(_iModelToken: IModelToken, _startChangesetId: string, _endChangesetId: string): Promise { return this.forward(arguments); } + public async isChangesetProcessed(_iModelToken: IModelToken, _changesetId: string): Promise { return this.forward(arguments); } } diff --git a/core/common/src/rpc/core/RpcConfiguration.ts b/core/common/src/rpc/core/RpcConfiguration.ts index 319a904..8e58a73 100644 --- a/core/common/src/rpc/core/RpcConfiguration.ts +++ b/core/common/src/rpc/core/RpcConfiguration.ts @@ -35,10 +35,10 @@ export abstract class RpcConfiguration { } /** Obtains the instance of an RPC configuration class. */ - public static obtain(constructor: { new(): T }): T { - let instance = (constructor as any)[INSTANCE] as T; + public static obtain(configurationConstructor: { new(): T }): T { + let instance = (configurationConstructor as any)[INSTANCE] as T; if (!instance) - instance = (constructor as any)[INSTANCE] = new constructor(); + instance = (configurationConstructor as any)[INSTANCE] = new configurationConstructor(); return instance; } diff --git a/core/common/src/rpc/core/RpcInvocation.ts b/core/common/src/rpc/core/RpcInvocation.ts index 1a3a06f..0c59e6f 100644 --- a/core/common/src/rpc/core/RpcInvocation.ts +++ b/core/common/src/rpc/core/RpcInvocation.ts @@ -80,7 +80,7 @@ export class RpcInvocation { const backend = this.operation.interfaceVersion; const frontend = this.request.operation.interfaceVersion; - if (!RpcInterface.isVersionCompatible(frontend, backend)) { + if (!RpcInterface.isVersionCompatible(backend, frontend)) { throw new IModelError(RpcInterfaceStatus.IncompatibleVersion, `Backend version ${backend} does not match frontend version ${frontend} for RPC interface ${this.operation.operationName}.`); } } catch (error) { diff --git a/core/common/src/rpc/core/RpcMarshaling.ts b/core/common/src/rpc/core/RpcMarshaling.ts index 75ebf98..80e609d 100644 --- a/core/common/src/rpc/core/RpcMarshaling.ts +++ b/core/common/src/rpc/core/RpcMarshaling.ts @@ -15,23 +15,26 @@ import { RpcMarshalingDirective } from "./RpcConstants"; let marshalingScope = ""; let marshalingTarget: RpcSerializedValue; +let chunkThreshold = 0; interface MarshalingBinaryMarker { [RpcMarshalingDirective.Binary]: true; type: number; index: number; size: number; + chunks: number; } export namespace MarshalingBinaryMarker { export function createDefault(): MarshalingBinaryMarker { - return { [RpcMarshalingDirective.Binary]: true, type: 0, index: 0, size: -1 }; + return { [RpcMarshalingDirective.Binary]: true, type: 0, index: 0, size: -1, chunks: 1 }; } } export interface RpcSerializedValue { objects: string; data: Uint8Array[]; + chunks?: number; } export namespace RpcSerializedValue { @@ -45,7 +48,7 @@ export class RpcMarshaling { private constructor() { } /** Serializes a value. */ - public static serialize(operation: RpcOperation | string, _protocol: RpcProtocol | undefined, value: any): RpcSerializedValue { + public static serialize(operation: RpcOperation | string, protocol: RpcProtocol | undefined, value: any): RpcSerializedValue { const serialized = RpcSerializedValue.create(); if (typeof (value) === "undefined") { @@ -54,21 +57,25 @@ export class RpcMarshaling { marshalingTarget = serialized; marshalingScope = typeof (operation) === "string" ? operation : operation.interfaceDefinition.name; + chunkThreshold = protocol ? protocol.transferChunkThreshold : 0; serialized.objects = JSON.stringify(value, WireFormat.marshal); marshalingTarget = undefined as any; + chunkThreshold = 0; return serialized; } /** Deserializes a value. */ - public static deserialize(_operation: RpcOperation, _protocol: RpcProtocol | undefined, value: RpcSerializedValue): any { + public static deserialize(_operation: RpcOperation, protocol: RpcProtocol | undefined, value: RpcSerializedValue): any { if (value.objects === "") { return undefined; } marshalingTarget = value; + chunkThreshold = protocol ? protocol.transferChunkThreshold : 0; const result = JSON.parse(value.objects, WireFormat.unmarshal); marshalingTarget = undefined as any; + chunkThreshold = 0; return result; } @@ -134,7 +141,7 @@ class WireFormat { private static marshalBinary(value: any): any { if (ArrayBuffer.isView(value) || Buffer.isBuffer(value)) { - const marker: MarshalingBinaryMarker = { [RpcMarshalingDirective.Binary]: true, type: -1, index: -1, size: -1 }; + const marker: MarshalingBinaryMarker = { [RpcMarshalingDirective.Binary]: true, type: -1, index: -1, size: -1, chunks: 1 }; let i = -1; if (Buffer.isBuffer(value)) { @@ -151,8 +158,32 @@ class WireFormat { throw new IModelError(BentleyStatus.ERROR, `Cannot marshal binary type "${value.constructor.name}".`); } else { marker.type = i; - marker.index = marshalingTarget.data.push(value as Uint8Array) - 1; marker.size = value.byteLength; + + if (chunkThreshold && value.byteLength > chunkThreshold) { + marker.index = marshalingTarget.data.length; + marker.chunks = 0; + + let cursor = value.byteOffset; + const end = cursor + value.byteLength; + let chunk = chunkThreshold; + + for (; ;) { + if (cursor >= end) { + break; + } + + marshalingTarget.data.push(new Uint8Array(value.buffer, cursor, chunk)); + ++marker.chunks; + cursor += chunk; + + const consumed = cursor - value.byteOffset; + const remaining = value.byteLength - consumed; + chunk = Math.min(chunkThreshold, remaining); + } + } else { + marker.index = marshalingTarget.data.push(value as Uint8Array) - 1; + } } return marker; @@ -170,7 +201,24 @@ class WireFormat { throw new IModelError(BentleyStatus.ERROR, `Cannot unmarshal missing binary value.`); } - return new WireFormat.binaryTypes[value.type](marshalingTarget.data[value.index]); + const type = WireFormat.binaryTypes[value.type]; + if (value.chunks === 0) { + return new type(); + } else if (value.chunks === 1) { + return new type(marshalingTarget.data[value.index]); + } else { + const buffer = new ArrayBuffer(value.size); + const view = new type(buffer); + + let cursor = 0; + for (let c = 0; c !== value.chunks; ++c) { + const chunk = marshalingTarget.data[value.index + c]; + view.set(chunk, cursor); + cursor += chunk.byteLength; + } + + return view; + } } private static marshalCustom(name: string, value: any, unregistered: boolean) { diff --git a/core/common/src/rpc/core/RpcProtocol.ts b/core/common/src/rpc/core/RpcProtocol.ts index 24a75f5..4ad6386 100644 --- a/core/common/src/rpc/core/RpcProtocol.ts +++ b/core/common/src/rpc/core/RpcProtocol.ts @@ -95,6 +95,9 @@ export abstract class RpcProtocol { /** The name of the authorization header. */ public get authorizationHeaderName() { return this.configuration.applicationAuthorizationKey; } + /** If greater than zero, specifies where to break large binary request payloads. */ + public transferChunkThreshold: number = 0; + /** Override to supply the status corresponding to a protocol-specific code value. */ public getStatus(code: number): RpcRequestStatus { return code; diff --git a/core/common/src/rpc/core/RpcRequest.ts b/core/common/src/rpc/core/RpcRequest.ts index 8d65b46..4b17415 100644 --- a/core/common/src/rpc/core/RpcRequest.ts +++ b/core/common/src/rpc/core/RpcRequest.ts @@ -136,9 +136,9 @@ export abstract class RpcRequest { public method: string; /** Finds the first parameter of a given type if present. */ - public findParameterOfType(constructor: { new(...args: any[]): T }): T | undefined { + public findParameterOfType(requestConstructor: { new(...args: any[]): T }): T | undefined { for (const param of this.parameters) { - if (param instanceof constructor) + if (param instanceof requestConstructor) return param; } @@ -194,6 +194,14 @@ export abstract class RpcRequest { this.operation.policy.sentCallback(this); const response = await sent; this.protocol.events.raiseEvent(RpcProtocolEvent.ResponseLoading, this); + + const status = this.protocol.getStatus(response); + if (status === RpcRequestStatus.Unknown) { + this._connecting = false; + this.handleUnknownResponse(response); + return; + } + const value = await this.load(); this.protocol.events.raiseEvent(RpcProtocolEvent.ResponseLoaded, this); this._connecting = false; @@ -205,6 +213,10 @@ export abstract class RpcRequest { } } + protected handleUnknownResponse(code: number) { + this.reject(new IModelError(BentleyStatus.ERROR, `Unknown response ${code}.`)); + } + private handleResponse(code: number, value: RpcSerializedValue) { const status = this.protocol.getStatus(code); @@ -281,7 +293,7 @@ export abstract class RpcRequest { this.dispose(); } - private reject(reason: any): void { + protected reject(reason: any): void { if (!this._active) return; diff --git a/core/common/src/rpc/electron/ElectronIpcTransport.ts b/core/common/src/rpc/electron/ElectronIpcTransport.ts new file mode 100644 index 0000000..9bcae1d --- /dev/null +++ b/core/common/src/rpc/electron/ElectronIpcTransport.ts @@ -0,0 +1,179 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module RpcInterface */ + +import { BentleyStatus } from "@bentley/bentleyjs-core"; +import { IModelError } from "../../IModelError"; +import { SerializedRpcRequest, RpcRequestFulfillment } from "../core/RpcProtocol"; +import { ElectronRpcRequest } from "./ElectronRpcRequest"; +import { RpcSerializedValue } from "../core/RpcMarshaling"; +import { ElectronRpcProtocol } from "./ElectronRpcProtocol"; + +const OBJECTS_CHANNEL = "@bentley/imodeljs-common/ElectronRpcProtocol/objects"; +const DATA_CHANNEL = "@bentley/imodeljs-common/ElectronRpcProtocol/data"; + +/** @hidden */ +export const interop = (() => { + let electron = null; + if (typeof (global) !== "undefined" && global && global.process && (global.process as any).type) { + // tslint:disable-next-line:no-eval + electron = eval("require")("electron"); + } + + return electron; +})(); + +interface PartialPayload { id: string; index: number; data: Uint8Array; } + +/** @hidden */ +export interface IpcTransportMessage { id: string; parameters?: RpcSerializedValue; result?: RpcSerializedValue; } + +/** @hidden */ +export abstract class ElectronIpcTransport { + private _ipc: any; + private _partials: Map; + + public sendRequest(request: SerializedRpcRequest) { + const value = this._extractValue(request); + this._send(request, value); + } + + public constructor(ipc: any) { + this._ipc = ipc; + this._partials = new Map(); + this._setupDataChannel(); + this._setupObjectsChannel(); + } + + private _setupDataChannel() { + this._ipc.on(DATA_CHANNEL, async (evt: any, chunk: PartialPayload) => { + let pending = this._partials.get(chunk.id); + if (!pending) { + pending = []; + this._partials.set(chunk.id, pending); + } + + if (Array.isArray(pending)) { + pending.push(chunk); + } else { + ++pending.received; + + const value = this._extractValue(pending.message); + value.data[chunk.index] = chunk.data; + + if (pending.received === (value.chunks || 0)) { + this.handleComplete(pending.message.id, evt); + } + } + }); + } + + private _setupObjectsChannel() { + this._ipc.on(OBJECTS_CHANNEL, async (evt: any, message: TIn) => { + const pending = this._partials.get(message.id); + if (pending && !Array.isArray(pending)) { + throw new IModelError(BentleyStatus.ERROR, `Message already received for id "${message.id}".`); + } + + const partial = { message, received: 0 }; + this._partials.set(message.id, partial); + const value = this._extractValue(partial.message); + + if (pending && Array.isArray(pending)) { + for (const chunk of pending) { + ++partial.received; + value.data[chunk.index] = chunk.data; + } + } + + if (partial.received === (value.chunks || 0)) { + this.handleComplete(message.id, evt); + } + }); + } + + private _extractValue(t: IpcTransportMessage): RpcSerializedValue { + if (t.parameters) { + return t.parameters; + } + + if (t.result) { + return t.result; + } + + throw new IModelError(BentleyStatus.ERROR, "Unknown value type."); + } + + private _send(message: IpcTransportMessage, value: RpcSerializedValue, evt?: any) { + const chunks = value.data; + if (chunks.length) { + value.chunks = chunks.length; + value.data = []; + } + + (evt ? evt.sender : this._ipc).send(OBJECTS_CHANNEL, message); + + for (let index = 0; index !== chunks.length; ++index) { + const chunk: PartialPayload = { id: message.id, index, data: chunks[index] }; + (evt ? evt.sender : this._ipc).send(DATA_CHANNEL, chunk); + } + } + + protected abstract handleComplete(id: string, evt: any): void; + + protected sendResponse(message: TOut, evt: any) { + const value = this._extractValue(message); + this._send(message, value, evt); + } + + protected loadMessage(id: string) { + const partial = this._partials.get(id); + if (!partial || Array.isArray(partial)) { + throw new IModelError(BentleyStatus.ERROR, `Incomplete transmission for id "${id}".`); + } + + this._partials.delete(id); + return partial.message; + } +} + +class FrontendIpcTransport extends ElectronIpcTransport { + protected async handleComplete(id: string) { + const message = this.loadMessage(id); + const protocol = ElectronRpcProtocol.instances.get(message.interfaceName) as ElectronRpcProtocol; + const request = protocol.requests.get(message.id) as ElectronRpcRequest; + protocol.requests.delete(message.id); + request.notifyResponse(message); + } +} + +class BackendIpcTransport extends ElectronIpcTransport { + protected async handleComplete(id: string, evt: any) { + const message = this.loadMessage(id); + + let response: RpcRequestFulfillment; + try { + const protocol = ElectronRpcProtocol.obtainInstance(message); + response = await protocol.fulfill(message); + } catch (err) { + response = RpcRequestFulfillment.forUnknownError(message, err); + } + + this.sendResponse(response, evt); + } +} + +let transport: ElectronIpcTransport | undefined; + +if (interop) { + if (interop.ipcMain) { + transport = new BackendIpcTransport(interop.ipcMain); + } else if (interop.ipcRenderer) { + transport = new FrontendIpcTransport(interop.ipcRenderer); + } +} + +/** @hidden */ +export const ipcTransport = transport; diff --git a/core/common/src/rpc/electron/ElectronRpcManager.ts b/core/common/src/rpc/electron/ElectronRpcManager.ts index 1f96bfd..9a2e3c9 100644 --- a/core/common/src/rpc/electron/ElectronRpcManager.ts +++ b/core/common/src/rpc/electron/ElectronRpcManager.ts @@ -7,7 +7,8 @@ import { RpcInterfaceDefinition } from "../../RpcInterface"; import { RpcManager } from "../../RpcManager"; import { RpcConfiguration } from "../core/RpcConfiguration"; -import { ElectronRpcProtocol, interop } from "./ElectronRpcProtocol"; +import { ElectronRpcProtocol } from "./ElectronRpcProtocol"; +import { interop } from "./ElectronIpcTransport"; /** Initialization parameters for ElectronRpcConfiguration. */ export interface ElectronRpcParams { diff --git a/core/common/src/rpc/electron/ElectronRpcProtocol.ts b/core/common/src/rpc/electron/ElectronRpcProtocol.ts index 659198b..f76bfeb 100644 --- a/core/common/src/rpc/electron/ElectronRpcProtocol.ts +++ b/core/common/src/rpc/electron/ElectronRpcProtocol.ts @@ -7,67 +7,33 @@ import { BentleyStatus } from "@bentley/bentleyjs-core"; import { IModelError } from "../../IModelError"; import { RpcInterface, RpcInterfaceDefinition } from "../../RpcInterface"; -import { RpcProtocol, SerializedRpcRequest, RpcRequestFulfillment } from "../core/RpcProtocol"; +import { RpcProtocol, SerializedRpcRequest } from "../core/RpcProtocol"; import { RpcRegistry } from "../core/RpcRegistry"; import { ElectronRpcConfiguration } from "./ElectronRpcManager"; import { ElectronRpcRequest } from "./ElectronRpcRequest"; -const instances: Map = new Map(); - -const lookupInstance = (request: SerializedRpcRequest) => { - const interfaceName = request.operation.interfaceDefinition; - - let protocol = instances.get(interfaceName) as ElectronRpcProtocol; - if (!protocol) { - RpcRegistry.instance.lookupImpl(interfaceName); - protocol = instances.get(interfaceName) as ElectronRpcProtocol; - } +/** RPC interface protocol for an Electron-based application. */ +export class ElectronRpcProtocol extends RpcProtocol { + public static instances: Map = new Map(); - return protocol; -}; + public static obtainInstance(request: SerializedRpcRequest) { + const interfaceName = request.operation.interfaceDefinition; -/** @hidden */ -export const CHANNEL = "@bentley/imodeljs-common/ElectronRpcProtocol"; + let protocol = ElectronRpcProtocol.instances.get(interfaceName) as ElectronRpcProtocol; + if (!protocol) { + RpcRegistry.instance.lookupImpl(interfaceName); + protocol = ElectronRpcProtocol.instances.get(interfaceName) as ElectronRpcProtocol; + } -/** @hidden */ -export const interop = (() => { - let electron = null; - if (typeof (global) !== "undefined" && global && global.process && (global.process as any).type) { - // tslint:disable-next-line:no-eval - electron = eval("require")("electron"); + return protocol; } - return electron; -})(); - -if (interop) { - if (interop.ipcMain) { - interop.ipcMain.on(CHANNEL, async (evt: any, request: SerializedRpcRequest) => { - let response: RpcRequestFulfillment; - try { - const protocol = lookupInstance(request); - response = await protocol.fulfill(request); - } catch (err) { - response = RpcRequestFulfillment.forUnknownError(request, err); - } - - evt.sender.send(CHANNEL, response); - }); - } else if (interop.ipcRenderer) { - interop.ipcRenderer.on(CHANNEL, (_evt: any, fulfillment: RpcRequestFulfillment) => { - const protocol = instances.get(fulfillment.interfaceName) as ElectronRpcProtocol; - const request = protocol.requests.get(fulfillment.id) as ElectronRpcRequest; - protocol.requests.delete(fulfillment.id); - request.notifyResponse(fulfillment); - }); - } -} - -/** RPC interface protocol for an Electron-based application. */ -export class ElectronRpcProtocol extends RpcProtocol { /** The RPC request class for this protocol. */ public readonly requestType = ElectronRpcRequest; + /** Specifies where to break large binary request payloads. */ + public transferChunkThreshold = 48 * 1024 * 1024; + /** @hidden */ public requests: Map = new Map(); @@ -97,13 +63,13 @@ export class ElectronRpcProtocol extends RpcProtocol { } private registerInterface(definition: RpcInterfaceDefinition) { - if (instances.has(definition.name)) + if (ElectronRpcProtocol.instances.has(definition.name)) throw new IModelError(BentleyStatus.ERROR, `RPC interface "${definition.name}"" is already associated with a protocol.`); - instances.set(definition.name, this); + ElectronRpcProtocol.instances.set(definition.name, this); } private purgeInterface(definition: RpcInterfaceDefinition) { - instances.delete(definition.name); + ElectronRpcProtocol.instances.delete(definition.name); } } diff --git a/core/common/src/rpc/electron/ElectronRpcRequest.ts b/core/common/src/rpc/electron/ElectronRpcRequest.ts index 6d4b369..aaf6513 100644 --- a/core/common/src/rpc/electron/ElectronRpcRequest.ts +++ b/core/common/src/rpc/electron/ElectronRpcRequest.ts @@ -6,8 +6,9 @@ import { RpcRequest } from "../core/RpcRequest"; import { RpcRequestFulfillment } from "../core/RpcProtocol"; -import { ElectronRpcProtocol, CHANNEL, interop } from "./ElectronRpcProtocol"; +import { ElectronRpcProtocol } from "./ElectronRpcProtocol"; import { RpcProtocolEvent } from "../core/RpcConstants"; +import { ipcTransport } from "./ElectronIpcTransport"; export class ElectronRpcRequest extends RpcRequest { private _response: (value: number) => void = () => undefined; @@ -21,7 +22,7 @@ export class ElectronRpcRequest extends RpcRequest { try { this.protocol.requests.set(this.id, this); const request = this.protocol.serialize(this); - interop.ipcRenderer.send(CHANNEL, request); + ipcTransport!.sendRequest(request); } catch (e) { this.protocol.events.raiseEvent(RpcProtocolEvent.ConnectionErrorReceived, this); } diff --git a/core/common/src/rpc/mobile/MobileRpcManager.ts b/core/common/src/rpc/mobile/MobileRpcManager.ts index 4e86577..2c99a97 100644 --- a/core/common/src/rpc/mobile/MobileRpcManager.ts +++ b/core/common/src/rpc/mobile/MobileRpcManager.ts @@ -41,6 +41,8 @@ export abstract class MobileRpcConfiguration extends RpcConfiguration { /** Check if running backend running on mobile */ public static get isMobileFrontend() { return MobileRpcConfiguration.platform !== RpcMobilePlatform.Unknown; } + /** Check if running backend running on wkwebview on ios */ + public static get isIOSFrontend() { return MobileRpcConfiguration.isMobileFrontend && (window as any).webkit && (window as any).webkit.messageHandlers; } } /** Coordinates usage of RPC interfaces for an Mobile-based application. */ @@ -68,5 +70,5 @@ export class MobileRpcManager { /** Initializes MobileRpcManager for the backend of an application. */ public static initializeImpl(interfaces: RpcInterfaceDefinition[]): MobileRpcConfiguration { return MobileRpcManager.performInitialization(interfaces, RpcEndpoint.Backend); - } + } } diff --git a/core/common/src/rpc/web/WebAppRpcProtocol.ts b/core/common/src/rpc/web/WebAppRpcProtocol.ts index a2a5094..bdfa399 100644 --- a/core/common/src/rpc/web/WebAppRpcProtocol.ts +++ b/core/common/src/rpc/web/WebAppRpcProtocol.ts @@ -109,6 +109,11 @@ export abstract class WebAppRpcProtocol extends RpcProtocol { } } + /** Whether an HTTP status code indicates a request timeout. */ + public isTimeout(code: number): boolean { + return code === 504; + } + /** An OpenAPI-compatible description of this protocol. */ public get openAPIDescription() { return new RpcOpenAPIDescription(this); } diff --git a/core/common/src/rpc/web/WebAppRpcRequest.ts b/core/common/src/rpc/web/WebAppRpcRequest.ts index 923b870..58b1a46 100644 --- a/core/common/src/rpc/web/WebAppRpcRequest.ts +++ b/core/common/src/rpc/web/WebAppRpcRequest.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ /** @module RpcInterface */ -import { IModelError, ServerError } from "../../IModelError"; +import { IModelError, ServerError, ServerTimeoutError } from "../../IModelError"; import { BentleyStatus } from "@bentley/bentleyjs-core"; import { RpcInterface } from "../../RpcInterface"; import { SerializedRpcRequest, RpcRequestFulfillment, SerializedRpcOperation } from "../core/RpcProtocol"; @@ -119,6 +119,14 @@ export class WebAppRpcRequest extends RpcRequest { }); } + protected handleUnknownResponse(code: number) { + if (this.protocol.isTimeout(code)) { + this.reject(new ServerTimeoutError(code, "Request timeout.")); + } else { + this.reject(new ServerError(code, "Unknown server response code.")); + } + } + protected async load(): Promise { return new Promise(async (resolve, reject) => { try { diff --git a/core/common/src/test/FeatureIndex.test.ts b/core/common/src/test/FeatureIndex.test.ts index f86195b..77409d5 100644 --- a/core/common/src/test/FeatureIndex.test.ts +++ b/core/common/src/test/FeatureIndex.test.ts @@ -38,7 +38,8 @@ describe("ColorIndex", () => { const numColors: number = 10; const colors: Uint32Array = new Uint32Array(numColors); - const indices: Uint16Array = new Uint16Array(numColors); + const indices: number[] = []; + indices.length = numColors; for (let i = 0; i < numColors; ++i) { colors[i] = ColorDef.from(i, i * 2, i * 4, 63).tbgr; indices[i] = i; diff --git a/core/ecschema-metadata/CHANGELOG.json b/core/ecschema-metadata/CHANGELOG.json index 942eaea..ad69c83 100644 --- a/core/ecschema-metadata/CHANGELOG.json +++ b/core/ecschema-metadata/CHANGELOG.json @@ -1,6 +1,48 @@ { "name": "@bentley/ecschema-metadata", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/ecschema-metadata_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "Changes package.json to include api-extractor and adds api-extractor.json" + }, + { + "comment": "Implemented BIS rules for schema validation." + }, + { + "comment": "Use new buildIModelJsBuild script" + }, + { + "comment": "Change the default version pattern to be padded with zeroes to match 'RR.ww.mm'." + }, + { + "comment": "Convert Schema._items from Array to Map and return IterableIterator instead of Array in Schema.getItems/getClasses" + }, + { + "comment": "Removing BIS Rules from ecschema-metadata" + }, + { + "comment": "SchemaContext is now required when constructing a Schema instance." + }, + { + "comment": "Added schema validation support via the configuration of rule sets that can be applied during schema traversal" + }, + { + "comment": "upgrade to TypeScript 3.2.2" + } + ] + } + }, + { + "version": "0.188.0", + "tag": "@bentley/ecschema-metadata_v0.188.0", + "date": "Wed, 16 Jan 2019 16:36:09 GMT", + "comments": {} + }, { "version": "0.187.0", "tag": "@bentley/ecschema-metadata_v0.187.0", diff --git a/core/ecschema-metadata/CHANGELOG.md b/core/ecschema-metadata/CHANGELOG.md index 88a101e..2ffbc55 100644 --- a/core/ecschema-metadata/CHANGELOG.md +++ b/core/ecschema-metadata/CHANGELOG.md @@ -1,6 +1,26 @@ # Change Log - @bentley/ecschema-metadata -This log was last generated on Tue, 15 Jan 2019 15:18:59 GMT and should not be manually modified. +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- Changes package.json to include api-extractor and adds api-extractor.json +- Implemented BIS rules for schema validation. +- Use new buildIModelJsBuild script +- Change the default version pattern to be padded with zeroes to match 'RR.ww.mm'. +- Convert Schema._items from Array to Map and return IterableIterator instead of Array in Schema.getItems/getClasses +- Removing BIS Rules from ecschema-metadata +- SchemaContext is now required when constructing a Schema instance. +- Added schema validation support via the configuration of rule sets that can be applied during schema traversal +- upgrade to TypeScript 3.2.2 + +## 0.188.0 +Wed, 16 Jan 2019 16:36:09 GMT + +*Version update only* ## 0.187.0 Tue, 15 Jan 2019 15:18:59 GMT diff --git a/core/ecschema-metadata/build/createLocalization.js b/core/ecschema-metadata/build/createLocalization.js index fc99faf..92cbc39 100644 --- a/core/ecschema-metadata/build/createLocalization.js +++ b/core/ecschema-metadata/build/createLocalization.js @@ -6,8 +6,8 @@ const fs = require("fs"); const path = require("path"); const root = process.cwd(); -const diagnosticsDir = path.resolve(root, "lib", "Validation", "Diagnostics"); -const diagnostics = require(diagnosticsDir); +const ecDiagnosticsDir = path.resolve(root, "lib", "Validation", "ECRules"); +const ecDiagnostics = require(ecDiagnosticsDir); function printErrorAndFail(errorMessage) { console.log(`Build failed creating localization file ECSchemaMetadata.json: ${errorMessage}`); @@ -18,8 +18,8 @@ function createLocalization() { const localesDir = path.resolve(root, "public", "locales", "en"); const entries = {}; - for (const [key, value] of Object.entries(diagnostics.DIAGNOSTICS)) { - entries[value.key] = value.message; + for (const [, value] of Object.entries(ecDiagnostics.Diagnostics)) { + entries[value.prototype.code] = value.prototype.messageText; } var jsonString = JSON.stringify(entries); diff --git a/core/ecschema-metadata/package.json b/core/ecschema-metadata/package.json index 6a9f300..ea8f7af 100644 --- a/core/ecschema-metadata/package.json +++ b/core/ecschema-metadata/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/ecschema-metadata", - "version": "0.187.0", + "version": "0.189.0", "description": "ECObjects core concepts in typescript", "license": "MIT", "main": "lib/ecschema-metadata.js", @@ -10,8 +10,9 @@ "url": "https://github.com/imodeljs/imodeljs" }, "scripts": { - "build": "tsc 1>&2", + "build": "tsc 1>&2 && node ./node_modules/@bentley/build-tools/scripts/ignoreargs.js 1>&2", "clean": "rimraf lib package-deps.json", + "extract-api": "node ./node_modules/@bentley/build-tools/scripts/extract-api.js --entry=ecschema-metadata", "lint": "tslint --project . 1>&2", "test": "node ./node_modules/@bentley/build-tools/scripts/test-tsnode.js --testDir=./test/", "docs": "node ./node_modules/@bentley/build-tools/scripts/docs.js --source=./src --includes=../../generated-docs/extract --json=../../generated-docs/core/ecschema-metadata/file.json --tsIndexFile=./ecschema-metadata.ts --onlyJson %TYPEDOC_THEME%", @@ -30,16 +31,15 @@ "url": "http://www.bentley.com" }, "devDependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/build-tools": "0.187.0", - "@bentley/imodeljs-i18n": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/build-tools": "0.189.0", + "@bentley/imodeljs-i18n": "0.189.0", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/glob": "^5.0.35", "@types/mocha": "^5.2.5", - "@types/node": "10.10.3", + "@types/node": "10.12.18", "@types/sinon": "^5.0.5", - "@types/i18next-node-fs-backend": "0.0.30", "chai": "^4.1.2", "chai-as-promised": "^7", "mocha": "^5.2.0", @@ -49,8 +49,7 @@ "ts-node": "^7.0.1", "tslint": "^5.11.0", "typedoc": "^0.11.1", - "typescript": "~3.1.0", - "i18next-node-fs-backend": "2.1.0" + "typescript": "~3.2.2" }, "dependencies": { "glob": "^7.1.2", @@ -58,8 +57,8 @@ "bunyan-seq": "^0.2.0" }, "peerDependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/imodeljs-i18n": "0.187.0" + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/imodeljs-i18n": "0.189.0" }, "nyc": { "nycrc-path": "./node_modules/@bentley/build-tools/.nycrc" diff --git a/core/ecschema-metadata/public/locales/en/ECSchemaMetadata.json b/core/ecschema-metadata/public/locales/en/ECSchemaMetadata.json index 1eb5552..fb855f5 100644 --- a/core/ecschema-metadata/public/locales/en/ECSchemaMetadata.json +++ b/core/ecschema-metadata/public/locales/en/ECSchemaMetadata.json @@ -1,6 +1,17 @@ { "Diagnostics": { - "BaseClassIsSealed": "Class '{0}' cannot derive from sealed base class '{1}'.", - "BaseClassOfDifferentType": "Class '{0}' cannot derive from base class '{1}' of type '{2}'." + "ECObjects:100": "Class '{0}' cannot derive from sealed base class '{1}'.", + "ECObjects:101": "Class '{0}' cannot derive from base class '{1}' of type '{2}'.", + "ECObjects:102": "The ECProperty '{0}:{1}' has a base property '{2}:{1}' with a value type of {3} which is incompatible with the value type of {4}.", + "ECObjects:103": "The ECProperty '{0}:{1}' has a base property '{2}:{1}' with a type of {3} which is incompatible with the type of {4}.", + "ECObjects:104": "The ECProperty '{0}:{1}' has a base property '{2}:{1}' with KindOfQuantity '{3}' with persistence unit '{4}' which is not the same as the persistence unit '{5}' of the provided KindOfQuantity '{6}'.", + "ECObjects:109": "The {0}-Constraint of '{1}' does not contain any constraint classes.", + "ECObjects:110": "The {0}-Constraint of '{1}' has multiple constraint classes which requires an abstract constraint to be defined.", + "ECObjects:112": "Enumeration '{0}' has invalid primitive type.", + "ECObjects:105": "Mixin '{0}' cannot be applied to the class '{1}' because it does not satisfy the applies to constraint '{2}'.", + "ECObjects:111": "The CustomAttribute container '{0}' has a CustomAttribute with the class '{1}' which is not a concrete class.", + "ECObjects:106": "The abstract constraint class '{0}' on the {1}-Constraint of '{2}' is not supported by the base class constraint in '{3}'.", + "ECObjects:107": "The constraint class '{0}' on the {1}-Constraint of '{2}' is not supported by the base class constraint in '{3}'.", + "ECObjects:108": "The constraint class '{0}' on the {1}-Constraint of '{2}' is not derived from the abstract constraint class '{3}'." } } \ No newline at end of file diff --git a/core/ecschema-metadata/src/Deserialization/Helper.ts b/core/ecschema-metadata/src/Deserialization/Helper.ts index d89ca19..ebfdf79 100644 --- a/core/ecschema-metadata/src/Deserialization/Helper.ts +++ b/core/ecschema-metadata/src/Deserialization/Helper.ts @@ -8,7 +8,7 @@ import { SchemaReferenceProps, ClassProps, RelationshipConstraintProps, Property import { SchemaContext } from "./../Context"; import { parsePrimitiveType, parseSchemaItemType, SchemaItemType } from "./../ECObjects"; import { ECObjectsError, ECObjectsStatus } from "./../Exception"; -import { AnyClass, AnySchemaItem, SchemaDeserializationVisitor } from "./../Interfaces"; +import { AnyClass, AnySchemaItem } from "./../Interfaces"; import { ECClass, MutableClass } from "./../Metadata/Class"; import { Constant } from "./../Metadata/Constant"; import { CustomAttribute } from "./../Metadata/CustomAttribute"; @@ -24,6 +24,7 @@ import { SchemaItem } from "./../Metadata/SchemaItem"; import { Unit } from "./../Metadata/Unit"; import { SchemaKey, ECVersion, SchemaItemKey } from "./../SchemaKey"; import { getItemNamesFromFormatString } from "../utils/FormatEnums"; +import { SchemaPartVisitorDelegate, ISchemaPartVisitor } from "../SchemaPartVisitorDelegate"; type AnyCAContainer = Schema | ECClass | Property | RelationshipConstraint; type AnyMutableCAContainer = MutableSchema | MutableClass | MutableProperty | MutableRelationshipConstraint; @@ -35,7 +36,7 @@ type AnyMutableCAContainer = MutableSchema | MutableClass | MutableProperty | Mu */ export class SchemaReadHelper { private _context: SchemaContext; - private _visitor?: SchemaDeserializationVisitor; + private _visitorHelper?: SchemaPartVisitorDelegate; private _parserType: AbstractParserConstructor; private _parser!: AbstractParser; @@ -43,9 +44,9 @@ export class SchemaReadHelper { // to not have to go back to the context every time we use this cache. private _schema?: Schema; - constructor(parserType: AbstractParserConstructor, context?: SchemaContext, visitor?: SchemaDeserializationVisitor) { + constructor(parserType: AbstractParserConstructor, context?: SchemaContext, visitor?: ISchemaPartVisitor) { this._context = (undefined !== context) ? context : new SchemaContext(); - this._visitor = visitor; + this._visitorHelper = visitor ? new SchemaPartVisitorDelegate(visitor) : undefined; this._parserType = parserType; } @@ -55,6 +56,14 @@ export class SchemaReadHelper { * @param rawSchema The serialized data to use to populate the Schema. */ public async readSchema(schema: U, rawSchema: T): Promise { + // Ensure context matches schema context + if (schema.context) { + if (this._context !== schema.context) + throw new ECObjectsError(ECObjectsStatus.DifferentSchemaContexts, "The SchemaContext of the schema must be the same SchemaContext held by the SchemaReadHelper."); + } else { + (schema as Schema as MutableSchema).setContext(this._context); + } + this._parser = new this._parserType(rawSchema); // Loads all of the properties on the Schema object @@ -71,8 +80,8 @@ export class SchemaReadHelper { await this.loadSchemaReference(reference); } - if (this._visitor && this._visitor.visitEmptySchema) - await this._visitor.visitEmptySchema(schema); + if (this._visitorHelper) + await this._visitorHelper.visitSchema(schema, false); // Load all schema items for (const [itemName, itemType, rawItem] of this._parser.getItems()) { @@ -82,15 +91,15 @@ export class SchemaReadHelper { continue; const loadedItem = await this.loadSchemaItem(schema, itemName, itemType, rawItem); - if (loadedItem && this._visitor) { - await loadedItem.accept(this._visitor); + if (loadedItem && this._visitorHelper) { + await this._visitorHelper.visitSchemaPart(loadedItem); } } await this.loadCustomAttributes(schema, this._parser.getSchemaCustomAttributes()); - if (this._visitor && this._visitor.visitFullSchema) - await this._visitor.visitFullSchema(schema); + if (this._visitorHelper) + await this._visitorHelper.visitSchema(schema); return schema; } @@ -117,8 +126,8 @@ export class SchemaReadHelper { this.loadSchemaReferenceSync(reference); } - if (this._visitor && this._visitor.visitEmptySchemaSync) - this._visitor.visitEmptySchemaSync(schema); + if (this._visitorHelper) + this._visitorHelper.visitSchemaSync(schema, false); // Load all schema items for (const [itemName, itemType, rawItem] of this._parser.getItems()) { @@ -128,15 +137,15 @@ export class SchemaReadHelper { continue; const loadedItem = this.loadSchemaItemSync(schema, itemName, itemType, rawItem); - if (loadedItem && this._visitor) { - loadedItem.acceptSync(this._visitor); + if (loadedItem && this._visitorHelper) { + this._visitorHelper.visitSchemaPartSync(loadedItem); } } this.loadCustomAttributesSync(schema, this._parser.getSchemaCustomAttributes()); - if (this._visitor && this._visitor.visitFullSchemaSync) - this._visitor.visitFullSchemaSync(schema); + if (this._visitorHelper) + this._visitorHelper.visitSchemaSync(schema); return schema; } @@ -340,8 +349,8 @@ export class SchemaReadHelper { const foundItem = this._parser.findItem(itemName); if (foundItem) { const schemaItem = await this.loadSchemaItem(this._schema!, ...foundItem); - if (!skipVisitor && schemaItem && this._visitor) { - await schemaItem.accept(this._visitor); + if (!skipVisitor && schemaItem && this._visitorHelper) { + await this._visitorHelper.visitSchemaPart(schemaItem); } return schemaItem; } @@ -372,8 +381,8 @@ export class SchemaReadHelper { const foundItem = this._parser.findItem(itemName); if (foundItem) { const schemaItem = this.loadSchemaItemSync(this._schema!, ...foundItem); - if (!skipVisitor && schemaItem && this._visitor) { - schemaItem.acceptSync(this._visitor); + if (!skipVisitor && schemaItem && this._visitorHelper) { + this._visitorHelper.visitSchemaPartSync(schemaItem); } return schemaItem; } @@ -561,8 +570,8 @@ export class SchemaReadHelper { await this.loadPropertyTypes(classObj, propName, propType, rawProp); } - if (baseClass && this._visitor) - await baseClass.accept(this._visitor); + if (baseClass && this._visitorHelper) + await this._visitorHelper.visitSchemaPart(baseClass); await this.loadCustomAttributes(classObj, this._parser.getClassCustomAttributes(rawClass)); } @@ -588,8 +597,8 @@ export class SchemaReadHelper { this.loadPropertyTypesSync(classObj, propName, propType, rawProp); } - if (baseClass && this._visitor) - baseClass.acceptSync(this._visitor); + if (baseClass && this._visitorHelper) + this._visitorHelper.visitSchemaPartSync(baseClass); this.loadCustomAttributesSync(classObj, this._parser.getClassCustomAttributes(rawClass)); } @@ -638,8 +647,8 @@ export class SchemaReadHelper { const appliesToClass = await this.findSchemaItem(mixinProps.appliesTo, true); await this.loadClass(mixin, mixinProps, rawMixin); - if (appliesToClass && this._visitor) - await appliesToClass.accept(this._visitor); + if (appliesToClass && this._visitorHelper) + await this._visitorHelper.visitSchemaPart(appliesToClass); } /** @@ -652,8 +661,8 @@ export class SchemaReadHelper { const appliesToClass = this.findSchemaItemSync(mixinProps.appliesTo, true); this.loadClassSync(mixin, mixinProps, rawMixin); - if (appliesToClass && this._visitor) - appliesToClass.acceptSync(this._visitor); + if (appliesToClass && this._visitorHelper) + this._visitorHelper.visitSchemaPartSync(appliesToClass); } /** diff --git a/core/ecschema-metadata/src/Deserialization/JsonParser.ts b/core/ecschema-metadata/src/Deserialization/JsonParser.ts index 8ab4335..c7479cf 100644 --- a/core/ecschema-metadata/src/Deserialization/JsonParser.ts +++ b/core/ecschema-metadata/src/Deserialization/JsonParser.ts @@ -173,7 +173,7 @@ export class JsonParser extends AbstractParser { if (!Array.isArray(properties)) throw new ECObjectsError(ECObjectsStatus.InvalidECJson, `The ECClass ${this._currentItemFullName} has an invalid 'properties' attribute. It should be of type 'object[]'.`); - for (const property of properties as Array) { + for (const property of properties as unknown[]) { if (!isObject(property)) throw new ECObjectsError(ECObjectsStatus.InvalidECJson, `An ECProperty in ${this._currentItemFullName} is an invalid JSON object.`); diff --git a/core/ecschema-metadata/src/Deserialization/SchemaFileLocater.ts b/core/ecschema-metadata/src/Deserialization/SchemaFileLocater.ts index f7b03a3..d2f26e3 100644 --- a/core/ecschema-metadata/src/Deserialization/SchemaFileLocater.ts +++ b/core/ecschema-metadata/src/Deserialization/SchemaFileLocater.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { SchemaMatchType } from "./../ECObjects"; +import { SchemaContext } from "./../Context"; import { Schema } from "./../Metadata/Schema"; import { SchemaKey } from "./../SchemaKey"; import * as fs from "fs"; @@ -231,7 +232,7 @@ export abstract class SchemaFileLocater { return foundFiles; } - public abstract getSchema(key: SchemaKey, matchType: SchemaMatchType): Promise; + public abstract getSchema(key: SchemaKey, matchType: SchemaMatchType, context: SchemaContext): Promise; /** * Compares two Schema versions. If the left-hand version is greater, 1 is returned. If the diff --git a/core/ecschema-metadata/src/Deserialization/SchemaJsonFileLocater.ts b/core/ecschema-metadata/src/Deserialization/SchemaJsonFileLocater.ts index 694b612..b47f4ce 100644 --- a/core/ecschema-metadata/src/Deserialization/SchemaJsonFileLocater.ts +++ b/core/ecschema-metadata/src/Deserialization/SchemaJsonFileLocater.ts @@ -45,9 +45,10 @@ export class SchemaJsonFileLocater extends SchemaFileLocater implements ISchemaL * Attempts to retrieve a Schema with the given SchemaKey by using the configured * search paths to locate the JSON schema file from the file system. * @param key The SchemaKey of the Schema to retrieve. - * @param matchType The SchemaMatchType + * @param matchType The SchemaMatchType. + * @param context The SchemaContext that will control the lifetime of the schema and holds the schema's references, if they exist. */ - public async getSchema(schemaKey: SchemaKey, matchType: SchemaMatchType, context?: SchemaContext): Promise { + public async getSchema(schemaKey: SchemaKey, matchType: SchemaMatchType, context: SchemaContext): Promise { // Grab all schema files that match the schema key const candidates: FileSchemaKey[] = this.findEligibleSchemaKeys(schemaKey, matchType, "json"); if (!candidates || candidates.length === 0) @@ -75,8 +76,9 @@ export class SchemaJsonFileLocater extends SchemaFileLocater implements ISchemaL * search paths to locate the JSON schema file from the file system. * @param key The SchemaKey of the Schema to retrieve. * @param matchType The SchemaMatchType + * @param context The SchemaContext that will control the lifetime of the schema. */ - public getSchemaSync(schemaKey: SchemaKey, matchType: SchemaMatchType, context?: SchemaContext): T | undefined { + public getSchemaSync(schemaKey: SchemaKey, matchType: SchemaMatchType, context: SchemaContext): T | undefined { // Grab all schema files that match the schema key const candidates: FileSchemaKey[] = this.findEligibleSchemaKeys(schemaKey, matchType, "json"); if (!candidates || candidates.length === 0) diff --git a/core/ecschema-metadata/src/Deserialization/SchemaXmlFileLocater.ts b/core/ecschema-metadata/src/Deserialization/SchemaXmlFileLocater.ts index 6fd1e02..eb3447f 100644 --- a/core/ecschema-metadata/src/Deserialization/SchemaXmlFileLocater.ts +++ b/core/ecschema-metadata/src/Deserialization/SchemaXmlFileLocater.ts @@ -31,8 +31,9 @@ export class SchemaXmlFileLocater extends SchemaFileLocater implements ISchemaLo * their keys populated. * @param key The SchemaKey of the Schema to retrieve. * @param matchType The SchemaMatchType. + * @param context The SchemaContext that will control the lifetime of the schema. */ - public async getSchema(key: SchemaKey, matchType: SchemaMatchType, context?: SchemaContext): Promise { + public async getSchema(key: SchemaKey, matchType: SchemaMatchType, context: SchemaContext): Promise { const candidates: FileSchemaKey[] = this.findEligibleSchemaKeys(key, matchType, "xml"); if (!candidates || candidates.length === 0) @@ -50,9 +51,8 @@ export class SchemaXmlFileLocater extends SchemaFileLocater implements ISchemaLo }); */ - const schema = new Schema(maxCandidate) as T; - if (context) - await context.addSchema(schema); + const schema = new Schema(context, maxCandidate) as T; + await context.addSchema(schema); await this.addSchemaReferences(schema, context); return schema; } @@ -63,17 +63,17 @@ export class SchemaXmlFileLocater extends SchemaFileLocater implements ISchemaLo * their keys populated. * @param key The SchemaKey of the Schema to retrieve. * @param matchType The SchemaMatchType. + * @param context The SchemaContext that will control the lifetime of the schema. */ - public getSchemaSync(key: SchemaKey, matchType: SchemaMatchType, context?: SchemaContext): T | undefined { + public getSchemaSync(key: SchemaKey, matchType: SchemaMatchType, context: SchemaContext): T | undefined { const candidates: FileSchemaKey[] = this.findEligibleSchemaKeys(key, matchType, "xml"); if (!candidates || candidates.length === 0) return undefined; const maxCandidate = candidates.sort(this.compareSchemaKeyByVersion)[candidates.length - 1]; - const schema = new Schema(maxCandidate) as T; - if (context) - context.addSchemaSync(schema); + const schema = new Schema(context, maxCandidate) as T; + context.addSchemaSync(schema); this.addSchemaReferencesSync(schema, context); return schema; @@ -134,8 +134,9 @@ export class SchemaXmlFileLocater extends SchemaFileLocater implements ISchemaLo /** * Loads a Schema from disk as a Promise. * @param schemaPath The path to the Schema file. + * @param context The SchemaContext that will control the lifetime of the schema. */ - public async loadSchema(schemaPath: string): Promise { + public async loadSchema(schemaPath: string, context: SchemaContext): Promise { // Load the file const schemaText = await this.readUtf8FileToString(schemaPath); @@ -149,7 +150,7 @@ export class SchemaXmlFileLocater extends SchemaFileLocater implements ISchemaLo // TODO - bad path // Load the schema and return it - const schema = new Schema(new FileSchemaKey(key, schemaPath, schemaText)); + const schema = new Schema(context, new FileSchemaKey(key, schemaPath, schemaText)); await this.addSchemaReferences(schema); return schema as T; } diff --git a/core/ecschema-metadata/src/Exception.ts b/core/ecschema-metadata/src/Exception.ts index 48da56d..73da071 100644 --- a/core/ecschema-metadata/src/Exception.ts +++ b/core/ecschema-metadata/src/Exception.ts @@ -29,6 +29,9 @@ export const enum ECObjectsStatus { UnableToLocateSchema = ECOBJECTS_ERROR_BASE + 19, InvalidSchemaXML = ECOBJECTS_ERROR_BASE + 20, InvalidSchemaString = ECOBJECTS_ERROR_BASE + 21, + ClassNotFound = ECOBJECTS_ERROR_BASE + 22, + SchemaContextUndefined = ECOBJECTS_ERROR_BASE + 23, + DifferentSchemaContexts = ECOBJECTS_ERROR_BASE + 24, } export class ECObjectsError extends BentleyError { @@ -58,6 +61,9 @@ export class ECObjectsError extends BentleyError { case ECObjectsStatus.InvalidType: return this._appendMessage("ECObjectsStatus.InvalidType"); case ECObjectsStatus.MissingSchemaUrl: return this._appendMessage("ECObjectsStatus.MissingSchemaUrl"); case ECObjectsStatus.UnableToLocateSchema: return this._appendMessage("ECObjectsStatus.UnableToLocateSchema"); + case ECObjectsStatus.ClassNotFound: return this._appendMessage("ECObjectsStatus.ClassNotFound"); + case ECObjectsStatus.SchemaContextUndefined: return this._appendMessage("ECObjectsStatus.SchemaContextUndefined"); + case ECObjectsStatus.DifferentSchemaContexts: return this._appendMessage("ECObjectsStatus.DifferentSchemaContexts"); default: assert(false); /* istanbul ignore next */ diff --git a/core/ecschema-metadata/src/Interfaces.ts b/core/ecschema-metadata/src/Interfaces.ts index b37cd0f..3552e69 100644 --- a/core/ecschema-metadata/src/Interfaces.ts +++ b/core/ecschema-metadata/src/Interfaces.ts @@ -14,13 +14,15 @@ import { KindOfQuantity } from "./Metadata/KindOfQuantity"; import { Mixin } from "./Metadata/Mixin"; import { Phenomenon } from "./Metadata/Phenomenon"; import { PropertyCategory } from "./Metadata/PropertyCategory"; -import { RelationshipClass } from "./Metadata/RelationshipClass"; +import { RelationshipClass, RelationshipConstraint } from "./Metadata/RelationshipClass"; import { Schema } from "./Metadata/Schema"; import { SchemaItem } from "./Metadata/SchemaItem"; import { Unit } from "./Metadata/Unit"; import { UnitSystem } from "./Metadata/UnitSystem"; import { Format } from "./Metadata/Format"; import { SchemaKey, SchemaItemKey } from "./SchemaKey"; +import { AnyProperty } from "./Metadata/Property"; +import { CustomAttributeContainerProps, CustomAttribute } from "./Metadata/CustomAttribute"; export type LazyLoadedSchema = Readonly & DelayedPromise & Promise; @@ -44,105 +46,4 @@ export type LazyLoadedFormat = LazyLoadedSchemaItem; export type AnyClass = EntityClass | Mixin | StructClass | CustomAttributeClass | RelationshipClass; export type AnySchemaItem = AnyClass | Enumeration | KindOfQuantity | PropertyCategory | Unit | InvertedUnit | Constant | Phenomenon | UnitSystem | Format; - -export interface SchemaItemVisitor { - /* async */ visitFormat?: (format: Format) => Promise; - /* async */ visitUnitSystem?: (unitSystem: UnitSystem) => Promise; - /* async */ visitPhenomenon?: (phenomenon: Phenomenon) => Promise; - /* async */ visitConstant?: (constant: Constant) => Promise; - /* async */ visitInvertedUnit?: (invertedUnit: InvertedUnit) => Promise; - /* async */ visitEnumeration?: (enumeration: Enumeration) => Promise; - /* async */ visitUnit?: (unit: Unit) => Promise; - /* async */ visitKindOfQuantity?: (koq: KindOfQuantity) => Promise; - /* async */ visitPropertyCategory?: (category: PropertyCategory) => Promise; - /* async */ visitClass?: (ecClass: AnyClass) => Promise; - - visitFormatSync?: (format: Format) => void; - visitUnitSystemSync?: (unitSystem: UnitSystem) => void; - visitPhenomenonSync?: (phenomenon: Phenomenon) => void; - visitConstantSync?: (constant: Constant) => void; - visitInvertedUnitSync?: (invertedUnit: InvertedUnit) => void; - visitEnumerationSync?: (enumeration: Enumeration) => void; - visitUnitSync?: (unit: Unit) => void; - visitKindOfQuantitySync?: (koq: KindOfQuantity) => void; - visitPropertyCategorySync?: (category: PropertyCategory) => void; - visitClassSync?: (ecClass: AnyClass) => void; -} - -export interface SchemaDeserializationVisitor extends SchemaItemVisitor { - /** - * Called after a schema and all its references are deserialized, - * but _before_ any of its items or custom attributes have been deserialized. - * @param schema a partially-loaded Schema - */ - /* async */ visitEmptySchema?: (schema: Schema) => Promise; - - /** - * Called after an Enumeration and all its Enumerators have been deserialized. - * @param enumeration a fully-loaded Enumeration - */ - /* async */ visitEnumeration?: (enumeration: Enumeration) => Promise; - - /** - * Called after a KindOfQuantity has been deserialized. - * @param koq a fully-loaded KindOfQuantity - */ - /* async */ visitKindOfQuantity?: (koq: KindOfQuantity) => Promise; - - /** - * Called after a PropertyCategory has been deserialized. - * @param category a fully-loaded PropertyCategory - */ - /* async */ visitPropertyCategory?: (category: PropertyCategory) => Promise; - - /** - * Called after an ECClass and its baseClass, properties, and custom attributes have been deserialized. - * @param ecClass a fully-loaded ECClass - */ - /* async */ visitClass?: (ecClass: AnyClass) => Promise; - - /** - * Called after a schema and all its references, items, and custom attributes have been deserialized, - * @param schema a fully-loaded Schema - */ - /* async */ visitFullSchema?: (schema: Schema) => Promise; - - /** - * Called after a Unit has been deserialized. - * @param unit a fully-loaded Unit - */ - /* async */ visitUnit?: (unit: Unit) => Promise; - - /** - * Called after an Inverted Unit has been deserialized. - * @param invertedUnit a fully-loaded InvertedUnit - */ - /* async */ visitInvertedUnit?: (invertedUnit: InvertedUnit) => Promise; - - /** - * Called after a Constant has been deserialized. - * @param constant a fully-loaded Constant - */ - /* async */ visitConstant?: (constant: Constant) => Promise; - - /** - * Called after a Phenomenon has been deserialized. - * @param phenomena fully-loaded Phenomenon - */ - /* async */ visitPhenomenon?: (phenomena: Phenomenon) => Promise; - - /** - * Called after a UnitSystem has been deserialized. - * @param unitSystem fully-loaded UnitSystem - */ - /* async */ visitUnitSystem?: (unitSystem: UnitSystem) => Promise; - - /** - * Called after a Format has been deserialized. - * @param format fully-loaded Format - */ - /* async */ visitFormat?: (format: Format) => Promise; - - visitEmptySchemaSync?: (schema: Schema) => void; - visitFullSchemaSync?: (schema: Schema) => void; -} +export type AnyECType = Schema | SchemaItem | AnyProperty | RelationshipConstraint | CustomAttributeContainerProps | CustomAttribute; diff --git a/core/ecschema-metadata/src/Metadata/Class.ts b/core/ecschema-metadata/src/Metadata/Class.ts index 348b32c..112ef33 100644 --- a/core/ecschema-metadata/src/Metadata/Class.ts +++ b/core/ecschema-metadata/src/Metadata/Class.ts @@ -19,8 +19,9 @@ import { parseClassModifier, parsePrimitiveType, PrimitiveType, SchemaItemType, } from "./../ECObjects"; import { ECObjectsError, ECObjectsStatus } from "./../Exception"; -import { AnyClass, LazyLoadedECClass, SchemaItemVisitor } from "./../Interfaces"; -import { SchemaItemKey } from "./../SchemaKey"; +import { AnyClass, LazyLoadedECClass } from "./../Interfaces"; +import { SchemaItemKey, SchemaKey } from "./../SchemaKey"; +import { assert } from "@bentley/bentleyjs-core"; /** * A common abstract class for all of the ECClass types. @@ -329,15 +330,15 @@ export abstract class ECClass extends SchemaItem implements CustomAttributeConta public toJson(standalone: boolean, includeSchemaVersion: boolean) { const schemaJson = super.toJson(standalone, includeSchemaVersion); - schemaJson.modifier = classModifierToString(this.modifier); + const isMixin = SchemaItemType.Mixin === this.schemaItemType; + const isRelationship = SchemaItemType.RelationshipClass === this.schemaItemType; + if (!isMixin && (ECClassModifier.None !== this.modifier || isRelationship)) + schemaJson.modifier = classModifierToString(this.modifier); if (this.baseClass !== undefined) schemaJson.baseClass = this.baseClass.fullName; - if (this.properties !== undefined && this.properties.length > 0) { - schemaJson.properties = []; - this.properties.forEach((prop: Property) => { - schemaJson.properties.push(prop.toJson()); - }); - } + if (this.properties !== undefined && this.properties.length > 0) + schemaJson.properties = this.properties.map((prop) => prop.toJson()); + const customAttributes = serializeCustomAttributes(this.customAttributes); if (customAttributes !== undefined) schemaJson.customAttributes = customAttributes; @@ -378,16 +379,6 @@ export abstract class ECClass extends SchemaItem implements CustomAttributeConta this._customAttributes.set(customAttribute.className, customAttribute); } - public async accept(visitor: SchemaItemVisitor) { - if (visitor.visitClass) - await visitor.visitClass(this as AnyClass); - } - - public acceptSync(visitor: SchemaItemVisitor) { - if (visitor.visitClassSync) - visitor.visitClassSync(this as AnyClass); - } - /** * Iterates (recursively) over all base classes and mixins, in "property override" order. * This is essentially a depth-first traversal through the inheritance tree. @@ -545,13 +536,28 @@ export abstract class ECClass extends SchemaItem implements CustomAttributeConta /** * Indicates if the targetClass is of this type. - * @param targetClass The class to check. + * @param targetClass The ECClass or ECClass name to check. + * @param schemaName The schema name. Required if targetClass is the ECClass name. */ - public async is(targetClass: ECClass): Promise { - if (SchemaItem.equalByKey(this, targetClass)) - return true; + public async is(targetClass: string, schemaName: string): Promise; + public async is(targetClass: ECClass): Promise; + public async is(targetClass: ECClass | string, schemaName?: string): Promise { + if (schemaName !== undefined) { + assert(typeof (targetClass) === "string", "Expected targetClass of type string because schemaName was specified"); + + const key = new SchemaItemKey(targetClass as string, new SchemaKey(schemaName)); + if (SchemaItem.equalByKey(this, key)) + return true; + + return this.traverseBaseClasses(SchemaItem.equalByKey, key); + } else { + assert(targetClass instanceof ECClass, "Expected targetClass to be of type ECClass"); - return this.traverseBaseClasses(SchemaItem.equalByKey, targetClass); + if (SchemaItem.equalByKey(this, targetClass as ECClass)) + return true; + + return this.traverseBaseClasses(SchemaItem.equalByKey, targetClass); + } } /** diff --git a/core/ecschema-metadata/src/Metadata/Constant.ts b/core/ecschema-metadata/src/Metadata/Constant.ts index 1be57d6..dcab6e3 100644 --- a/core/ecschema-metadata/src/Metadata/Constant.ts +++ b/core/ecschema-metadata/src/Metadata/Constant.ts @@ -10,7 +10,7 @@ import { DelayedPromiseWithProps } from "./../DelayedPromise"; import { ConstantProps } from "./../Deserialization/JsonProps"; import { SchemaItemType } from "./../ECObjects"; import { ECObjectsError, ECObjectsStatus } from "./../Exception"; -import { LazyLoadedPhenomenon, SchemaItemVisitor } from "./../Interfaces"; +import { LazyLoadedPhenomenon } from "./../Interfaces"; import { SchemaItemKey } from "./../SchemaKey"; /** @@ -80,14 +80,4 @@ export class Constant extends SchemaItem { public async deserialize(constantProps: ConstantProps) { this.deserializeSync(constantProps); } - - public async accept(visitor: SchemaItemVisitor) { - if (visitor.visitConstant) - await visitor.visitConstant(this); - } - - public acceptSync(visitor: SchemaItemVisitor) { - if (visitor.visitConstantSync) - visitor.visitConstantSync(this); - } } diff --git a/core/ecschema-metadata/src/Metadata/CustomAttribute.ts b/core/ecschema-metadata/src/Metadata/CustomAttribute.ts index 692cf42..daf63e4 100644 --- a/core/ecschema-metadata/src/Metadata/CustomAttribute.ts +++ b/core/ecschema-metadata/src/Metadata/CustomAttribute.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ +import { Schema } from "./Schema"; + export interface CustomAttribute { className: string; [propName: string]: any; @@ -16,6 +18,8 @@ export interface CustomAttributeSet { export interface CustomAttributeContainerProps { customAttributes?: CustomAttributeSet; + fullName: string; + schema: Schema; } export function serializeCustomAttributes(customAttributes: CustomAttributeSet | undefined): any[] | undefined { @@ -29,7 +33,7 @@ export function serializeCustomAttributes(customAttributes: CustomAttributeSet | }); attributes.push(attribute); } - return attributes; + return (attributes.length > 0) ? attributes : undefined; } return undefined; } diff --git a/core/ecschema-metadata/src/Metadata/EntityClass.ts b/core/ecschema-metadata/src/Metadata/EntityClass.ts index 38d9cd7..ec11b8e 100644 --- a/core/ecschema-metadata/src/Metadata/EntityClass.ts +++ b/core/ecschema-metadata/src/Metadata/EntityClass.ts @@ -162,12 +162,8 @@ export class EntityClass extends ECClass { public toJson(standalone: boolean, includeSchemaVersion: boolean): any | void { const schemaJson = super.toJson(standalone, includeSchemaVersion); - if (this.mixins.length > 0) { - schemaJson.mixins = []; - this.mixins.forEach(async (mixin: LazyLoadedMixin) => { - schemaJson.mixins.push(mixin.fullName); - }); - } + if (this.mixins.length > 0) + schemaJson.mixins = this.mixins.map((mixin) => mixin.fullName); return schemaJson; } diff --git a/core/ecschema-metadata/src/Metadata/Enumeration.ts b/core/ecschema-metadata/src/Metadata/Enumeration.ts index 5c409d8..be5d2b1 100644 --- a/core/ecschema-metadata/src/Metadata/Enumeration.ts +++ b/core/ecschema-metadata/src/Metadata/Enumeration.ts @@ -8,7 +8,6 @@ import { SchemaItem } from "./SchemaItem"; import { EnumerationProps, EnumeratorProps } from "./../Deserialization/JsonProps"; import { PrimitiveType, SchemaItemType } from "./../ECObjects"; import { ECObjectsError, ECObjectsStatus } from "./../Exception"; -import { SchemaItemVisitor } from "./../Interfaces"; import { ECName } from "./../SchemaKey"; export interface Enumerator { @@ -107,16 +106,13 @@ export class Enumeration extends SchemaItem { const schemaJson = super.toJson(standalone, includeSchemaVersion); schemaJson.type = (this.isInt) ? "int" : "string"; schemaJson.isStrict = this.isStrict; - schemaJson.enumerators = []; - this._enumerators.forEach((element: AnyEnumerator) => { - const enumJson: any = {}; - enumJson.name = element.name; - enumJson.value = element.value; - if (undefined !== element.label) - enumJson.label = element.label; - if (undefined !== element.description) - enumJson.description = element.description; - schemaJson.enumerators.push(enumJson); + schemaJson.enumerators = this._enumerators.map(({ name, label, value, description }) => { + const enumJson: any = { name, value }; + if (undefined !== label) + enumJson.label = label; + if (undefined !== description) + enumJson.description = description; + return enumJson; }); return schemaJson; } @@ -149,16 +145,6 @@ export class Enumeration extends SchemaItem { public async deserialize(enumerationProps: EnumerationProps) { this.deserializeSync(enumerationProps); } - - public async accept(visitor: SchemaItemVisitor) { - if (visitor.visitEnumeration) - await visitor.visitEnumeration(this); - } - - public acceptSync(visitor: SchemaItemVisitor) { - if (visitor.visitEnumerationSync) - visitor.visitEnumerationSync(this); - } } /** @hidden */ diff --git a/core/ecschema-metadata/src/Metadata/Format.ts b/core/ecschema-metadata/src/Metadata/Format.ts index 134b92b..9ecc7a0 100644 --- a/core/ecschema-metadata/src/Metadata/Format.ts +++ b/core/ecschema-metadata/src/Metadata/Format.ts @@ -10,33 +10,13 @@ import { Unit } from "./Unit"; import { FormatProps } from "./../Deserialization/JsonProps"; import { SchemaItemType } from "./../ECObjects"; import { ECObjectsError, ECObjectsStatus } from "./../Exception"; -import { SchemaItemVisitor } from "./../Interfaces"; import { DecimalPrecision, FormatTraits, formatTraitsToArray, FormatType, formatTypeToString, FractionalPrecision, parseFormatTrait, parseFormatType, parsePrecision, parseScientificType, parseShowSignOption, ScientificType, scientificTypeToString, ShowSignOption, showSignOptionToString, } from "./../utils/FormatEnums"; -export interface IFormat { - readonly name: string; - readonly roundFactor: number; - readonly type: FormatType; - readonly precision: DecimalPrecision | FractionalPrecision; - readonly minWidth: number | undefined; - readonly formatTraits: FormatTraits; - readonly showSignOption: ShowSignOption; - readonly decimalSeparator: string; - readonly thousandSeparator: string; - readonly uomSeparator: string; - readonly scientificType?: ScientificType; - readonly stationSeparator?: string; - readonly stationOffsetSize?: number; - readonly spacer?: string; - readonly includeZero?: boolean; - readonly units?: Array<[Unit | InvertedUnit, string | undefined]>; -} - -export class Format extends SchemaItem implements IFormat { +export class Format extends SchemaItem { public readonly schemaItemType!: SchemaItemType.Format; // tslint:disable-line protected _roundFactor: number; protected _type: FormatType; // required; options are decimal, frational, scientific, station @@ -288,14 +268,4 @@ export class Format extends SchemaItem implements IFormat { return schemaJson; } - - public async accept(visitor: SchemaItemVisitor) { - if (visitor.visitFormat) - await visitor.visitFormat(this); - } - - public acceptSync(visitor: SchemaItemVisitor) { - if (visitor.visitFormatSync) - visitor.visitFormatSync(this); - } } diff --git a/core/ecschema-metadata/src/Metadata/InvertedUnit.ts b/core/ecschema-metadata/src/Metadata/InvertedUnit.ts index b909319..6f8c24a 100644 --- a/core/ecschema-metadata/src/Metadata/InvertedUnit.ts +++ b/core/ecschema-metadata/src/Metadata/InvertedUnit.ts @@ -11,7 +11,7 @@ import { DelayedPromiseWithProps } from "./../DelayedPromise"; import { SchemaItemType } from "./../ECObjects"; import { InvertedUnitProps } from "./../Deserialization/JsonProps"; import { ECObjectsError, ECObjectsStatus } from "./../Exception"; -import { LazyLoadedUnit, LazyLoadedUnitSystem, SchemaItemVisitor } from "./../Interfaces"; +import { LazyLoadedUnit, LazyLoadedUnitSystem } from "./../Interfaces"; import { SchemaItemKey } from "./../SchemaKey"; /** @@ -63,14 +63,4 @@ export class InvertedUnit extends SchemaItem { public async deserialize(invertedUnitProps: InvertedUnitProps) { this.deserializeSync(invertedUnitProps); } - - public async accept(visitor: SchemaItemVisitor) { - if (visitor.visitInvertedUnit) - await visitor.visitInvertedUnit(this); - } - - public acceptSync(visitor: SchemaItemVisitor) { - if (visitor.visitInvertedUnitSync) - visitor.visitInvertedUnitSync(this); - } } diff --git a/core/ecschema-metadata/src/Metadata/KindOfQuantity.ts b/core/ecschema-metadata/src/Metadata/KindOfQuantity.ts index cdabc4a..e86087a 100644 --- a/core/ecschema-metadata/src/Metadata/KindOfQuantity.ts +++ b/core/ecschema-metadata/src/Metadata/KindOfQuantity.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Format, IFormat } from "./Format"; +import { Format } from "./Format"; import { InvertedUnit } from "./InvertedUnit"; import { OverrideFormat } from "./OverrideFormat"; import { Schema } from "./Schema"; @@ -13,7 +13,7 @@ import { DelayedPromiseWithProps } from "./../DelayedPromise"; import { KindOfQuantityProps } from "./../Deserialization/JsonProps"; import { SchemaItemType } from "./../ECObjects"; import { ECObjectsError, ECObjectsStatus } from "./../Exception"; -import { LazyLoadedInvertedUnit, LazyLoadedUnit, SchemaItemVisitor } from "./../Interfaces"; +import { LazyLoadedInvertedUnit, LazyLoadedUnit } from "./../Interfaces"; import { formatStringRgx } from "./../utils/FormatEnums"; /** @@ -215,12 +215,8 @@ export class KindOfQuantity extends SchemaItem { const schemaJson = super.toJson(standalone, includeSchemaVersion); schemaJson.relativeError = this.relativeError; schemaJson.persistenceUnit = this.persistenceUnit!.fullName; - if (this.presentationUnits !== undefined) { - schemaJson.presentationUnits = []; - this.presentationUnits.forEach((unit: IFormat) => { - schemaJson.presentationUnits.push(unit.name); - }); - } + if (this.presentationUnits !== undefined && this.presentationUnits.length > 0) + schemaJson.presentationUnits = this.presentationUnits.map((unit) => unit.fullName); return schemaJson; } @@ -251,13 +247,4 @@ export class KindOfQuantity extends SchemaItem { if (kindOfQuantityProps.presentationUnits) await this.processPresentationUnits(kindOfQuantityProps.presentationUnits); } - - public async accept(visitor: SchemaItemVisitor) { - if (visitor.visitKindOfQuantity) - await visitor.visitKindOfQuantity((this as any) as KindOfQuantity); - } - public acceptSync(visitor: SchemaItemVisitor) { - if (visitor.visitKindOfQuantitySync) - visitor.visitKindOfQuantitySync((this as any) as KindOfQuantity); - } } diff --git a/core/ecschema-metadata/src/Metadata/OverrideFormat.ts b/core/ecschema-metadata/src/Metadata/OverrideFormat.ts index bb60ca0..f26d7d6 100644 --- a/core/ecschema-metadata/src/Metadata/OverrideFormat.ts +++ b/core/ecschema-metadata/src/Metadata/OverrideFormat.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Format, IFormat } from "./Format"; +import { Format } from "./Format"; import { InvertedUnit } from "./InvertedUnit"; import { Unit } from "./Unit"; import { DecimalPrecision, FormatTraits, FormatType, FractionalPrecision, ScientificType, ShowSignOption } from "./../utils/FormatEnums"; @@ -11,7 +11,7 @@ import { DecimalPrecision, FormatTraits, FormatType, FractionalPrecision, Scient /** * Overrides of a Format, from a Schema, and is SchemaItem that is used specifically on KindOfQuantity. */ -export class OverrideFormat implements IFormat { +export class OverrideFormat { private _precision?: DecimalPrecision | FractionalPrecision; private _units?: Array<[Unit | InvertedUnit, string | undefined]>; @@ -36,6 +36,7 @@ export class OverrideFormat implements IFormat { get units() { return (undefined === this._units) ? this.parent.units : this._units; } // Properties that cannot be overriden + get fullName(): string { return this.name; } get roundFactor(): number { return this.parent.roundFactor; } get type(): FormatType { return this.parent.type; } get minWidth(): number | undefined { return this.parent.minWidth; } diff --git a/core/ecschema-metadata/src/Metadata/Phenomenon.ts b/core/ecschema-metadata/src/Metadata/Phenomenon.ts index cb914b8..36912bd 100644 --- a/core/ecschema-metadata/src/Metadata/Phenomenon.ts +++ b/core/ecschema-metadata/src/Metadata/Phenomenon.ts @@ -8,7 +8,6 @@ import { SchemaItem } from "./SchemaItem"; import { PhenomenonProps } from "./../Deserialization/JsonProps"; import { SchemaItemType } from "./../ECObjects"; import { ECObjectsError, ECObjectsStatus } from "./../Exception"; -import { SchemaItemVisitor } from "./../Interfaces"; export class Phenomenon extends SchemaItem { public readonly schemaItemType!: SchemaItemType.Phenomenon; // tslint:disable-line @@ -28,16 +27,6 @@ export class Phenomenon extends SchemaItem { return schemaJson; } - public async accept(visitor: SchemaItemVisitor) { - if (visitor.visitPhenomenon) - await visitor.visitPhenomenon(this); - } - - public acceptSync(visitor: SchemaItemVisitor) { - if (visitor.visitPhenomenonSync) - visitor.visitPhenomenonSync(this); - } - public deserializeSync(phenomenonProps: PhenomenonProps) { super.deserializeSync(phenomenonProps); if (this._definition !== "" && phenomenonProps.definition.toLowerCase() !== this._definition.toLowerCase()) diff --git a/core/ecschema-metadata/src/Metadata/Property.ts b/core/ecschema-metadata/src/Metadata/Property.ts index bb7ee38..054de85 100644 --- a/core/ecschema-metadata/src/Metadata/Property.ts +++ b/core/ecschema-metadata/src/Metadata/Property.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ECClass, StructClass } from "./Class"; -import { CustomAttributeSet, serializeCustomAttributes, CustomAttribute } from "./CustomAttribute"; +import { CustomAttributeSet, serializeCustomAttributes, CustomAttribute, CustomAttributeContainerProps } from "./CustomAttribute"; import { Enumeration } from "./Enumeration"; import { KindOfQuantity } from "./KindOfQuantity"; import { PropertyCategory } from "./PropertyCategory"; @@ -19,19 +19,20 @@ import { ECObjectsError, ECObjectsStatus } from "./../Exception"; import { AnyClass, LazyLoadedEnumeration, LazyLoadedKindOfQuantity, LazyLoadedPropertyCategory, LazyLoadedRelationshipClass } from "./../Interfaces"; import { PropertyType, propertyTypeToString, PropertyTypeUtils } from "./../PropertyTypes"; import { ECName, SchemaItemKey } from "./../SchemaKey"; +import { Schema } from "./Schema"; /** * A common abstract class for all ECProperty types. */ -export abstract class Property { +export abstract class Property implements CustomAttributeContainerProps { protected _name: ECName; protected _type: PropertyType; protected _class: AnyClass; // TODO: class seems to be unused? protected _description?: string; protected _label?: string; - protected _isReadOnly: boolean; - protected _priority: number; + protected _isReadOnly?: boolean; + protected _priority?: number; protected _category?: LazyLoadedPropertyCategory; protected _kindOfQuantity?: LazyLoadedKindOfQuantity; private _customAttributes?: Map; @@ -40,8 +41,6 @@ export abstract class Property { this._class = ecClass as AnyClass; this._name = new ECName(name); this._type = type; - this._isReadOnly = false; - this._priority = 0; } public isArray(): this is AnyArrayProperty { return PropertyTypeUtils.isArray(this._type); } @@ -58,9 +57,9 @@ export abstract class Property { get description() { return this._description; } - get isReadOnly() { return this._isReadOnly; } + get isReadOnly() { return this._isReadOnly || false; } - get priority() { return this._priority; } + get priority() { return this._priority || 0; } get category(): LazyLoadedPropertyCategory | undefined { return this._category; } @@ -68,6 +67,12 @@ export abstract class Property { get customAttributes(): CustomAttributeSet | undefined { return this._customAttributes; } + /** Returns the name in the format 'ClassName.PropertyName'. */ + get fullName(): string { return this._class.name + "." + name; } + + /** Returns the schema of the class holding the property. */ + get schema(): Schema { return this._class.schema; } + public getCategorySync(): PropertyCategory | undefined { if (!this._category) return undefined; @@ -90,11 +95,14 @@ export abstract class Property { schemaJson.description = this.description; if (this.label !== undefined) schemaJson.label = this.label; - schemaJson.isReadOnly = this.isReadOnly; + if (this._isReadOnly !== undefined) + schemaJson.isReadOnly = this._isReadOnly; if (this.category !== undefined) schemaJson.category = this.category.fullName; // needs to be fully qualified name - if (this.priority !== undefined) - schemaJson.priority = this.priority; + if (this._priority !== undefined) + schemaJson.priority = this._priority; + if (this.kindOfQuantity !== undefined) + schemaJson.kindOfQuantity = this.kindOfQuantity.fullName; const customAttributes = serializeCustomAttributes(this.customAttributes); if (customAttributes !== undefined) schemaJson.customAttributes = customAttributes; diff --git a/core/ecschema-metadata/src/Metadata/PropertyCategory.ts b/core/ecschema-metadata/src/Metadata/PropertyCategory.ts index 980e732..b800f5d 100644 --- a/core/ecschema-metadata/src/Metadata/PropertyCategory.ts +++ b/core/ecschema-metadata/src/Metadata/PropertyCategory.ts @@ -7,7 +7,6 @@ import { Schema } from "./Schema"; import { SchemaItem } from "./SchemaItem"; import { PropertyCategoryProps } from "./../Deserialization/JsonProps"; import { SchemaItemType } from "./../ECObjects"; -import { SchemaItemVisitor } from "./../Interfaces"; export class PropertyCategory extends SchemaItem { public readonly schemaItemType!: SchemaItemType.PropertyCategory; // tslint:disable-line @@ -35,14 +34,4 @@ export class PropertyCategory extends SchemaItem { public async deserialize(propertyCategoryProps: PropertyCategoryProps) { this.deserializeSync(propertyCategoryProps); } - - public async accept(visitor: SchemaItemVisitor) { - if (visitor.visitPropertyCategory) - await visitor.visitPropertyCategory(this); - } - - public acceptSync(visitor: SchemaItemVisitor) { - if (visitor.visitPropertyCategorySync) - visitor.visitPropertyCategorySync(this); - } } diff --git a/core/ecschema-metadata/src/Metadata/RelationshipClass.ts b/core/ecschema-metadata/src/Metadata/RelationshipClass.ts index a6e6b7c..8fa8915 100644 --- a/core/ecschema-metadata/src/Metadata/RelationshipClass.ts +++ b/core/ecschema-metadata/src/Metadata/RelationshipClass.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ECClass } from "./Class"; -import { CustomAttributeSet, serializeCustomAttributes, CustomAttribute } from "./CustomAttribute"; +import { CustomAttributeContainerProps, CustomAttributeSet, serializeCustomAttributes, CustomAttribute } from "./CustomAttribute"; import { EntityClass, createNavigationProperty, createNavigationPropertySync } from "./EntityClass"; import { Mixin } from "./Mixin"; import { NavigationProperty } from "./Property"; @@ -96,7 +96,7 @@ export class RelationshipClass extends ECClass { /** * A Typescript class representation of a ECRelationshipConstraint. */ -export class RelationshipConstraint { +export class RelationshipConstraint implements CustomAttributeContainerProps { protected _abstractConstraint?: LazyLoadedRelationshipConstraintClass; protected _relationshipClass: RelationshipClass; protected _relationshipEnd: RelationshipEnd; @@ -126,6 +126,12 @@ export class RelationshipConstraint { get relationshipEnd() { return this._relationshipEnd; } get customAttributes(): CustomAttributeSet | undefined { return this._customAttributes; } + /** Returns the constraint name, ie. 'RelationshipName.Source/Target' */ + get fullName() { return this._relationshipClass.name + ":" + this.isSource ? "Source" : "Target"; } + + /** Returns the schema of the RelationshipClass. */ + get schema(): Schema { return this._relationshipClass.schema; } + get abstractConstraint(): LazyLoadedRelationshipConstraintClass | undefined { if (this._abstractConstraint) return this._abstractConstraint; @@ -165,15 +171,11 @@ export class RelationshipConstraint { schemaJson.multiplicity = this.multiplicity!.toString(); schemaJson.roleLabel = this.roleLabel; schemaJson.polymorphic = this.polymorphic; - if (undefined !== this.abstractConstraint) { - schemaJson.abstractConstraint = this.abstractConstraint.fullName; - } - if (this.constraintClasses !== undefined && this.constraintClasses.length > 0) { - schemaJson.constraintClasses = []; - this.constraintClasses.forEach(async (constraintClass: LazyLoadedRelationshipConstraintClass) => { - schemaJson.constraintClasses.push(constraintClass.fullName); - }); - } + if (undefined !== this._abstractConstraint) + schemaJson.abstractConstraint = this._abstractConstraint.fullName; + if (undefined !== this.constraintClasses && this.constraintClasses.length > 0) + schemaJson.constraintClasses = this.constraintClasses.map((constraintClass) => constraintClass.fullName); + const customAttributes = serializeCustomAttributes(this.customAttributes); if (undefined !== customAttributes) schemaJson.customAttributes = customAttributes; diff --git a/core/ecschema-metadata/src/Metadata/Schema.ts b/core/ecschema-metadata/src/Metadata/Schema.ts index 8586197..1b003f2 100644 --- a/core/ecschema-metadata/src/Metadata/Schema.ts +++ b/core/ecschema-metadata/src/Metadata/Schema.ts @@ -28,46 +28,47 @@ import { ECObjectsError, ECObjectsStatus } from "./../Exception"; import { AnyClass, AnySchemaItem } from "./../Interfaces"; import { SchemaKey, ECVersion, SchemaItemKey } from "./../SchemaKey"; -const SCHEMAURL3_2 = "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema"; +const SCHEMAURL3_2 = "https://dev.bentley.com/json_schemas/ec/32/ecschema"; /** * */ export class Schema implements CustomAttributeContainerProps { - // private _context?: SchemaContext; unused currently, but in the future we may use it for item lookup + private _context: SchemaContext; protected _schemaKey?: SchemaKey; protected _alias?: string; protected _label?: string; protected _description?: string; public readonly references: Schema[]; - private readonly _items: SchemaItem[]; + private readonly _items: Map; private _customAttributes?: Map; /** - * Constructs an empty Schema with the given name and version, (optionally) in a given context. + * Constructs an empty Schema with the given name and version in the provided context. + * @param context The SchemaContext that will control the lifetime of the schema * @param name The schema's name * @param readVersion The integer read (major) version of the schema * @param writeVersion The integer write version of the schema * @param minorVersion The integer minor version of the schema - * @param context The SchemaContext that will control the lifetime of the schema */ - constructor(name: string, readVersion: number, writeVersion: number, minorVersion: number, context?: SchemaContext); + constructor(context: SchemaContext, name: string, readVersion: number, writeVersion: number, minorVersion: number); /** - * Constructs an empty Schema with the given key, (optionally) in a given context. + * Constructs an empty Schema with the given key in the provided context. + * @param context The SchemaContext that will control the lifetime of the schema * @param key A SchemaKey that uniquely identifies the schema - * @param context The SchemaContext that will control the lifetime of the schema. */ - constructor(key: SchemaKey, context?: SchemaContext); // tslint:disable-line:unified-signatures + constructor(context: SchemaContext, key: SchemaKey); // tslint:disable-line:unified-signatures /** - * Constructs an empty Schema (without a SchemaKey). + * Constructs an empty Schema (without a SchemaKey) in the provided context. * This should only be used when the schema name and version will be deserialized (via `fromJson()`) immediately after this Schema is instantiated. + * @param context The SchemaContext that will control the lifetime of the schema * @hidden */ - constructor(); - constructor(nameOrKey?: SchemaKey | string, readVerOrCtx?: SchemaContext | number, writeVer?: number, minorVer?: number, _otherCtx?: SchemaContext) { - this._schemaKey = (typeof (nameOrKey) === "string") ? new SchemaKey(nameOrKey, new ECVersion(readVerOrCtx as number, writeVer, minorVer)) : nameOrKey; - // this._context = (typeof(readVerOrCtx) === "number") ? otherCtx : readVerOrCtx; + constructor(context: SchemaContext); + constructor(context: SchemaContext, nameOrKey?: SchemaKey | string, readVer?: SchemaContext | number, writeVer?: number, minorVer?: number) { + this._schemaKey = (typeof (nameOrKey) === "string") ? new SchemaKey(nameOrKey, new ECVersion(readVer as number, writeVer, minorVer)) : nameOrKey; + this._context = context; this.references = []; - this._items = []; + this._items = new Map(); } get schemaKey() { @@ -90,6 +91,15 @@ export class Schema implements CustomAttributeContainerProps { get customAttributes(): CustomAttributeSet | undefined { return this._customAttributes; } + /** Returns the schema name. */ + get fullName() { return this.schemaKey.name; } + + /** Returns the schema. */ + get schema(): Schema { return this; } + + /** Returns the schema context. */ + get context(): SchemaContext { return this._context; } + /** * Returns a SchemaItemKey given the item name and the schema it belongs to * @param fullName: fully qualified name {Schema name}.{Item Name} @@ -295,7 +305,7 @@ export class Schema implements CustomAttributeContainerProps { */ public async getItem(name: string): Promise { // this method exists so we can rewire it later when we load partial schemas, for now it is identical to the sync version - return this.getItemSync(name); + return this.getItemSync(name); } /** @@ -304,7 +314,7 @@ export class Schema implements CustomAttributeContainerProps { */ public getItemSync(name: string): T | undefined { // Case-insensitive search - return this._items.find((item) => item.name.toUpperCase() === name.toUpperCase()) as T; + return this._items.get(name.toUpperCase()) as T; } /** @@ -322,14 +332,12 @@ export class Schema implements CustomAttributeContainerProps { if (!schemaName || schemaName.toUpperCase() === this.name.toUpperCase()) { return this.getItem(itemName); - // this._context. } const refSchema = await this.getReference(schemaName); if (!refSchema) return undefined; - // TODO: use the context and not get the full schema here return refSchema.getItem(itemName); } @@ -348,14 +356,12 @@ export class Schema implements CustomAttributeContainerProps { if (!schemaName || schemaName.toUpperCase() === this.name.toUpperCase()) { return this.getItemSync(itemName); - // this._context. } const refSchema = this.getReferenceSync(schemaName); if (!refSchema) return undefined; - // TODO: use the context and not get the full schema here return refSchema.getItemSync(itemName); } @@ -363,26 +369,21 @@ export class Schema implements CustomAttributeContainerProps { if (undefined !== this.getItemSync(item.name)) throw new ECObjectsError(ECObjectsStatus.DuplicateItem, `The SchemaItem ${item.name} cannot be added to the schema ${this.name} because it already exists`); - this._items.push(item); + this._items.set(item.name.toUpperCase(), item); } - public getItems(): T[] { + public getItems(): IterableIterator { if (!this._items) - return []; + return new Map().values() as IterableIterator; - return this._items as T[]; + return this._items.values() as IterableIterator; } - public getClasses(): ECClass[] { - if (!this._items) - return []; - - const classList = this._items.filter((item) => item instanceof ECClass); - - if (!classList) - return []; - - return classList as ECClass[]; + public *getClasses(): IterableIterator { + for (const [, value] of this._items) { + if (value instanceof ECClass) + yield value; + } } /** @@ -422,19 +423,13 @@ export class Schema implements CustomAttributeContainerProps { schemaJson.label = this.label; if (undefined !== this.description) // description is optional schemaJson.description = this.description; - if (undefined !== this.references && this.references.length > 0) { // references is optional - schemaJson.references = []; - this.references.forEach((refSchema: Schema) => { - schemaJson.references.push({ - name: refSchema.name, - version: refSchema.schemaKey.version.toString(true), - }); - }); - } + if (undefined !== this.references && this.references.length > 0) // references is optional + schemaJson.references = this.references.map(({ name, schemaKey }) => ({ name, version: schemaKey.version.toString() })); + const customAttributes = serializeCustomAttributes(this.customAttributes); if (undefined !== customAttributes) schemaJson.customAttributes = customAttributes; - if (this._items.length > 0) { + if (this._items.size > 0) { schemaJson.items = {}; this._items.forEach((schemaItem: SchemaItem) => { schemaJson.items[schemaItem.name] = schemaItem.toJson(false, true); @@ -444,9 +439,6 @@ export class Schema implements CustomAttributeContainerProps { } public deserializeSync(schemaProps: SchemaProps) { - if (SCHEMAURL3_2 !== schemaProps.$schema) // TODO: Allow for 3.x URI versions to allow the API to read newer specs. (Start at 3.2 though) - throw new ECObjectsError(ECObjectsStatus.MissingSchemaUrl, `The Schema ${this.name} has an unsupported namespace '${schemaProps.$schema}'.`); - if (undefined === this._schemaKey) { const schemaName = schemaProps.name; const version = ECVersion.fromString(schemaProps.version); @@ -454,10 +446,13 @@ export class Schema implements CustomAttributeContainerProps { } else { if (schemaProps.name.toLowerCase() !== this.name.toLowerCase()) throw new ECObjectsError(ECObjectsStatus.InvalidECJson, `The Schema ${this.name} does not match the provided name, '${schemaProps.name}'.`); - if (schemaProps.version !== this.schemaKey.version.toString()) + if (this.schemaKey.version.compare(ECVersion.fromString(schemaProps.version))) throw new ECObjectsError(ECObjectsStatus.InvalidECJson, `The Schema ${this.name} has the version '${this.schemaKey.version}' that does not match the provided version '${schemaProps.version}'.`); } + if (SCHEMAURL3_2 !== schemaProps.$schema) // TODO: Allow for 3.x URI versions to allow the API to read newer specs. (Start at 3.2 though) + throw new ECObjectsError(ECObjectsStatus.MissingSchemaUrl, `The Schema ${this.name} has an unsupported namespace '${schemaProps.$schema}'.`); + if (undefined !== schemaProps.alias) this._alias = schemaProps.alias; @@ -479,8 +474,8 @@ export class Schema implements CustomAttributeContainerProps { this._customAttributes.set(customAttribute.className, customAttribute); } - public static async fromJson(jsonObj: object | string, context?: SchemaContext): Promise { - let schema: Schema = new Schema(); + public static async fromJson(jsonObj: object | string, context: SchemaContext): Promise { + let schema: Schema = new Schema(context); const reader = new SchemaReadHelper(JsonParser, context); const rawSchema = typeof jsonObj === "string" ? JSON.parse(jsonObj) : jsonObj; @@ -489,8 +484,8 @@ export class Schema implements CustomAttributeContainerProps { return schema; } - public static fromJsonSync(jsonObj: object | string, context?: SchemaContext): Schema { - let schema: Schema = new Schema(); + public static fromJsonSync(jsonObj: object | string, context: SchemaContext): Schema { + let schema: Schema = new Schema(context); const reader = new SchemaReadHelper(JsonParser, context); const rawSchema = typeof jsonObj === "string" ? JSON.parse(jsonObj) : jsonObj; @@ -539,4 +534,5 @@ export abstract class MutableSchema extends Schema { public abstract addItem(item: T): void; public abstract async addReference(refSchema: Schema): Promise; public abstract addReferenceSync(refSchema: Schema): void; + public abstract setContext(schemaContext: SchemaContext): void; } diff --git a/core/ecschema-metadata/src/Metadata/SchemaItem.ts b/core/ecschema-metadata/src/Metadata/SchemaItem.ts index 4fd9b16..c26616d 100644 --- a/core/ecschema-metadata/src/Metadata/SchemaItem.ts +++ b/core/ecschema-metadata/src/Metadata/SchemaItem.ts @@ -7,8 +7,7 @@ import { Schema } from "./Schema"; import { SchemaItemProps } from "./../Deserialization/JsonProps"; import { SchemaItemType, schemaItemTypeToString } from "./../ECObjects"; import { ECObjectsError, ECObjectsStatus } from "./../Exception"; -import { SchemaItemVisitor } from "./../Interfaces"; -import { SchemaItemKey } from "./../SchemaKey"; +import { SchemaItemKey, ECVersion } from "./../SchemaKey"; const SCHEMAURL3_2 = "https://dev.bentley.com/json_schemas/ec/32/draft-01/schemaitem"; @@ -55,38 +54,36 @@ export abstract class SchemaItem { } public deserializeSync(schemaItemProps: SchemaItemProps) { - if (undefined !== schemaItemProps.label) { + if (undefined !== schemaItemProps.label) this._label = schemaItemProps.label; - } this._description = schemaItemProps.description; if (undefined !== schemaItemProps.schema) { if (schemaItemProps.schema.toLowerCase() !== this.schema.name.toLowerCase()) - throw new ECObjectsError(ECObjectsStatus.InvalidECJson, ``); + throw new ECObjectsError(ECObjectsStatus.InvalidECJson, `Unable to deserialize the SchemaItem '${this.fullName}' with a different schema name, ${schemaItemProps.schema}, than the current Schema of this SchemaItem, ${this.schema.fullName}.`); } if (undefined !== schemaItemProps.schemaVersion) { - if (schemaItemProps.schemaVersion !== this.key.schemaKey.version.toString()) - throw new ECObjectsError(ECObjectsStatus.InvalidECJson, ``); + if (this.key.schemaKey.version.compare(ECVersion.fromString(schemaItemProps.schemaVersion))) + throw new ECObjectsError(ECObjectsStatus.InvalidECJson, `Unable to deserialize the SchemaItem '${this.fullName}' with a different schema version, ${schemaItemProps.schemaVersion}, than the current Schema version of this SchemaItem, ${this.key.schemaKey.version}.`); } } public async deserialize(schemaItemProps: SchemaItemProps) { - if (undefined !== schemaItemProps.label) { + if (undefined !== schemaItemProps.label) this._label = schemaItemProps.label; - } this._description = schemaItemProps.description; if (undefined !== schemaItemProps.schema) { if (schemaItemProps.schema.toLowerCase() !== this.schema.name.toLowerCase()) - throw new ECObjectsError(ECObjectsStatus.InvalidECJson, ``); + throw new ECObjectsError(ECObjectsStatus.InvalidECJson, `Unable to deserialize the SchemaItem ${this.fullName}' with a different schema name, ${schemaItemProps.schema}, than the current Schema of this SchemaItem, ${this.schema.fullName}`); } if (undefined !== schemaItemProps.schemaVersion) { - if (schemaItemProps.schemaVersion !== this.key.schemaKey.version.toString()) - throw new ECObjectsError(ECObjectsStatus.InvalidECJson, ``); + if (this.key.schemaKey.version.compare(ECVersion.fromString(schemaItemProps.schemaVersion))) + throw new ECObjectsError(ECObjectsStatus.InvalidECJson, `Unable to deserialize the SchemaItem '${this.fullName}' with a different schema version, ${schemaItemProps.schemaVersion}, than the current Schema version of this SchemaItem, ${this.key.schemaKey.version}.`); } } @@ -108,15 +105,13 @@ export abstract class SchemaItem { /** * Indicates if the two SchemaItem objects are equal by comparing their respective [[key]] properties. * @param thisSchemaItem The first SchemaItem. - * @param thatSchemaItem The second SchemaItem. + * @param thatSchemaItemOrKey The second SchemaItem or SchemaItemKey. */ - public static equalByKey(thisSchemaItem: SchemaItem, thatSchemaItem?: SchemaItem) { - if (!thatSchemaItem) + public static equalByKey(thisSchemaItem: SchemaItem, thatSchemaItemOrKey?: SchemaItem | SchemaItemKey) { + if (!thatSchemaItemOrKey) return true; - return thisSchemaItem.key.matches(thatSchemaItem.key); + const key = thatSchemaItemOrKey instanceof SchemaItem ? thatSchemaItemOrKey.key : thatSchemaItemOrKey; + return thisSchemaItem.key.matches(key); } - - public abstract async accept(visitor: SchemaItemVisitor): Promise; - public abstract acceptSync(visitor: SchemaItemVisitor): void; } diff --git a/core/ecschema-metadata/src/Metadata/Unit.ts b/core/ecschema-metadata/src/Metadata/Unit.ts index 7e298fc..edce7f1 100644 --- a/core/ecschema-metadata/src/Metadata/Unit.ts +++ b/core/ecschema-metadata/src/Metadata/Unit.ts @@ -11,7 +11,7 @@ import { DelayedPromiseWithProps } from "./../DelayedPromise"; import { SchemaItemType } from "./../ECObjects"; import { UnitProps } from "./../Deserialization/JsonProps"; import { ECObjectsError, ECObjectsStatus } from "./../Exception"; -import { LazyLoadedPhenomenon, LazyLoadedUnitSystem, SchemaItemVisitor } from "./../Interfaces"; +import { LazyLoadedPhenomenon, LazyLoadedUnitSystem } from "./../Interfaces"; import { SchemaItemKey } from "./../SchemaKey"; /** @@ -106,14 +106,4 @@ export class Unit extends SchemaItem { public async deserialize(unitProps: UnitProps) { this.deserializeSync(unitProps); } - - public async accept(visitor: SchemaItemVisitor) { - if (visitor.visitUnit) - await visitor.visitUnit(this); - } - - public acceptSync(visitor: SchemaItemVisitor) { - if (visitor.visitUnitSync) - visitor.visitUnitSync(this); - } } diff --git a/core/ecschema-metadata/src/Metadata/UnitSystem.ts b/core/ecschema-metadata/src/Metadata/UnitSystem.ts index ab267d9..945189c 100644 --- a/core/ecschema-metadata/src/Metadata/UnitSystem.ts +++ b/core/ecschema-metadata/src/Metadata/UnitSystem.ts @@ -6,7 +6,6 @@ import { Schema } from "./Schema"; import { SchemaItem } from "./SchemaItem"; import { SchemaItemType } from "./../ECObjects"; -import { SchemaItemVisitor } from "./../Interfaces"; export class UnitSystem extends SchemaItem { public readonly schemaItemType!: SchemaItemType.UnitSystem; // tslint:disable-line @@ -15,14 +14,4 @@ export class UnitSystem extends SchemaItem { super(schema, name); this.schemaItemType = SchemaItemType.UnitSystem; } - - public async accept(visitor: SchemaItemVisitor) { - if (visitor.visitUnitSystem) - await visitor.visitUnitSystem(this); - } - - public acceptSync(visitor: SchemaItemVisitor) { - if (visitor.visitUnitSystemSync) - visitor.visitUnitSystemSync(this); - } } diff --git a/core/ecschema-metadata/src/SchemaKey.ts b/core/ecschema-metadata/src/SchemaKey.ts index dc4d61f..091a69e 100644 --- a/core/ecschema-metadata/src/SchemaKey.ts +++ b/core/ecschema-metadata/src/SchemaKey.ts @@ -31,10 +31,10 @@ export class ECVersion { /** * Creates a string, in the format 'RR.ww.mm', representing this ECVersion. - * @ note The default is to not pad with zeroes. + * @note The default is to pad with zeroes. * @param padZeroes If true, the returned string will strictly follow `RR.ww.mm` and add leading zeroes if necessary. */ - public toString(padZeroes: boolean = false): string { + public toString(padZeroes: boolean = true): string { if (!padZeroes) return `${this.read}.${this.write}.${this.minor}`; @@ -132,7 +132,12 @@ export class SchemaKey { get minorVersion() { return this.version.minor; } - public toString() { return `${this.name}.${this.readVersion}.${this.writeVersion}.${this.minorVersion}`; } + /** + * Creates a string, in the format 'RR.ww.mm', representing this SchemaKey. + * @note The default is to pad the full name with zeroes. + * @param padZeroes If true, the returned string will strictly follow `Name.RR.ww.mm` and add leading zeroes if necessary. + */ + public toString(padZeroes: boolean = true) { return `${this.name}.${this.version.toString(padZeroes)}`; } public static parseString(fullName: string) { const keyPieces = fullName.split("."); diff --git a/core/ecschema-metadata/src/SchemaPartVisitorDelegate.ts b/core/ecschema-metadata/src/SchemaPartVisitorDelegate.ts new file mode 100644 index 0000000..307c3f0 --- /dev/null +++ b/core/ecschema-metadata/src/SchemaPartVisitorDelegate.ts @@ -0,0 +1,504 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { AnyECType, AnyClass } from "./Interfaces"; +import { SchemaItem } from "./Metadata/SchemaItem"; +import { Schema } from "./Metadata/Schema"; +import { SchemaItemType } from "./ECObjects"; +import { Constant } from "./Metadata/Constant"; +import { CustomAttributeClass } from "./Metadata/CustomAttributeClass"; +import { ECClass, StructClass } from "./Metadata/Class"; +import { EntityClass } from "./Metadata/EntityClass"; +import { Enumeration } from "./Metadata/Enumeration"; +import { Format } from "./Metadata/Format"; +import { Mixin } from "./Metadata/Mixin"; +import { Unit } from "./Metadata/Unit"; +import { InvertedUnit } from "./Metadata/InvertedUnit"; +import { KindOfQuantity } from "./Metadata/KindOfQuantity"; +import { Phenomenon } from "./Metadata/Phenomenon"; +import { PropertyCategory } from "./Metadata/PropertyCategory"; +import { RelationshipClass, RelationshipConstraint } from "./Metadata/RelationshipClass"; +import { UnitSystem } from "./Metadata/UnitSystem"; +import { CustomAttributeContainerProps } from "./Metadata/CustomAttribute"; +import { Property, AnyProperty } from "./Metadata/Property"; + +/** + * Interface to allow schema traversal/deserialization workflows to visit + * each part, item, class, etc. that exists in a given schema. + */ +export interface ISchemaPartVisitor { + /** + * Called for a partially loaded schema. During deserialization, this would + * be after a schema and all its references are deserialized, but _before_ + * any of its items or custom attributes have been deserialized. + * @param schema A partially-loaded Schema. + */ + /* async */ visitEmptySchema?: (schema: Schema) => Promise; + + /** + * Called for a partially loaded schema. During deserialization, this would + * be after a schema and all its references are deserialized, but _before_ + * any of its items or custom attributes have been deserialized. + * @param schema A partially-loaded Schema. + */ + visitEmptySchemaSync?: (schema: Schema) => void; + + /** + * Called for a fully loaded schema. + * @param schema A fully-loaded Schema. + */ + /* async */ visitFullSchema?: (schema: Schema) => Promise; + + /** + * Called for a fully loaded schema. + * @param schema A fully-loaded Schema. + */ + visitFullSchemaSync?: (schema: Schema) => void; + + /** + * Called for each [[SchemaItem]] instance. + * @param schemaItem a SchemaItem object. + */ + /* async */ visitSchemaItem?: (schemaItem: SchemaItem) => Promise; + + /** + * Called for each [[SchemaItem]] instance. + * @param schemaItem a SchemaItem object. + */ + visitSchemaItemSync?: (schemaItem: SchemaItem) => void; + + /** + * Called for each [[AnyClass]] instance. + * @param ecClass an ECClass object. + */ + /* async */ visitClass?: (ecClass: AnyClass) => Promise; + + /** + * Called for each [[AnyClass]] instance. + * @param ecClass an ECClass object. + */ + visitClassSync?: (ecClass: AnyClass) => void; + + /** + * Called for each [[AnyProperty]] instance of an ECClass. + * @param property an AnyProperty object. + */ + /* async */ visitProperty?: (property: AnyProperty) => Promise; + + /** + * Called for each [[AnyProperty]] instance of an ECClass. + * @param property an AnyProperty object. + */ + visitPropertySync?: (property: AnyProperty) => void; + + /** + * Called for each [[EntityClass]] instance. + * @param entityClass an EntityClass object. + */ + /* async */ visitEntityClass?: (entityClass: EntityClass) => Promise; + + /** + * Called for each [[EntityClass]] instance. + * @param entityClass an EntityClass object. + */ + visitEntityClassSync?: (entityClass: EntityClass) => void; + + /** + * Called for each [[StructClass]] instance. + * @param structClass a StructClass object. + */ + /* async */ visitStructClass?: (structClass: StructClass) => Promise; + + /** + * Called for each [[StructClass]] instance. + * @param structClass a StructClass object. + */ + visitStructClassSync?: (structClass: StructClass) => void; + + /** + * Called for each [[Mixin]] instance. + * @param mixin a Mixin object. + */ + /* async */ visitMixin?: (mixin: Mixin) => Promise; + + /** + * Called for each [[Mixin]] instance. + * @param mixin a Mixin object. + */ + visitMixinSync?: (mixin: Mixin) => void; + + /** + * Called for each [[RelationshipClass]] instance. + * @param relationshipClass a RelationshipClass object. + */ + /* async */ visitRelationshipClass?: (relationshipClass: RelationshipClass) => Promise; + + /** + * Called for each [[RelationshipClass]] instance. + * @param relationshipClass a RelationshipClass object. + */ + visitRelationshipClassSync?: (relationshipClass: RelationshipClass) => void; + + /** + * Called for each [[RelationshipConstraint]] of each RelationshipClass. + * @param relationshipConstraint a RelationshipConstraint object. + */ + /* async */ visitRelationshipConstraint?: (relationshipConstraint: RelationshipConstraint) => Promise; + + /** + * Called for each [[RelationshipConstraint]] of each RelationshipClass. + * @param relationshipConstraint a RelationshipConstraint object. + */ + visitRelationshipConstraintSync?: (relationshipConstraint: RelationshipConstraint) => void; + + /** + * Called for each [[CustomAttributeClass]] instance. + * @param customAttributeClass a CustomAttributeClass object. + */ + /* async */ visitCustomAttributeClass?: (customAttributeClass: CustomAttributeClass) => Promise; + + /** + * Called for each [[CustomAttributeClass]] instance. + * @param customAttributeClass a CustomAttributeClass object. + */ + visitCustomAttributeClassSync?: (customAttributeClass: CustomAttributeClass) => void; + + /** + * Called for each CustomAttribute container in the schema. + * @param customAttributeContainer a CustomAttributeContainerProps object. + */ + /* async */ visitCustomAttributeContainer?: (customAttributeContainer: CustomAttributeContainerProps) => Promise; + + /** + * Called for each CustomAttribute container in the schema. + * @param customAttributeContainer a CustomAttributeContainerProps object. + */ + visitCustomAttributeContainerSync?: (customAttributeContainer: CustomAttributeContainerProps) => void; + + /** + * Called for each [[Enumeration]] instance. + * @param enumeration an Enumeration object. + */ + /* async */ visitEnumeration?: (enumeration: Enumeration) => Promise; + + /** + * Called for each [[Enumeration]] instance. + * @param enumeration an Enumeration object. + */ + visitEnumerationSync?: (enumeration: Enumeration) => void; + + /** + * Called for each [[KindOfQuantity]] instance. + * @param koq a KindOfQuantity object. + */ + /* async */ visitKindOfQuantity?: (koq: KindOfQuantity) => Promise; + + /** + * Called for each [[KindOfQuantity]] instance. + * @param koq a KindOfQuantity object. + */ + visitKindOfQuantitySync?: (koq: KindOfQuantity) => void; + + /** + * Called for each [[PropertyCategory]] instance. + * @param category a PropertyCategory object. + */ + /* async */ visitPropertyCategory?: (category: PropertyCategory) => Promise; + + /** + * Called for each [[PropertyCategory]] instance. + * @param category a PropertyCategory object. + */ + visitPropertyCategorySync?: (category: PropertyCategory) => void; + + /** + * Called for each [[Format]] instance. + * @param format a Format object. + */ + /* async */ visitFormat?: (format: Format) => Promise; + + /** + * Called for each [[Format]] instance. + * @param format a Format object. + */ + visitFormatSync?: (format: Format) => void; + + /** + * Called for each [[Unit]] instance. + * @param unit a Unit object. + */ + /* async */ visitUnit?: (unit: Unit) => Promise; + + /** + * Called for each [[Unit]] instance. + * @param unit a Unit object. + */ + visitUnitSync?: (unit: Unit) => void; + + /** + * Called for each [[InvertedUnit]] instance. + * @param invertedUnit an InvertedUnit object. + */ + /* async */ visitInvertedUnit?: (invertedUnit: InvertedUnit) => Promise; + + /** + * Called for each [[InvertedUnit]] instance. + * @param invertedUnit an InvertedUnit object. + */ + visitInvertedUnitSync?: (invertedUnit: InvertedUnit) => void; + + /** + * Called for each [[UnitSystem]] instance. + * @param unitSystem a UnitSystem object. + */ + /* async */ visitUnitSystem?: (unitSystem: UnitSystem) => Promise; + + /** + * Called for each [[UnitSystem]] instance. + * @param unitSystem a UnitSystem object. + */ + visitUnitSystemSync?: (unitSystem: UnitSystem) => void; + + /** + * Called for each [[Phenomenon]] instance. + * @param phenomena a Phenomenon object. + */ + /* async */ visitPhenomenon?: (phenomena: Phenomenon) => Promise; + + /** + * Called for each [[Phenomenon]] instance. + * @param phenomena a Phenomenon object. + */ + visitPhenomenonSync?: (phenomena: Phenomenon) => void; + + /** + * Called for each [[Constant]] instance. + * @param constant a Constant object. + */ + /* async */ visitConstant?: (constant: Constant) => Promise; + + /** + * Called for each [[Constant]] instance. + * @param constant a Constant object. + */ + visitConstantSync?: (constant: Constant) => void; +} + +function isCustomAttributeContainer(object: any): object is CustomAttributeContainerProps { + return "customAttributes" in object; +} + +/** + * A helper class to call methods on the provided [[ISchemaPartVisitor]]. + */ +export class SchemaPartVisitorDelegate { + private _visitor: ISchemaPartVisitor; + + constructor (visitor: ISchemaPartVisitor) { + this._visitor = visitor; + } + + /** + * Calls (async) visitEmptySchema or visitFullSchema on the configured [[ISchemaPartVisitor]]. + * @param schema The schema to pass to the visitor. + * @param fullSchema Indicates if the schema is partially or fully-loaded. + */ + public async visitSchema(schema: Schema, fullSchema: boolean = true): Promise { + if (!fullSchema && this._visitor.visitEmptySchema) + await this._visitor.visitEmptySchema(schema); + + if (fullSchema && this._visitor.visitFullSchema) + await this._visitor.visitFullSchema(schema); + } + + /** + * Calls (synchronously) visitEmptySchema or visitFullSchema on the configured [[ISchemaPartVisitor]]. + * @param schema The schema to pass to the visitor. + * @param fullSchema Indicates if the schema is partially or fully-loaded. + */ + public visitSchemaSync(schema: Schema, fullSchema: boolean = true): void { + if (!fullSchema && this._visitor.visitEmptySchemaSync) + this._visitor.visitEmptySchemaSync(schema); + + if (fullSchema && this._visitor.visitFullSchemaSync) + this._visitor.visitFullSchemaSync(schema); + } + + /** + * Calls (async) the appropriate visit methods on the configured [[ISchemaPartVisitor]] + * based on the type of the part specified. + * @param schemaPart The schema part to pass to the visitor methods. + */ + public async visitSchemaPart(schemaPart: AnyECType): Promise { + const schemaItem = schemaPart as SchemaItem; + + if (schemaPart instanceof SchemaItem) { + await this.visitSchemaItem(schemaItem); + } else if (schemaPart instanceof Property && this._visitor.visitProperty) { + await this._visitor.visitProperty(schemaPart); + } else if (schemaPart instanceof RelationshipConstraint && this._visitor.visitRelationshipConstraint) { + await this._visitor.visitRelationshipConstraint(schemaPart); + } + + if (isCustomAttributeContainer(schemaPart) && this._visitor.visitCustomAttributeContainer) { + await this._visitor.visitCustomAttributeContainer(schemaPart); + } + } + + /** + * Calls (synchronously) the appropriate visit methods on the configured [[ISchemaPartVisitor]] + * based on the type of the part specified. + * @param schemaPart The schema part to pass to the visitor methods. + */ + public visitSchemaPartSync(schemaPart: AnyECType): void { + const schemaItem = schemaPart as SchemaItem; + + if (schemaPart instanceof SchemaItem) { + this.visitSchemaItemSync(schemaItem); + } else if (schemaPart instanceof Property && this._visitor.visitPropertySync) { + this._visitor.visitPropertySync(schemaPart); + } else if (schemaPart instanceof RelationshipConstraint && this._visitor.visitRelationshipConstraintSync) { + this._visitor.visitRelationshipConstraintSync(schemaPart); + } + + if (isCustomAttributeContainer(schemaPart) && this._visitor.visitCustomAttributeContainerSync) { + this._visitor.visitCustomAttributeContainerSync(schemaPart); + } + } + + private async visitSchemaItem(schemaItem: SchemaItem) { + if (this._visitor.visitSchemaItem) + await this._visitor.visitSchemaItem(schemaItem); + + if (schemaItem instanceof ECClass && this._visitor.visitClass) + await this._visitor.visitClass(schemaItem as AnyClass); + + switch (schemaItem.schemaItemType) { + case SchemaItemType.Constant: + if (this._visitor.visitConstant) + await this._visitor.visitConstant(schemaItem as Constant); + break; + case SchemaItemType.CustomAttributeClass: + if (this._visitor.visitCustomAttributeClass) + await this._visitor.visitCustomAttributeClass(schemaItem as CustomAttributeClass); + break; + case SchemaItemType.EntityClass: + if (this._visitor.visitEntityClass) + await this._visitor.visitEntityClass(schemaItem as EntityClass); + break; + case SchemaItemType.Enumeration: + if (this._visitor.visitEnumeration) + await this._visitor.visitEnumeration(schemaItem as Enumeration); + break; + case SchemaItemType.Format: + if (this._visitor.visitFormat) + await this._visitor.visitFormat(schemaItem as Format); + break; + case SchemaItemType.InvertedUnit: + if (this._visitor.visitInvertedUnit) + await this._visitor.visitInvertedUnit(schemaItem as InvertedUnit); + break; + case SchemaItemType.KindOfQuantity: + if (this._visitor.visitKindOfQuantity) + await this._visitor.visitKindOfQuantity(schemaItem as KindOfQuantity); + break; + case SchemaItemType.Mixin: + if (this._visitor.visitMixin) + await this._visitor.visitMixin(schemaItem as Mixin); + break; + case SchemaItemType.Phenomenon: + if (this._visitor.visitPhenomenon) + await this._visitor.visitPhenomenon(schemaItem as Phenomenon); + break; + case SchemaItemType.PropertyCategory: + if (this._visitor.visitPropertyCategory) + await this._visitor.visitPropertyCategory(schemaItem as PropertyCategory); + break; + case SchemaItemType.RelationshipClass: + if (this._visitor.visitRelationshipClass) + await this._visitor.visitRelationshipClass(schemaItem as RelationshipClass); + break; + case SchemaItemType.StructClass: + if (this._visitor.visitStructClass) + await this._visitor.visitStructClass(schemaItem as StructClass); + break; + case SchemaItemType.Unit: + if (this._visitor.visitUnit) + await this._visitor.visitUnit(schemaItem as Unit); + break; + case SchemaItemType.UnitSystem: + if (this._visitor.visitUnitSystem) + await this._visitor.visitUnitSystem(schemaItem as UnitSystem); + break; + } + } + + private visitSchemaItemSync(schemaItem: SchemaItem) { + if (this._visitor.visitSchemaItemSync) + this._visitor.visitSchemaItemSync(schemaItem); + + if (schemaItem instanceof ECClass && this._visitor.visitClassSync) + this._visitor.visitClassSync(schemaItem as AnyClass); + + switch (schemaItem.schemaItemType) { + case SchemaItemType.Constant: + if (this._visitor.visitConstantSync) + this._visitor.visitConstantSync(schemaItem as Constant); + break; + case SchemaItemType.CustomAttributeClass: + if (this._visitor.visitCustomAttributeClassSync) + this._visitor.visitCustomAttributeClassSync(schemaItem as CustomAttributeClass); + break; + case SchemaItemType.EntityClass: + if (this._visitor.visitEntityClassSync) + this._visitor.visitEntityClassSync(schemaItem as EntityClass); + break; + case SchemaItemType.Enumeration: + if (this._visitor.visitEnumerationSync) + this._visitor.visitEnumerationSync(schemaItem as Enumeration); + break; + case SchemaItemType.Format: + if (this._visitor.visitFormatSync) + this._visitor.visitFormatSync(schemaItem as Format); + break; + case SchemaItemType.InvertedUnit: + if (this._visitor.visitInvertedUnitSync) + this._visitor.visitInvertedUnitSync(schemaItem as InvertedUnit); + break; + case SchemaItemType.KindOfQuantity: + if (this._visitor.visitKindOfQuantitySync) + this._visitor.visitKindOfQuantitySync(schemaItem as KindOfQuantity); + break; + case SchemaItemType.Mixin: + if (this._visitor.visitMixinSync) + this._visitor.visitMixinSync(schemaItem as Mixin); + break; + case SchemaItemType.Phenomenon: + if (this._visitor.visitPhenomenonSync) + this._visitor.visitPhenomenonSync(schemaItem as Phenomenon); + break; + case SchemaItemType.PropertyCategory: + if (this._visitor.visitPropertyCategorySync) + this._visitor.visitPropertyCategorySync(schemaItem as PropertyCategory); + break; + case SchemaItemType.RelationshipClass: + if (this._visitor.visitRelationshipClassSync) + this._visitor.visitRelationshipClassSync(schemaItem as RelationshipClass); + break; + case SchemaItemType.StructClass: + if (this._visitor.visitStructClassSync) + this._visitor.visitStructClassSync(schemaItem as StructClass); + break; + case SchemaItemType.Unit: + if (this._visitor.visitUnitSync) + this._visitor.visitUnitSync(schemaItem as Unit); + break; + case SchemaItemType.UnitSystem: + if (this._visitor.visitUnitSystemSync) + this._visitor.visitUnitSystemSync(schemaItem as UnitSystem); + break; + } + } +} diff --git a/core/ecschema-metadata/src/Validation/Diagnostic.ts b/core/ecschema-metadata/src/Validation/Diagnostic.ts new file mode 100644 index 0000000..b4fba3d --- /dev/null +++ b/core/ecschema-metadata/src/Validation/Diagnostic.ts @@ -0,0 +1,274 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { AnyClass, AnyECType } from "../Interfaces"; +import { CustomAttributeContainerProps } from "../Metadata/CustomAttribute"; +import { AnyProperty } from "../Metadata/Property"; +import { RelationshipConstraint } from "../Metadata/RelationshipClass"; +import { Schema } from "../Metadata/Schema"; +import { SchemaItem } from "../Metadata/SchemaItem"; + +/** Defines the possible diagnostic types. */ +export const enum DiagnosticType { + None, + Schema, + SchemaItem, + Property, + CustomAttributeContainer, + RelationshipConstraint, +} + +/** Defines the possible diagnostic categories. */ +export const enum DiagnosticCategory { + Warning, + Error, + Suggestion, + Message, +} + +/** + * The interface implemented by all diagnostics used during schema validation. + */ +export interface IDiagnostic { + /** The diagnostic category (error, warning, etc...). Value is static across all instances. */ + category: DiagnosticCategory; + /** The unique string identifier of the diagnostic in the format ':. Value is static across all instances. */ + code: string; + /** The context type of diagnostic (schema, schema item, property, etc...). Value is static across all instances. */ + diagnosticType: DiagnosticType; + /** The unformatted message text associated with the diagnostic. Value is static across all instances. */ + messageText: string; + /** The arguments used when formatted the diagnostic instance's message. */ + messageArgs: ARGS; + /** The EC object associated with the diagnostic instance. */ + ecDefinition: TYPE; +} + +/** Type which encapsulates all possible diagnostics. */ +export type AnyDiagnostic = IDiagnostic; + +/** + * The abstract base class for all [[IDiagnostic]] implementations. + */ +export abstract class BaseDiagnostic implements IDiagnostic { + /** + * Initializes a new BaseDiagnostic. + * @param ecDefinition The EC object to associate with the diagnostic. + * @param messageArgs The arguments used when formatting the diagnostic message. + */ + constructor(ecDefinition: TYPE, messageArgs: ARGS) { + this.ecDefinition = ecDefinition; + this.messageArgs = messageArgs; + } + + /** Gets the category of the diagnostic. */ + public abstract get category(): DiagnosticCategory; + /** Gets the unique string identifier for the diagnostic in the format ':'. */ + public abstract get code(): string; + /** Gets the context type of the diagnostic (schema, schema item, property, etc...) */ + public abstract get diagnosticType(): DiagnosticType; + /** Gets the message associated with the diagnostic. */ + public abstract get messageText(): string; + + /** The EC object to associate with the diagnostic. */ + public ecDefinition: TYPE; + /** The arguments used when formatting the diagnostic message. */ + public messageArgs: ARGS; +} + +/** + * An [[IDiagnostic]] implementation used for [[Schema]] diagnostics. + */ +export abstract class SchemaDiagnostic extends BaseDiagnostic { + public static diagnosticType = DiagnosticType.SchemaItem; + + constructor(schema: Schema, messageArgs: ARGS) { + super(schema, messageArgs); + } + + public get diagnosticType(): DiagnosticType { return DiagnosticType.Schema; } +} + +/** + * An [[IDiagnostic]] implementation used for [[SchemaItem]] diagnostics. + */ +export abstract class SchemaItemDiagnostic extends BaseDiagnostic { + public static diagnosticType = DiagnosticType.SchemaItem; + + constructor(ecDefinition: TYPE, messageArgs: ARGS) { + super(ecDefinition, messageArgs); + } + + public get diagnosticType(): DiagnosticType { return DiagnosticType.SchemaItem; } +} + +/** + * An [[IDiagnostic]] implementation used for [[ECClass]] diagnostics. + */ +export abstract class ClassDiagnostic extends SchemaItemDiagnostic { + constructor(ecClass: AnyClass, messageArgs: ARGS) { + super(ecClass, messageArgs); + } +} + +/** + * An [[IDiagnostic]] implementation used for [[Property]] diagnostics. + */ +export abstract class PropertyDiagnostic extends BaseDiagnostic { + constructor(property: AnyProperty, messageArgs: ARGS) { + super(property, messageArgs); + } + + public get diagnosticType(): DiagnosticType { return DiagnosticType.Property; } +} + +/** + * An [[IDiagnostic]] implementation used for [[RelationshipConstraint]] diagnostics. + */ +export abstract class RelationshipConstraintDiagnostic extends BaseDiagnostic { + constructor(constraint: RelationshipConstraint, messageArgs: ARGS) { + super(constraint, messageArgs); + } + + public get diagnosticType(): DiagnosticType { return DiagnosticType.RelationshipConstraint; } +} + +/** + * An [[IDiagnostic]] implementation used for [[CustomAttributeContainerProps]] diagnostics. + */ +export abstract class CustomAttributeContainerDiagnostic extends BaseDiagnostic { + constructor(container: CustomAttributeContainerProps, messageArgs: ARGS) { + super(container, messageArgs); + } + + public get diagnosticType(): DiagnosticType { return DiagnosticType.CustomAttributeContainer; } +} + +/** + * Helper method for creating [[SchemaDiagnostic]] child classes. + * @param code The string that uniquely identifies the diagnostic in the format ':'. + * @param messageText The message to associate with the diagnostic class. + * @param category The [[DiagnosticCategory]] to associate with the diagnostic class. + */ +export function createSchemaDiagnosticClass(code: string, messageText: string, category: DiagnosticCategory = DiagnosticCategory.Error) { + validateCode(code); + return class extends SchemaDiagnostic { + public get code(): string { return code; } + public get category(): DiagnosticCategory { return category; } + public get messageText(): string { return messageText; } + }; +} + +/** + * Helper method for creating [[SchemaItemDiagnostic]] child classes. + * @param code The string that uniquely identifies the diagnostic in the format ':'. + * @param messageText The message to associate with the diagnostic class. + * @param category The [[DiagnosticCategory]] to associate with the diagnostic class. + */ +export function createSchemaItemDiagnosticClass(code: string, messageText: string, category: DiagnosticCategory = DiagnosticCategory.Error) { + validateCode(code); + return class extends SchemaItemDiagnostic { + public get code(): string { return code; } + public get category(): DiagnosticCategory { return category; } + public get messageText(): string { return messageText; } + }; +} + +/** + * Helper method for creating [[ClassDiagnostic]] child classes. + * @param code The string that uniquely identifies the diagnostic in the format ':'. + * @param messageText The message to associate with the diagnostic class. + * @param category The [[DiagnosticCategory]] to associate with the diagnostic class. + */ +export function createClassDiagnosticClass(code: string, messageText: string, category: DiagnosticCategory = DiagnosticCategory.Error) { + validateCode(code); + return class extends ClassDiagnostic { + public get code(): string { return code; } + public get category(): DiagnosticCategory { return category; } + public get messageText(): string { return messageText; } + }; +} + +/** + * Helper method for creating [[PropertyDiagnostic]] child classes. + * @param code The string that uniquely identifies the diagnostic in the format ':'. + * @param messageText The message to associate with the diagnostic class. + * @param category The [[DiagnosticCategory]] to associate with the diagnostic class. + */ +export function createPropertyDiagnosticClass(code: string, messageText: string, category: DiagnosticCategory = DiagnosticCategory.Error) { + validateCode(code); + return class extends PropertyDiagnostic { + public get code(): string { return code; } + public get category(): DiagnosticCategory { return category; } + public get messageText(): string { return messageText; } + }; +} + +/** + * Helper method for creating [[RelationshipConstraintDiagnostic]] child classes. + * @param code The string that uniquely identifies the type of diagnostic in the format ':'. + * @param messageText The message to associate with the diagnostic class. + * @param category The [[DiagnosticCategory]] to associate with the diagnostic class. + */ +export function createRelationshipConstraintDiagnosticClass(code: string, messageText: string, category: DiagnosticCategory = DiagnosticCategory.Error) { + validateCode(code); + return class extends RelationshipConstraintDiagnostic { + public get code(): string { return code; } + public get category(): DiagnosticCategory { return category; } + public get messageText(): string { return messageText; } + }; +} + +/** + * Helper method for creating [[CustomAttributeContainerDiagnostic]] child classes. + * @param code The that uniquely identifies the type of diagnostic in the format ':'. + * @param messageText The message to associate with the diagnostic class. + * @param category The [[DiagnosticCategory]] to associate with the diagnostic class. + */ +export function createCustomAttributeContainerDiagnosticClass(code: string, messageText: string, category: DiagnosticCategory = DiagnosticCategory.Error) { + validateCode(code); + return class extends CustomAttributeContainerDiagnostic { + public get code(): string { return code; } + public get category(): DiagnosticCategory { return category; } + public get messageText(): string { return messageText; } + }; +} + +export function diagnosticCategoryToString(category: DiagnosticCategory) { + switch (category) { + case DiagnosticCategory.Error: + return "Error"; + case DiagnosticCategory.Warning: + return "Warning"; + case DiagnosticCategory.Message: + return "Message"; + case DiagnosticCategory.Suggestion: + return "Suggestion"; + } +} + +export function diagnosticTypeToString(type: DiagnosticType) { + switch (type) { + case DiagnosticType.CustomAttributeContainer: + return "CustomAttributeContainer"; + case DiagnosticType.None: + return "None"; + case DiagnosticType.Property: + return "Property"; + case DiagnosticType.RelationshipConstraint: + return "RelationshipConstraint"; + case DiagnosticType.Schema: + return "Schema"; + case DiagnosticType.SchemaItem: + return "SchemaItem"; + } +} + +function validateCode(code: string) { + const msg = `Diagnostic code ${code} is invalid. Expected the format :.`; + const parts = code.split(":"); + if (parts.length !== 2) throw new Error(msg); + if (isNaN(Number(parts[1]))) throw new Error(msg); +} diff --git a/core/ecschema-metadata/src/Validation/DiagnosticReporter.ts b/core/ecschema-metadata/src/Validation/DiagnosticReporter.ts new file mode 100644 index 0000000..7e4f18d --- /dev/null +++ b/core/ecschema-metadata/src/Validation/DiagnosticReporter.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { I18N } from "@bentley/imodeljs-i18n"; +import { AnyDiagnostic } from "./Diagnostic"; +import assert = require("assert"); + +const translationNamespace = "ECSchemaMetaData"; +const subTranslationNamespace = "Diagnostics"; +const baseTranslationKey = translationNamespace + ":" + subTranslationNamespace; + +/** + * Interface used to report [[IDiagnostics]] objects created during schema validation. + */ +export interface IDiagnosticReporter { + /** The I18N object to use for message translation. */ + i18N?: I18N; + + /** + * Handles the given [[IDiagnostic]] based on the implementation requirements for a + * given reporter. + * @param diagnostic The diagnostic to report. + */ + report(diagnostic: AnyDiagnostic): void; +} + +/** + * The abstract base class for all [[IDiagnosticReporter]] implementations. + */ +export abstract class DiagnosticReporterBase implements IDiagnosticReporter { + constructor(i18n?: I18N) { + this.i18N = i18n; + } + + /** The I18N object to use for message translation. If undefined, no translation will occur. */ + public i18N?: I18N; + + /** + * Prior to reporting the [[IDiagnostic]], the diagnostic message is formatted (with translations) + * base on arguments contained in the diagnostic. Calls reportDiagnostic after the message is formatted. + * @param diagnostic The diagnostic to report. + */ + public report(diagnostic: AnyDiagnostic) { + const message = this.formatMessage(diagnostic); + this.reportDiagnostic(diagnostic, message); + } + + /** + * Handles the given [[IDiagnostic]] based on the implementation requirements for a + * given reporter. + * @param diagnostic The diagnostic to report. + * @param messageText The formatted message. + */ + protected abstract reportDiagnostic(diagnostic: AnyDiagnostic, messageText: string): void; + + /** + * Helper method that formats string with provided arguments where the place holders + * are in the the format '{0}', '{1}', etc. + * @param text The text to format. + * @param args The arguments to place in the text. + * @param baseIndex The base index for the args, used for validation (typically 0, which is the default). + */ + protected formatStringFromArgs(text: string, args: ArrayLike, baseIndex = 0): string { + return text.replace(/{(\d+)}/g, (_match, index: string) => this.assertDefined(args[+index + baseIndex])); + } + + private formatMessage(diagnostic: AnyDiagnostic): string { + let translatedMessage = this.translateMessage(diagnostic); + + if (diagnostic.messageArgs.length > 0) { + translatedMessage = this.formatStringFromArgs(translatedMessage, diagnostic.messageArgs); + } + + return translatedMessage; + } + + private translateMessage(diagnostic: AnyDiagnostic): string { + if (!this.i18N) + return diagnostic.messageText; + + return this.i18N.translate(this.getTranslationKey(diagnostic)); + } + + private getTranslationKey(diagnostic: AnyDiagnostic): string { + return baseTranslationKey + "." + diagnostic.code; + } + + private assertDefined(value: T | null | undefined, message?: string): T { + if (value === undefined || value === null) return assert(false, message) as never; + return value; + } +} diff --git a/core/ecschema-metadata/src/Validation/DiagnosticReporters.ts b/core/ecschema-metadata/src/Validation/DiagnosticReporters.ts deleted file mode 100644 index fcb0bbf..0000000 --- a/core/ecschema-metadata/src/Validation/DiagnosticReporters.ts +++ /dev/null @@ -1,55 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ - -import { Diagnostic, DiagnosticCategory, IDiagnosticReporter } from "./Diagnostics"; -import { ECValidationError } from "./ValidationException"; -import { Logger } from "@bentley/bentleyjs-core"; - -const loggingCategory = "ecschema-metadata"; - -/** A [[IDiagnosticReporter]] for logging [[Diagnostic]] objects. */ -export class LoggingDiagnosticReporter implements IDiagnosticReporter { - public report(diagnostic: Diagnostic) { - switch (diagnostic.category) { - case DiagnosticCategory.Error: - Logger.logError(loggingCategory, diagnostic.defaultMessageText, () => this.getLogMetaData(diagnostic)); - return; - case DiagnosticCategory.Warning: - Logger.logWarning(loggingCategory, diagnostic.defaultMessageText, () => this.getLogMetaData(diagnostic)); - return; - case DiagnosticCategory.Message: - case DiagnosticCategory.Suggestion: - Logger.logInfo(loggingCategory, diagnostic.defaultMessageText, () => this.getLogMetaData(diagnostic)); - return; - default: - Logger.logTrace(loggingCategory, diagnostic.defaultMessageText, () => this.getLogMetaData(diagnostic)); - } - } - - private getLogMetaData(diagnostic: Diagnostic) { - return { ...diagnostic, ...{ messageText: undefined, defaultMessageText: undefined } }; - } -} - -/** A [[IDiagnosticReporter]] for throwing errors for a given [[Diagnostic]] object. */ -export class ExceptionDiagnosticReporter implements IDiagnosticReporter { - public report(diagnostic: Diagnostic) { - if (diagnostic.category === DiagnosticCategory.Error) - throw new ECValidationError(diagnostic.code, diagnostic.messageText); - } -} - -/** A [[IDiagnosticReporter]] for storing all reported [[Diagnostic]] objects in a collection. */ -export class CollectionDiagnosticReporter implements IDiagnosticReporter { - private _diagnostics: Diagnostic[] = []; - - public report(diagnostic: Diagnostic) { - this._diagnostics.push(diagnostic); - } - - public get ReportedDiagnostics(): Diagnostic[] { - return this._diagnostics; - } -} diff --git a/core/ecschema-metadata/src/Validation/Diagnostics.ts b/core/ecschema-metadata/src/Validation/Diagnostics.ts deleted file mode 100644 index 4c620ea..0000000 --- a/core/ecschema-metadata/src/Validation/Diagnostics.ts +++ /dev/null @@ -1,112 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ - -import { SchemaItemType } from "../ECObjects"; -import { Schema } from "../Metadata/Schema"; -import { SchemaItem } from "../Metadata/SchemaItem"; - -export enum ECValidationStatus { - VALIDATION_ERROR_BASE = 0x98EC, - Success = 0, - BaseClassIsSealed = VALIDATION_ERROR_BASE + 1, - BaseClassOfDifferentType = VALIDATION_ERROR_BASE + 2, -} - -function createMessage(code: number, category: DiagnosticCategory, diagnosticType: DiagnosticType, key: string, message: string): DiagnosticMessage { - return { code, category, diagnosticType, key, message }; -} - -export function isDiagnostic(obj: any): obj is Diagnostic { - return !!obj && - typeof obj === "object" && - "category" in obj && - "code" in obj && - "messageText" in obj && - "defaultMessageText" in obj; -} - -export function isSchemaDiagnostic(obj: any): obj is DiagnosticWithSchema { - return isDiagnostic(obj) && "schema" in obj; -} - -export function isSchemaItemDiagnostic(obj: any): obj is DiagnosticWithSchema { - return isSchemaDiagnostic(obj) && "schemaItem" in obj; -} - -export function isPropertyDiagnostic(obj: any): obj is DiagnosticWithSchema { - return isSchemaItemDiagnostic(obj) && "propertyName" in obj; -} - -/** Interface used to report [[Diagnostic]] objects. */ -export interface IDiagnosticReporter { - report (diagnostic: Diagnostic): any; -} - -/** Interface that defines the required structure of a diagnostic message. */ -export interface DiagnosticMessage { - key: string; - category: DiagnosticCategory; - diagnosticType: DiagnosticType; - code: ECValidationStatus; - message: string; -} - -/** Defines the possible diagnostic categories. */ -export enum DiagnosticCategory { - Warning, - Error, - Suggestion, - Message, -} - -/** Defines the possible diagnostic types. */ -export enum DiagnosticType { - None, - Schema, - SchemaItem, - Property, -} - -/* Interface that defines the structure of a Diagnostic */ -export interface Diagnostic extends DiagnosticRelatedInformation { - relatedInformation?: DiagnosticRelatedInformation[]; -} - -/* Interface that defines all the possible information of a Diagnostic */ -export interface DiagnosticRelatedInformation { - category: DiagnosticCategory; - code: ECValidationStatus; - schema: Schema | undefined; - schemaItem: SchemaItem | undefined; - schemaItemType: SchemaItemType | undefined; - propertyName: string | undefined; - messageText: string; - defaultMessageText: string; -} - -/* Interface that defines a [[Diagnostic]] in the context of a [[Schema]].*/ -export interface DiagnosticWithSchema extends Diagnostic { - schema: Schema; -} - -/* Interface that defines a [[Diagnostic]] with the context of a [[SchemaItem]].*/ -export interface DiagnosticWithSchemaItem extends DiagnosticWithSchema { - schemaItem: SchemaItem; -} - -/* Interface that defines a [[Diagnostic]] that contains property information.*/ -export interface DiagnosticWithProperty extends DiagnosticWithSchemaItem { - propertyName: string; -} - -/** - * The list of [[DiagnosticMessage]] objects supported by the schema diagnostics framework. - */ -export const DIAGNOSTICS = { - /** Required message parameters: childClass.FullName, baseClass.FullName */ - BaseClassIsSealed: createMessage(ECValidationStatus.BaseClassIsSealed, DiagnosticCategory.Error, DiagnosticType.SchemaItem, ECValidationStatus[ECValidationStatus.BaseClassIsSealed], "Class '{0}' cannot derive from sealed base class '{1}'."), - /** Required message parameters: childClass.FullName, baseClass.FullName, baseClass.schemaItemType */ - BaseClassOfDifferentType: createMessage(ECValidationStatus.BaseClassOfDifferentType, DiagnosticCategory.Error, DiagnosticType.SchemaItem, ECValidationStatus[ECValidationStatus.BaseClassOfDifferentType], "Class '{0}' cannot derive from base class '{1}' of type '{2}'."), -}; diff --git a/core/ecschema-metadata/src/Validation/ECRules.ts b/core/ecschema-metadata/src/Validation/ECRules.ts new file mode 100644 index 0000000..2298e74 --- /dev/null +++ b/core/ecschema-metadata/src/Validation/ECRules.ts @@ -0,0 +1,442 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { ECStringConstants } from "../Constants"; +import { ECClassModifier, PrimitiveType, primitiveTypeToString, SchemaItemType, schemaItemTypeToString } from "../ECObjects"; +import { AnyClass } from "../Interfaces"; +import { ECClass } from "../Metadata/Class"; +import { CustomAttribute, CustomAttributeContainerProps } from "../Metadata/CustomAttribute"; +import { EntityClass } from "../Metadata/EntityClass"; +import { Enumeration } from "../Metadata/Enumeration"; +import { Mixin } from "../Metadata/Mixin"; +import { AnyProperty, PrimitiveProperty, Property } from "../Metadata/Property"; +import { RelationshipClass, RelationshipConstraint } from "../Metadata/RelationshipClass"; +import * as Diagnostic from "./Diagnostic"; +import { IRuleSet } from "./Rules"; + +const ruleSetName = "ECObjects"; + +function getCode(code: number): string { + return ruleSetName + ":" + code; +} + +/** The unique diagnostic codes for ECObjects rules. */ +// tslint:disable-next-line:variable-name +export const DiagnosticCodes = { + BaseClassIsSealed: getCode(100), + BaseClassOfDifferentType: getCode(101), + IncompatibleValueTypePropertyOverride: getCode(102), + IncompatibleTypePropertyOverride: getCode(103), + IncompatibleUnitPropertyOverride: getCode(104), + MixinAppliedToClassMustDeriveFromConstraint: getCode(105), + AbstractConstraintMustNarrowBaseConstraints: getCode(106), + DerivedConstraintsMustNarrowBaseConstraints: getCode(107), + ConstraintClassesDeriveFromAbstractContraint: getCode(108), + AtLeastOneConstraintClassDefined: getCode(109), + AbstractConstraintMustExistWithMultipleConstraints: getCode(110), + CustomAttributeNotOfConcreteClass: getCode(111), + EnumerationTypeUnsupported: getCode(112), +}; + +/** + * The list of [[IDiagnostic]] implementation classes used by the EC rule implementations. + */ +// tslint:disable-next-line:variable-name +export const Diagnostics = { + /** Required message parameters: childClass.FullName, baseClass.FullName */ + BaseClassIsSealed: Diagnostic.createClassDiagnosticClass<[string, string]>(DiagnosticCodes.BaseClassIsSealed, + "Class '{0}' cannot derive from sealed base class '{1}'."), + + /** Required message parameters: childClass.FullName, baseClass.FullName, baseClass.schemaItemType */ + BaseClassIsOfDifferentType: Diagnostic.createClassDiagnosticClass<[string, string, string]>(DiagnosticCodes.BaseClassOfDifferentType, + "Class '{0}' cannot derive from base class '{1}' of type '{2}'."), + + /** Required message parameters: childClass.FullName, property name, baseClass.FullName, base value type, child value type */ + IncompatibleValueTypePropertyOverride: Diagnostic.createPropertyDiagnosticClass<[string, string, string, string, string]>(DiagnosticCodes.IncompatibleValueTypePropertyOverride, + "The ECProperty '{0}:{1}' has a base property '{2}:{1}' with a value type of {3} which is incompatible with the value type of {4}."), + + /** Required message parameters: childClass.FullName, property name, baseClass.FullName, base property type, child property type */ + IncompatibleTypePropertyOverride: Diagnostic.createPropertyDiagnosticClass<[string, string, string, string, string]>(DiagnosticCodes.IncompatibleTypePropertyOverride, + "The ECProperty '{0}:{1}' has a base property '{2}:{1}' with a type of {3} which is incompatible with the type of {4}."), + + /** Required message parameters: childClass.Name, property name, baseClass.Name, baseClass Koq name, baseClass Koq persistence unit name, child class Koq persistence unit name, child class Koq name */ + IncompatibleUnitPropertyOverride: Diagnostic.createPropertyDiagnosticClass<[string, string, string, string, string, string, string]>(DiagnosticCodes.IncompatibleUnitPropertyOverride, + "The ECProperty '{0}:{1}' has a base property '{2}:{1}' with KindOfQuantity '{3}' with persistence unit '{4}' which is not the same as the persistence unit '{5}' of the provided KindOfQuantity '{6}'."), + + /** Required message parameters: relationship end (source/target), relationship name */ + AtLeastOneConstraintClassDefined: Diagnostic.createRelationshipConstraintDiagnosticClass<[string, string]>(DiagnosticCodes.AtLeastOneConstraintClassDefined, + "The {0}-Constraint of '{1}' does not contain any constraint classes."), + + /** Required message parameters: relationship end (source/target), relationship name */ + AbstractConstraintMustExistWithMultipleConstraints: Diagnostic.createRelationshipConstraintDiagnosticClass<[string, string]>(DiagnosticCodes.AbstractConstraintMustExistWithMultipleConstraints, + "The {0}-Constraint of '{1}' has multiple constraint classes which requires an abstract constraint to be defined."), + + /** Required message parameters: Enumeration name */ + EnumerationTypeUnsupported: Diagnostic.createSchemaItemDiagnosticClass(DiagnosticCodes.EnumerationTypeUnsupported, + "Enumeration '{0}' has invalid primitive type."), + + /** Required message parameters: mixin class fullName, class fullName, applies to constraint class fullName */ + MixinAppliedToClassMustDeriveFromConstraint: Diagnostic.createSchemaItemDiagnosticClass(DiagnosticCodes.MixinAppliedToClassMustDeriveFromConstraint, + "Mixin '{0}' cannot be applied to the class '{1}' because it does not satisfy the applies to constraint '{2}'."), + + /** Required message parameters: CustomAttribute container name and CustomAttributeClass name. */ + CustomAttributeNotOfConcreteClass: Diagnostic.createCustomAttributeContainerDiagnosticClass<[string, string]>(DiagnosticCodes.CustomAttributeNotOfConcreteClass, + "The CustomAttribute container '{0}' has a CustomAttribute with the class '{1}' which is not a concrete class."), + + /** Required message parameters: abstract constraint class name, relationship end (source/target), relationship name, base relationship name */ + AbstractConstraintMustNarrowBaseConstraints: Diagnostic.createSchemaItemDiagnosticClass(DiagnosticCodes.AbstractConstraintMustNarrowBaseConstraints, + "The abstract constraint class '{0}' on the {1}-Constraint of '{2}' is not supported by the base class constraint in '{3}'."), + + /** Required message parameters: constraint class name, relationship end (source/target), relationship name, base relationship name */ + DerivedConstraintsMustNarrowBaseConstraints: Diagnostic.createSchemaItemDiagnosticClass(DiagnosticCodes.DerivedConstraintsMustNarrowBaseConstraints, + "The constraint class '{0}' on the {1}-Constraint of '{2}' is not supported by the base class constraint in '{3}'."), + + /** Required message parameters: constraint class name, relationship end (source/target), relationship name, abstract constraint class name */ + ConstraintClassesDeriveFromAbstractContraint: Diagnostic.createSchemaItemDiagnosticClass(DiagnosticCodes.ConstraintClassesDeriveFromAbstractContraint, + "The constraint class '{0}' on the {1}-Constraint of '{2}' is not derived from the abstract constraint class '{3}'."), +}; + +/** All schema validation rules that fall under the category of ECObjects. */ +// tslint:disable-next-line:variable-name +export const ECRuleSet: IRuleSet = { + name: ruleSetName, + classRules: [ + baseClassIsSealed, + baseClassIsOfDifferentType, + ], + propertyRules: [ + incompatibleValueTypePropertyOverride, + incompatibleTypePropertyOverride, + incompatibleUnitPropertyOverride, + ], + relationshipRules: [ + abstractConstraintMustNarrowBaseConstraints, + constraintClassesDeriveFromAbstractContraint, + derivedConstraintsMustNarrowBaseConstraints, + ], + relationshipConstraintRules: [ + atLeastOneConstraintClassDefined, + abstractConstraintMustExistWithMultipleConstraints, + ], + enumerationRules: [ + enumerationTypeUnsupported, + ], + entityClassRules: [ + mixinAppliedToClassMustDeriveFromConstraint, + ], + customAttributeInstanceRules: [ + customAttributeNotOfConcreteClass, + ], +}; + +/** EC Rule: Sealed classes cannot be a base class. */ +export async function* baseClassIsSealed(ecClass: AnyClass): AsyncIterable> { + if (!ecClass.baseClass) + return; + + const baseClass = await ecClass.baseClass; + // return if rule passed + if (baseClass.modifier !== ECClassModifier.Sealed) + return; + + yield new Diagnostics.BaseClassIsSealed(ecClass, [ecClass.fullName, baseClass!.fullName]); +} + +/** EC Rule: Base and child class must be of the same type (i.e. Entity, Mixin, Relationship, etc.) */ +export async function* baseClassIsOfDifferentType(ecClass: AnyClass): AsyncIterable> { + if (!ecClass.baseClass) + return; + + const baseClass = await ecClass.baseClass; + // return if rule passed + if (baseClass.schemaItemType === ecClass.schemaItemType) + return; + + const itemType = schemaItemTypeToString(baseClass.schemaItemType); + yield new Diagnostics.BaseClassIsOfDifferentType(ecClass, [ecClass.fullName, baseClass!.fullName, itemType]); +} + +/** EC Rule: When overriding a class primitive property, the child and base property must be of the same type (string, number, etc...). */ +export async function* incompatibleValueTypePropertyOverride(property: AnyProperty): AsyncIterable> { + if (!property.class.baseClass) + return; + + const primitiveType = getPrimitiveType(property); + if (!primitiveType) + return; + + async function callback(baseClass: ECClass): Promise | undefined> { + const baseProperty = await baseClass.getProperty(property.name); + if (!baseProperty) + return; + + // Other rules will catch this if false, but we need to make sure + // types match for this rule to be valid. + if (!propertyTypesMatch(property, baseProperty)) + return; + + const baseType = getPrimitiveType(baseProperty); + + // Return if rule passed + if (!baseType || primitiveType === baseType) + return; + + return new Diagnostics.IncompatibleValueTypePropertyOverride(property, [property.class.fullName, property.name, baseClass.fullName, primitiveTypeToString(baseType), primitiveTypeToString(primitiveType!)]); + } + + for await (const baseClass of property.class.getAllBaseClasses()) { + const result = await callback(baseClass); + if (result) + yield result; + } +} + +/** EC Rule: When overriding a class property, the child and base property must be of the same property type (primitive, struct, enumeration, etc...). */ +export async function* incompatibleTypePropertyOverride(property: AnyProperty): AsyncIterable> { + if (!property.class.baseClass) + return; + + async function callback(baseClass: ECClass): Promise | undefined> { + const baseProperty = await baseClass.getProperty(property.name); + if (!baseProperty) + return; + + // Return if rule passed + if (propertyTypesMatch(property, baseProperty)) + return; + + return new Diagnostics.IncompatibleTypePropertyOverride(property, [property.class.fullName, property.name, baseClass.fullName, baseProperty.constructor.name, property.constructor.name]); + } + + for await (const baseClass of property.class.getAllBaseClasses()) { + const result = await callback(baseClass); + if (result) + yield result; + } +} + +/** EC Rule: When overriding a kindOfQuantity property, the child and base property units must be the same. */ +export async function* incompatibleUnitPropertyOverride(property: AnyProperty): AsyncIterable> { + if (!property.kindOfQuantity || !property.class.baseClass) + return; + + async function callback(baseClass: ECClass): Promise | undefined> { + const baseProperty = await baseClass.getProperty(property.name); + if (!baseProperty || !baseProperty.kindOfQuantity) + return; + + // Other rules will catch this if false, but we need to make sure + // types match for this rule to be valid. + if (!propertyTypesMatch(property, baseProperty)) + return; + + const koq = await property.kindOfQuantity; + const baseKoq = await baseProperty.kindOfQuantity; + if (!koq || !baseKoq) + return; + + const unit = await koq.persistenceUnit; + const baseUnit = await baseKoq.persistenceUnit; + + if (!unit || !baseUnit) + return; + + // return if rule passed + if (unit.key.matches(baseUnit.key)) + return; + + return new Diagnostics.IncompatibleUnitPropertyOverride(property, [property.class.fullName, property.name, baseClass.fullName, + baseKoq.fullName, baseUnit.fullName, unit.fullName, koq.fullName]); + } + + for await (const baseClass of property.class.getAllBaseClasses()) { + const result = await callback(baseClass); + if (result) + yield result; + } +} + +/** EC Rule: When overriding a RelationshipClass, the derived abstract constraint must narrow the base constraint classes. */ +export async function* abstractConstraintMustNarrowBaseConstraints(ecClass: RelationshipClass): AsyncIterable> { + if (!ecClass.baseClass) + return; + + const baseRelationship = await ecClass.baseClass as RelationshipClass; + + const sourceResult = await applyAbstractConstraintMustNarrowBaseConstraints(ecClass, ecClass.source, baseRelationship); + if (sourceResult) + yield sourceResult; + const targetResult = await applyAbstractConstraintMustNarrowBaseConstraints(ecClass, ecClass.target, baseRelationship); + if (targetResult) + yield targetResult; +} + +/** EC Rule: When overriding a RelationshipClass, derived constraint classes must narrow base constraint classes. */ +export async function* derivedConstraintsMustNarrowBaseConstraints(ecClass: RelationshipClass): AsyncIterable> { + if (!ecClass.baseClass) + return; + + const baseRelationship = await ecClass.baseClass as RelationshipClass; + + const sourceResult = await applyDerivedConstraintsMustNarrowBaseConstraints(ecClass, ecClass.source, baseRelationship); + if (sourceResult) + yield sourceResult; + const targetResult = await applyDerivedConstraintsMustNarrowBaseConstraints(ecClass, ecClass.target, baseRelationship); + if (targetResult) + yield targetResult; +} + +/** EC Rule: All constraint classes must have a common base class specified in the abstract constraint. */ +export async function* constraintClassesDeriveFromAbstractContraint(ecClass: RelationshipClass): AsyncIterable> { + const sourceResult = await applyConstraintClassesDeriveFromAbstractContraint(ecClass, ecClass.source); + if (sourceResult) + yield sourceResult; + const targetResult = await applyConstraintClassesDeriveFromAbstractContraint(ecClass, ecClass.target); + if (targetResult) + yield targetResult; +} + +/** EC Rule: At least on concrete constraint class must be defined in the list of constraint classes. */ +export async function* atLeastOneConstraintClassDefined(constraint: RelationshipConstraint): AsyncIterable> { + if (!constraint.constraintClasses || constraint.constraintClasses.length === 0) { + const constraintType = constraint.isSource ? ECStringConstants.RELATIONSHIP_END_SOURCE : ECStringConstants.RELATIONSHIP_END_TARGET; + yield new Diagnostics.AtLeastOneConstraintClassDefined(constraint, [constraintType, constraint.relationshipClass.fullName]); + } +} + +/** EC Rule: If multiple constraints exist, an abstract constraint must be defined. */ +export async function* abstractConstraintMustExistWithMultipleConstraints(constraint: RelationshipConstraint): AsyncIterable> { + if (!constraint.constraintClasses || constraint.constraintClasses.length <= 1) { + return; + } + + const abstractConstraint = getAbstractConstraint(constraint); + if (abstractConstraint) + return; + + const constraintType = constraint.isSource ? ECStringConstants.RELATIONSHIP_END_SOURCE : ECStringConstants.RELATIONSHIP_END_TARGET; + yield new Diagnostics.AbstractConstraintMustExistWithMultipleConstraints(constraint, [constraintType, constraint.relationshipClass.fullName]); +} + +function propertyTypesMatch(propertyA: Property, propertyB: Property) { + return propertyA.constructor.name === propertyB.constructor.name; +} + +function getPrimitiveType(property: Property): PrimitiveType | undefined { + if (property.isPrimitive) + return (property as PrimitiveProperty).primitiveType; + + return undefined; +} + +/** EC Rule: Enumeration type must be a string or integer */ +export async function* enumerationTypeUnsupported(enumeration: Enumeration): AsyncIterable> { + const type = enumeration.type; + if (type === PrimitiveType.Integer || type === PrimitiveType.String) + return; + + yield new Diagnostics.EnumerationTypeUnsupported(enumeration, [enumeration.fullName]); +} + +/** EC Rule: Mixin applied to class must derived from the Mixin appliesTo constraint. */ +export async function* mixinAppliedToClassMustDeriveFromConstraint(entityClass: EntityClass): AsyncIterable> { + for (const lazyMixin of entityClass.mixins) { + const mixin = await lazyMixin; + if (!mixin.appliesTo) + continue; + + if (!await entityClass.is(await mixin.appliesTo)) + yield new Diagnostics.MixinAppliedToClassMustDeriveFromConstraint(entityClass, [mixin.fullName, entityClass.fullName, mixin.appliesTo.fullName]); + } + + return; +} + +/** EC Rule: CustomAttribute instance must be of a concrete CustomAttributeClass. */ +export async function* customAttributeNotOfConcreteClass(container: CustomAttributeContainerProps, customAttribute: CustomAttribute): AsyncIterable> { + const schema = container.schema; + const caClass = await schema.lookupItem(customAttribute.className) as ECClass; + if (!caClass) + return; + + if (caClass.modifier !== ECClassModifier.Abstract) + return; + + yield new Diagnostics.CustomAttributeNotOfConcreteClass(container, [container.fullName, caClass.fullName]); +} + +async function applyAbstractConstraintMustNarrowBaseConstraints(ecClass: RelationshipClass, constraint: RelationshipConstraint, baseRelationship: RelationshipClass): Promise | undefined> { + const baseConstraint = constraint.isSource ? baseRelationship.source : baseRelationship.target; + const abstractConstraint = await constraint.abstractConstraint; + if (!abstractConstraint) + return; + + if (await baseConstraint.supportsClass(abstractConstraint)) + return; + + const constraintType = constraint.isSource ? ECStringConstants.RELATIONSHIP_END_SOURCE : ECStringConstants.RELATIONSHIP_END_TARGET; + return new Diagnostics.AbstractConstraintMustNarrowBaseConstraints(ecClass, [abstractConstraint.fullName, constraintType, constraint.relationshipClass.fullName, baseRelationship.fullName]); +} + +async function applyDerivedConstraintsMustNarrowBaseConstraints(ecClass: RelationshipClass, constraint: RelationshipConstraint, baseRelationship: RelationshipClass): Promise | undefined> { + const baseConstraint = constraint.isSource ? baseRelationship.source : baseRelationship.target; + + if (!constraint.constraintClasses) + return; + + for (const classPromise of constraint.constraintClasses) { + const constraintClass = await classPromise; + + if (await baseConstraint.supportsClass(constraintClass)) + continue; + + const constraintType = constraint.isSource ? ECStringConstants.RELATIONSHIP_END_SOURCE : ECStringConstants.RELATIONSHIP_END_TARGET; + return new Diagnostics.DerivedConstraintsMustNarrowBaseConstraints(ecClass, [constraintClass.fullName, constraintType, constraint.relationshipClass.fullName, baseRelationship.fullName]); + } + + return; +} + +async function applyConstraintClassesDeriveFromAbstractContraint(ecClass: RelationshipClass, constraint: RelationshipConstraint): Promise | undefined> { + const abstractConstraint = await getAbstractConstraint(constraint); + if (!abstractConstraint) + return; + + if (!constraint.constraintClasses) + return; + + for (const classPromise of constraint.constraintClasses) { + const constraintClass = await classPromise; + + if (constraintClass.schemaItemType === SchemaItemType.Mixin && abstractConstraint.schemaItemType === SchemaItemType.EntityClass) { + if (!await (constraintClass as Mixin).applicableTo(abstractConstraint as EntityClass)) { + const constraintType = constraint.isSource ? ECStringConstants.RELATIONSHIP_END_SOURCE : ECStringConstants.RELATIONSHIP_END_TARGET; + return new Diagnostics.ConstraintClassesDeriveFromAbstractContraint(ecClass, [constraintClass.fullName, constraintType, constraint.relationshipClass.fullName, abstractConstraint.fullName]); + } + continue; + } + + if (!await constraintClass.is(abstractConstraint)) { + const constraintType = constraint.isSource ? ECStringConstants.RELATIONSHIP_END_SOURCE : ECStringConstants.RELATIONSHIP_END_TARGET; + return new Diagnostics.ConstraintClassesDeriveFromAbstractContraint(ecClass, [constraintClass.fullName, constraintType, constraint.relationshipClass.fullName, abstractConstraint.fullName]); + } + } + + return; +} + +async function getAbstractConstraint(constraint: RelationshipConstraint): Promise { + const abstractConstraint = await constraint.abstractConstraint; + if (abstractConstraint) + return abstractConstraint; + + const baseRelationship = await constraint.relationshipClass.baseClass as RelationshipClass; + if (!baseRelationship) + return; + + const baseConstraint = constraint.isSource ? baseRelationship.source : baseRelationship.target; + + return getAbstractConstraint(baseConstraint); +} diff --git a/core/ecschema-metadata/src/Validation/LoggingDiagnosticReporter.ts b/core/ecschema-metadata/src/Validation/LoggingDiagnosticReporter.ts new file mode 100644 index 0000000..adb2805 --- /dev/null +++ b/core/ecschema-metadata/src/Validation/LoggingDiagnosticReporter.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { Logger } from "@bentley/bentleyjs-core"; +import { AnyDiagnostic, DiagnosticCategory } from "./Diagnostic"; +import { DiagnosticReporterBase } from "./DiagnosticReporter"; + +const loggingCategory = "ecschema-metadata"; + +/** An [[IDiagnosticReporter]] for logging [[IDiagnostic]] objects. */ +export class LoggingDiagnosticReporter extends DiagnosticReporterBase { + public reportDiagnostic(diagnostic: AnyDiagnostic, messageText: string) { + switch (diagnostic.category) { + case DiagnosticCategory.Error: + Logger.logError(loggingCategory, messageText, () => this.getLogMetaData(diagnostic)); + return; + case DiagnosticCategory.Warning: + Logger.logWarning(loggingCategory, messageText, () => this.getLogMetaData(diagnostic)); + return; + case DiagnosticCategory.Message: + case DiagnosticCategory.Suggestion: + Logger.logInfo(loggingCategory, messageText, () => this.getLogMetaData(diagnostic)); + return; + default: + Logger.logTrace(loggingCategory, messageText, () => this.getLogMetaData(diagnostic)); + } + } + + private getLogMetaData(diagnostic: AnyDiagnostic) { + return { ...diagnostic, ...{ code: diagnostic.code, category: diagnostic.category, diagnosticType: diagnostic.diagnosticType, messageText: undefined, messageArgs: undefined } }; + } +} diff --git a/core/ecschema-metadata/src/Validation/Rules.ts b/core/ecschema-metadata/src/Validation/Rules.ts new file mode 100644 index 0000000..85e6d9b --- /dev/null +++ b/core/ecschema-metadata/src/Validation/Rules.ts @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { AnyClass, AnyECType } from "../Interfaces"; +import { StructClass } from "../Metadata/Class"; +import { Constant } from "../Metadata/Constant"; +import { CustomAttribute, CustomAttributeContainerProps } from "../Metadata/CustomAttribute"; +import { CustomAttributeClass } from "../Metadata/CustomAttributeClass"; +import { EntityClass } from "../Metadata/EntityClass"; +import { Enumeration } from "../Metadata/Enumeration"; +import { Format } from "../Metadata/Format"; +import { InvertedUnit } from "../Metadata/InvertedUnit"; +import { KindOfQuantity } from "../Metadata/KindOfQuantity"; +import { Mixin } from "../Metadata/Mixin"; +import { Phenomenon } from "../Metadata/Phenomenon"; +import { AnyProperty } from "../Metadata/Property"; +import { PropertyCategory } from "../Metadata/PropertyCategory"; +import { RelationshipClass, RelationshipConstraint } from "../Metadata/RelationshipClass"; +import { Schema } from "../Metadata/Schema"; +import { SchemaItem } from "../Metadata/SchemaItem"; +import { Unit } from "../Metadata/Unit"; +import { UnitSystem } from "../Metadata/UnitSystem"; +import { BaseDiagnostic } from "./Diagnostic"; + +/** Interface used for all rule implementations used during schema validation. */ +export type IRule = (ecDefinition: T, ...args: U[]) => AsyncIterable>; +export type BaseRule = IRule; + +/** Interface used to represent logical collection of [[IRule]] instances. */ +export interface IRuleSet { + /** The name of the rule set. */ + name: string; + + /** The rules that apply to [[Schema]] objects. */ + schemaRules?: Array>; + /** The rules that apply to [[SchemaItem]] objects. */ + schemaItemRules?: Array>; + /** The rules that apply to [[ECClass]] objects. */ + classRules?: Array>; + /** The rules that apply to [[Property]] objects. */ + propertyRules?: Array>; + /** The rules that apply to [[EntityClass]] objects. */ + entityClassRules?: Array>; + /** The rules that apply to [[StructClass]] objects. */ + structClassRules?: Array>; + /** The rules that apply to [[Mixin]] objects. */ + mixinRules?: Array>; + /** The rules that apply to [[RelationshipClass]] objects. */ + relationshipRules?: Array>; + /** The rules that apply to [[RelationshipConstraint]] objects. */ + relationshipConstraintRules?: Array>; + /** The rules that apply to [[CustomAttributeClass]] objects. */ + customAttributeClassRules?: Array>; + /** The rules that apply to [[CustomAttributeContainerProps]] objects. */ + customAttributeContainerRules?: Array>; + /** The rules that apply to [[CustomAttribute]] objects. */ + customAttributeInstanceRules?: Array>; + /** The rules that apply to [[Enumeration]] objects. */ + enumerationRules?: Array>; + /** The rules that apply to [[KindOfQuantity]] objects. */ + kindOfQuantityRules?: Array>; + /** The rules that apply to [[PropertyCategory]] objects. */ + propertyCategoryRules?: Array>; + /** The rules that apply to [[Format]] objects. */ + formatRules?: Array>; + /** The rules that apply to [[Unit]] objects. */ + unitRules?: Array>; + /** The rules that apply to [[InvertedUnit]] objects. */ + invertedUnitRules?: Array>; + /** The rules that apply to [[UnitSystem]] objects. */ + unitSystemRules?: Array>; + /** The rules that apply to [[Phenomenon]] objects. */ + phenomenonRules?: Array>; + /** The rules that apply to [[Constant]] objects. */ + constantRules?: Array>; +} diff --git a/core/ecschema-metadata/src/Validation/SchemaDiagnostics.ts b/core/ecschema-metadata/src/Validation/SchemaDiagnostics.ts deleted file mode 100644 index dda4320..0000000 --- a/core/ecschema-metadata/src/Validation/SchemaDiagnostics.ts +++ /dev/null @@ -1,197 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ - -import { I18N } from "@bentley/imodeljs-i18n"; -import { Schema } from "../Metadata/Schema"; -import { SchemaItem } from "../Metadata/SchemaItem"; -import * as Diagnostics from "./Diagnostics"; -import assert = require("assert"); - -const translationNamespace = "ECSchemaMetaData"; -const subTranslationNamespace = "Diagnostics"; -const baseTranslationKey = translationNamespace + ":" + subTranslationNamespace; - -/** - * A utility class that 'reports' messages defined as [[DiagnosticMessage]] objects. [[IDiagnosticReporter]] objects may be registered using - * the static method [[SchemaDiagnosticReporter.registerReporter]]. - */ -export class SchemaDiagnosticReporter { - private static _reporters: Diagnostics.IDiagnosticReporter[] = []; - private static _initialized = false; - - /** The [[I18N]] for this session. */ - public static i18n?: I18N; - - /** Indicates the reporter has been initialized and ready for use. */ - public static get initialized() { return SchemaDiagnosticReporter._initialized; } - - /** Gets the registered reporters. */ - public static get reporters() { return SchemaDiagnosticReporter._reporters; } - - /** - * Initializes the [[SchemaDiagnosticReporter]] instance. - * @param i18n The I18N used to for translation of messages. If not specified all messages will be in english. - */ - public static async startup(i18n?: I18N) { - if (SchemaDiagnosticReporter._initialized) - return; - - SchemaDiagnosticReporter.i18n = i18n; - if (SchemaDiagnosticReporter.i18n) - await SchemaDiagnosticReporter.i18n.registerNamespace(translationNamespace).readFinished; - - SchemaDiagnosticReporter._initialized = true; - } - - /** Un-initializes the reporter. */ - public static shutdown() { - this.unregisterAllReporters(); - this._initialized = false; - } - - /** - * Registers a [[IDiagnosticReporter]] which will be called when calling [[SchemaDiagnosticReporter.reportDiagnostic]]. - * @param reporter The [[IDiagnosticReporter]] to register. - */ - public static registerReporter(reporter: Diagnostics.IDiagnosticReporter) { - this._reporters.push(reporter); - } - - /** - * Calls all registered [[IDiagnosticReporter]] objects with the given [[DiagnosticMessage]]. - * @param message The [[DiagnosticMessage]] to report. - * @param args Any parameters required for message formatting. - */ - public static reportDiagnostic(message: Diagnostics.DiagnosticMessage, ...args: Array) { - if (!this._initialized) - throw new Error("SchemaDiagnosticReporter uninitialized"); - - const diagnostic = this.createDiagnostic(message, ...args); - this.report(diagnostic); - } - - /** - * Calls all registered [[IDiagnosticReporter]] objects with the given [[DiagnosticMessage]]. - * @param schema The [[Schema]] associated with the given message. - * @param message The [[DiagnosticMessage]] to report. - * @param args Any parameters required for message formatting. - */ - public static reportSchemaDiagnostic(schema: Schema, message: Diagnostics.DiagnosticMessage, ...args: Array): any { - if (!this._initialized) - throw new Error("SchemaDiagnosticReporter uninitialized"); - - const diagnostic = this.createSchemaDiagnostic(schema, message, ...args); - this.report(diagnostic); - } - - /** - * Calls all registered [[IDiagnosticReporter]] objects with the given [[DiagnosticMessage]]. - * @param schemaItem The [[SchemaItem]] associated with the given message. - * @param message The [[DiagnosticMessage]] to report. - * @param args Any parameters required for message formatting. - */ - public static reportSchemaItemDiagnostic(schemaItem: SchemaItem, message: Diagnostics.DiagnosticMessage, ...args: Array): any { - if (!this._initialized) - throw new Error("SchemaDiagnosticReporter uninitialized"); - - const diagnostic = this.createSchemaItemDiagnostic(schemaItem, message, ...args); - this.report(diagnostic); - } - - /** - * Calls all registered [[IDiagnosticReporter]] objects with the given [[DiagnosticMessage]]. - * @param schemaItem The [[SchemaItem]] associated with the given message. - * @param propertyName The property name associated with the given message. - * @param message The [[DiagnosticMessage]] to report. - * @param args Any parameters required for message formatting. - */ - public static reportSchemaPropertyDiagnostic(schemaItem: SchemaItem, propertyName: string, message: Diagnostics.DiagnosticMessage, ...args: Array): any { - if (!this._initialized) - throw new Error("SchemaDiagnosticReporter uninitialized"); - - const diagnostic = this.createSchemaPropertyDiagnostic(schemaItem, propertyName, message, ...args); - this.report(diagnostic); - } - - /** Removes all registered [[DiagnosticReporter]] instances. */ - public static unregisterAllReporters() { - this._reporters = []; - } - - /** - * A utility method that can be called by registered reporters that will translate the given - * [[DiagnosticMessage]] to the configured language. - * @param message The [[DiagnosticMessage]] to translate. - * - */ - public static translateMessage(message: Diagnostics.DiagnosticMessage): string { - if (!SchemaDiagnosticReporter.i18n) - return message.message; - - return SchemaDiagnosticReporter.i18n.translate(this.getTranslationKey(message)); - } - - private static report(diagnostic: Diagnostics.Diagnostic) { - for (const reporterEntry of this._reporters) { - reporterEntry.report(diagnostic); - } - } - - private static getTranslationKey(message: Diagnostics.DiagnosticMessage): string { - return baseTranslationKey + "." + message.key; - } - - private static createDiagnostic(message: Diagnostics.DiagnosticMessage, ...args: Array): Diagnostics.Diagnostic { - let translation = SchemaDiagnosticReporter.translateMessage(message); - let defaultText = message.message; - - if (args.length > 0) { - translation = this.formatStringFromArgs(translation, arguments, 1); - defaultText = this.formatStringFromArgs(defaultText, arguments, 1); - } - - return { - schema: undefined, - schemaItem: undefined, - schemaItemType: undefined, - propertyName: undefined, - - defaultMessageText: defaultText, - messageText: translation, - category: message.category, - code: message.code, - }; - } - - private static createSchemaDiagnostic(schema: Schema, message: Diagnostics.DiagnosticMessage, ...args: Array): Diagnostics.DiagnosticWithSchema { - const diagnostic = this.createDiagnostic(message, ...args); - diagnostic.schema = schema; - - return diagnostic as Diagnostics.DiagnosticWithSchema; - } - - private static createSchemaItemDiagnostic(schemaItem: SchemaItem, message: Diagnostics.DiagnosticMessage, ...args: Array): Diagnostics.DiagnosticWithSchemaItem { - const diagnostic = this.createSchemaDiagnostic(schemaItem.schema, message, ...args); - diagnostic.schemaItem = schemaItem; - - return diagnostic as Diagnostics.DiagnosticWithSchemaItem; - } - - private static createSchemaPropertyDiagnostic(schemaItem: SchemaItem, propertyName: string, message: Diagnostics.DiagnosticMessage, ...args: Array): Diagnostics.DiagnosticWithProperty { - const diagnostic = this.createSchemaItemDiagnostic(schemaItem, message, ...args); - diagnostic.propertyName = propertyName; - - return diagnostic as Diagnostics.DiagnosticWithProperty; - } - - private static formatStringFromArgs(text: string, args: ArrayLike, baseIndex = 0): string { - return text.replace(/{(\d+)}/g, (_match, index: string) => this.assertDefined(args[+index + baseIndex])); - } - - private static assertDefined(value: T | null | undefined, message?: string): T { - if (value === undefined || value === null) return assert(false, message) as never; - return value; - } -} diff --git a/core/ecschema-metadata/src/Validation/SchemaValidationVisitor.ts b/core/ecschema-metadata/src/Validation/SchemaValidationVisitor.ts new file mode 100644 index 0000000..63b3924 --- /dev/null +++ b/core/ecschema-metadata/src/Validation/SchemaValidationVisitor.ts @@ -0,0 +1,501 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { AnyClass } from "../Interfaces"; +import { StructClass } from "../Metadata/Class"; +import { Constant } from "../Metadata/Constant"; +import { CustomAttribute, CustomAttributeContainerProps } from "../Metadata/CustomAttribute"; +import { CustomAttributeClass } from "../Metadata/CustomAttributeClass"; +import { EntityClass } from "../Metadata/EntityClass"; +import { Enumeration } from "../Metadata/Enumeration"; +import { Format } from "../Metadata/Format"; +import { InvertedUnit } from "../Metadata/InvertedUnit"; +import { KindOfQuantity } from "../Metadata/KindOfQuantity"; +import { Mixin } from "../Metadata/Mixin"; +import { Phenomenon } from "../Metadata/Phenomenon"; +import { AnyProperty } from "../Metadata/Property"; +import { PropertyCategory } from "../Metadata/PropertyCategory"; +import { RelationshipClass, RelationshipConstraint } from "../Metadata/RelationshipClass"; +import { Schema } from "../Metadata/Schema"; +import { SchemaItem } from "../Metadata/SchemaItem"; +import { Unit } from "../Metadata/Unit"; +import { UnitSystem } from "../Metadata/UnitSystem"; +import { AnyDiagnostic } from "./Diagnostic"; +import { IDiagnosticReporter } from "./DiagnosticReporter"; +import { IRuleSet } from "./Rules"; +import { ISchemaPartVisitor } from "../SchemaPartVisitorDelegate"; + +interface RuleSetArray { + [name: string]: IRuleSet; +} + +/** + * A SchemaWalkerVisitor interface implementation that is used to validate ECObjects schemas using + * [[IRuleSet]] objects registered with the instance. Also allows for reporting of [[IDiagnostic]] + * objects returned from failing rules using [[IDiagnosticReporter]] implementations registered + * with an instance of this class. + */ +export class SchemaValidationVisitor implements ISchemaPartVisitor { + private _reporters: IDiagnosticReporter[] = []; + private _ruleSets: RuleSetArray = {}; + + /** Gets the IRule objects registered with the visitor. */ + public get ruleSets(): RuleSetArray { + return this._ruleSets; + } + + /** Gets the IDiagnosticReporter objects registered with the visitor. */ + public get diagnosticReporters(): IDiagnosticReporter[] { + return this._reporters; + } + + /** + * Registers a [[IDiagnosticReporter]] allowing the reporter to be + * notified when a [[IDiagnostic]] is created due to a rule violation. + * @param reporter The [[DiagnosticReporter]] to register. + */ + public registerReporter(...reporters: IDiagnosticReporter[]) { + for (const reporter of reporters) { + this._reporters.push(reporter); + } + } + + /** + * Registers a [[IRuleSet]] that will applied during schema traversal. + * @param ruleSet The [[IRuleSet]] to register. + */ + public registerRuleSet(ruleSet: IRuleSet) { + if (undefined !== this.ruleSets[ruleSet.name]) + throw new Error(`A RuleSet with the name '${ruleSet.name}' has already been registered.`); + + this.ruleSets[ruleSet.name] = ruleSet; + } + + /** + * Called before schema traversal. + * @param schema a Schema object. + */ + public async visitFullSchema(schema: Schema) { + for (const index of Object.keys(this._ruleSets)) { + await this.applySchemaRules(schema, this._ruleSets[index]); + } + } + + /** + * Called for each [[SchemaItem]] instance found during schema traversal. + * @param schemaItem a SchemaItem object. + */ + public async visitSchemaItem(schemaItem: SchemaItem) { + for (const index of Object.keys(this._ruleSets)) { + await this.applySchemaItemRules(schemaItem, this._ruleSets[index]); + } + } + + /** + * Called for each [[AnyClass]] instance found during schema traversal. + * @param ecClass an ECClass object. + */ + public async visitClass(ecClass: AnyClass): Promise { + for (const index of Object.keys(this._ruleSets)) { + await this.applyClassRules(ecClass, this._ruleSets[index]); + } + } + + /** + * Called for each [[AnyProperty]] instance of an ECClass. + * @param property an AnyProperty object. + */ + public async visitProperty(property: AnyProperty): Promise { + for (const index of Object.keys(this._ruleSets)) { + await this.applyPropertyRules(property, this._ruleSets[index]); + } + } + + /** + * Called for each [[EntityClass]] instance found during schema traversal. + * @param entityClass an EntityClass object. + */ + public async visitEntityClass(entity: EntityClass): Promise { + for (const index of Object.keys(this._ruleSets)) { + await this.applyEntityRules(entity, this._ruleSets[index]); + } + } + + /** + * Called for each [[StructClass]] instance found during schema traversal. + * @param structClass a StructClass object. + */ + public async visitStructClass(struct: StructClass): Promise { + for (const index of Object.keys(this._ruleSets)) { + await this.applyStructRules(struct, this._ruleSets[index]); + } + } + + /** + * Called for each [[Mixin]] instance found during schema traversal. + * @param mixin a Mixin object. + */ + public async visitMixin(mixin: Mixin): Promise { + for (const index of Object.keys(this._ruleSets)) { + await this.applyMixinRules(mixin, this._ruleSets[index]); + } + } + + /** + * Called for each [[RelationshipClass]] instance found during schema traversal. + * @param relationshipClass a RelationshipClass object. + */ + public async visitRelationshipClass(relationship: RelationshipClass): Promise { + for (const index of Object.keys(this._ruleSets)) { + await this.applyRelationshipRules(relationship, this._ruleSets[index]); + } + } + + /** + * Called for each [[RelationshipConstraint]] of each RelationshipClass found during schema traversal. + * @param relationshipConstraint a RelationshipConstraint object. + */ + public async visitRelationshipConstraint(constraint: RelationshipConstraint): Promise { + for (const index of Object.keys(this._ruleSets)) { + await this.applyRelationshipConstraintRules(constraint, this._ruleSets[index]); + } + } + + /** + * Called for each [[CustomAttributeClass]] instance found during schema traversal. + * @param customAttributeClass a CustomAttributeClass object. + */ + public async visitCustomAttributeClass(customAttribute: CustomAttributeClass): Promise { + for (const index of Object.keys(this._ruleSets)) { + await this.applyCustomAttributeRules(customAttribute, this._ruleSets[index]); + } + } + + /** + * Called for each [[CustomAttribute]] container in the schema. + * @param customAttributeContainer a [[CustomAttributeContainerProps]] object. + */ + public async visitCustomAttributeContainer(container: CustomAttributeContainerProps): Promise { + for (const index of Object.keys(this._ruleSets)) { + await this.applyCustomAttributeContainerRules(container, this._ruleSets[index]); + } + + if (!container.customAttributes) + return; + + for (const [, customAttribute] of container.customAttributes) { + for (const index of Object.keys(this._ruleSets)) { + await this.applyCustomAttributeInstanceRules(container, customAttribute, this._ruleSets[index]); + } + } + } + + /** + * Called for each [[Enumeration]] instance found during schema traversal. + * @param enumeration an Enumeration object. + */ + public async visitEnumeration(enumeration: Enumeration) { + for (const index of Object.keys(this._ruleSets)) { + await this.applyEnumerationRules(enumeration, this._ruleSets[index]); + } + } + + /** + * Called for each [[KindOfQuantity]] instance found during schema traversal. + * @param koq a KindOfQuantity object. + */ + public async visitKindOfQuantity(koq: KindOfQuantity) { + for (const index of Object.keys(this._ruleSets)) { + await this.applyKindOfQuantityRules(koq, this._ruleSets[index]); + } + } + + /** + * Called for each [[PropertyCategory]] instance found during schema traversal. + * @param category a PropertyCategory object. + */ + public async visitPropertyCategory(category: PropertyCategory) { + for (const index of Object.keys(this._ruleSets)) { + await this.applyPropertyCategoryRules(category, this._ruleSets[index]); + } + } + + /** + * Called for each [[Format]] instance found during schema traversal. + * @param format a Format object. + */ + public async visitFormat(format: Format): Promise { + for (const index of Object.keys(this._ruleSets)) { + await this.applyFormatRules(format, this._ruleSets[index]); + } + } + + /** + * Called for each [[Unit]] instance found during schema traversal. + * @param unit a Unit object. + */ + public async visitUnit(unit: Unit): Promise { + for (const index of Object.keys(this._ruleSets)) { + await this.applyUnitRules(unit, this._ruleSets[index]); + } + } + + /** + * Called for each [[InvertedUnit]] instance found during schema traversal. + * @param invertedUnit an InvertedUnit object. + */ + public async visitInvertedUnit(invertedUnit: InvertedUnit): Promise { + for (const index of Object.keys(this._ruleSets)) { + await this.applyInvertedUnitRules(invertedUnit, this._ruleSets[index]); + } + } + + /** + * Called for each [[UnitSystem]] instance found during schema traversal. + * @param unitSystem a UnitSystem object. + */ + public async visitUnitSystem(unitSystem: UnitSystem): Promise { + for (const index of Object.keys(this._ruleSets)) { + await this.applyUnitSystemRules(unitSystem, this._ruleSets[index]); + } + } + + /** + * Called for each [[Phenomenon]] instance found during schema traversal. + * @param phenomena a Phenomenon object. + */ + public async visitPhenomenon(phenomenon: Phenomenon): Promise { + for (const index of Object.keys(this._ruleSets)) { + await this.applyPhenomenonRules(phenomenon, this._ruleSets[index]); + } + } + + /** + * Called for each [[Constant]] instance found during schema traversal. + * @param constant a Constant object. + */ + public async visitConstant(constant: Constant): Promise { + for (const index of Object.keys(this._ruleSets)) { + await this.applyConstantRules(constant, this._ruleSets[index]); + } + } + + public async applySchemaRules(schema: Schema, ruleSet: IRuleSet) { + if (!ruleSet.schemaRules) + return; + + for (const rule of ruleSet.schemaRules) { + const result = rule(schema); + await this.reportDiagnostics(result); + } + } + + public async applySchemaItemRules(schemaItem: SchemaItem, ruleSet: IRuleSet) { + if (!ruleSet.schemaItemRules) + return; + + for (const rule of ruleSet.schemaItemRules) { + const result = rule(schemaItem); + await this.reportDiagnostics(result); + } + } + + public async applyClassRules(ecClass: AnyClass, ruleSet: IRuleSet) { + if (!ruleSet.classRules) + return; + + for (const rule of ruleSet.classRules) { + const result = rule(ecClass); + await this.reportDiagnostics(result); + } + } + + public async applyPropertyRules(property: AnyProperty, ruleSet: IRuleSet) { + if (!ruleSet.propertyRules) + return; + + for (const rule of ruleSet.propertyRules) { + const result = rule(property); + await this.reportDiagnostics(result); + } + } + + public async applyEntityRules(entityClass: EntityClass, ruleSet: IRuleSet) { + if (!ruleSet.entityClassRules) + return; + + for (const rule of ruleSet.entityClassRules) { + const result = rule(entityClass); + await this.reportDiagnostics(result); + } + } + + public async applyStructRules(structClass: StructClass, ruleSet: IRuleSet) { + if (!ruleSet.structClassRules) + return; + + for (const rule of ruleSet.structClassRules) { + const result = rule(structClass); + await this.reportDiagnostics(result); + } + } + + public async applyMixinRules(mixin: Mixin, ruleSet: IRuleSet) { + if (!ruleSet.mixinRules) + return; + + for (const rule of ruleSet.mixinRules) { + const result = rule(mixin); + await this.reportDiagnostics(result); + } + } + + public async applyRelationshipRules(relationship: RelationshipClass, ruleSet: IRuleSet) { + if (!ruleSet.relationshipRules) + return; + + for (const rule of ruleSet.relationshipRules) { + const result = rule(relationship); + await this.reportDiagnostics(result); + } + } + + public async applyRelationshipConstraintRules(constraint: RelationshipConstraint, ruleSet: IRuleSet) { + if (!ruleSet.relationshipConstraintRules) + return; + + for (const rule of ruleSet.relationshipConstraintRules) { + const result = rule(constraint); + await this.reportDiagnostics(result); + } + } + + public async applyCustomAttributeRules(customAttribute: CustomAttributeClass, ruleSet: IRuleSet) { + if (!ruleSet.customAttributeClassRules) + return; + + for (const rule of ruleSet.customAttributeClassRules) { + const result = rule(customAttribute); + await this.reportDiagnostics(result); + } + } + + public async applyCustomAttributeContainerRules(container: CustomAttributeContainerProps, ruleSet: IRuleSet) { + if (!ruleSet.customAttributeContainerRules) + return; + + for (const rule of ruleSet.customAttributeContainerRules) { + const result = rule(container); + await this.reportDiagnostics(result); + } + } + + public async applyCustomAttributeInstanceRules(container: CustomAttributeContainerProps, customAttribute: CustomAttribute, ruleSet: IRuleSet) { + if (!ruleSet.customAttributeInstanceRules) + return; + + for (const rule of ruleSet.customAttributeInstanceRules) { + const result = rule(container, customAttribute); + await this.reportDiagnostics(result); + } + } + + public async applyEnumerationRules(enumeration: Enumeration, ruleSet: IRuleSet) { + if (!ruleSet.enumerationRules) + return; + + for (const rule of ruleSet.enumerationRules) { + const result = rule(enumeration); + await this.reportDiagnostics(result); + } + } + + public async applyKindOfQuantityRules(kindOfQuantity: KindOfQuantity, ruleSet: IRuleSet) { + if (!ruleSet.kindOfQuantityRules) + return; + + for (const rule of ruleSet.kindOfQuantityRules) { + const result = rule(kindOfQuantity); + await this.reportDiagnostics(result); + } + } + + public async applyPropertyCategoryRules(propertyCategory: PropertyCategory, ruleSet: IRuleSet) { + if (!ruleSet.propertyCategoryRules) + return; + + for (const rule of ruleSet.propertyCategoryRules) { + const result = rule(propertyCategory); + await this.reportDiagnostics(result); + } + } + + public async applyFormatRules(format: Format, ruleSet: IRuleSet) { + if (!ruleSet.formatRules) + return; + + for (const rule of ruleSet.formatRules) { + const result = rule(format); + await this.reportDiagnostics(result); + } + } + + public async applyUnitRules(unit: Unit, ruleSet: IRuleSet) { + if (!ruleSet.unitRules) + return; + + for (const rule of ruleSet.unitRules) { + const result = rule(unit); + await this.reportDiagnostics(result); + } + } + + public async applyInvertedUnitRules(invertedUnit: InvertedUnit, ruleSet: IRuleSet) { + if (!ruleSet.invertedUnitRules) + return; + + for (const rule of ruleSet.invertedUnitRules) { + const result = rule(invertedUnit); + await this.reportDiagnostics(result); + } + } + + public async applyUnitSystemRules(unitSystem: UnitSystem, ruleSet: IRuleSet) { + if (!ruleSet.unitSystemRules) + return; + + for (const rule of ruleSet.unitSystemRules) { + const result = rule(unitSystem); + await this.reportDiagnostics(result); + } + } + + public async applyPhenomenonRules(phenomenon: Phenomenon, ruleSet: IRuleSet) { + if (!ruleSet.phenomenonRules) + return; + + for (const rule of ruleSet.phenomenonRules) { + const result = rule(phenomenon); + await this.reportDiagnostics(result); + } + } + + public async applyConstantRules(constant: Constant, ruleSet: IRuleSet) { + if (!ruleSet.constantRules) + return; + + for (const rule of ruleSet.constantRules) { + const result = rule(constant); + await this.reportDiagnostics(result); + } + } + + private async reportDiagnostics(diagnostics: AsyncIterable) { + for await (const diagnostic of diagnostics) { + if (diagnostic) + this._reporters.forEach((reporter) => { reporter.report(diagnostic); }); + } + } +} diff --git a/core/ecschema-metadata/src/Validation/SchemaWalker.ts b/core/ecschema-metadata/src/Validation/SchemaWalker.ts new file mode 100644 index 0000000..675962e --- /dev/null +++ b/core/ecschema-metadata/src/Validation/SchemaWalker.ts @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { SchemaItemType } from "../ECObjects"; +import { ECClass } from "../Metadata/Class"; +import { RelationshipClass } from "../Metadata/RelationshipClass"; +import { SchemaItem } from "../Metadata/SchemaItem"; +import { ISchemaPartVisitor, SchemaPartVisitorDelegate } from "../SchemaPartVisitorDelegate"; +import { Schema } from "./../Metadata/Schema"; + +/** + * @hidden + * The purpose of this class is to traverse a given schema, allowing clients to hook into + * the traversal process via Visitors to allow for custom processing of the schema elements. + */ +export class SchemaWalker { + private _visitorHelper: SchemaPartVisitorDelegate; + + // This is a cache of the schema we are traversing. The schema also exists within the _context but in order + // to not have to go back to the context every time we use this cache. + private _schema?: Schema; + + /** + * Initializes a new SchemaWalker instance. + * @param visitor An ISchemaWalkerVisitor implementation whose methods will be called during schema traversal. + */ + constructor(visitor: ISchemaPartVisitor) { + this._visitorHelper = new SchemaPartVisitorDelegate(visitor); + } + + /** + * Traverses the given Schema, calling ISchemaWalkerVisitor methods along the way. + * @param schema The Schema to traverse. + */ + public async traverseSchema(schema: T): Promise { + this._schema = schema; + + await this._visitorHelper.visitSchema(schema); + await this._visitorHelper.visitSchemaPart(schema); + + const schemaItems = this._schema.getItems(); + + for (const item of schemaItems) { + await this.traverseSchemaItem(item); + } + + return schema; + } + + private async traverseSchemaItem(schemaItem: SchemaItem): Promise { + await this._visitorHelper.visitSchemaPart(schemaItem); + + if (schemaItem instanceof ECClass) { + await this.traverseClass(schemaItem as ECClass); + } + } + + private async traverseClass(ecClass: ECClass): Promise { + if (ecClass.properties) { + for (const property of ecClass.properties) { + await this._visitorHelper.visitSchemaPart(property); + } + } + + if (ecClass.schemaItemType === SchemaItemType.RelationshipClass) { + await this._visitorHelper.visitSchemaPart((ecClass as RelationshipClass).source); + await this._visitorHelper.visitSchemaPart((ecClass as RelationshipClass).target); + } + } +} diff --git a/core/ecschema-metadata/src/Validation/ValidationException.ts b/core/ecschema-metadata/src/Validation/ValidationException.ts deleted file mode 100644 index 4c9e398..0000000 --- a/core/ecschema-metadata/src/Validation/ValidationException.ts +++ /dev/null @@ -1,28 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ - -import { assert, BentleyError } from "@bentley/bentleyjs-core"; -import { ECValidationStatus } from "./Diagnostics"; - -export class ECValidationError extends BentleyError { - public constructor(public readonly errorNumber: number, message?: string) { - super(errorNumber, message); - assert(errorNumber as number !== ECValidationStatus.Success as number, message); - } - - public toDebugString(): string { - switch (this.errorNumber) { - case ECValidationStatus.BaseClassIsSealed: return this._appendMessage("ECValidationStatus.BaseClassIsSealed"); - default: - assert(false); - /* istanbul ignore next */ - return this._appendMessage("Error " + this.errorNumber.toString()); - } - } - - private _appendMessage(e: string): string { - return this.message ? e + ": " + this.message : e; - } -} diff --git a/core/ecschema-metadata/src/ecschema-metadata.ts b/core/ecschema-metadata/src/ecschema-metadata.ts index 89bd41d..b43bd69 100644 --- a/core/ecschema-metadata/src/ecschema-metadata.ts +++ b/core/ecschema-metadata/src/ecschema-metadata.ts @@ -38,3 +38,11 @@ export * from "./Metadata/UnitSystem"; export * from "./PropertyTypes"; export * from "./SchemaKey"; export * from "./utils/FormatEnums"; +export * from "./Validation/Diagnostic"; +export * from "./Validation/DiagnosticReporter"; +export { DiagnosticCodes, Diagnostics, ECRuleSet } from "./Validation/ECRules"; +export * from "./Validation/LoggingDiagnosticReporter"; +export * from "./Validation/Rules"; +export * from "./Validation/SchemaValidationVisitor"; +export * from "./Validation/SchemaWalker"; +export * from "./SchemaPartVisitorDelegate"; diff --git a/core/ecschema-metadata/test/Assets/BadSchemaName.ecschema.json b/core/ecschema-metadata/test/Assets/BadSchemaName.ecschema.json index 8fd2dab..4af028b 100644 --- a/core/ecschema-metadata/test/Assets/BadSchemaName.ecschema.json +++ b/core/ecschema-metadata/test/Assets/BadSchemaName.ecschema.json @@ -1,5 +1,5 @@ { - "$schema": "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + "$schema": "https://dev.bentley.com/json_schemas/ec/32/ecschema", "alias": "a", "description": "This is a test schema.", "label": "SchemaA", diff --git a/core/ecschema-metadata/test/Assets/BadSchemaVersion.ecschema.json b/core/ecschema-metadata/test/Assets/BadSchemaVersion.ecschema.json index 1193f71..1b5ebf6 100644 --- a/core/ecschema-metadata/test/Assets/BadSchemaVersion.ecschema.json +++ b/core/ecschema-metadata/test/Assets/BadSchemaVersion.ecschema.json @@ -1,5 +1,5 @@ { - "$schema": "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + "$schema": "https://dev.bentley.com/json_schemas/ec/32/ecschema", "alias": "a", "description": "This is a test schema.", "name": "SchemaA", diff --git a/core/ecschema-metadata/test/Assets/SchemaA.02.00.02.ecschema.json b/core/ecschema-metadata/test/Assets/SchemaA.02.00.02.ecschema.json index 26b5ef5..26f6ca9 100644 --- a/core/ecschema-metadata/test/Assets/SchemaA.02.00.02.ecschema.json +++ b/core/ecschema-metadata/test/Assets/SchemaA.02.00.02.ecschema.json @@ -1,5 +1,5 @@ { - "$schema": "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + "$schema": "https://dev.bentley.com/json_schemas/ec/32/ecschema", "alias": "a", "description": "This is a test Schema.", "label": "SchemaA", diff --git a/core/ecschema-metadata/test/Assets/SchemaA.ecschema.json b/core/ecschema-metadata/test/Assets/SchemaA.ecschema.json index 67d7e04..decc8d1 100644 --- a/core/ecschema-metadata/test/Assets/SchemaA.ecschema.json +++ b/core/ecschema-metadata/test/Assets/SchemaA.ecschema.json @@ -1,5 +1,5 @@ { - "$schema": "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + "$schema": "https://dev.bentley.com/json_schemas/ec/32/ecschema", "alias": "a", "description": "This is a test Schema.", "label": "SchemaA", diff --git a/core/ecschema-metadata/test/Assets/SchemaB.ecschema.json b/core/ecschema-metadata/test/Assets/SchemaB.ecschema.json index 698a025..3c623f4 100644 --- a/core/ecschema-metadata/test/Assets/SchemaB.ecschema.json +++ b/core/ecschema-metadata/test/Assets/SchemaB.ecschema.json @@ -1,5 +1,5 @@ { - "$schema": "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + "$schema": "https://dev.bentley.com/json_schemas/ec/32/ecschema", "alias": "b", "description": "This is a test Schema.", "label": "SchemaB", diff --git a/core/ecschema-metadata/test/Assets/SchemaC.04.00.04.ecschema.json b/core/ecschema-metadata/test/Assets/SchemaC.04.00.04.ecschema.json index 543c352..6efa96e 100644 --- a/core/ecschema-metadata/test/Assets/SchemaC.04.00.04.ecschema.json +++ b/core/ecschema-metadata/test/Assets/SchemaC.04.00.04.ecschema.json @@ -1,5 +1,5 @@ { - "$schema": "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + "$schema": "https://dev.bentley.com/json_schemas/ec/32/ecschema", "alias": "c", "description": "This is a test Schema.", "label": "SchemaC", diff --git a/core/ecschema-metadata/test/Assets/SchemaC.ecschema.json b/core/ecschema-metadata/test/Assets/SchemaC.ecschema.json index ff66efb..497136a 100644 --- a/core/ecschema-metadata/test/Assets/SchemaC.ecschema.json +++ b/core/ecschema-metadata/test/Assets/SchemaC.ecschema.json @@ -1,5 +1,5 @@ { - "$schema": "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + "$schema": "https://dev.bentley.com/json_schemas/ec/32/ecschema", "alias": "c", "description": "This is a test Schema.", "label": "SchemaC", diff --git a/core/ecschema-metadata/test/Assets/SchemaD.ecschema.json b/core/ecschema-metadata/test/Assets/SchemaD.ecschema.json index 357b050..1c03d07 100644 --- a/core/ecschema-metadata/test/Assets/SchemaD.ecschema.json +++ b/core/ecschema-metadata/test/Assets/SchemaD.ecschema.json @@ -1,5 +1,5 @@ { - "$schema": "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + "$schema": "https://dev.bentley.com/json_schemas/ec/32/ecschema", "alias": "d", "description": "This is a test Schema.", "label": "SchemaD", diff --git a/core/ecschema-metadata/test/Context/SchemaCache.test.ts b/core/ecschema-metadata/test/Context/SchemaCache.test.ts index a6fe40c..b579257 100644 --- a/core/ecschema-metadata/test/Context/SchemaCache.test.ts +++ b/core/ecschema-metadata/test/Context/SchemaCache.test.ts @@ -10,7 +10,7 @@ const expect = chai.expect; import * as chaiAsPromised from "chai-as-promised"; chai.use(chaiAsPromised); -import { SchemaCache } from "./../../src/Context"; +import { SchemaCache, SchemaContext } from "./../../src/Context"; import { Schema } from "./../../src/Metadata/Schema"; import { ECObjectsError } from "./../../src/Exception"; import { SchemaKey } from "./../../src/SchemaKey"; @@ -18,29 +18,29 @@ import { SchemaKey } from "./../../src/SchemaKey"; describe("Schema Cache", () => { it("adding should succeed", async () => { const cache = new SchemaCache(); - - const schema = new Schema(new SchemaKey("TestSchema")); + const schema = new Schema(new SchemaContext(), new SchemaKey("TestSchema")); await cache.addSchema(schema); - assert.equal(cache.count, 1); + assert.strictEqual(cache.count, 1); }); it("should not be able to add multiple schemas that match using SchemaMatchType Latest", async () => { const cache = new SchemaCache(); + const context = new SchemaContext(); - const schema1 = new Schema(new SchemaKey("TestSchema")); + const schema1 = new Schema(context, new SchemaKey("TestSchema")); await cache.addSchema(schema1); - const schema2 = new Schema(new SchemaKey("TestSchema")); - await expect(cache.addSchema(schema2)).to.be.rejectedWith(ECObjectsError, "The schema, TestSchema.0.0.0, already exists within this cache."); + const schema2 = new Schema(context, new SchemaKey("TestSchema")); + await expect(cache.addSchema(schema2)).to.be.rejectedWith(ECObjectsError, "The schema, TestSchema.00.00.00, already exists within this cache."); - const schema3 = new Schema(new SchemaKey("TestSchema", 1)); - await expect(cache.addSchema(schema3)).to.be.rejectedWith(ECObjectsError, "The schema, TestSchema.1.0.0, already exists within this cache."); + const schema3 = new Schema(context, new SchemaKey("TestSchema", 1)); + await expect(cache.addSchema(schema3)).to.be.rejectedWith(ECObjectsError, "The schema, TestSchema.01.00.00, already exists within this cache."); - const schema4 = new Schema(new SchemaKey("TestSchema", 1, 0)); - await expect(cache.addSchema(schema4)).to.be.rejectedWith(ECObjectsError, "The schema, TestSchema.1.0.0, already exists within this cache."); + const schema4 = new Schema(context, new SchemaKey("TestSchema", 1, 0)); + await expect(cache.addSchema(schema4)).to.be.rejectedWith(ECObjectsError, "The schema, TestSchema.01.00.00, already exists within this cache."); - const schema5 = new Schema("TestSchema", 1, 0, 0); - await expect(cache.addSchema(schema5)).to.be.rejectedWith(ECObjectsError, "The schema, TestSchema.1.0.0, already exists within this cache."); + const schema5 = new Schema(context, "TestSchema", 1, 0, 0); + await expect(cache.addSchema(schema5)).to.be.rejectedWith(ECObjectsError, "The schema, TestSchema.01.00.00, already exists within this cache."); }); }); diff --git a/core/ecschema-metadata/test/Context/SchemaContext.test.ts b/core/ecschema-metadata/test/Context/SchemaContext.test.ts index 247d0df..052dba1 100644 --- a/core/ecschema-metadata/test/Context/SchemaContext.test.ts +++ b/core/ecschema-metadata/test/Context/SchemaContext.test.ts @@ -18,16 +18,16 @@ import { ECObjectsError } from "./../../src/Exception"; describe("Schema Context", () => { it("should succeed locating added schema", async () => { - const schema = new Schema("TestSchema", 1, 5, 9); - const context = new SchemaContext(); + const schema = new Schema(context, "TestSchema", 1, 5, 9); + await context.addSchema(schema); const testKey = new SchemaKey("TestSchema", 1, 5, 9); const foundSchema = await context.getSchema(testKey); assert.isDefined(foundSchema); - assert.equal(foundSchema, schema); + assert.strictEqual(foundSchema, schema); }); it("returns undefined when schema does not exist", async () => { @@ -42,8 +42,8 @@ describe("Schema Context", () => { it("does not allow duplicate schemas", async () => { const context = new SchemaContext(); - const schema = new Schema("TestSchema", 1, 0, 5); - const schema2 = new Schema("TestSchema", 1, 0, 5); + const schema = new Schema(context, "TestSchema", 1, 0, 5); + const schema2 = new Schema(context, "TestSchema", 1, 0, 5); await context.addSchema(schema); await expect(context.addSchema(schema2)).to.be.rejectedWith(ECObjectsError); @@ -53,7 +53,7 @@ describe("Schema Context", () => { const context = new SchemaContext(); const cache = new SchemaCache(); - const schema = new Schema("TestSchema", 1, 0, 5); + const schema = new Schema(context, "TestSchema", 1, 0, 5); await cache.addSchema(schema); context.addLocater(cache); @@ -63,7 +63,7 @@ describe("Schema Context", () => { // Check if the schema is found if it is added to the cache after the cache is added as a locater const cache2 = new SchemaCache(); context.addLocater(cache2); - const schema2 = new Schema("TestSchema", 1, 0, 10); + const schema2 = new Schema(context, "TestSchema", 1, 0, 10); await cache2.addSchema(schema2); expect(await context.getSchema(schema2.schemaKey, SchemaMatchType.Exact)).to.equal(schema2); diff --git a/core/ecschema-metadata/test/Deserialization/JsonParser.test.ts b/core/ecschema-metadata/test/Deserialization/JsonParser.test.ts index cb338ff..1f25ef1 100644 --- a/core/ecschema-metadata/test/Deserialization/JsonParser.test.ts +++ b/core/ecschema-metadata/test/Deserialization/JsonParser.test.ts @@ -665,7 +665,7 @@ describe("JsonParser", () => { it("should throw for invalid version", () => { const json = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "BadSchema", version: 0, }; @@ -675,7 +675,7 @@ describe("JsonParser", () => { it("should throw for missing version", () => { const json = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "BadSchema", }; parser = new JsonParser(json); @@ -684,7 +684,7 @@ describe("JsonParser", () => { function testInvalidAttribute(attributeName: string, expectedType: string, value: any) { const json = { - $schema: "https://dev.bentley.com/json_schemas/ec/31/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", name: "TestSchema", version: "1.2.3", [attributeName]: value, diff --git a/core/ecschema-metadata/test/ECVersion.test.ts b/core/ecschema-metadata/test/ECVersion.test.ts index 5c1c15a..feba514 100644 --- a/core/ecschema-metadata/test/ECVersion.test.ts +++ b/core/ecschema-metadata/test/ECVersion.test.ts @@ -47,11 +47,11 @@ describe("ECVersion", () => { describe("toString", () => { it("fully defined version string", () => { const testVersion = new ECVersion(1, 0, 14); - assert.equal("1.0.14", testVersion.toString()); + assert.strictEqual("01.00.14", testVersion.toString()); }); - it("fully defined version string with leading zero", () => { + it("fully defined version string without leading zero", () => { const testVersion = new ECVersion(1, 0, 14); - assert.equal("01.00.14", testVersion.toString(true)); + assert.strictEqual("1.0.14", testVersion.toString(false)); }); }); @@ -102,7 +102,7 @@ describe("ECVersion", () => { const leftVersion = new ECVersion(1, 2, 3); const rightVersion = new ECVersion(1, 2, 3); const result = leftVersion.compare(rightVersion); - assert.equal(result, 0); + assert.strictEqual(result, 0); }); }); }); diff --git a/core/ecschema-metadata/test/Locaters/SchemaJsonFileLocator.test.ts b/core/ecschema-metadata/test/Locaters/SchemaJsonFileLocator.test.ts index 7ca0f39..7e8d16e 100644 --- a/core/ecschema-metadata/test/Locaters/SchemaJsonFileLocator.test.ts +++ b/core/ecschema-metadata/test/Locaters/SchemaJsonFileLocator.test.ts @@ -26,87 +26,65 @@ describe("SchemaJsonFileLocater tests: ", () => { }); it("locate valid schema with multiple references", async () => { - // Arrange const schemaKey = new SchemaKey("SchemaA", 1, 1, 1); - - console.log(schemaKey.toString()); - - // Act const schema = await context.getSchema(schemaKey, SchemaMatchType.Exact); - // Assert assert.isDefined(schema); - assert.equal(schema!.schemaKey.name, "SchemaA"); - assert.equal(schema!.schemaKey.version.toString(), "1.1.1"); + assert.strictEqual(schema!.schemaKey.name, "SchemaA"); + assert.strictEqual(schema!.schemaKey.version.toString(), "01.01.01"); }); it("locate valid schema with multiple references synchronously", () => { - // Arrange const schemaKey = new SchemaKey("SchemaA", 1, 1, 1); - - // Act const schema = context.getSchemaSync(schemaKey, SchemaMatchType.Exact); - // Assert assert.isDefined(schema); - assert.equal(schema!.schemaKey.name, "SchemaA"); - assert.equal(schema!.schemaKey.version.toString(), "1.1.1"); + assert.strictEqual(schema!.schemaKey.name, "SchemaA"); + assert.strictEqual(schema!.schemaKey.version.toString(), "01.01.01"); }); it("getSchema called multiple times for same schema", async () => { - // Arrange const schemaKey = new SchemaKey("SchemaD", 4, 4, 4); - // Act - const locater1 = await locater.getSchema(schemaKey, SchemaMatchType.Exact); - const locater2 = await locater.getSchema(schemaKey, SchemaMatchType.Exact); + const locater1 = await locater.getSchema(schemaKey, SchemaMatchType.Exact, new SchemaContext()); + const locater2 = await locater.getSchema(schemaKey, SchemaMatchType.Exact, new SchemaContext()); const context1 = await context.getSchema(schemaKey, SchemaMatchType.Exact); const context2 = await context.getSchema(schemaKey, SchemaMatchType.Exact); - // Assert // locater should not cache, but context should cache assert.notEqual(locater1, locater2); assert.notEqual(locater1, context1); - assert.equal(context1, context2); + assert.strictEqual(context1, context2); }); it("getSchema called multiple times for same schema synchronously", () => { - // Arrange const schemaKey = new SchemaKey("SchemaD", 4, 4, 4); - // Act - const locater1 = locater.getSchemaSync(schemaKey, SchemaMatchType.Exact); - const locater2 = locater.getSchemaSync(schemaKey, SchemaMatchType.Exact); + const locater1 = locater.getSchemaSync(schemaKey, SchemaMatchType.Exact, new SchemaContext()); + const locater2 = locater.getSchemaSync(schemaKey, SchemaMatchType.Exact, new SchemaContext()); const context1 = context.getSchemaSync(schemaKey, SchemaMatchType.Exact); const context2 = context.getSchemaSync(schemaKey, SchemaMatchType.Exact); - // Assert // locater should not cache, but context should cache assert.notEqual(locater1, locater2); assert.notEqual(locater1, context1); - assert.equal(context1, context2); + assert.strictEqual(context1, context2); }); it("getSchema which does not exist, returns undefined", async () => { - // Arrange const schemaKey = new SchemaKey("DoesNotExist"); - - // Act - const result = await locater.getSchema(schemaKey, SchemaMatchType.Exact); - + const result = await locater.getSchema(schemaKey, SchemaMatchType.Exact, context); assert.isUndefined(result); }); it("loadSchema from file, bad schema name, throws", async () => { - // Arrange const schemaKey = new SchemaKey("BadSchemaName"); - // Act / Assert try { - await locater.getSchema(schemaKey, SchemaMatchType.Exact); + await locater.getSchema(schemaKey, SchemaMatchType.Exact, context); } catch (e) { const error = e as ECObjectsError; - assert.equal(error.errorNumber, ECObjectsStatus.InvalidECJson); + assert.strictEqual(error.errorNumber, ECObjectsStatus.InvalidECJson); return; } @@ -114,15 +92,13 @@ describe("SchemaJsonFileLocater tests: ", () => { }); it("loadSchema from file, bad schema version, throws", async () => { - // Arrange const schemaKey = new SchemaKey("BadSchemaVersion"); - // Act / Assert try { - await locater.getSchema(schemaKey, SchemaMatchType.Exact); + await locater.getSchema(schemaKey, SchemaMatchType.Exact, context); } catch (e) { const error = e as ECObjectsError; - assert.equal(error.errorNumber, ECObjectsStatus.InvalidECJson); + assert.strictEqual(error.errorNumber, ECObjectsStatus.InvalidECJson); return; } @@ -130,69 +106,46 @@ describe("SchemaJsonFileLocater tests: ", () => { }); it("getSchema, full version, succeeds", async () => { - // Arrange - - // Act const stub = await locater.getSchema(new SchemaKey("SchemaA", 1, 1, 1), SchemaMatchType.Exact, context); - // Assert assert.isDefined(stub); const key = stub!.schemaKey as FileSchemaKey; - assert.equal(key.name, "SchemaA"); - assert.equal(key.version.toString(), "1.1.1"); + assert.strictEqual(key.name, "SchemaA"); + assert.strictEqual(key.version.toString(), "01.01.01"); }); it("getSchema, exact version, wrong minor, fails", async () => { - // Act - const schema = await locater.getSchema(new SchemaKey("SchemaA", 1, 1, 2), SchemaMatchType.Exact, context); - - // Assert - assert.isUndefined(schema); + assert.isUndefined(await locater.getSchema(new SchemaKey("SchemaA", 1, 1, 2), SchemaMatchType.Exact, context)); }); it("getSchema, latest, succeeds", async () => { - // Act const schema = await locater.getSchema(new SchemaKey("SchemaA", 1, 1, 0), SchemaMatchType.Latest, context); - - // Assert assert.isDefined(schema); - assert.equal(schema!.schemaKey.name, "SchemaA"); - assert.equal(schema!.schemaKey.version.toString(), "2.0.2"); + assert.strictEqual(schema!.schemaKey.name, "SchemaA"); + assert.strictEqual(schema!.schemaKey.version.toString(), "02.00.02"); }); it("getSchema, latest write compatible, succeeds", async () => { - // Act const stub = await locater.getSchema(new SchemaKey("SchemaA", 1, 1, 0), SchemaMatchType.LatestWriteCompatible, context); - // Assert assert.isDefined(stub); - assert.equal(stub!.schemaKey.name, "SchemaA"); - assert.equal(stub!.schemaKey.version.toString(), "1.1.1"); + assert.strictEqual(stub!.schemaKey.name, "SchemaA"); + assert.strictEqual(stub!.schemaKey.version.toString(), "01.01.01"); }); it("getSchema, latest write compatible, write version wrong, fails", async () => { - // Act - const stub = await locater.getSchema(new SchemaKey("SchemaA", 1, 2, 0), SchemaMatchType.LatestWriteCompatible, context); - - // Assert - assert.isUndefined(stub); + assert.isUndefined(await locater.getSchema(new SchemaKey("SchemaA", 1, 2, 0), SchemaMatchType.LatestWriteCompatible, context)); }); it("getSchema, latest read compatible, succeeds", async () => { - // Act const stub = await locater.getSchema(new SchemaKey("SchemaA", 1, 0, 0), SchemaMatchType.LatestReadCompatible, context); - // Assert assert.isDefined(stub); - assert.equal(stub!.schemaKey.name, "SchemaA"); - assert.equal(stub!.schemaKey.version.toString(), "1.1.1"); + assert.strictEqual(stub!.schemaKey.name, "SchemaA"); + assert.strictEqual(stub!.schemaKey.version.toString(), "01.01.01"); }); it("getSchema, latest read compatible, read version wrong, fails", async () => { - // Act - const stub = await locater.getSchema(new SchemaKey("SchemaA", 2, 1, 1), SchemaMatchType.LatestReadCompatible, context); - - // Assert - assert.isUndefined(stub); + assert.isUndefined(await locater.getSchema(new SchemaKey("SchemaA", 2, 1, 1), SchemaMatchType.LatestReadCompatible, context)); }); }); diff --git a/core/ecschema-metadata/test/Locaters/SchemaXmlFileLocator.test.ts b/core/ecschema-metadata/test/Locaters/SchemaXmlFileLocator.test.ts index f8c5e3e..eb9805a 100644 --- a/core/ecschema-metadata/test/Locaters/SchemaXmlFileLocator.test.ts +++ b/core/ecschema-metadata/test/Locaters/SchemaXmlFileLocator.test.ts @@ -5,7 +5,6 @@ import * as path from "path"; import { assert } from "chai"; - import { SchemaXmlFileLocater } from "./../../src/Deserialization/SchemaXmlFileLocater"; import { FileSchemaKey } from "./../../src/Deserialization/SchemaFileLocater"; import { SchemaContext } from "./../../src/Context"; @@ -25,68 +24,50 @@ describe("SchemaXmlFileLocater tests:", () => { }); it("locate valid schema with multiple references", async () => { - // Arrange const schemaKey = new SchemaKey("SchemaA", 1, 1, 1); - - // Act const schema = await context.getSchema(schemaKey, SchemaMatchType.Exact); - // Assert assert.isDefined(schema); - assert.equal(schema!.schemaKey.name, "SchemaA"); - assert.equal(schema!.schemaKey.version.toString(), "1.1.1"); + assert.strictEqual(schema!.schemaKey.name, "SchemaA"); + assert.strictEqual(schema!.schemaKey.version.toString(), "01.01.01"); }); it("locate valid schema with multiple references synchronously", () => { - // Arrange const schemaKey = new SchemaKey("SchemaA", 1, 1, 1); - - // Act const schema = context.getSchemaSync(schemaKey, SchemaMatchType.Exact); - // Assert assert.isDefined(schema); - assert.equal(schema!.schemaKey.name, "SchemaA"); - assert.equal(schema!.schemaKey.version.toString(), "1.1.1"); + assert.strictEqual(schema!.schemaKey.name, "SchemaA"); + assert.strictEqual(schema!.schemaKey.version.toString(), "01.01.01"); }); it("getSchema called multiple times for same schema", async () => { - // Arrange const schemaKey = new SchemaKey("SchemaD", 4, 4, 4); - // Act - const locater1 = await locater.getSchema(schemaKey, SchemaMatchType.Exact); - const locater2 = await locater.getSchema(schemaKey, SchemaMatchType.Exact); + const locater1 = await locater.getSchema(schemaKey, SchemaMatchType.Exact, new SchemaContext()); + const locater2 = await locater.getSchema(schemaKey, SchemaMatchType.Exact, new SchemaContext()); const context1 = await context.getSchema(schemaKey, SchemaMatchType.Exact); const context2 = await context.getSchema(schemaKey, SchemaMatchType.Exact); - // Assert // locater should not cache, but context should cache assert.notEqual(locater1, locater2); assert.notEqual(locater1, context1); - assert.equal(context1, context2); + assert.strictEqual(context1, context2); }); it("getSchema which does not exist, returns undefined", async () => { - // Arrange const schemaKey = new SchemaKey("DoesNotExist"); - // Act - const result = await locater.getSchema(schemaKey, SchemaMatchType.Exact); - - assert.isUndefined(result); + assert.isUndefined(await locater.getSchema(schemaKey, SchemaMatchType.Exact, context)); }); it("loadSchema from file, bad schema tag, throws", async () => { - // Arrange const schemaKey = new SchemaKey("BadSchemaTag"); - - // Act / Assert try { await locater.getSchema(schemaKey, SchemaMatchType.Latest, context); } catch (e) { const error = e as ECObjectsError; - assert.equal(error.errorNumber, ECObjectsStatus.InvalidSchemaXML); + assert.strictEqual(error.errorNumber, ECObjectsStatus.InvalidSchemaXML); return; } @@ -94,15 +75,12 @@ describe("SchemaXmlFileLocater tests:", () => { }); it("loadSchema from file, bad schema name tag, throws", async () => { - // Arrange const schemaKey = new SchemaKey("BadSchemaNameTag"); - - // Act / Assert try { await locater.getSchema(schemaKey, SchemaMatchType.Latest, context); } catch (e) { const error = e as ECObjectsError; - assert.equal(error.errorNumber, ECObjectsStatus.InvalidSchemaXML); + assert.strictEqual(error.errorNumber, ECObjectsStatus.InvalidSchemaXML); return; } @@ -110,15 +88,13 @@ describe("SchemaXmlFileLocater tests:", () => { }); it("loadSchema from file, bad schema tag, throws", async () => { - // Arrange const schemaKey = new SchemaKey("BadSchemaVersionTag"); - // Act / Assert try { await locater.getSchema(schemaKey, SchemaMatchType.Latest, context); } catch (e) { const error = e as ECObjectsError; - assert.equal(error.errorNumber, ECObjectsStatus.InvalidSchemaXML); + assert.strictEqual(error.errorNumber, ECObjectsStatus.InvalidSchemaXML); return; } @@ -126,44 +102,33 @@ describe("SchemaXmlFileLocater tests:", () => { }); it("getSchema, full version, succeeds", async () => { - // Arrange - - // Act const stub = await locater.getSchema(new SchemaKey("SchemaA", 1, 1, 1), SchemaMatchType.Exact, context); - - // Assert assert.isDefined(stub); const key = stub!.schemaKey as FileSchemaKey; - assert.equal(key.name, "SchemaA"); - assert.equal(key.version.toString(), "1.1.1"); + assert.strictEqual(key.name, "SchemaA"); + assert.strictEqual(key.version.toString(), "01.01.01"); }); it("getSchema, reference does not exist, throws.", async () => { - // Arrange - - // Act try { await locater.getSchema(new SchemaKey("RefDoesNotExist", 1, 1, 1), SchemaMatchType.Exact, context); } catch (e) { const error = e as ECObjectsError; - assert.equal(error.errorNumber, ECObjectsStatus.UnableToLocateSchema); + assert.strictEqual(error.errorNumber, ECObjectsStatus.UnableToLocateSchema); return; } - // Assert assert.fail(); }); it("getSchema, references set", async () => { - // Act const stub = await context.getSchema(new SchemaKey("SchemaA", 1, 1, 1), SchemaMatchType.Exact); const schemaB = await context.getSchema(new SchemaKey("SchemaB", 2, 2, 2), SchemaMatchType.Exact); const schemaC = await context.getSchema(new SchemaKey("SchemaC", 3, 3, 3), SchemaMatchType.Exact); const schemaD = await context.getSchema(new SchemaKey("SchemaD", 4, 4, 4), SchemaMatchType.Exact); - // Assert assert.isDefined(stub); - assert.equal(stub!.references.length, 2); + assert.strictEqual(stub!.references.length, 2); assert.deepEqual(stub!.references[0], schemaC); assert.deepEqual(stub!.references[1], schemaB); assert.deepEqual(stub!.references[0].references[0], schemaD); @@ -172,15 +137,13 @@ describe("SchemaXmlFileLocater tests:", () => { }); it("getSchema, 2 digit references, references set", async () => { - // Act const stub = await context.getSchema(new SchemaKey("SchemaA", 2, 0, 2), SchemaMatchType.Exact); const schemaB = await context.getSchema(new SchemaKey("SchemaB", 3, 0, 3), SchemaMatchType.Exact); const schemaC = await context.getSchema(new SchemaKey("SchemaC", 4, 0, 4), SchemaMatchType.Exact); const schemaD = await context.getSchema(new SchemaKey("SchemaD", 5, 0, 5), SchemaMatchType.Exact); - // Assert assert.isDefined(stub); - assert.equal(stub!.references.length, 2); + assert.strictEqual(stub!.references.length, 2); assert.deepEqual(stub!.references[0], schemaC); assert.deepEqual(stub!.references[1], schemaB); assert.deepEqual(stub!.references[0].references[0], schemaD); @@ -189,106 +152,80 @@ describe("SchemaXmlFileLocater tests:", () => { }); it("getSchema, exact version, wrong minor, fails", async () => { - // Act - const stub = await context.getSchema(new SchemaKey("SchemaA", 1, 1, 2), SchemaMatchType.Exact); - - // Assert - assert.isUndefined(stub); + assert.isUndefined(await context.getSchema(new SchemaKey("SchemaA", 1, 1, 2), SchemaMatchType.Exact)); }); it("getSchema, latest, succeeds", async () => { - // Act const stub = await locater.getSchema(new SchemaKey("SchemaA", 1, 1, 0), SchemaMatchType.Latest, context); - // Assert assert.isDefined(stub); - assert.equal(stub!.schemaKey.name, "SchemaA"); - assert.equal(stub!.schemaKey.version.toString(), "2.0.2"); + assert.strictEqual(stub!.schemaKey.name, "SchemaA"); + assert.strictEqual(stub!.schemaKey.version.toString(), "02.00.02"); }); it("getSchema, latest write compatible, succeeds", async () => { - // Act const stub = await context.getSchema(new SchemaKey("SchemaA", 1, 1, 0), SchemaMatchType.LatestWriteCompatible); - // Assert assert.isDefined(stub); - assert.equal(stub!.schemaKey.name, "SchemaA"); - assert.equal(stub!.schemaKey.version.toString(), "1.1.1"); + assert.strictEqual(stub!.schemaKey.name, "SchemaA"); + assert.strictEqual(stub!.schemaKey.version.toString(), "01.01.01"); }); it("getSchema, latest write compatible, write version wrong, fails", async () => { - // Act - const stub = await context.getSchema(new SchemaKey("SchemaA", 1, 2, 0), SchemaMatchType.LatestWriteCompatible); - - // Assert - assert.isUndefined(stub); + assert.isUndefined(await context.getSchema(new SchemaKey("SchemaA", 1, 2, 0), SchemaMatchType.LatestWriteCompatible)); }); it("getSchema, latest read compatible, succeeds", async () => { - // Act const stub = await context.getSchema(new SchemaKey("SchemaA", 1, 0, 0), SchemaMatchType.LatestReadCompatible); - // Assert assert.isDefined(stub); - assert.equal(stub!.schemaKey.name, "SchemaA"); - assert.equal(stub!.schemaKey.version.toString(), "1.1.1"); + assert.strictEqual(stub!.schemaKey.name, "SchemaA"); + assert.strictEqual(stub!.schemaKey.version.toString(), "01.01.01"); }); it("getSchema, latest read compatible, read version wrong, fails", async () => { - // Act - const stub = await context.getSchema(new SchemaKey("SchemaA", 2, 1, 1), SchemaMatchType.LatestReadCompatible); - - // Assert - assert.isUndefined(stub); + assert.isUndefined(await context.getSchema(new SchemaKey("SchemaA", 2, 1, 1), SchemaMatchType.LatestReadCompatible)); }); + it("sync - should ignore commented out schema references", () => { - // Act const stub = context.getSchemaSync(new SchemaKey("RefCommentedOut", 1, 1, 1), SchemaMatchType.LatestReadCompatible); - // Assert assert.isDefined(stub); - assert.equal(stub!.schemaKey.name, "RefCommentedOut"); - assert.equal(stub!.schemaKey.version.toString(), "1.1.1"); + assert.strictEqual(stub!.schemaKey.name, "RefCommentedOut"); + assert.strictEqual(stub!.schemaKey.version.toString(), "01.01.01"); }); + it("async - should ignore commented out schema references", async () => { - // Act const stub = await context.getSchema(new SchemaKey("RefCommentedOut", 1, 1, 1), SchemaMatchType.LatestReadCompatible); - // Assert assert.isDefined(stub); - assert.equal(stub!.schemaKey.name, "RefCommentedOut"); - assert.equal(stub!.schemaKey.version.toString(), "1.1.1"); + assert.strictEqual(stub!.schemaKey.name, "RefCommentedOut"); + assert.strictEqual(stub!.schemaKey.version.toString(), "01.01.01"); }); + it("getSchemaKey, valid version and name, succeeds", () => { - const schemaXml = " " - // Act + const schemaXml = ` `; const key = locater.getSchemaKey(schemaXml); - // Assert assert.deepEqual(key, new SchemaKey("SchemaA", new ECVersion(1, 1, 1))); }); it("getSchemaKey, invalid xml, throws", () => { - const schemaXml = " " - // Act / Assert + const schemaXml = ` `; assert.throws(() => locater.getSchemaKey(schemaXml), ECObjectsError, `Could not find '' tag in the given file`); }); it("getSchemaKey, invalid schemaName attribute, throws", () => { - const schemaXml = " " - // Act / Assert + const schemaXml = ` `; assert.throws(() => locater.getSchemaKey(schemaXml), ECObjectsError, `Could not find the ECSchema 'schemaName' or 'version' tag in the given file`); }); it("getSchemaKey, invalid schemaName, throws", () => { - const schemaXml = " " - // Act / Assert + const schemaXml = ` `; assert.throws(() => locater.getSchemaKey(schemaXml), ECObjectsError, `Could not find the ECSchema 'schemaName' or 'version' tag in the given file`); }); it("getSchemaKey, invalid version attribute, throws", () => { - const schemaXml = " " - // Act / Assert + const schemaXml = ` `; assert.throws(() => locater.getSchemaKey(schemaXml), ECObjectsError, `Could not find the ECSchema 'schemaName' or 'version' tag in the given file`); }); it("getSchemaKey, invalid version, throws", () => { - const schemaXml = " " - // Act / Assert + const schemaXml = ` `; assert.throws(() => locater.getSchemaKey(schemaXml), ECObjectsError, `Could not find the ECSchema 'schemaName' or 'version' tag in the given file`); }); }); diff --git a/core/ecschema-metadata/test/Metadata/Class.test.ts b/core/ecschema-metadata/test/Metadata/Class.test.ts index 4a5fb39..fd99b65 100644 --- a/core/ecschema-metadata/test/Metadata/Class.test.ts +++ b/core/ecschema-metadata/test/Metadata/Class.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { assert, expect } from "chai"; -import * as sinon from "sinon"; + import { SchemaContext } from "../../src/Context"; import { DelayedPromiseWithProps } from "../../src/DelayedPromise"; import { ECObjectsError } from "../../src/Exception"; @@ -21,7 +21,7 @@ describe("ECClass", () => { describe("get properties", () => { beforeEach(() => { - schema = new Schema("TestSchema", 1, 0, 0); + schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); }); it("inherited properties from base class", async () => { @@ -83,7 +83,7 @@ describe("ECClass", () => { describe("deserialization", () => { it("class with base class", async () => { const schemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", items: { @@ -97,7 +97,7 @@ describe("ECClass", () => { }, }; - schema = await Schema.fromJson(schemaJson); + schema = await Schema.fromJson(schemaJson, new SchemaContext()); assert.isDefined(schema); const testClass = await schema.getItem("testClass"); @@ -111,7 +111,7 @@ describe("ECClass", () => { it("class with base class in reference schema", async () => { const schemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", references: [ @@ -128,7 +128,7 @@ describe("ECClass", () => { }, }; - const refSchema = new Schema("RefSchema", 1, 0, 5); + const refSchema = new Schema(new SchemaContext(), "RefSchema", 1, 0, 5); const refBaseClass = await (refSchema as MutableSchema).createEntityClass("BaseClassInRef"); const context = new SchemaContext(); @@ -145,7 +145,7 @@ describe("ECClass", () => { it("should throw for missing base class", () => { const schemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", items: { @@ -156,12 +156,11 @@ describe("ECClass", () => { }, }; - const context = new SchemaContext(); - expect(Schema.fromJson(schemaJson, context)).to.be.rejectedWith(ECObjectsError); + expect(Schema.fromJson(schemaJson, new SchemaContext())).to.be.rejectedWith(ECObjectsError); }); const oneCustomAttributeJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", items: { @@ -180,25 +179,25 @@ describe("ECClass", () => { }; it("async - Deserialize One Custom Attribute", async () => { - schema = await Schema.fromJson(oneCustomAttributeJson); + schema = await Schema.fromJson(oneCustomAttributeJson, new SchemaContext()); const testClass = await schema.getItem("testClass"); assert.isDefined(testClass); assert.isDefined(testClass!.customAttributes!.get("TestSchema.TestCAClass")); - assert(testClass!.customAttributes!.get("TestSchema.TestCAClass")!.ShowClasses === true); + assert.isTrue(testClass!.customAttributes!.get("TestSchema.TestCAClass")!.ShowClasses); }); it("sync - Deserialize One Custom Attribute", () => { - schema = Schema.fromJsonSync(oneCustomAttributeJson); + schema = Schema.fromJsonSync(oneCustomAttributeJson, new SchemaContext()); const testClass = schema.getItemSync("testClass"); assert.isDefined(testClass); assert.isDefined(testClass!.customAttributes!.get("TestSchema.TestCAClass")); - assert(testClass!.customAttributes!.get("TestSchema.TestCAClass")!.ShowClasses === true); + assert.isTrue(testClass!.customAttributes!.get("TestSchema.TestCAClass")!.ShowClasses); }); const twoCustomAttributesJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", items: { @@ -219,7 +218,7 @@ describe("ECClass", () => { }; it("async - Deserialize Two Custom Attributes", async () => { - schema = await Schema.fromJson(twoCustomAttributesJson); + schema = await Schema.fromJson(twoCustomAttributesJson, new SchemaContext()); const testClass = await schema.getItem("testClass"); @@ -228,7 +227,7 @@ describe("ECClass", () => { assert.isDefined(testClass!.customAttributes!.get("TestSchema.TestCAClassB")); }); it("sync - Deserialize Two Custom Attributes", () => { - schema = Schema.fromJsonSync(twoCustomAttributesJson); + schema = Schema.fromJsonSync(twoCustomAttributesJson, new SchemaContext()); const testClass = schema.getItemSync("testClass"); @@ -237,7 +236,7 @@ describe("ECClass", () => { assert.isDefined(testClass!.customAttributes!.get("TestSchema.TestCAClassB")); }); const mustBeAnArrayJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", items: { @@ -248,14 +247,14 @@ describe("ECClass", () => { }, }; it("async - Custom Attributes must be an array", async () => { - await expect(Schema.fromJson(mustBeAnArrayJson)).to.be.rejectedWith(ECObjectsError, `The ECClass TestSchema.testClass has an invalid 'customAttributes' attribute. It should be of type 'array'.`); + await expect(Schema.fromJson(mustBeAnArrayJson, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The ECClass TestSchema.testClass has an invalid 'customAttributes' attribute. It should be of type 'array'.`); }); it("sync - Custom Attributes must be an array", async () => { - assert.throws(() => Schema.fromJsonSync(mustBeAnArrayJson), ECObjectsError, `The ECClass TestSchema.testClass has an invalid 'customAttributes' attribute. It should be of type 'array'.`); + assert.throws(() => Schema.fromJsonSync(mustBeAnArrayJson, new SchemaContext()), ECObjectsError, `The ECClass TestSchema.testClass has an invalid 'customAttributes' attribute. It should be of type 'array'.`); }); it("sync - Deserialize Multiple Custom Attributes with additional properties", () => { const classJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", items: { @@ -281,7 +280,7 @@ describe("ECClass", () => { }, }, }; - schema = Schema.fromJsonSync(classJson); + schema = Schema.fromJsonSync(classJson, new SchemaContext()); const testClass = schema.getItemSync("testClass"); @@ -289,15 +288,16 @@ describe("ECClass", () => { assert.isDefined(testClass!.customAttributes!.get("TestSchema.TestCAClassA")); assert.isDefined(testClass!.customAttributes!.get("TestSchema.TestCAClassB")); assert.isDefined(testClass!.customAttributes!.get("TestSchema.TestCAClassC")); - assert(testClass!.customAttributes!.get("TestSchema.TestCAClassA")!.ShowClasses === 1.2); - assert(testClass!.customAttributes!.get("TestSchema.TestCAClassB")!.ExampleAttribute === true); - assert(testClass!.customAttributes!.get("TestSchema.TestCAClassC")!.Example2Attribute === "example"); - }) + assert.strictEqual(testClass!.customAttributes!.get("TestSchema.TestCAClassA")!.ShowClasses, 1.2); + assert.isTrue(testClass!.customAttributes!.get("TestSchema.TestCAClassB")!.ExampleAttribute); + assert.strictEqual(testClass!.customAttributes!.get("TestSchema.TestCAClassC")!.Example2Attribute, "example"); + }); + // Used to test that all property types are deserialized correctly. For failure and other tests look at the property // specific test files. it("with properties", async () => { const schemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", items: { @@ -332,7 +332,7 @@ describe("ECClass", () => { }, }; - const ecSchema = await Schema.fromJson(schemaJson); + const ecSchema = await Schema.fromJson(schemaJson, new SchemaContext()); assert.isDefined(ecSchema); const testEntity = await ecSchema.getItem("testClass"); @@ -352,7 +352,7 @@ describe("ECClass", () => { describe("deserialization sync", () => { it("class with base class", () => { const schemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", items: { @@ -366,7 +366,7 @@ describe("ECClass", () => { }, }; - schema = Schema.fromJsonSync(schemaJson); + schema = Schema.fromJsonSync(schemaJson, new SchemaContext()); assert.isDefined(schema); const testClass = schema.getItemSync("testClass"); @@ -380,7 +380,7 @@ describe("ECClass", () => { it("class with base class in reference schema", () => { const schemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", references: [ @@ -397,7 +397,7 @@ describe("ECClass", () => { }, }; - const refSchema = new Schema("RefSchema", 1, 0, 5); + const refSchema = new Schema(new SchemaContext(), "RefSchema", 1, 0, 5); const refBaseClass = (refSchema as MutableSchema).createEntityClassSync("BaseClassInRef"); const context = new SchemaContext(); @@ -415,7 +415,7 @@ describe("ECClass", () => { // specific test files. it("with properties", () => { const schemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", items: { @@ -450,7 +450,7 @@ describe("ECClass", () => { }, }; - const ecSchema = Schema.fromJsonSync(schemaJson); + const ecSchema = Schema.fromJsonSync(schemaJson, new SchemaContext()); assert.isDefined(ecSchema); const testEntity = ecSchema.getItemSync("testClass"); @@ -468,63 +468,100 @@ describe("ECClass", () => { }); describe("toJson", () => { - const schemaJsonOne = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", - name: "TestSchema", - version: "1.2.3", - items: { - testBaseClass: { - schemaItemType: "EntityClass", - }, - testClass: { - schemaItemType: "EntityClass", - baseClass: "TestSchema.testBaseClass", - properties: [ - { - name: "ValidProp", - description: "A really long description...", - label: "SomeDisplayLabel", - type: "PrimitiveProperty", - isReadOnly: true, - priority: 100, - typeName: "double", - }, - ], + function getTestSchemaJson(classJson: any = {}) { + return { + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", + name: "TestSchema", + version: "1.2.3", + items: { + testBaseClass: { + schemaItemType: "EntityClass", + }, + testClass: { + schemaItemType: "EntityClass", + baseClass: "TestSchema.testBaseClass", + properties: [ + { + name: "ValidProp", + description: "A really long description...", + label: "SomeDisplayLabel", + type: "PrimitiveProperty", + isReadOnly: true, + priority: 100, + typeName: "double", + }, + ], + ...classJson, + }, }, - }, - }; + }; + } + const schemaJsonOne = getTestSchemaJson(); + it("async - Simple serialization", async () => { - schema = await Schema.fromJson(schemaJsonOne); + schema = await Schema.fromJson(schemaJsonOne, new SchemaContext()); assert.isDefined(schema); const testClass = await schema.getItem("testClass"); assert.isDefined(testClass); expect(testClass).to.exist; const serialized = testClass!.toJson(true, true); - expect(serialized.baseClass).eql("TestSchema.testBaseClass"); - expect(serialized.properties[0].name).eql("ValidProp"); - expect(serialized.properties[0].description).eql("A really long description..."); - expect(serialized.properties[0].label).eql("SomeDisplayLabel"); - expect(serialized.properties[0].type).eql("PrimitiveProperty"); - expect(serialized.properties[0].isReadOnly).eql(true); - expect(serialized.properties[0].priority).eql(100); + const expectedJson = { + $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/schemaitem", + name: "testClass", + schema: "TestSchema", + schemaVersion: "01.02.03", + ...schemaJsonOne.items.testClass, + }; + expect(serialized).eql(expectedJson); + }); + + it("should omit modifier if 'None'", async () => { + schema = await Schema.fromJson(getTestSchemaJson({ modifier: "None" }), new SchemaContext()); + const testClass = await schema.getItem("testClass"); + expect(testClass).to.exist; + expect(testClass!.toJson(true, true)).to.not.have.property("modifier"); + }); + + it("should include modifier if 'Abstract'", async () => { + schema = await Schema.fromJson(getTestSchemaJson({ modifier: "Abstract" }), new SchemaContext()); + const testClass = await schema.getItem("testClass"); + expect(testClass).to.exist; + expect(testClass!.toJson(true, true)).to.include({ modifier: "Abstract" }); + }); + + it("should include modifier if 'Sealed'", async () => { + schema = await Schema.fromJson(getTestSchemaJson({ modifier: "Sealed" }), new SchemaContext()); + const testClass = await schema.getItem("testClass"); + expect(testClass).to.exist; + expect(testClass!.toJson(true, true)).to.include({ modifier: "Sealed" }); }); + + it("should omit customAttributes if empty", async () => { + schema = await Schema.fromJson(getTestSchemaJson({ customAttributes: [] }), new SchemaContext()); + const testClass = await schema.getItem("testClass"); + expect(testClass).to.exist; + expect(testClass!.toJson(true, true)).to.not.have.property("customAttributes"); + }); + it("sync - Simple serialization", () => { - schema = Schema.fromJsonSync(schemaJsonOne); + schema = Schema.fromJsonSync(schemaJsonOne, new SchemaContext()); assert.isDefined(schema); const testClass = schema.getItemSync("testClass"); assert.isDefined(testClass); const serialized = testClass!.toJson(true, true); - assert(serialized.properties[0].name, "ValidProp"); - assert(serialized.properties[0].description, "A really long description..."); - assert(serialized.properties[0].label, "SomeDisplayLabel"); - assert(serialized.properties[0].type, "PrimitiveProperty"); - assert(serialized.properties[0].isReadOnly === true); - assert(serialized.properties[0].priority === 100); + const expectedJson = { + $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/schemaitem", + name: "testClass", + schema: "TestSchema", + schemaVersion: "01.02.03", + ...schemaJsonOne.items.testClass, + }; + expect(serialized).eql(expectedJson); }); const schemaJsonFive = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", items: { @@ -566,29 +603,29 @@ describe("ECClass", () => { }, }; it("async - Serialization with multiple custom attributes- additional properties", async () => { - schema = await Schema.fromJson(schemaJsonFive); + schema = await Schema.fromJson(schemaJsonFive, new SchemaContext()); assert.isDefined(schema); const testClass = await schema.getItem("testClass"); assert.isDefined(testClass); const serialized = testClass!.toJson(true, true); - assert(serialized.properties[0].customAttributes[0].ShowClasses === true); - assert(serialized.properties[0].customAttributes[1].FloatValue === 1.2); - assert(serialized.properties[0].customAttributes[2].IntegerValue === 5); + assert.isTrue(serialized.properties[0].customAttributes[0].ShowClasses); + assert.strictEqual(serialized.properties[0].customAttributes[1].FloatValue, 1.2); + assert.strictEqual(serialized.properties[0].customAttributes[2].IntegerValue, 5); }); it("sync - Serialization with multiple custom attributes- additional properties", () => { - schema = Schema.fromJsonSync(schemaJsonFive); + schema = Schema.fromJsonSync(schemaJsonFive, new SchemaContext()); assert.isDefined(schema); const testClass = schema.getItemSync("testClass"); assert.isDefined(testClass); const serialized = testClass!.toJson(true, true); - assert(serialized.properties[0].customAttributes[0].ShowClasses === true); - assert(serialized.properties[0].customAttributes[1].FloatValue === 1.2); - assert(serialized.properties[0].customAttributes[2].IntegerValue === 5); + assert.isTrue(serialized.properties[0].customAttributes[0].ShowClasses); + assert.strictEqual(serialized.properties[0].customAttributes[1].FloatValue, 1.2); + assert.strictEqual(serialized.properties[0].customAttributes[2].IntegerValue, 5); }); const schemaJsonSix = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", items: { @@ -624,50 +661,28 @@ describe("ECClass", () => { }, }; it("async - Serialization with proper order of properties", async () => { - schema = await Schema.fromJson(schemaJsonSix); + schema = await Schema.fromJson(schemaJsonSix, new SchemaContext()); assert.isDefined(schema); const testClass = await schema.getItem("testClass"); assert.isDefined(testClass); const serialized = testClass!.toJson(true, true); - assert(serialized.properties[0].name, "A"); - assert(serialized.properties[1].name, "B"); - assert(serialized.properties[2].name, "C"); - assert(serialized.properties[3].name, "D"); + assert.strictEqual(serialized.properties[0].name, "A"); + assert.strictEqual(serialized.properties[1].name, "B"); + assert.strictEqual(serialized.properties[2].name, "C"); + assert.strictEqual(serialized.properties[3].name, "D"); }); it("sync - Serialization with proper order of properties", () => { - schema = Schema.fromJsonSync(schemaJsonSix); + schema = Schema.fromJsonSync(schemaJsonSix, new SchemaContext()); assert.isDefined(schema); const testClass = schema.getItemSync("testClass"); assert.isDefined(testClass); const serialized = testClass!.toJson(true, true); - assert(serialized.properties[0].name, "A"); - assert(serialized.properties[1].name, "B"); - assert(serialized.properties[2].name, "C"); - assert(serialized.properties[3].name, "D"); - }); - }); - - describe("accept", () => { - let testClass: ECClass; - class MockECClass extends ECClass { } - - beforeEach(() => { - testClass = new MockECClass(schema, "TestClass"); - }); - - it("should call visitClass on a SchemaItemVisitor object", async () => { - expect(testClass).to.exist; - const mockVisitor = { visitClass: sinon.spy() }; - await testClass.accept(mockVisitor); - expect(mockVisitor.visitClass.calledOnce).to.be.true; - expect(mockVisitor.visitClass.calledWithExactly(testClass)).to.be.true; - }); - - it("should safely handle a SchemaItemVisitor without visitClass defined", async () => { - expect(testClass).to.exist; - await testClass.accept({}); + assert.strictEqual(serialized.properties[0].name, "A"); + assert.strictEqual(serialized.properties[1].name, "B"); + assert.strictEqual(serialized.properties[2].name, "C"); + assert.strictEqual(serialized.properties[3].name, "D"); }); }); @@ -682,7 +697,7 @@ describe("ECClass", () => { // [ H ] // const testSchemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "01.00.00", alias: "ts", @@ -697,12 +712,45 @@ describe("ECClass", () => { H: { schemaItemType: "EntityClass", baseClass: "TestSchema.G", mixins: ["TestSchema.E", "TestSchema.F"] }, }, }; + + const childSchemaJson = { + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", + name: "ChildSchema", + version: "01.00.00", + alias: "ts", + references: [ + { + name: "TestSchema", + version: "1.0.0", + }, + ], + items: { + I: { schemaItemType: "EntityClass", baseClass: "TestSchema.H" }, + }, + }; + + const grandChildSchemaJson = { + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", + name: "GrandChildSchema", + version: "01.00.00", + alias: "ts", + references: [ + { + name: "ChildSchema", + version: "1.0.0", + }, + ], + items: { + J: { schemaItemType: "EntityClass", baseClass: "ChildSchema.I" }, + }, + }; + const expectedNames = ["G", "A", "B", "E", "C", "F", "D"]; it("getAllBaseClasses, should correctly traverse a complex inheritance hierarchy", async () => { const actualNames: string[] = []; - schema = await Schema.fromJson(testSchemaJson); + schema = await Schema.fromJson(testSchemaJson, new SchemaContext()); expect(schema).to.exist; const testClass = await schema.getItem("H"); @@ -715,7 +763,7 @@ describe("ECClass", () => { }); it("getAllBaseClassesSync, should correctly traverse a complex inheritance hierarchy synchronously", () => { - schema = Schema.fromJsonSync(testSchemaJson); + schema = Schema.fromJsonSync(testSchemaJson, new SchemaContext()); expect(schema).to.exist; const testClass = schema.getItemSync("H"); expect(testClass).to.exist; @@ -731,35 +779,35 @@ describe("ECClass", () => { { name: "C", arg: "testArg" }, { name: "F", arg: "testArg" }, { name: "D", arg: "testArg" }]; it("traverseBaseClasses, should correctly traverse a complex inheritance hierarchy", async () => { - const result: { name: string, arg: string }[] = []; + const result: Array<{ name: string, arg: string }> = []; - schema = await Schema.fromJson(testSchemaJson); + schema = await Schema.fromJson(testSchemaJson, new SchemaContext()); expect(schema).to.exist; const testClass = await schema.getItem("H"); expect(testClass).to.exist; - await testClass!.traverseBaseClasses((ecClass, arg) => { result.push({ name: ecClass.name, arg: arg }); return false }, "testArg"); + await testClass!.traverseBaseClasses((ecClass, arg) => { result.push({ name: ecClass.name, arg }); return false; }, "testArg"); expect(result).to.eql(expectedCallBackObjects); }); it("traverseBaseClassesSync, should correctly traverse a complex inheritance hierarchy synchronously", () => { - const result: { name: string, arg: string }[] = []; + const result: Array<{ name: string, arg: string }> = []; - schema = Schema.fromJsonSync(testSchemaJson); + schema = Schema.fromJsonSync(testSchemaJson, new SchemaContext()); expect(schema).to.exist; const testClass = schema.getItemSync("H"); expect(testClass).to.exist; - testClass!.traverseBaseClassesSync((ecClass, arg) => { result.push({ name: ecClass.name, arg: arg }); return false }, "testArg"); + testClass!.traverseBaseClassesSync((ecClass, arg) => { result.push({ name: ecClass.name, arg }); return false; }, "testArg"); expect(result).to.eql(expectedCallBackObjects); }); it("class 'is' a base class", async () => { - schema = Schema.fromJsonSync(testSchemaJson); + schema = Schema.fromJsonSync(testSchemaJson, new SchemaContext()); expect(schema).to.exist; const aClass = await schema.getItem("A"); @@ -784,8 +832,48 @@ describe("ECClass", () => { expect(await gClass!.is(hClass!)).to.be.false; }); + it("class 'is' a base class from different schema", async () => { + const context = new SchemaContext(); + schema = await Schema.fromJson(testSchemaJson, context); + const childSchema = await Schema.fromJson(childSchemaJson, context); + const grandChildSchema = await Schema.fromJson(grandChildSchemaJson, context); + + const aClass = await schema.getItem("A"); + const bClass = await schema.getItem("B"); + const cClass = await schema.getItem("C"); + const dClass = await schema.getItem("D"); + const eClass = await schema.getItem("E"); + const fClass = await schema.getItem("F"); + const gClass = await schema.getItem("G"); + const hClass = await schema.getItem("H"); + const iClass = await childSchema.getItem("I"); + const jClass = await grandChildSchema.getItem("J"); + + expect(await iClass!.is(gClass!)).to.be.true; + expect(await iClass!.is(aClass!)).to.be.true; + expect(await iClass!.is(bClass!)).to.be.true; + expect(await iClass!.is(eClass!)).to.be.true; + expect(await iClass!.is(cClass!)).to.be.true; + expect(await iClass!.is(fClass!)).to.be.true; + expect(await iClass!.is(dClass!)).to.be.true; + expect(await iClass!.is(hClass!)).to.be.true; + + expect(await jClass!.is(gClass!)).to.be.true; + expect(await jClass!.is(aClass!)).to.be.true; + expect(await jClass!.is(bClass!)).to.be.true; + expect(await jClass!.is(eClass!)).to.be.true; + expect(await jClass!.is(cClass!)).to.be.true; + expect(await jClass!.is(fClass!)).to.be.true; + expect(await jClass!.is(dClass!)).to.be.true; + expect(await jClass!.is(hClass!)).to.be.true; + expect(await jClass!.is(iClass!)).to.be.true; + + expect(await gClass!.is(iClass!)).to.be.false; + expect(await gClass!.is(jClass!)).to.be.false; + }); + it("class 'is' a base class synchronous", () => { - schema = Schema.fromJsonSync(testSchemaJson); + schema = Schema.fromJsonSync(testSchemaJson, new SchemaContext()); expect(schema).to.exist; const aClass = schema.getItemSync("A"); @@ -855,7 +943,7 @@ describe("ECClass", () => { ], }); - await assert.isRejected(Schema.fromJson(json), "The Navigation Property TestCA.testNavProp is invalid, because only EntityClasses, Mixins, and RelationshipClasses can have NavigationProperties."); + await assert.isRejected(Schema.fromJson(json, new SchemaContext()), "The Navigation Property TestCA.testNavProp is invalid, because only EntityClasses, Mixins, and RelationshipClasses can have NavigationProperties."); }); it("should throw synchronously", () => { @@ -871,15 +959,15 @@ describe("ECClass", () => { ], }); - assert.throw(() => Schema.fromJsonSync(json), "The Navigation Property TestCA.testNavProp is invalid, because only EntityClasses, Mixins, and RelationshipClasses can have NavigationProperties."); + assert.throw(() => Schema.fromJsonSync(json, new SchemaContext()), "The Navigation Property TestCA.testNavProp is invalid, because only EntityClasses, Mixins, and RelationshipClasses can have NavigationProperties."); }); }); describe("classesAreEqualByKey tests", () => { const schemaKeyA = new SchemaKey("SchemaTest", 1, 2, 3); const schemaKeyB = new SchemaKey("OtherTestSchema", 1, 2, 3); - const schemaA = new Schema(schemaKeyA) - const schemaB = new Schema(schemaKeyB) + const schemaA = new Schema(new SchemaContext(), schemaKeyA); + const schemaB = new Schema(new SchemaContext(), schemaKeyB); it("should return false if names do not match", () => { const testClassA = new Mixin(schemaA, "MixinA"); diff --git a/core/ecschema-metadata/test/Metadata/Constant.test.ts b/core/ecschema-metadata/test/Metadata/Constant.test.ts index b9fe1bb..d35632e 100644 --- a/core/ecschema-metadata/test/Metadata/Constant.test.ts +++ b/core/ecschema-metadata/test/Metadata/Constant.test.ts @@ -4,35 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import { assert, expect } from "chai"; -import { Schema } from "../../src/Metadata/Schema"; -import { Constant } from "../../src/Metadata/Constant"; + +import { SchemaContext } from "../../src/Context"; +import { SchemaItemType } from "../../src/ECObjects"; import { ECObjectsError } from "../../src/Exception"; -import * as sinon from "sinon"; +import { Constant } from "../../src/Metadata/Constant"; import { Phenomenon } from "../../src/Metadata/Phenomenon"; +import { Schema } from "../../src/Metadata/Schema"; import { createSchemaJsonWithItems } from "../TestUtils/DeserializationHelpers"; -import { SchemaItemType } from "../../src/ECObjects"; describe("Constant", () => { - describe("accept", () => { - let testConstant: Constant; - beforeEach(() => { - const schema = new Schema("TestSchema", 1, 0, 0); - testConstant = new Constant(schema, "TestEnumeration"); - }); - - it("should call visitConstant on a SchemaItemVisitor object", async () => { - expect(testConstant).to.exist; - const mockVisitor = { visitConstant: sinon.spy() }; - await testConstant.accept(mockVisitor); - expect(mockVisitor.visitConstant.calledOnce).to.be.true; - expect(mockVisitor.visitConstant.calledWithExactly(testConstant)).to.be.true; - }); - - it("should safely handle a SchemaItemVisitor without visitConstant defined", async () => { - expect(testConstant).to.exist; - await testConstant.accept({}); - }); - }); function createSchemaJson(constantJson: any): any { return createSchemaJsonWithItems({ @@ -62,7 +43,7 @@ describe("Constant", () => { }); it("async - should succeed with fully defined", async () => { - const ecSchema = await Schema.fromJson(fullyDefinedConstant); + const ecSchema = await Schema.fromJson(fullyDefinedConstant, new SchemaContext()); assert.isDefined(ecSchema); const testItem = await ecSchema.getItem("TestConstant"); assert.isDefined(testItem); @@ -78,13 +59,13 @@ describe("Constant", () => { expect(testConst.numerator).eql(5.5); expect(testConst.denominator).eql(5.1); - assert(testConst.definition, "PI"); + assert.strictEqual(testConst.definition, "PI"); assert.isDefined(testConst.phenomenon); expect(await testConst.phenomenon).eql(await ecSchema.getItem(testConst.phenomenon!.name)); }); it("sync - should succeed with fully defined", () => { - const ecSchema = Schema.fromJsonSync(fullyDefinedConstant); + const ecSchema = Schema.fromJsonSync(fullyDefinedConstant, new SchemaContext()); assert.isDefined(ecSchema); const testItem = ecSchema.getItemSync("TestConstant"); assert.isDefined(testItem); @@ -112,7 +93,7 @@ describe("Constant", () => { phenomenon: "TestSchema.TestPhenomenon", }); it("async - should succeed with defaults with minimum required properties provided", async () => { - const ecSchema = await Schema.fromJson(minimumRequired); + const ecSchema = await Schema.fromJson(minimumRequired, new SchemaContext()); assert.isDefined(ecSchema); const testItem = await ecSchema.getItem("TestConstant"); assert.isDefined(testItem); @@ -122,7 +103,7 @@ describe("Constant", () => { }); it("sync - should succeed with defaults with minimum required properties provided", () => { - const ecSchema = Schema.fromJsonSync(minimumRequired); + const ecSchema = Schema.fromJsonSync(minimumRequired, new SchemaContext()); assert.isDefined(ecSchema); const testItem = ecSchema.getItemSync("TestConstant"); assert.isDefined(testItem); @@ -136,10 +117,10 @@ describe("Constant", () => { definition: "testing", }; it("async - should throw for missing phenomenon", async () => { - await expect(Schema.fromJson(createSchemaJson(missingPhenomenon))).to.be.rejectedWith(ECObjectsError, `The Constant TestSchema.TestConstant does not have the required 'phenomenon' attribute.`); + await expect(Schema.fromJson(createSchemaJson(missingPhenomenon), new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The Constant TestSchema.TestConstant does not have the required 'phenomenon' attribute.`); }); it("sync - should throw for missing phenomenon", () => { - assert.throws(() => Schema.fromJsonSync(createSchemaJson(missingPhenomenon)), ECObjectsError, `The Constant TestSchema.TestConstant does not have the required 'phenomenon' attribute.`); + assert.throws(() => Schema.fromJsonSync(createSchemaJson(missingPhenomenon), new SchemaContext()), ECObjectsError, `The Constant TestSchema.TestConstant does not have the required 'phenomenon' attribute.`); }); // Invalid phenomenon @@ -148,10 +129,10 @@ describe("Constant", () => { phenomenon: 5, }; it("async - should throw for invalid phenomenon", async () => { - await expect(Schema.fromJson(createSchemaJson(invalidPhenomenon))).to.be.rejectedWith(ECObjectsError, `The Constant TestSchema.TestConstant has an invalid 'phenomenon' attribute. It should be of type 'string'.`); + await expect(Schema.fromJson(createSchemaJson(invalidPhenomenon), new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The Constant TestSchema.TestConstant has an invalid 'phenomenon' attribute. It should be of type 'string'.`); }); it("sync - should throw for invalid phenomenon", () => { - assert.throws(() => Schema.fromJsonSync(createSchemaJson(invalidPhenomenon)), ECObjectsError, `The Constant TestSchema.TestConstant has an invalid 'phenomenon' attribute. It should be of type 'string'.`); + assert.throws(() => Schema.fromJsonSync(createSchemaJson(invalidPhenomenon), new SchemaContext()), ECObjectsError, `The Constant TestSchema.TestConstant has an invalid 'phenomenon' attribute. It should be of type 'string'.`); }); // Not found phenomenon @@ -160,10 +141,10 @@ describe("Constant", () => { phenomenon: "TestSchema.BadPhenomenonName", }; it("async - should throw for phenomenon not found", async () => { - await expect(Schema.fromJson(createSchemaJson(nonexistentPhenomenon))).to.be.rejectedWith(ECObjectsError, `Unable to locate SchemaItem TestSchema.BadPhenomenonName.`); + await expect(Schema.fromJson(createSchemaJson(nonexistentPhenomenon), new SchemaContext())).to.be.rejectedWith(ECObjectsError, `Unable to locate SchemaItem TestSchema.BadPhenomenonName.`); }); it("sync - should throw for phenomenon not found", () => { - assert.throws(() => Schema.fromJsonSync(createSchemaJson(nonexistentPhenomenon)), ECObjectsError, `Unable to locate SchemaItem TestSchema.BadPhenomenonName.`); + assert.throws(() => Schema.fromJsonSync(createSchemaJson(nonexistentPhenomenon), new SchemaContext()), ECObjectsError, `Unable to locate SchemaItem TestSchema.BadPhenomenonName.`); }); // Missing definition @@ -171,10 +152,10 @@ describe("Constant", () => { phenomenon: "TestSchema.TestPhenomenon", }; it("async - should throw for missing definition", async () => { - await expect(Schema.fromJson(createSchemaJson(missingDefinition))).to.be.rejectedWith(ECObjectsError, `The Constant TestSchema.TestConstant does not have the required 'definition' attribute.`); + await expect(Schema.fromJson(createSchemaJson(missingDefinition), new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The Constant TestSchema.TestConstant does not have the required 'definition' attribute.`); }); it("sync - should throw for missing definition", () => { - assert.throws(() => Schema.fromJsonSync(createSchemaJson(missingDefinition)), ECObjectsError, `The Constant TestSchema.TestConstant does not have the required 'definition' attribute.`); + assert.throws(() => Schema.fromJsonSync(createSchemaJson(missingDefinition), new SchemaContext()), ECObjectsError, `The Constant TestSchema.TestConstant does not have the required 'definition' attribute.`); }); // Invalid definition @@ -183,10 +164,10 @@ describe("Constant", () => { definition: 5, }; it("async - should throw for invalid definition", async () => { - await expect(Schema.fromJson(createSchemaJson(invalidDefinition))).to.be.rejectedWith(ECObjectsError, `The Constant TestSchema.TestConstant has an invalid 'definition' attribute. It should be of type 'string'.`); + await expect(Schema.fromJson(createSchemaJson(invalidDefinition), new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The Constant TestSchema.TestConstant has an invalid 'definition' attribute. It should be of type 'string'.`); }); it("sync - should throw for invalid definition", () => { - assert.throws(() => Schema.fromJsonSync(createSchemaJson(invalidDefinition)), ECObjectsError, `The Constant TestSchema.TestConstant has an invalid 'definition' attribute. It should be of type 'string'.`); + assert.throws(() => Schema.fromJsonSync(createSchemaJson(invalidDefinition), new SchemaContext()), ECObjectsError, `The Constant TestSchema.TestConstant has an invalid 'definition' attribute. It should be of type 'string'.`); }); }); describe("toJson", () => { @@ -201,7 +182,7 @@ describe("Constant", () => { }); it("async - should succeed with fully defined with standalone", async () => { - const ecSchema = await Schema.fromJson(fullyDefinedConstant); + const ecSchema = await Schema.fromJson(fullyDefinedConstant, new SchemaContext()); assert.isDefined(ecSchema); const testItem = await ecSchema.getItem("TestConstant"); assert.isDefined(testItem); @@ -212,7 +193,7 @@ describe("Constant", () => { expect(constantSerialization.$schema).eql("https://dev.bentley.com/json_schemas/ec/32/draft-01/schemaitem"); expect(constantSerialization.name).eql("TestConstant"); - expect(constantSerialization.schemaVersion).eql("1.2.3"); + expect(constantSerialization.schemaVersion).eql("01.02.03"); expect(constantSerialization.schema).eql("TestSchema"); expect(constantSerialization.label).eql("Test Constant"); @@ -221,12 +202,12 @@ describe("Constant", () => { expect(constantSerialization.numerator).eql(5.5); expect(constantSerialization.denominator).eql(5.1); - assert(constantSerialization.definition, "PI"); - assert(constantSerialization.phenomenon, "TestSchema.TestPhenomenon"); + assert.strictEqual(constantSerialization.definition, "PI"); + assert.strictEqual(constantSerialization.phenomenon, "TestSchema.TestPhenomenon"); }); it("sync - should succeed with fully defined with standalone", () => { - const ecSchema = Schema.fromJsonSync(fullyDefinedConstant); + const ecSchema = Schema.fromJsonSync(fullyDefinedConstant, new SchemaContext()); assert.isDefined(ecSchema); const testItem = ecSchema.getItemSync("TestConstant"); assert.isDefined(testItem); @@ -237,7 +218,7 @@ describe("Constant", () => { expect(constantSerialization.$schema).eql("https://dev.bentley.com/json_schemas/ec/32/draft-01/schemaitem"); expect(constantSerialization.name).eql("TestConstant"); - expect(constantSerialization.schemaVersion).eql("1.2.3"); + expect(constantSerialization.schemaVersion).eql("01.02.03"); expect(constantSerialization.schema).eql("TestSchema"); expect(constantSerialization.label).eql("Test Constant"); @@ -246,11 +227,11 @@ describe("Constant", () => { expect(constantSerialization.numerator).eql(5.5); expect(constantSerialization.denominator).eql(5.1); - assert(constantSerialization.definition, "PI"); - assert(constantSerialization.phenomenon, "TestSchema.TestPhenomenon"); + assert.strictEqual(constantSerialization.definition, "PI"); + assert.strictEqual(constantSerialization.phenomenon, "TestSchema.TestPhenomenon"); }); it("async - should succeed with fully defined without standalone", async () => { - const ecSchema = await Schema.fromJson(fullyDefinedConstant); + const ecSchema = await Schema.fromJson(fullyDefinedConstant, new SchemaContext()); assert.isDefined(ecSchema); const testItem = await ecSchema.getItem("TestConstant"); assert.isDefined(testItem); @@ -265,12 +246,12 @@ describe("Constant", () => { expect(constantSerialization.numerator).eql(5.5); expect(constantSerialization.denominator).eql(5.1); - assert(constantSerialization.definition, "PI"); - assert(constantSerialization.phenomenon, "TestSchema.TestPhenomenon"); + assert.strictEqual(constantSerialization.definition, "PI"); + assert.strictEqual(constantSerialization.phenomenon, "TestSchema.TestPhenomenon"); }); it("sync - should succeed with fully defined without standalone", () => { - const ecSchema = Schema.fromJsonSync(fullyDefinedConstant); + const ecSchema = Schema.fromJsonSync(fullyDefinedConstant, new SchemaContext()); assert.isDefined(ecSchema); const testItem = ecSchema.getItemSync("TestConstant"); assert.isDefined(testItem); @@ -285,8 +266,8 @@ describe("Constant", () => { expect(constantSerialization.numerator).eql(5.5); expect(constantSerialization.denominator).eql(5.1); - assert(constantSerialization.definition, "PI"); - assert(constantSerialization.phenomenon, "TestSchema.TestPhenomenon"); + assert.strictEqual(constantSerialization.definition, "PI"); + assert.strictEqual(constantSerialization.phenomenon, "TestSchema.TestPhenomenon"); }); }); }); diff --git a/core/ecschema-metadata/test/Metadata/CustomAttributeClass.test.ts b/core/ecschema-metadata/test/Metadata/CustomAttributeClass.test.ts index 0e0be43..a1966f8 100644 --- a/core/ecschema-metadata/test/Metadata/CustomAttributeClass.test.ts +++ b/core/ecschema-metadata/test/Metadata/CustomAttributeClass.test.ts @@ -6,11 +6,11 @@ import { assert, expect } from "chai"; import { createSchemaJsonWithItems } from "../TestUtils/DeserializationHelpers"; -import { Schema } from "../../src/Metadata/Schema"; +import { SchemaContext } from "../../src/Context"; +import { CustomAttributeContainerType, ECClassModifier } from "../../src/ECObjects"; import { ECObjectsError } from "../../src/Exception"; import { CustomAttributeClass } from "../../src/Metadata/CustomAttributeClass"; -import { ECClassModifier } from "../../src/ECObjects"; -import { CustomAttributeContainerType } from "../../src/ECObjects"; +import { Schema } from "../../src/Metadata/Schema"; describe("CustomAttributeClass", () => { describe("deserialization", () => { @@ -31,7 +31,7 @@ describe("CustomAttributeClass", () => { appliesTo: "AnyClass", }); - const ecschema = await Schema.fromJson(schemaJson); + const ecschema = await Schema.fromJson(schemaJson, new SchemaContext()); const testCAClass = await ecschema.getItem("TestCAClass"); expect(testCAClass).to.exist; @@ -48,7 +48,7 @@ describe("CustomAttributeClass", () => { appliesTo: "Schema", properties: [{ name: "navProp", type: "NavigationProperty" }], }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The Navigation Property TestCAClass.navProp is invalid, because only EntityClasses, Mixins, and RelationshipClasses can have NavigationProperties.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The Navigation Property TestCAClass.navProp is invalid, because only EntityClasses, Mixins, and RelationshipClasses can have NavigationProperties.`); }); }); @@ -62,13 +62,13 @@ describe("CustomAttributeClass", () => { let testClass: CustomAttributeClass; beforeEach(() => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); testClass = new CustomAttributeClass(schema, "TestCustomAttribute"); }); it("async - should succeed with fully defined standalone", async () => { const schemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/31/draft-01/schemaitem", + $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/schemaitem", schema: "TestSchema", schemaVersion: "1.0.0", schemaItemType: "CustomAttributeClass", @@ -79,17 +79,17 @@ describe("CustomAttributeClass", () => { await testClass.deserialize(schemaJson); const caJson = testClass!.toJson(true, true); - assert(caJson.$schema, "https://dev.bentley.com/json_schemas/ec/31/draft-01/schemaitem"); - assert(caJson.appliesTo, "Schema,AnyProperty"); - assert(caJson.modifier, "Sealed"); - assert(caJson.name, "TestCustomAttribute"); - assert(caJson.schema, "TestSchema"); - assert(caJson.schemaItemType, "CustomAttributeClass"); - assert(caJson.schemaVersion, "1.0.0"); + assert.strictEqual(caJson.$schema, "https://dev.bentley.com/json_schemas/ec/32/draft-01/schemaitem"); + assert.strictEqual(caJson.appliesTo, "Schema, AnyProperty"); + assert.strictEqual(caJson.modifier, "Sealed"); + assert.strictEqual(caJson.name, "TestCustomAttribute"); + assert.strictEqual(caJson.schema, "TestSchema"); + assert.strictEqual(caJson.schemaItemType, "CustomAttributeClass"); + assert.strictEqual(caJson.schemaVersion, "01.00.00"); }); it("sync - should succeed with fully defined standalone", () => { const schemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/31/draft-01/schemaitem", + $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/schemaitem", schema: "TestSchema", schemaVersion: "1.0.0", schemaItemType: "CustomAttributeClass", @@ -100,13 +100,13 @@ describe("CustomAttributeClass", () => { testClass.deserializeSync(schemaJson); const caJson = testClass!.toJson(true, true); - assert(caJson.$schema, "https://dev.bentley.com/json_schemas/ec/31/draft-01/schemaitem"); - assert(caJson.appliesTo, "Schema,AnyProperty"); - assert(caJson.modifier, "Sealed"); - assert(caJson.name, "TestCustomAttribute"); - assert(caJson.schema, "TestSchema"); - assert(caJson.schemaItemType, "CustomAttributeClass"); - assert(caJson.schemaVersion, "1.0.0"); + assert.strictEqual(caJson.$schema, "https://dev.bentley.com/json_schemas/ec/32/draft-01/schemaitem"); + assert.strictEqual(caJson.appliesTo, "Schema, AnyProperty"); + assert.strictEqual(caJson.modifier, "Sealed"); + assert.strictEqual(caJson.name, "TestCustomAttribute"); + assert.strictEqual(caJson.schema, "TestSchema"); + assert.strictEqual(caJson.schemaItemType, "CustomAttributeClass"); + assert.strictEqual(caJson.schemaVersion, "01.00.00"); }); it("async - should succeed with fully defined without standalone", async () => { const schemaJson = createSchemaJsonWithItems({ @@ -124,7 +124,7 @@ describe("CustomAttributeClass", () => { appliesTo: "Schema, AnyProperty", }, }); - const ecschema = await Schema.fromJson(schemaJson); + const ecschema = await Schema.fromJson(schemaJson, new SchemaContext()); assert.isDefined(ecschema); const testCustomAttribute = await ecschema.getItem("testCustomAttribute"); @@ -152,7 +152,7 @@ describe("CustomAttributeClass", () => { appliesTo: "Schema, AnyProperty", }, }); - const ecschema = Schema.fromJsonSync(schemaJson); + const ecschema = Schema.fromJsonSync(schemaJson, new SchemaContext()); assert.isDefined(ecschema); const testCustomAttribute = ecschema.getItemSync("testCustomAttribute"); diff --git a/core/ecschema-metadata/test/Metadata/Deserialization.test.ts b/core/ecschema-metadata/test/Metadata/Deserialization.test.ts index 0e98698..baa0694 100644 --- a/core/ecschema-metadata/test/Metadata/Deserialization.test.ts +++ b/core/ecschema-metadata/test/Metadata/Deserialization.test.ts @@ -6,28 +6,28 @@ import { assert, expect } from "chai"; import * as sinon from "sinon"; -import { Schema } from "./../../src/Metadata/Schema"; +import { JsonParser } from "../../src/Deserialization/JsonParser"; import { SchemaContext } from "./../../src/Context"; -import { ECObjectsError } from "./../../src/Exception"; -import { SchemaDeserializationVisitor } from "./../../src/Interfaces"; import { SchemaReadHelper } from "./../../src/Deserialization/Helper"; -import { AnyClass } from "./../../src/Interfaces"; import { SchemaItemType } from "./../../src/ECObjects"; +import { ECObjectsError } from "./../../src/Exception"; +import { AnyClass } from "./../../src/Interfaces"; import { NavigationProperty } from "./../../src/Metadata/Property"; -import { JsonParser } from "../../src/Deserialization/JsonParser"; +import { Schema } from "./../../src/Metadata/Schema"; +import { ISchemaPartVisitor } from "../../src/SchemaPartVisitorDelegate"; describe("Full Schema Deserialization", () => { describe("basic (empty) schemas", () => { it("should successfully deserialize a valid JSON string", async () => { const schemaString = JSON.stringify({ - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", description: "This is a test description", label: "This is a test label", }); - const ecschema = await Schema.fromJson(schemaString); + const ecschema = await Schema.fromJson(schemaString, new SchemaContext()); expect(ecschema.name).equal("TestSchema"); expect(ecschema.readVersion).equal(1); expect(ecschema.writeVersion).equal(2); @@ -37,14 +37,14 @@ describe("Full Schema Deserialization", () => { }); it("should successfully deserialize a valid JSON string synchronously", () => { const schemaString = JSON.stringify({ - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", description: "This is a test description", label: "This is a test label", }); - const ecschema = Schema.fromJsonSync(schemaString); + const ecschema = Schema.fromJsonSync(schemaString, new SchemaContext()); expect(ecschema.name).equal("TestSchema"); expect(ecschema.readVersion).equal(1); expect(ecschema.writeVersion).equal(2); @@ -55,14 +55,14 @@ describe("Full Schema Deserialization", () => { it("should successfully deserialize name and version from a valid JSON object", async () => { const schemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", description: "This is a test description", label: "This is a test label", }; - const ecschema = await Schema.fromJson(schemaJson); + const ecschema = await Schema.fromJson(schemaJson, new SchemaContext()); expect(ecschema.name).equal("TestSchema"); expect(ecschema.readVersion).equal(1); expect(ecschema.writeVersion).equal(2); @@ -73,28 +73,28 @@ describe("Full Schema Deserialization", () => { it("should throw for invalid schema version", async () => { const schemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.100.0", }; - await expect(Schema.fromJson(schemaJson)).to.be.rejectedWith(ECObjectsError); + await expect(Schema.fromJson(schemaJson, new SchemaContext())).to.be.rejectedWith(ECObjectsError); }); it("should throw for invalid schema name", async () => { const schemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "0TestSchema", version: "1.0.0", }; - await expect(Schema.fromJson(schemaJson)).to.be.rejectedWith(ECObjectsError); + await expect(Schema.fromJson(schemaJson, new SchemaContext())).to.be.rejectedWith(ECObjectsError); }); }); describe("with schema reference", () => { const baseJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", }; @@ -109,8 +109,8 @@ describe("Full Schema Deserialization", () => { }; it("should succeed when referenced schema is already in the schema context", async () => { - const refSchema = new Schema("RefSchema", 1, 0, 5); const context = new SchemaContext(); + const refSchema = new Schema(context, "RefSchema", 1, 0, 5); await context.addSchema(refSchema); const schema = await Schema.fromJson(validSchemaJson, context); @@ -126,16 +126,15 @@ describe("Full Schema Deserialization", () => { }); it("should throw if the referenced schema cannot be found", async () => { - const context = new SchemaContext(); - await expect(Schema.fromJson(validSchemaJson, context)).to.be.rejectedWith(ECObjectsError, "Could not locate the referenced schema, RefSchema.1.0.5, of TestSchema"); + await expect(Schema.fromJson(validSchemaJson, new SchemaContext())).to.be.rejectedWith(ECObjectsError, "Could not locate the referenced schema, RefSchema.1.0.5, of TestSchema"); }); it("should throw for invalid references attribute", async () => { let json: any = { ...baseJson, references: 0 }; - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The schema TestSchema has an invalid 'references' attribute. It should be of type 'object[]'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The schema TestSchema has an invalid 'references' attribute. It should be of type 'object[]'.`); json = { ...baseJson, references: [0] }; - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The schema TestSchema has an invalid 'references' attribute. It should be of type 'object[]'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The schema TestSchema has an invalid 'references' attribute. It should be of type 'object[]'.`); }); it("should throw for missing reference name", async () => { @@ -143,7 +142,7 @@ describe("Full Schema Deserialization", () => { ...baseJson, references: [{ version: "1.0.5" }], }; - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The schema TestSchema has an invalid 'references' attribute. One of the references is missing the required 'name' attribute.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The schema TestSchema has an invalid 'references' attribute. One of the references is missing the required 'name' attribute.`); }); it("should throw for invalid reference name", async () => { @@ -151,7 +150,7 @@ describe("Full Schema Deserialization", () => { ...baseJson, references: [{ name: 0, version: "1.0.5" }], }; - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The schema TestSchema has an invalid 'references' attribute. One of the references has an invalid 'name' attribute. It should be of type 'string'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The schema TestSchema has an invalid 'references' attribute. One of the references has an invalid 'name' attribute. It should be of type 'string'.`); }); it("should throw for missing reference version", async () => { @@ -159,7 +158,7 @@ describe("Full Schema Deserialization", () => { ...baseJson, references: [{ name: "RefSchema" }], }; - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The schema TestSchema has an invalid 'references' attribute. One of the references is missing the required 'version' attribute.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The schema TestSchema has an invalid 'references' attribute. One of the references is missing the required 'version' attribute.`); }); it("should throw for invalid reference version", async () => { @@ -167,28 +166,28 @@ describe("Full Schema Deserialization", () => { ...baseJson, references: [{ name: "RefSchema", version: 0 }], }; - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The schema TestSchema has an invalid 'references' attribute. One of the references has an invalid 'version' attribute. It should be of type 'string'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The schema TestSchema has an invalid 'references' attribute. One of the references has an invalid 'version' attribute. It should be of type 'string'.`); }); }); describe("with items", () => { const baseJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", }; it("should throw for invalid items attribute", async () => { let json: any = { ...baseJson, items: 0 }; - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The schema TestSchema has an invalid 'items' attribute. It should be of type 'object'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The schema TestSchema has an invalid 'items' attribute. It should be of type 'object'.`); json = { ...baseJson, items: [{}] }; - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The schema TestSchema has an invalid 'items' attribute. It should be of type 'object'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The schema TestSchema has an invalid 'items' attribute. It should be of type 'object'.`); }); it("should throw for item with invalid name", async () => { const json = { ...baseJson, items: { "": {} } }; - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `A SchemaItem in TestSchema has an invalid 'name' attribute. '' is not a valid ECName.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `A SchemaItem in TestSchema has an invalid 'name' attribute. '' is not a valid ECName.`); }); it("should throw for item with missing schemaItemType", async () => { @@ -196,7 +195,7 @@ describe("Full Schema Deserialization", () => { ...baseJson, items: { BadItem: {} }, }; - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The SchemaItem TestSchema.BadItem is missing the required 'schemaItemType' attribute.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The SchemaItem TestSchema.BadItem is missing the required 'schemaItemType' attribute.`); }); it("should throw for item with invalid schemaItemType", async () => { @@ -204,18 +203,18 @@ describe("Full Schema Deserialization", () => { ...baseJson, items: { BadItem: { schemaItemType: 0 } }, }; - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The SchemaItem TestSchema.BadItem has an invalid 'schemaItemType' attribute. It should be of type 'string'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The SchemaItem TestSchema.BadItem has an invalid 'schemaItemType' attribute. It should be of type 'string'.`); }); }); describe("with visitor", () => { const baseJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", }; type Mock = { readonly [P in keyof T]: sinon.SinonSpy; }; - let mockVisitor: Mock; + let mockVisitor: Mock; beforeEach(() => { mockVisitor = { @@ -266,8 +265,10 @@ describe("Full Schema Deserialization", () => { }, }, }; - let testSchema = new Schema(); - const reader = new SchemaReadHelper(JsonParser, undefined, mockVisitor); + + const context = new SchemaContext(); + let testSchema = new Schema(context); + const reader = new SchemaReadHelper(JsonParser, context, mockVisitor); testSchema = await reader.readSchema(testSchema, schemaJson); expect(testSchema).to.exist; expect(mockVisitor!.visitEmptySchema!.calledOnce).to.be.true; @@ -332,8 +333,9 @@ describe("Full Schema Deserialization", () => { }), }; - let testSchema = new Schema(); - const reader = new SchemaReadHelper(JsonParser, undefined, mockVisitor); + const context = new SchemaContext(); + let testSchema = new Schema(context); + const reader = new SchemaReadHelper(JsonParser, context, mockVisitor); testSchema = await reader.readSchema(testSchema, schemaJson); expect(testSchema).to.exist; @@ -348,11 +350,11 @@ describe("Full Schema Deserialization", () => { expect(mockVisitor!.visitClass!.firstCall.calledWithExactly(testEntity)).to.be.true; expect(descriptions[0]).to.equal("Description for AMixin", - `SchemaDeserializationVisitor.visitClass was called for "BEntityClass" before its base class, "AMixin" was fully deserialized.`); + `ISchemaPartVisitor.visitClass was called for "BEntityClass" before its base class, "AMixin" was fully deserialized.`); expect(mockVisitor!.visitClass!.secondCall.calledWithExactly(testMixin)).to.be.true; expect(descriptions[1]).to.equal("Description for BEntityClass", - `SchemaDeserializationVisitor.visitClass was called for "AMixin" before its appliesTo class, "BEntityClass" was fully deserialized.`); + `ISchemaPartVisitor.visitClass was called for "AMixin" before its appliesTo class, "BEntityClass" was fully deserialized.`); }); it("should safely handle EntityClass-extends-Mixin-appliesTo-EntityClass cycle", async () => { @@ -382,8 +384,9 @@ describe("Full Schema Deserialization", () => { }), }; - let testSchema = new Schema(); - const reader = new SchemaReadHelper(JsonParser, undefined, mockVisitor); + const context = new SchemaContext(); + let testSchema = new Schema(context); + const reader = new SchemaReadHelper(JsonParser, context, mockVisitor); testSchema = await reader.readSchema(testSchema, schemaJson); expect(testSchema).to.exist; @@ -398,11 +401,11 @@ describe("Full Schema Deserialization", () => { expect(mockVisitor!.visitClass!.firstCall.calledWithExactly(testMixin)).to.be.true; expect(descriptions[0]).to.equal("Description for AEntityClass", - `SchemaDeserializationVisitor.visitClass was called for "BMixin" before its appliesTo class, "AEntityClass" was fully deserialized.`); + `ISchemaPartVisitor.visitClass was called for "BMixin" before its appliesTo class, "AEntityClass" was fully deserialized.`); expect(mockVisitor!.visitClass!.secondCall.calledWithExactly(testEntity)).to.be.true; expect(descriptions[1]).to.equal("Description for BMixin", - `SchemaDeserializationVisitor.visitClass was called for "AEntityClass" before its base class, "BMixin" was fully deserialized.`); + `ISchemaPartVisitor.visitClass was called for "AEntityClass" before its base class, "BMixin" was fully deserialized.`); }); it("should safely handle EntityClass-navProp-RelationshipClass-constraint-EntityClass cycle", async () => { @@ -454,8 +457,9 @@ describe("Full Schema Deserialization", () => { }), }; - let testSchema = new Schema(); - const reader = new SchemaReadHelper(JsonParser, undefined, mockVisitor); + const context = new SchemaContext(); + let testSchema = new Schema(context); + const reader = new SchemaReadHelper(JsonParser, context, mockVisitor); testSchema = await reader.readSchema(testSchema, schemaJson); expect(testSchema).to.exist; @@ -470,11 +474,11 @@ describe("Full Schema Deserialization", () => { expect(mockVisitor!.visitClass!.firstCall.calledWithExactly(testRelationship)).to.be.true; expect(descriptions[0]).to.equal("Description for AEntityClass", - `SchemaDeserializationVisitor.visitClass was called for "BRelationshipClass" before the entity class its constraints use, "AEntityClass" was fully deserialized.`); + `ISchemaPartVisitor.visitClass was called for "BRelationshipClass" before the entity class its constraints use, "AEntityClass" was fully deserialized.`); expect(mockVisitor!.visitClass!.secondCall.calledWithExactly(testEntity)).to.be.true; expect(descriptions[1]).to.equal("Description for BRelationshipClass", - `SchemaDeserializationVisitor.visitClass was called for "AEntityClass" before the relationship its NavigationProperty uses, "BRelationshipClass" was fully deserialized.`); + `ISchemaPartVisitor.visitClass was called for "AEntityClass" before the relationship its NavigationProperty uses, "BRelationshipClass" was fully deserialized.`); }); it("should safely handle RelationshipClass-constraint-EntityClass-navProp-RelationshipClass cycle", async () => { @@ -526,8 +530,9 @@ describe("Full Schema Deserialization", () => { }), }; - let testSchema = new Schema(); - const reader = new SchemaReadHelper(JsonParser, undefined, mockVisitor); + const context = new SchemaContext(); + let testSchema = new Schema(context); + const reader = new SchemaReadHelper(JsonParser, context, mockVisitor); testSchema = await reader.readSchema(testSchema, schemaJson); expect(testSchema).to.exist; @@ -542,18 +547,18 @@ describe("Full Schema Deserialization", () => { expect(mockVisitor!.visitClass!.firstCall.calledWithExactly(testEntity)).to.be.true; expect(descriptions[0]).to.equal("Description for ARelationshipClass", - `SchemaDeserializationVisitor.visitClass was called for "BEntityClass" before the relationship its NavigationProperty uses, "ARelationshipClass" was fully deserialized.`); + `ISchemaPartVisitor.visitClass was called for "BEntityClass" before the relationship its NavigationProperty uses, "ARelationshipClass" was fully deserialized.`); expect(mockVisitor!.visitClass!.secondCall.calledWithExactly(testRelationship)).to.be.true; expect(descriptions[1]).to.equal("Description for BEntityClass", - `SchemaDeserializationVisitor.visitClass was called for "ARelationshipClass" before the entity class its constraints use, "BEntityClass" was fully deserialized.`); + `ISchemaPartVisitor.visitClass was called for "ARelationshipClass" before the entity class its constraints use, "BEntityClass" was fully deserialized.`); }); }); describe("with schema custom attributes", () => { const baseJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "ValidSchema", version: "1.2.3", alias: "vs", @@ -562,7 +567,7 @@ describe("Full Schema Deserialization", () => { items: { TestCAClassA: { schemaItemType: "CustomAttributeClass", appliesTo: "AnyClass" }, TestCAClassB: { schemaItemType: "CustomAttributeClass", appliesTo: "AnyClass" }, - } + }, }; const oneCustomAttributeJson = { @@ -576,17 +581,17 @@ describe("Full Schema Deserialization", () => { }; it("async - single schema CustomAttribute", async () => { - const testSchema = await Schema.fromJson(oneCustomAttributeJson); + const testSchema = await Schema.fromJson(oneCustomAttributeJson, new SchemaContext()); expect(testSchema).to.exist; expect(testSchema.customAttributes!.get("ValidSchema.TestCAClassA")).to.exist; - assert(testSchema.customAttributes!.get("ValidSchema.TestCAClassA")!.ShowClasses === true); + assert.isTrue(testSchema.customAttributes!.get("ValidSchema.TestCAClassA")!.ShowClasses); }); it("sync - single schema CustomAttribute", () => { - const testSchema = Schema.fromJsonSync(oneCustomAttributeJson); + const testSchema = Schema.fromJsonSync(oneCustomAttributeJson, new SchemaContext()); expect(testSchema).to.exist; expect(testSchema.customAttributes!.get("ValidSchema.TestCAClassA")).to.exist; - assert(testSchema.customAttributes!.get("ValidSchema.TestCAClassA")!.ShowClasses === true); + assert.isTrue(testSchema.customAttributes!.get("ValidSchema.TestCAClassA")!.ShowClasses); }); const twoCustomAttributeJson = { @@ -602,14 +607,14 @@ describe("Full Schema Deserialization", () => { }; it("async - multiple schema CustomAttributes", async () => { - const testSchema = await Schema.fromJson(twoCustomAttributeJson); + const testSchema = await Schema.fromJson(twoCustomAttributeJson, new SchemaContext()); expect(testSchema).to.exist; expect(testSchema.customAttributes!.get("ValidSchema.TestCAClassA")).to.exist; expect(testSchema.customAttributes!.get("ValidSchema.TestCAClassB")).to.exist; }); it("sync - multiple schema CustomAttributes", () => { - const testSchema = Schema.fromJsonSync(twoCustomAttributeJson); + const testSchema = Schema.fromJsonSync(twoCustomAttributeJson, new SchemaContext()); expect(testSchema).to.exist; expect(testSchema.customAttributes!.get("ValidSchema.TestCAClassA")).to.exist; expect(testSchema.customAttributes!.get("ValidSchema.TestCAClassB")).to.exist; @@ -630,17 +635,17 @@ describe("Full Schema Deserialization", () => { ], }; - const testSchema = Schema.fromJsonSync(propertyJson); + const testSchema = Schema.fromJsonSync(propertyJson, new SchemaContext()); expect(testSchema.customAttributes!.get("ValidSchema.TestCAClassA")).to.exist; expect(testSchema.customAttributes!.get("ValidSchema.TestCAClassB")).to.exist; - assert(testSchema.customAttributes!.get("ValidSchema.TestCAClassA")!.ShowClasses === false); - assert(testSchema.customAttributes!.get("ValidSchema.TestCAClassB")!.ShowClasses === true); + assert.isFalse(testSchema.customAttributes!.get("ValidSchema.TestCAClassA")!.ShowClasses); + assert.isTrue(testSchema.customAttributes!.get("ValidSchema.TestCAClassB")!.ShowClasses); }); }); describe("with property custom attributes", () => { const getSchemaJson = (propJson: any) => ({ - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "ValidSchema", version: "1.2.3", alias: "vs", @@ -656,7 +661,7 @@ describe("Full Schema Deserialization", () => { type: "PrimitiveProperty", typeName: "int", ...propJson, - } + }, ], }, }, @@ -672,23 +677,23 @@ describe("Full Schema Deserialization", () => { }); it("async - single property CustomAttribute", async () => { - const testSchema = await Schema.fromJson(oneCustomAttributeJson); + const testSchema = await Schema.fromJson(oneCustomAttributeJson, new SchemaContext()); expect(testSchema).to.exist; const testProp = (await testSchema.getItem("TestClass") as AnyClass).properties![0]; expect(testProp).to.exist; expect(testProp.name).to.eql("TestProp"); expect(testProp.customAttributes!.get("ValidSchema.TestCAClassA")).to.exist; - assert(testProp.customAttributes!.get("ValidSchema.TestCAClassA")!.ExampleAttribute === 1234); + assert.strictEqual(testProp.customAttributes!.get("ValidSchema.TestCAClassA")!.ExampleAttribute, 1234); }); it("sync - single property CustomAttribute", () => { - const testSchema = Schema.fromJsonSync(oneCustomAttributeJson); + const testSchema = Schema.fromJsonSync(oneCustomAttributeJson, new SchemaContext()); expect(testSchema).to.exist; const testProp = (testSchema.getItemSync("TestClass") as AnyClass).properties![0]; expect(testProp).to.exist; expect(testProp.name).to.eql("TestProp"); expect(testProp.customAttributes!.get("ValidSchema.TestCAClassA")).to.exist; - assert(testProp.customAttributes!.get("ValidSchema.TestCAClassA")!.ExampleAttribute === 1234); + assert.strictEqual(testProp.customAttributes!.get("ValidSchema.TestCAClassA")!.ExampleAttribute, 1234); }); const twoCustomAttributesJson = getSchemaJson({ @@ -703,7 +708,7 @@ describe("Full Schema Deserialization", () => { }); it("async - multiple property CustomAttributes", async () => { - const testSchema = await Schema.fromJson(twoCustomAttributesJson); + const testSchema = await Schema.fromJson(twoCustomAttributesJson, new SchemaContext()); expect(testSchema).to.exist; const testProp = (await testSchema.getItem("TestClass") as AnyClass).properties![0]; expect(testProp).to.exist; @@ -713,7 +718,7 @@ describe("Full Schema Deserialization", () => { }); it("sync - multiple property CustomAttributes", () => { - const testSchema = Schema.fromJsonSync(twoCustomAttributesJson); + const testSchema = Schema.fromJsonSync(twoCustomAttributesJson, new SchemaContext()); expect(testSchema).to.exist; const testProp = (testSchema.getItemSync("TestClass") as AnyClass).properties![0]; expect(testProp).to.exist; @@ -739,14 +744,15 @@ describe("Full Schema Deserialization", () => { }, ], }; - const testSchema = Schema.fromJsonSync(getSchemaJson(propertyJson)); + + const testSchema = Schema.fromJsonSync(getSchemaJson(propertyJson), new SchemaContext()); expect(testSchema).to.exist; const testProp = (testSchema.getItemSync("TestClass") as AnyClass).properties![0]; expect(testProp).to.exist; - assert(testProp.customAttributes!.get("ValidSchema.TestCAClassA")!.ShowClasses === 1.2); - assert(testProp.customAttributes!.get("ValidSchema.TestCAClassB")!.ExampleAttribute === true); - assert(testProp.customAttributes!.get("ValidSchema.TestCAClassC")!.Example2Attribute === "example"); + assert.strictEqual(testProp.customAttributes!.get("ValidSchema.TestCAClassA")!.ShowClasses, 1.2); + assert.isTrue(testProp.customAttributes!.get("ValidSchema.TestCAClassB")!.ExampleAttribute); + assert.strictEqual(testProp.customAttributes!.get("ValidSchema.TestCAClassC")!.Example2Attribute, "example"); }); }); }); diff --git a/core/ecschema-metadata/test/Metadata/EntityClass.test.ts b/core/ecschema-metadata/test/Metadata/EntityClass.test.ts index ff60b74..42d290c 100644 --- a/core/ecschema-metadata/test/Metadata/EntityClass.test.ts +++ b/core/ecschema-metadata/test/Metadata/EntityClass.test.ts @@ -4,23 +4,24 @@ *--------------------------------------------------------------------------------------------*/ import { assert, expect } from "chai"; -import { createSchemaJsonWithItems } from "./../TestUtils/DeserializationHelpers"; +import { SchemaContext } from "../../src/Context"; -import { Schema, MutableSchema } from "./../../src/Metadata/Schema"; +import { DelayedPromiseWithProps } from "./../../src/DelayedPromise"; +import { ECClassModifier } from "./../../src/ECObjects"; +import { ECObjectsError } from "./../../src/Exception"; import { ECClass, MutableClass } from "./../../src/Metadata/Class"; import { EntityClass, MutableEntityClass } from "./../../src/Metadata/EntityClass"; import { Mixin } from "./../../src/Metadata/Mixin"; import { RelationshipClass } from "./../../src/Metadata/RelationshipClass"; -import { ECClassModifier } from "./../../src/ECObjects"; -import { DelayedPromiseWithProps } from "./../../src/DelayedPromise"; -import { ECObjectsError } from "./../../src/Exception"; +import { MutableSchema, Schema } from "./../../src/Metadata/Schema"; +import { createSchemaJsonWithItems } from "./../TestUtils/DeserializationHelpers"; describe("EntityClass", () => { describe("get inherited properties", () => { let schema: Schema; beforeEach(() => { - schema = new Schema("TestSchema", 1, 0, 0); + schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); }); it("from mixins", async () => { @@ -121,7 +122,7 @@ describe("EntityClass", () => { modifier: "None", }); - const ecschema = await Schema.fromJson(schemaJson); + const ecschema = await Schema.fromJson(schemaJson, new SchemaContext()); const testClass = await ecschema.getItem("TestEntityClass"); assert.isDefined(testClass); @@ -146,7 +147,7 @@ describe("EntityClass", () => { }, }); - const ecschema = await Schema.fromJson(schemaJson); + const ecschema = await Schema.fromJson(schemaJson, new SchemaContext()); assert.isDefined(ecschema); const testClass = await ecschema.getItem("testClass"); @@ -183,7 +184,7 @@ describe("EntityClass", () => { appliesTo: "TestSchema.testClass", }, }); - const ecschema = await Schema.fromJson(schemaJson); + const ecschema = await Schema.fromJson(schemaJson, new SchemaContext()); assert.isDefined(ecschema); }); @@ -205,7 +206,7 @@ describe("EntityClass", () => { appliesTo: "TestSchema.testClass", }, }); - const ecschema = Schema.fromJsonSync(schemaJson); + const ecschema = Schema.fromJsonSync(schemaJson, new SchemaContext()); assert.isDefined(ecschema); }); @@ -220,7 +221,7 @@ describe("EntityClass", () => { }, }); - const ecSchema = await Schema.fromJson(schemaJson); + const ecSchema = await Schema.fromJson(schemaJson, new SchemaContext()); assert.isDefined(ecSchema); const testEntity = await ecSchema.getItem("testClass"); @@ -246,7 +247,7 @@ describe("EntityClass", () => { }, }); - const ecSchema = Schema.fromJsonSync(schemaJson); + const ecSchema = Schema.fromJsonSync(schemaJson, new SchemaContext()); assert.isDefined(ecSchema); const testEntity = ecSchema.getItemSync("testClass"); @@ -274,7 +275,7 @@ describe("EntityClass", () => { ], }); - const schema = await Schema.fromJson(schemaJson); + const schema = await Schema.fromJson(schemaJson, new SchemaContext()); assert.isDefined(schema); const entityClass = await schema.getItem("TestEntityClass"); @@ -302,7 +303,7 @@ describe("EntityClass", () => { ], }); - const schema = Schema.fromJsonSync(schemaJson); + const schema = Schema.fromJsonSync(schemaJson, new SchemaContext()); assert.isDefined(schema); const entityClass = schema.getItemSync("TestEntityClass"); @@ -320,67 +321,67 @@ describe("EntityClass", () => { it("should throw for invalid baseClass", async () => { const json = createSchemaJson({ baseClass: 0 }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The ECClass TestSchema.TestEntityClass has an invalid 'baseClass' attribute. It should be of type 'string'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The ECClass TestSchema.TestEntityClass has an invalid 'baseClass' attribute. It should be of type 'string'.`); }); it("should throw for invalid mixins", async () => { let json: any = createSchemaJson({ mixins: 0, schema: "TestSchema" }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The ECEntityClass TestSchema.TestEntityClass has an invalid 'mixins' attribute. It should be of type 'string[]'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The ECEntityClass TestSchema.TestEntityClass has an invalid 'mixins' attribute. It should be of type 'string[]'.`); json = createSchemaJson({ mixins: [0], schema: "TestSchema" }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The ECEntityClass TestSchema.TestEntityClass has an invalid 'mixins' attribute. It should be of type 'string[]'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The ECEntityClass TestSchema.TestEntityClass has an invalid 'mixins' attribute. It should be of type 'string[]'.`); }); it("should throw for invalid properties", async () => { let json: any = createSchemaJson({ properties: 0 }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The ECClass TestSchema.TestEntityClass has an invalid 'properties' attribute. It should be of type 'object[]'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The ECClass TestSchema.TestEntityClass has an invalid 'properties' attribute. It should be of type 'object[]'.`); json = createSchemaJson({ properties: [0], }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `An ECProperty in TestSchema.TestEntityClass is an invalid JSON object.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `An ECProperty in TestSchema.TestEntityClass is an invalid JSON object.`); }); it("should throw for property with missing name", async () => { const json = createSchemaJson({ properties: [{ type: "PrimitiveProperty" }], }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `An ECProperty in TestSchema.TestEntityClass is missing the required 'name' attribute.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `An ECProperty in TestSchema.TestEntityClass is missing the required 'name' attribute.`); }); it("should throw for property with invalid name", async () => { const json = createSchemaJson({ properties: [{ type: "PrimitiveProperty", name: 0 }], }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `An ECProperty in TestSchema.TestEntityClass has an invalid 'name' attribute. It should be of type 'string'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `An ECProperty in TestSchema.TestEntityClass has an invalid 'name' attribute. It should be of type 'string'.`); }); it("should throw for property with missing type", async () => { const json = createSchemaJson({ properties: [{ name: "badProp" }], }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The ECProperty TestSchema.TestEntityClass.badProp does not have the required 'type' attribute.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The ECProperty TestSchema.TestEntityClass.badProp does not have the required 'type' attribute.`); }); it("should throw for property with invalid type", async () => { const json = createSchemaJson({ properties: [{ name: "badProp", type: 0 }], }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The ECProperty TestSchema.TestEntityClass.badProp has an invalid 'type' attribute. It should be of type 'string'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The ECProperty TestSchema.TestEntityClass.badProp has an invalid 'type' attribute. It should be of type 'string'.`); }); it("should throw for property with missing typeName", async () => { const json = createSchemaJson({ properties: [{ name: "badProp", type: "PrimitiveProperty" }], }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The ECProperty TestSchema.TestEntityClass.badProp is missing the required 'typeName' attribute.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The ECProperty TestSchema.TestEntityClass.badProp is missing the required 'typeName' attribute.`); }); it("should throw for property with invalid typeName", async () => { const json = createSchemaJson({ properties: [{ name: "badProp", type: "PrimitiveProperty", typeName: 0 }], }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The ECProperty TestSchema.TestEntityClass.badProp has an invalid 'typeName' attribute. It should be of type 'string'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The ECProperty TestSchema.TestEntityClass.badProp has an invalid 'typeName' attribute. It should be of type 'string'.`); }); it("should throw for property with invalid category", async () => { @@ -394,7 +395,7 @@ describe("EntityClass", () => { }, ], }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The ECProperty TestSchema.TestEntityClass.testProp has an invalid 'category' attribute. It should be of type 'string'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The ECProperty TestSchema.TestEntityClass.testProp has an invalid 'category' attribute. It should be of type 'string'.`); }); it("should throw for property with invalid kindOfQuantity", async () => { @@ -408,7 +409,7 @@ describe("EntityClass", () => { }, ], }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The ECProperty TestSchema.TestEntityClass.testProp has an invalid 'kindOfQuantity' attribute. It should be of type 'string'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The ECProperty TestSchema.TestEntityClass.testProp has an invalid 'kindOfQuantity' attribute. It should be of type 'string'.`); }); it("should throw for navigation property with missing relationshipName", async () => { @@ -421,7 +422,7 @@ describe("EntityClass", () => { }, ], }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The Navigation Property TestSchema.TestEntityClass.testNavProp is missing the required 'relationshipName' property.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The Navigation Property TestSchema.TestEntityClass.testNavProp is missing the required 'relationshipName' property.`); }); it("should throw for navigation property with invalid relationshipName", async () => { @@ -435,7 +436,7 @@ describe("EntityClass", () => { }, ], }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The Navigation Property TestSchema.TestEntityClass.testNavProp has an invalid 'relationshipName' property. It should be of type 'string'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The Navigation Property TestSchema.TestEntityClass.testNavProp has an invalid 'relationshipName' property. It should be of type 'string'.`); }); it("should throw for navigation property with nonexistent relationship", async () => { @@ -449,7 +450,7 @@ describe("EntityClass", () => { }, ], }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `Unable to locate SchemaItem BadSchema.ThisDoesNotExist.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `Unable to locate SchemaItem BadSchema.ThisDoesNotExist.`); }); it("should throw for navigation property with missing direction", async () => { @@ -462,7 +463,7 @@ describe("EntityClass", () => { }, ], }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The Navigation Property TestSchema.TestEntityClass.testNavProp is missing the required 'direction' property.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The Navigation Property TestSchema.TestEntityClass.testNavProp is missing the required 'direction' property.`); }); it("should throw for navigation property with invalid direction", async () => { @@ -476,7 +477,7 @@ describe("EntityClass", () => { }, ], }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The Navigation Property TestSchema.TestEntityClass.testNavProp has an invalid 'direction' property. It should be of type 'string'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The Navigation Property TestSchema.TestEntityClass.testNavProp has an invalid 'direction' property. It should be of type 'string'.`); }); }); @@ -488,7 +489,7 @@ describe("EntityClass", () => { }; beforeEach(() => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); testClass = new EntityClass(schema, "TestEntity"); }); @@ -505,10 +506,10 @@ describe("EntityClass", () => { }); }); describe("toJson", () => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); const testEntityClass = new EntityClass(schema, "testClass"); const schemaJsonOne = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", version: "1.2.3", name: "testClass", schemaItemType: "EntityClass", @@ -517,18 +518,18 @@ describe("EntityClass", () => { it("async - Simple serialization", async () => { await testEntityClass.deserialize(schemaJsonOne); const serialized = testEntityClass.toJson(true, true); - assert(serialized.baseClass, "TestSchema.testBaseClass"); - assert(serialized.modifier, "None"); - assert(serialized.schemaVersion, "1.0.0"); - assert(serialized.name, "testClass"); + assert.strictEqual(serialized.baseClass, "TestSchema.testBaseClass"); + assert.notProperty(serialized, "modifier"); + assert.strictEqual(serialized.schemaVersion, "01.00.00"); + assert.strictEqual(serialized.name, "testClass"); }); it("sync - Simple serialization", () => { testEntityClass.deserializeSync(schemaJsonOne); const serialized = testEntityClass.toJson(true, true); - assert(serialized.baseClass, "TestSchema.testBaseClass"); - assert(serialized.modifier, "None"); - assert(serialized.schemaVersion, "1.0.0"); - assert(serialized.name, "testClass"); + assert.strictEqual(serialized.baseClass, "TestSchema.testBaseClass"); + assert.notProperty(serialized, "modifier"); + assert.strictEqual(serialized.schemaVersion, "01.00.00"); + assert.strictEqual(serialized.name, "testClass"); }); it("should succeed with mixin", async () => { const schemaJson = createSchemaJsonWithItems({ @@ -542,7 +543,7 @@ describe("EntityClass", () => { }, }); - const ecschema = await Schema.fromJson(schemaJson); + const ecschema = await Schema.fromJson(schemaJson, new SchemaContext()); assert.isDefined(ecschema); const testClass = await ecschema.getItem("testClass"); @@ -552,7 +553,6 @@ describe("EntityClass", () => { const entityClassSerialization = entityClass!.toJson(false, true); const expectedResult = { schemaItemType: "EntityClass", - modifier: "None", mixins: ["TestSchema.testMixin"], }; expect(entityClassSerialization).to.deep.equal(expectedResult); diff --git a/core/ecschema-metadata/test/Metadata/Enumeration.test.ts b/core/ecschema-metadata/test/Metadata/Enumeration.test.ts index 5ffe533..d537396 100644 --- a/core/ecschema-metadata/test/Metadata/Enumeration.test.ts +++ b/core/ecschema-metadata/test/Metadata/Enumeration.test.ts @@ -3,43 +3,20 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import * as sinon from "sinon"; import { assert, expect } from "chai"; - import { Schema } from "../../src/Metadata/Schema"; import { Enumeration, MutableEnumeration } from "./../../src/Metadata/Enumeration"; import { ECObjectsError } from "./../../src/Exception"; import { PrimitiveType } from "./../../src/ECObjects"; +import { SchemaContext } from "../../src/Context"; describe("Enumeration", () => { - describe("accept", () => { - let testEnum: Enumeration; - - beforeEach(() => { - const schema = new Schema("TestSchema", 1, 0, 0); - testEnum = new Enumeration(schema, "TestEnumeration", PrimitiveType.Integer); - }); - - it("should call visitEnumeration on a SchemaItemVisitor object", async () => { - expect(testEnum).to.exist; - const mockVisitor = { visitEnumeration: sinon.spy() }; - await testEnum.accept(mockVisitor); - expect(mockVisitor.visitEnumeration.calledOnce).to.be.true; - expect(mockVisitor.visitEnumeration.calledWithExactly(testEnum)).to.be.true; - }); - - it("should safely handle a SchemaItemVisitor without visitEnumeration defined", async () => { - expect(testEnum).to.exist; - await testEnum.accept({}); - }); - }); - describe("addEnumerator tests", () => { let testEnum: Enumeration; let testStringEnum: Enumeration; beforeEach(() => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); testEnum = new Enumeration(schema, "TestEnumeration", PrimitiveType.Integer); testStringEnum = new Enumeration(schema, "TestEnumeration", PrimitiveType.String); }); @@ -48,14 +25,14 @@ describe("Enumeration", () => { (testStringEnum as MutableEnumeration).addEnumerator(testStringEnum.createEnumerator("Enum2", "Val2")); (testStringEnum as MutableEnumeration).addEnumerator(testStringEnum.createEnumerator("Enum3", "Val3")); (testStringEnum as MutableEnumeration).addEnumerator(testStringEnum.createEnumerator("Enum4", "Val4")); - assert(testStringEnum.enumerators.length === 4); + assert.strictEqual(testStringEnum.enumerators.length, 4); }); it("Basic Integer Enumeration Test", async () => { (testEnum as MutableEnumeration).addEnumerator(testEnum.createEnumerator("Enum1", 1)); (testEnum as MutableEnumeration).addEnumerator(testEnum.createEnumerator("Enum2", 2)); (testEnum as MutableEnumeration).addEnumerator(testEnum.createEnumerator("Enum3", 3)); (testEnum as MutableEnumeration).addEnumerator(testEnum.createEnumerator("Enum4", 4)); - assert(testEnum.enumerators.length === 4); + assert.strictEqual(testEnum.enumerators.length, 4); }); it("Add duplicate enumerator", async () => { const newEnum = testStringEnum.createEnumerator("Enum1", "Val1"); @@ -73,7 +50,7 @@ describe("Enumeration", () => { describe("deserialization", () => { it("minimum values", async () => { const testSchema = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", items: { @@ -93,7 +70,7 @@ describe("Enumeration", () => { }, }; - const ecSchema = await Schema.fromJson(testSchema); + const ecSchema = await Schema.fromJson(testSchema, new SchemaContext()); const testEnum = await ecSchema.getItem("testEnum"); assert.isDefined(testEnum); @@ -107,7 +84,7 @@ describe("Enumeration", () => { it("with enumerators", async () => { const testSchema = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", items: { @@ -125,7 +102,7 @@ describe("Enumeration", () => { }, }; - const ecSchema = await Schema.fromJson(testSchema); + const ecSchema = await Schema.fromJson(testSchema, new SchemaContext()); const testEnum = await ecSchema.getItem("testEnum"); assert.isDefined(testEnum); }); @@ -138,7 +115,7 @@ describe("Enumeration", () => { const baseJson = { schemaItemType: "Enumeration" }; beforeEach(() => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); testEnum = new Enumeration(schema, "TestEnumeration", PrimitiveType.Integer); testStringEnum = new Enumeration(schema, "TestEnumeration", PrimitiveType.String); testEnumSansPrimType = new Enumeration(schema, "TestEnumeration"); @@ -348,7 +325,7 @@ describe("Enumeration", () => { describe("toJson", () => { let testEnumSansPrimType: Enumeration; const baseJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/31/draft-01/schemaitem", + $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/schemaitem", schemaItemType: "Enumeration", name: "TestEnumeration", schema: "TestSchema", @@ -356,7 +333,7 @@ describe("Enumeration", () => { }; beforeEach(() => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); testEnumSansPrimType = new Enumeration(schema, "TestEnumeration"); }); describe("Basic serialization tests", () => { diff --git a/core/ecschema-metadata/test/Metadata/Format.test.ts b/core/ecschema-metadata/test/Metadata/Format.test.ts index 7d234dd..5b67bc8 100644 --- a/core/ecschema-metadata/test/Metadata/Format.test.ts +++ b/core/ecschema-metadata/test/Metadata/Format.test.ts @@ -4,10 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { assert, expect } from "chai"; -import * as sinon from "sinon"; import { TestSchemaLocater } from "../TestUtils/FormatTestHelper"; import { createSchemaJsonWithItems } from "../TestUtils/DeserializationHelpers"; - import { Schema, MutableSchema } from "./../../src/Metadata/Schema"; import { Format } from "./../../src/Metadata/Format"; import { ShowSignOption, FormatType, FormatTraits, DecimalPrecision } from "./../../src/utils/FormatEnums"; @@ -37,31 +35,12 @@ function createSchemaJson(koq: any) { describe("Format", () => { let schema: Schema; let testFormat: Format; - describe("accept", () => { - beforeEach(() => { - schema = new Schema("TestSchema", 1, 0, 0); - testFormat = new Format(schema, "TestFormat"); - }); - - it("should call visitFormat on a SchemaItemVisitor object", async () => { - expect(testFormat).to.exist; - const mockVisitor = { visitFormat: sinon.spy() }; - await testFormat.accept(mockVisitor); - expect(mockVisitor.visitFormat.calledOnce).to.be.true; - expect(mockVisitor.visitFormat.calledWithExactly(testFormat)).to.be.true; - }); - - it("should safely handle a SchemaItemVisitor without visitFormat defined", async () => { - expect(testFormat).to.exist; - await testFormat.accept({}); - }); - }); describe("type checking json", () => { let jsonParser: JsonParser; // This is an easy way to test the logic directly in the parser without having to go through deserialization every time. const rawSchema = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", items: { @@ -214,7 +193,7 @@ describe("Format", () => { describe("deserialize formatted ECJSON", () => { beforeEach(() => { - schema = new Schema("TestSchema", 1, 0, 0); + schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); testFormat = (schema as MutableSchema).createFormatSync("TestFormat"); }); @@ -320,28 +299,28 @@ describe("Format", () => { }; it("sync - precision value is valid with different format types", () => { testFormat.deserializeSync(validPrecisionDecimal); - assert(testFormat.precision === 3); + assert.strictEqual(testFormat.precision, 3); testFormat = (schema as MutableSchema).createFormatSync("TestFormatA"); testFormat.deserializeSync(validPrecisionScientific); - assert(testFormat.precision === 0); + assert.strictEqual(testFormat.precision, 0); testFormat = (schema as MutableSchema).createFormatSync("TestFormatB"); testFormat.deserializeSync(validPrecisionStation); - assert(testFormat.precision === 12); + assert.strictEqual(testFormat.precision, 12); }); it("async - precision value is valid with different format types", async () => { await testFormat.deserialize(validPrecisionDecimal); - assert(testFormat.precision === 3); + assert.strictEqual(testFormat.precision, 3); testFormat = (schema as MutableSchema).createFormatSync("TestFormatA"); await testFormat.deserialize(validPrecisionScientific); - assert(testFormat.precision === 0); + assert.strictEqual(testFormat.precision, 0); testFormat = (schema as MutableSchema).createFormatSync("TestFormatB"); await testFormat.deserialize(validPrecisionStation); - assert(testFormat.precision === 12); + assert.strictEqual(testFormat.precision, 12); }); const invalidMinWidth: Mutable = { diff --git a/core/ecschema-metadata/test/Metadata/InvertedUnit.test.ts b/core/ecschema-metadata/test/Metadata/InvertedUnit.test.ts index 3bea7b9..c6e0498 100644 --- a/core/ecschema-metadata/test/Metadata/InvertedUnit.test.ts +++ b/core/ecschema-metadata/test/Metadata/InvertedUnit.test.ts @@ -4,55 +4,35 @@ *--------------------------------------------------------------------------------------------*/ import { assert, expect } from "chai"; -import * as sinon from "sinon"; - import { Schema } from "../../src/Metadata/Schema"; import { InvertedUnit } from "../../src/Metadata/InvertedUnit"; import { ECObjectsError } from "../../src/Exception"; import { UnitSystem } from "../../src/Metadata/UnitSystem"; import { Unit } from "../../src/Metadata/Unit"; import { schemaItemTypeToString, SchemaItemType } from "../../src/ECObjects"; +import { SchemaContext } from "../../src/Context"; describe("Inverted Unit tests", () => { let testUnit: InvertedUnit; - describe("accept", () => { - beforeEach(() => { - const schema = new Schema("TestSchema", 1, 0, 0); - testUnit = new InvertedUnit(schema, "TestInvertedUnit"); - }); - - it("should call visitInvertedUnit on a SchemaItemVisitor object", async () => { - expect(testUnit).to.exist; - const mockVisitor = { visitInvertedUnit: sinon.spy() }; - await testUnit.accept(mockVisitor); - expect(mockVisitor.visitInvertedUnit.calledOnce).to.be.true; - expect(mockVisitor.visitInvertedUnit.calledWithExactly(testUnit)).to.be.true; - }); - - it("should safely handle a SchemaItemVisitor without visitInvertedUnit defined", async () => { - expect(testUnit).to.exist; - await testUnit.accept({}); - }); - }); describe("SchemaItemType", () => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); testUnit = new InvertedUnit(schema, "Test"); it("should return correct item type and string", () => { - assert.equal(testUnit.schemaItemType, SchemaItemType.InvertedUnit); - assert.equal(schemaItemTypeToString(testUnit.schemaItemType), "InvertedUnit"); + assert.strictEqual(testUnit.schemaItemType, SchemaItemType.InvertedUnit); + assert.strictEqual(schemaItemTypeToString(testUnit.schemaItemType), "InvertedUnit"); }); }); describe("Async fromJson", () => { beforeEach(() => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); testUnit = new InvertedUnit(schema, "HORIZONTAL_PER_VERTICAL"); }); it("Basic test for label", async () => { const json = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", version: "1.0.0", name: "TestSchema", items: { @@ -79,18 +59,18 @@ describe("Inverted Unit tests", () => { }, }, }; - const ecSchema = await Schema.fromJson(json); + const ecSchema = await Schema.fromJson(json, new SchemaContext()); assert.isDefined(ecSchema); const testItem = await ecSchema.getItem("HORIZONTAL_PER_VERTICAL"); assert.isDefined(testItem); assert.isTrue(testItem instanceof InvertedUnit); const testInvertedUnit: InvertedUnit = testItem as InvertedUnit; assert.isDefined(testInvertedUnit); - assert(testInvertedUnit.label, "Horizontal/Vertical"); + assert.strictEqual(testInvertedUnit.label, "Horizontal/Vertical"); }); it("Label and description are optional", async () => { const json = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", version: "1.0.0", name: "TestSchema", items: { @@ -117,19 +97,19 @@ describe("Inverted Unit tests", () => { }, }, }; - const ecSchema = await Schema.fromJson(json); + const ecSchema = await Schema.fromJson(json, new SchemaContext()); assert.isDefined(ecSchema); const testItem = await ecSchema.getItem("HORIZONTAL_PER_VERTICAL"); assert.isDefined(testItem); assert.isTrue(testItem instanceof InvertedUnit); const testInvertedUnit: InvertedUnit = testItem as InvertedUnit; assert.isDefined(testInvertedUnit); - assert(testInvertedUnit.unitSystem, "TestSchema.INTERNATIONAL"); - assert(testInvertedUnit.invertsUnit, "TestSchema.VERTICAL_PER_HORIZONTAL"); + assert.strictEqual(testInvertedUnit.unitSystem!.fullName, "TestSchema.INTERNATIONAL"); + assert.strictEqual(testInvertedUnit.invertsUnit!.fullName, "TestSchema.VERTICAL_PER_HORIZONTAL"); }); it("unitSystem is required", async () => { const json = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", version: "1.0.0", name: "TestSchema", items: { @@ -155,11 +135,11 @@ describe("Inverted Unit tests", () => { }, }, }; - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The InvertedUnit TestSchema.HORIZONTAL_PER_VERTICAL does not have the required 'unitSystem' attribute.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The InvertedUnit TestSchema.HORIZONTAL_PER_VERTICAL does not have the required 'unitSystem' attribute.`); }); it("Resolve all dependencies for inverts unit and unit system", async () => { const json = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", version: "1.0.0", name: "TestSchema", items: { @@ -185,7 +165,7 @@ describe("Inverted Unit tests", () => { }, }, }; - const ecSchema = await Schema.fromJson(json); + const ecSchema = await Schema.fromJson(json, new SchemaContext()); assert.isDefined(ecSchema); const testItem = await ecSchema.getItem("HORIZONTAL_PER_VERTICAL"); assert.isDefined(testItem); @@ -200,24 +180,24 @@ describe("Inverted Unit tests", () => { assert.isDefined(testUnitSys); assert.isTrue(testUnitSys instanceof UnitSystem); const testUnitSysItem: UnitSystem = testUnitSys as UnitSystem; - assert(unitSysFromInvertedUnit!.description, testUnitSysItem!.description); + assert.strictEqual(unitSysFromInvertedUnit!.description, testUnitSysItem!.description); const testInvertsUnit = await ecSchema.getItem("VERTICAL_PER_HORIZONTAL"); assert.isDefined(testInvertsUnit); assert.isTrue(testInvertsUnit instanceof Unit); const testInvertsUnitItem: Unit = testInvertsUnit as Unit; - assert(invertsUnitFromInvertedUnit!.definition, testInvertsUnitItem!.definition); + assert.strictEqual(invertsUnitFromInvertedUnit!.definition, testInvertsUnitItem!.definition); }); }); describe("Sync fromJson", () => { beforeEach(() => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); testUnit = new InvertedUnit(schema, "HORIZONTAL_PER_VERTICAL"); }); it("Basic test for label", () => { const json = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", version: "1.0.0", name: "TestSchema", items: { @@ -244,18 +224,18 @@ describe("Inverted Unit tests", () => { }, }, }; - const ecSchema = Schema.fromJsonSync(json); + const ecSchema = Schema.fromJsonSync(json, new SchemaContext()); assert.isDefined(ecSchema); const testItem = ecSchema.getItemSync("HORIZONTAL_PER_VERTICAL"); assert.isDefined(testItem); assert.isTrue(testItem instanceof InvertedUnit); const testInvertedUnit: InvertedUnit = testItem as InvertedUnit; assert.isDefined(testInvertedUnit); - assert(testInvertedUnit.label, "Horizontal/Vertical"); + assert.strictEqual(testInvertedUnit.label, "Horizontal/Vertical"); }); it("Label and description are optional", () => { const json = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", version: "1.0.0", name: "TestSchema", items: { @@ -282,19 +262,19 @@ describe("Inverted Unit tests", () => { }, }, }; - const ecSchema = Schema.fromJsonSync(json); + const ecSchema = Schema.fromJsonSync(json, new SchemaContext()); assert.isDefined(ecSchema); const testItem = ecSchema.getItemSync("HORIZONTAL_PER_VERTICAL"); assert.isDefined(testItem); assert.isTrue(testItem instanceof InvertedUnit); const testInvertedUnit: InvertedUnit = testItem as InvertedUnit; assert.isDefined(testInvertedUnit); - assert(testInvertedUnit.unitSystem, "TestSchema.INTERNATIONAL"); - assert(testInvertedUnit.invertsUnit, "TestSchema.VERTICAL_PER_HORIZONTAL"); + assert.strictEqual(testInvertedUnit.unitSystem!.fullName, "TestSchema.INTERNATIONAL"); + assert.strictEqual(testInvertedUnit.invertsUnit!.fullName, "TestSchema.VERTICAL_PER_HORIZONTAL"); }); it("Resolve all dependencies for inverts unit and unit system", async () => { const json = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", version: "1.0.0", name: "TestSchema", items: { @@ -320,7 +300,7 @@ describe("Inverted Unit tests", () => { }, }, }; - const ecSchema = Schema.fromJsonSync(json); + const ecSchema = Schema.fromJsonSync(json, new SchemaContext()); assert.isDefined(ecSchema); const testItem = ecSchema.getItemSync("HORIZONTAL_PER_VERTICAL"); assert.isDefined(testItem); @@ -335,23 +315,23 @@ describe("Inverted Unit tests", () => { assert.isDefined(testUnitSys); assert.isTrue(testUnitSys instanceof UnitSystem); const testUnitSysItem: UnitSystem = testUnitSys as UnitSystem; - assert(unitSysFromInvertedUnit!.description, testUnitSysItem!.description); + assert.strictEqual(unitSysFromInvertedUnit!.description, testUnitSysItem!.description); const testInvertsUnit = await ecSchema.getItem("VERTICAL_PER_HORIZONTAL"); assert.isDefined(testInvertsUnit); assert.isTrue(testInvertsUnit instanceof Unit); const testInvertsUnitItem: Unit = testInvertsUnit as Unit; - assert(invertsUnitFromInvertedUnit!.definition, testInvertsUnitItem!.definition); + assert.strictEqual(invertsUnitFromInvertedUnit!.definition, testInvertsUnitItem!.definition); }); }); describe("toJson", () => { beforeEach(() => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); testUnit = new InvertedUnit(schema, "HORIZONTAL_PER_VERTICAL"); }); const jsonOne = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", version: "1.0.0", name: "TestSchema", items: { @@ -378,7 +358,7 @@ describe("Inverted Unit tests", () => { }, }; it("async- Serialization of fully defined inverted unit", async () => { - const ecSchema = await Schema.fromJson(jsonOne); + const ecSchema = await Schema.fromJson(jsonOne, new SchemaContext()); assert.isDefined(ecSchema); const testItem = await ecSchema.getItem("HORIZONTAL_PER_VERTICAL"); assert.isTrue(testItem instanceof InvertedUnit); @@ -389,7 +369,7 @@ describe("Inverted Unit tests", () => { expect(invertedUnitSerialization.invertsUnit).to.eql("VERTICAL_PER_HORIZONTAL"); }); it("sync- Serialization of fully defined inverted unit", () => { - const ecSchema = Schema.fromJsonSync(jsonOne); + const ecSchema = Schema.fromJsonSync(jsonOne, new SchemaContext()); assert.isDefined(ecSchema); const testItem = ecSchema.getItemSync("HORIZONTAL_PER_VERTICAL"); assert.isTrue(testItem instanceof InvertedUnit); diff --git a/core/ecschema-metadata/test/Metadata/KindOfQuantity.test.ts b/core/ecschema-metadata/test/Metadata/KindOfQuantity.test.ts index b802b30..0352830 100644 --- a/core/ecschema-metadata/test/Metadata/KindOfQuantity.test.ts +++ b/core/ecschema-metadata/test/Metadata/KindOfQuantity.test.ts @@ -4,13 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { assert, expect } from "chai"; -import * as sinon from "sinon"; - import { ECObjectsError } from "../../src/Exception"; import { KindOfQuantity } from "../../src/Metadata/KindOfQuantity"; import { OverrideFormat } from "../../src/Metadata/OverrideFormat"; import { Schema } from "../../src/Metadata/Schema"; - import { Format } from "../../src/Metadata/Format"; import { SchemaContext } from "../../src/Context"; import { DecimalPrecision } from "../../src/utils/FormatEnums"; @@ -41,34 +38,12 @@ describe("KindOfQuantity", () => { description: "A really long description...", }; - describe("accept", () => { - let testKoq: KindOfQuantity; - let schema: Schema; - beforeEach(() => { - schema = new Schema("TestSchema", 1, 2, 3); - testKoq = new KindOfQuantity(schema, "TestKindOfQuantity"); - }); - - it("should call visitKindOfQuantity on a SchemaItemVisitor object", async () => { - expect(testKoq).to.exist; - const mockVisitor = { visitKindOfQuantity: sinon.spy() }; - await testKoq.accept(mockVisitor); - expect(mockVisitor.visitKindOfQuantity.calledOnce).to.be.true; - expect(mockVisitor.visitKindOfQuantity.calledWithExactly(testKoq)).to.be.true; - }); - - it("should safely handle a SchemaItemVisitor without visitKindOfQuantity defined", async () => { - expect(testKoq).to.exist; - await testKoq.accept({}); - }); - }); - describe("deserialization", () => { let context: SchemaContext; let schema: Schema; beforeEach(() => { - schema = new Schema("TestSchema", 1, 2, 3); context = new SchemaContext(); + schema = new Schema(new SchemaContext(), "TestSchema", 1, 2, 3); context.addLocater(new TestSchemaLocater()); }); it("should successfully deserialize valid JSON", async () => { @@ -157,8 +132,8 @@ describe("KindOfQuantity", () => { let schema: Schema; let context: SchemaContext; beforeEach(() => { - schema = new Schema("TestSchema", 1, 2, 3); context = new SchemaContext(); + schema = new Schema(context, "TestSchema", 1, 2, 3); context.addLocater(new TestSchemaLocater()); }); @@ -233,7 +208,7 @@ describe("KindOfQuantity", () => { expect(defaultFormat!.units!.length).to.eql(1); const unitOverride = defaultFormat!.units![0]; const unitFromSchema = await schema.lookupItem(unitOverride[0].key.schemaName + "." + unitOverride[0].name); - assert.equal(await unitOverride[0], unitFromSchema); + assert.strictEqual(await unitOverride[0], unitFromSchema); assert.isUndefined(unitOverride[1]); }); it("sync - single unit override", () => { @@ -251,7 +226,7 @@ describe("KindOfQuantity", () => { expect(defaultFormat!.units!.length).to.eql(1); const unitOverride = defaultFormat!.units![0]; const unitFromSchema = schema.lookupItemSync(unitOverride[0].key.schemaName + "." + unitOverride[0].name); - assert.equal(unitOverride[0], unitFromSchema); + assert.strictEqual(unitOverride[0], unitFromSchema); assert.isUndefined(unitOverride[1]); }); @@ -277,7 +252,7 @@ describe("KindOfQuantity", () => { expect(defaultFormat!.units!.length).to.eql(1); const unitOverride = defaultFormat!.units![0]; const unitFromSchema = await schema.lookupItem(unitOverride[0].key.schemaName + "." + unitOverride[0].name); - assert.equal(await unitOverride[0], unitFromSchema); + assert.strictEqual(await unitOverride[0], unitFromSchema); expect(unitOverride[1]).to.be.eql(" in"); }); it("sync - single unit label override", () => { @@ -293,7 +268,7 @@ describe("KindOfQuantity", () => { expect(defaultFormat!.units!.length).to.eql(1); const unitOverride = defaultFormat!.units![0]; const unitFromSchema = schema.lookupItemSync(unitOverride[0].key.schemaName + "." + unitOverride[0].name); - assert.equal(unitOverride[0], unitFromSchema); + assert.strictEqual(unitOverride[0], unitFromSchema); expect(unitOverride[1]).to.be.eql(" in"); }); @@ -334,12 +309,12 @@ describe("KindOfQuantity", () => { let schema: Schema; let context: SchemaContext; beforeEach(() => { - schema = new Schema("TestSchema", 1, 2, 3); context = new SchemaContext(); + schema = new Schema(context, "TestSchema", 1, 2, 3); context.addLocater(new TestSchemaLocater()); }); - it("should successfully deserialize valid JSON", async () => { + it("should successfully round-trip valid JSON", async () => { const koqJson = { ...baseJson, relativeError: 1.234, @@ -351,18 +326,26 @@ describe("KindOfQuantity", () => { }; schema = await Schema.fromJson(createSchemaJson(koqJson), context); const testKoq = await schema.getItem(koqJson.name); + const expectedJson = { + $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/schemaitem", + schema: "TestSchema", + schemaVersion: "01.02.03", + ...koqJson, + }; + expect(testKoq).to.exist; + expect(testKoq!.toJson(true, true)).to.deep.equal(expectedJson); + }); - const koqSerialization = testKoq!.toJson(true, true); - assert.isDefined(koqSerialization); - expect(koqSerialization.name).to.eql("TestKindOfQuantity"); - expect(koqSerialization.label).to.eql("SomeDisplayLabel"); - expect(koqSerialization.description).to.eql("A really long description..."); - expect(koqSerialization.relativeError).to.eql(1.234); - expect(koqSerialization.presentationUnits).to.exist; - expect(koqSerialization.presentationUnits.length).to.eql(2); - expect(koqSerialization.presentationUnits[0]).to.eql("IN"); - expect(koqSerialization.presentationUnits[1]).to.eql("DefaultReal"); - expect(koqSerialization.persistenceUnit).to.eql("Formats.DefaultReal"); + it("should omit presentationUnits if empty", async () => { + const koqJson = { + ...baseJson, + relativeError: 1.234, + persistenceUnit: "Formats.DefaultReal", + presentationUnits: [], + }; + schema = await Schema.fromJson(createSchemaJson(koqJson), context); + const testKoq = await schema.getItem(koqJson.name); + expect(testKoq!.toJson(true, true)).to.not.have.property("presentationUnits"); }); }); }); diff --git a/core/ecschema-metadata/test/Metadata/Mixin.test.ts b/core/ecschema-metadata/test/Metadata/Mixin.test.ts index d6aefd7..1d24abb 100644 --- a/core/ecschema-metadata/test/Metadata/Mixin.test.ts +++ b/core/ecschema-metadata/test/Metadata/Mixin.test.ts @@ -14,6 +14,7 @@ import { NavigationProperty } from "../../src/Metadata/Property"; import { StrengthDirection } from "../../src/ECObjects"; import sinon = require("sinon"); import { DelayedPromiseWithProps } from "../../src/DelayedPromise"; +import { SchemaContext } from "../../src/Context"; describe("Mixin", () => { @@ -64,7 +65,7 @@ describe("Mixin", () => { }, }); - const schema = await Schema.fromJson(testSchema); + const schema = await Schema.fromJson(testSchema, new SchemaContext()); assert.isDefined(schema); const entity = await schema.getItem("TestEntity"); @@ -92,7 +93,7 @@ describe("Mixin", () => { ], }); - const schema = await Schema.fromJson(json); + const schema = await Schema.fromJson(json, new SchemaContext()); expect(schema).to.exist; const mixin = await schema.getItem("TestMixin"); @@ -117,7 +118,7 @@ describe("Mixin", () => { ], }); - const schema = Schema.fromJsonSync(json); + const schema = Schema.fromJsonSync(json, new SchemaContext()); expect(schema).to.exist; const mixin = schema.getItemSync("TestMixin"); @@ -133,7 +134,35 @@ describe("Mixin", () => { const json = createSchemaJson({ appliesTo: 0, }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The Mixin TestSchema.TestMixin has an invalid 'appliesTo' attribute. It should be of type 'string'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The Mixin TestSchema.TestMixin has an invalid 'appliesTo' attribute. It should be of type 'string'.`); + }); + + it("applicableTo, wrong entity, fails", async () => { + const json = createSchemaJson({ + appliesTo: "TestSchema.TestEntity", + properties: [ + { + type: "NavigationProperty", + name: "testNavProp", + relationshipName: "TestSchema.NavPropRelationship", + direction: "forward", + }, + ], + }); + + const schema = Schema.fromJsonSync(json, new SchemaContext()); + expect(schema).to.exist; + + const mixin = schema.getItemSync("TestMixin"); + expect(mixin).to.exist; + + const validEntity = schema.getItemSync("TestEntity"); + expect(validEntity).to.exist; + + const invalidEntity = new EntityClass(schema, "TestEntityB"); + + expect(await mixin!.applicableTo(validEntity!)).to.be.true; + expect(await mixin!.applicableTo(invalidEntity)).to.be.false; }); }); @@ -143,7 +172,7 @@ describe("Mixin", () => { const baseJson = { schemaItemType: "Mixin" }; beforeEach(async () => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); testEntity = await (schema as MutableSchema).createEntityClass("TestEntity"); testMixin = new Mixin(schema, "TestMixin"); }); @@ -171,7 +200,7 @@ describe("Mixin", () => { const baseJson = { schemaItemType: "Mixin" }; beforeEach(() => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); testEntity = (schema as MutableSchema).createEntityClassSync("TestEntity"); testMixin = new Mixin(schema, "TestMixin"); }); @@ -211,7 +240,7 @@ describe("Mixin", () => { ], }); - const schema = Schema.fromJsonSync(json); + const schema = Schema.fromJsonSync(json, new SchemaContext()); expect(schema).to.exist; const mixin = schema.getItemSync("TestMixin"); @@ -227,7 +256,7 @@ describe("Mixin", () => { }); it("applicableTo, appliesTo undefined, should throw", async () => { - const schema = new Schema("TestSchema", 1, 1, 1); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 1, 1); const mixin = new Mixin(schema, "TestMixin"); const entity = new EntityClass(schema, "TestEntity"); @@ -235,14 +264,40 @@ describe("Mixin", () => { }); it("applicableTo, appliesTo resolves undefined, should throw", async () => { - const schema = new Schema("TestSchema", 1, 1, 1); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 1, 1); const entity = new EntityClass(schema, "TestEntity"); const mixin = new Mixin(schema, "TestMixin"); - let undefinedEntity: EntityClass; - const promise = new DelayedPromiseWithProps(entity.key, async () => undefinedEntity); + const promise = new DelayedPromiseWithProps(entity.key, async () => undefined); sinon.stub(Mixin.prototype, "appliesTo").get(() => promise); await expect(mixin!.applicableTo(entity)).to.be.rejectedWith(`Unable to locate the appliesTo ${promise.fullName}`); }); }); + + describe("toJson", () => { + it("should always omit modifier", async () => { + const testSchema = createSchemaJsonWithItems({ + TestMixin: { + schemaItemType: "Mixin", + appliesTo: "TestSchema.TestEntity", + }, + TestEntity: { + schemaItemType: "EntityClass", + }, + }); + + const schemaA = await Schema.fromJson(testSchema, new SchemaContext()); + assert.isDefined(schemaA); + const mixinA = await schemaA.getItem("TestMixin"); + expect(mixinA).to.exist; + expect(mixinA!.toJson(true, true)).to.not.have.property("modifier"); + + testSchema.items.TestMixin.modifier = "Abstract"; + const schemaB = await Schema.fromJson(testSchema, new SchemaContext()); + assert.isDefined(schemaB); + const mixinB = await schemaB.getItem("TestMixin"); + expect(mixinB).to.exist; + expect(mixinB!.toJson(true, true)).to.not.have.property("modifier"); + }); + }); }); diff --git a/core/ecschema-metadata/test/Metadata/OverrideFormat.test.ts b/core/ecschema-metadata/test/Metadata/OverrideFormat.test.ts index 348e32a..63d9ba0 100644 --- a/core/ecschema-metadata/test/Metadata/OverrideFormat.test.ts +++ b/core/ecschema-metadata/test/Metadata/OverrideFormat.test.ts @@ -4,16 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import { assert, expect } from "chai"; -import { OverrideFormat } from "../../src/Metadata/OverrideFormat"; -import { Schema } from "../../src/Metadata/Schema"; -import { Format } from "../../src/Metadata/Format"; -import { Unit } from "../../src/Metadata/Unit"; -import { FractionalPrecision, ShowSignOption, FormatTraits } from "../../src/utils/FormatEnums"; - import { createSchemaJsonWithItems } from "../TestUtils/DeserializationHelpers"; -import { SchemaContext } from "../../src/Context"; import { TestSchemaLocater } from "../TestUtils/FormatTestHelper"; + +import { SchemaContext } from "../../src/Context"; +import { Format } from "../../src/Metadata/Format"; import { InvertedUnit } from "../../src/Metadata/InvertedUnit"; +import { OverrideFormat } from "../../src/Metadata/OverrideFormat"; +import { Schema } from "../../src/Metadata/Schema"; +import { Unit } from "../../src/Metadata/Unit"; +import { FormatTraits, FractionalPrecision, ShowSignOption } from "../../src/utils/FormatEnums"; function createSchemaJson(format: any) { return createSchemaJsonWithItems({ @@ -52,7 +52,7 @@ describe("OverrideFormat", () => { uomSeparator: "-", formatTraits: [ "TrailZeroes", - "PrependUnitLabel" + "PrependUnitLabel", ], composite: { includeZero: false, @@ -63,7 +63,7 @@ describe("OverrideFormat", () => { label: "yard(s)", }, ], - } + }, }); const schema = Schema.fromJsonSync(schemaJson, context); diff --git a/core/ecschema-metadata/test/Metadata/Phenomenon.test.ts b/core/ecschema-metadata/test/Metadata/Phenomenon.test.ts index 552fba0..a246d21 100644 --- a/core/ecschema-metadata/test/Metadata/Phenomenon.test.ts +++ b/core/ecschema-metadata/test/Metadata/Phenomenon.test.ts @@ -3,35 +3,16 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { assert, expect } from "chai"; +import { assert } from "chai"; import { Schema } from "../../src/Metadata/Schema"; import { Phenomenon } from "../../src/Metadata/Phenomenon"; -import * as sinon from "sinon"; +import { SchemaContext } from "../../src/Context"; describe("Phenomenon tests", () => { let testPhenomenon: Phenomenon; - describe("accept", () => { - beforeEach(() => { - const schema = new Schema("TestSchema", 1, 0, 0); - testPhenomenon = new Phenomenon(schema, "TestEnumeration"); - }); - - it("should call visitPhenomenon on a SchemaItemVisitor object", async () => { - expect(testPhenomenon).to.exist; - const mockVisitor = { visitPhenomenon: sinon.spy() }; - await testPhenomenon.accept(mockVisitor); - expect(mockVisitor.visitPhenomenon.calledOnce).to.be.true; - expect(mockVisitor.visitPhenomenon.calledWithExactly(testPhenomenon)).to.be.true; - }); - - it("should safely handle a SchemaItemVisitor without visitPhenomenon defined", async () => { - expect(testPhenomenon).to.exist; - await testPhenomenon.accept({}); - }); - }); describe("Async fromJson", () => { beforeEach(() => { - const schema = new Schema("ExampleSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "ExampleSchema", 1, 0, 0); testPhenomenon = new Phenomenon(schema, "AREA"); }); it("Basic test", async () => { @@ -43,13 +24,13 @@ describe("Phenomenon tests", () => { definition: "Units.LENGTH(2)", }; await testPhenomenon.deserialize(json); - assert(testPhenomenon.label, "Area"); - assert(testPhenomenon.definition, "Units.LENGTH(2)"); + assert.strictEqual(testPhenomenon.label, "Area"); + assert.strictEqual(testPhenomenon.definition, "Units.LENGTH(2)"); }); }); describe("Sync fromJson", () => { beforeEach(() => { - const schema = new Schema("ExampleSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "ExampleSchema", 1, 0, 0); testPhenomenon = new Phenomenon(schema, "AREA"); }); it("Basic test", () => { @@ -60,14 +41,14 @@ describe("Phenomenon tests", () => { label: "Area", definition: "Units.LENGTH(2)", }; - testPhenomenon.deserializeSync(json) - assert(testPhenomenon.label, "Area"); - assert(testPhenomenon.definition, "Units.LENGTH(2)"); + testPhenomenon.deserializeSync(json); + assert.strictEqual(testPhenomenon.label, "Area"); + assert.strictEqual(testPhenomenon.definition, "Units.LENGTH(2)"); }); }); describe("toJson", () => { beforeEach(() => { - const schema = new Schema("ExampleSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "ExampleSchema", 1, 0, 0); testPhenomenon = new Phenomenon(schema, "AREA"); }); it("async - Basic test", async () => { @@ -79,7 +60,7 @@ describe("Phenomenon tests", () => { }; await testPhenomenon.deserialize(json); const phenomSerialization = testPhenomenon.toJson(true, true); - assert(phenomSerialization.definition, "Units.LENGTH(2)"); + assert.strictEqual(phenomSerialization.definition, "Units.LENGTH(2)"); }); it("sync - Basic test", () => { const json = { @@ -90,7 +71,7 @@ describe("Phenomenon tests", () => { }; testPhenomenon.deserializeSync(json); const phenomSerialization = testPhenomenon.toJson(true, true); - assert(phenomSerialization.definition, "Units.LENGTH(2)"); + assert.strictEqual(phenomSerialization.definition, "Units.LENGTH(2)"); }); }); }); diff --git a/core/ecschema-metadata/test/Metadata/Property.test.ts b/core/ecschema-metadata/test/Metadata/Property.test.ts index 230c847..337a542 100644 --- a/core/ecschema-metadata/test/Metadata/Property.test.ts +++ b/core/ecschema-metadata/test/Metadata/Property.test.ts @@ -5,22 +5,20 @@ import { assert, expect } from "chai"; -import { Schema, MutableSchema } from "../../src/Metadata/Schema"; -import { EntityClass } from "../../src/Metadata/EntityClass"; +import { SchemaContext } from "../../src/Context"; +import { DelayedPromiseWithProps } from "../../src/DelayedPromise"; +import { PrimitiveType } from "../../src/ECObjects"; import { ECObjectsError } from "../../src/Exception"; -import { - Property, PrimitiveProperty, PrimitiveArrayProperty, EnumerationProperty, StructProperty, - StructArrayProperty, EnumerationArrayProperty, NavigationProperty, MutableProperty, -} from "../../src/Metadata/Property"; -import { PropertyType } from "../../src/PropertyTypes"; +import { ECClass, MutableClass, StructClass } from "../../src/Metadata/Class"; +import { CustomAttribute } from "../../src/Metadata/CustomAttribute"; +import { EntityClass } from "../../src/Metadata/EntityClass"; import { Enumeration } from "../../src/Metadata/Enumeration"; -import { ECClass, StructClass, MutableClass } from "../../src/Metadata/Class"; -import { PropertyCategory } from "../../src/Metadata/PropertyCategory"; import { KindOfQuantity } from "../../src/Metadata/KindOfQuantity"; +import { EnumerationArrayProperty, EnumerationProperty, MutableProperty, NavigationProperty, PrimitiveArrayProperty, PrimitiveProperty, Property, StructArrayProperty, StructProperty } from "../../src/Metadata/Property"; +import { PropertyCategory } from "../../src/Metadata/PropertyCategory"; import { RelationshipClass } from "../../src/Metadata/RelationshipClass"; -import { DelayedPromiseWithProps } from "../../src/DelayedPromise"; -import { PrimitiveType } from "../../src/ECObjects"; -import { CustomAttribute } from "../../src/Metadata/CustomAttribute"; +import { MutableSchema, Schema } from "../../src/Metadata/Schema"; +import { PropertyType } from "../../src/PropertyTypes"; describe("Property", () => { let testClass: EntityClass; @@ -41,7 +39,7 @@ describe("Property", () => { } beforeEach(async () => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); const mutable = schema as MutableSchema; testClass = await mutable.createEntityClass("TestClass"); testCategory = await mutable.createPropertyCategory("TestCategory"); @@ -151,7 +149,7 @@ describe("Property", () => { const propertyJson = { name: "BadProp", type: "PrimitiveProperty", - category: "TestSchema.NonExistentPropertyCategory" + category: "TestSchema.NonExistentPropertyCategory", }; await testProp.deserialize(propertyJson); await expect(testProp.category).to.be.rejectedWith(ECObjectsError, `The Property BadProp has a 'category' ("TestSchema.NonExistentPropertyCategory") that cannot be found.`); @@ -163,7 +161,7 @@ describe("Property", () => { const propertyJson = { name: "BadProp", type: "PrimitiveProperty", - kindOfQuantity: "TestSchema.NonExistentKindOfQuantity" + kindOfQuantity: "TestSchema.NonExistentKindOfQuantity", }; await testProp.deserialize(propertyJson); await expect(testProp.kindOfQuantity).to.be.rejectedWith(ECObjectsError, `The Property BadProp has a 'kindOfQuantity' ("TestSchema.NonExistentKindOfQuantity") that cannot be found.`); @@ -172,7 +170,6 @@ describe("Property", () => { describe("toJson", () => { it("Simple serialization", async () => { const propertyJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/31/draft-01/schemaitem", name: "ValidProp", description: "A really long description...", label: "SomeDisplayLabel", @@ -181,19 +178,89 @@ describe("Property", () => { priority: 100, }; const testProp = new MockProperty("ValidProp"); - expect(testProp).to.exist; await testProp.deserialize(propertyJson); const serialized = testProp.toJson(); - assert(serialized.name, "ValidProp"); - assert(serialized.description, "A really long description..."); - assert(serialized.label, "SomeDisplayLabel"); - assert(serialized.type, "PrimitiveProperty"); - assert(serialized.isReadOnly === true); - assert(serialized.priority === 100); + expect(serialized).to.deep.equal({ ...propertyJson }); }); + + it("should omit undefined isReadOnly", async () => { + const propertyJson = { + name: "ValidProp", + type: "PrimitiveProperty", + }; + const testProp = new MockProperty("ValidProp"); + await testProp.deserialize(propertyJson); + expect(testProp.toJson()).to.not.have.property("isReadOnly"); + }); + + it("should include false isReadOnly", async () => { + const propertyJson = { + name: "ValidProp", + type: "PrimitiveProperty", + isReadOnly: false, + }; + const testProp = new MockProperty("ValidProp"); + await testProp.deserialize(propertyJson); + expect(testProp.toJson()).to.include({ isReadOnly: false }); + }); + + it("should omit undefined priority", async () => { + const propertyJson = { + name: "ValidProp", + type: "PrimitiveProperty", + }; + const testProp = new MockProperty("ValidProp"); + await testProp.deserialize(propertyJson); + expect(testProp.toJson()).to.not.have.property("priority"); + }); + + it("should include 0 priority", async () => { + const propertyJson = { + name: "ValidProp", + type: "PrimitiveProperty", + priority: 0, + }; + const testProp = new MockProperty("ValidProp"); + await testProp.deserialize(propertyJson); + expect(testProp.toJson()).to.include({ priority: 0 }); + }); + + it("should include kindOfQuantity", async () => { + const propertyJson = { + name: "ValidProp", + type: "PrimitiveProperty", + kindOfQuantity: "TestSchema.TestKoQ", + }; + const testProp = new MockProperty("ValidProp"); + await testProp.deserialize(propertyJson); + expect(testProp.toJson()).to.include({ kindOfQuantity: "TestSchema.TestKoQ" }); + }); + + it("should include category", async () => { + const propertyJson = { + name: "ValidProp", + type: "PrimitiveProperty", + category: "TestSchema.TestCategory", + }; + const testProp = new MockProperty("ValidProp"); + await testProp.deserialize(propertyJson); + expect(testProp.toJson()).to.include({ category: "TestSchema.TestCategory" }); + }); + + it("should omit customAttributes if empty", async () => { + const propertyJson = { + name: "ValidProp", + type: "PrimitiveProperty", + customAttributes: [], + }; + const testProp = new MockProperty("ValidProp"); + await testProp.deserialize(propertyJson); + expect(testProp!.toJson()).to.not.have.property("customAttributes"); + }); + it("Serialization with one custom attribute- only class name", async () => { const propertyJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/31/draft-01/schemaitem", + $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/schemaitem", name: "ValidProp", type: "PrimitiveProperty", }; @@ -204,11 +271,11 @@ describe("Property", () => { className: "CoreCustomAttributes.HiddenSchema", }); const serialized = testProp.toJson(); - assert(serialized.customAttributes[0].className === "CoreCustomAttributes.HiddenSchema"); + assert.strictEqual(serialized.customAttributes[0].className, "CoreCustomAttributes.HiddenSchema"); }); it("Serialization with one custom attribute- additional properties", () => { const propertyJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/31/draft-01/schemaitem", + $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/schemaitem", name: "ValidProp", type: "PrimitiveProperty", }; @@ -220,12 +287,12 @@ describe("Property", () => { ShowClasses: true, }); const serialized = testProp.toJson(); - assert(serialized.customAttributes[0].className === "CoreCustomAttributes.HiddenSchema"); - assert(serialized.customAttributes[0].ShowClasses === true); + assert.strictEqual(serialized.customAttributes[0].className, "CoreCustomAttributes.HiddenSchema"); + assert.isTrue(serialized.customAttributes[0].ShowClasses); }); it("Serialization with multiple custom attributes- only class name", async () => { const propertyJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/31/draft-01/schemaitem", + $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/schemaitem", name: "ValidProp", type: "PrimitiveProperty", }; @@ -236,13 +303,13 @@ describe("Property", () => { testProp.addCustomAttribute({ className: "CoreAttributes.HiddenSchema" }); testProp.addCustomAttribute({ className: "CoreCustom.HiddenSchema" }); const serialized = testProp.toJson(); - assert(serialized.customAttributes[0].className === "CoreCustomAttributes.HiddenSchema"); - assert(serialized.customAttributes[1].className === "CoreAttributes.HiddenSchema"); - assert(serialized.customAttributes[2].className === "CoreCustom.HiddenSchema"); + assert.strictEqual(serialized.customAttributes[0].className, "CoreCustomAttributes.HiddenSchema"); + assert.strictEqual(serialized.customAttributes[1].className, "CoreAttributes.HiddenSchema"); + assert.strictEqual(serialized.customAttributes[2].className, "CoreCustom.HiddenSchema"); }); it("Serialization with multiple custom attributes- additional properties", async () => { const propertyJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/31/draft-01/schemaitem", + $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/schemaitem", name: "ValidProp", type: "PrimitiveProperty", }; @@ -262,9 +329,9 @@ describe("Property", () => { IntegerValue: 5, }); const serialized = testProp.toJson(); - assert(serialized.customAttributes[0].ShowClasses === true); - assert(serialized.customAttributes[1].FloatValue === 1.2); - assert(serialized.customAttributes[2].IntegerValue === 5); + assert.isTrue(serialized.customAttributes[0].ShowClasses); + assert.strictEqual(serialized.customAttributes[1].FloatValue, 1.2); + assert.strictEqual(serialized.customAttributes[2].IntegerValue, 5); }); }); }); @@ -274,7 +341,7 @@ describe("PrimitiveProperty", () => { let testProperty: PrimitiveProperty; beforeEach(() => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); const testClass = new EntityClass(schema, "TestClass"); testProperty = new PrimitiveProperty(testClass, "TestProperty", PrimitiveType.Double); }); @@ -304,7 +371,7 @@ describe("PrimitiveProperty", () => { const propertyJson = { name: "TestProperty", type: "PrimitiveProperty", - typeName: "string" + typeName: "string", }; expect(testProperty).to.exist; await expect(testProperty.deserialize(propertyJson)).to.be.rejectedWith(ECObjectsError); @@ -314,10 +381,11 @@ describe("PrimitiveProperty", () => { describe("KindOfQuantity in referenced schema", () => { let testProperty: PrimitiveProperty; beforeEach(() => { - const referencedSchema = new Schema("Reference", 1, 0, 0) as MutableSchema; + const context = new SchemaContext(); + const referencedSchema = new Schema(context, "Reference", 1, 0, 0) as MutableSchema; referencedSchema.createKindOfQuantitySync("MyKindOfQuantity"); - const schema = new Schema("TestSchema", 1, 0, 0) as MutableSchema; + const schema = new Schema(context, "TestSchema", 1, 0, 0) as MutableSchema; schema.addReferenceSync(referencedSchema); const testClass = schema.createEntityClassSync("TestClass") as ECClass as MutableClass; @@ -335,24 +403,25 @@ describe("PrimitiveProperty", () => { testProperty.deserializeSync(propertyJson); const koq = testProperty.getKindOfQuantitySync(); assert(koq !== undefined); - assert(koq!.name === "MyKindOfQuantity"); + assert.strictEqual(koq!.name, "MyKindOfQuantity"); }); it("Should load KindOfQuantity", async () => { await testProperty.deserialize(propertyJson); const koq = await testProperty.kindOfQuantity; assert(koq !== undefined); - assert(koq!.name === "MyKindOfQuantity"); + assert.strictEqual(koq!.name, "MyKindOfQuantity"); }); }); describe("PropertyCategory in referenced schema", () => { let testProperty: PrimitiveProperty; beforeEach(() => { - const referencedSchema = new Schema("Reference", 1, 0, 0) as MutableSchema; + const context = new SchemaContext(); + const referencedSchema = new Schema(context, "Reference", 1, 0, 0) as MutableSchema; referencedSchema.createPropertyCategorySync("MyCategory"); - const schema = new Schema("TestSchema", 1, 0, 0) as MutableSchema; + const schema = new Schema(context, "TestSchema", 1, 0, 0) as MutableSchema; schema.addReferenceSync(referencedSchema); const testClass = schema.createEntityClassSync("TestClass") as ECClass as MutableClass; @@ -370,21 +439,21 @@ describe("PrimitiveProperty", () => { testProperty.deserializeSync(propertyJson); const cat = testProperty.getCategorySync(); assert(cat !== undefined); - assert(cat!.name === "MyCategory"); + assert.strictEqual(cat!.name, "MyCategory"); }); it("Should load PropertyCategory", async () => { await testProperty.deserialize(propertyJson); const cat = await testProperty.category; assert(cat !== undefined); - assert(cat!.name === "MyCategory"); + assert.strictEqual(cat!.name, "MyCategory"); }); }); describe("toJson", () => { let testProperty: PrimitiveProperty; beforeEach(() => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); const testClass = new EntityClass(schema, "TestClass"); testProperty = new PrimitiveProperty(testClass, "TestProperty", PrimitiveType.Double); }); @@ -418,7 +487,7 @@ describe("EnumerationProperty", () => { let testEnum: Enumeration; beforeEach(async () => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); const testClass = await (schema as MutableSchema).createEntityClass("TestClass"); testEnum = await (schema as MutableSchema).createEnumeration("TestEnum"); testProperty = new EnumerationProperty(testClass, "TestProperty", new DelayedPromiseWithProps(testEnum.key, async () => testEnum)); @@ -439,7 +508,7 @@ describe("EnumerationProperty", () => { const propertyJson = { name: "TestProperty", type: "PrimitiveProperty", - typeName: "ThisDoesNotMatch" + typeName: "ThisDoesNotMatch", }; expect(testProperty).to.exist; await expect(testProperty.deserialize(propertyJson)).to.be.rejectedWith(ECObjectsError); @@ -450,7 +519,7 @@ describe("EnumerationProperty", () => { let testEnum: Enumeration; beforeEach(async () => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); const testClass = await (schema as MutableSchema).createEntityClass("TestClass"); testEnum = await (schema as MutableSchema).createEnumeration("TestEnum"); testProperty = new EnumerationProperty(testClass, "TestProperty", new DelayedPromiseWithProps(testEnum.key, async () => testEnum)); @@ -465,7 +534,7 @@ describe("EnumerationProperty", () => { expect(testProperty).to.exist; await testProperty.deserialize(propertyJson); const testPropSerialization = testProperty.toJson(); - assert(testPropSerialization.typeName, "TestSchema.TestEnum"); + assert.strictEqual(testPropSerialization.typeName, "TestSchema.TestEnum"); }); }); }); @@ -476,7 +545,7 @@ describe("StructProperty", () => { let testStruct: StructClass; beforeEach(async () => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); const testClass = await (schema as MutableSchema).createEntityClass("TestClass"); testStruct = await (schema as MutableSchema).createStructClass("TestStruct"); testProperty = new StructProperty(testClass, "TestProperty", testStruct); @@ -497,7 +566,7 @@ describe("StructProperty", () => { const propertyJson = { name: "TestProperty", type: "StructProperty", - typeName: "ThisDoesNotMatch" + typeName: "ThisDoesNotMatch", }; expect(testProperty).to.exist; await expect(testProperty.deserialize(propertyJson)).to.be.rejectedWith(ECObjectsError); @@ -508,7 +577,7 @@ describe("StructProperty", () => { let testStruct: StructClass; beforeEach(async () => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); const testClass = await (schema as MutableSchema).createEntityClass("TestClass"); testStruct = await (schema as MutableSchema).createStructClass("TestStruct"); testProperty = new StructProperty(testClass, "TestProperty", testStruct); @@ -523,7 +592,7 @@ describe("StructProperty", () => { expect(testProperty).to.exist; await testProperty.deserialize(propertyJson); const testPropSerialization = testProperty.toJson(); - assert(testPropSerialization.typeName, "TestStruct"); + assert.strictEqual(testPropSerialization.typeName, "TestSchema.TestStruct"); }); }); }); @@ -533,7 +602,7 @@ describe("PrimitiveArrayProperty", () => { let testProperty: PrimitiveArrayProperty; beforeEach(() => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); const testClass = new EntityClass(schema, "TestClass"); testProperty = new PrimitiveArrayProperty(testClass, "TestProperty"); }); @@ -553,12 +622,12 @@ describe("PrimitiveArrayProperty", () => { }); describe("toJson", () => { - let testProperty: PrimitiveArrayProperty; + let testArrayProperty: PrimitiveArrayProperty; beforeEach(() => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); const testClass = new EntityClass(schema, "TestClass"); - testProperty = new PrimitiveArrayProperty(testClass, "TestProperty"); + testArrayProperty = new PrimitiveArrayProperty(testClass, "TestProperty"); }); it("should successfully serialize valid JSON", async () => { @@ -568,9 +637,9 @@ describe("PrimitiveArrayProperty", () => { minOccurs: 2, maxOccurs: 4, }; - expect(testProperty).to.exist; - await testProperty.deserialize(propertyJson); - const testPropSerialization = testProperty.toJson(); + expect(testArrayProperty).to.exist; + await testArrayProperty.deserialize(propertyJson); + const testPropSerialization = testArrayProperty.toJson(); expect(testPropSerialization.minOccurs).to.eql(2); expect(testPropSerialization.maxOccurs).to.eql(4); }); diff --git a/core/ecschema-metadata/test/Metadata/PropertyCategory.test.ts b/core/ecschema-metadata/test/Metadata/PropertyCategory.test.ts index 9439420..a8edd59 100644 --- a/core/ecschema-metadata/test/Metadata/PropertyCategory.test.ts +++ b/core/ecschema-metadata/test/Metadata/PropertyCategory.test.ts @@ -6,13 +6,13 @@ import { assert, expect } from "chai"; import { Schema } from "../../src/Metadata/Schema"; import { PropertyCategory } from "../../src/Metadata/PropertyCategory"; -import * as sinon from "sinon"; +import { SchemaContext } from "../../src/Context"; describe("PropertyCategory", () => { describe("deserialization", () => { it("fully defined ", async () => { const testSchema = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", alias: "TestSchema", @@ -26,7 +26,7 @@ describe("PropertyCategory", () => { }, }; - const ecSchema = await Schema.fromJson(testSchema); + const ecSchema = await Schema.fromJson(testSchema, new SchemaContext()); assert.isDefined(ecSchema); const item = await ecSchema.getItem("TestPropertyCategory"); @@ -48,7 +48,7 @@ describe("PropertyCategory", () => { describe("toJson", () => { it("fully defined", async () => { const testSchema = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", items: { @@ -61,7 +61,7 @@ describe("PropertyCategory", () => { }, }; - const ecSchema = await Schema.fromJson(testSchema); + const ecSchema = await Schema.fromJson(testSchema, new SchemaContext()); assert.isDefined(ecSchema); const item = await ecSchema.getItem("TestPropertyCategory"); @@ -74,26 +74,4 @@ describe("PropertyCategory", () => { expect(propCatSerialization.priority).equal(5); }); }); - - describe("accept", () => { - let testCategory: PropertyCategory; - - beforeEach(() => { - const schema = new Schema("TestSchema", 1, 0, 0); - testCategory = new PropertyCategory(schema, "TestCategory"); - }); - - it("should call visitPropertyCategory on a SchemaItemVisitor object", async () => { - expect(testCategory).to.exist; - const mockVisitor = { visitPropertyCategory: sinon.spy() }; - await testCategory.accept(mockVisitor); - expect(mockVisitor.visitPropertyCategory.calledOnce).to.be.true; - expect(mockVisitor.visitPropertyCategory.calledWithExactly(testCategory)).to.be.true; - }); - - it("should safely handle a SchemaItemVisitor without visitPropertyCategory defined", async () => { - expect(testCategory).to.exist; - await testCategory.accept({}); - }); - }); }); diff --git a/core/ecschema-metadata/test/Metadata/PropertyInheritance.test.ts b/core/ecschema-metadata/test/Metadata/PropertyInheritance.test.ts index bf2bb33..d336a80 100644 --- a/core/ecschema-metadata/test/Metadata/PropertyInheritance.test.ts +++ b/core/ecschema-metadata/test/Metadata/PropertyInheritance.test.ts @@ -7,6 +7,7 @@ import { assert, expect } from "chai"; import { Schema, MutableSchema } from "../../src/Metadata/Schema"; import { ECClass, MutableClass } from "../../src/Metadata/Class"; import { PrimitiveType } from "../../src/ECObjects"; +import { SchemaContext } from "../../src/Context"; describe("Property Inheritance", () => { describe("Struct class with two levels of base classes", () => { @@ -18,7 +19,7 @@ describe("Property Inheritance", () => { // | // [TestClass:P4,P3] const schemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "01.00.00", alias: "ts", @@ -26,18 +27,18 @@ describe("Property Inheritance", () => { RootClass: { schemaItemType: "StructClass", properties: [{ name: "P1", type: "PrimitiveProperty", typeName: "string" }, - { name: "P2", type: "PrimitiveProperty", typeName: "string" }] + { name: "P2", type: "PrimitiveProperty", typeName: "string" }], }, MiddleClass: { schemaItemType: "StructClass", baseClass: "TestSchema.RootClass", properties: [{ name: "P2", type: "PrimitiveProperty", typeName: "string" }, { name: "P1", type: "PrimitiveProperty", typeName: "string" }, - { name: "P3", type: "PrimitiveProperty", typeName: "string" }] + { name: "P3", type: "PrimitiveProperty", typeName: "string" }], }, TestClass: { schemaItemType: "StructClass", baseClass: "TestSchema.MiddleClass", properties: [{ name: "P4", type: "PrimitiveProperty", typeName: "string" }, - { name: "P3", type: "PrimitiveProperty", typeName: "string" }] + { name: "P3", type: "PrimitiveProperty", typeName: "string" }], }, }, }; @@ -45,7 +46,7 @@ describe("Property Inheritance", () => { const expectedResult = ["P1(MiddleClass)", "P2(MiddleClass)", "P3(TestClass)", "P4(TestClass)"]; it("async iteration", async () => { - const schema = (await Schema.fromJson(schemaJson)) as MutableSchema; + const schema = (await Schema.fromJson(schemaJson, new SchemaContext())) as MutableSchema; const testClass = await schema.getItem("TestClass"); const props = await testClass!.getProperties(); const names = props.map((p) => `${p.name}(${p.class.name})`); @@ -53,7 +54,7 @@ describe("Property Inheritance", () => { }); it("sync iteration", () => { - const schema = Schema.fromJsonSync(schemaJson) as MutableSchema; + const schema = Schema.fromJsonSync(schemaJson, new SchemaContext()) as MutableSchema; const testClass = schema.getItemSync("TestClass"); const props = testClass!.getPropertiesSync(); const names = props.map((p) => `${p.name}(${p.class.name})`); @@ -68,7 +69,7 @@ describe("Property Inheritance", () => { // | // [TestClass:P4,P3] const schemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "01.00.00", alias: "ts", @@ -76,18 +77,18 @@ describe("Property Inheritance", () => { RootClass: { schemaItemType: "EntityClass", properties: [{ name: "P1", type: "PrimitiveProperty", typeName: "string" }, - { name: "P2", type: "PrimitiveProperty", typeName: "string" }] + { name: "P2", type: "PrimitiveProperty", typeName: "string" }], }, MiddleClass: { schemaItemType: "EntityClass", baseClass: "TestSchema.RootClass", properties: [{ name: "P2", type: "PrimitiveProperty", typeName: "string" }, { name: "P1", type: "PrimitiveProperty", typeName: "string" }, - { name: "P3", type: "PrimitiveProperty", typeName: "string" }] + { name: "P3", type: "PrimitiveProperty", typeName: "string" }], }, TestClass: { schemaItemType: "EntityClass", baseClass: "TestSchema.MiddleClass", properties: [{ name: "P4", type: "PrimitiveProperty", typeName: "string" }, - { name: "P3", type: "PrimitiveProperty", typeName: "string" }] + { name: "P3", type: "PrimitiveProperty", typeName: "string" }], }, }, }; @@ -95,7 +96,7 @@ describe("Property Inheritance", () => { const expectedResult = ["P1(MiddleClass)", "P2(MiddleClass)", "P3(TestClass)", "P4(TestClass)"]; it("async iteration", async () => { - const schema = (await Schema.fromJson(schemaJson)) as MutableSchema; + const schema = (await Schema.fromJson(schemaJson, new SchemaContext())) as MutableSchema; const testClass = await schema.getItem("TestClass"); const props = await testClass!.getProperties(); const names = props.map((p) => `${p.name}(${p.class.name})`); @@ -103,7 +104,7 @@ describe("Property Inheritance", () => { }); it("sync iteration", () => { - const schema = Schema.fromJsonSync(schemaJson) as MutableSchema; + const schema = Schema.fromJsonSync(schemaJson, new SchemaContext()) as MutableSchema; const testClass = schema.getItemSync("TestClass"); const props = testClass!.getPropertiesSync(); const names = props.map((p) => `${p.name}(${p.class.name})`); @@ -118,18 +119,18 @@ describe("Property Inheritance", () => { // | / // [TestClass:P4,P3] const schemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "01.00.00", alias: "ts", items: { RootClass: { schemaItemType: "EntityClass", - properties: [{ name: "P1", type: "PrimitiveProperty", typeName: "string" }] + properties: [{ name: "P1", type: "PrimitiveProperty", typeName: "string" }], }, Mixin: { schemaItemType: "Mixin", appliesTo: "TestSchema.RootClass", - properties: [{ name: "P5", type: "PrimitiveProperty", typeName: "string" }] + properties: [{ name: "P5", type: "PrimitiveProperty", typeName: "string" }], }, TestClass: { schemaItemType: "EntityClass", baseClass: "TestSchema.RootClass", mixins: ["TestSchema.Mixin"] }, }, @@ -140,7 +141,7 @@ describe("Property Inheritance", () => { const expectedResult3 = ["P1(RootClass)", "P2(RootClass)", "P5(Mixin)", "P3(TestClass)"]; it("async iteration", async () => { - const schema = (await Schema.fromJson(schemaJson)) as MutableSchema; + const schema = (await Schema.fromJson(schemaJson, new SchemaContext())) as MutableSchema; const testClass = await schema.getItem("TestClass") as MutableClass; const rootClass = await schema.getItem("RootClass") as MutableClass; let props = await testClass!.getProperties(); @@ -171,7 +172,7 @@ describe("Property Inheritance", () => { }); it("sync iteration", () => { - const schema = Schema.fromJsonSync(schemaJson) as MutableSchema; + const schema = Schema.fromJsonSync(schemaJson, new SchemaContext()) as MutableSchema; const testClass = schema.getItemSync("TestClass") as MutableClass; const rootClass = schema.getItemSync("RootClass") as MutableClass; let props = testClass!.getPropertiesSync(); @@ -209,14 +210,14 @@ describe("Property Inheritance", () => { // | // [TestClass:P4,P3] const schemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "01.00.00", alias: "ts", items: { RootClass: { schemaItemType: "StructClass", - properties: [{ name: "P1", type: "PrimitiveProperty", typeName: "string" }] + properties: [{ name: "P1", type: "PrimitiveProperty", typeName: "string" }], }, TestClass: { schemaItemType: "StructClass", baseClass: "TestSchema.RootClass" }, }, @@ -227,7 +228,7 @@ describe("Property Inheritance", () => { const expectedResult3 = ["P1(RootClass)", "P2(RootClass)", "P3(TestClass)"]; it("async iteration", async () => { - const schema = (await Schema.fromJson(schemaJson)) as MutableSchema; + const schema = (await Schema.fromJson(schemaJson, new SchemaContext())) as MutableSchema; const testClass = await schema.getItem("TestClass") as MutableClass; const rootClass = await schema.getItem("RootClass") as MutableClass; let props = await testClass!.getProperties(); @@ -258,7 +259,7 @@ describe("Property Inheritance", () => { }); it("sync iteration", () => { - const schema = Schema.fromJsonSync(schemaJson) as MutableSchema; + const schema = Schema.fromJsonSync(schemaJson, new SchemaContext()) as MutableSchema; const testClass = schema.getItemSync("TestClass") as MutableClass; const rootClass = schema.getItemSync("RootClass") as MutableClass; let props = testClass!.getPropertiesSync(); @@ -300,7 +301,7 @@ describe("Property Inheritance", () => { // [H:P2] // We are using the labels to tell the properties apart which have been overwritten const testSchemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "01.00.00", alias: "ts", @@ -308,33 +309,33 @@ describe("Property Inheritance", () => { A: { schemaItemType: "EntityClass", properties: [{ name: "P1", type: "PrimitiveProperty", typeName: "string" }, - { name: "P2", type: "PrimitiveProperty", typeName: "string" }] + { name: "P2", type: "PrimitiveProperty", typeName: "string" }], }, B: { schemaItemType: "Mixin", appliesTo: "TestSchema.A" }, C: { schemaItemType: "Mixin", appliesTo: "TestSchema.A", - properties: [{ name: "P3", type: "PrimitiveProperty", typeName: "string" }] + properties: [{ name: "P3", type: "PrimitiveProperty", typeName: "string" }], }, D: { schemaItemType: "Mixin", appliesTo: "TestSchema.A", - properties: [{ name: "P4", type: "PrimitiveProperty", typeName: "string" }] + properties: [{ name: "P4", type: "PrimitiveProperty", typeName: "string" }], }, E: { schemaItemType: "Mixin", appliesTo: "TestSchema.A", baseClass: "TestSchema.C" }, F: { schemaItemType: "Mixin", appliesTo: "TestSchema.A", baseClass: "TestSchema.D" }, G: { schemaItemType: "EntityClass", baseClass: "TestSchema.A", mixins: ["TestSchema.B"], - properties: [{ name: "P1", type: "PrimitiveProperty", typeName: "string" }] + properties: [{ name: "P1", type: "PrimitiveProperty", typeName: "string" }], }, H: { schemaItemType: "EntityClass", baseClass: "TestSchema.G", mixins: ["TestSchema.E", "TestSchema.F"], - properties: [{ name: "P2", type: "PrimitiveProperty", typeName: "string" }] + properties: [{ name: "P2", type: "PrimitiveProperty", typeName: "string" }], }, }, }; const expectedOrder = ["P1(G)", "P2(H)", "P3(C)", "P4(D)"]; it("async iteration", async () => { - const schema = await Schema.fromJson(testSchemaJson); + const schema = await Schema.fromJson(testSchemaJson, new SchemaContext()); expect(schema).to.exist; const testClass = await schema.getItem("H"); @@ -346,7 +347,7 @@ describe("Property Inheritance", () => { }); it("sync iteration", () => { - const schema = Schema.fromJsonSync(testSchemaJson); + const schema = Schema.fromJsonSync(testSchemaJson, new SchemaContext()); expect(schema).to.exist; const testClass = schema.getItemSync("H"); diff --git a/core/ecschema-metadata/test/Metadata/Relationship.test.ts b/core/ecschema-metadata/test/Metadata/Relationship.test.ts index 0be1b1c..dc92b45 100644 --- a/core/ecschema-metadata/test/Metadata/Relationship.test.ts +++ b/core/ecschema-metadata/test/Metadata/Relationship.test.ts @@ -4,16 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import { assert, expect } from "chai"; -import { Schema } from "../../src/Metadata/Schema"; -import { EntityClass } from "../../src/Metadata/EntityClass"; -import { RelationshipClass, RelationshipMultiplicity, RelationshipConstraint } from "../../src/Metadata/RelationshipClass"; -import { StrengthType, StrengthDirection, RelationshipEnd } from "../../src/ECObjects"; -import { ECObjectsError } from "../../src/Exception"; -import { createSchemaJsonWithItems } from "../TestUtils/DeserializationHelpers"; import sinon = require("sinon"); + +import { SchemaContext } from "../../src/Context"; +import { DelayedPromiseWithProps } from "../../src/DelayedPromise"; +import { RelationshipEnd, StrengthDirection, StrengthType } from "../../src/ECObjects"; +import { ECObjectsError } from "../../src/Exception"; import { CustomAttributeClass } from "../../src/Metadata/CustomAttributeClass"; +import { EntityClass } from "../../src/Metadata/EntityClass"; import { Mixin } from "../../src/Metadata/Mixin"; -import { DelayedPromiseWithProps } from "../../src/DelayedPromise"; +import { RelationshipClass, RelationshipConstraint, RelationshipMultiplicity } from "../../src/Metadata/RelationshipClass"; +import { Schema } from "../../src/Metadata/Schema"; +import { createSchemaJsonWithItems } from "../TestUtils/DeserializationHelpers"; describe("RelationshipMultiplicity", () => { describe("fromString", () => { @@ -58,7 +60,7 @@ describe("RelationshipClass", () => { function createSchemaJson(relClassJson: any): any { return { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", items: { @@ -109,7 +111,7 @@ describe("RelationshipClass", () => { }, }); - const schema = await Schema.fromJson(schemaJson); + const schema = await Schema.fromJson(schemaJson, new SchemaContext()); assert.isDefined(schema); const sourceBaseEntity = await schema.getItem("SourceBaseEntity"); @@ -178,7 +180,7 @@ describe("RelationshipClass", () => { }, ], }); - const schema = await Schema.fromJson(json); + const schema = await Schema.fromJson(json, new SchemaContext()); assert.isDefined(schema); const relClass = await schema.getItem("TestRelationship"); @@ -224,7 +226,7 @@ describe("RelationshipClass", () => { }, ], }); - const schema = Schema.fromJsonSync(json); + const schema = Schema.fromJsonSync(json, new SchemaContext()); assert.isDefined(schema); const relClass = schema.getItemSync("TestRelationship"); @@ -254,7 +256,7 @@ describe("RelationshipClass", () => { strengthDirection: "backward", target: validConstraint, }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The RelationshipClass TestSchema.TestRelationship is missing the required source constraint.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The RelationshipClass TestSchema.TestRelationship is missing the required source constraint.`); }); it("should throw for missing target constraint", async () => { @@ -263,7 +265,7 @@ describe("RelationshipClass", () => { strengthDirection: "forward", source: validConstraint, }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The RelationshipClass TestSchema.TestRelationship is missing the required target constraint.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The RelationshipClass TestSchema.TestRelationship is missing the required target constraint.`); }); it("should throw for invalid source constraint", async () => { @@ -273,7 +275,7 @@ describe("RelationshipClass", () => { source: 0, target: validConstraint, }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The RelationshipClass TestSchema.TestRelationship has an invalid source constraint. It should be of type 'object'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The RelationshipClass TestSchema.TestRelationship has an invalid source constraint. It should be of type 'object'.`); }); it("should throw for invalid target constraint", async () => { @@ -283,7 +285,7 @@ describe("RelationshipClass", () => { source: validConstraint, target: 0, }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The RelationshipClass TestSchema.TestRelationship has an invalid target constraint. It should be of type 'object'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The RelationshipClass TestSchema.TestRelationship has an invalid target constraint. It should be of type 'object'.`); }); it("should throw for invalid abstractConstraint", async () => { @@ -301,7 +303,7 @@ describe("RelationshipClass", () => { }, target: {}, }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The Source Constraint of TestSchema.TestRelationship has an invalid 'abstractConstraint' attribute. It should be of type 'string'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The Source Constraint of TestSchema.TestRelationship has an invalid 'abstractConstraint' attribute. It should be of type 'string'.`); }); it("should throw for invalid constraintClasses", async () => { @@ -316,7 +318,7 @@ describe("RelationshipClass", () => { }, target: {}, }); - await expect(Schema.fromJson(json)).to.be.rejectedWith(ECObjectsError, `The Source Constraint of TestSchema.TestRelationship has an invalid 'constraintClasses' attribute. It should be of type 'string[]'.`); + await expect(Schema.fromJson(json, new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The Source Constraint of TestSchema.TestRelationship has an invalid 'constraintClasses' attribute. It should be of type 'string[]'.`); }); }); @@ -329,7 +331,7 @@ describe("RelationshipClass", () => { function createSchemaJson(relClassJson: any): any { return { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", items: { @@ -355,95 +357,156 @@ describe("RelationshipClass", () => { }; } + const validRelationshipJson = { + strength: "Embedding", + strengthDirection: "Backward", + modifier: "Sealed", + source: { + polymorphic: true, + multiplicity: "(0..*)", + roleLabel: "Source RoleLabel", + abstractConstraint: "TestSchema.SourceBaseEntity", + constraintClasses: [ + "TestSchema.TestSourceEntity", + ], + }, + target: { + polymorphic: true, + multiplicity: "(0..*)", + roleLabel: "Target RoleLabel", + abstractConstraint: "TestSchema.TargetBaseEntity", + constraintClasses: [ + "TestSchema.TestTargetEntity", + ], + }, + }; + it("async - Serialization of fully defined relationship", async () => { - const schemaJson = createSchemaJson({ - strength: "Embedding", - strengthDirection: "Backward", - modifier: "Sealed", + const schema = await Schema.fromJson(createSchemaJson(validRelationshipJson), new SchemaContext()); + const relClass = await schema.getItem("TestRelationship"); + const expectedJson = { + $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/schemaitem", + name: "TestRelationship", + schema: "TestSchema", + schemaVersion: "01.02.03", + schemaItemType: "RelationshipClass", + ...validRelationshipJson, + }; + expect(relClass).to.exist; + expect(relClass!.toJson(true, true)).to.deep.equal(expectedJson); + }); + + it("should include modifier if 'None'", async () => { + const schema = await Schema.fromJson(createSchemaJson({ ...validRelationshipJson, modifier: "None" }), new SchemaContext()); + const relClass = await schema.getItem("TestRelationship"); + expect(relClass).to.exist; + expect(relClass!.toJson(true, true)).to.include({ modifier: "None" }); + }); + + it("should include modifier if 'Abstract'", async () => { + const schema = await Schema.fromJson(createSchemaJson({ ...validRelationshipJson, modifier: "Abstract" }), new SchemaContext()); + const relClass = await schema.getItem("TestRelationship"); + expect(relClass).to.exist; + expect(relClass!.toJson(true, true)).to.include({ modifier: "Abstract" }); + }); + + it("should include modifier if 'Sealed'", async () => { + const schema = await Schema.fromJson(createSchemaJson({ ...validRelationshipJson, modifier: "Sealed" }), new SchemaContext()); + const relClass = await schema.getItem("TestRelationship"); + expect(relClass).to.exist; + expect(relClass!.toJson(true, true)).to.include({ modifier: "Sealed" }); + }); + + it("should omit customAttributes if empty", async () => { + const schema = await Schema.fromJson(createSchemaJson({ ...validRelationshipJson, customAttributes: [] }), new SchemaContext()); + const relClass = await schema.getItem("TestRelationship"); + expect(relClass).to.exist; + expect(relClass!.toJson(true, true)).to.not.have.property("customAttributes"); + }); + + it("should omit constraint customAttributes if empty", async () => { + const relClassJson = { + ...validRelationshipJson, + customAttributes: [], source: { - polymorphic: true, - multiplicity: "(0..*)", - roleLabel: "Source RoleLabel", - abstractConstraint: "TestSchema.SourceBaseEntity", - constraintClasses: [ - "TestSchema.TestSourceEntity", - ], + ...validRelationshipJson.source, + customAttributes: [], }, target: { - polymorphic: true, - multiplicity: "(0..*)", - roleLabel: "Target RoleLabel", - abstractConstraint: "TestSchema.TargetBaseEntity", - constraintClasses: [ - "TestSchema.TestTargetEntity", - ], + ...validRelationshipJson.target, + customAttributes: [], }, - }); - - const schema = await Schema.fromJson(schemaJson); - assert.isDefined(schema); + }; + const schema = await Schema.fromJson(createSchemaJson(relClassJson), new SchemaContext()); const relClass = await schema.getItem("TestRelationship"); - assert.isDefined(relClass); - const relClassJson = relClass!.toJson(true, true); - assert.isDefined(relClassJson); - assert(relClassJson.strength, "Embedding"); - assert(relClassJson.strengthDirection, "Backward"); - assert(relClassJson.modifier, "Sealed"); - assert(relClassJson.source.polymorphic === true); - assert(relClassJson.source.multiplicity, "(0..*)"); - assert(relClassJson.source.roleLabel, "Source RoleLabel"); - assert(relClassJson.source.abstractConstraint, "TestSchema.SourceBaseEntity"); - assert(relClassJson.source.constraintClasses[0], "TestSchema.TestSourceEntity"); - assert(relClassJson.target.polymorphic === true); - assert(relClassJson.target.multiplicity, "(0..*)"); - assert(relClassJson.target.roleLabel, "Target RoleLabel"); - assert(relClassJson.target.abstractConstraint, "TestSchema.TargetBaseEntity"); - assert(relClassJson.target.constraintClasses[0], "TestSchema.TestTargetEntity"); + expect(relClass).to.exist; + const actualJson = relClass!.toJson(true, true); + expect(actualJson).to.not.have.property("customAttributes"); + expect(actualJson.source).to.not.have.property("customAttributes"); + expect(actualJson.target).to.not.have.property("customAttributes"); }); - it("sync - Serialization of fully defined relationship", async () => { - const schemaJson = createSchemaJson({ - strength: "Embedding", - strengthDirection: "Backward", - modifier: "Sealed", + + it("should omit implicit abstractConstraint", async () => { + const relClassJson = { + ...validRelationshipJson, source: { polymorphic: true, multiplicity: "(0..*)", roleLabel: "Source RoleLabel", - abstractConstraint: "TestSchema.SourceBaseEntity", constraintClasses: [ "TestSchema.TestSourceEntity", ], }, - target: { + }; + const schema = await Schema.fromJson(createSchemaJson(relClassJson), new SchemaContext()); + const relClass = await schema.getItem("TestRelationship"); + expect(relClass).to.exist; + const actualJson = relClass!.toJson(true, true); + expect(actualJson.source).to.not.have.property("abstractConstraint"); + }); + + it("should include explicit abstractConstraint", async () => { + const relClassJson = { + ...validRelationshipJson, + source: { polymorphic: true, multiplicity: "(0..*)", - roleLabel: "Target RoleLabel", - abstractConstraint: "TestSchema.TargetBaseEntity", + roleLabel: "Source RoleLabel", + abstractConstraint: "TestSchema.TestSourceEntity", constraintClasses: [ - "TestSchema.TestTargetEntity", + "TestSchema.TestSourceEntity", ], }, - }); + }; + const schema = await Schema.fromJson(createSchemaJson(relClassJson), new SchemaContext()); + const relClass = await schema.getItem("TestRelationship"); + expect(relClass).to.exist; + const actualJson = relClass!.toJson(true, true); + expect(actualJson.source).to.include({ abstractConstraint: "TestSchema.TestSourceEntity" }); + }); + + it("sync - Serialization of fully defined relationship", async () => { + const schemaJson = createSchemaJson(validRelationshipJson); - const schema = Schema.fromJsonSync(schemaJson); + const schema = Schema.fromJsonSync(schemaJson, new SchemaContext()); assert.isDefined(schema); const relClass = schema.getItemSync("TestRelationship"); assert.isDefined(relClass); const relClassJson = relClass!.toJson(true, true); assert.isDefined(relClassJson); - assert(relClassJson.strength, "Embedding"); - assert(relClassJson.strengthDirection, "Backward"); - assert(relClassJson.modifier, "Sealed"); - assert(relClassJson.source.polymorphic === true); - assert(relClassJson.source.multiplicity, "(0..*)"); - assert(relClassJson.source.roleLabel, "Source RoleLabel"); - assert(relClassJson.source.abstractConstraint, "TestSchema.SourceBaseEntity"); - assert(relClassJson.source.constraintClasses[0], "TestSchema.TestSourceEntity"); - assert(relClassJson.target.polymorphic === true); - assert(relClassJson.target.multiplicity, "(0..*)"); - assert(relClassJson.target.roleLabel, "Target RoleLabel"); - assert(relClassJson.target.abstractConstraint, "TestSchema.TargetBaseEntity"); - assert(relClassJson.target.constraintClasses[0], "TestSchema.TestTargetEntity"); + assert.strictEqual(relClassJson.strength, "Embedding"); + assert.strictEqual(relClassJson.strengthDirection, "Backward"); + assert.strictEqual(relClassJson.modifier, "Sealed"); + assert.isTrue(relClassJson.source.polymorphic); + assert.strictEqual(relClassJson.source.multiplicity, "(0..*)"); + assert.strictEqual(relClassJson.source.roleLabel, "Source RoleLabel"); + assert.strictEqual(relClassJson.source.abstractConstraint, "TestSchema.SourceBaseEntity"); + assert.strictEqual(relClassJson.source.constraintClasses[0], "TestSchema.TestSourceEntity"); + assert.isTrue(relClassJson.target.polymorphic); + assert.strictEqual(relClassJson.target.multiplicity, "(0..*)"); + assert.strictEqual(relClassJson.target.roleLabel, "Target RoleLabel"); + assert.strictEqual(relClassJson.target.abstractConstraint, "TestSchema.TargetBaseEntity"); + assert.strictEqual(relClassJson.target.constraintClasses[0], "TestSchema.TestTargetEntity"); }); }); @@ -569,7 +632,7 @@ describe("RelationshipClass", () => { } before(async () => { - schema = await Schema.fromJson(createSchemaJson()); + schema = await Schema.fromJson(createSchemaJson(), new SchemaContext()); assert.isDefined(schema); vehicleOwner = schema.getItemSync("VehicleOwner") as RelationshipClass; childVehicleOwner = schema.getItemSync("ChildVehicleOwner") as RelationshipClass; diff --git a/core/ecschema-metadata/test/Metadata/RelationshipConstraint.test.ts b/core/ecschema-metadata/test/Metadata/RelationshipConstraint.test.ts index 0011854..9dfeda4 100644 --- a/core/ecschema-metadata/test/Metadata/RelationshipConstraint.test.ts +++ b/core/ecschema-metadata/test/Metadata/RelationshipConstraint.test.ts @@ -9,6 +9,7 @@ import { ECObjectsError } from "../../src/Exception"; import { RelationshipClass, RelationshipConstraint } from "../../src/Metadata/RelationshipClass"; import { RelationshipEnd } from "../../src/ECObjects"; import { createSchemaJsonWithItems } from "../TestUtils/DeserializationHelpers"; +import { SchemaContext } from "../../src/Context"; function createSchemaJson(sourceConst: any, targetConst: any) { return createSchemaJsonWithItems({ @@ -48,7 +49,7 @@ describe("RelationshipConstraint", () => { let testConstraint: RelationshipConstraint; beforeEach(() => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); const relClass = new RelationshipClass(schema, "TestRelationship"); testConstraint = new RelationshipConstraint(relClass, RelationshipEnd.Source); }); @@ -86,21 +87,21 @@ describe("RelationshipConstraint", () => { ], }; it("async - Deserialize One Custom Attribute", async () => { - const schema = await Schema.fromJson(createSchemaJson(oneCustomAttributeJson, targetStubJson)); + const schema = await Schema.fromJson(createSchemaJson(oneCustomAttributeJson, targetStubJson), new SchemaContext()); testConstraint = (await schema.getItem("TestRelationship"))!.source; expect(testConstraint).to.exist; expect(testConstraint.customAttributes!.get("TestSchema.TestCAClassA")).to.exist; - assert(testConstraint.customAttributes!.get("TestSchema.TestCAClassA")!.ShowClasses === true); + assert.isTrue(testConstraint.customAttributes!.get("TestSchema.TestCAClassA")!.ShowClasses); }); it("sync - Deserialize One Custom Attribute", () => { - const schema = Schema.fromJsonSync(createSchemaJson(oneCustomAttributeJson, targetStubJson)); + const schema = Schema.fromJsonSync(createSchemaJson(oneCustomAttributeJson, targetStubJson), new SchemaContext()); testConstraint = schema.getItemSync("TestRelationship")!.source; expect(testConstraint).to.exist; expect(testConstraint.customAttributes!.get("TestSchema.TestCAClassA")).to.exist; - assert(testConstraint.customAttributes!.get("TestSchema.TestCAClassA")!.ShowClasses === true); + assert.isTrue(testConstraint.customAttributes!.get("TestSchema.TestCAClassA")!.ShowClasses); }); const twoCustomAttributesJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "ValidSchema", polymorphic: true, multiplicity: "(0..1)", @@ -118,14 +119,14 @@ describe("RelationshipConstraint", () => { ], }; it("async - Deserialize Two Custom Attributes", async () => { - const schema = await Schema.fromJson(createSchemaJson(twoCustomAttributesJson, targetStubJson)); + const schema = await Schema.fromJson(createSchemaJson(twoCustomAttributesJson, targetStubJson), new SchemaContext()); testConstraint = (await schema.getItem("TestRelationship"))!.source; expect(testConstraint).to.exist; expect(testConstraint!.customAttributes!.get("TestSchema.TestCAClassA")).to.exist; expect(testConstraint!.customAttributes!.get("TestSchema.TestCAClassB")).to.exist; }); it("sync - Deserialize Two Custom Attributes", () => { - const schema = Schema.fromJsonSync(createSchemaJson(twoCustomAttributesJson, targetStubJson)); + const schema = Schema.fromJsonSync(createSchemaJson(twoCustomAttributesJson, targetStubJson), new SchemaContext()); testConstraint = schema.getItemSync("TestRelationship")!.source; expect(testConstraint).to.exist; expect(testConstraint.customAttributes!.get("TestSchema.TestCAClassA")).to.exist; @@ -150,11 +151,11 @@ describe("RelationshipConstraint", () => { }, ], }; - const schema = Schema.fromJsonSync(createSchemaJson(relConstraintJson, targetStubJson)); + const schema = Schema.fromJsonSync(createSchemaJson(relConstraintJson, targetStubJson), new SchemaContext()); testConstraint = schema.getItemSync("TestRelationship")!.source; expect(testConstraint).to.exist; - assert(testConstraint.customAttributes!.get("TestSchema.TestCAClassA")!.ShowClasses === false); - assert(testConstraint.customAttributes!.get("TestSchema.TestCAClassB")!.ShowClasses === true); + assert.isFalse(testConstraint.customAttributes!.get("TestSchema.TestCAClassA")!.ShowClasses); + assert.isTrue(testConstraint.customAttributes!.get("TestSchema.TestCAClassB")!.ShowClasses); }); }); }); diff --git a/core/ecschema-metadata/test/Metadata/Schema.test.ts b/core/ecschema-metadata/test/Metadata/Schema.test.ts index 1df1a22..2d67d40 100644 --- a/core/ecschema-metadata/test/Metadata/Schema.test.ts +++ b/core/ecschema-metadata/test/Metadata/Schema.test.ts @@ -5,36 +5,42 @@ import { assert, expect } from "chai"; -import { Schema, MutableSchema } from "./../../src/Metadata/Schema"; -import { ECClass } from "./../../src/Metadata/Class"; -import { EntityClass } from "./../../src/Metadata/EntityClass"; -import { Mixin } from "./../../src/Metadata/Mixin"; import { SchemaContext } from "./../../src/Context"; -import { StructClass } from "./../../src/Metadata/Class"; -import { ECObjectsError } from "./../../src/Exception"; import { SchemaMatchType } from "./../../src/ECObjects"; +import { ECObjectsError } from "./../../src/Exception"; +import { ECClass, StructClass } from "./../../src/Metadata/Class"; +import { EntityClass } from "./../../src/Metadata/EntityClass"; +import { Mixin } from "./../../src/Metadata/Mixin"; +import { MutableSchema, Schema } from "./../../src/Metadata/Schema"; import { SchemaKey } from "./../../src/SchemaKey"; +import { AnySchemaItem } from "../../src/Interfaces"; +import { Enumeration } from "./../../src/Metadata/Enumeration"; +import { Format } from "./../../src/Metadata/Format"; +import { KindOfQuantity } from "./../../src/Metadata/KindOfQuantity"; +import { PropertyCategory } from "./../../src/Metadata/PropertyCategory"; +import { Unit } from "./../../src/Metadata/Unit"; describe("Schema", () => { describe("api creation of schema", () => { it("with only the essentials", () => { - const testSchema = new Schema("TestSchemaCreation", 10, 99, 15); - assert.equal(testSchema.name, "TestSchemaCreation"); - assert.equal(testSchema.readVersion, 10); - assert.equal(testSchema.writeVersion, 99); - assert.equal(testSchema.minorVersion, 15); + const testSchema = new Schema(new SchemaContext(), "TestSchemaCreation", 10, 99, 15); + assert.strictEqual(testSchema.name, "TestSchemaCreation"); + assert.strictEqual(testSchema.readVersion, 10); + assert.strictEqual(testSchema.writeVersion, 99); + assert.strictEqual(testSchema.minorVersion, 15); }); it("with invalid version numbers should fail", () => { - expect(() => { new Schema("NewSchemaWithInvalidReadVersion", 123, 4, 5); }).to.throw(ECObjectsError); - expect(() => { new Schema("NewSchemaWithInvalidWriteVersion", 12, 345, 6); }).to.throw(ECObjectsError); - expect(() => { new Schema("NewSchemaWithInvalidMinorVersion", 12, 34, 567); }).to.throw(ECObjectsError); + const context = new SchemaContext(); + expect(() => { new Schema(context, "NewSchemaWithInvalidReadVersion", 123, 4, 5); }).to.throw(ECObjectsError); + expect(() => { new Schema(context, "NewSchemaWithInvalidWriteVersion", 12, 345, 6); }).to.throw(ECObjectsError); + expect(() => { new Schema(context, "NewSchemaWithInvalidMinorVersion", 12, 34, 567); }).to.throw(ECObjectsError); }); }); describe("create schema items", () => { it("should succeed for entity class", async () => { - const testSchema = new Schema("TestSchema", 1, 1, 1); + const testSchema = new Schema(new SchemaContext(), "TestSchema", 1, 1, 1); await (testSchema as MutableSchema).createEntityClass("TestEntity"); expect(await testSchema.getItem("TestEntity")).instanceof(ECClass); @@ -42,7 +48,7 @@ describe("Schema", () => { }); it("should succeed for mixin class", async () => { - const testSchema = new Schema("TestSchema", 1, 2, 3); + const testSchema = new Schema(new SchemaContext(), "TestSchema", 1, 2, 3); await (testSchema as MutableSchema).createMixinClass("TestMixin"); expect(await testSchema.getItem("TestMixin")).instanceof(ECClass); @@ -50,15 +56,33 @@ describe("Schema", () => { }); it("should succeed for struct class", async () => { - const testSchema = new Schema("TestSchema", 1, 2, 3); + const testSchema = new Schema(new SchemaContext(), "TestSchema", 1, 2, 3); await (testSchema as MutableSchema).createStructClass("TestStruct"); expect(await testSchema.getItem("TestStruct")).instanceof(ECClass); expect(await testSchema.getItem("TestStruct")).instanceof(StructClass); }); + it("should succeed for non-class schema items", async () => { + const testSchema = new Schema(new SchemaContext(), "TestSchema", 1, 2, 3); + await (testSchema as MutableSchema).createKindOfQuantity("TestKindOfQuantity"); + await (testSchema as MutableSchema).createEnumeration("TestEnumeration"); + await (testSchema as MutableSchema).createUnit("TestUnit"); + await (testSchema as MutableSchema).createPropertyCategory("TestPropertyCategory"); + await (testSchema as MutableSchema).createFormat("TestFormat"); + + const schemaItems = testSchema.getItems(); + + expect(schemaItems.next().value).instanceOf(KindOfQuantity); + expect(schemaItems.next().value).instanceOf(Enumeration); + expect(schemaItems.next().value).instanceOf(Unit); + expect(schemaItems.next().value).instanceOf(PropertyCategory); + expect(schemaItems.next().value).instanceOf(Format); + expect(schemaItems.next().done).to.eql(true); + }); + it("should succeed with case-insensitive search", async () => { - const testSchema = new Schema("TestSchema", 1, 0, 0); + const testSchema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); await (testSchema as MutableSchema).createEntityClass("testEntity"); expect(await testSchema.getItem("TESTENTITY")).not.undefined; @@ -67,6 +91,63 @@ describe("Schema", () => { }); }); + describe("bulk get methods for schema items", () => { + let testSchema: Schema; + + before(async () => { + testSchema = new Schema(new SchemaContext(), "TestSchema", 1, 2, 3); + await (testSchema as MutableSchema).createEntityClass("TestEntity"); + await (testSchema as MutableSchema).createMixinClass("TestMixin"); + await (testSchema as MutableSchema).createStructClass("TestStruct"); + await (testSchema as MutableSchema).createKindOfQuantity("TestKindOfQuantity"); + await (testSchema as MutableSchema).createEnumeration("TestEnumeration"); + await (testSchema as MutableSchema).createUnit("TestUnit"); + await (testSchema as MutableSchema).createPropertyCategory("TestPropertyCategory"); + await (testSchema as MutableSchema).createFormat("TestFormat"); + }); + + describe("getItems", () => { + let schemaItems: IterableIterator; + + before(() => { + schemaItems = testSchema.getItems(); + }); + + it("should return all schema items in schema", () => { + const itemArray = Array.from(testSchema.getItems()); + expect(itemArray.length).to.eql(8); + + expect(schemaItems.next().value).instanceOf(EntityClass); + expect(schemaItems.next().value).instanceOf(Mixin); + expect(schemaItems.next().value).instanceOf(StructClass); + expect(schemaItems.next().value).instanceOf(KindOfQuantity); + expect(schemaItems.next().value).instanceOf(Enumeration); + expect(schemaItems.next().value).instanceOf(Unit); + expect(schemaItems.next().value).instanceOf(PropertyCategory); + expect(schemaItems.next().value).instanceOf(Format); + expect(schemaItems.next().done).to.eql(true); + }); + }); + + describe("getClasses", () => { + let schemaClasses: IterableIterator; + + before(() => { + schemaClasses = testSchema.getClasses(); + }); + + it("should return only class items in schema", async () => { + const classArray = Array.from(testSchema.getClasses()); + expect(classArray.length).to.eql(3); + + expect(schemaClasses.next().value).instanceOf(EntityClass); + expect(schemaClasses.next().value).instanceOf(Mixin); + expect(schemaClasses.next().value).instanceOf(StructClass); + expect(schemaClasses.next().done).to.eql(true); + }); + }); + }); + describe("fromJson", () => { describe("should successfully deserialize valid JSON", () => { function assertValidSchema(testSchema: Schema) { @@ -81,14 +162,14 @@ describe("Schema", () => { it("with name/version first specified in JSON", async () => { const propertyJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "ValidSchema", version: "1.2.3", alias: "vs", label: "SomeDisplayLabel", description: "A really long description...", }; - const testSchema = new Schema(); + const testSchema = new Schema(new SchemaContext()); expect(testSchema).to.exist; await testSchema.deserialize(propertyJson); assertValidSchema(testSchema); @@ -96,14 +177,14 @@ describe("Schema", () => { it("with name/version repeated in JSON", async () => { const propertyJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "ValidSchema", version: "1.2.3", alias: "vs", label: "SomeDisplayLabel", description: "A really long description...", }; - const testSchema = new Schema("ValidSchema", 1, 2, 3); + const testSchema = new Schema(new SchemaContext(), "ValidSchema", 1, 2, 3); expect(testSchema).to.exist; await testSchema.deserialize(propertyJson); assertValidSchema(testSchema); @@ -115,132 +196,129 @@ describe("Schema", () => { name: "InvalidSchema", version: "1.2.3", }; - const testSchema = new Schema("BadSchema", 1, 2, 3); + const context = new SchemaContext(); + const testSchema = new Schema(context, "InvalidSchema", 1, 2, 3); expect(testSchema).to.exist; - await expect(testSchema.deserialize(schemaJson as any)).to.be.rejectedWith(ECObjectsError); + await expect(testSchema.deserialize(schemaJson as any)).to.be.rejectedWith(ECObjectsError, "The Schema InvalidSchema has an unsupported namespace 'https://badmetaschema.com'."); + await expect(Schema.fromJson(schemaJson as any, context)).to.be.rejectedWith(ECObjectsError, "The Schema InvalidSchema has an unsupported namespace 'https://badmetaschema.com'."); }); it("should throw for mismatched name", async () => { const json = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "ThisDoesNotMatch", version: "1.2.3", alias: "bad", }; - const testSchema = new Schema("BadSchema", 1, 2, 3); + const testSchema = new Schema(new SchemaContext(), "BadSchema", 1, 2, 3); expect(testSchema).to.exist; await expect(testSchema.deserialize(json)).to.be.rejectedWith(ECObjectsError); }); it("should throw for mismatched version", async () => { const json = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "BadSchema", version: "1.2.6", alias: "bad", }; - const testSchema = new Schema("BadSchema", 1, 2, 3); + const testSchema = new Schema(new SchemaContext(), "BadSchema", 1, 2, 3); expect(testSchema).to.exist; await expect(testSchema.deserialize(json)).to.be.rejectedWith(ECObjectsError); }); }); describe("toJSON", () => { it("Simple serialization", async () => { - const propertyJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + const schemaJson = { + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "ValidSchema", version: "1.2.3", alias: "vs", label: "SomeDisplayLabel", description: "A really long description...", }; - const testSchema = new Schema("ValidSchema", 1, 2, 3); + const testSchema = new Schema(new SchemaContext(), "ValidSchema", 1, 2, 3); expect(testSchema).to.exist; - await testSchema.deserialize(propertyJson); + await testSchema.deserialize(schemaJson); const serialized = testSchema.toJson(); - assert(serialized.$schema, "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema"); - assert(serialized.name, "ValidSchema"); - assert(serialized.version, "01.02.03"); - assert(serialized.alias, "vs"); - assert(serialized.label, "SomeDisplayLabel"); - assert(serialized.description, "A really long description..."); + expect(serialized).to.deep.equal({ ...schemaJson, version: "01.02.03" }); }); it("Serialization with one custom attribute- only class name", async () => { const propertyJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "ValidSchema", version: "1.2.3", alias: "vs", label: "SomeDisplayLabel", description: "A really long description...", }; - const testSchema = new Schema("ValidSchema", 1, 2, 3); + const testSchema = new Schema(new SchemaContext(), "ValidSchema", 1, 2, 3); expect(testSchema).to.exist; await testSchema.deserialize(propertyJson); (testSchema as MutableSchema).addCustomAttribute({ className: "CoreCustomAttributes.HiddenSchema" }); const serialized = testSchema.toJson(); - assert(serialized.customAttributes[0].className === "CoreCustomAttributes.HiddenSchema"); + assert.strictEqual(serialized.customAttributes[0].className, "CoreCustomAttributes.HiddenSchema"); }); it("Serialization with one custom attribute- additional properties", () => { const propertyJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "ValidSchema", version: "1.2.3", alias: "vs", label: "SomeDisplayLabel", description: "A really long description...", }; - const testSchema = new Schema("ValidSchema", 1, 2, 3); + const testSchema = new Schema(new SchemaContext(), "ValidSchema", 1, 2, 3); expect(testSchema).to.exist; testSchema.deserializeSync(propertyJson); (testSchema as MutableSchema).addCustomAttribute({ className: "CoreCustomAttributes.HiddenSchema", ShowClasses: true }); const serialized = testSchema.toJson(); - assert(serialized.customAttributes[0].className === "CoreCustomAttributes.HiddenSchema"); - assert(serialized.customAttributes[0].ShowClasses === true); + assert.strictEqual(serialized.customAttributes[0].className, "CoreCustomAttributes.HiddenSchema"); + assert.isTrue(serialized.customAttributes[0].ShowClasses); }); it("Serialization with multiple custom attributes- only class name", async () => { const propertyJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "ValidSchema", version: "1.2.3", alias: "vs", label: "SomeDisplayLabel", description: "A really long description...", }; - const testSchema = new Schema("ValidSchema", 1, 2, 3); + const testSchema = new Schema(new SchemaContext(), "ValidSchema", 1, 2, 3); expect(testSchema).to.exist; await testSchema.deserialize(propertyJson); (testSchema as MutableSchema).addCustomAttribute({ className: "CoreCustomAttributes.HiddenSchema" }); (testSchema as MutableSchema).addCustomAttribute({ className: "CoreAttributes.HiddenSchema" }); (testSchema as MutableSchema).addCustomAttribute({ className: "CoreCustom.HiddenSchema" }); const serialized = testSchema.toJson(); - assert(serialized.customAttributes[0].className === "CoreCustomAttributes.HiddenSchema"); - assert(serialized.customAttributes[1].className === "CoreAttributes.HiddenSchema"); - assert(serialized.customAttributes[2].className === "CoreCustom.HiddenSchema"); + assert.strictEqual(serialized.customAttributes[0].className, "CoreCustomAttributes.HiddenSchema"); + assert.strictEqual(serialized.customAttributes[1].className, "CoreAttributes.HiddenSchema"); + assert.strictEqual(serialized.customAttributes[2].className, "CoreCustom.HiddenSchema"); }); it("Serialization with multiple custom attributes- additional properties", async () => { const propertyJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "ValidSchema", version: "1.2.3", alias: "vs", label: "SomeDisplayLabel", description: "A really long description...", }; - const testSchema = new Schema("ValidSchema", 1, 2, 3); + const testSchema = new Schema(new SchemaContext(), "ValidSchema", 1, 2, 3); expect(testSchema).to.exist; await testSchema.deserialize(propertyJson); (testSchema as MutableSchema).addCustomAttribute({ className: "CoreCustomAttributes.HiddenSchema", ShowClasses: true }); (testSchema as MutableSchema).addCustomAttribute({ className: "CoreAttributes.HiddenSchema", FloatValue: 1.2 }); (testSchema as MutableSchema).addCustomAttribute({ className: "CoreCustom.HiddenSchema", IntegerValue: 5 }); const serialized = testSchema.toJson(); - assert(serialized.customAttributes[0].ShowClasses === true); - assert(serialized.customAttributes[1].FloatValue === 1.2); - assert(serialized.customAttributes[2].IntegerValue === 5); + assert.isTrue(serialized.customAttributes[0].ShowClasses); + assert.strictEqual(serialized.customAttributes[1].FloatValue, 1.2); + assert.strictEqual(serialized.customAttributes[2].IntegerValue, 5); }); it("Serialization with one reference", async () => { const schemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "ValidSchema", version: "1.2.3", alias: "vs", @@ -253,20 +331,20 @@ describe("Schema", () => { }, ], }; - const refSchema = new Schema("RefSchema", 1, 0, 0); + const refSchema = new Schema(new SchemaContext(), "RefSchema", 1, 0, 0); const context = new SchemaContext(); await context.addSchema(refSchema); - let testSchema = new Schema("ValidSchema", 1, 2, 3); + let testSchema = new Schema(new SchemaContext(), "ValidSchema", 1, 2, 3); testSchema = await Schema.fromJson(schemaJson, context); expect(testSchema).to.exist; const entityClassJson = testSchema.toJson(); assert.isDefined(entityClassJson); - assert(entityClassJson.references[0].name === "RefSchema"); - assert(entityClassJson.references[0].version === "01.00.00"); + assert.strictEqual(entityClassJson.references[0].name, "RefSchema"); + assert.strictEqual(entityClassJson.references[0].version, "01.00.00"); }); it("Serialization with multiple references", () => { const schemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "ValidSchema", version: "1.2.3", alias: "vs", @@ -283,24 +361,24 @@ describe("Schema", () => { }, ], }; - const refSchema = new Schema("RefSchema", 1, 0, 0); - const anotherRefSchema = new Schema("AnotherRefSchema", 1, 0, 2); const context = new SchemaContext(); + const refSchema = new Schema(context, "RefSchema", 1, 0, 0); + const anotherRefSchema = new Schema(context, "AnotherRefSchema", 1, 0, 2); context.addSchemaSync(refSchema); context.addSchemaSync(anotherRefSchema); - let testSchema = new Schema("ValidSchema", 1, 2, 3); + let testSchema = new Schema(context, "ValidSchema", 1, 2, 3); testSchema = Schema.fromJsonSync(schemaJson, context); expect(testSchema).to.exist; const entityClassJson = testSchema.toJson(); assert.isDefined(entityClassJson); - assert(entityClassJson.references[0].name === "RefSchema"); - assert(entityClassJson.references[0].version === "01.00.00"); - assert(entityClassJson.references[1].name === "AnotherRefSchema"); - assert(entityClassJson.references[1].version === "01.00.02"); + assert.strictEqual(entityClassJson.references[0].name, "RefSchema"); + assert.strictEqual(entityClassJson.references[0].version, "01.00.00"); + assert.strictEqual(entityClassJson.references[1].name, "AnotherRefSchema"); + assert.strictEqual(entityClassJson.references[1].version, "01.00.02"); }); it("Serialization with one reference and item", async () => { const schemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", references: [ @@ -318,23 +396,23 @@ describe("Schema", () => { }, }; - const refSchema = new Schema("RefSchema", 1, 0, 5); + const context = new SchemaContext(); + const refSchema = new Schema(context, "RefSchema", 1, 0, 5); const refBaseClass = await (refSchema as MutableSchema).createEntityClass("BaseClassInRef"); assert.isDefined(refBaseClass); - const context = new SchemaContext(); await context.addSchema(refSchema); - let testSchema = new Schema("TestSchema", 1, 2, 3); + let testSchema = new Schema(context, "TestSchema", 1, 2, 3); testSchema = await Schema.fromJson(schemaJson, context); const entityClassJson = testSchema.toJson(); assert.isDefined(entityClassJson); assert.isDefined(entityClassJson.items.testClass); - assert(entityClassJson.items.testClass.schemaItemType, "EntityClass"); - assert(entityClassJson.items.testClass.label, "ExampleEntity"); - assert(entityClassJson.items.testClass.description, "An example entity class."); + assert.strictEqual(entityClassJson.items.testClass.schemaItemType, "EntityClass"); + assert.strictEqual(entityClassJson.items.testClass.label, "ExampleEntity"); + assert.strictEqual(entityClassJson.items.testClass.description, "An example entity class."); }); it("Serialization with one reference and multiple items", async () => { const schemaJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", references: [ @@ -381,29 +459,29 @@ describe("Schema", () => { }, }; - const refSchema = new Schema("RefSchema", 1, 0, 5); + const context = new SchemaContext(); + const refSchema = new Schema(context, "RefSchema", 1, 0, 5); const refBaseClass = await (refSchema as MutableSchema).createEntityClass("BaseClassInRef"); assert.isDefined(refBaseClass); - const context = new SchemaContext(); await context.addSchema(refSchema); - let testSchema = new Schema("TestSchema", 1, 2, 3); + let testSchema = new Schema(context, "TestSchema", 1, 2, 3); testSchema = await Schema.fromJson(schemaJson, context); const entityClassJson = testSchema.toJson(); assert.isDefined(entityClassJson); assert.isDefined(entityClassJson.items.testClass); - assert(entityClassJson.items.testClass.schemaItemType, "EntityClass"); - assert(entityClassJson.items.testClass.label, "ExampleEntity"); - assert(entityClassJson.items.testClass.description, "An example entity class."); + assert.strictEqual(entityClassJson.items.testClass.schemaItemType, "EntityClass"); + assert.strictEqual(entityClassJson.items.testClass.label, "ExampleEntity"); + assert.strictEqual(entityClassJson.items.testClass.description, "An example entity class."); assert.isDefined(entityClassJson.items.ExampleMixin); - assert(entityClassJson.items.ExampleMixin.schemaItemType, "Mixin"); + assert.strictEqual(entityClassJson.items.ExampleMixin.schemaItemType, "Mixin"); assert.isDefined(entityClassJson.items.ExampleStruct); - assert(entityClassJson.items.ExampleMixin.schemaItemType, "Mixin"); + assert.strictEqual(entityClassJson.items.ExampleMixin.schemaItemType, "Mixin"); assert.isDefined(entityClassJson.items.testEnum); - assert(entityClassJson.items.testEnum.schemaItemType, "Enumeration"); + assert.strictEqual(entityClassJson.items.testEnum.schemaItemType, "Enumeration"); }); }); }); // Schema tests @@ -493,10 +571,11 @@ describe("Schema", () => { // for more comprehensive cases. describe("compareByVersion", () => { it("exact match, returns zero", async () => { - const leftSchema = new Schema("LeftSchema", 1, 2, 3); - const rightSchema = new Schema("RightSchema", 1, 2, 3); + const context = new SchemaContext(); + const leftSchema = new Schema(context, "LeftSchema", 1, 2, 3); + const rightSchema = new Schema(context, "RightSchema", 1, 2, 3); const result = leftSchema.schemaKey.compareByVersion(rightSchema.schemaKey); - assert.equal(result, 0); + assert.strictEqual(result, 0); }); }); }); diff --git a/core/ecschema-metadata/test/Metadata/SchemaItem.test.ts b/core/ecschema-metadata/test/Metadata/SchemaItem.test.ts index 77ce7db..cc0678c 100644 --- a/core/ecschema-metadata/test/Metadata/SchemaItem.test.ts +++ b/core/ecschema-metadata/test/Metadata/SchemaItem.test.ts @@ -8,18 +8,21 @@ import { assert, expect } from "chai"; import { Schema } from "./../../src/Metadata/Schema"; import { SchemaKey, SchemaItemKey } from "./../../src/SchemaKey"; import { EntityClass } from "./../../src/Metadata/EntityClass"; +import { SchemaContext } from "../../src/Context"; describe("SchemaItem", () => { describe("toJson", () => { let baseClass: any; let schema; + before(() => { - schema = new Schema("ExampleSchema", 1, 0, 0); + schema = new Schema(new SchemaContext(), "ExampleSchema", 1, 0, 0); baseClass = new EntityClass(schema, "ExampleEntity"); }); + it("Serialize SchemaItem Standalone", async () => { const propertyJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/31/draft-01/schemaitem", + $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/schemaitem", schema: "ExampleSchema", version: "1.0.0", schemaItemType: "EntityClass", @@ -30,17 +33,17 @@ describe("SchemaItem", () => { await (baseClass as EntityClass).deserialize(propertyJson); const testClass = await (baseClass as EntityClass).toJson(true, true); expect(testClass).to.exist; - assert(testClass.$schema, "https://dev.bentley.com/json_schemas/ec/31/draft-01/schemaitem"); - assert(testClass.schema, "ExampleSchema"); - assert(testClass.schemaVersion, "1.0.0"); - assert(testClass.schemaItemType, "EntityClass"); - assert(testClass.name, "ExampleEntity"); - assert(testClass.label, "ExampleEntity"); - assert(testClass.description, "An example entity class."); + assert.strictEqual(testClass.$schema, "https://dev.bentley.com/json_schemas/ec/32/draft-01/schemaitem"); + assert.strictEqual(testClass.schema, "ExampleSchema"); + assert.strictEqual(testClass.schemaVersion, "01.00.00"); + assert.strictEqual(testClass.schemaItemType, "EntityClass"); + assert.strictEqual(testClass.name, "ExampleEntity"); + assert.strictEqual(testClass.label, "ExampleEntity"); + assert.strictEqual(testClass.description, "An example entity class."); }); it("Serialize SchemaItem", async () => { const schemaItemJson = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "ExampleSchema", version: "1.0.0", alias: "ex", @@ -52,14 +55,14 @@ describe("SchemaItem", () => { }, }, }; - const ecschema = await Schema.fromJson(schemaItemJson); + const ecschema = await Schema.fromJson(schemaItemJson, new SchemaContext()); const testEntity = await ecschema.getItem("ExampleEntity"); assert.isDefined(testEntity); const testClass = await testEntity!.toJson(true, true); expect(testClass).to.exist; - assert(testClass.schemaItemType, "EntityClass"); - assert(testClass.name, "ExampleEntity"); - assert(testClass.description, "An example entity class."); + assert.strictEqual(testClass.schemaItemType, "EntityClass"); + assert.strictEqual(testClass.name, "ExampleEntity"); + assert.strictEqual(testClass.description, "An example entity class."); }); }); }); diff --git a/core/ecschema-metadata/test/Metadata/Unit.test.ts b/core/ecschema-metadata/test/Metadata/Unit.test.ts index ad7dfef..e6e181d 100644 --- a/core/ecschema-metadata/test/Metadata/Unit.test.ts +++ b/core/ecschema-metadata/test/Metadata/Unit.test.ts @@ -4,36 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { assert, expect } from "chai"; -import * as sinon from "sinon"; import { createSchemaJsonWithItems } from "../TestUtils/DeserializationHelpers"; - import { Schema } from "../../src/Metadata/Schema"; import { Unit } from "../../src/Metadata/Unit"; import { Phenomenon } from "../../src/Metadata/Phenomenon"; import { UnitSystem } from "../../src/Metadata/UnitSystem"; import { ECObjectsError } from "../../src/Exception"; +import { SchemaContext } from "../../src/Context"; describe("Unit", () => { - describe("accept", () => { - let testUnit: Unit; - beforeEach(() => { - const schema = new Schema("TestSchema", 1, 0, 0); - testUnit = new Unit(schema, "TestUnit"); - }); - - it("should call visitUnit on a SchemaItemVisitor object", async () => { - expect(testUnit).to.exist; - const mockVisitor = { visitUnit: sinon.spy() }; - await testUnit.accept(mockVisitor); - expect(mockVisitor.visitUnit.calledOnce).to.be.true; - expect(mockVisitor.visitUnit.calledWithExactly(testUnit)).to.be.true; - }); - - it("should safely handle a SchemaItemVisitor without visitUnit defined", async () => { - expect(testUnit).to.exist; - await testUnit.accept({}); - }); - }); function createSchemaJson(unitJson: any): any { return createSchemaJsonWithItems({ @@ -64,7 +43,7 @@ describe("Unit", () => { }); it("async - should succeed with fully defined", async () => { - const ecSchema = await Schema.fromJson(fullyDefinedUnit); + const ecSchema = await Schema.fromJson(fullyDefinedUnit, new SchemaContext()); const unit = await ecSchema.getItem("TestUnit"); assert.isDefined(unit); @@ -83,17 +62,17 @@ describe("Unit", () => { }); it("sync - should succeed with fully defined", () => { - const ecSchema = Schema.fromJsonSync(fullyDefinedUnit); + const ecSchema = Schema.fromJsonSync(fullyDefinedUnit, new SchemaContext()); const unit = ecSchema.getItemSync("TestUnit"); assert.isDefined(unit); const phen = ecSchema.getItemSync("TestPhenomenon"); assert.isDefined(phen); - assert.equal(phen, ecSchema.getItemSync(unit!.phenomenon!.name)); + assert.strictEqual(phen, ecSchema.getItemSync(unit!.phenomenon!.name)); const unitSystem = ecSchema.getItemSync("TestUnitSystem"); assert.isDefined(unitSystem); - assert.equal(unitSystem, ecSchema.getItemSync(unit!.unitSystem!.name)); + assert.strictEqual(unitSystem, ecSchema.getItemSync(unit!.unitSystem!.name)); expect(unit!.definition).to.eql("[MILLI]*Units.MM"); expect(unit!.denominator).to.equal(1); @@ -120,7 +99,7 @@ describe("Unit", () => { }, }); it("async - order shouldn't matter", async () => { - const ecSchema = await Schema.fromJson(reverseOrderJson); + const ecSchema = await Schema.fromJson(reverseOrderJson, new SchemaContext()); assert.isDefined(ecSchema); assert.isDefined(await ecSchema.getItem("Length")); assert.isDefined(await ecSchema.getItem("Metric")); @@ -128,7 +107,7 @@ describe("Unit", () => { }); it("sync - should succeed with dependency order", () => { - const ecSchema = Schema.fromJsonSync(reverseOrderJson); + const ecSchema = Schema.fromJsonSync(reverseOrderJson, new SchemaContext()); assert.isDefined(ecSchema); assert.isDefined(ecSchema.getItemSync("Length")); assert.isDefined(ecSchema.getItemSync("Metric")); @@ -141,10 +120,10 @@ describe("Unit", () => { definition: "[MILLI]*Units.M", }; it("async - should throw for missing phenomenon", async () => { - await expect(Schema.fromJson(createSchemaJson(missingPhenomenonJson))).to.be.rejectedWith(ECObjectsError, `The Unit TestSchema.TestUnit does not have the required 'phenomenon' attribute.`); + await expect(Schema.fromJson(createSchemaJson(missingPhenomenonJson), new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The Unit TestSchema.TestUnit does not have the required 'phenomenon' attribute.`); }); it("sync - should throw for missing phenomenon", () => { - assert.throws(() => Schema.fromJsonSync(createSchemaJson(missingPhenomenonJson)), ECObjectsError, `The Unit TestSchema.TestUnit does not have the required 'phenomenon' attribute.`); + assert.throws(() => Schema.fromJsonSync(createSchemaJson(missingPhenomenonJson), new SchemaContext()), ECObjectsError, `The Unit TestSchema.TestUnit does not have the required 'phenomenon' attribute.`); }); // Invalid phenomenon @@ -154,10 +133,10 @@ describe("Unit", () => { definition: "[MILLI]*Units.M", }; it("async - should throw for invalid phenomenon", async () => { - await expect(Schema.fromJson(createSchemaJson(invalidPhenomenonJson))).to.be.rejectedWith(ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'phenomenon' attribute. It should be of type 'string'`); + await expect(Schema.fromJson(createSchemaJson(invalidPhenomenonJson), new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'phenomenon' attribute. It should be of type 'string'`); }); it("sync - should throw for invalid phenomenon", () => { - assert.throws(() => Schema.fromJsonSync(createSchemaJson(invalidPhenomenonJson)), ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'phenomenon' attribute. It should be of type 'string'`); + assert.throws(() => Schema.fromJsonSync(createSchemaJson(invalidPhenomenonJson), new SchemaContext()), ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'phenomenon' attribute. It should be of type 'string'`); }); // Missing UnitSystem @@ -166,10 +145,10 @@ describe("Unit", () => { definition: "[MILLI]*Units.M", }; it("async - should throw for missing unit system", async () => { - await expect(Schema.fromJson(createSchemaJson(missingUnitSystemJson))).to.be.rejectedWith(ECObjectsError, `The Unit TestSchema.TestUnit does not have the required 'unitSystem' attribute.`); + await expect(Schema.fromJson(createSchemaJson(missingUnitSystemJson), new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The Unit TestSchema.TestUnit does not have the required 'unitSystem' attribute.`); }); it("sync - should throw for missing unit system", () => { - assert.throws(() => Schema.fromJsonSync(createSchemaJson(missingUnitSystemJson)), ECObjectsError, `The Unit TestSchema.TestUnit does not have the required 'unitSystem' attribute.`); + assert.throws(() => Schema.fromJsonSync(createSchemaJson(missingUnitSystemJson), new SchemaContext()), ECObjectsError, `The Unit TestSchema.TestUnit does not have the required 'unitSystem' attribute.`); }); // Invalid UnitSystem @@ -179,10 +158,10 @@ describe("Unit", () => { definition: "[MILLI]*Units.M", }; it("async - should throw for invalid unit system", async () => { - await expect(Schema.fromJson(createSchemaJson(invalidUnitSystemJson))).to.be.rejectedWith(ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'unitSystem' attribute. It should be of type 'string'`); + await expect(Schema.fromJson(createSchemaJson(invalidUnitSystemJson), new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'unitSystem' attribute. It should be of type 'string'`); }); it("sync - should throw for invalid unit system", () => { - assert.throws(() => Schema.fromJsonSync(createSchemaJson(invalidUnitSystemJson)), ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'unitSystem' attribute. It should be of type 'string'`); + assert.throws(() => Schema.fromJsonSync(createSchemaJson(invalidUnitSystemJson), new SchemaContext()), ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'unitSystem' attribute. It should be of type 'string'`); }); // Missing Definition @@ -191,10 +170,10 @@ describe("Unit", () => { unitSystem: "TestSchema.TestUnitSystem", }; it("async - should throw for missing definition", async () => { - await expect(Schema.fromJson(createSchemaJson(missingDefinitionJson))).to.be.rejectedWith(ECObjectsError, `The Unit TestSchema.TestUnit does not have the required 'definition' attribute.`); + await expect(Schema.fromJson(createSchemaJson(missingDefinitionJson), new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The Unit TestSchema.TestUnit does not have the required 'definition' attribute.`); }); it("sync - should throw for missing definition", () => { - assert.throws(() => Schema.fromJsonSync(createSchemaJson(missingDefinitionJson)), ECObjectsError, `The Unit TestSchema.TestUnit does not have the required 'definition' attribute.`); + assert.throws(() => Schema.fromJsonSync(createSchemaJson(missingDefinitionJson), new SchemaContext()), ECObjectsError, `The Unit TestSchema.TestUnit does not have the required 'definition' attribute.`); }); // Missing Definition @@ -204,10 +183,10 @@ describe("Unit", () => { unitSystem: "TestSchema.TestUnitSystem", }; it("async - should throw for invalid definition", async () => { - await expect(Schema.fromJson(createSchemaJson(invalidDefinitionJson))).to.be.rejectedWith(ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'definition' attribute. It should be of type 'string'`); + await expect(Schema.fromJson(createSchemaJson(invalidDefinitionJson), new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'definition' attribute. It should be of type 'string'`); }); it("sync - should throw for invalid definition", () => { - assert.throws(() => Schema.fromJsonSync(createSchemaJson(invalidDefinitionJson)), ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'definition' attribute. It should be of type 'string'`); + assert.throws(() => Schema.fromJsonSync(createSchemaJson(invalidDefinitionJson), new SchemaContext()), ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'definition' attribute. It should be of type 'string'`); }); // Invalid numerator @@ -218,10 +197,10 @@ describe("Unit", () => { numerator: "5", }; it("async - should throw for invalid numerator", async () => { - await expect(Schema.fromJson(createSchemaJson(invalidNumeratorJson))).to.be.rejectedWith(ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'numerator' attribute. It should be of type 'number'.`); + await expect(Schema.fromJson(createSchemaJson(invalidNumeratorJson), new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'numerator' attribute. It should be of type 'number'.`); }); it("sync - should throw for invalid numerator", () => { - assert.throws(() => Schema.fromJsonSync(createSchemaJson(invalidNumeratorJson)), ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'numerator' attribute. It should be of type 'number'.`); + assert.throws(() => Schema.fromJsonSync(createSchemaJson(invalidNumeratorJson), new SchemaContext()), ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'numerator' attribute. It should be of type 'number'.`); }); // Invalid denominator @@ -232,10 +211,10 @@ describe("Unit", () => { denominator: "5", }; it("async - should throw for invalid denominator", async () => { - await expect(Schema.fromJson(createSchemaJson(invalidDenominatorJson))).to.be.rejectedWith(ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'denominator' attribute. It should be of type 'number'.`); + await expect(Schema.fromJson(createSchemaJson(invalidDenominatorJson), new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'denominator' attribute. It should be of type 'number'.`); }); it("sync - should throw for invalid denominator", () => { - assert.throws(() => Schema.fromJsonSync(createSchemaJson(invalidDenominatorJson)), ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'denominator' attribute. It should be of type 'number'.`); + assert.throws(() => Schema.fromJsonSync(createSchemaJson(invalidDenominatorJson), new SchemaContext()), ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'denominator' attribute. It should be of type 'number'.`); }); // Invalid offset @@ -246,10 +225,10 @@ describe("Unit", () => { offset: "5", }; it("async - should throw for invalid offset", async () => { - await expect(Schema.fromJson(createSchemaJson(invalidOffsetJson))).to.be.rejectedWith(ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'offset' attribute. It should be of type 'number'.`); + await expect(Schema.fromJson(createSchemaJson(invalidOffsetJson), new SchemaContext())).to.be.rejectedWith(ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'offset' attribute. It should be of type 'number'.`); }); it("sync - should throw for invalid offset", () => { - assert.throws(() => Schema.fromJsonSync(createSchemaJson(invalidOffsetJson)), ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'offset' attribute. It should be of type 'number'.`); + assert.throws(() => Schema.fromJsonSync(createSchemaJson(invalidOffsetJson), new SchemaContext()), ECObjectsError, `The Unit TestSchema.TestUnit has an invalid 'offset' attribute. It should be of type 'number'.`); }); }); describe("toJson", () => { @@ -265,7 +244,7 @@ describe("Unit", () => { }); it("async - should succeed with fully defined", async () => { - const ecSchema = await Schema.fromJson(fullyDefinedUnit); + const ecSchema = await Schema.fromJson(fullyDefinedUnit, new SchemaContext()); const unit = await ecSchema.getItem("TestUnit"); assert.isDefined(unit); const unitSerialization = unit!.toJson(true, true); @@ -279,7 +258,7 @@ describe("Unit", () => { }); it("sync - should succeed with fully defined", () => { - const ecSchema = Schema.fromJsonSync(fullyDefinedUnit); + const ecSchema = Schema.fromJsonSync(fullyDefinedUnit, new SchemaContext()); const unit = ecSchema.getItemSync("TestUnit"); assert.isDefined(unit); const unitSerialization = unit!.toJson(true, true); @@ -311,7 +290,7 @@ describe("Unit", () => { }, }); it("async - order shouldn't matter", async () => { - const ecSchema = await Schema.fromJson(reverseOrderJson); + const ecSchema = await Schema.fromJson(reverseOrderJson, new SchemaContext()); assert.isDefined(ecSchema); const unit = await ecSchema.getItem("M"); assert.isDefined(unit); @@ -323,7 +302,7 @@ describe("Unit", () => { }); it("sync - should succeed with dependency order", () => { - const ecSchema = Schema.fromJsonSync(reverseOrderJson); + const ecSchema = Schema.fromJsonSync(reverseOrderJson, new SchemaContext()); assert.isDefined(ecSchema); const unit = ecSchema.getItemSync("M"); assert.isDefined(unit); diff --git a/core/ecschema-metadata/test/Metadata/UnitSystem.test.ts b/core/ecschema-metadata/test/Metadata/UnitSystem.test.ts index 7fb4725..5afad35 100644 --- a/core/ecschema-metadata/test/Metadata/UnitSystem.test.ts +++ b/core/ecschema-metadata/test/Metadata/UnitSystem.test.ts @@ -3,46 +3,28 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { assert, expect } from "chai"; -import * as sinon from "sinon"; +import { expect } from "chai"; + +import { SchemaContext } from "../../src/Context"; +import { SchemaItemType, schemaItemTypeToString } from "../../src/ECObjects"; import { Schema } from "../../src/Metadata/Schema"; import { UnitSystem } from "../../src/Metadata/UnitSystem"; -import { schemaItemTypeToString, SchemaItemType } from "../../src/ECObjects"; describe("UnitSystem tests", () => { let testUnitSystem: UnitSystem; - describe("accept", () => { - beforeEach(() => { - const schema = new Schema("TestSchema", 1, 0, 0); - testUnitSystem = new UnitSystem(schema, "TestEnumeration"); - }); - - it("should call visitUnitSystem on a SchemaItemVisitor object", async () => { - expect(testUnitSystem).to.exist; - const mockVisitor = { visitUnitSystem: sinon.spy() }; - await testUnitSystem.accept(mockVisitor); - expect(mockVisitor.visitUnitSystem.calledOnce).to.be.true; - expect(mockVisitor.visitUnitSystem.calledWithExactly(testUnitSystem)).to.be.true; - }); - - it("should safely handle a SchemaItemVisitor without visitUnitSystem defined", async () => { - expect(testUnitSystem).to.exist; - await testUnitSystem.accept({}); - }); - }); describe("SchemaItemType", () => { - const schema = new Schema("TestSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); testUnitSystem = new UnitSystem(schema, "Test"); it("should return correct item type and string", () => { - assert.equal(testUnitSystem.schemaItemType, SchemaItemType.UnitSystem); - assert.equal(schemaItemTypeToString(testUnitSystem.schemaItemType), "UnitSystem"); + expect(testUnitSystem.schemaItemType).to.equal(SchemaItemType.UnitSystem); + expect(schemaItemTypeToString(testUnitSystem.schemaItemType)).to.equal("UnitSystem"); }); }); describe("Async fromJson", () => { beforeEach(() => { - const schema = new Schema("ExampleSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "ExampleSchema", 1, 0, 0); testUnitSystem = new UnitSystem(schema, "IMPERIAL"); }); it("Basic test", async () => { @@ -53,13 +35,13 @@ describe("UnitSystem tests", () => { label: "Imperial", }; await testUnitSystem.deserialize(json); - assert(testUnitSystem.label, "Imperial"); - assert(testUnitSystem.description === undefined); + expect(testUnitSystem.label).to.equal("Imperial"); + expect(testUnitSystem.description).to.be.undefined; }); describe("Sync fromJson", () => { beforeEach(() => { - const schema = new Schema("ExampleSchema", 1, 0, 0); + const schema = new Schema(new SchemaContext(), "ExampleSchema", 1, 0, 0); testUnitSystem = new UnitSystem(schema, "IMPERIAL"); }); it("Basic test", () => { @@ -70,8 +52,8 @@ describe("UnitSystem tests", () => { label: "Imperial", }; testUnitSystem.deserializeSync(json); - assert(testUnitSystem.label, "Imperial"); - assert(testUnitSystem.description === undefined); + expect(testUnitSystem.label).to.equal("Imperial"); + expect(testUnitSystem.description).to.be.undefined; }); }); }); diff --git a/core/ecschema-metadata/test/SchemaGraphUtil.test.ts b/core/ecschema-metadata/test/SchemaGraphUtil.test.ts index 3e94611..acaf7e0 100644 --- a/core/ecschema-metadata/test/SchemaGraphUtil.test.ts +++ b/core/ecschema-metadata/test/SchemaGraphUtil.test.ts @@ -26,24 +26,24 @@ describe("SchemaGraphUtil tests:", () => { // ensure refs in wrong order for valid test await importSchema!.references[0]; - assert.equal(importSchema!.references[0].name, "SchemaC"); - assert.equal(importSchema!.references[1].name, "SchemaB"); + assert.strictEqual(importSchema!.references[0].name, "SchemaC"); + assert.strictEqual(importSchema!.references[1].name, "SchemaB"); // Act const schemaList = SchemaGraphUtil.buildDependencyOrderedSchemaList(importSchema!); // Assert - assert.equal(schemaList.length, 4); - assert.equal(schemaList[0].name, "SchemaD"); - assert.equal(schemaList[1].name, "SchemaC"); - assert.equal(schemaList[2].name, "SchemaB"); - assert.equal(schemaList[3].name, "SchemaA"); + assert.strictEqual(schemaList.length, 4); + assert.strictEqual(schemaList[0].name, "SchemaD"); + assert.strictEqual(schemaList[1].name, "SchemaC"); + assert.strictEqual(schemaList[2].name, "SchemaB"); + assert.strictEqual(schemaList[3].name, "SchemaA"); }); it("buildDependencyOrderedSchemaList with same schema references, contains schema once", () => { // Arrange - const importSchema = new Schema(new SchemaKey("SchemaA", 1, 0, 0)); - const refSchema = new Schema(new SchemaKey("SchemaB", 1, 0, 0)); + const importSchema = new Schema(context, new SchemaKey("SchemaA", 1, 0, 0)); + const refSchema = new Schema(context, new SchemaKey("SchemaB", 1, 0, 0)); importSchema.references.push(refSchema); importSchema.references.push(refSchema); @@ -51,6 +51,6 @@ describe("SchemaGraphUtil tests:", () => { const schemaList = SchemaGraphUtil.buildDependencyOrderedSchemaList(importSchema); // Assert - assert.equal(schemaList.length, 2); + assert.strictEqual(schemaList.length, 2); }); }); diff --git a/core/ecschema-metadata/test/SchemaPartVisitorDelegate.test.ts b/core/ecschema-metadata/test/SchemaPartVisitorDelegate.test.ts new file mode 100644 index 0000000..ebc0bf8 --- /dev/null +++ b/core/ecschema-metadata/test/SchemaPartVisitorDelegate.test.ts @@ -0,0 +1,851 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { expect } from "chai"; +import { SchemaPartVisitorDelegate } from "../src/SchemaPartVisitorDelegate"; +import sinon = require("sinon"); +import { Schema } from "../src/Metadata/Schema"; +import { EntityClass } from "../src/Metadata/EntityClass"; +import { Constant } from "../src/Metadata/Constant"; +import { CustomAttributeClass } from "../src/Metadata/CustomAttributeClass"; +import { Enumeration } from "../src/Metadata/Enumeration"; +import { Format } from "../src/Metadata/Format"; +import { InvertedUnit } from "../src/Metadata/InvertedUnit"; +import { KindOfQuantity } from "../src/Metadata/KindOfQuantity"; +import { Mixin } from "../src/Metadata/Mixin"; +import { Phenomenon } from "../src/Metadata/Phenomenon"; +import { PropertyCategory } from "../src/Metadata/PropertyCategory"; +import { RelationshipClass, RelationshipConstraint } from "../src/Metadata/RelationshipClass"; +import { StructClass, MutableClass, ECClass } from "../src/Metadata/Class"; +import { Unit } from "../src/Metadata/Unit"; +import { UnitSystem } from "../src/Metadata/UnitSystem"; +import { PrimitiveType, RelationshipEnd } from "../src/ECObjects"; +import { SchemaContext } from "../src/Context"; + +describe("SchemaPartVisitorDelegate Tests", () => { + let schema: Schema; + let helper: SchemaPartVisitorDelegate; + let mockVisitor: any; + + beforeEach(() => { + schema = new Schema(new SchemaContext(), "Test", 1, 2, 3); + }); + + describe("visitSchema", () => { + it("full schema is false, visitEmptySchema called once.", async () => { + mockVisitor = { visitEmptySchema: sinon.spy(), visitFullSchema: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchema(schema, false); + expect(mockVisitor.visitEmptySchema.calledOnceWithExactly(schema)).to.be.true; + expect(mockVisitor.visitFullSchema.notCalled).to.be.true; + }); + + it("full schema is true, visitFullSchema called once.", async () => { + mockVisitor = { visitEmptySchema: sinon.spy(), visitFullSchema: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchema(schema); + expect(mockVisitor.visitFullSchema.calledOnceWithExactly(schema)).to.be.true; + expect(mockVisitor.visitEmptySchema.notCalled).to.be.true; + }); + }); + + describe("visitSchemaSync", () => { + it("full schema is false, visitEmptySchemaSync called once.", () => { + mockVisitor = { visitEmptySchemaSync: sinon.spy(), visitFullSchemaSync: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaSync(schema, false); + expect(mockVisitor.visitEmptySchemaSync.calledOnceWithExactly(schema)).to.be.true; + expect(mockVisitor.visitFullSchemaSync.notCalled).to.be.true; + }); + + it("full schema is true, visitFullSchemaSync called once.", () => { + mockVisitor = { visitEmptySchemaSync: sinon.spy(), visitFullSchemaSync: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaSync(schema); + expect(mockVisitor.visitFullSchemaSync.calledOnceWithExactly(schema)).to.be.true; + expect(mockVisitor.visitEmptySchemaSync.notCalled).to.be.true; + }); + }); + + describe("visitSchemaPart", () => { + beforeEach(() => { + mockVisitor = { + visitSchemaItem: sinon.spy(), + visitClass: sinon.spy(), + visitCustomAttributeContainer: sinon.spy(), + }; + }); + + it("Constant, visit methods called correctly", async () => { + const testItem = new Constant(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitConstant: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitConstant.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.notCalled).to.be.true; + expect(mockVisitor.visitClass.notCalled).to.be.true; + }); + + it("Constant, no visitor, call does not error", async () => { + const testItem = new Constant(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.notCalled).to.be.true; + expect(mockVisitor.visitClass.notCalled).to.be.true; + }); + + it("CustomAttributeClass, visit methods called correctly", async () => { + const testItem = new CustomAttributeClass(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitCustomAttributeClass: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitCustomAttributeClass.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClass.calledOnceWithExactly(testItem)).to.be.true; + }); + + it("CustomAttributeClass, no visitor, call does not error", async () => { + const testItem = new CustomAttributeClass(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClass.calledOnceWithExactly(testItem)).to.be.true; + }); + + it("EntityClass, visit methods called correctly", async () => { + const testItem = new EntityClass(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitEntityClass: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitEntityClass.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClass.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.calledOnceWithExactly(testItem)).to.be.true; + }); + + it("EntityClass, no visitor, call does not error", async () => { + const testItem = new EntityClass(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClass.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.calledOnceWithExactly(testItem)).to.be.true; + }); + + it("Enumeration, visit methods called correctly", async () => { + const testItem = new Enumeration(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitEnumeration: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitEnumeration.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClass.notCalled).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.notCalled).to.be.true; + }); + + it("Enumeration, no visitor, call does not error", async () => { + const testItem = new Enumeration(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.notCalled).to.be.true; + expect(mockVisitor.visitClass.notCalled).to.be.true; + }); + + it("Format, visit methods called correctly", async () => { + const testItem = new Format(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitFormat: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitFormat.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClass.notCalled).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.notCalled).to.be.true; + }); + + it("Format, no visitor, call does not error", async () => { + const testItem = new Format(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.notCalled).to.be.true; + expect(mockVisitor.visitClass.notCalled).to.be.true; + }); + + it("InvertedUnit, visit methods called correctly", async () => { + const testItem = new InvertedUnit(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitInvertedUnit: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitInvertedUnit.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClass.notCalled).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.notCalled).to.be.true; + }); + + it("InvertedUnit, no visitor, call does not error", async () => { + const testItem = new InvertedUnit(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.notCalled).to.be.true; + expect(mockVisitor.visitClass.notCalled).to.be.true; + }); + + it("KindOfQuantity, visit methods called correctly", async () => { + const testItem = new KindOfQuantity(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitKindOfQuantity: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitKindOfQuantity.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClass.notCalled).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.notCalled).to.be.true; + }); + + it("KindOfQuantity, no visitor, call does not error", async () => { + const testItem = new KindOfQuantity(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.notCalled).to.be.true; + expect(mockVisitor.visitClass.notCalled).to.be.true; + }); + + it("Mixin, visit methods called correctly", async () => { + const testItem = new Mixin(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitMixin: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitMixin.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClass.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.calledOnceWithExactly(testItem)).to.be.true; + }); + + it("Mixin, no visitor, call does not error", async () => { + const testItem = new Mixin(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClass.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.calledOnceWithExactly(testItem)).to.be.true; + }); + + it("Phenomenon, visit methods called correctly", async () => { + const testItem = new Phenomenon(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitPhenomenon: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitPhenomenon.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClass.notCalled).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.notCalled).to.be.true; + }); + + it("Phenomenon, no visitor, call does not error", async () => { + const testItem = new Phenomenon(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.notCalled).to.be.true; + expect(mockVisitor.visitClass.notCalled).to.be.true; + }); + + it("PropertyCategory, visit methods, called correctly", async () => { + const testItem = new PropertyCategory(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitPropertyCategory: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitPropertyCategory.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClass.notCalled).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.notCalled).to.be.true; + }); + + it("PropertyCategory, no visitor, call does not error", async () => { + const testItem = new PropertyCategory(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.notCalled).to.be.true; + expect(mockVisitor.visitClass.notCalled).to.be.true; + }); + + it("RelationshipClass, visit methods called correctly", async () => { + const testItem = new RelationshipClass(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitRelationshipClass: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitRelationshipClass.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClass.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.calledOnceWithExactly(testItem)).to.be.true; + }); + + it("RelationshipClass, no visitor, call does not error", async () => { + const testItem = new RelationshipClass(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClass.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.calledOnceWithExactly(testItem)).to.be.true; + }); + + it("StructClass, visit methods called correctly", async () => { + const testItem = new StructClass(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitStructClass: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitStructClass.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClass.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.calledOnceWithExactly(testItem)).to.be.true; + }); + + it("StructClass, no visitor, call does not error", async () => { + const testItem = new StructClass(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClass.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.calledOnceWithExactly(testItem)).to.be.true; + }); + + it("Unit, visit methods called correctly", async () => { + const testItem = new Unit(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitUnit: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitUnit.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClass.notCalled).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.notCalled).to.be.true; + }); + + it("Unit, no visitor, call does not error", async () => { + const testItem = new Unit(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.notCalled).to.be.true; + expect(mockVisitor.visitClass.notCalled).to.be.true; + }); + + it("UnitSystem, visit methods called correctly", async () => { + const testItem = new UnitSystem(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitUnitSystem: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitUnitSystem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClass.notCalled).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.notCalled).to.be.true; + }); + + it("UnitSystem, no visitor, call does not error", async () => { + const testItem = new UnitSystem(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitSchemaItem.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.notCalled).to.be.true; + expect(mockVisitor.visitClass.notCalled).to.be.true; + }); + + it("Property, visit methods called correctly", async () => { + const testItem = new EntityClass(schema, "TestItem"); + const property = await (testItem as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); + mockVisitor = { ...mockVisitor, visitProperty: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(property); + + expect(mockVisitor.visitProperty.calledOnceWithExactly(property)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.calledOnceWithExactly(property)).to.be.true; + expect(mockVisitor.visitSchemaItem.notCalled).to.be.true; + expect(mockVisitor.visitClass.notCalled).to.be.true; + }); + + it("RelationshipConstraint, visit methods called correctly", async () => { + const relationship = new RelationshipClass(schema, "TestRelationship"); + const testItem = new RelationshipConstraint(relationship, RelationshipEnd.Source); + mockVisitor = { ...mockVisitor, visitRelationshipConstraint: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPart(testItem); + + expect(mockVisitor.visitRelationshipConstraint.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainer.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItem.notCalled).to.be.true; + expect(mockVisitor.visitClass.notCalled).to.be.true; + }); + }); + + describe("visitSchemaPartSync", () => { + beforeEach(() => { + mockVisitor = { + visitSchemaItemSync: sinon.spy(), + visitClassSync: sinon.spy(), + visitCustomAttributeContainerSync: sinon.spy(), + }; + }); + + it("Constant, visit methods called correctly", () => { + const testItem = new Constant(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitConstantSync: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitConstantSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.notCalled).to.be.true; + expect(mockVisitor.visitClassSync.notCalled).to.be.true; + }); + + it("Constant, no visitor, call does not error", () => { + const testItem = new Constant(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.notCalled).to.be.true; + expect(mockVisitor.visitClassSync.notCalled).to.be.true; + }); + + it("CustomAttributeClass, visit methods called correctly", () => { + const testItem = new CustomAttributeClass(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitCustomAttributeClassSync: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitCustomAttributeClassSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClassSync.calledOnceWithExactly(testItem)).to.be.true; + }); + + it("CustomAttributeClass, no visitor, call does not error", async () => { + const testItem = new CustomAttributeClass(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClassSync.calledOnceWithExactly(testItem)).to.be.true; + }); + + it("EntityClass, visit methods called correctly", async () => { + const testItem = new EntityClass(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitEntityClassSync: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitEntityClassSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClassSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.calledOnceWithExactly(testItem)).to.be.true; + }); + + it("EntityClass, no visitor, call does not error", async () => { + const testItem = new EntityClass(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + await helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClassSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.calledOnceWithExactly(testItem)).to.be.true; + }); + + it("Enumeration, visit methods called correctly", () => { + const testItem = new Enumeration(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitEnumerationSync: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitEnumerationSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClassSync.notCalled).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.notCalled).to.be.true; + }); + + it("Enumeration, no visitor, call does not error", () => { + const testItem = new Enumeration(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.notCalled).to.be.true; + expect(mockVisitor.visitClassSync.notCalled).to.be.true; + }); + + it("Format, visit methods called correctly", () => { + const testItem = new Format(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitFormatSync: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitFormatSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClassSync.notCalled).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.notCalled).to.be.true; + }); + + it("Format, no visitor, call does not error", () => { + const testItem = new Format(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.notCalled).to.be.true; + expect(mockVisitor.visitClassSync.notCalled).to.be.true; + }); + + it("InvertedUnit, visit methods called correctly", () => { + const testItem = new InvertedUnit(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitInvertedUnitSync: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitInvertedUnitSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClassSync.notCalled).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.notCalled).to.be.true; + }); + + it("InvertedUnit, no visitor, call does not error", () => { + const testItem = new InvertedUnit(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.notCalled).to.be.true; + expect(mockVisitor.visitClassSync.notCalled).to.be.true; + }); + + it("KindOfQuantity, visit methods called correctly", () => { + const testItem = new KindOfQuantity(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitKindOfQuantitySync: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitKindOfQuantitySync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClassSync.notCalled).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.notCalled).to.be.true; + }); + + it("KindOfQuantity, no visitor, call does not error", () => { + const testItem = new KindOfQuantity(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.notCalled).to.be.true; + expect(mockVisitor.visitClassSync.notCalled).to.be.true; + }); + + it("Mixin, visit methods called correctly", () => { + const testItem = new Mixin(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitMixinSync: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitMixinSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClassSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.calledOnceWithExactly(testItem)).to.be.true; + }); + + it("Mixin, no visitor, call does not error", () => { + const testItem = new Mixin(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClassSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.calledOnceWithExactly(testItem)).to.be.true; + }); + + it("Phenomenon, visit methods called correctly", () => { + const testItem = new Phenomenon(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitPhenomenonSync: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitPhenomenonSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClassSync.notCalled).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.notCalled).to.be.true; + }); + + it("Phenomenon, no visitor, call does not error", () => { + const testItem = new Phenomenon(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.notCalled).to.be.true; + expect(mockVisitor.visitClassSync.notCalled).to.be.true; + }); + + it("PropertyCategory, visit methods called correctly", () => { + const testItem = new PropertyCategory(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitPropertyCategorySync: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitPropertyCategorySync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClassSync.notCalled).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.notCalled).to.be.true; + }); + + it("PropertyCategory, no visitor, call does not error", () => { + const testItem = new PropertyCategory(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.notCalled).to.be.true; + expect(mockVisitor.visitClassSync.notCalled).to.be.true; + }); + + it("RelationshipClass, visit methods called correctly", () => { + const testItem = new RelationshipClass(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitRelationshipClassSync: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitRelationshipClassSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClassSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.calledOnceWithExactly(testItem)).to.be.true; + }); + + it("RelationshipClass, no visitor, call does not error", () => { + const testItem = new RelationshipClass(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClassSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.calledOnceWithExactly(testItem)).to.be.true; + }); + + it("StructClass, visit methods called correctly", () => { + const testItem = new StructClass(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitStructClassSync: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitStructClassSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClassSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.calledOnceWithExactly(testItem)).to.be.true; + }); + + it("StructClass, no visitor, call does not error", () => { + const testItem = new StructClass(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClassSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.calledOnceWithExactly(testItem)).to.be.true; + }); + + it("Unit, visit methods called correctly", () => { + const testItem = new Unit(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitUnitSync: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitUnitSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClassSync.notCalled).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.notCalled).to.be.true; + }); + + it("Unit, no visitor, call does not error", () => { + const testItem = new Unit(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.notCalled).to.be.true; + expect(mockVisitor.visitClassSync.notCalled).to.be.true; + }); + + it("UnitSystem, visit methods called correctly", () => { + const testItem = new UnitSystem(schema, "TestItem"); + mockVisitor = { ...mockVisitor, visitUnitSystemSync: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitUnitSystemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitClassSync.notCalled).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.notCalled).to.be.true; + }); + + it("UnitSystem, no visitor, call does not error", () => { + const testItem = new UnitSystem(schema, "TestItem"); + mockVisitor = { ...mockVisitor }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitSchemaItemSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.notCalled).to.be.true; + expect(mockVisitor.visitClassSync.notCalled).to.be.true; + }); + + it("Property, visit methods called correctly", async () => { + const testItem = new EntityClass(schema, "TestItem"); + const property = await (testItem as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); + mockVisitor = { ...mockVisitor, visitPropertySync: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(property); + + expect(mockVisitor.visitPropertySync.calledOnceWithExactly(property)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.calledOnceWithExactly(property)).to.be.true; + expect(mockVisitor.visitSchemaItemSync.notCalled).to.be.true; + expect(mockVisitor.visitClassSync.notCalled).to.be.true; + }); + + it("RelationshipConstraint, visit methods called correctly", () => { + const relationship = new RelationshipClass(schema, "TestRelationship"); + const testItem = new RelationshipConstraint(relationship, RelationshipEnd.Source); + mockVisitor = { ...mockVisitor, visitRelationshipConstraintSync: sinon.spy() }; + helper = new SchemaPartVisitorDelegate(mockVisitor); + + helper.visitSchemaPartSync(testItem); + + expect(mockVisitor.visitRelationshipConstraintSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitCustomAttributeContainerSync.calledOnceWithExactly(testItem)).to.be.true; + expect(mockVisitor.visitSchemaItemSync.notCalled).to.be.true; + expect(mockVisitor.visitClassSync.notCalled).to.be.true; + }); + }); +}); diff --git a/core/ecschema-metadata/test/TestUtils/BisTestHelper.ts b/core/ecschema-metadata/test/TestUtils/BisTestHelper.ts new file mode 100644 index 0000000..2186922 --- /dev/null +++ b/core/ecschema-metadata/test/TestUtils/BisTestHelper.ts @@ -0,0 +1,146 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { SchemaContext } from "../../src/Context"; +import { Schema } from "../../src/Metadata/Schema"; + +export class BisTestHelper { + public static async getNewContext(): Promise { + const context = new SchemaContext(); + await Schema.fromJson(coreCustomAttributesSchema, context); + await Schema.fromJson(bisCoreSchema, context); + + return context; + } +} + +const coreCustomAttributesSchema = { + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", + name: "CoreCustomAttributes", + alias: "CoreCA", + version: "01.00.01", + description: "Custom attributes to indicate core EC concepts, may include struct classes intended for use in core custom attributes.", + items: { + Deprecated: { + appliesTo: "Any", + description: "Identifies a schema or item within a schema as deprecated. Deprecated things should not be used.", + modifier: "sealed", + schemaItemType: "CustomAttributeClass", + }, + }, +}; + +const bisCoreSchema = { + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", + alias: "bis", + name: "BisCore", + version: "01.00.01", + label: "BIS Core", + references : [ + { + name: "CoreCustomAttributes", + version: "01.00.01", + }, + ], + items: { + Element: { + modifier: "abstract", + schemaItemType: "EntityClass", + }, + Model: { + modifier: "abstract", + schemaItemType: "EntityClass", + }, + IParentElement: { + appliesTo: "BisCore.Element", + description: "An interface that indicates that this Element is capable of being a parent (owning child Elements). This interface is mutually exclusive with ISubModeledElement.", + label: "Parent Element", + schemaItemType: "Mixin", + }, + ISubModeledElement: { + appliesTo: "BisCore.Element", + description: "An interface which indicates that an Element can be broken down or described by a (sub) Model. This interface is mutually exclusive with IParentElement.", + label: "Modellable Element", + schemaItemType: "Mixin", + }, + ElementAspect: { + modifier: "abstract", + schemaItemType: "EntityClass", + }, + ElementMultiAspect: { + modifier: "abstract", + schemaItemType: "EntityClass", + }, + ElementUniqueAspect: { + baseClass: "BisCore.ElementAspect", + modifier: "abstract", + schemaItemType: "EntityClass", + }, + ElementOwnsMultiAspects: { + modifier: "none", + schemaItemType: "RelationshipClass", + source: { + constraintClasses: [ "BisCore.Element" ], + multiplicity: "(1..1)", + polymorphic: true, + roleLabel: "owns", + }, + strength: "embedding", + strengthDirection: "forward", + target: { + constraintClasses: [ "BisCore.ElementMultiAspect" ], + multiplicity: "(0..*)", + polymorphic: true, + roleLabel: "is owned by", + }, + }, + ElementOwnsUniqueAspect: { + modifier: "none", + schemaItemType: "RelationshipClass", + source: { + constraintClasses: [ "BisCore.Element" ], + multiplicity: "(1..1)", + polymorphic: true, + roleLabel: "owns", + }, + strength: "embedding", + strengthDirection: "forward", + target: { + constraintClasses: [ "BisCore.ElementUniqueAspect" ], + multiplicity: "(0..*)", + polymorphic: true, + roleLabel: "is owned by", + }, + }, + PhysicalModel: { + modifier: "none", + schemaItemType: "EntityClass", + }, + SpatialLocationModel: { + modifier: "none", + schemaItemType: "EntityClass", + }, + GroupInformationModel: { + modifier: "none", + schemaItemType: "EntityClass", + }, + InformationRecordModel: { + modifier: "none", + schemaItemType: "EntityClass", + }, + DefinitionModel: { + modifier: "none", + schemaItemType: "EntityClass", + }, + DocumentListModel: { + modifier: "none", + schemaItemType: "EntityClass", + }, + LinkModel: { + modifier: "none", + schemaItemType: "EntityClass", + }, + }, +}; diff --git a/core/ecschema-metadata/test/TestUtils/DeserializationHelpers.ts b/core/ecschema-metadata/test/TestUtils/DeserializationHelpers.ts index daee77b..4e397b1 100644 --- a/core/ecschema-metadata/test/TestUtils/DeserializationHelpers.ts +++ b/core/ecschema-metadata/test/TestUtils/DeserializationHelpers.ts @@ -5,7 +5,7 @@ export function createSchemaJsonWithItems(itemsJson: any, referenceJson?: any): any { return { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "TestSchema", version: "1.2.3", items: { diff --git a/core/ecschema-metadata/test/TestUtils/DiagnosticHelpers.ts b/core/ecschema-metadata/test/TestUtils/DiagnosticHelpers.ts new file mode 100644 index 0000000..103cce5 --- /dev/null +++ b/core/ecschema-metadata/test/TestUtils/DiagnosticHelpers.ts @@ -0,0 +1,496 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { AnyClass } from "../../src/Interfaces"; +import { Constant } from "../../src/Metadata/Constant"; +import { CustomAttributeContainerProps, CustomAttribute } from "../../src/Metadata/CustomAttribute"; +import { Enumeration } from "../../src/Metadata/Enumeration"; +import { Format } from "../../src/Metadata/Format"; +import { InvertedUnit } from "../../src/Metadata/InvertedUnit"; +import { KindOfQuantity } from "../../src/Metadata/KindOfQuantity"; +import { Phenomenon } from "../../src/Metadata/Phenomenon"; +import { AnyProperty } from "../../src/Metadata/Property"; +import { PropertyCategory } from "../../src/Metadata/PropertyCategory"; +import { RelationshipClass, RelationshipConstraint } from "../../src/Metadata/RelationshipClass"; +import { Schema } from "../../src/Metadata/Schema"; +import { Unit } from "../../src/Metadata/Unit"; +import { UnitSystem } from "../../src/Metadata/UnitSystem"; +import { IDiagnosticReporter } from "../../src/Validation/DiagnosticReporter"; +import * as Diagnostics from "../../src/Validation/Diagnostic"; +import { IRuleSet } from "../../src/Validation/Rules"; +import sinon = require("sinon"); +import { SchemaItem } from "../../src/Metadata/SchemaItem"; +import { EntityClass, StructClass, Mixin, CustomAttributeClass } from "../../src/ecschema-metadata"; + +export class TestReporter implements IDiagnosticReporter { + public async report(_diagnostic: Diagnostics.AnyDiagnostic) { + } +} + +const ruleSetName = "TestDiagnostics"; + +function getCode(code: number): string { + return ruleSetName + ":" + code; +} + +// tslint:disable-next-line:variable-name +export const TestDiagnostics = { + FailingSchemaDiagnostic: Diagnostics.createSchemaDiagnosticClass<[string, string]>(getCode(1), + "Failed with param {0} {1}"), + FailingSchemaItemDiagnostic: Diagnostics.createSchemaItemDiagnosticClass(getCode(2), + "Failed with param {0} {1}"), + FailingClassDiagnostic: Diagnostics.createClassDiagnosticClass<[string, string]>(getCode(3), + "Failed with params {0} {1}"), + FailingPropertyDiagnostic: Diagnostics.createPropertyDiagnosticClass<[string, string]>(getCode(4), + "Failed with param {0} {1}"), + FailingEntityClassDiagnostic: Diagnostics.createSchemaItemDiagnosticClass(getCode(5), + "Failed with params {0} {1}"), + FailingStructClassDiagnostic: Diagnostics.createSchemaItemDiagnosticClass(getCode(6), + "Failed with params {0} {1}"), + FailingMixinDiagnostic: Diagnostics.createSchemaItemDiagnosticClass(getCode(8), + "Failed with params {0} {1}"), + FailingRelationshipDiagnostic: Diagnostics.createSchemaItemDiagnosticClass(getCode(8), + "Failed with param {0} {1}"), + FailingRelationshipConstraintDiagnostic: Diagnostics.createRelationshipConstraintDiagnosticClass<[string, string]>(getCode(9), + "Failed with param {0} {1}"), + FailingCustomAttributeClassDiagnostic: Diagnostics.createSchemaItemDiagnosticClass(getCode(10), + "Failed with param {0} {1}"), + FailingCustomAttributeContainerDiagnostic: Diagnostics.createCustomAttributeContainerDiagnosticClass<[string, string]>(getCode(11), + "Failed with param {0} {1}"), + FailingEnumerationDiagnostic: Diagnostics.createSchemaItemDiagnosticClass(getCode(12), + "Failed with param {0} {1}"), + FailingKindOfQuantityDiagnostic: Diagnostics.createSchemaItemDiagnosticClass(getCode(13), + "Failed with param {0} {1}"), + FailingPropertyCategoryDiagnostic: Diagnostics.createSchemaItemDiagnosticClass(getCode(14), + "Failed with param {0} {1}"), + FailingFormatDiagnostic: Diagnostics.createSchemaItemDiagnosticClass(getCode(15), + "Failed with param {0} {1}"), + FailingUnitDiagnostic: Diagnostics.createSchemaItemDiagnosticClass(getCode(16), + "Failed with param {0} {1}"), + FailingInvertedUnitFormatDiagnostic: Diagnostics.createSchemaItemDiagnosticClass(getCode(17), + "Failed with param {0} {1}"), + FailingUnitSystemDiagnostic: Diagnostics.createSchemaItemDiagnosticClass(getCode(18), + "Failed with param {0} {1}"), + FailingPhenomenonDiagnostic: Diagnostics.createSchemaItemDiagnosticClass(getCode(19), + "Failed with param {0} {1}"), + FailingConstantDiagnostic: Diagnostics.createSchemaItemDiagnosticClass(getCode(20), + "Failed with param {0} {1}"), +}; + +export async function* failingSchemaRule(schema: Schema): AsyncIterable> | undefined { + yield new TestDiagnostics.FailingSchemaDiagnostic(schema, ["Param1", "Param2"]); +} + +export async function* passingSchemaRule(_schema: Schema): AsyncIterable> | undefined { + return undefined; +} + +export async function* failingSchemaItemRule(schemaItem: SchemaItem): AsyncIterable> | undefined { + yield new TestDiagnostics.FailingSchemaItemDiagnostic(schemaItem, ["Param1", "Param2"]); +} + +export async function* passingSchemaItemRule(_schemaItem: SchemaItem): AsyncIterable> | undefined { + return undefined; +} + +export async function* failingClassRule(ecClass: AnyClass): AsyncIterable> | undefined { + yield new TestDiagnostics.FailingClassDiagnostic(ecClass, ["Param1", "Param2"]); +} + +export async function* passingClassRule(_ecClass: AnyClass): AsyncIterable> | undefined { + return undefined; +} + +export async function* failingPropertyRule(property: AnyProperty): AsyncIterable> | undefined { + yield new TestDiagnostics.FailingPropertyDiagnostic(property, ["Param1", "Param2"]); +} + +export async function* passingPropertyRule(_relationship: RelationshipClass): AsyncIterable> | undefined { + return undefined; +} + +export async function* failingEntityClassRule(entityClass: EntityClass): AsyncIterable> | undefined { + yield new TestDiagnostics.FailingEntityClassDiagnostic(entityClass, ["Param1", "Param2"]); +} + +export async function* passingEntityClassRule(_entityClass: EntityClass): AsyncIterable> | undefined { + return undefined; +} + +export async function* failingStructClassRule(structClass: StructClass): AsyncIterable> | undefined { + yield new TestDiagnostics.FailingStructClassDiagnostic(structClass, ["Param1", "Param2"]); +} + +export async function* passingStructClassRule(_structClass: StructClass): AsyncIterable> | undefined { + return undefined; +} + +export async function* failingMixinRule(mixin: Mixin): AsyncIterable> | undefined { + yield new TestDiagnostics.FailingMixinDiagnostic(mixin, ["Param1", "Param2"]); +} + +export async function* passingMixinRule(_mixin: Mixin): AsyncIterable> | undefined { + return undefined; +} + +export async function* failingRelationshipRule(relationship: RelationshipClass): AsyncIterable> | undefined { + yield new TestDiagnostics.FailingRelationshipDiagnostic(relationship, ["Param1", "Param2"]); +} + +export async function* passingRelationshipRule(_relationship: RelationshipClass): AsyncIterable> | undefined { + return undefined; +} + +export async function* failingRelationshipConstraintRule(constraint: RelationshipConstraint): AsyncIterable> | undefined { + yield new TestDiagnostics.FailingRelationshipConstraintDiagnostic(constraint, ["Param1", "Param2"]); +} + +export async function* passingRelationshipConstraintRule(_constraint: RelationshipConstraint): AsyncIterable> | undefined { + return undefined; +} + +export async function* failingCustomAttributeClassRule(customAttributeClass: CustomAttributeClass): AsyncIterable> | undefined { + yield new TestDiagnostics.FailingCustomAttributeClassDiagnostic(customAttributeClass, ["Param1", "Param2"]); +} + +export async function* passingCustomAttributeClassRule(_customAttributeClass: CustomAttributeClass): AsyncIterable> | undefined { + return undefined; +} + +export async function* failingCustomAttributeContainerRule(container: CustomAttributeContainerProps): AsyncIterable> | undefined { + yield new TestDiagnostics.FailingCustomAttributeContainerDiagnostic(container, ["Param1", "Param2"]); +} + +export async function* passingCustomAttributeContainerRule(_container: CustomAttributeContainerProps): AsyncIterable> | undefined { + return undefined; +} + +export async function* failingCustomAttributeRule(container: CustomAttributeContainerProps, _customAttribute: CustomAttribute): AsyncIterable> | undefined { + yield new TestDiagnostics.FailingCustomAttributeContainerDiagnostic(container, ["Param1", "Param2"]); +} + +export async function* passingCustomAttributeRule(_container: CustomAttributeContainerProps, _customAttribute: CustomAttribute): AsyncIterable> | undefined { + return undefined; +} + +export async function* failingEnumerationRule(enumeration: Enumeration): AsyncIterable> | undefined { + yield new TestDiagnostics.FailingEnumerationDiagnostic(enumeration, ["Param1", "Param2"]); +} + +export async function* passingEnumerationRule(_enumeration: Enumeration): AsyncIterable> | undefined { + return undefined; +} + +export async function* failingKindOfQuantityRule(kindOfQuantity: KindOfQuantity): AsyncIterable> | undefined { + yield new TestDiagnostics.FailingKindOfQuantityDiagnostic(kindOfQuantity, ["Param1", "Param2"]); +} + +export async function* passingKindOfQuantityRule(_kindOfQuantity: KindOfQuantity): AsyncIterable> | undefined { + return undefined; +} + +export async function* failingPropertyCategoryRule(propertyCategory: PropertyCategory): AsyncIterable> | undefined { + yield new TestDiagnostics.FailingPropertyCategoryDiagnostic(propertyCategory, ["Param1", "Param2"]); +} + +export async function* passingPropertyCategoryRule(_propertyCategory: PropertyCategory): AsyncIterable> | undefined { + return undefined; +} + +export async function* failingFormatRule(format: Format): AsyncIterable> | undefined { + yield new TestDiagnostics.FailingFormatDiagnostic(format, ["Param1", "Param2"]); +} + +export async function* passingFormatRule(_format: Format): AsyncIterable> | undefined { + return undefined; +} + +export async function* failingUnitRule(unit: Unit): AsyncIterable> | undefined { + yield new TestDiagnostics.FailingUnitDiagnostic(unit, ["Param1", "Param2"]); +} + +export async function* passingUnitRule(_unit: Unit): AsyncIterable> | undefined { + return undefined; +} + +export async function* failingInvertedUnitRule(invertedUnit: InvertedUnit): AsyncIterable> | undefined { + yield new TestDiagnostics.FailingInvertedUnitFormatDiagnostic(invertedUnit, ["Param1", "Param2"]); +} + +export async function* passingInvertedUnitRule(_invertedUnit: InvertedUnit): AsyncIterable> | undefined { + return undefined; +} + +export async function* failingUnitSystemRule(unitSystem: UnitSystem): AsyncIterable> | undefined { + yield new TestDiagnostics.FailingUnitSystemDiagnostic(unitSystem, ["Param1", "Param2"]); +} + +export async function* passingUnitSystemRule(_format: UnitSystem): AsyncIterable> | undefined { + return undefined; +} + +export async function* failingPhenomenonRule(phenomenon: Phenomenon): AsyncIterable> | undefined { + yield new TestDiagnostics.FailingPhenomenonDiagnostic(phenomenon, ["Param1", "Param2"]); +} + +export async function* passingPhenomenonRule(_phenomenon: Phenomenon): AsyncIterable> | undefined { + return undefined; +} + +export async function* failingConstantRule(constant: Constant): AsyncIterable> | undefined { + yield new TestDiagnostics.FailingConstantDiagnostic(constant, ["Param1", "Param2"]); +} + +export async function* passingConstantRule(_constant: Constant): AsyncIterable> | undefined { + return undefined; +} + +export class EmptyRuleSet implements IRuleSet { + public name: string = "EmptyRuleSet"; +} + +export class TestRuleSet implements IRuleSet { + public name = "TestRuleSet"; + public schemaRules = [ + sinon.spy(failingSchemaRule), + sinon.spy(passingSchemaRule), + ]; + public schemaItemRules = [ + sinon.spy(failingSchemaItemRule), + sinon.spy(passingSchemaItemRule), + ]; + public classRules = [ + sinon.spy(failingClassRule), + sinon.spy(passingClassRule), + ]; + public propertyRules = [ + sinon.spy(failingPropertyRule), + sinon.spy(passingPropertyRule), + ]; + public entityClassRules = [ + sinon.spy(failingEntityClassRule), + sinon.spy(passingEntityClassRule), + ]; + public structClassRules = [ + sinon.spy(failingStructClassRule), + sinon.spy(passingStructClassRule), + ]; + public mixinRules = [ + sinon.spy(failingMixinRule), + sinon.spy(passingMixinRule), + ]; + public relationshipRules = [ + sinon.spy(failingRelationshipRule), + sinon.spy(passingRelationshipRule), + ]; + public relationshipConstraintRules = [ + sinon.spy(failingRelationshipConstraintRule), + sinon.spy(passingRelationshipConstraintRule), + ]; + public customAttributeClassRules = [ + sinon.spy(failingCustomAttributeClassRule), + sinon.spy(passingCustomAttributeClassRule), + ]; + public customAttributeContainerRules = [ + sinon.spy(failingCustomAttributeContainerRule), + sinon.spy(passingCustomAttributeContainerRule), + ]; + public customAttributeInstanceRules = [ + sinon.spy(failingCustomAttributeRule), + sinon.spy(passingCustomAttributeRule), + ]; + public enumerationRules = [ + sinon.spy(failingEnumerationRule), + sinon.spy(passingEnumerationRule), + ]; + public kindOfQuantityRules = [ + sinon.spy(failingKindOfQuantityRule), + sinon.spy(passingKindOfQuantityRule), + ]; + public propertyCategoryRules = [ + sinon.spy(failingPropertyCategoryRule), + sinon.spy(passingPropertyCategoryRule), + ]; + public formatRules = [ + sinon.spy(failingFormatRule), + sinon.spy(passingFormatRule), + ]; + public unitRules = [ + sinon.spy(failingUnitRule), + sinon.spy(passingUnitRule), + ]; + public invertedUnitRules = [ + sinon.spy(failingInvertedUnitRule), + sinon.spy(passingInvertedUnitRule), + ]; + public unitSystemRules = [ + sinon.spy(failingUnitSystemRule), + sinon.spy(passingUnitSystemRule), + ]; + public phenomenonRules = [ + sinon.spy(failingPhenomenonRule), + sinon.spy(passingPhenomenonRule), + ]; + public constantRules = [ + sinon.spy(failingConstantRule), + sinon.spy(passingConstantRule), + ]; +} + +export class TestRuleSetB implements IRuleSet { + public name = "TestRuleSetB"; + + public schemaRules = [ + sinon.spy(failingSchemaRule), + sinon.spy(passingSchemaRule), + ]; + public schemaItemRules = [ + sinon.spy(failingSchemaItemRule), + sinon.spy(passingSchemaItemRule), + ]; + public classRules = [ + sinon.spy(failingClassRule), + sinon.spy(passingClassRule), + ]; + public propertyRules = [ + sinon.spy(failingPropertyRule), + sinon.spy(passingPropertyRule), + ]; + public entityClassRules = [ + sinon.spy(failingEntityClassRule), + sinon.spy(passingEntityClassRule), + ]; + public structClassRules = [ + sinon.spy(failingStructClassRule), + sinon.spy(passingStructClassRule), + ]; + public mixinRules = [ + sinon.spy(failingMixinRule), + sinon.spy(passingMixinRule), + ]; + public relationshipRules = [ + sinon.spy(failingRelationshipRule), + sinon.spy(passingRelationshipRule), + ]; + public customAttributeClassRules = [ + sinon.spy(failingCustomAttributeClassRule), + sinon.spy(passingCustomAttributeClassRule), + ]; + public relationshipConstraintRules = [ + sinon.spy(failingRelationshipConstraintRule), + sinon.spy(passingRelationshipConstraintRule), + ]; + public customAttributeContainerRules = [ + sinon.spy(failingCustomAttributeContainerRule), + sinon.spy(passingCustomAttributeContainerRule), + ]; + public customAttributeRules = [ + sinon.spy(failingCustomAttributeRule), + sinon.spy(passingCustomAttributeRule), + ]; + public enumerationRules = [ + sinon.spy(failingEnumerationRule), + sinon.spy(passingEnumerationRule), + ]; + public kindOfQuantityRules = [ + sinon.spy(failingKindOfQuantityRule), + sinon.spy(passingKindOfQuantityRule), + ]; + public propertyCategoryRules = [ + sinon.spy(failingPropertyCategoryRule), + sinon.spy(passingPropertyCategoryRule), + ]; + public formatRules = [ + sinon.spy(failingFormatRule), + sinon.spy(passingFormatRule), + ]; + public unitRules = [ + sinon.spy(failingUnitRule), + sinon.spy(passingUnitRule), + ]; + public invertedUnitRules = [ + sinon.spy(failingInvertedUnitRule), + sinon.spy(passingInvertedUnitRule), + ]; + public unitSystemRules = [ + sinon.spy(failingUnitSystemRule), + sinon.spy(passingUnitSystemRule), + ]; + public phenomenonRules = [ + sinon.spy(failingPhenomenonRule), + sinon.spy(passingPhenomenonRule), + ]; + public constantRules = [ + sinon.spy(failingConstantRule), + sinon.spy(passingConstantRule), + ]; +} + +export class PassingRuleSet implements IRuleSet { + public name = "PassingRuleSet"; + public schemaRules = [ + sinon.spy(passingSchemaRule), + ]; + public schemaItemRules = [ + sinon.spy(passingSchemaItemRule), + ]; + public classRules = [ + sinon.spy(passingClassRule), + ]; + public propertyRules = [ + sinon.spy(passingPropertyRule), + ]; + public entityClassRules = [ + sinon.spy(passingEntityClassRule), + ]; + public structClassRules = [ + sinon.spy(passingStructClassRule), + ]; + public mixinRules = [ + sinon.spy(passingMixinRule), + ]; + public relationshipRules = [ + sinon.spy(passingRelationshipRule), + ]; + public relationshipConstraintRules = [ + sinon.spy(passingRelationshipConstraintRule), + ]; + public customAttributeClassRules = [ + sinon.spy(passingCustomAttributeClassRule), + ]; + public customAttributeContainerRules = [ + sinon.spy(passingCustomAttributeContainerRule), + ]; + public customAttributeInstanceRules = [ + sinon.spy(passingCustomAttributeRule), + ]; + public enumerationRules = [ + sinon.spy(passingEnumerationRule), + ]; + public kindOfQuantityRules = [ + sinon.spy(passingKindOfQuantityRule), + ]; + public propertyCategoryRules = [ + sinon.spy(passingPropertyCategoryRule), + ]; + public formatRules = [ + sinon.spy(passingFormatRule), + ]; + public unitRules = [ + sinon.spy(passingUnitRule), + ]; + public invertedUnitRules = [ + sinon.spy(passingInvertedUnitRule), + ]; + public unitSystemRules = [ + sinon.spy(passingUnitSystemRule), + ]; + public phenomenonRules = [ + sinon.spy(passingPhenomenonRule), + ]; + public constantRules = [ + sinon.spy(passingConstantRule), + ]; +} diff --git a/core/ecschema-metadata/test/TestUtils/FormatTestHelper.ts b/core/ecschema-metadata/test/TestUtils/FormatTestHelper.ts index 3ffc5aa..b1fd87d 100644 --- a/core/ecschema-metadata/test/TestUtils/FormatTestHelper.ts +++ b/core/ecschema-metadata/test/TestUtils/FormatTestHelper.ts @@ -11,14 +11,14 @@ import { Schema } from "../../src/Metadata/Schema"; const formatsKey = new SchemaKey("Formats", 1, 0, 0); export class TestSchemaLocater implements ISchemaLocater { - public async getSchema(schemaKey: SchemaKey, matchType: SchemaMatchType, context?: SchemaContext): Promise { + public async getSchema(schemaKey: SchemaKey, matchType: SchemaMatchType, context: SchemaContext): Promise { if (!schemaKey.matches(formatsKey, matchType)) return undefined; return (await Schema.fromJson(testFormatSchema, context)) as T; } - public getSchemaSync(schemaKey: SchemaKey, matchType: SchemaMatchType, context?: SchemaContext): T | undefined { + public getSchemaSync(schemaKey: SchemaKey, matchType: SchemaMatchType, context: SchemaContext): T | undefined { if (!schemaKey.matches(formatsKey, matchType)) return undefined; @@ -27,7 +27,7 @@ export class TestSchemaLocater implements ISchemaLocater { } const testFormatSchema = { - $schema: "https://dev.bentley.com/json_schemas/ec/32/draft-01/ecschema", + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", name: "Formats", version: "1.0.0", items: { @@ -35,9 +35,23 @@ const testFormatSchema = { schemaItemType: "Phenomenon", definition: "LENGTH(1)", }, + PERCENTAGE: { + schemaItemType: "Phenomenon", + definition: "NUMBER", + }, USCustom: { schemaItemType: "UnitSystem", }, + SI: { + schemaItemType: "UnitSystem", + }, + M: { + schemaItemType: "Unit", + label: "m", + phenomenon: "Formats.Length", + unitSystem: "Formats.SI", + definition: "M", + }, MILE: { schemaItemType: "Unit", label: "mile", @@ -77,6 +91,13 @@ const testFormatSchema = { unitSystem: "Formats.USCustom", definition: "[MILLI]*IN", }, + PERCENT: { + schemaItemType: "Unit", + label: "%", + phenomenon: "Formats.PERCENTAGE", + unitSystem: "Formats.USCustom", + definition: "ONE", + }, DefaultReal: { schemaItemType: "Format", type: "decimal", diff --git a/core/ecschema-metadata/test/TestUtils/I18NTestHelper.ts b/core/ecschema-metadata/test/TestUtils/I18NTestHelper.ts index d15790f..6dd5c5e 100644 --- a/core/ecschema-metadata/test/TestUtils/I18NTestHelper.ts +++ b/core/ecschema-metadata/test/TestUtils/I18NTestHelper.ts @@ -3,21 +3,21 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { DiagnosticCategory, ECValidationStatus, DiagnosticType } from "../../src/Validation/Diagnostics"; +import { DiagnosticCategory, DiagnosticType, DiagnosticCode } from "../../src/Validation/Diagnostic"; import { I18N } from "@bentley/imodeljs-i18n"; -import * as I18NodeFSBackend from "i18next-node-fs-backend" +import * as I18NodeFSBackend from "i18next-node-fs-backend"; -export const TestMessages = { - TestErrorA: { code: ECValidationStatus.BaseClassIsSealed, category: DiagnosticCategory.Error, diagnosticType: DiagnosticType.SchemaItem, key: 'TestErrorA', message: "Test message with parameter '{0}'" }, - TestErrorB: { code: ECValidationStatus.BaseClassIsSealed, category: DiagnosticCategory.Error, diagnosticType: DiagnosticType.SchemaItem, key: 'TestErrorB', message: "Second test message with parameter '{0}'" }, - TestWarning: { code: ECValidationStatus.BaseClassIsSealed, category: DiagnosticCategory.Warning, diagnosticType: DiagnosticType.SchemaItem, key: 'TestWarning', message: "Test warning message." }, - TestMessage: { code: ECValidationStatus.BaseClassIsSealed, category: DiagnosticCategory.Message, diagnosticType: DiagnosticType.SchemaItem, key: 'TestWarning', message: "Test message." }, - TestSuggestion: { code: ECValidationStatus.BaseClassIsSealed, category: DiagnosticCategory.Suggestion, diagnosticType: DiagnosticType.SchemaItem, key: 'TestSuggestion', message: "Test suggestion." } -} +export const testMessages = { + TestErrorA: { code: DiagnosticCode.BaseClassIsSealed, category: DiagnosticCategory.Error, diagnosticType: DiagnosticType.SchemaItem, key: "TestErrorA", message: "Test message with parameter '{0}'" }, + TestErrorB: { code: DiagnosticCode.BaseClassIsSealed, category: DiagnosticCategory.Error, diagnosticType: DiagnosticType.SchemaItem, key: "TestErrorB", message: "Second test message with parameter '{0}'" }, + TestWarning: { code: DiagnosticCode.BaseClassIsSealed, category: DiagnosticCategory.Warning, diagnosticType: DiagnosticType.SchemaItem, key: "TestWarning", message: "Test warning message." }, + TestMessage: { code: DiagnosticCode.BaseClassIsSealed, category: DiagnosticCategory.Message, diagnosticType: DiagnosticType.SchemaItem, key: "TestWarning", message: "Test message." }, + TestSuggestion: { code: DiagnosticCode.BaseClassIsSealed, category: DiagnosticCategory.Suggestion, diagnosticType: DiagnosticType.SchemaItem, key: "TestSuggestion", message: "Test suggestion." }, +}; type LoadCallback = (error: any, result: any) => void; -interface BackendOptions { } +interface BackendOptions { test: undefined; } export class I18NTestHelper { private static _i18n?: I18N; @@ -26,7 +26,7 @@ export class I18NTestHelper { public static get i18n(): I18N { if (!I18NTestHelper._i18n) { - const i18nOptions = { urlTemplate: "public/locales/{{lng}}/{{ns}}.json", backend: TestBackend }; + const i18nOptions = { urlTemplate: "public/locales/{{lng}}/{{ns}}.json", backend: testBackend }; I18NTestHelper._i18n = new I18N([], "", i18nOptions); } return I18NTestHelper._i18n; @@ -42,7 +42,7 @@ export class I18NTestHelper { public static get i18nFrench(): I18N { if (!I18NTestHelper._i18nFrench) { - const i18nOptions = { urlTemplate: "public/locales/{{lng}}/{{ns}}.json", backend: TestBackend, languageDetector: TestLangDetector }; + const i18nOptions = { urlTemplate: "public/locales/{{lng}}/{{ns}}.json", backend: testBackend, languageDetector: testLangDetector }; I18NTestHelper._i18nFrench = new I18N([], "", i18nOptions); } return I18NTestHelper._i18nFrench; @@ -54,8 +54,8 @@ export class I18NTestHelper { } } -export const TestBackend = { - type: 'backend', +export const testBackend = { + type: "backend", init(services?: any, options?: BackendOptions) { /* use services and options */ services; @@ -68,7 +68,7 @@ export const TestBackend = { /* return resources */ if (language === "en") { - const result = { Diagnostics: { TestErrorA: TestMessages.TestErrorA.message } }; + const result = { Diagnostics: { TestErrorA: testMessages.TestErrorA.message } }; callback(undefined, result); } @@ -85,14 +85,14 @@ export const TestBackend = { languages; key; fallbackValue; - } -} + }, +}; -export const TestLangDetector = { - type: 'languageDetector', +export const testLangDetector = { + type: "languageDetector", init: Function.prototype, detect: () => { - return 'fr'; + return "fr"; }, - cacheUserLanguage: Function.prototype -} \ No newline at end of file + cacheUserLanguage: Function.prototype, +}; diff --git a/core/ecschema-metadata/test/TestUtils/PerfTestHelper.ts b/core/ecschema-metadata/test/TestUtils/PerfTestHelper.ts new file mode 100644 index 0000000..3a20769 --- /dev/null +++ b/core/ecschema-metadata/test/TestUtils/PerfTestHelper.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import * as Benchmark from "benchmark"; + +// tslint:disable:no-console +export async function comparePerformance(...funcs: Array<() => Promise>): Promise { + const suite = new Benchmark.Suite(); + return new Promise((resolve) => { + + funcs.forEach((testFunc, i) => { + suite.add(testFunc.name || String.fromCharCode(65 + i), { + defer: true, + async fn(deferred: any) { + await testFunc(); + deferred.resolve(); + }, + }); + }); + suite.on("cycle", (event: any) => { + console.log(String(event.target)); + }); + suite.on("complete", function (this: any) { + console.log("Fastest is " + this.filter("fastest").map("name")); + resolve(); + }); + suite.run({ async: false }); + }); +} + +export function comparePerformanceSync(...funcs: Array<() => any>): void { + const suite = new Benchmark.Suite(); + + funcs.forEach((testFunc, i) => { + suite.add(testFunc.name || String.fromCharCode(65 + i), testFunc); + }); + suite.on("cycle", (event: any) => { + console.log(String(event.target)); + }); + suite.on("complete", function (this: any) { + console.log("Fastest is " + this.filter("fastest").map("name")); + }); + suite.run({ async: false }); +} diff --git a/core/ecschema-metadata/test/Validation/DiagnosticClasses.test.ts b/core/ecschema-metadata/test/Validation/DiagnosticClasses.test.ts new file mode 100644 index 0000000..cd83771 --- /dev/null +++ b/core/ecschema-metadata/test/Validation/DiagnosticClasses.test.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { expect } from "chai"; +import { Diagnostics as ECDiagnostics } from "./../../src/Validation/ECRules"; + +describe("DiagnosticClasses tests", () => { + function getMessageArgsHolders(text: string): string[] { + const holders: string[] = []; + const matches = text.match(/{(\d+)}/g); + if (!matches) + return holders; + + for (const match of matches) { + if (holders.includes(match.charAt(1))) + continue; + holders.push(match.charAt(1)); + } + holders.sort(); + return holders; + } + + it("EC Diagnostic messages have valid arg place holders.", async () => { + for (const [key, value] of Object.entries(ECDiagnostics)) { + const params = getMessageArgsHolders(value.prototype.messageText); + let index = 0; + for (const param of params) { + expect(param, `Diagnostic ${key} has invalid message arguments`).to.equal(index.toString()); + index ++; + } + } + }); +}); diff --git a/core/ecschema-metadata/test/Validation/DiagnosticReporters.test.ts b/core/ecschema-metadata/test/Validation/DiagnosticReporters.test.ts index 292a5d7..0fbda62 100644 --- a/core/ecschema-metadata/test/Validation/DiagnosticReporters.test.ts +++ b/core/ecschema-metadata/test/Validation/DiagnosticReporters.test.ts @@ -3,174 +3,151 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ +import { Logger } from "@bentley/bentleyjs-core"; +import { I18N, I18NNamespace } from "@bentley/imodeljs-i18n"; import { assert, expect } from "chai"; -import { TestMessages } from "../TestUtils/I18NTestHelper"; import sinon = require("sinon"); -import { Logger } from "@bentley/bentleyjs-core"; -import * as Diagnostics from "../../src/Validation/Diagnostics"; -import * as Reporters from "../../src/Validation/DiagnosticReporters"; -import { SchemaItem, Schema, EntityClass } from "../../src/ecschema-metadata"; -import { ECValidationError } from "../../src/Validation/ValidationException"; + +import { PrimitiveType } from "../../src/ECObjects"; +import { EntityClass, PrimitiveProperty, Schema, SchemaContext } from "../../src/ecschema-metadata"; +import { ECClass, MutableClass } from "../../src/Metadata/Class"; +import { AnyDiagnostic, createPropertyDiagnosticClass, DiagnosticCategory } from "../../src/Validation/Diagnostic"; +import { LoggingDiagnosticReporter } from "../../src/Validation/LoggingDiagnosticReporter"; describe("DiagnosticReporters tests", () => { - function createDiagnostic(message: Diagnostics.DiagnosticMessage, translation: string, schemaItem?: SchemaItem, propertyName?: string | undefined): Diagnostics.Diagnostic { - return { - schema: schemaItem ? schemaItem.schema : undefined, - schemaItem: schemaItem, - schemaItemType: schemaItem ? schemaItem.schemaItemType : undefined, - propertyName: propertyName, - - defaultMessageText: message.message, - messageText: translation, - category: message.category, - code: message.code, - }; + let testSchema: Schema; + let testSchemaItem: EntityClass; + let testProperty: PrimitiveProperty; + let testDiagnostics: AnyDiagnostic[]; + + async function createTestDiagnostic(category: DiagnosticCategory, messageArgs: any[] = ["Param1", "Param2"]): Promise { + testSchema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); + testSchemaItem = new EntityClass(testSchema, "TestEntity"); + testProperty = await (testSchemaItem as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); + const diagnosticClass = createPropertyDiagnosticClass("TestRuleSet:100", "Test Message {0} {1}", category); + const diagnostic = new diagnosticClass(testProperty, messageArgs); + // These were added to a test collection because the generator, createAsyncIterableDiagnostic, + // can only be consumed once, hence the need for the collection, which allows the tests access + // to the created diagnostics. + testDiagnostics.push(diagnostic); + return diagnostic; } + beforeEach(() => { + testDiagnostics = []; + }); + afterEach(() => { sinon.restore(); }); describe("LoggingDiagnosticReporter tests", () => { - let schema: Schema; - let schemaItem: SchemaItem; - let propertyName: string; - - beforeEach(() => { - schema = new Schema("TestSchema", 1, 0, 0); - schemaItem = new EntityClass(schema, "TestEntity"); - propertyName = "TestProperty"; - }); - it("should log expected error", async () => { const logMessage = sinon.stub(Logger, "logError"); - const reporter = new Reporters.LoggingDiagnosticReporter(); - const diag = createDiagnostic(TestMessages.TestErrorA, "test translation", schemaItem, propertyName); + const reporter = new LoggingDiagnosticReporter(); + const diag = await createTestDiagnostic(DiagnosticCategory.Error); - reporter.report(diag); + await reporter.report(diag); - expect(logMessage.calledOnceWithExactly("ecschema-metadata", TestMessages.TestErrorA.message)); + expect(logMessage.calledOnceWith("ecschema-metadata", "Test Message Param1 Param2")).to.be.true; const metaDataFunc = logMessage.firstCall.args[2]; assert.isDefined(metaDataFunc); const metaData = metaDataFunc!(); assert.isDefined(metaData); - expect(metaData.code).to.equal(Diagnostics.ECValidationStatus.BaseClassIsSealed); - expect(metaData.category).to.equal(Diagnostics.DiagnosticCategory.Error); - expect(metaData.schema).to.equal(schema); - expect(metaData.schemaItem).to.equal(schemaItem); - expect(metaData.propertyName).to.equal(propertyName); + expect(metaData.code).to.equal("TestRuleSet:100"); + expect(metaData.category).to.equal(DiagnosticCategory.Error); + expect(metaData.ecDefinition).to.equal(testProperty); expect(metaData.messageText).to.be.undefined; - expect(metaData.defaultMessageText).to.be.undefined; + expect(metaData.messageArgs).to.be.undefined; + }); + + it("should log expected error with translated message", async () => { + const i18n = new I18N([], ""); + const i18nMock = sinon.mock(i18n); + const registerNamespace = i18nMock.expects("registerNamespace"); + registerNamespace.resolves(new I18NNamespace("ECSchemaMetaData", Promise.resolve())); + const translate = i18nMock.expects("translate"); + translate.returns("Translated text {0} {1}"); + const logMessage = sinon.stub(Logger, "logError"); + const reporter = new LoggingDiagnosticReporter(i18n); + const diag = await createTestDiagnostic(DiagnosticCategory.Error); + + await reporter.report(diag); + + expect(logMessage.calledOnceWith("ecschema-metadata", "Translated text Param1 Param2")).to.be.true; + }); + + it("no message args, should log expected error with translated message", async () => { + const i18n = new I18N([], ""); + const i18nMock = sinon.mock(i18n); + const registerNamespace = i18nMock.expects("registerNamespace"); + registerNamespace.resolves(new I18NNamespace("ECSchemaMetaData", Promise.resolve())); + const translate = i18nMock.expects("translate"); + translate.returns("Translated text"); + const logMessage = sinon.stub(Logger, "logError"); + const reporter = new LoggingDiagnosticReporter(i18n); + const diag = await createTestDiagnostic(DiagnosticCategory.Error, []); + + await reporter.report(diag); + + expect(logMessage.calledOnceWith("ecschema-metadata", "Translated text")).to.be.true; }); it("should log expected warning", async () => { const logMessage = sinon.stub(Logger, "logWarning"); - const reporter = new Reporters.LoggingDiagnosticReporter(); - const diag = createDiagnostic(TestMessages.TestWarning, "test translation", schemaItem, propertyName); + const reporter = new LoggingDiagnosticReporter(); + const diag = await createTestDiagnostic(DiagnosticCategory.Warning); - reporter.report(diag); + await reporter.report(diag); - expect(logMessage.calledOnceWithExactly("ecschema-metadata", TestMessages.TestErrorA.message)); + expect(logMessage.calledOnceWith("ecschema-metadata", "Test Message Param1 Param2")).to.be.true; const metaDataFunc = logMessage.firstCall.args[2]; assert.isDefined(metaDataFunc); const metaData = metaDataFunc!(); assert.isDefined(metaData); - expect(metaData.code).to.equal(Diagnostics.ECValidationStatus.BaseClassIsSealed); - expect(metaData.category).to.equal(Diagnostics.DiagnosticCategory.Warning); - expect(metaData.schema).to.equal(schema); - expect(metaData.schemaItem).to.equal(schemaItem); - expect(metaData.propertyName).to.equal(propertyName); + expect(metaData.code).to.equal("TestRuleSet:100"); + expect(metaData.category).to.equal(DiagnosticCategory.Warning); + expect(metaData.ecDefinition).to.equal(testProperty); expect(metaData.messageText).to.be.undefined; - expect(metaData.defaultMessageText).to.be.undefined; + expect(metaData.messageArgs).to.be.undefined; }); it("should log expected message", async () => { const logMessage = sinon.stub(Logger, "logInfo"); - const reporter = new Reporters.LoggingDiagnosticReporter(); - const diag = createDiagnostic(TestMessages.TestMessage, "test translation", schemaItem, propertyName); + const reporter = new LoggingDiagnosticReporter(); + const diag = await createTestDiagnostic(DiagnosticCategory.Message); - reporter.report(diag); + await reporter.report(diag); - expect(logMessage.calledOnceWithExactly("ecschema-metadata", TestMessages.TestErrorA.message)); + expect(logMessage.calledOnceWith("ecschema-metadata", "Test Message Param1 Param2")).to.be.true; const metaDataFunc = logMessage.firstCall.args[2]; assert.isDefined(metaDataFunc); const metaData = metaDataFunc!(); assert.isDefined(metaData); - expect(metaData.code).to.equal(Diagnostics.ECValidationStatus.BaseClassIsSealed); - expect(metaData.category).to.equal(Diagnostics.DiagnosticCategory.Message); - expect(metaData.schema).to.equal(schema); - expect(metaData.schemaItem).to.equal(schemaItem); - expect(metaData.propertyName).to.equal(propertyName); + expect(metaData.code).to.equal("TestRuleSet:100"); + expect(metaData.category).to.equal(DiagnosticCategory.Message); + expect(metaData.ecDefinition).to.equal(testProperty); expect(metaData.messageText).to.be.undefined; - expect(metaData.defaultMessageText).to.be.undefined; + expect(metaData.messageArgs).to.be.undefined; }); it("should log expected suggestion", async () => { const logMessage = sinon.stub(Logger, "logInfo"); - const reporter = new Reporters.LoggingDiagnosticReporter(); - const diag = createDiagnostic(TestMessages.TestSuggestion, "test translation", schemaItem, propertyName); + const reporter = new LoggingDiagnosticReporter(); + const diag = await createTestDiagnostic(DiagnosticCategory.Suggestion); - reporter.report(diag); + await reporter.report(diag); - expect(logMessage.calledOnceWithExactly("ecschema-metadata", TestMessages.TestErrorA.message)); + expect(logMessage.calledOnceWith("ecschema-metadata", "Test Message Param1 Param2")).to.be.true; const metaDataFunc = logMessage.firstCall.args[2]; assert.isDefined(metaDataFunc); const metaData = metaDataFunc!(); assert.isDefined(metaData); - expect(metaData.code).to.equal(Diagnostics.ECValidationStatus.BaseClassIsSealed); - expect(metaData.category).to.equal(Diagnostics.DiagnosticCategory.Suggestion); - expect(metaData.schema).to.equal(schema); - expect(metaData.schemaItem).to.equal(schemaItem); - expect(metaData.propertyName).to.equal(propertyName); + expect(metaData.code).to.equal("TestRuleSet:100"); + expect(metaData.category).to.equal(DiagnosticCategory.Suggestion); + expect(metaData.ecDefinition).to.equal(testProperty); expect(metaData.messageText).to.be.undefined; - expect(metaData.defaultMessageText).to.be.undefined; - }); - }); - - describe("ExceptionDiagnosticReporter tests", () => { - - it("Error diagnostic, should throw expected error", async () => { - const reporter = new Reporters.ExceptionDiagnosticReporter(); - const diag = createDiagnostic(TestMessages.TestErrorA, "test translation"); - - assert.throws(() => reporter.report(diag), ECValidationError, "test translation"); - }); - - it("Warning diagnostic, should not throw error", async () => { - const reporter = new Reporters.ExceptionDiagnosticReporter(); - const diag = createDiagnostic(TestMessages.TestWarning, "test translation"); - - reporter.report(diag); - }); - - it("Message diagnostic, should not throw error", async () => { - const reporter = new Reporters.ExceptionDiagnosticReporter(); - const diag = createDiagnostic(TestMessages.TestMessage, "test translation"); - - reporter.report(diag); - }); - - it("Suggestion diagnostic, should not throw error", async () => { - const reporter = new Reporters.ExceptionDiagnosticReporter(); - const diag = createDiagnostic(TestMessages.TestSuggestion, "test translation"); - - reporter.report(diag); - }); - }); - - describe("CollectionDiagnosticReporter tests", () => { - - it("Error diagnostic, should throw expected error", async () => { - const reporter = new Reporters.CollectionDiagnosticReporter(); - const diag1 = createDiagnostic(TestMessages.TestErrorA, "test translation"); - const diag2 = createDiagnostic(TestMessages.TestErrorB, "test translation"); - - reporter.report(diag1); - reporter.report(diag2); - - expect(reporter.ReportedDiagnostics[0]).to.equal(diag1); - expect(reporter.ReportedDiagnostics[1]).to.equal(diag2); + expect(metaData.messageArgs).to.be.undefined; }); }); }); - diff --git a/core/ecschema-metadata/test/Validation/Diagnostics.test.ts b/core/ecschema-metadata/test/Validation/Diagnostics.test.ts index d601d76..950861f 100644 --- a/core/ecschema-metadata/test/Validation/Diagnostics.test.ts +++ b/core/ecschema-metadata/test/Validation/Diagnostics.test.ts @@ -4,63 +4,160 @@ *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; -import { ECObjectsStatus } from "../../src/Exception"; -import * as Diagnostics from "../../src/Validation/Diagnostics"; -import { Schema } from "../../src/Metadata/Schema"; -import { EntityClass } from "../../src/Metadata/EntityClass"; +import * as Diagnostics from "../../src/Validation/Diagnostic"; describe("Diagnostics tests", () => { - const validDiagnostic = { category: Diagnostics.DiagnosticCategory.Error, code: ECObjectsStatus.InvalidECJson, messageText: "message", defaultMessageText: "default message" }; - afterEach(() => { + + function invalidCodeMsg(code: string) { + return `Diagnostic code ${code} is invalid. Expected the format :.`; + } + + beforeEach(async () => { }); - it("isDiagnostic, valid Diagnostic, returns true", async () => { - expect(Diagnostics.isDiagnostic(validDiagnostic)).to.be.true; + it("diagnosticCategoryToString, Error, proper string returned", () => { + const result = Diagnostics.diagnosticCategoryToString(Diagnostics.DiagnosticCategory.Error); + expect(result).to.equal("Error"); }); - it("isDiagnostic, valid DiagnosticWithSchema, returns true", async () => { - const diag = { ...validDiagnostic, ...{ schema: new Schema() } }; - expect(Diagnostics.isDiagnostic(diag)).to.be.true; + it("diagnosticCategoryToString, Message, proper string returned", () => { + const result = Diagnostics.diagnosticCategoryToString(Diagnostics.DiagnosticCategory.Message); + expect(result).to.equal("Message"); }); - it("isDiagnostic, invalid Diagnostic, returns false", async () => { - const diag = { category: Diagnostics.DiagnosticCategory.Error, code: ECObjectsStatus.InvalidECJson, messageText: "message" }; - expect(Diagnostics.isDiagnostic(diag)).to.be.false; + it("diagnosticCategoryToString, Suggestion, proper string returned", () => { + const result = Diagnostics.diagnosticCategoryToString(Diagnostics.DiagnosticCategory.Suggestion); + expect(result).to.equal("Suggestion"); }); - it("isSchemaDiagnostic, valid DiagnosticWithSchema, returns true", async () => { - const diag = { ...validDiagnostic, ...{ schema: new Schema() } }; - expect(Diagnostics.isSchemaDiagnostic(diag)).to.be.true; + it("diagnosticCategoryToString, Warning, proper string returned", () => { + const result = Diagnostics.diagnosticCategoryToString(Diagnostics.DiagnosticCategory.Warning); + expect(result).to.equal("Warning"); }); - it("isSchemaDiagnostic, invalid DiagnosticWithSchema, returns false", async () => { - const diag = validDiagnostic; - expect(Diagnostics.isSchemaDiagnostic(diag)).to.be.false; + it("diagnosticTypeToString, CustomAttributeContainer, proper string returned", () => { + const result = Diagnostics.diagnosticTypeToString(Diagnostics.DiagnosticType.CustomAttributeContainer); + expect(result).to.equal("CustomAttributeContainer"); }); - it("isSchemaItemDiagnostic, valid DiagnosticWithSchemaItem, returns true", async () => { - const schema = new Schema("TestSchema", 1, 0, 0); - const diag = { ...validDiagnostic, ...{ schema: schema, schemaItem: new EntityClass(schema, "TestEntityClass") } }; - expect(Diagnostics.isSchemaItemDiagnostic(diag)).to.be.true; + it("diagnosticTypeToString, None, proper string returned", () => { + const result = Diagnostics.diagnosticTypeToString(Diagnostics.DiagnosticType.None); + expect(result).to.equal("None"); }); - it("isSchemaItemDiagnostic, invalid DiagnosticWithSchemaItem, returns false", async () => { - const diag = { ...validDiagnostic, ...{ schema: new Schema("TestSchema", 1, 0, 0) } }; - expect(Diagnostics.isSchemaItemDiagnostic(diag)).to.be.false; + it("diagnosticTypeToString, Property, proper string returned", () => { + const result = Diagnostics.diagnosticTypeToString(Diagnostics.DiagnosticType.Property); + expect(result).to.equal("Property"); }); - it("isPropertyDiagnostic, valid DiagnosticWithProperty, returns true", async () => { - const schema = new Schema("TestSchema", 1, 0, 0); - const item = new EntityClass(schema, "TestEntityClass"); - const diag = { ...validDiagnostic, ...{ schema: schema, schemaItem: item, propertyName: "TestProperty" } }; - expect(Diagnostics.isPropertyDiagnostic(diag)).to.be.true; + it("diagnosticTypeToString, RelationshipConstraint, proper string returned", () => { + const result = Diagnostics.diagnosticTypeToString(Diagnostics.DiagnosticType.RelationshipConstraint); + expect(result).to.equal("RelationshipConstraint"); }); - it("isPropertyDiagnostic, invalid DiagnosticWithProperty, returns false", async () => { - const schema = new Schema("TestSchema", 1, 0, 0); - const item = new EntityClass(schema, "TestEntityClass"); - const diag = { ...validDiagnostic, ...{ schema: schema, schemaItem: item } }; - expect(Diagnostics.isPropertyDiagnostic(diag)).to.be.false; + it("diagnosticTypeToString, Schema, proper string returned", () => { + const result = Diagnostics.diagnosticTypeToString(Diagnostics.DiagnosticType.Schema); + expect(result).to.equal("Schema"); + }); + + it("diagnosticTypeToString, SchemaItem, proper string returned", () => { + const result = Diagnostics.diagnosticTypeToString(Diagnostics.DiagnosticType.SchemaItem); + expect(result).to.equal("SchemaItem"); + }); + + it("createSchemaDiagnosticClass, class created properly", async () => { + const newClass = Diagnostics.createSchemaDiagnosticClass("TestRuleSet:100", "Test Message", Diagnostics.DiagnosticCategory.Message); + expect(newClass.prototype.diagnosticType).to.equal(Diagnostics.DiagnosticType.Schema); + expect(newClass.prototype.code).to.equal("TestRuleSet:100"); + expect(newClass.prototype.category).to.equal(Diagnostics.DiagnosticCategory.Message); + expect(newClass.prototype.messageText).to.equal("Test Message"); + }); + + it("createSchemaDiagnosticClass, invalid code, throws", () => { + let code = "InvalidCode"; + expect(() => Diagnostics.createSchemaDiagnosticClass(code, "Test Message", Diagnostics.DiagnosticCategory.Error)).to.throw(Error, invalidCodeMsg(code)); + + code = "Invalid:NotNumber"; + expect(() => Diagnostics.createSchemaDiagnosticClass(code, "Test Message", Diagnostics.DiagnosticCategory.Error)).to.throw(Error, invalidCodeMsg(code)); }); + it("createSchemaItemDiagnosticClass, class created properly", async () => { + const newClass = Diagnostics.createSchemaItemDiagnosticClass("TestRuleSet:100", "Test Message", Diagnostics.DiagnosticCategory.Message); + expect(newClass.prototype.diagnosticType).to.equal(Diagnostics.DiagnosticType.SchemaItem); + expect(newClass.prototype.code).to.equal("TestRuleSet:100"); + expect(newClass.prototype.category).to.equal(Diagnostics.DiagnosticCategory.Message); + expect(newClass.prototype.messageText).to.equal("Test Message"); + }); + + it("createSchemaItemDiagnosticClass, invalid code, throws", () => { + let code = "InvalidCode"; + expect(() => Diagnostics.createSchemaItemDiagnosticClass(code, "Test Message", Diagnostics.DiagnosticCategory.Error)).to.throw(Error, invalidCodeMsg(code)); + + code = "Invalid:NotNumber"; + expect(() => Diagnostics.createSchemaItemDiagnosticClass(code, "Test Message", Diagnostics.DiagnosticCategory.Error)).to.throw(Error, invalidCodeMsg(code)); + }); + + it("createClassDiagnosticClass, class created properly", async () => { + const newClass = Diagnostics.createClassDiagnosticClass("TestRuleSet:100", "Test Message", Diagnostics.DiagnosticCategory.Message); + expect(newClass.prototype.diagnosticType).to.equal(Diagnostics.DiagnosticType.SchemaItem); + expect(newClass.prototype.code).to.equal("TestRuleSet:100"); + expect(newClass.prototype.category).to.equal(Diagnostics.DiagnosticCategory.Message); + expect(newClass.prototype.messageText).to.equal("Test Message"); + }); + + it("createClassDiagnosticClass, invalid code, throws", () => { + let code = "InvalidCode"; + expect(() => Diagnostics.createClassDiagnosticClass(code, "Test Message", Diagnostics.DiagnosticCategory.Error)).to.throw(Error, invalidCodeMsg(code)); + + code = "Invalid:NotNumber"; + expect(() => Diagnostics.createClassDiagnosticClass(code, "Test Message", Diagnostics.DiagnosticCategory.Error)).to.throw(Error, invalidCodeMsg(code)); + }); + + it("createPropertyDiagnosticClass, class created properly", async () => { + const newClass = Diagnostics.createPropertyDiagnosticClass("TestRuleSet:100", "Test Message", Diagnostics.DiagnosticCategory.Warning); + expect(newClass.prototype.diagnosticType).to.equal(Diagnostics.DiagnosticType.Property); + expect(newClass.prototype.code).to.equal("TestRuleSet:100"); + expect(newClass.prototype.category).to.equal(Diagnostics.DiagnosticCategory.Warning); + expect(newClass.prototype.messageText).to.equal("Test Message"); + }); + + it("createPropertyDiagnosticClass, invalid code, throws", () => { + let code = "InvalidCode"; + expect(() => Diagnostics.createPropertyDiagnosticClass(code, "Test Message", Diagnostics.DiagnosticCategory.Error)).to.throw(Error, invalidCodeMsg(code)); + + code = "Invalid:NotNumber"; + expect(() => Diagnostics.createPropertyDiagnosticClass(code, "Test Message", Diagnostics.DiagnosticCategory.Error)).to.throw(Error, invalidCodeMsg(code)); + }); + + it("createRelationshipConstraintDiagnosticClass, class created properly", async () => { + const newClass = Diagnostics.createRelationshipConstraintDiagnosticClass("TestRuleSet:100", "Test Message", Diagnostics.DiagnosticCategory.Error); + expect(newClass.prototype.diagnosticType).to.equal(Diagnostics.DiagnosticType.RelationshipConstraint); + expect(newClass.prototype.code).to.equal("TestRuleSet:100"); + expect(newClass.prototype.category).to.equal(Diagnostics.DiagnosticCategory.Error); + expect(newClass.prototype.messageText).to.equal("Test Message"); + }); + + it("createRelationshipConstraintDiagnosticClass, invalid code, throws", () => { + let code = "InvalidCode"; + expect(() => Diagnostics.createRelationshipConstraintDiagnosticClass(code, "Test Message", Diagnostics.DiagnosticCategory.Error)).to.throw(Error, invalidCodeMsg(code)); + + code = "Invalid:NotNumber"; + expect(() => Diagnostics.createRelationshipConstraintDiagnosticClass(code, "Test Message", Diagnostics.DiagnosticCategory.Error)).to.throw(Error, invalidCodeMsg(code)); + }); + + it("createCustomAttributeContainerDiagnosticClass, class created properly", async () => { + const newClass = Diagnostics.createCustomAttributeContainerDiagnosticClass("TestRuleSet:100", "Test Message", Diagnostics.DiagnosticCategory.Error); + expect(newClass.prototype.diagnosticType).to.equal(Diagnostics.DiagnosticType.CustomAttributeContainer); + expect(newClass.prototype.code).to.equal("TestRuleSet:100"); + expect(newClass.prototype.category).to.equal(Diagnostics.DiagnosticCategory.Error); + expect(newClass.prototype.messageText).to.equal("Test Message"); + }); + + it("createCustomAttributeContainerDiagnosticClass, invalid code, throws", () => { + let code = "InvalidCode"; + expect(() => Diagnostics.createCustomAttributeContainerDiagnosticClass(code, "Test Message", Diagnostics.DiagnosticCategory.Error)).to.throw(Error, invalidCodeMsg(code)); + + code = "Invalid:NotNumber"; + expect(() => Diagnostics.createCustomAttributeContainerDiagnosticClass(code, "Test Message", Diagnostics.DiagnosticCategory.Error)).to.throw(Error, invalidCodeMsg(code)); + }); }); diff --git a/core/ecschema-metadata/test/Validation/ECRules/ClassRules.test.ts b/core/ecschema-metadata/test/Validation/ECRules/ClassRules.test.ts new file mode 100644 index 0000000..c1d7655 --- /dev/null +++ b/core/ecschema-metadata/test/Validation/ECRules/ClassRules.test.ts @@ -0,0 +1,106 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { expect } from "chai"; + +import { DelayedPromiseWithProps, ECClassModifier, RelationshipClass, SchemaContext, schemaItemTypeToString } from "../../../src/ecschema-metadata"; +import { EntityClass } from "../../../src/Metadata/EntityClass"; +import { Schema } from "../../../src/Metadata/Schema"; +import { DiagnosticCategory, DiagnosticType } from "../../../src/Validation/Diagnostic"; +import * as Rules from "../../../src/Validation/ECRules"; + +describe("ClassRule tests", () => { + let schema: Schema; + + beforeEach(async () => { + schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); + }); + + it("BaseClassIsSealed, rule violated.", async () => { + const baseClass = new EntityClass(schema, "TestBase", ECClassModifier.Sealed); + const entityClass = new EntityClass(schema, "TestClass"); + entityClass.baseClass = new DelayedPromiseWithProps(baseClass.key, async () => baseClass); + + const result = await Rules.baseClassIsSealed(entityClass); + + expect(result).not.undefined; + let resultHasEntries = false; + for await (const diagnostic of result!) { + resultHasEntries = true; + expect(diagnostic).to.not.be.undefined; + expect(diagnostic!.ecDefinition).to.equal(entityClass); + expect(diagnostic!.messageArgs).to.eql([entityClass.fullName, baseClass.fullName]); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.BaseClassIsSealed); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.SchemaItem); + } + expect(resultHasEntries, "expected rule to return an AsyncIterable with entries.").to.be.true; + }); + + it("BaseClassIsSealed, base is not sealed, rule passes.", async () => { + const baseClass = new EntityClass(schema, "TestBase"); + const entityClass = new EntityClass(schema, "TestClass"); + entityClass.baseClass = new DelayedPromiseWithProps(baseClass.key, async () => baseClass); + + const result = await Rules.baseClassIsSealed(entityClass); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("BaseClassIsSealed, no base class, rule passes.", async () => { + const entityClass = new EntityClass(schema, "TestClass"); + + const result = await Rules.baseClassIsSealed(entityClass); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("BaseClassIsOfDifferentType, rule violated.", async () => { + const baseClass = new RelationshipClass(schema, "TestBase"); + const entityClass = new EntityClass(schema, "TestClass"); + entityClass.baseClass = new DelayedPromiseWithProps(baseClass.key, async () => baseClass); + const baseType = schemaItemTypeToString(baseClass.schemaItemType); + + const result = await Rules.baseClassIsOfDifferentType(entityClass); + expect(result).not.undefined; + let resultHasEntries = false; + for await (const diagnostic of result!) { + resultHasEntries = true; + expect(diagnostic).to.not.be.undefined; + expect(diagnostic!.ecDefinition).to.equal(entityClass); + expect(diagnostic!.messageArgs).to.eql([entityClass.fullName, baseClass.fullName, baseType]); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.BaseClassOfDifferentType); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.SchemaItem); + } + expect(resultHasEntries, "expected rule to return an AsyncIterable with entries.").to.be.true; + }); + + it("BaseClassIsOfDifferentType, same type, rule passes.", async () => { + const baseClass = new EntityClass(schema, "TestBase"); + const entityClass = new EntityClass(schema, "TestClass"); + entityClass.baseClass = new DelayedPromiseWithProps(baseClass.key, async () => baseClass); + + const result = await Rules.baseClassIsOfDifferentType(entityClass); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("BaseClassIsOfDifferentType, no base class, rule passes.", async () => { + const entityClass = new EntityClass(schema, "TestClass"); + + const result = await Rules.baseClassIsOfDifferentType(entityClass); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); +}); diff --git a/core/ecschema-metadata/test/Validation/ECRules/CustomAttributeRules.test.ts b/core/ecschema-metadata/test/Validation/ECRules/CustomAttributeRules.test.ts new file mode 100644 index 0000000..7110aba --- /dev/null +++ b/core/ecschema-metadata/test/Validation/ECRules/CustomAttributeRules.test.ts @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { expect } from "chai"; + +import { SchemaContext } from "../../../src/Context"; +import { ECClassModifier } from "../../../src/ECObjects"; +import { MutableClass } from "../../../src/Metadata/Class"; +import { CustomAttributeClass } from "../../../src/Metadata/CustomAttributeClass"; +import { EntityClass } from "../../../src/Metadata/EntityClass"; +import { MutableSchema, Schema } from "../../../src/Metadata/Schema"; +import { DiagnosticCategory, DiagnosticType } from "../../../src/Validation/Diagnostic"; +import * as Rules from "../../../src/Validation/ECRules"; + +describe("CustomAttribute Rules Tests", () => { + let schema: Schema; + let context: SchemaContext; + let caSchema: Schema; + + function addCA(modifier: ECClassModifier) { + const testCA = new CustomAttributeClass(caSchema, "TestCA", modifier); + (caSchema as MutableSchema).addItem(testCA); + } + + beforeEach(async () => { + context = new SchemaContext(); + schema = new Schema(context, "TestSchema", 1, 0, 0); + caSchema = new Schema(context, "TestCASchema", 1, 0, 0); + await (schema as MutableSchema).addReference(caSchema); + }); + + describe("CustomAttributeNotOfConcreteClass tests", () => { + it("CustomAttribute is abstract, rule violated", async () => { + addCA(ECClassModifier.Abstract); + const testEntity = new EntityClass(schema, "TestEntity"); + (testEntity as unknown as MutableClass).addCustomAttribute({ className: "TestCASchema.TestCA" }); + + const result = await Rules.customAttributeNotOfConcreteClass(testEntity, testEntity.customAttributes!.get("TestCASchema.TestCA")!); + + let resultHasEntries = false; + for await (const diagnostic of result!) { + resultHasEntries = true; + expect(diagnostic).to.not.be.undefined; + expect(diagnostic!.ecDefinition).to.equal(testEntity); + expect(diagnostic!.messageArgs).to.eql([testEntity.fullName, "TestCASchema.TestCA"]); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.CustomAttributeNotOfConcreteClass); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.CustomAttributeContainer); + } + expect(resultHasEntries, "expected rule to return an AsyncIterable with entries.").to.be.true; + }); + + it("CustomAttribute is concrete, rule passes", async () => { + addCA(ECClassModifier.None); + const testEntity = new EntityClass(schema, "TestEntity"); + (testEntity as unknown as MutableClass).addCustomAttribute({ className: "TestCASchema.TestCA" }); + + const result = await Rules.customAttributeNotOfConcreteClass(testEntity, testEntity.customAttributes!.get("TestCASchema.TestCA")!); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + }); +}); diff --git a/core/ecschema-metadata/test/Validation/ECRules/ECRules.test.ts b/core/ecschema-metadata/test/Validation/ECRules/ECRules.test.ts new file mode 100644 index 0000000..4d29d2d --- /dev/null +++ b/core/ecschema-metadata/test/Validation/ECRules/ECRules.test.ts @@ -0,0 +1,97 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import * as Rules from "../../../src/Validation/ECRules"; +import { assert } from "@bentley/bentleyjs-core"; + +describe("ECRules Tests", () => { + + it("DiagnosticCodes must be unique.", () => { + const seenCodes: string[] = []; + for (const [, value] of Object.entries(Rules.DiagnosticCodes)) { + if (seenCodes.includes(value)) + assert(false, `Diagnostic code ${value} already exists. Codes must be unique`); + seenCodes.push(value); + } + }); + + it("All rules should be in the rule set.", () => { + const missingRules: string[] = []; + for (const [key] of Object.entries(Rules.Diagnostics)) { + if (Rules.ECRuleSet.classRules && Rules.ECRuleSet.classRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + if (Rules.ECRuleSet.constantRules && Rules.ECRuleSet.constantRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + if (Rules.ECRuleSet.customAttributeClassRules && Rules.ECRuleSet.customAttributeClassRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + if (Rules.ECRuleSet.customAttributeContainerRules && Rules.ECRuleSet.customAttributeContainerRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + if (Rules.ECRuleSet.customAttributeInstanceRules && Rules.ECRuleSet.customAttributeInstanceRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + if (Rules.ECRuleSet.entityClassRules && Rules.ECRuleSet.entityClassRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + if (Rules.ECRuleSet.enumerationRules && Rules.ECRuleSet.enumerationRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + if (Rules.ECRuleSet.formatRules && Rules.ECRuleSet.formatRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + if (Rules.ECRuleSet.formatRules && Rules.ECRuleSet.formatRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + if (Rules.ECRuleSet.invertedUnitRules && Rules.ECRuleSet.invertedUnitRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + if (Rules.ECRuleSet.kindOfQuantityRules && Rules.ECRuleSet.kindOfQuantityRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + if (Rules.ECRuleSet.mixinRules && Rules.ECRuleSet.mixinRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + if (Rules.ECRuleSet.phenomenonRules && Rules.ECRuleSet.phenomenonRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + if (Rules.ECRuleSet.propertyCategoryRules && Rules.ECRuleSet.propertyCategoryRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + if (Rules.ECRuleSet.propertyRules && Rules.ECRuleSet.propertyRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + if (Rules.ECRuleSet.relationshipConstraintRules && Rules.ECRuleSet.relationshipConstraintRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + if (Rules.ECRuleSet.relationshipRules && Rules.ECRuleSet.relationshipRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + if (Rules.ECRuleSet.schemaItemRules && Rules.ECRuleSet.schemaItemRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + if (Rules.ECRuleSet.schemaRules && Rules.ECRuleSet.schemaRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + if (Rules.ECRuleSet.structClassRules && Rules.ECRuleSet.structClassRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + if (Rules.ECRuleSet.unitRules && Rules.ECRuleSet.unitRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + if (Rules.ECRuleSet.unitSystemRules && Rules.ECRuleSet.unitSystemRules.find((x) => x.name.toLowerCase() === key.toLowerCase())) + continue; + + missingRules.push(key); + } + + if (missingRules.length === 0) + return; + + assert(false, "Rules not found in rule set: " + missingRules.toString()); + }); +}); diff --git a/core/ecschema-metadata/test/Validation/ECRules/EnumerationRules.test.ts b/core/ecschema-metadata/test/Validation/ECRules/EnumerationRules.test.ts new file mode 100644 index 0000000..8c7d091 --- /dev/null +++ b/core/ecschema-metadata/test/Validation/ECRules/EnumerationRules.test.ts @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { expect } from "chai"; + +import { SchemaContext } from "../../../src/Context"; +import { PrimitiveType } from "../../../src/ECObjects"; +import { Enumeration } from "../../../src/Metadata/Enumeration"; +import { Schema } from "../../../src/Metadata/Schema"; +import { DiagnosticCategory, DiagnosticType } from "../../../src/Validation/Diagnostic"; +import * as Rules from "../../../src/Validation/ECRules"; + +describe("Enumeration rule tests", () => { + let schema: Schema; + + beforeEach(async () => { + schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); + }); + + it("enumerationTypeUnsupported, rule violated.", async () => { + const enumeration = new Enumeration(schema, "TestEnum"); + + const result = await Rules.enumerationTypeUnsupported(enumeration); + + expect(result).not.undefined; + let resultHasEntries = false; + for await (const diagnostic of result!) { + resultHasEntries = true; + expect(diagnostic).to.not.be.undefined; + expect(diagnostic!.ecDefinition).to.equal(enumeration); + expect(diagnostic!.messageArgs).to.eql([enumeration.fullName]); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.EnumerationTypeUnsupported); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.SchemaItem); + } + expect(resultHasEntries, "expected rule to return an AsyncIterable with entries.").to.be.true; + }); + + it("enumerationTypeUnsupported, string type, rule passes.", async () => { + const enumeration = new Enumeration(schema, "TestEnum", PrimitiveType.String); + + const result = await Rules.enumerationTypeUnsupported(enumeration); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("enumerationTypeUnsupported, integer type, rule passes.", async () => { + const enumeration = new Enumeration(schema, "TestEnum", PrimitiveType.Integer); + + const result = await Rules.enumerationTypeUnsupported(enumeration); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); +}); diff --git a/core/ecschema-metadata/test/Validation/ECRules/MixinRules.test.ts b/core/ecschema-metadata/test/Validation/ECRules/MixinRules.test.ts new file mode 100644 index 0000000..f81a21e --- /dev/null +++ b/core/ecschema-metadata/test/Validation/ECRules/MixinRules.test.ts @@ -0,0 +1,99 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { expect } from "chai"; + +import { SchemaContext } from "../../../src/Context"; +import { DelayedPromiseWithProps } from "../../../src/DelayedPromise"; +import { EntityClass, MutableEntityClass } from "../../../src/Metadata/EntityClass"; +import { Mixin } from "../../../src/Metadata/Mixin"; +import { Schema } from "../../../src/Metadata/Schema"; +import { DiagnosticCategory, DiagnosticType } from "../../../src/Validation/Diagnostic"; +import * as Rules from "../../../src/Validation/ECRules"; + +describe("Mixin Rule Tests", () => { + let schema: Schema; + + beforeEach(async () => { + schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); + }); + + class TestMixin extends Mixin { + constructor(testSchema: Schema, name: string, appliesTo: EntityClass) { + super(testSchema, name); + this._appliesTo = new DelayedPromiseWithProps(appliesTo.key, async () => appliesTo); + } + } + + it("MixinAppliedToClassMustDeriveFromConstraint, entity derives from constraint, rule passes.", async () => { + const constraintClass = new EntityClass(schema, "TestConstraint"); + const mixin = new TestMixin(schema, "TestMixin", constraintClass); + const entityClass = new EntityClass(schema, "TestClass"); + (entityClass as MutableEntityClass).addMixin(mixin); + entityClass.baseClass = new DelayedPromiseWithProps(constraintClass.key, async () => constraintClass); + + const result = await Rules.mixinAppliedToClassMustDeriveFromConstraint(entityClass); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("MixinAppliedToClassMustDeriveFromConstraint, entity class is the constraint, rule passes.", async () => { + const entityClass = new EntityClass(schema, "TestClass"); + const mixin = new TestMixin(schema, "TestMixin", entityClass); + (entityClass as MutableEntityClass).addMixin(mixin); + + const result = await Rules.mixinAppliedToClassMustDeriveFromConstraint(entityClass); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("MixinAppliedToClassMustDeriveFromConstraint, entity has no base class and class is not the constraint, rule violated.", async () => { + const constraintClass = new EntityClass(schema, "TestConstraint"); + const mixin = new TestMixin(schema, "TestMixin", constraintClass); + const entityClass = new EntityClass(schema, "TestClass"); + (entityClass as MutableEntityClass).addMixin(mixin); + + const result = await Rules.mixinAppliedToClassMustDeriveFromConstraint(entityClass); + + let resultHasEntries = false; + for await (const diagnostic of result!) { + resultHasEntries = true; + expect(diagnostic).to.not.be.undefined; + expect(diagnostic!.ecDefinition).to.equal(entityClass); + expect(diagnostic!.messageArgs).to.eql([mixin.fullName, entityClass.fullName, mixin.appliesTo!.fullName]); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.MixinAppliedToClassMustDeriveFromConstraint); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.SchemaItem); + } + expect(resultHasEntries, "expected rule to return an AsyncIterable with entries.").to.be.true; + }); + + it("MixinAppliedToClassMustDeriveFromConstraint, entity base incompatible with constraint, rule violated.", async () => { + const constraintClass = new EntityClass(schema, "TestConstraint"); + const baseClass = new EntityClass(schema, "TestBase"); + const mixin = new TestMixin(schema, "TestMixin", constraintClass); + const entityClass = new EntityClass(schema, "TestClass"); + (entityClass as MutableEntityClass).addMixin(mixin); + entityClass.baseClass = new DelayedPromiseWithProps(baseClass.key, async () => baseClass); + + const result = await Rules.mixinAppliedToClassMustDeriveFromConstraint(entityClass); + + let resultHasEntries = false; + for await (const diagnostic of result!) { + resultHasEntries = true; + expect(diagnostic).to.not.be.undefined; + expect(diagnostic!.ecDefinition).to.equal(entityClass); + expect(diagnostic!.messageArgs).to.eql([mixin.fullName, entityClass.fullName, mixin.appliesTo!.fullName]); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.MixinAppliedToClassMustDeriveFromConstraint); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.SchemaItem); + } + expect(resultHasEntries, "expected rule to return an AsyncIterable with entries.").to.be.true; + }); +}); diff --git a/core/ecschema-metadata/test/Validation/ECRules/PropertyRules.test.ts b/core/ecschema-metadata/test/Validation/ECRules/PropertyRules.test.ts new file mode 100644 index 0000000..0e4937d --- /dev/null +++ b/core/ecschema-metadata/test/Validation/ECRules/PropertyRules.test.ts @@ -0,0 +1,419 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { expect } from "chai"; + +import { SchemaContext } from "../../../src/Context"; +import { DelayedPromiseWithProps } from "../../../src/DelayedPromise"; +import { PrimitiveType } from "../../../src/ECObjects"; +import { ECClass, MutableClass, StructClass } from "../../../src/Metadata/Class"; +import { EntityClass } from "../../../src/Metadata/EntityClass"; +import { KindOfQuantity } from "../../../src/Metadata/KindOfQuantity"; +import { PrimitiveProperty } from "../../../src/Metadata/Property"; +import { MutableSchema, Schema } from "../../../src/Metadata/Schema"; +import { Unit } from "../../../src/Metadata/Unit"; +import { DiagnosticCategory, DiagnosticType } from "../../../src/Validation/Diagnostic"; +import * as Rules from "../../../src/Validation/ECRules"; + +describe("PropertyRule tests", () => { + let schema: Schema; + let context: SchemaContext; + let testClass: EntityClass; + let testBaseClass: EntityClass; + let testKindOfQuantity: KindOfQuantity; + let testBaseKindOfQuantity: KindOfQuantity; + + beforeEach(async () => { + context = new SchemaContext(); + schema = new Schema(context, "TestSchema", 1, 0, 0); + const mutable = schema as MutableSchema; + testClass = await mutable.createEntityClass("TestClass"); + testBaseClass = await mutable.createEntityClass("TestBaseClass"); + testKindOfQuantity = await mutable.createKindOfQuantity("TestKoQ"); + testBaseKindOfQuantity = await mutable.createKindOfQuantity("TestBaseKoQ"); + testClass.baseClass = new DelayedPromiseWithProps(testBaseClass.key, async () => testBaseClass); + }); + + it("IncompatibleValueTypePropertyOverride, rule violated.", async () => { + await (testBaseClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); + await (testClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.Integer); + + const result = await Rules.incompatibleValueTypePropertyOverride(testClass.properties![0] as PrimitiveProperty); + + let resultHasEntries = false; + for await (const diagnostic of result!) { + resultHasEntries = true; + expect(diagnostic).to.not.be.undefined; + expect(diagnostic!.ecDefinition).to.equal(testClass.properties![0]); + expect(diagnostic!.messageArgs).to.eql([testClass.fullName, "TestProperty", testBaseClass.fullName, "string", "int"]); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.IncompatibleValueTypePropertyOverride); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.Property); + } + expect(resultHasEntries, "expected rule to return an AsyncIterable with entries.").to.be.true; + }); + + it("IncompatibleValueTypePropertyOverride, multiple base classes, rule violated.", async () => { + const rootBaseClass = new EntityClass(schema, "RootBaseClass"); + await (rootBaseClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); + await (testClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.Integer); + testBaseClass.baseClass = new DelayedPromiseWithProps(rootBaseClass.key, async () => rootBaseClass); + + const result = await Rules.incompatibleValueTypePropertyOverride(testClass.properties![0] as PrimitiveProperty); + + let resultHasEntries = false; + for await (const diagnostic of result!) { + resultHasEntries = true; + expect(diagnostic).to.not.be.undefined; + expect(diagnostic!.ecDefinition).to.equal(testClass.properties![0]); + expect(diagnostic!.messageArgs).to.eql([testClass.fullName, "TestProperty", rootBaseClass.fullName, "string", "int"]); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.IncompatibleValueTypePropertyOverride); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.Property); + } + expect(resultHasEntries, "expected rule to return an AsyncIterable with entries.").to.be.true; + }); + + it("IncompatibleValueTypePropertyOverride, different property types, rule passes.", async () => { + await (testBaseClass as ECClass as MutableClass).createPrimitiveArrayProperty("TestProperty", PrimitiveType.String); + await (testClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.Integer); + + const result = await Rules.incompatibleValueTypePropertyOverride(testClass.properties![0] as PrimitiveProperty); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("IncompatibleValueTypePropertyOverride, same value types, rule passes.", async () => { + await (testBaseClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); + await (testClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); + + const result = await Rules.incompatibleValueTypePropertyOverride(testClass.properties![0] as PrimitiveProperty); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("IncompatibleValueTypePropertyOverride, same array value types, rule passes.", async () => { + await (testBaseClass as ECClass as MutableClass).createPrimitiveArrayProperty("TestProperty", PrimitiveType.String); + await (testClass as ECClass as MutableClass).createPrimitiveArrayProperty("TestProperty", PrimitiveType.String); + + const result = await Rules.incompatibleValueTypePropertyOverride(testClass.properties![0] as PrimitiveProperty); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("IncompatibleValueTypePropertyOverride, non-primitive type, rule passes.", async () => { + await (testBaseClass as ECClass as MutableClass).createStructProperty("TestProperty", new StructClass(schema, "TestStruct")); + await (testClass as ECClass as MutableClass).createStructProperty("TestProperty", new StructClass(schema, "TestStruct")); + + const result = await Rules.incompatibleValueTypePropertyOverride(testClass.properties![0] as PrimitiveProperty); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("IncompatibleValueTypePropertyOverride, no base class, rule passes.", async () => { + const entityClass = new EntityClass(new Schema(new SchemaContext(), "TestSchema", 1, 2, 3), "TestEntity"); + await (entityClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.Integer); + + const result = await Rules.incompatibleValueTypePropertyOverride(entityClass.properties![0] as PrimitiveProperty); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("IncompatibleTypePropertyOverride, rule violated.", async () => { + await (testBaseClass as ECClass as MutableClass).createPrimitiveArrayProperty("TestProperty", PrimitiveType.String); + await (testClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.Integer); + + const result = await Rules.incompatibleTypePropertyOverride(testClass.properties![0] as PrimitiveProperty); + + let resultHasEntries = false; + for await (const diagnostic of result!) { + resultHasEntries = true; + expect(diagnostic).to.not.be.undefined; + expect(diagnostic!.ecDefinition).to.equal(testClass.properties![0]); + expect(diagnostic!.messageArgs).to.eql([testClass.fullName, "TestProperty", testBaseClass.fullName, "PrimitiveArrayProperty", "PrimitiveProperty"]); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.IncompatibleTypePropertyOverride); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.Property); + } + expect(resultHasEntries, "expected rule to return an AsyncIterable with entries.").to.be.true; + }); + + it("IncompatibleTypePropertyOverride, multiple base classes, rule violated.", async () => { + const rootBaseClass = new EntityClass(schema, "RootBaseClass"); + await (rootBaseClass as ECClass as MutableClass).createPrimitiveArrayProperty("TestProperty", PrimitiveType.String); + await (testClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); + testBaseClass.baseClass = new DelayedPromiseWithProps(rootBaseClass.key, async () => rootBaseClass); + + const result = await Rules.incompatibleTypePropertyOverride(testClass.properties![0] as PrimitiveProperty); + + let resultHasEntries = false; + for await (const diagnostic of result!) { + resultHasEntries = true; + expect(diagnostic).to.not.be.undefined; + expect(diagnostic!.ecDefinition).to.equal(testClass.properties![0]); + expect(diagnostic!.messageArgs).to.eql([testClass.fullName, "TestProperty", rootBaseClass.fullName, "PrimitiveArrayProperty", "PrimitiveProperty"]); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.IncompatibleTypePropertyOverride); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.Property); + } + expect(resultHasEntries, "expected rule to return an AsyncIterable with entries.").to.be.true; + }); + + it("IncompatibleTypePropertyOverride, types compatible, rule passes.", async () => { + await (testBaseClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); + // different value type, but THIS rule should still pass + await (testClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.Integer); + + const result = await Rules.incompatibleTypePropertyOverride(testClass.properties![0] as PrimitiveProperty); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("IncompatibleTypePropertyOverride, no base class, rule passes.", async () => { + const entityClass = new EntityClass(new Schema(new SchemaContext(), "TestSchema", 1, 2, 3), "TestEntity"); + await (entityClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.Integer); + + const result = await Rules.incompatibleTypePropertyOverride(entityClass.properties![0] as PrimitiveProperty); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("IncompatibleUnitPropertyOverride, rule violated.", async () => { + await (testBaseClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); + await (testClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.Integer); + + const basePropJson = { + name: "TestProperty", + type: "PrimitiveProperty", + kindOfQuantity: "TestSchema.TestBaseKoQ", + }; + + const childPropJson = { + name: "TestProperty", + type: "PrimitiveProperty", + kindOfQuantity: "TestSchema.TestKoQ", + }; + + const baseProperty = testBaseClass.properties![0]; + baseProperty.deserialize(basePropJson); + + const childProperty = testClass.properties![0]; + childProperty.deserialize(childPropJson); + + const baseUnit = new Unit(schema, "BaseTestUnit"); + testBaseKindOfQuantity.persistenceUnit = new DelayedPromiseWithProps(baseUnit.key, async () => baseUnit); + + const childUnit = new Unit(schema, "TestUnit"); + testKindOfQuantity.persistenceUnit = new DelayedPromiseWithProps(childUnit.key, async () => childUnit); + + const result = await Rules.incompatibleUnitPropertyOverride(testClass.properties![0] as PrimitiveProperty); + + let resultHasEntries = false; + for await (const diagnostic of result!) { + resultHasEntries = true; + expect(diagnostic).to.not.be.undefined; + expect(diagnostic!.ecDefinition).to.equal(testClass.properties![0]); + expect(diagnostic!.messageArgs).to.eql([ + testClass.fullName, "TestProperty", testBaseClass.fullName, + testBaseKindOfQuantity.fullName, baseUnit.fullName, childUnit.fullName, testKindOfQuantity.fullName, + ]); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.IncompatibleUnitPropertyOverride); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.Property); + } + expect(resultHasEntries, "expected rule to return an AsyncIterable with entries.").to.be.true; + }); + + it("IncompatibleUnitPropertyOverride, multiple base classes, rule violated.", async () => { + const rootBaseClass = new EntityClass(schema, "RootBaseClass"); + await (rootBaseClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); + await (testClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.Integer); + testBaseClass.baseClass = new DelayedPromiseWithProps(rootBaseClass.key, async () => rootBaseClass); + + const basePropJson = { + name: "TestProperty", + type: "PrimitiveProperty", + kindOfQuantity: "TestSchema.TestBaseKoQ", + }; + + const childPropJson = { + name: "TestProperty", + type: "PrimitiveProperty", + kindOfQuantity: "TestSchema.TestKoQ", + }; + + const baseProperty = rootBaseClass.properties![0]; + baseProperty.deserialize(basePropJson); + + const childProperty = testClass.properties![0]; + childProperty.deserialize(childPropJson); + + const baseUnit = new Unit(schema, "BaseTestUnit"); + testBaseKindOfQuantity.persistenceUnit = new DelayedPromiseWithProps(baseUnit.key, async () => baseUnit); + + const childUnit = new Unit(schema, "TestUnit"); + testKindOfQuantity.persistenceUnit = new DelayedPromiseWithProps(childUnit.key, async () => childUnit); + + const result = await Rules.incompatibleUnitPropertyOverride(testClass.properties![0] as PrimitiveProperty); + + let resultHasEntries = false; + for await (const diagnostic of result!) { + resultHasEntries = true; + expect(diagnostic).to.not.be.undefined; + expect(diagnostic!.ecDefinition).to.equal(testClass.properties![0]); + expect(diagnostic!.messageArgs).to.eql([ + testClass.fullName, "TestProperty", rootBaseClass.fullName, + testBaseKindOfQuantity.fullName, baseUnit.fullName, childUnit.fullName, testKindOfQuantity.fullName, + ]); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.IncompatibleUnitPropertyOverride); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.Property); + } + expect(resultHasEntries, "expected rule to return an AsyncIterable with entries.").to.be.true; + }); + + it("IncompatibleUnitPropertyOverride, compatible units, rule passes.", async () => { + await (testBaseClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); + await (testClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.Integer); + + const basePropJson = { + name: "TestProperty", + type: "PrimitiveProperty", + kindOfQuantity: "TestSchema.TestBaseKoQ", + }; + + const childPropJson = { + name: "TestProperty", + type: "PrimitiveProperty", + kindOfQuantity: "TestSchema.TestKoQ", + }; + + const baseProperty = testBaseClass.properties![0]; + baseProperty.deserialize(basePropJson); + + const childProperty = testClass.properties![0]; + childProperty.deserialize(childPropJson); + + const baseUnit = new Unit(schema, "TestUnit"); + testBaseKindOfQuantity.persistenceUnit = new DelayedPromiseWithProps(baseUnit.key, async () => baseUnit); + + const childUnit = new Unit(schema, "TestUnit"); + testKindOfQuantity.persistenceUnit = new DelayedPromiseWithProps(childUnit.key, async () => childUnit); + + const result = await Rules.incompatibleUnitPropertyOverride(testClass.properties![0] as PrimitiveProperty); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("IncompatibleUnitPropertyOverride, no base unit, rule passes.", async () => { + await (testBaseClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); + await (testClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); + + const basePropJson = { + name: "TestProperty", + type: "PrimitiveProperty", + kindOfQuantity: "TestSchema.TestBaseKoQ", + }; + + const childPropJson = { + name: "TestProperty", + type: "PrimitiveProperty", + kindOfQuantity: "TestSchema.TestKoQ", + }; + + const baseProperty = testBaseClass.properties![0]; + baseProperty.deserialize(basePropJson); + + const childProperty = testClass.properties![0]; + childProperty.deserialize(childPropJson); + + const childUnit = new Unit(schema, "TestUnit"); + testKindOfQuantity.persistenceUnit = new DelayedPromiseWithProps(childUnit.key, async () => childUnit); + + const result = await Rules.incompatibleUnitPropertyOverride(testClass.properties![0] as PrimitiveProperty); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("IncompatibleUnitPropertyOverride, not a KOQ, rule passes.", async () => { + await (testBaseClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); + await (testClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); + + const basePropJson = { + name: "TestProperty", + type: "PrimitiveProperty", + }; + + const childPropJson = { + name: "TestProperty", + type: "PrimitiveProperty", + }; + + const baseProperty = testBaseClass.properties![0]; + baseProperty.deserialize(basePropJson); + + const childProperty = testClass.properties![0]; + childProperty.deserialize(childPropJson); + + const result = await Rules.incompatibleUnitPropertyOverride(testClass.properties![0] as PrimitiveProperty); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("IncompatibleUnitPropertyOverride, incompatible property types, rule passes.", async () => { + await (testBaseClass as ECClass as MutableClass).createPrimitiveProperty("TestProperty", PrimitiveType.String); + await (testClass as ECClass as MutableClass).createPrimitiveArrayProperty("TestProperty", PrimitiveType.Integer); + + const basePropJson = { + name: "TestProperty", + type: "PrimitiveProperty", + kindOfQuantity: "TestSchema.TestBaseKoQ", + }; + + const childPropJson = { + name: "TestProperty", + type: "PrimitiveArrayProperty", + kindOfQuantity: "TestSchema.TestKoQ", + }; + + const baseProperty = testBaseClass.properties![0]; + baseProperty.deserialize(basePropJson); + + const childProperty = testClass.properties![0]; + childProperty.deserialize(childPropJson); + + const baseUnit = new Unit(schema, "TestUnit"); + testBaseKindOfQuantity.persistenceUnit = new DelayedPromiseWithProps(baseUnit.key, async () => baseUnit); + + const childUnit = new Unit(schema, "TestUnit"); + testKindOfQuantity.persistenceUnit = new DelayedPromiseWithProps(childUnit.key, async () => childUnit); + + const result = await Rules.incompatibleUnitPropertyOverride(testClass.properties![0] as PrimitiveProperty); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); +}); diff --git a/core/ecschema-metadata/test/Validation/ECRules/RelationshipConstraintRules.test.ts b/core/ecschema-metadata/test/Validation/ECRules/RelationshipConstraintRules.test.ts new file mode 100644 index 0000000..45c60c5 --- /dev/null +++ b/core/ecschema-metadata/test/Validation/ECRules/RelationshipConstraintRules.test.ts @@ -0,0 +1,234 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { expect } from "chai"; + +import { SchemaContext } from "../../../src/Context"; +import { RelationshipClass } from "../../../src/Metadata/RelationshipClass"; +import { Schema } from "../../../src/Metadata/Schema"; +import { DiagnosticCategory, DiagnosticType } from "../../../src/Validation/Diagnostic"; +import * as Rules from "../../../src/Validation/ECRules"; +import { createSchemaJsonWithItems } from "../../TestUtils/DeserializationHelpers"; + +describe("RelationshipConstraintRule tests", () => { + let schema: Schema; + + function createBaseRelationship(polymorphic: boolean, sourceConstraintClasses: any, targetConstraintClasses: any) { + return { + BaseRelationship: { + schemaItemType: "RelationshipClass", + strength: "referencing", + strengthDirection: "forward", + source: { + polymorphic, + multiplicity: "(0..*)", + roleLabel: "Source RoleLabel", + ...sourceConstraintClasses, + }, + target: { + polymorphic, + multiplicity: "(0..*)", + roleLabel: "Target RoleLabel", + ...targetConstraintClasses, + }, + }, + }; + } + + function createChildRelationship(polymorphic: boolean, sourceConstraintClasses: any, targetConstraintClasses: any) { + return { + ChildRelationship: { + baseClass: "TestSchema.BaseRelationship", + schemaItemType: "RelationshipClass", + strength: "referencing", + strengthDirection: "forward", + source: { + polymorphic, + multiplicity: "(0..*)", + roleLabel: "Source RoleLabel", + ...sourceConstraintClasses, + }, + target: { + polymorphic, + multiplicity: "(0..*)", + roleLabel: "Target RoleLabel", + ...targetConstraintClasses, + }, + }, + }; + } + + /** Create test constraint classes along with the provided relationships where: + * S: Source, T: Target, B: Base, D: Derived, E: Entity, M: Mixin, R: Relationship, A: Abstract + * Example: SBE1= Source Base Class #1 + */ + function createSchemaJson(baseRelationship: any, childRelationship: any) { + return createSchemaJsonWithItems({ + ...baseRelationship, + ...childRelationship, + + SBE1: { schemaItemType: "EntityClass", modifier: "Abstract" }, + SDE1: { schemaItemType: "EntityClass", baseClass: "TestSchema.SBE1" }, + SDE2: { schemaItemType: "EntityClass", baseClass: "TestSchema.SBE1" }, + TBE1: { schemaItemType: "EntityClass", modifier: "Abstract" }, + TDE1: { schemaItemType: "EntityClass", baseClass: "TestSchema.TBE1" }, + TDE2: { schemaItemType: "EntityClass", baseClass: "TestSchema.TBE1" }, + }); + } + + describe("atLeastOneConstraintClassDefined rule tests", () => { + it("constraints contains one concrete class, rule passes", async () => { + const sourceConstraints = { + abstractConstraint: "TestSchema.SBE1", + constraintClasses: ["TestSchema.SDE1"], + }; + const targetConstraints = { + abstractConstraint: "TestSchema.TBE1", + constraintClasses: ["TestSchema.TDE1"], + }; + const baseJson = createBaseRelationship(true, sourceConstraints, targetConstraints); + schema = await Schema.fromJson(createSchemaJson(baseJson, undefined), new SchemaContext()); + const relationship = schema.getItemSync("BaseRelationship") as RelationshipClass; + + const sourceResult = await Rules.atLeastOneConstraintClassDefined(relationship.source); + const targetResult = await Rules.atLeastOneConstraintClassDefined(relationship.target); + + for await (const _diagnostic of sourceResult!) { + expect(false, "Rule should have passed").to.be.true; + } + for await (const _diagnostic of targetResult!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("source constraint contains no constraint class, rule violated", async () => { + const sourceConstraints = { + abstractConstraint: "TestSchema.SBE1", + constraintClasses: [], + }; + const targetConstraints = { + abstractConstraint: "TestSchema.TBE1", + constraintClasses: ["TestSchema.TDE1"], + }; + const baseJson = createBaseRelationship(true, sourceConstraints, targetConstraints); + schema = await Schema.fromJson(createSchemaJson(baseJson, undefined), new SchemaContext()); + const relationship = schema.getItemSync("BaseRelationship") as RelationshipClass; + + const sourceResult = await Rules.atLeastOneConstraintClassDefined(relationship.source); + const targetResult = await Rules.atLeastOneConstraintClassDefined(relationship.target); + + for await (const _diagnostic of targetResult!) { + expect(false, "Rule should have passed").to.be.true; + } + + let resultHasEntries = false; + for await (const diagnostic of sourceResult!) { + resultHasEntries = true; + expect(diagnostic).not.undefined; + expect(diagnostic!.ecDefinition).to.equal(relationship.source); + expect(diagnostic!.messageArgs).to.eql(["Source", "TestSchema.BaseRelationship"]); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.AtLeastOneConstraintClassDefined); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.RelationshipConstraint); + break; + } + expect(resultHasEntries, "expected rule to return an AsyncIterable with entries.").to.be.true; + }); + + it("target constraint contains no concrete class, rule violated", async () => { + const sourceConstraints = { + abstractConstraint: "TestSchema.SBE1", + constraintClasses: ["TestSchema.SDE1"], + }; + const targetConstraints = { + abstractConstraint: "TestSchema.TBE1", + constraintClasses: [], + }; + const baseJson = createBaseRelationship(true, sourceConstraints, targetConstraints); + schema = await Schema.fromJson(createSchemaJson(baseJson, undefined), new SchemaContext()); + const relationship = schema.getItemSync("BaseRelationship") as RelationshipClass; + + const sourceResult = await Rules.atLeastOneConstraintClassDefined(relationship.source); + const targetResult = await Rules.atLeastOneConstraintClassDefined(relationship.target); + + for await (const _diagnostic of sourceResult!) { + expect(false, "Rule should have passed").to.be.true; + } + + let resultHasEntries = false; + for await (const diagnostic of targetResult!) { + resultHasEntries = true; + expect(diagnostic).not.undefined; + expect(diagnostic!.ecDefinition).to.equal(relationship.target); + expect(diagnostic!.messageArgs).to.eql(["Target", "TestSchema.BaseRelationship"]); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.AtLeastOneConstraintClassDefined); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.RelationshipConstraint); + break; + } + expect(resultHasEntries, "expected rule to return an AsyncIterable with entries.").to.be.true; + }); + }); + + describe("abstractConstraintMustExistWithMultipleConstraints", () => { + it("multiple constraints, abstract constraint exists, rule passes", async () => { + const sourceConstraints = { + abstractConstraint: "TestSchema.SBE1", + constraintClasses: ["TestSchema.SDE1", "TestSchema.SDE2"], + }; + const targetConstraints = { + abstractConstraint: "TestSchema.TBE1", + constraintClasses: ["TestSchema.TDE1", "TestSchema.TDE2"], + }; + const baseJson = createBaseRelationship(true, sourceConstraints, targetConstraints); + schema = await Schema.fromJson(createSchemaJson(baseJson, undefined), new SchemaContext()); + const relationship = schema.getItemSync("BaseRelationship") as RelationshipClass; + + const sourceResult = await Rules.abstractConstraintMustExistWithMultipleConstraints(relationship.source); + const targetResult = await Rules.abstractConstraintMustExistWithMultipleConstraints(relationship.target); + + for await (const _diagnostic of sourceResult!) { + expect(false, "Rule should have passed").to.be.true; + } + for await (const _diagnostic of targetResult!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("multiple constraints, abstract constraint exists in base class, rule passes", async () => { + const baseSourceConstraints = { + abstractConstraint: "TestSchema.SBE1", + constraintClasses: ["TestSchema.SBE1"], + }; + + const baseTargetConstraints = { + abstractConstraint: "TestSchema.TBE1", + constraintClasses: ["TestSchema.TBE1"], + }; + + const childSourceConstraints = { + constraintClasses: ["TestSchema.SDE1", "TestSchema.SDE2"], + }; + const childTargetConstraints = { + constraintClasses: ["TestSchema.TDE1", "TestSchema.TDE2"], + }; + + const baseJson = createBaseRelationship(true, baseSourceConstraints, baseTargetConstraints); + const childJson = createChildRelationship(true, childSourceConstraints, childTargetConstraints); + schema = await Schema.fromJson(createSchemaJson(baseJson, childJson), new SchemaContext()); + const relationship = schema.getItemSync("ChildRelationship") as RelationshipClass; + + const sourceResult = await Rules.abstractConstraintMustExistWithMultipleConstraints(relationship.source); + const targetResult = await Rules.abstractConstraintMustExistWithMultipleConstraints(relationship.target); + + for await (const _diagnostic of sourceResult!) { + expect(false, "Rule should have passed").to.be.true; + } + for await (const _diagnostic of targetResult!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + }); +}); diff --git a/core/ecschema-metadata/test/Validation/ECRules/RelationshipRules.test.ts b/core/ecschema-metadata/test/Validation/ECRules/RelationshipRules.test.ts new file mode 100644 index 0000000..da081c0 --- /dev/null +++ b/core/ecschema-metadata/test/Validation/ECRules/RelationshipRules.test.ts @@ -0,0 +1,548 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { expect } from "chai"; + +import { SchemaContext } from "../../../src/Context"; +import { RelationshipClass } from "../../../src/Metadata/RelationshipClass"; +import { Schema } from "../../../src/Metadata/Schema"; +import { DiagnosticCategory, DiagnosticType } from "../../../src/Validation/Diagnostic"; +import * as Rules from "../../../src/Validation/ECRules"; +import { createSchemaJsonWithItems } from "../../TestUtils/DeserializationHelpers"; + +describe("RelationshipRule tests", () => { + let schema: Schema; + + function createBaseRelationship(polymorphic: boolean, sourceConstraintClasses: any, targetConstraintClasses: any) { + return { + BaseRelationship: { + schemaItemType: "RelationshipClass", + strength: "referencing", + strengthDirection: "forward", + source: { + polymorphic, + multiplicity: "(0..*)", + roleLabel: "Source RoleLabel", + ...sourceConstraintClasses, + }, + target: { + polymorphic, + multiplicity: "(0..*)", + roleLabel: "Target RoleLabel", + ...targetConstraintClasses, + }, + }, + }; + } + + function createChildRelationship(polymorphic: boolean, sourceConstraintClasses: any, targetConstraintClasses: any) { + return { + ChildRelationship: { + baseClass: "TestSchema.BaseRelationship", + schemaItemType: "RelationshipClass", + strength: "referencing", + strengthDirection: "forward", + source: { + polymorphic, + multiplicity: "(0..*)", + roleLabel: "Source RoleLabel", + ...sourceConstraintClasses, + }, + target: { + polymorphic, + multiplicity: "(0..*)", + roleLabel: "Target RoleLabel", + ...targetConstraintClasses, + }, + }, + }; + } + + /** Create test constraint classes along with the provided relationships where: + * S: Source, T: Target, B: Base, D: Derived, E: Entity, M: Mixin, R: Relationship, A: Abstract + * Example: SBE1= Source Base Class #1 + */ + function createSchemaJson(baseRelationship: any, childRelationship: any) { + return createSchemaJsonWithItems({ + ...baseRelationship, + ...childRelationship, + + SBE1: { schemaItemType: "EntityClass" }, + SDE1: { schemaItemType: "EntityClass", baseClass: "TestSchema.SBE1" }, + SDE2: { schemaItemType: "EntityClass", baseClass: "TestSchema.SBE1" }, + TBE1: { schemaItemType: "EntityClass" }, + TDE1: { schemaItemType: "EntityClass", baseClass: "TestSchema.TBE1" }, + TDE2: { schemaItemType: "EntityClass", baseClass: "TestSchema.TBE1" }, + + SM1: { schemaItemType: "Mixin", appliesTo: "TestSchema.SDE1" }, + TM1: { schemaItemType: "Mixin", appliesTo: "TestSchema.TDE1" }, + + SBR1: { ...createNavPropRelationship({ constraintClasses: ["TestSchema.SBE1"] }, { constraintClasses: ["TestSchema.SBE1"] }) }, + SDR1: { baseClass: "TestSchema.SBR1", ...createNavPropRelationship({ constraintClasses: ["TestSchema.SDE1"] }, { constraintClasses: ["TestSchema.SDE1"] }) }, + TBR1: { ...createNavPropRelationship({ constraintClasses: ["TestSchema.TBE1"] }, { constraintClasses: ["TestSchema.TBE1"] }) }, + TDR1: { baseClass: "TestSchema.TBR1", ...createNavPropRelationship({ constraintClasses: ["TestSchema.TDE1"] }, { constraintClasses: ["TestSchema.TDE1"] }) }, + + E1: { schemaItemType: "EntityClass" }, + E2: { schemaItemType: "EntityClass" }, + M1: { schemaItemType: "Mixin", appliesTo: "TestSchema.E1" }, + R1: { ...createNavPropRelationship({ constraintClasses: ["TestSchema.E1"] }, { constraintClasses: ["TestSchema.E1"] }) }, + }); + } + + function createNavPropRelationship(sourceConstraintClasses: any, targetConstraintClasses: any) { + return { + schemaItemType: "RelationshipClass", + strength: "Embedding", + strengthDirection: "Forward", + modifier: "Sealed", + source: { + polymorphic: true, + multiplicity: "(0..*)", + roleLabel: "Source RoleLabel", + ...sourceConstraintClasses, + }, + target: { + polymorphic: true, + multiplicity: "(0..*)", + roleLabel: "Target RoleLabel", + ...targetConstraintClasses, + }, + }; + } + + describe("AbstractConstraintMustNarrowBaseConstraints rule tests", () => { + it("supported source and target constraint classes, rule passes", async () => { + const baseJson = createBaseRelationship(true, { constraintClasses: ["TestSchema.SBE1"] }, { constraintClasses: ["TestSchema.TBE1"] }); + const childJson = createChildRelationship(true, { constraintClasses: ["TestSchema.SDE1", "TestSchema.SDE2"] }, { constraintClasses: ["TestSchema.TDE1", "TestSchema.TDE2"] }); + schema = await Schema.fromJson(createSchemaJson(baseJson, childJson), new SchemaContext()); + const relationship = schema.getItemSync("ChildRelationship") as RelationshipClass; + + const result = await Rules.abstractConstraintMustNarrowBaseConstraints(relationship); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("no base class, rule passes", async () => { + const baseJson = createBaseRelationship(true, { constraintClasses: ["TestSchema.SBE1"] }, { constraintClasses: ["TestSchema.TBE1"] }); + const childJson = createChildRelationship(true, { constraintClasses: ["TestSchema.SDE1", "TestSchema.SDE2"] }, { constraintClasses: ["TestSchema.TDE1", "TestSchema.TDE2"] }); + delete childJson.ChildRelationship.baseClass; + schema = await Schema.fromJson(createSchemaJson(baseJson, childJson), new SchemaContext()); + const relationship = schema.getItemSync("ChildRelationship") as RelationshipClass; + + const result = await Rules.abstractConstraintMustNarrowBaseConstraints(relationship); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("unsupported source constraint class, rule violated", async () => { + const baseJson = createBaseRelationship(true, { constraintClasses: ["TestSchema.SBE1"] }, { constraintClasses: ["TestSchema.TBE1"] }); + const childJson = createChildRelationship(true, { constraintClasses: ["TestSchema.E1"] }, { constraintClasses: ["TestSchema.TDE1"] }); + schema = await Schema.fromJson(createSchemaJson(baseJson, childJson), new SchemaContext()); + const relationship = schema.getItemSync("ChildRelationship") as RelationshipClass; + + const result = await Rules.abstractConstraintMustNarrowBaseConstraints(relationship); + + let resultHasEntries = false; + for await (const diagnostic of result!) { + resultHasEntries = true; + expect(diagnostic).not.undefined; + expect(diagnostic!.ecDefinition).to.equal(relationship); + expect(diagnostic!.messageArgs).to.eql(["TestSchema.E1", "Source", "TestSchema.ChildRelationship", "TestSchema.BaseRelationship"]); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.AbstractConstraintMustNarrowBaseConstraints); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.SchemaItem); + break; + } + expect(resultHasEntries, "expected rule to return an AsyncIterable with entries.").to.be.true; + }); + + it("unsupported source and target constraint classes, rule violated twice", async () => { + const baseJson = createBaseRelationship(true, { constraintClasses: ["TestSchema.SBE1"] }, { constraintClasses: ["TestSchema.TBE1"] }); + const childJson = createChildRelationship(true, { constraintClasses: ["TestSchema.E1"] }, { constraintClasses: ["TestSchema.E2"] }); + schema = await Schema.fromJson(createSchemaJson(baseJson, childJson), new SchemaContext()); + const relationship = schema.getItemSync("ChildRelationship") as RelationshipClass; + + const result = await Rules.abstractConstraintMustNarrowBaseConstraints(relationship); + + let count = 0; + for await (const diagnostic of result!) { + count++; + const expectedArgs = count === 1 ? ["TestSchema.E1", "Source", "TestSchema.ChildRelationship", "TestSchema.BaseRelationship"] : + ["TestSchema.E2", "Target", "TestSchema.ChildRelationship", "TestSchema.BaseRelationship"]; + + expect(diagnostic).not.undefined; + expect(diagnostic!.ecDefinition).to.equal(relationship); + expect(diagnostic!.messageArgs).to.eql(expectedArgs); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.AbstractConstraintMustNarrowBaseConstraints); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.SchemaItem); + } + expect(count, "expected rule to return an AsyncIterable with entries.").to.equal(2); + }); + }); + + describe("DerivedConstraintsMustNarrowBaseConstraints rule tests", () => { + it("supported source and target constraint classes, rule passes", async () => { + const baseJson = createBaseRelationship(true, { constraintClasses: ["TestSchema.SBE1"] }, { constraintClasses: ["TestSchema.TBE1"] }); + const childJson = createChildRelationship(true, { constraintClasses: ["TestSchema.SDE1", "TestSchema.SDE2"] }, { constraintClasses: ["TestSchema.TDE1", "TestSchema.TDE2"] }); + schema = await Schema.fromJson(createSchemaJson(baseJson, childJson), new SchemaContext()); + const relationship = schema.getItemSync("ChildRelationship") as RelationshipClass; + + const result = await Rules.derivedConstraintsMustNarrowBaseConstraints(relationship); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("no base class, rule passes", async () => { + const baseJson = createBaseRelationship(true, { constraintClasses: ["TestSchema.SBE1"] }, { constraintClasses: ["TestSchema.TBE1"] }); + const childJson = createChildRelationship(true, { constraintClasses: ["TestSchema.SDE1", "TestSchema.SDE2"] }, { constraintClasses: ["TestSchema.TDE1", "TestSchema.TDE2"] }); + delete childJson.ChildRelationship.baseClass; + schema = await Schema.fromJson(createSchemaJson(baseJson, childJson), new SchemaContext()); + const relationship = schema.getItemSync("ChildRelationship") as RelationshipClass; + + const result = await Rules.derivedConstraintsMustNarrowBaseConstraints(relationship); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("unsupported source constraint class, rule violated", async () => { + const baseJson = createBaseRelationship(true, { constraintClasses: ["TestSchema.SBE1"] }, { constraintClasses: ["TestSchema.TBE1"] }); + const childJson = createChildRelationship(true, { constraintClasses: ["TestSchema.SDE1", "TestSchema.E1"] }, { constraintClasses: ["TestSchema.TDE1"] }); + schema = await Schema.fromJson(createSchemaJson(baseJson, childJson), new SchemaContext()); + const relationship = schema.getItemSync("ChildRelationship") as RelationshipClass; + + const result = await Rules.derivedConstraintsMustNarrowBaseConstraints(relationship); + + let resultHasEntries = false; + for await (const diagnostic of result!) { + resultHasEntries = true; + expect(diagnostic).not.undefined; + expect(diagnostic!.ecDefinition).to.equal(relationship); + expect(diagnostic!.messageArgs).to.eql(["TestSchema.E1", "Source", "TestSchema.ChildRelationship", "TestSchema.BaseRelationship"]); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.DerivedConstraintsMustNarrowBaseConstraints); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.SchemaItem); + break; + } + expect(resultHasEntries, "expected rule to return an AsyncIterable with entries.").to.be.true; + }); + + it("unsupported source and target constraint classes, rule violated twice", async () => { + const baseJson = createBaseRelationship(true, { constraintClasses: ["TestSchema.SBE1"] }, { constraintClasses: ["TestSchema.TBE1"] }); + const childJson = createChildRelationship(true, { constraintClasses: ["TestSchema.SDE1", "TestSchema.E1"] }, { constraintClasses: ["TestSchema.E2"] }); + schema = await Schema.fromJson(createSchemaJson(baseJson, childJson), new SchemaContext()); + const relationship = schema.getItemSync("ChildRelationship") as RelationshipClass; + + const result = await Rules.derivedConstraintsMustNarrowBaseConstraints(relationship); + + let count = 0; + for await (const diagnostic of result!) { + count++; + const expectedArgs = count === 1 ? ["TestSchema.E1", "Source", "TestSchema.ChildRelationship", "TestSchema.BaseRelationship"] : + ["TestSchema.E2", "Target", "TestSchema.ChildRelationship", "TestSchema.BaseRelationship"]; + + expect(diagnostic).not.undefined; + expect(diagnostic!.ecDefinition).to.equal(relationship); + expect(diagnostic!.messageArgs).to.eql(expectedArgs); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.DerivedConstraintsMustNarrowBaseConstraints); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.SchemaItem); + } + expect(count, "expected rule to return an AsyncIterable with entries.").to.equal(2); + }); + }); + + describe("ConstraintClassesDeriveFromAbstractContraint rule tests", () => { + + it("supported source and target constraint Entity classes, rule passes", async () => { + const sourceConstraints = { + abstractConstraint: "TestSchema.SBE1", + constraintClasses: ["TestSchema.SDE1", "TestSchema.SDE2"], + }; + const targetConstraints = { + abstractConstraint: "TestSchema.TBE1", + constraintClasses: ["TestSchema.TDE1", "TestSchema.TDE2"], + }; + const baseJson = createBaseRelationship(true, sourceConstraints, targetConstraints); + schema = await Schema.fromJson(createSchemaJson(baseJson, undefined), new SchemaContext()); + const relationship = schema.getItemSync("BaseRelationship") as RelationshipClass; + + const result = await Rules.constraintClassesDeriveFromAbstractContraint(relationship); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("source and target constraint same as abstract constraint, rule passes", async () => { + const sourceConstraints = { + abstractConstraint: "TestSchema.SBE1", + constraintClasses: ["TestSchema.SBE1"], + }; + const targetConstraints = { + abstractConstraint: "TestSchema.TBE1", + constraintClasses: ["TestSchema.TBE1"], + }; + const baseJson = createBaseRelationship(true, sourceConstraints, targetConstraints); + schema = await Schema.fromJson(createSchemaJson(baseJson, undefined), new SchemaContext()); + const relationship = schema.getItemSync("BaseRelationship") as RelationshipClass; + + const result = await Rules.constraintClassesDeriveFromAbstractContraint(relationship); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("supported source and target constraint Mixin classes, rule passes", async () => { + const sourceConstraints = { + abstractConstraint: "TestSchema.SBE1", + constraintClasses: ["TestSchema.SM1"], + }; + const targetConstraints = { + abstractConstraint: "TestSchema.TBE1", + constraintClasses: ["TestSchema.TM1"], + }; + + const baseJson = createBaseRelationship(true, sourceConstraints, targetConstraints); + schema = await Schema.fromJson(createSchemaJson(baseJson, undefined), new SchemaContext()); + const relationship = schema.getItemSync("BaseRelationship") as RelationshipClass; + + const result = await Rules.constraintClassesDeriveFromAbstractContraint(relationship); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("source and target constraint Mixin classes same as abstract, rule passes", async () => { + const sourceConstraints = { + abstractConstraint: "TestSchema.SM1", + constraintClasses: ["TestSchema.SM1"], + }; + const targetConstraints = { + abstractConstraint: "TestSchema.TM1", + constraintClasses: ["TestSchema.TM1"], + }; + + const baseJson = createBaseRelationship(true, sourceConstraints, targetConstraints); + schema = await Schema.fromJson(createSchemaJson(baseJson, undefined), new SchemaContext()); + const relationship = schema.getItemSync("BaseRelationship") as RelationshipClass; + + const result = await Rules.constraintClassesDeriveFromAbstractContraint(relationship); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("supported source and target constraint Relationship classes, rule passes", async () => { + const sourceConstraints = { + abstractConstraint: "TestSchema.SBR1", + constraintClasses: ["TestSchema.SDR1"], + }; + const targetConstraints = { + abstractConstraint: "TestSchema.TBR1", + constraintClasses: ["TestSchema.TDR1"], + }; + + const baseJson = createBaseRelationship(true, sourceConstraints, targetConstraints); + schema = await Schema.fromJson(createSchemaJson(baseJson, undefined), new SchemaContext()); + const relationship = schema.getItemSync("BaseRelationship") as RelationshipClass; + + const result = await Rules.constraintClassesDeriveFromAbstractContraint(relationship); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("supported constraints, abstract constraint defined in base Relationship, rule passes", async () => { + const baseSourceConstraints = { + abstractConstraint: "TestSchema.SBE1", + constraintClasses: ["TestSchema.SBE1"], + }; + + const baseTargetConstraints = { + abstractConstraint: "TestSchema.TBE1", + constraintClasses: ["TestSchema.TBE1"], + }; + + const childSourceConstraints = { + constraintClasses: ["TestSchema.SDE1", "TestSchema.SDE2"], + }; + const childTargetConstraints = { + constraintClasses: ["TestSchema.TDE1", "TestSchema.TDE2"], + }; + + const baseJson = createBaseRelationship(true, baseSourceConstraints, baseTargetConstraints); + const childJson = createChildRelationship(true, childSourceConstraints, childTargetConstraints); + schema = await Schema.fromJson(createSchemaJson(baseJson, childJson), new SchemaContext()); + const relationship = schema.getItemSync("ChildRelationship") as RelationshipClass; + + const result = await Rules.constraintClassesDeriveFromAbstractContraint(relationship); + + for await (const _diagnostic of result!) { + expect(false, "Rule should have passed").to.be.true; + } + }); + + it("unsupported source constraint Entity class, rule violated", async () => { + const sourceConstraints = { + abstractConstraint: "TestSchema.SBE1", + constraintClasses: ["TestSchema.SDE1", "TestSchema.E1"], + }; + const targetConstraints = { + abstractConstraint: "TestSchema.TBE1", + constraintClasses: ["TestSchema.TDE1", "TestSchema.TDE2"], + }; + const baseJson = createBaseRelationship(true, sourceConstraints, targetConstraints); + schema = await Schema.fromJson(createSchemaJson(baseJson, undefined), new SchemaContext()); + const relationship = schema.getItemSync("BaseRelationship") as RelationshipClass; + + const result = await Rules.constraintClassesDeriveFromAbstractContraint(relationship); + + let resultHasEntries = false; + for await (const diagnostic of result!) { + resultHasEntries = true; + expect(diagnostic).not.undefined; + expect(diagnostic!.ecDefinition).to.equal(relationship); + expect(diagnostic!.messageArgs).to.eql(["TestSchema.E1", "Source", "TestSchema.BaseRelationship", "TestSchema.SBE1"]); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.ConstraintClassesDeriveFromAbstractContraint); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.SchemaItem); + break; + } + expect(resultHasEntries, "expected rule to return an AsyncIterable with entries.").to.be.true; + }); + + it("unsupported source constraint Mixin class, rule violated", async () => { + const sourceConstraints = { + abstractConstraint: "TestSchema.SBE1", + constraintClasses: ["TestSchema.M1"], + }; + const targetConstraints = { + abstractConstraint: "TestSchema.TBE1", + constraintClasses: ["TestSchema.TM1"], + }; + const baseJson = createBaseRelationship(true, sourceConstraints, targetConstraints); + schema = await Schema.fromJson(createSchemaJson(baseJson, undefined), new SchemaContext()); + const relationship = schema.getItemSync("BaseRelationship") as RelationshipClass; + + const result = await Rules.constraintClassesDeriveFromAbstractContraint(relationship); + + let resultHasEntries = false; + for await (const diagnostic of result!) { + resultHasEntries = true; + expect(diagnostic).not.undefined; + expect(diagnostic!.ecDefinition).to.equal(relationship); + expect(diagnostic!.messageArgs).to.eql(["TestSchema.M1", "Source", "TestSchema.BaseRelationship", "TestSchema.SBE1"]); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.ConstraintClassesDeriveFromAbstractContraint); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.SchemaItem); + break; + } + expect(resultHasEntries, "expected rule to return an AsyncIterable with entries.").to.be.true; + }); + + it("abstract constraint not an Entity class, constraint class is Mixin, rule violated", async () => { + const sourceConstraints = { + abstractConstraint: "TestSchema.R1", + constraintClasses: ["TestSchema.SM1"], + }; + const targetConstraints = { + abstractConstraint: "TestSchema.TBE1", + constraintClasses: ["TestSchema.TM1"], + }; + const baseJson = createBaseRelationship(true, sourceConstraints, targetConstraints); + schema = await Schema.fromJson(createSchemaJson(baseJson, undefined), new SchemaContext()); + const relationship = schema.getItemSync("BaseRelationship") as RelationshipClass; + + const result = await Rules.constraintClassesDeriveFromAbstractContraint(relationship); + + let resultHasEntries = false; + for await (const diagnostic of result!) { + resultHasEntries = true; + expect(diagnostic).not.undefined; + expect(diagnostic!.ecDefinition).to.equal(relationship); + expect(diagnostic!.messageArgs).to.eql(["TestSchema.SM1", "Source", "TestSchema.BaseRelationship", "TestSchema.R1"]); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.ConstraintClassesDeriveFromAbstractContraint); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.SchemaItem); + break; + } + expect(resultHasEntries, "expected rule to return an AsyncIterable with entries.").to.be.true; + }); + + it("unsupported source constraint Relationship class, rule violated", async () => { + const sourceConstraints = { + abstractConstraint: "TestSchema.SBR1", + constraintClasses: ["TestSchema.R1"], + }; + const targetConstraints = { + abstractConstraint: "TestSchema.TBR1", + constraintClasses: ["TestSchema.TDR1"], + }; + const baseJson = createBaseRelationship(true, sourceConstraints, targetConstraints); + schema = await Schema.fromJson(createSchemaJson(baseJson, undefined), new SchemaContext()); + const relationship = schema.getItemSync("BaseRelationship") as RelationshipClass; + + const result = await Rules.constraintClassesDeriveFromAbstractContraint(relationship); + + let resultHasEntries = false; + for await (const diagnostic of result!) { + resultHasEntries = true; + expect(diagnostic).not.undefined; + expect(diagnostic!.ecDefinition).to.equal(relationship); + expect(diagnostic!.messageArgs).to.eql(["TestSchema.R1", "Source", "TestSchema.BaseRelationship", "TestSchema.SBR1"]); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.ConstraintClassesDeriveFromAbstractContraint); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.SchemaItem); + break; + } + expect(resultHasEntries, "expected rule to return an AsyncIterable with entries.").to.be.true; + }); + + it("unsupported source and target constraint Entity classes, rule violated twice", async () => { + const sourceConstraints = { + abstractConstraint: "TestSchema.SBE1", + constraintClasses: ["TestSchema.SDE1", "TestSchema.E1"], + }; + const targetConstraints = { + abstractConstraint: "TestSchema.TBE1", + constraintClasses: ["TestSchema.TDE1", "TestSchema.E2"], + }; + + const baseJson = createBaseRelationship(true, sourceConstraints, targetConstraints); + schema = await Schema.fromJson(createSchemaJson(baseJson, undefined), new SchemaContext()); + const relationship = schema.getItemSync("BaseRelationship") as RelationshipClass; + + const result = await Rules.constraintClassesDeriveFromAbstractContraint(relationship); + + let count = 0; + for await (const diagnostic of result!) { + count++; + const expectedArgs = count === 1 ? ["TestSchema.E1", "Source", "TestSchema.BaseRelationship", "TestSchema.SBE1"] : + ["TestSchema.E2", "Target", "TestSchema.BaseRelationship", "TestSchema.TBE1"]; + + expect(diagnostic).not.undefined; + expect(diagnostic!.ecDefinition).to.equal(relationship); + expect(diagnostic!.messageArgs).to.eql(expectedArgs); + expect(diagnostic!.category).to.equal(DiagnosticCategory.Error); + expect(diagnostic!.code).to.equal(Rules.DiagnosticCodes.ConstraintClassesDeriveFromAbstractContraint); + expect(diagnostic!.diagnosticType).to.equal(DiagnosticType.SchemaItem); + } + expect(count, "expected rule to return an AsyncIterable with entries.").to.equal(2); + }); + }); +}); diff --git a/core/ecschema-metadata/test/Validation/SchemaDiagnostics.test.ts b/core/ecschema-metadata/test/Validation/SchemaDiagnostics.test.ts deleted file mode 100644 index 5a8232b..0000000 --- a/core/ecschema-metadata/test/Validation/SchemaDiagnostics.test.ts +++ /dev/null @@ -1,178 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ - -import { expect } from "chai"; -import { IDiagnosticReporter, Diagnostic } from "../../src/Validation/Diagnostics"; -import { SchemaDiagnosticReporter } from "../../src/Validation/SchemaDiagnostics"; -import { I18NTestHelper, TestMessages } from "../TestUtils/I18NTestHelper"; -import sinon = require("sinon"); -import { Schema } from "../../src/Metadata/Schema"; -import { EntityClass } from "../../src/Metadata/EntityClass"; -import { I18N, I18NNamespace } from "@bentley/imodeljs-i18n"; - -describe("SchemaDiagnosticReporter tests", () => { - - class TestReporter implements IDiagnosticReporter { - public report(diagnostic: Diagnostic) { - console.assert(diagnostic !== undefined); - } - } - - afterEach(() => { - SchemaDiagnosticReporter.shutdown(); - I18NTestHelper.cleanup(); - sinon.restore(); - }); - - it("startup, i18n specified, registerNamespace called correctly", async () => { - const i18n = new I18N([], ""); - const i18nMock = sinon.mock(i18n); - const registerNamespace = i18nMock.expects("registerNamespace"); - registerNamespace.resolves(new I18NNamespace("ECSchemaMetaData", Promise.resolve())); - expect(!SchemaDiagnosticReporter.initialized); - await SchemaDiagnosticReporter.startup(i18n); - expect(registerNamespace.calledOnceWithExactly("ECSchemaMetaData")); - expect(SchemaDiagnosticReporter.initialized); - }); - - it("startup, i18n not specified, initialized is true", async () => { - expect(!SchemaDiagnosticReporter.initialized); - await SchemaDiagnosticReporter.startup(undefined); - expect(SchemaDiagnosticReporter.initialized); - }); - - it("startup called twice, registerNamespace called once", async () => { - const i18n = new I18N([], ""); - const i18nMock = sinon.mock(i18n); - const registerNamespace = i18nMock.expects("registerNamespace"); - registerNamespace.resolves(new I18NNamespace("ECSchemaMetaData", Promise.resolve())); - expect(!SchemaDiagnosticReporter.initialized); - await SchemaDiagnosticReporter.startup(i18n); - await SchemaDiagnosticReporter.startup(i18n); - expect(registerNamespace.calledOnceWithExactly("ECSchemaMetaData")); - }); - - it("register reporter succeeds", async () => { - await SchemaDiagnosticReporter.startup(undefined); - const reporter = new TestReporter; - SchemaDiagnosticReporter.registerReporter(reporter); - expect(SchemaDiagnosticReporter.reporters[0]).to.equal(reporter) - }); - - it("shutdown succeeds", async () => { - await SchemaDiagnosticReporter.startup(undefined); - expect(SchemaDiagnosticReporter.initialized); - SchemaDiagnosticReporter.registerReporter(new TestReporter); - SchemaDiagnosticReporter.shutdown(); - expect(!SchemaDiagnosticReporter.initialized); - expect(SchemaDiagnosticReporter.reporters.length).to.equal(0) - }); - - it("reportDiagnostic, no translation, succeeds", async () => { - const report = sinon.spy(TestReporter.prototype, "report"); - await SchemaDiagnosticReporter.startup(undefined); - expect(SchemaDiagnosticReporter.initialized); - SchemaDiagnosticReporter.registerReporter(new TestReporter); - - SchemaDiagnosticReporter.reportDiagnostic(TestMessages.TestErrorA, "Param1"); - - expect(report.calledOnce); - const diagnostic = report.args[0][0] as Diagnostic; - expect(diagnostic).not.undefined; - expect(diagnostic.category).equals(TestMessages.TestErrorA.category); - expect(diagnostic.code).equals(TestMessages.TestErrorA.code); - expect(diagnostic.defaultMessageText).equals("Test message with parameter 'Param1'"); - expect(diagnostic.messageText).equals("Test message with parameter 'Param1'"); - expect(diagnostic.schema).is.undefined; - expect(diagnostic.schemaItem).is.undefined; - expect(diagnostic.propertyName).is.undefined; - }); - - it("reportDiagnostic, with translation, succeeds", async () => { - const i18n = new I18N([], ""); - const i18nMock = sinon.mock(i18n); - const registerNamespace = i18nMock.expects("registerNamespace"); - registerNamespace.resolves(new I18NNamespace("ECSchemaMetaData", Promise.resolve())); - const translate = i18nMock.expects("translate"); - translate.returns("Translated Text '{0}'"); - const report = sinon.spy(TestReporter.prototype, "report"); - await SchemaDiagnosticReporter.startup(i18n); - SchemaDiagnosticReporter.registerReporter(new TestReporter); - - SchemaDiagnosticReporter.reportDiagnostic(TestMessages.TestErrorA, "Param1"); - - expect(translate.calledOnceWithExactly("ECSchemaMetaData:Diagnostics" + TestMessages.TestErrorA.key)); - expect(report.calledOnce); - const diagnostic = report.args[0][0] as Diagnostic; - expect(diagnostic).not.undefined; - expect(diagnostic.defaultMessageText).equals("Test message with parameter 'Param1'"); - expect(diagnostic.messageText).equals("Translated Text 'Param1'"); - }); - - it("reportSchemaDiagnostic, succeeds", async () => { - const report = sinon.spy(TestReporter.prototype, "report"); - await SchemaDiagnosticReporter.startup(undefined); - expect(SchemaDiagnosticReporter.initialized); - SchemaDiagnosticReporter.registerReporter(new TestReporter); - - const schema = new Schema("TestSchema", 1, 0, 0); - SchemaDiagnosticReporter.reportSchemaDiagnostic(schema, TestMessages.TestErrorA, "Param1"); - - expect(report.calledOnce); - const diagnostic = report.args[0][0] as Diagnostic; - expect(diagnostic).not.undefined; - expect(diagnostic.category).equals(TestMessages.TestErrorA.category); - expect(diagnostic.code).equals(TestMessages.TestErrorA.code); - expect(diagnostic.defaultMessageText).equals("Test message with parameter 'Param1'"); - expect(diagnostic.messageText).equals("Test message with parameter 'Param1'"); - expect(diagnostic.schema).equals(schema); - expect(diagnostic.schemaItem).is.undefined; - expect(diagnostic.propertyName).is.undefined; - }); - - it("reportSchemaItemDiagnostic, succeeds", async () => { - const report = sinon.spy(TestReporter.prototype, "report"); - await SchemaDiagnosticReporter.startup(undefined); - expect(SchemaDiagnosticReporter.initialized); - SchemaDiagnosticReporter.registerReporter(new TestReporter); - - const schema = new Schema("TestSchema", 1, 0, 0); - const schemaItem = new EntityClass(schema, "TestEntity"); - SchemaDiagnosticReporter.reportSchemaItemDiagnostic(schemaItem, TestMessages.TestErrorA, "Param1"); - - expect(report.calledOnce); - const diagnostic = report.args[0][0] as Diagnostic; - expect(diagnostic).not.undefined; - expect(diagnostic.category).equals(TestMessages.TestErrorA.category); - expect(diagnostic.code).equals(TestMessages.TestErrorA.code); - expect(diagnostic.defaultMessageText).equals("Test message with parameter 'Param1'"); - expect(diagnostic.messageText).equals("Test message with parameter 'Param1'"); - expect(diagnostic.schema).equals(schema); - expect(diagnostic.schemaItem).equals(schemaItem); - expect(diagnostic.propertyName).is.undefined; - }); - - it("reportPropertyDiagnostic, succeeds", async () => { - const report = sinon.spy(TestReporter.prototype, "report"); - await SchemaDiagnosticReporter.startup(undefined); - expect(SchemaDiagnosticReporter.initialized); - SchemaDiagnosticReporter.registerReporter(new TestReporter); - - const schema = new Schema("TestSchema", 1, 0, 0); - const schemaItem = new EntityClass(schema, "TestEntity"); - SchemaDiagnosticReporter.reportSchemaPropertyDiagnostic(schemaItem, "TestProperty", TestMessages.TestErrorA, "Param1"); - - expect(report.calledOnce); - const diagnostic = report.args[0][0] as Diagnostic; - expect(diagnostic).not.undefined; - expect(diagnostic.category).equals(TestMessages.TestErrorA.category); - expect(diagnostic.code).equals(TestMessages.TestErrorA.code); - expect(diagnostic.defaultMessageText).equals("Test message with parameter 'Param1'"); - expect(diagnostic.messageText).equals("Test message with parameter 'Param1'"); - expect(diagnostic.schema).equals(schema); - expect(diagnostic.schemaItem).equals(schemaItem); - expect(diagnostic.propertyName).equals("TestProperty"); - }); -}); diff --git a/core/ecschema-metadata/test/Validation/SchemaValidationVisitor.test.ts b/core/ecschema-metadata/test/Validation/SchemaValidationVisitor.test.ts new file mode 100644 index 0000000..151e5d1 --- /dev/null +++ b/core/ecschema-metadata/test/Validation/SchemaValidationVisitor.test.ts @@ -0,0 +1,801 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { expect } from "chai"; +import sinon = require("sinon"); + +import { SchemaContext } from "../../src/Context"; +import { PrimitiveType, RelationshipEnd } from "../../src/ECObjects"; +import { ECClass, MutableClass, StructClass } from "../../src/Metadata/Class"; +import { Constant } from "../../src/Metadata/Constant"; +import { EntityClass } from "../../src/Metadata/EntityClass"; +import { Enumeration } from "../../src/Metadata/Enumeration"; +import { Format } from "../../src/Metadata/Format"; +import { InvertedUnit } from "../../src/Metadata/InvertedUnit"; +import { KindOfQuantity } from "../../src/Metadata/KindOfQuantity"; +import { Mixin } from "../../src/Metadata/Mixin"; +import { Phenomenon } from "../../src/Metadata/Phenomenon"; +import { AnyProperty, MutableProperty } from "../../src/Metadata/Property"; +import { PropertyCategory } from "../../src/Metadata/PropertyCategory"; +import { RelationshipClass, RelationshipConstraint } from "../../src/Metadata/RelationshipClass"; +import { Schema } from "../../src/Metadata/Schema"; +import { Unit } from "../../src/Metadata/Unit"; +import { UnitSystem } from "../../src/Metadata/UnitSystem"; +import { SchemaValidationVisitor } from "../../src/Validation/SchemaValidationVisitor"; +import { TestReporter, TestRuleSet, TestRuleSetB, EmptyRuleSet, TestDiagnostics } from "../TestUtils/DiagnosticHelpers"; +import { CustomAttributeClass } from "../../src/Metadata/CustomAttributeClass"; + +describe("SchemaValidationVisitor tests", () => { + let visitor: SchemaValidationVisitor; + let schema: Schema; + + beforeEach(async () => { + visitor = new SchemaValidationVisitor(); + schema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); + }); + + afterEach(() => { + sinon.restore(); + }); + + it("registerRuleSet, rule sets registered properly", async () => { + const testRuleSet = new TestRuleSet(); + visitor.registerRuleSet(testRuleSet); + + const testRuleSetB = new TestRuleSetB(); + visitor.registerRuleSet(testRuleSetB); + + expect(visitor.ruleSets[testRuleSet.name]).to.be.not.undefined; + expect(visitor.ruleSets[testRuleSetB.name]).to.be.not.undefined; + }); + + it("registerRuleSet, rule set already registered, throws", async () => { + const testRuleSet = new TestRuleSet(); + visitor.registerRuleSet(testRuleSet); + + expect(() => visitor.registerRuleSet(testRuleSet)).to.throw(Error, `A RuleSet with the name '${testRuleSet.name}' has already been registered.`); + }); + + it("registerReporter, reporter registered properly", async () => { + expect(visitor.diagnosticReporters.length).to.equal(0); + const reporter = new TestReporter(); + + visitor.registerReporter(reporter); + + expect(visitor.diagnosticReporters.length).to.equal(1); + expect(visitor.diagnosticReporters[0]).to.equal(reporter); + }); + + describe("visitSchema tests", () => { + it("calls rules properly", async () => { + const ruleSetA = new TestRuleSet(); + const ruleSetB = new TestRuleSetB(); + visitor.registerRuleSet(ruleSetA); + visitor.registerRuleSet(ruleSetB); + const testSchema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); + + await visitor.visitFullSchema(testSchema); + + ruleSetA.schemaRules.forEach((spy) => expect(spy.calledOnceWithExactly(schema)).to.be.true); + ruleSetB.schemaRules.forEach((spy) => expect(spy.calledOnceWithExactly(schema)).to.be.true); + }); + + it("No rules, visit does not fail", async () => { + visitor.registerRuleSet(new EmptyRuleSet()); + const testSchema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); + + await visitor.visitFullSchema(testSchema); + }); + + it("failing rules, reporter called properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const reporter = new TestReporter(); + const reportSpy = sinon.spy(reporter, "report"); + visitor.registerReporter(reporter); + const testSchema = new Schema(new SchemaContext(), "TestSchema", 1, 0, 0); + + await visitor.visitFullSchema(testSchema); + + const diagnostic = new TestDiagnostics.FailingSchemaDiagnostic(schema, ["Param1", "Param2"]); + expect(reportSpy.calledOnceWithExactly(diagnostic)).to.be.true; + }); + }); + + describe("visitSchemaItem tests", () => { + it("calls rules properly", async () => { + const ruleSetA = new TestRuleSet(); + const ruleSetB = new TestRuleSetB(); + visitor.registerRuleSet(ruleSetA); + visitor.registerRuleSet(ruleSetB); + const schemaItem = new EntityClass(schema, "TestClass"); + + await visitor.visitSchemaItem(schemaItem); + + ruleSetA.schemaItemRules.forEach((spy) => expect(spy.calledOnceWithExactly(schemaItem)).to.be.true); + ruleSetB.schemaItemRules.forEach((spy) => expect(spy.calledOnceWithExactly(schemaItem)).to.be.true); + }); + + it("No rules, visit does not fail", async () => { + visitor.registerRuleSet(new EmptyRuleSet()); + const schemaItem = new EntityClass(schema, "TestClass"); + + await visitor.visitSchemaItem(schemaItem); + }); + + it("failing rules, reporter called properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const reporter = new TestReporter(); + const reportSpy = sinon.spy(reporter, "report"); + visitor.registerReporter(reporter); + const schemaItem = new EntityClass(schema, "TestClass"); + + await visitor.visitSchemaItem(schemaItem); + + const diagnostic = new TestDiagnostics.FailingSchemaItemDiagnostic(schemaItem, ["Param1", "Param2"]); + expect(reportSpy.calledOnceWithExactly(diagnostic)).to.be.true; + }); + }); + + describe("visitClass tests", () => { + it("calls class rules properly", async () => { + const ruleSetA = new TestRuleSet(); + const ruleSetB = new TestRuleSetB(); + visitor.registerRuleSet(ruleSetA); + visitor.registerRuleSet(ruleSetB); + const entityClass = new EntityClass(schema, "TestClass"); + + await visitor.visitClass(entityClass); + + ruleSetA.classRules.forEach((spy) => expect(spy.calledOnceWithExactly(entityClass)).to.be.true); + ruleSetB.classRules.forEach((spy) => expect(spy.calledOnceWithExactly(entityClass)).to.be.true); + }); + + it("No rules, visit does not fail", async () => { + visitor.registerRuleSet(new EmptyRuleSet()); + const schemaItem = new EntityClass(schema, "TestClass"); + + await visitor.visitClass(schemaItem); + }); + + it("failing rules, reporter called properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const reporter = new TestReporter(); + const reportSpy = sinon.spy(reporter, "report"); + visitor.registerReporter(reporter); + const entityClass = new EntityClass(schema, "TestClass"); + + await visitor.visitClass(entityClass); + + const diagnostic = new TestDiagnostics.FailingClassDiagnostic(entityClass, ["Param1", "Param2"]); + expect(reportSpy.calledOnceWithExactly(diagnostic)).to.be.true; + }); + }); + + describe("visitProperty tests", () => { + it("calls property rules properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const entityClass = new EntityClass(schema, "TestClass"); + const property = await (entityClass as ECClass as MutableClass).createPrimitiveProperty("TestPropertyA", PrimitiveType.String); + + await visitor.visitProperty(property as AnyProperty); + + ruleSet.propertyRules.forEach((spy) => expect(spy.calledOnceWithExactly(property)).to.be.true); + }); + + it("No rules, visit does not fail", async () => { + visitor.registerRuleSet(new EmptyRuleSet()); + const schemaItem = new EntityClass(schema, "TestClass"); + const property = await (schemaItem as ECClass as MutableClass).createPrimitiveProperty("TestPropertyA", PrimitiveType.String); + + await visitor.visitProperty(property); + }); + + it("failing rules, reporter called properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const reporter = new TestReporter(); + const reportSpy = sinon.spy(reporter, "report"); + visitor.registerReporter(reporter); + const entityClass = new EntityClass(schema, "TestClass"); + await (entityClass as ECClass as MutableClass).createPrimitiveProperty("TestPropertyA", PrimitiveType.String); + + await visitor.visitProperty(entityClass.properties![0] as AnyProperty); + + const diagnostic = new TestDiagnostics.FailingPropertyDiagnostic(entityClass.properties![0] as AnyProperty, ["Param1", "Param2"]); + expect(reportSpy.calledOnceWithExactly(diagnostic)).to.be.true; + }); + }); + + describe("visitEntityClass tests", () => { + it("calls rules properly", async () => { + const ruleSetA = new TestRuleSet(); + const ruleSetB = new TestRuleSetB(); + visitor.registerRuleSet(ruleSetA); + visitor.registerRuleSet(ruleSetB); + const entity = new EntityClass(schema, "TestClass"); + + await visitor.visitEntityClass(entity); + + ruleSetA.entityClassRules.forEach((spy) => expect(spy.calledOnceWithExactly(entity)).to.be.true); + ruleSetB.entityClassRules.forEach((spy) => expect(spy.calledOnceWithExactly(entity)).to.be.true); + }); + + it("No rules, visit does not fail", async () => { + visitor.registerRuleSet(new EmptyRuleSet()); + const schemaItem = new EntityClass(schema, "TestClass"); + + await visitor.visitEntityClass(schemaItem); + }); + + it("failing rules, reporter called properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const reporter = new TestReporter(); + const reportSpy = sinon.spy(reporter, "report"); + visitor.registerReporter(reporter); + const entity = new EntityClass(schema, "TestClass"); + + await visitor.visitEntityClass(entity); + + const diagnostic = new TestDiagnostics.FailingEntityClassDiagnostic(entity, ["Param1", "Param2"]); + expect(reportSpy.calledOnceWithExactly(diagnostic)).to.be.true; + }); + }); + + describe("visitStructClass tests", () => { + it("calls rules properly", async () => { + const ruleSetA = new TestRuleSet(); + const ruleSetB = new TestRuleSetB(); + visitor.registerRuleSet(ruleSetA); + visitor.registerRuleSet(ruleSetB); + const struct = new StructClass(schema, "TestClass"); + + await visitor.visitStructClass(struct); + + ruleSetA.structClassRules.forEach((spy) => expect(spy.calledOnceWithExactly(struct)).to.be.true); + ruleSetB.structClassRules.forEach((spy) => expect(spy.calledOnceWithExactly(struct)).to.be.true); + }); + + it("No rules, visit does not fail", async () => { + visitor.registerRuleSet(new EmptyRuleSet()); + const schemaItem = new StructClass(schema, "TestClass"); + + await visitor.visitStructClass(schemaItem); + }); + + it("failing rules, reporter called properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const reporter = new TestReporter(); + const reportSpy = sinon.spy(reporter, "report"); + visitor.registerReporter(reporter); + const struct = new StructClass(schema, "TestClass"); + + await visitor.visitStructClass(struct); + + const diagnostic = new TestDiagnostics.FailingStructClassDiagnostic(struct, ["Param1", "Param2"]); + expect(reportSpy.calledOnceWithExactly(diagnostic)).to.be.true; + }); + }); + + describe("visitMixin tests", () => { + it("calls rules properly", async () => { + const ruleSetA = new TestRuleSet(); + const ruleSetB = new TestRuleSetB(); + visitor.registerRuleSet(ruleSetA); + visitor.registerRuleSet(ruleSetB); + const mixin = new Mixin(schema, "TestClass"); + + await visitor.visitMixin(mixin); + + ruleSetA.mixinRules.forEach((spy) => expect(spy.calledOnceWithExactly(mixin)).to.be.true); + ruleSetB.mixinRules.forEach((spy) => expect(spy.calledOnceWithExactly(mixin)).to.be.true); + }); + + it("No rules, visit does not fail", async () => { + visitor.registerRuleSet(new EmptyRuleSet()); + const schemaItem = new Mixin(schema, "TestClass"); + + await visitor.visitMixin(schemaItem); + }); + + it("failing rules, reporter called properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const reporter = new TestReporter(); + const reportSpy = sinon.spy(reporter, "report"); + visitor.registerReporter(reporter); + const mixin = new Mixin(schema, "TestClass"); + + await visitor.visitMixin(mixin); + + const diagnostic = new TestDiagnostics.FailingMixinDiagnostic(mixin, ["Param1", "Param2"]); + expect(reportSpy.calledOnceWithExactly(diagnostic)).to.be.true; + }); + }); + + describe("visitRelationshipClass tests", () => { + it("calls relationship rules properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const relationshipClass = new RelationshipClass(schema, "TestClass"); + + await visitor.visitRelationshipClass(relationshipClass); + + ruleSet.relationshipRules.forEach((spy) => expect(spy.calledOnceWithExactly(relationshipClass)).to.be.true); + }); + + it("No rules, visit does not fail", async () => { + visitor.registerRuleSet(new EmptyRuleSet()); + const schemaItem = new RelationshipClass(schema, "TestClass"); + + await visitor.visitRelationshipClass(schemaItem); + }); + + it("failing rules, reporter called properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const reporter = new TestReporter(); + const reportSpy = sinon.spy(reporter, "report"); + visitor.registerReporter(reporter); + const relationship = new RelationshipClass(schema, "TestClass"); + + await visitor.visitRelationshipClass(relationship); + + const diagnostic = new TestDiagnostics.FailingRelationshipDiagnostic(relationship, ["Param1", "Param2"]); + expect(reportSpy.calledOnceWithExactly(diagnostic)).to.be.true; + }); + }); + + describe("visitRelationshipConstraint tests", () => { + it("calls relationship constraint rules properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const relationshipClass = new RelationshipClass(schema, "TestClass"); + const constraint = new RelationshipConstraint(relationshipClass, RelationshipEnd.Source); + + await visitor.visitRelationshipConstraint(constraint); + + ruleSet.relationshipConstraintRules.forEach((spy) => expect(spy.calledOnceWithExactly(constraint)).to.be.true); + }); + + it("No rules, visit does not fail", async () => { + visitor.registerRuleSet(new EmptyRuleSet()); + const relationshipClass = new RelationshipClass(schema, "TestClass"); + const constraint = new RelationshipConstraint(relationshipClass, RelationshipEnd.Source); + + await visitor.visitRelationshipConstraint(constraint); + }); + + it("failing rules, reporter called properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const reporter = new TestReporter(); + const reportSpy = sinon.spy(reporter, "report"); + visitor.registerReporter(reporter); + const relationship = new RelationshipClass(schema, "TestClass"); + const constraint = new RelationshipConstraint(relationship, RelationshipEnd.Source); + + await visitor.visitRelationshipConstraint(constraint); + + const diagnostic = new TestDiagnostics.FailingRelationshipConstraintDiagnostic(constraint, ["Param1", "Param2"]); + expect(reportSpy.calledOnceWithExactly(diagnostic)).to.be.true; + }); + }); + + describe("visitCustomAttributeClass tests", () => { + it("calls CustomAttributeClass rules properly", async () => { + const ruleSetA = new TestRuleSet(); + const ruleSetB = new TestRuleSetB(); + visitor.registerRuleSet(ruleSetA); + visitor.registerRuleSet(ruleSetB); + const schemaItem = new CustomAttributeClass(schema, "TestClass"); + + await visitor.visitCustomAttributeClass(schemaItem); + + ruleSetA.customAttributeClassRules.forEach((spy) => expect(spy.calledOnceWithExactly(schemaItem)).to.be.true); + ruleSetB.customAttributeClassRules.forEach((spy) => expect(spy.calledOnceWithExactly(schemaItem)).to.be.true); + }); + + it("No rules, visit does not fail", async () => { + visitor.registerRuleSet(new EmptyRuleSet()); + const schemaItem = new CustomAttributeClass(schema, "TestClass"); + + await visitor.visitCustomAttributeClass(schemaItem); + }); + + it("failing rules, reporter called properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const reporter = new TestReporter(); + const reportSpy = sinon.spy(reporter, "report"); + visitor.registerReporter(reporter); + const schemaItem = new CustomAttributeClass(schema, "TestClass"); + + await visitor.visitCustomAttributeClass(schemaItem); + + const diagnostic = new TestDiagnostics.FailingCustomAttributeClassDiagnostic(schemaItem, ["Param1", "Param2"]); + expect(reportSpy.calledOnceWithExactly(diagnostic)).to.be.true; + }); + }); + + describe("visitCustomAttributeContainerTests tests", () => { + it("EntityClass, calls CustomAttributeContainer and CustomAttribute rules properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const entityClass = new EntityClass(schema, "TestClass"); + (entityClass as unknown as MutableClass).addCustomAttribute({ className: "TestSchema.TestCA" }); + + await visitor.visitCustomAttributeContainer(entityClass); + + ruleSet.customAttributeContainerRules.forEach((spy) => expect(spy.calledOnceWithExactly(entityClass)).to.be.true); + ruleSet.customAttributeInstanceRules.forEach((spy) => expect(spy.calledOnceWithExactly(entityClass, entityClass.customAttributes!.get("TestSchema.TestCA"))).to.be.true); + }); + + it("No rules, visit does not fail", async () => { + visitor.registerRuleSet(new EmptyRuleSet()); + const entityClass = new EntityClass(schema, "TestClass"); + (entityClass as unknown as MutableClass).addCustomAttribute({ className: "TestSchema.TestCA" }); + + await visitor.visitCustomAttributeContainer(entityClass); + }); + + it("Property, calls CustomAttributeContainer rules properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const entityClass = new EntityClass(schema, "TestClass"); + const property = await (entityClass as ECClass as MutableClass).createPrimitiveProperty("TestPropertyA", PrimitiveType.String); + (property as unknown as MutableProperty).addCustomAttribute({ className: "TestSchema.TestCA" }); + + await visitor.visitCustomAttributeContainer(entityClass.properties![0] as AnyProperty); + + ruleSet.customAttributeContainerRules.forEach((spy) => expect(spy.calledOnceWithExactly(entityClass.properties![0])).to.be.true); + ruleSet.customAttributeInstanceRules.forEach((spy) => expect(spy.calledOnceWithExactly(entityClass.properties![0], property.customAttributes!.get("TestSchema.TestCA"))).to.be.true); + }); + + it("RelationshipClass, calls CustomAttributeContainer rules properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const relationshipClass = new RelationshipClass(schema, "TestClass"); + (relationshipClass as unknown as MutableClass).addCustomAttribute({ className: "TestSchema.TestCA" }); + + await visitor.visitCustomAttributeContainer(relationshipClass); + + ruleSet.customAttributeContainerRules.forEach((spy) => expect(spy.calledOnceWithExactly(relationshipClass)).to.be.true); + ruleSet.customAttributeInstanceRules.forEach((spy) => expect(spy.calledOnceWithExactly(relationshipClass, relationshipClass.customAttributes!.get("TestSchema.TestCA"))).to.be.true); + }); + + it("RelationshipConstraint, calls CustomAttributeContainer rules properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const relationshipClass = new RelationshipClass(schema, "TestClass"); + const constraint = new RelationshipConstraint(relationshipClass, RelationshipEnd.Source); + (constraint as unknown as MutableClass).addCustomAttribute({ className: "TestSchema.TestCA" }); + + await visitor.visitCustomAttributeContainer(constraint); + + ruleSet.customAttributeContainerRules.forEach((spy) => expect(spy.calledOnceWithExactly(constraint)).to.be.true); + ruleSet.customAttributeInstanceRules.forEach((spy) => expect(spy.calledOnceWithExactly(constraint, constraint.customAttributes!.get("TestSchema.TestCA"))).to.be.true); + }); + + it("failing rules, reporter called properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const entityClass = new EntityClass(schema, "TestClass"); + (entityClass as unknown as MutableClass).addCustomAttribute({ className: "TestSchema.TestCA" }); + + const reporter = new TestReporter(); + const reportSpy = sinon.spy(reporter, "report"); + visitor.registerReporter(reporter); + + await visitor.visitCustomAttributeContainer(entityClass); + + const diagnostic = new TestDiagnostics.FailingCustomAttributeContainerDiagnostic(entityClass, ["Param1", "Param2"]); + expect(reportSpy.calledTwice).to.be.true; + expect(reportSpy.calledWithExactly(diagnostic)).to.be.true; + }); + }); + + describe("visitEnumeration tests", () => { + it("calls rules properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const enumeration = new Enumeration(schema, "TestClass"); + + await visitor.visitEnumeration(enumeration); + + ruleSet.enumerationRules.forEach((spy) => expect(spy.calledOnceWithExactly(enumeration)).to.be.true); + }); + + it("No rules, visit does not fail", async () => { + visitor.registerRuleSet(new EmptyRuleSet()); + const schemaItem = new Enumeration(schema, "TestClass"); + + await visitor.visitEnumeration(schemaItem); + }); + + it("failing rules, reporter called properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const reporter = new TestReporter(); + const reportSpy = sinon.spy(reporter, "report"); + visitor.registerReporter(reporter); + const enumeration = new Enumeration(schema, "TestClass"); + + await visitor.visitEnumeration(enumeration); + + const diagnostic = new TestDiagnostics.FailingEnumerationDiagnostic(enumeration, ["Param1", "Param2"]); + expect(reportSpy.calledOnceWithExactly(diagnostic)).to.be.true; + }); + }); + + describe("visitKindOfQuantity tests", () => { + it("calls rules properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const koq = new KindOfQuantity(schema, "TestClass"); + + await visitor.visitKindOfQuantity(koq); + + ruleSet.kindOfQuantityRules.forEach((spy) => expect(spy.calledOnceWithExactly(koq)).to.be.true); + }); + + it("No rules, visit does not fail", async () => { + visitor.registerRuleSet(new EmptyRuleSet()); + const schemaItem = new KindOfQuantity(schema, "TestClass"); + + await visitor.visitKindOfQuantity(schemaItem); + }); + + it("failing rules, reporter called properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const reporter = new TestReporter(); + const reportSpy = sinon.spy(reporter, "report"); + visitor.registerReporter(reporter); + const koq = new KindOfQuantity(schema, "TestClass"); + + await visitor.visitKindOfQuantity(koq); + + const diagnostic = new TestDiagnostics.FailingKindOfQuantityDiagnostic(koq, ["Param1", "Param2"]); + expect(reportSpy.calledOnceWithExactly(diagnostic)).to.be.true; + }); + }); + + describe("visitPropertyCategory tests", () => { + it("calls rules properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const category = new PropertyCategory(schema, "TestClass"); + + await visitor.visitPropertyCategory(category); + + ruleSet.propertyCategoryRules.forEach((spy) => expect(spy.calledOnceWithExactly(category)).to.be.true); + }); + + it("No rules, visit does not fail", async () => { + visitor.registerRuleSet(new EmptyRuleSet()); + const schemaItem = new PropertyCategory(schema, "TestClass"); + + await visitor.visitPropertyCategory(schemaItem); + }); + + it("failing rules, reporter called properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const reporter = new TestReporter(); + const reportSpy = sinon.spy(reporter, "report"); + visitor.registerReporter(reporter); + const category = new PropertyCategory(schema, "TestClass"); + + await visitor.visitPropertyCategory(category); + + const diagnostic = new TestDiagnostics.FailingPropertyCategoryDiagnostic(category, ["Param1", "Param2"]); + expect(reportSpy.calledOnceWithExactly(diagnostic)).to.be.true; + }); + }); + + describe("visitFormat tests", () => { + it("calls rules properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const format = new Format(schema, "TestClass"); + + await visitor.visitFormat(format); + + ruleSet.formatRules.forEach((spy) => expect(spy.calledOnceWithExactly(format)).to.be.true); + }); + + it("No rules, visit does not fail", async () => { + visitor.registerRuleSet(new EmptyRuleSet()); + const schemaItem = new Format(schema, "TestClass"); + + await visitor.visitFormat(schemaItem); + }); + + it("failing rules, reporter called properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const reporter = new TestReporter(); + const reportSpy = sinon.spy(reporter, "report"); + visitor.registerReporter(reporter); + const format = new Format(schema, "TestClass"); + + await visitor.visitFormat(format); + + const diagnostic = new TestDiagnostics.FailingFormatDiagnostic(format, ["Param1", "Param2"]); + expect(reportSpy.calledOnceWithExactly(diagnostic)).to.be.true; + }); + }); + + describe("visitUnit tests", () => { + it("calls rules properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const unit = new Unit(schema, "TestClass"); + + await visitor.visitUnit(unit); + + ruleSet.unitRules.forEach((spy) => expect(spy.calledOnceWithExactly(unit)).to.be.true); + }); + + it("No rules, visit does not fail", async () => { + visitor.registerRuleSet(new EmptyRuleSet()); + const schemaItem = new Unit(schema, "TestClass"); + + await visitor.visitUnit(schemaItem); + }); + + it("failing rules, reporter called properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const reporter = new TestReporter(); + const reportSpy = sinon.spy(reporter, "report"); + visitor.registerReporter(reporter); + const unit = new Unit(schema, "TestClass"); + + await visitor.visitUnit(unit); + + const diagnostic = new TestDiagnostics.FailingUnitDiagnostic(unit, ["Param1", "Param2"]); + expect(reportSpy.calledOnceWithExactly(diagnostic)).to.be.true; + }); + }); + + describe("visitInvertedUnit tests", () => { + it("calls rules properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const invertedUnit = new InvertedUnit(schema, "TestClass"); + + await visitor.visitInvertedUnit(invertedUnit); + + ruleSet.invertedUnitRules.forEach((spy) => expect(spy.calledOnceWithExactly(invertedUnit)).to.be.true); + }); + + it("No rules, visit does not fail", async () => { + visitor.registerRuleSet(new EmptyRuleSet()); + const schemaItem = new InvertedUnit(schema, "TestClass"); + + await visitor.visitInvertedUnit(schemaItem); + }); + + it("failing rules, reporter called properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const reporter = new TestReporter(); + const reportSpy = sinon.spy(reporter, "report"); + visitor.registerReporter(reporter); + const invertedUnit = new InvertedUnit(schema, "TestClass"); + + await visitor.visitInvertedUnit(invertedUnit); + + const diagnostic = new TestDiagnostics.FailingInvertedUnitFormatDiagnostic(invertedUnit, ["Param1", "Param2"]); + expect(reportSpy.calledOnceWithExactly(diagnostic)).to.be.true; + }); + }); + + describe("visitUnitSystem tests", () => { + it("calls rules properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const unitSystem = new UnitSystem(schema, "TestClass"); + + await visitor.visitUnitSystem(unitSystem); + + ruleSet.unitSystemRules.forEach((spy) => expect(spy.calledOnceWithExactly(unitSystem)).to.be.true); + }); + + it("No rules, visit does not fail", async () => { + visitor.registerRuleSet(new EmptyRuleSet()); + const schemaItem = new UnitSystem(schema, "TestClass"); + + await visitor.visitUnitSystem(schemaItem); + }); + + it("failing rules, reporter called properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const reporter = new TestReporter(); + const reportSpy = sinon.spy(reporter, "report"); + visitor.registerReporter(reporter); + const unitSystem = new UnitSystem(schema, "TestClass"); + + await visitor.visitUnitSystem(unitSystem); + + const diagnostic = new TestDiagnostics.FailingUnitSystemDiagnostic(unitSystem, ["Param1", "Param2"]); + expect(reportSpy.calledOnceWithExactly(diagnostic)).to.be.true; + }); + }); + + describe("visitPhenomenon tests", () => { + it("calls rules properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const phenomenon = new Phenomenon(schema, "TestClass"); + + await visitor.visitPhenomenon(phenomenon); + + ruleSet.phenomenonRules.forEach((spy) => expect(spy.calledOnceWithExactly(phenomenon)).to.be.true); + }); + + it("No rules, visit does not fail", async () => { + visitor.registerRuleSet(new EmptyRuleSet()); + const schemaItem = new Phenomenon(schema, "TestClass"); + + await visitor.visitPhenomenon(schemaItem); + }); + + it("failing rules, reporter called properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const reporter = new TestReporter(); + const reportSpy = sinon.spy(reporter, "report"); + visitor.registerReporter(reporter); + const phenomenon = new Phenomenon(schema, "TestClass"); + + await visitor.visitPhenomenon(phenomenon); + + const diagnostic = new TestDiagnostics.FailingPhenomenonDiagnostic(phenomenon, ["Param1", "Param2"]); + expect(reportSpy.calledOnceWithExactly(diagnostic)).to.be.true; + }); + }); + + describe("visitConstant tests", () => { + it("calls rules properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const constant = new Constant(schema, "TestClass"); + + await visitor.visitConstant(constant); + + ruleSet.constantRules.forEach((spy) => expect(spy.calledOnceWithExactly(constant)).to.be.true); + }); + + it("No rules, visit does not fail", async () => { + visitor.registerRuleSet(new EmptyRuleSet()); + const schemaItem = new Constant(schema, "TestClass"); + + await visitor.visitConstant(schemaItem); + }); + + it("failing rules, reporter called properly", async () => { + const ruleSet = new TestRuleSet(); + visitor.registerRuleSet(ruleSet); + const reporter = new TestReporter(); + const reportSpy = sinon.spy(reporter, "report"); + visitor.registerReporter(reporter); + const constant = new Constant(schema, "TestClass"); + + await visitor.visitConstant(constant); + + const diagnostic = new TestDiagnostics.FailingConstantDiagnostic(constant, ["Param1", "Param2"]); + expect(reportSpy.calledOnceWithExactly(diagnostic)).to.be.true; + }); + }); +}); diff --git a/core/ecschema-metadata/test/Validation/SchemaWalker.test.ts b/core/ecschema-metadata/test/Validation/SchemaWalker.test.ts new file mode 100644 index 0000000..02171c5 --- /dev/null +++ b/core/ecschema-metadata/test/Validation/SchemaWalker.test.ts @@ -0,0 +1,261 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { expect } from "chai"; +import sinon = require("sinon"); + +import { SchemaContext } from "../../src/Context"; +import { SchemaReadHelper } from "../../src/Deserialization/Helper"; +import { JsonParser } from "../../src/Deserialization/JsonParser"; +import { ISchemaPartVisitor } from "../../src/SchemaPartVisitorDelegate"; +import { ECClass } from "../../src/Metadata/Class"; +import { RelationshipClass } from "../../src/Metadata/RelationshipClass"; +import { Schema } from "../../src/Metadata/Schema"; +import { SchemaWalker } from "../../src/Validation/SchemaWalker"; + +describe("SchemaWalker tests", () => { + let testSchema: Schema; + const baseJson = { + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", + name: "TestSchema", + version: "1.2.3", + }; + + const schemaJson = { + ...baseJson, + items: { + TestEntityBase: { + schemaItemType: "EntityClass", + properties: [ + { + name: "A", + type: "PrimitiveProperty", + typeName: "double", + }, + { + name: "B", + type: "PrimitiveProperty", + typeName: "double", + }, + ], + }, + TestEntityA: { + schemaItemType: "EntityClass", + BaseClass: "TestSchema.TestEntityBase", + }, + TestEntityB: { + schemaItemType: "EntityClass", + BaseClass: "TestSchema.TestEntityBase", + }, + TestRelationship: { + schemaItemType: "RelationshipClass", + strength: "Embedding", + strengthDirection: "Backward", + modifier: "Sealed", + source: { + polymorphic: false, + multiplicity: "(0..*)", + roleLabel: "Source RoleLabel", + abstractConstraint: "TestSchema.TestEntityBase", + constraintClasses: [ + "TestSchema.TestEntityA", + ], + }, + target: { + polymorphic: false, + multiplicity: "(0..*)", + roleLabel: "Target RoleLabel", + abstractConstraint: "TestSchema.TestEntityBase", + constraintClasses: [ + "TestSchema.TestEntityB", + ], + }, + }, + TestEnum: { + schemaItemType: "Enumeration", + type: "int", + enumerators: [ + { + name: "TestEnumeration", + value: 2, + }, + ], + }, + TestStruct: { + schemaItemType: "StructClass", + }, + TestMixin: { + schemaItemType: "Mixin", + appliesTo: "TestSchema.TestEntityA", + }, + TestCAClass: { + schemaItemType: "CustomAttributeClass", + appliesTo: "AnyClass", + }, + TestCategory: { + schemaItemType: "PropertyCategory", + }, + Length: { + schemaItemType: "Phenomenon", + definition: "LENGTH(1)", + }, + Metric: { + schemaItemType: "UnitSystem", + }, + M: { + schemaItemType: "Unit", + phenomenon: "TestSchema.Length", + unitSystem: "TestSchema.Metric", + definition: "[MILLI]*M", + }, + TestInvertedUnit: { + schemaItemType: "InvertedUnit", + unitSystem: "TestSchema.Metric", + invertsUnit: "TestSchema.M", + }, + TestFormat: { + schemaItemType: "Format", + type: "Decimal", + }, + TestConstant: { + schemaItemType: "Constant", + phenomenon: "TestSchema.Length", + definition: "TestLength", + numerator: 1.2, + denominator: 1, + }, + TestKoQ: { + schemaItemType: "KindOfQuantity", + relativeError: 5, + persistenceUnit: "TestSchema.M", + }, + }, + }; + + type Mock = { readonly [P in keyof T]: sinon.SinonSpy; }; + let mockVisitor: Mock; + + beforeEach( async () => { + mockVisitor = { + visitClass: sinon.spy(), + visitCustomAttributeContainer: sinon.spy(), + visitProperty: sinon.spy(), + visitRelationshipClass: sinon.spy(), + visitRelationshipConstraint: sinon.spy(), + visitEntityClass: sinon.spy(), + visitStructClass: sinon.spy(), + visitMixin: sinon.spy(), + visitCustomAttributeClass: sinon.spy(), + visitEnumeration: sinon.spy(), + visitKindOfQuantity: sinon.spy(), + visitPropertyCategory: sinon.spy(), + visitUnit: sinon.spy(), + visitInvertedUnit: sinon.spy(), + visitUnitSystem: sinon.spy(), + visitPhenomenon: sinon.spy(), + visitFormat: sinon.spy(), + visitConstant: sinon.spy(), + visitFullSchema: sinon.spy(), + }; + + const context = new SchemaContext(); + testSchema = new Schema(context); + const reader = new SchemaReadHelper(JsonParser, context); + testSchema = await reader.readSchema(testSchema, schemaJson); + }); + + it("should call all visit methods", async () => { + const reader = new SchemaWalker(mockVisitor); + testSchema = await reader.traverseSchema(testSchema); + expect(testSchema).to.exist; + + expect(mockVisitor!.visitFullSchema!.calledOnce).to.be.true; + expect(mockVisitor!.visitFullSchema!.calledWithExactly(testSchema)).to.be.true; + expect(mockVisitor!.visitFullSchema!.calledBefore(mockVisitor!.visitClass!)).to.be.true; + expect(mockVisitor!.visitCustomAttributeContainer!.calledWithExactly(testSchema)).to.be.true; + + const testEntityBase = await testSchema.getItem("TestEntityBase") as ECClass; + expect(mockVisitor!.visitClass!.calledWithExactly(testEntityBase)).to.be.true; + expect(mockVisitor!.visitCustomAttributeContainer!.calledWithExactly(testEntityBase)).to.be.true; + expect(mockVisitor!.visitEntityClass!.calledWithExactly(testEntityBase)).to.be.true; + + const aProp = testEntityBase.properties![0]; + const bProp = testEntityBase.properties![1]; + expect(mockVisitor!.visitProperty!.calledTwice).to.be.true; + expect(mockVisitor!.visitProperty!.calledOnceWithExactly(aProp)); + expect(mockVisitor!.visitProperty!.calledOnceWithExactly(bProp)); + expect(mockVisitor!.visitCustomAttributeContainer!.calledWithExactly(aProp)).to.be.true; + expect(mockVisitor!.visitCustomAttributeContainer!.calledWithExactly(bProp)).to.be.true; + + const testEntityA = await testSchema.getItem("TestEntityA") as ECClass; + expect(mockVisitor!.visitClass!.calledWithExactly(testEntityA)).to.be.true; + expect(mockVisitor!.visitCustomAttributeContainer!.calledWithExactly(testEntityA)).to.be.true; + expect(mockVisitor!.visitEntityClass!.calledWithExactly(testEntityA)).to.be.true; + + const testEntityB = await testSchema.getItem("TestEntityB") as ECClass; + expect(mockVisitor!.visitClass!.calledWithExactly(testEntityB)).to.be.true; + expect(mockVisitor!.visitCustomAttributeContainer!.calledWithExactly(testEntityB)).to.be.true; + expect(mockVisitor!.visitEntityClass!.calledWithExactly(testEntityB)).to.be.true; + + const testRelationship = await testSchema.getItem("TestRelationship") as RelationshipClass; + expect(mockVisitor!.visitRelationshipClass!.calledWithExactly(testRelationship)).to.be.true; + expect(mockVisitor!.visitCustomAttributeContainer!.calledWithExactly(testRelationship)).to.be.true; + expect(mockVisitor!.visitRelationshipConstraint!.calledWithExactly(testRelationship.source)).to.be.true; + expect(mockVisitor!.visitRelationshipConstraint!.calledWithExactly(testRelationship.target)).to.be.true; + expect(mockVisitor!.visitCustomAttributeContainer!.calledWithExactly(testRelationship.source)).to.be.true; + expect(mockVisitor!.visitCustomAttributeContainer!.calledWithExactly(testRelationship.target)).to.be.true; + + const testStruct = await testSchema.getItem("TestStruct") as ECClass; + expect(mockVisitor!.visitClass!.calledWithExactly(testStruct)).to.be.true; + expect(mockVisitor!.visitCustomAttributeContainer!.calledWithExactly(testStruct)).to.be.true; + expect(mockVisitor!.visitStructClass!.calledWithExactly(testStruct)).to.be.true; + + const testMixin = await testSchema.getItem("TestMixin") as ECClass; + expect(mockVisitor!.visitClass!.calledWithExactly(testMixin)).to.be.true; + expect(mockVisitor!.visitCustomAttributeContainer!.calledWithExactly(testMixin)).to.be.true; + expect(mockVisitor!.visitMixin!.calledWithExactly(testMixin)).to.be.true; + + const testCAClass = await testSchema.getItem("TestCAClass") as ECClass; + expect(mockVisitor!.visitClass!.calledWithExactly(testCAClass)).to.be.true; + expect(mockVisitor!.visitCustomAttributeContainer!.calledWithExactly(testCAClass)).to.be.true; + expect(mockVisitor!.visitCustomAttributeClass!.calledWithExactly(testCAClass)).to.be.true; + + const testEnum = await testSchema.getItem("TestEnum"); + expect(mockVisitor!.visitEnumeration!.calledOnce).to.be.true; + expect(mockVisitor!.visitEnumeration!.calledWithExactly(testEnum)).to.be.true; + + const testCategory = await testSchema.getItem("TestCategory"); + expect(mockVisitor!.visitPropertyCategory!.calledOnce).to.be.true; + expect(mockVisitor!.visitPropertyCategory!.calledWithExactly(testCategory)).to.be.true; + + const testKoq = await testSchema.getItem("TestKoQ"); + expect(mockVisitor!.visitKindOfQuantity!.calledOnce).to.be.true; + expect(mockVisitor!.visitKindOfQuantity!.calledWithExactly(testKoq)).to.be.true; + + const testUnitSystem = await testSchema.getItem("Metric"); + expect(mockVisitor!.visitUnitSystem!.calledOnce).to.be.true; + expect(mockVisitor!.visitUnitSystem!.calledWithExactly(testUnitSystem)).to.be.true; + + const testUnit = await testSchema.getItem("M"); + expect(mockVisitor!.visitUnit!.calledOnce).to.be.true; + expect(mockVisitor!.visitUnit!.calledWithExactly(testUnit)).to.be.true; + + const testInvertedUnit = await testSchema.getItem("TestInvertedUnit"); + expect(mockVisitor!.visitInvertedUnit!.calledOnce).to.be.true; + expect(mockVisitor!.visitInvertedUnit!.calledWithExactly(testInvertedUnit)).to.be.true; + + const testPhenomenon = await testSchema.getItem("Length"); + expect(mockVisitor!.visitPhenomenon!.calledOnce).to.be.true; + expect(mockVisitor!.visitPhenomenon!.calledWithExactly(testPhenomenon)).to.be.true; + + const testFormat = await testSchema.getItem("TestFormat"); + expect(mockVisitor!.visitFormat!.calledOnce).to.be.true; + expect(mockVisitor!.visitFormat!.calledWithExactly(testFormat)).to.be.true; + + const testConstant = await testSchema.getItem("TestConstant"); + expect(mockVisitor!.visitConstant!.calledOnce).to.be.true; + expect(mockVisitor!.visitConstant!.calledWithExactly(testConstant)).to.be.true; + }); +}); diff --git a/core/electron-manager/CHANGELOG.json b/core/electron-manager/CHANGELOG.json index bb425b3..440b85b 100644 --- a/core/electron-manager/CHANGELOG.json +++ b/core/electron-manager/CHANGELOG.json @@ -1,6 +1,33 @@ { "name": "@bentley/electron-manager", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/electron-manager_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "Changes package.json to include api-extractor and adds api-extractor.json" + }, + { + "comment": "Use new buildIModelJsBuild script" + }, + { + "comment": "Remove uneeded typedoc plugin depedency" + }, + { + "comment": "upgrade to TypeScript 3.2.2" + } + ] + } + }, + { + "version": "0.188.0", + "tag": "@bentley/electron-manager_v0.188.0", + "date": "Wed, 16 Jan 2019 16:36:09 GMT", + "comments": {} + }, { "version": "0.187.0", "tag": "@bentley/electron-manager_v0.187.0", diff --git a/core/electron-manager/CHANGELOG.md b/core/electron-manager/CHANGELOG.md index 6d407f7..5c5be2d 100644 --- a/core/electron-manager/CHANGELOG.md +++ b/core/electron-manager/CHANGELOG.md @@ -1,6 +1,21 @@ # Change Log - @bentley/electron-manager -This log was last generated on Tue, 15 Jan 2019 15:18:59 GMT and should not be manually modified. +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- Changes package.json to include api-extractor and adds api-extractor.json +- Use new buildIModelJsBuild script +- Remove uneeded typedoc plugin depedency +- upgrade to TypeScript 3.2.2 + +## 0.188.0 +Wed, 16 Jan 2019 16:36:09 GMT + +*Version update only* ## 0.187.0 Tue, 15 Jan 2019 15:18:59 GMT diff --git a/core/electron-manager/package.json b/core/electron-manager/package.json index ebb8b27..060dbe7 100644 --- a/core/electron-manager/package.json +++ b/core/electron-manager/package.json @@ -1,17 +1,18 @@ { "name": "@bentley/electron-manager", - "version": "0.187.0", + "version": "0.189.0", "description": "iModel.js electron utilities", "main": "lib/ElectronManager.js", "typings": "lib/ElectronManager", "license": "MIT", "engines": { - "node": ">=8.9.0 <9.0" + "node": ">=10.14.0 <11.0" }, "scripts": { - "build": "tsc 1>&2", + "build": "tsc 1>&2 && node ./node_modules/@bentley/build-tools/scripts/ignoreargs.js 1>&2", "clean": "rimraf lib package-deps.json", "docs": "node ./node_modules/@bentley/build-tools/scripts/docs.js --source=./src --includes=../../generated-docs/extract --json=../../generated-docs/core/electron-manager/file.json --tsIndexFile=./ElectronManager.ts --onlyJson %TYPEDOC_THEME%", + "extract-api": "node ./node_modules/@bentley/build-tools/scripts/extract-api.js --entry=ElectronManager", "lint": "tslint --project . 1>&2", "test": "", "cover": "" @@ -37,14 +38,13 @@ "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/build-tools": "0.187.0", - "@types/node": "10.10.3", + "@bentley/build-tools": "0.189.0", + "@types/node": "10.12.18", "electron": "^4.0.1", "rimraf": "^2.6.2", "tslint": "^5.11.0", "typedoc": "^0.11.1", - "typedoc-plugin-external-module-name": "^1.1.1", - "typescript": "~3.1.0" + "typescript": "~3.2.2" }, "dependencies": {} } diff --git a/core/frontend/CHANGELOG.json b/core/frontend/CHANGELOG.json index cbd6805..f685485 100644 --- a/core/frontend/CHANGELOG.json +++ b/core/frontend/CHANGELOG.json @@ -1,6 +1,267 @@ { "name": "@bentley/imodeljs-frontend", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/imodeljs-frontend_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "add ios oidc client" + }, + { + "comment": "geometry-core camel case" + }, + { + "comment": "Add Selection Scope toolsettings to SelectTool." + }, + { + "comment": "allow subclasses of Range to use static methods" + }, + { + "comment": "Raise events when a Viewport's always- or never-drawn element sets change." + }, + { + "comment": "OIDC changes needed for Angular client" + }, + { + "comment": "Changes package.json to include api-extractor and adds api-extractor.json" + }, + { + "comment": "#66826 Default SelectTool to select all members of the selected element's assembly." + }, + { + "comment": "Default scope to element." + }, + { + "comment": "Use new buildIModelJsBuild script" + }, + { + "comment": "Generalize support for reading tiles to include tiles generated for Bimium." + }, + { + "comment": "AxisAlignedBox and ElementAlignedBox are now typed to Range3d rather than classes" + }, + { + "comment": "clone methods are no longer generic" + }, + { + "comment": "Optimize renderer to elide debug-only code paths unless explicitly enabled." + }, + { + "comment": "Generalize 3d tile support. Handle transform on child nodes." + }, + { + "comment": "Avoid using cutting planes while animating if not displayed, completely hidden or completely visible." + }, + { + "comment": "Started work on webworker to decode jpeg files for GLTF" + }, + { + "comment": "Defer Draco support until moved to web worker" + }, + { + "comment": "Reduce memory consumption when ambient occlusion is disabled." + }, + { + "comment": "Fix incorrect colors for some decoration graphics." + }, + { + "comment": "Remove uneeded typedoc plugin depedency" + }, + { + "comment": "Add support for Draco compressed meshes" + }, + { + "comment": "Change drag select to exclude non locatable" + }, + { + "comment": "noMotion doesn't need to call beginDynamicUpdate" + }, + { + "comment": "example code (in comments) for frustum interpolator" + }, + { + "comment": "Consistent naming of \"get\" methods in Growable arrays." + }, + { + "comment": "Add EmphasizeElements to/from wire format methods" + }, + { + "comment": "Draw non-emphasized elements in \"fade-out\", non-locatable mode." + }, + { + "comment": "Move neverDrawn/alwaysDrawn to Viewport they are not part of the persistent ViewState. Change Viewport.addFeatureOverrides to an interface." + }, + { + "comment": "Rework and simplify ecef transform for reality models." + }, + { + "comment": "Correct ID for loading classifier trees." + }, + { + "comment": "Fix clipping volume being inconsistently applied to view." + }, + { + "comment": "Dont make textures transparent unless technique enables it." + }, + { + "comment": "Fix incorrect \"fit view\" behavior when empty tiles exist." + }, + { + "comment": "Handle relative subpaths in reality model tile trees. Handle Y for axis/ " + }, + { + "comment": "Fix handling of null animation visibility - should be 100% not 0." + }, + { + "comment": "Added spatial <-> cartographic methods that check/use the geographic coordinate system before using ecef location." + }, + { + "comment": "DefaultViewTouchTool should not call handleEvent until it's installed as the active ViewTool." + }, + { + "comment": "Traverse GLTF node structure rather than meshes so that node transforms are used correctly." + }, + { + "comment": "Ensure viewport updates immediately when background map settings are changed." + }, + { + "comment": "Add a test to determine if GCS is present before using GCS converter." + }, + { + "comment": "Documentation improvements" + }, + { + "comment": "Support instanced rendering of .i3dm 3D tiles." + }, + { + "comment": "Preliminary support for drawing instanced geometry." + }, + { + "comment": "Fix branch transform for animation correctly - back out incorrect fix to BranchState." + }, + { + "comment": "Implemented, then commented out, doing jpeg decompression in a web worker" + }, + { + "comment": "added markup mode" + }, + { + "comment": "events are now on ScreenViewport.parentDiv rather than canvas" + }, + { + "comment": "update for geometry GrowableXYArray usage." + }, + { + "comment": "Measure Distance - change selected segment hilite. Measure Location - WIP use ecef transform." + }, + { + "comment": "More ui-framework unit tests" + }, + { + "comment": "Make it possible to define editor params for default Type Editors not explicitly specified by name." + }, + { + "comment": "Fixed a bug which caused non-locatable geometry to be rendered when no other symbology was overridden." + }, + { + "comment": "Defer loading of edges until needed" + }, + { + "comment": "Omit animation branches that are not visible." + }, + { + "comment": "Improve efficiency and completeness of SubCategory loading for ViewStates." + }, + { + "comment": "Save BUILD_SEMVER to globally accessible map. PluginAdmin and Plugin classes defined. IModelJsLoader improved." + }, + { + "comment": "add optional iModel argument to EntityState.clone " + }, + { + "comment": "added GeometricModelState.queryModelRange" + }, + { + "comment": "Added creatorId, new method to list RD per project, identified numerous area for changes WIP" + }, + { + "comment": "IModelConnection.close() always disposes the briefcase held at the backend in the case of ReadWrite connections. " + }, + { + "comment": "Implemented spatial criterai when searching through all reality data associated to a project." + }, + { + "comment": "Problem with root document of reality data not in root of blob. Tiles could not be fetched. Root path is added to tiles names." + }, + { + "comment": "Threading issue accessing Reality Data, RealityData class was transformed to be the main data access object instead of the client that was used by most/all reality data causing cache data clash and mix between many reality data." + }, + { + "comment": "Optimze containment test with spheres." + }, + { + "comment": "Move the IModelUnitTestRpcInterface into the testbed and out of the public AP" + }, + { + "comment": "Retry tile requests on time-out." + }, + { + "comment": "Remove loadNativeAsset and formatElements RPC calls from the IModelReadRpcInterface" + }, + { + "comment": "Removed IModelConnection.connectionId, added IModelApp.sessionId" + }, + { + "comment": "make view transition animations smoother" + }, + { + "comment": "Optimizations to tile format and schedule animation." + }, + { + "comment": "Tile requests can optionally specify a retryInterval." + }, + { + "comment": " Cleanup of DefaultToolSetting provider and EnumButtonGroup editor including new EditorParams." + }, + { + "comment": "Move property definitions to imodeljs-frontend so they could be used by tools to define properties for tool settings. Add toolsettings to Select Tool." + }, + { + "comment": "Added a new property to PropertyRecord - links." + }, + { + "comment": "IModelConnection.connectionTimeout is public to allow application customization." + }, + { + "comment": "upgrade to TypeScript 3.2.2" + } + ] + } + }, + { + "version": "0.188.0", + "tag": "@bentley/imodeljs-frontend_v0.188.0", + "date": "Wed, 16 Jan 2019 16:36:09 GMT", + "comments": { + "none": [ + { + "comment": "Report unsigned measure distance deltas." + }, + { + "comment": "Add batch id to schedule scripts" + }, + { + "comment": "Add batchID to schedule scripts" + }, + { + "comment": "Handle wider variety of GLTF bounding boxes etc." + } + ] + } + }, { "version": "0.187.0", "tag": "@bentley/imodeljs-frontend_v0.187.0", diff --git a/core/frontend/CHANGELOG.md b/core/frontend/CHANGELOG.md index 990e995..f37fc5f 100644 --- a/core/frontend/CHANGELOG.md +++ b/core/frontend/CHANGELOG.md @@ -1,6 +1,99 @@ # Change Log - @bentley/imodeljs-frontend -This log was last generated on Tue, 15 Jan 2019 15:18:59 GMT and should not be manually modified. +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- add ios oidc client +- geometry-core camel case +- Add Selection Scope toolsettings to SelectTool. +- allow subclasses of Range to use static methods +- Raise events when a Viewport's always- or never-drawn element sets change. +- OIDC changes needed for Angular client +- Changes package.json to include api-extractor and adds api-extractor.json +- #66826 Default SelectTool to select all members of the selected element's assembly. +- Default scope to element. +- Use new buildIModelJsBuild script +- Generalize support for reading tiles to include tiles generated for Bimium. +- AxisAlignedBox and ElementAlignedBox are now typed to Range3d rather than classes +- clone methods are no longer generic +- Optimize renderer to elide debug-only code paths unless explicitly enabled. +- Generalize 3d tile support. Handle transform on child nodes. +- Avoid using cutting planes while animating if not displayed, completely hidden or completely visible. +- Started work on webworker to decode jpeg files for GLTF +- Defer Draco support until moved to web worker +- Reduce memory consumption when ambient occlusion is disabled. +- Fix incorrect colors for some decoration graphics. +- Remove uneeded typedoc plugin depedency +- Add support for Draco compressed meshes +- Change drag select to exclude non locatable +- noMotion doesn't need to call beginDynamicUpdate +- example code (in comments) for frustum interpolator +- Consistent naming of "get" methods in Growable arrays. +- Add EmphasizeElements to/from wire format methods +- Draw non-emphasized elements in "fade-out", non-locatable mode. +- Move neverDrawn/alwaysDrawn to Viewport they are not part of the persistent ViewState. Change Viewport.addFeatureOverrides to an interface. +- Rework and simplify ecef transform for reality models. +- Correct ID for loading classifier trees. +- Fix clipping volume being inconsistently applied to view. +- Dont make textures transparent unless technique enables it. +- Fix incorrect "fit view" behavior when empty tiles exist. +- Handle relative subpaths in reality model tile trees. Handle Y for axis/ +- Fix handling of null animation visibility - should be 100% not 0. +- Added spatial <-> cartographic methods that check/use the geographic coordinate system before using ecef location. +- DefaultViewTouchTool should not call handleEvent until it's installed as the active ViewTool. +- Traverse GLTF node structure rather than meshes so that node transforms are used correctly. +- Ensure viewport updates immediately when background map settings are changed. +- Add a test to determine if GCS is present before using GCS converter. +- Documentation improvements +- Support instanced rendering of .i3dm 3D tiles. +- Preliminary support for drawing instanced geometry. +- Fix branch transform for animation correctly - back out incorrect fix to BranchState. +- Implemented, then commented out, doing jpeg decompression in a web worker +- added markup mode +- events are now on ScreenViewport.parentDiv rather than canvas +- update for geometry GrowableXYArray usage. +- Measure Distance - change selected segment hilite. Measure Location - WIP use ecef transform. +- More ui-framework unit tests +- Make it possible to define editor params for default Type Editors not explicitly specified by name. +- Fixed a bug which caused non-locatable geometry to be rendered when no other symbology was overridden. +- Defer loading of edges until needed +- Omit animation branches that are not visible. +- Improve efficiency and completeness of SubCategory loading for ViewStates. +- Save BUILD_SEMVER to globally accessible map. PluginAdmin and Plugin classes defined. IModelJsLoader improved. +- add optional iModel argument to EntityState.clone +- added GeometricModelState.queryModelRange +- Added creatorId, new method to list RD per project, identified numerous area for changes WIP +- IModelConnection.close() always disposes the briefcase held at the backend in the case of ReadWrite connections. +- Implemented spatial criterai when searching through all reality data associated to a project. +- Problem with root document of reality data not in root of blob. Tiles could not be fetched. Root path is added to tiles names. +- Threading issue accessing Reality Data, RealityData class was transformed to be the main data access object instead of the client that was used by most/all reality data causing cache data clash and mix between many reality data. +- Optimze containment test with spheres. +- Move the IModelUnitTestRpcInterface into the testbed and out of the public AP +- Retry tile requests on time-out. +- Remove loadNativeAsset and formatElements RPC calls from the IModelReadRpcInterface +- Removed IModelConnection.connectionId, added IModelApp.sessionId +- make view transition animations smoother +- Optimizations to tile format and schedule animation. +- Tile requests can optionally specify a retryInterval. +- Cleanup of DefaultToolSetting provider and EnumButtonGroup editor including new EditorParams. +- Move property definitions to imodeljs-frontend so they could be used by tools to define properties for tool settings. Add toolsettings to Select Tool. +- Added a new property to PropertyRecord - links. +- IModelConnection.connectionTimeout is public to allow application customization. +- upgrade to TypeScript 3.2.2 + +## 0.188.0 +Wed, 16 Jan 2019 16:36:09 GMT + +### Updates + +- Report unsigned measure distance deltas. +- Add batch id to schedule scripts +- Add batchID to schedule scripts +- Handle wider variety of GLTF bounding boxes etc. ## 0.187.0 Tue, 15 Jan 2019 15:18:59 GMT diff --git a/core/frontend/package.json b/core/frontend/package.json index bb454a0..9fd7f6f 100644 --- a/core/frontend/package.json +++ b/core/frontend/package.json @@ -1,21 +1,43 @@ { "name": "@bentley/imodeljs-frontend", - "version": "0.187.0", + "version": "0.189.0", "description": "iModel.js frontend components", "main": "lib/imodeljs-frontend.js", "typings": "lib/imodeljs-frontend", "license": "MIT", "scripts": { - "build": "tsc 1>&2 && npm run copy:public && npm run webpackModule-dev && npm run webpack-loader", + "build": "node ./node_modules/@bentley/webpack-tools/bin/buildIModelJsModule.js", "clean": "rimraf lib package-deps.json", - "copy:public": "cpx \"./src/public/**/*\" ./lib/public", "docs": "node ./node_modules/@bentley/build-tools/scripts/docs.js --source=./src --includes=../../generated-docs/extract --json=../../generated-docs/core/imodeljs-frontend/file.json --tsIndexFile=./imodeljs-frontend.ts --onlyJson %TYPEDOC_THEME% --excludes=webgl/**/*,**/primitives", + "extract-api": "node ./node_modules/@bentley/build-tools/scripts/extract-api.js --entry=imodeljs-frontend", "lint": "tslint --project . 1>&2", "test": "", - "cover": "", - "webpack-loader": "webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/loader/IModelJsLoader.js --env.bundlename=IModelJsLoader --env.sourcedir=./", - "webpackModule-dev": "make-dir ./lib/module/dev/ && webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/imodeljs-frontend.js --env.bundlename=imodeljs-frontend --json >./lib/module/dev/webpackStats.json", - "webpackModule-prod": "webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/imodeljs-frontend.js --env.bundlename=imodeljs-frontend --env.prod" + "cover": "" + }, + "iModelJs": { + "buildModule": { + "type": "system", + "tscOptions": "-build", + "sourceResources": [ + { + "source": "./src/public/**/*", + "dest": "./lib/public" + } + ], + "webpack": { + "dest": "./lib/module", + "entry": "./lib/imodeljs-frontend.js", + "bundleName": "imodeljs-frontend" + }, + "subModules": [ + { + "dest": "./lib/module", + "entry": "./lib/loader/IModelJsLoader.js", + "bundleName": "IModelJsLoader", + "type": "system" + } + ] + } }, "repository": { "type": "git", @@ -31,38 +53,33 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/geometry-core": "0.187.0", - "@bentley/imodeljs-clients": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-i18n": "0.187.0", - "@bentley/imodeljs-quantity": "0.187.0" + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/imodeljs-clients": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-i18n": "0.189.0", + "@bentley/imodeljs-quantity": "0.189.0" }, "//devDependencies": [ "NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install", "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/build-tools": "0.187.0", - "@bentley/geometry-core": "0.187.0", - "@bentley/imodeljs-clients": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-i18n": "0.187.0", - "@bentley/imodeljs-quantity": "0.187.0", - "@bentley/webpack-tools": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/build-tools": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/imodeljs-clients": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-i18n": "0.189.0", + "@bentley/imodeljs-quantity": "0.189.0", + "@bentley/webpack-tools": "0.189.0", "@types/js-base64": "^2.3.1", - "@types/node": "10.10.3", - "cpx": "^1.5.0", - "make-dir-cli": "^1.0.0", + "@types/node": "10.12.18", + "@types/semver": "^5.5.0", "rimraf": "^2.6.2", - "source-map-loader": "^0.2.3", "tslint": "^5.11.0", "typedoc": "^0.11.1", - "typedoc-plugin-external-module-name": "^1.1.1", - "typescript": "~3.1.0", - "webpack": "^4.20.2", - "webpack-cli": "^3.1.0" + "typescript": "~3.2.2" }, "//dependencies": [ "NOTE: these dependencies should be only for things that DO NOT APPEAR IN THE API", @@ -71,6 +88,7 @@ "dependencies": { "fuse.js": "^3.3.0", "js-base64": "^2.4.5", - "oidc-client": "^1.5.2" + "oidc-client": "^1.5.2", + "semver": "^5.5.0" } } diff --git a/core/frontend/src/AccuDraw.ts b/core/frontend/src/AccuDraw.ts index d40e778..ba6284b 100644 --- a/core/frontend/src/AccuDraw.ts +++ b/core/frontend/src/AccuDraw.ts @@ -2807,7 +2807,7 @@ export class AccuDraw { return; const worldToView = this.currentView.worldToViewMap.transform0; - const detail = CurveCurve.IntersectionProjectedXY(worldToView, usePointOnSnap ? curveSegment : curve, true, usePointOnSnap ? curve : curveSegment, true); + const detail = CurveCurve.intersectionProjectedXY(worldToView, usePointOnSnap ? curveSegment : curve, true, usePointOnSnap ? curve : curveSegment, true); if (0 === detail.dataA.length) return; diff --git a/core/frontend/src/AccuSnap.ts b/core/frontend/src/AccuSnap.ts index 76e8368..210f22d 100644 --- a/core/frontend/src/AccuSnap.ts +++ b/core/frontend/src/AccuSnap.ts @@ -553,7 +553,7 @@ export class AccuSnap implements Decorator { return undefined; const worldToView = second.viewport.worldToViewMap.transform0; - const detail = CurveCurve.IntersectionProjectedXY(worldToView, tpSegment, true, segment, true); + const detail = CurveCurve.intersectionProjectedXY(worldToView, tpSegment, true, segment, true); if (0 === detail.dataA.length) return undefined; diff --git a/core/frontend/src/CategorySelectorState.ts b/core/frontend/src/CategorySelectorState.ts index 21f2a24..b44161b 100644 --- a/core/frontend/src/CategorySelectorState.ts +++ b/core/frontend/src/CategorySelectorState.ts @@ -15,6 +15,7 @@ import { CategorySelectorProps } from "@bentley/imodeljs-common"; * @note To change the set of categories visible in a [[ViewState]] currently associated with a [[Viewport]], * use [[ViewState.changeCategoryDisplay]] to ensure the view updates appropriately on screen. * @see [[Category]] + * @public */ export class CategorySelectorState extends ElementState { public categories: Set = new Set(); diff --git a/core/frontend/src/ContextRealityModelState.ts b/core/frontend/src/ContextRealityModelState.ts index 62de6e6..9799b41 100644 --- a/core/frontend/src/ContextRealityModelState.ts +++ b/core/frontend/src/ContextRealityModelState.ts @@ -9,23 +9,28 @@ import { IModelApp } from "./IModelApp"; import { TileTreeModelState } from "./ModelState"; import { TileTree, TileTreeState } from "./tile/TileTree"; import { RealityModelTileTree, RealityModelTileClient, RealityModelTileUtils } from "./tile/RealityModelTileTree"; +import { RealityDataServicesClient, RealityData } from "@bentley/imodeljs-clients"; +import { ActivityLoggingContext, Guid, Id64String } from "@bentley/bentleyjs-core"; export class ContextRealityModelState implements TileTreeModelState { protected _tilesetUrl: string; protected _name: string; protected _tileTreeState: TileTreeState; protected _iModel: IModelConnection; + protected _modelId: Id64String; constructor(props: ContextRealityModelProps, iModel: IModelConnection) { this._name = props.name ? props.name : ""; this._tilesetUrl = props.tilesetUrl; - this._tileTreeState = new TileTreeState(iModel, true, ""); + this._modelId = iModel.transientIds.next; + this._tileTreeState = new TileTreeState(iModel, true, this._modelId); this._iModel = iModel; } public get name() { return this._name; } public get url() { return this._tilesetUrl; } public get tileTree(): TileTree | undefined { return this._tileTreeState.tileTree; } public get loadStatus(): TileTree.LoadStatus { return this._tileTreeState.loadStatus; } - public loadTileTree(_asClassifier?: boolean, _classifierExpansion?: number): TileTree.LoadStatus { + public get treeModelId(): Id64String { return this._modelId; } + public loadTileTree(_edgesRequired: boolean, _animationId?: Id64String, _asClassifier?: boolean, _classifierExpansion?: number): TileTree.LoadStatus { const tileTreeState = this._tileTreeState; if (TileTree.LoadStatus.NotLoaded !== tileTreeState.loadStatus) return tileTreeState.loadStatus; @@ -35,6 +40,10 @@ export class ContextRealityModelState implements TileTreeModelState { RealityModelTileTree.loadRealityModelTileTree(this._tilesetUrl, undefined, tileTreeState); return tileTreeState.loadStatus; } + /** + * Indicates if the reality model overlaps the project extent + * @returns a bool that indicates if the model and the reality data overlap + */ public async intersectsProjectExtents(): Promise { if (undefined === this._iModel.ecefLocation || undefined === IModelApp.accessToken) return false; @@ -44,7 +53,7 @@ export class ContextRealityModelState implements TileTreeModelState { let tileTreeRange, tileTreeTransform; if (json === undefined || undefined === json.root || - undefined === (tileTreeRange = RealityModelTileUtils.rangeFromBoundingVolume(json.root.boundingVolume, this._iModel.ecefLocation)) || + undefined === (tileTreeRange = RealityModelTileUtils.rangeFromBoundingVolume(json.root.boundingVolume)) || undefined === (tileTreeTransform = RealityModelTileUtils.transformFromJson(json.root.transform))) return false; @@ -53,21 +62,60 @@ export class ContextRealityModelState implements TileTreeModelState { return treeCartographicRange.intersectsRange(projectCartographicRange); } + /** + * Gets a tileset's tile data blob key url + * @param other Another ContextRealityModelState oject to compare with self. + * @returns a bool that indicates if the two match + */ public matches(other: ContextRealityModelState) { return other.name === this.name && other.url === this.url; } - public static findAvailableRealityModels(): ContextRealityModelProps[] { + /** + * Returns a list of reality data associated to the given CONNECT project + * @param projectId id of associated connect project + * @param modelCartographicRange optional cartographic range of the model that can limit the spatial range for the search + * @returns a list of reality model properties associated with the project + */ + public static async findAvailableRealityModels(projectid: string, modelCartographicRange?: CartographicRange | undefined): Promise { + const availableRealityModels: ContextRealityModelProps[] = []; - /* This is location to query all reality models available for this project. They will be filtered spatially to only those - that overlap the project extents later.... */ + if (undefined !== IModelApp.accessToken) { + const client = new RealityDataServicesClient(); + const alctx = new ActivityLoggingContext(Guid.createValue()); + + let realityData: RealityData[]; + if (modelCartographicRange) { + const iModelRange = modelCartographicRange.getLongitudeLatitudeBoundingBox(); + + realityData = await client.getRealityDataInProjectOverlapping(alctx, IModelApp.accessToken, projectid, iModelRange); + + } else { + realityData = await client.getRealityDataInProject(alctx, IModelApp.accessToken, projectid); + } + + // We obtain the reality data name, and RDS URL for each RD retuned. + for (const currentRealityData of realityData) { + let realityDataName: string = ""; + let validRd: boolean = true; + if (currentRealityData.name && currentRealityData.name !== "") { + realityDataName = currentRealityData.name as string; + } else if (currentRealityData.rootDocument) { + // In case root document contains a relative path we only keep the filename + const rootDocParts = (currentRealityData.rootDocumentb as string).split("/"); + realityDataName = rootDocParts[rootDocParts.length - 1]; + } else { + // This case would not occur normally but if it does the RD is considered invalid + validRd = false; + } - availableRealityModels.push({ name: "Sample", tilesetUrl: "http://localhost:8080/TilesetWithDiscreteLOD/tileset.json" }); - /* Testing... should be quering RDS to find models. - availableRealityModels.push({ name: "Clark Island", tilesetUrl: "http://localhost:8080/clarkIsland/74/TileRoot.json" }); - availableRealityModels.push({ name: "Philadelphia LoRes", tilesetUrl: "http://localhost:8080/PhiladelphiaLoResClassification/80/TileRoot.json" }); - availableRealityModels.push({ name: "Philadelphia HiRes", tilesetUrl: "http://localhost:8080/PhiladelphiaHiResClassification/80/TileRoot.json" }); - */ + // If the RealityData is valid then we add it to the list. + if (currentRealityData.id && validRd === true) { + const url = await client.getRealityDataUrl(alctx, projectid, currentRealityData.id as string); + availableRealityModels.push({ tilesetUrl: url, name: realityDataName, description: (currentRealityData.description ? currentRealityData.description : "") }); + } + } + } return availableRealityModels; } diff --git a/core/frontend/src/DisplayStyleState.ts b/core/frontend/src/DisplayStyleState.ts index cc2fe61..7f55b0c 100644 --- a/core/frontend/src/DisplayStyleState.ts +++ b/core/frontend/src/DisplayStyleState.ts @@ -35,6 +35,7 @@ import { RenderScheduleState } from "./RenderScheduleState"; * @note If the DisplayStyle is associated with a [[ViewState]] which is being rendered inside a [[Viewport]], modifying * the DisplayStyle directly will generally not result in immediately visible changes on the screen. * [[ViewState]] provides APIs which forward to the DisplayStyle API and also ensure the screen is updated promptly. + * @public */ export abstract class DisplayStyleState extends ElementState implements DisplayStyleProps { private _backgroundMap: BackgroundMapState; @@ -168,7 +169,9 @@ export abstract class DisplayStyleState extends ElementState implements DisplayS public getSubCategoryOverride(id: Id64String): SubCategoryOverride | undefined { return this.settings.getSubCategoryOverride(id); } } -/** A DisplayStyle for 2d views */ +/** A DisplayStyle for 2d views + * @public + */ export class DisplayStyle2dState extends DisplayStyleState { private readonly _settings: DisplayStyleSettings; @@ -185,6 +188,7 @@ export class DisplayStyle2dState extends DisplayStyleState { * - A cube with a texture image mapped to each face; * - A sphere with a single texture image mapped to its surface; * - A sphere with a [[Gradient]] mapped to its surface. + * @public */ export abstract class SkyBox implements SkyBoxProps { /** Whether or not the skybox should be displayed. */ @@ -228,6 +232,7 @@ export abstract class SkyBox implements SkyBoxProps { * - A cube with a texture image mapped to each face; * - A sphere with a single texture image mapped to its surface; * - A sphere with a [[Gradient]] mapped to its surface. + * @hidden */ export namespace SkyBox { /** @hidden */ @@ -255,7 +260,8 @@ export namespace SkyBox { } } -/** ###TODO Document me... */ +// ###TODO Document me... +/** @beta Needs documentation before moving to public */ export class SkyGradient extends SkyBox { public readonly twoColor: boolean = false; public readonly zenithColor: ColorDef; // the color of the zenith part of the sky gradient (shown when looking straight up.) @@ -300,6 +306,7 @@ export class SkyGradient extends SkyBox { /** A [[SkyBox]] drawn as a sphere with an image mapped to its interior surface. * @see [[SkyBox.createFromJSON]] + * @public */ export class SkySphere extends SkyBox { /** The ID of the texture element which supplies the skybox image. */ @@ -339,6 +346,7 @@ export class SkySphere extends SkyBox { /** A [[SkyBox]] drawn as a cube with an image mapped to each of its interior faces. * Each member specifies the ID of a texture element from which the image mapped to the corresponding face is obtained. * @see [[SkyBox.createFromJSON]]. + * @public */ export class SkyCube extends SkyBox implements SkyCubeProps { public readonly front: Id64String; @@ -447,7 +455,9 @@ export class Environment implements EnvironmentProps { } } -/** A {{DisplayStyle]] for 3d views */ +/** A [[DisplayStyle]] for 3d views + * @public + */ export class DisplayStyle3dState extends DisplayStyleState { /** @hidden */ public skyboxMaterial: RenderMaterial | undefined; diff --git a/core/frontend/src/ElementLocateManager.ts b/core/frontend/src/ElementLocateManager.ts index 3810d1e..106b9be 100644 --- a/core/frontend/src/ElementLocateManager.ts +++ b/core/frontend/src/ElementLocateManager.ts @@ -12,21 +12,24 @@ import { Pixel } from "./rendering"; import { InputSource, InteractiveTool } from "./tools/Tool"; import { Id64 } from "@bentley/bentleyjs-core"; -/** The possible actions for which a locate filter can be called. */ +/** The possible actions for which a locate filter can be called. + * @public + */ export const enum LocateAction { Identify = 0, AutoLocate = 1, } -/** - * Values to return from a locate filter. +/** Values to return from a locate filter. * Return `Reject` to indicate the element is unacceptable. + * @public */ export const enum LocateFilterStatus { Accept = 0, Reject = 1, } +/** @public */ export const enum SnapStatus { Success = 0, Aborted = 1, @@ -38,10 +41,14 @@ export const enum SnapStatus { FilteredByAppQuietly = 700, } -/** Options that customize the way element location (i.e. *picking*) works. */ +/** Options that customize the way element location (i.e. *picking*) works. + * @public + */ export class LocateOptions { /** If true, also test graphics from view decorations. */ public allowDecorations = false; + /** If true, also test graphics with non-locatable flag set. */ + public allowNonLocatable = false; /** Maximum number of hits to return. */ public maxHits = 20; /** The [[HitSource]] identifying the caller. */ @@ -50,11 +57,12 @@ export class LocateOptions { public clone(): LocateOptions { const other = new LocateOptions(); other.allowDecorations = this.allowDecorations; + other.allowNonLocatable = this.allowNonLocatable; other.maxHits = this.maxHits; other.hitSource = this.hitSource; return other; } - public init() { this.allowDecorations = false; this.maxHits = 20; this.hitSource = HitSource.DataPoint; } + public init() { this.allowDecorations = this.allowNonLocatable = false; this.maxHits = 20; this.hitSource = HitSource.DataPoint; } } export class LocateResponse { @@ -180,7 +188,7 @@ export class ElementPicker { this.hitList!.hits.length = options.maxHits; // truncate array... } result = this.hitList!.length; - }); + }, !options.allowNonLocatable); return result; } diff --git a/core/frontend/src/EmphasizeElements.ts b/core/frontend/src/EmphasizeElements.ts new file mode 100644 index 0000000..a36e6e6 --- /dev/null +++ b/core/frontend/src/EmphasizeElements.ts @@ -0,0 +1,459 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module Rendering */ + +import { FeatureOverrideProvider, Viewport } from "./Viewport"; +import { ColorDef, ColorDefProps, RgbColor } from "@bentley/imodeljs-common"; +import { Id64Set, Id64Arg, Id64 } from "@bentley/bentleyjs-core"; +import { FeatureSymbology } from "./rendering"; + +/** Whether override includes both color and alpha, only color, or only alpha. */ +export const enum FeatureOverrideType { ColorOnly, AlphaOnly, ColorAndAlpha } + +export interface AppearanceOverrideProps { + overrideType?: FeatureOverrideType; + color?: ColorDefProps; + ids?: Id64Set; +} + +export interface EmphasizeElementsProps { + neverDrawn?: Id64Set; + alwaysDrawn?: Id64Set; + isAlwaysDrawnExclusive?: boolean; + defaultAppearance?: FeatureSymbology.AppearanceProps; + appearanceOverride?: AppearanceOverrideProps[]; +} + +/** + * An implementation of [[FeatureOverrideProvider]] for emphasizing selected elements through simple color/transparency appearance overrides. + */ +export class EmphasizeElements implements FeatureOverrideProvider { + private _defaultAppearance?: FeatureSymbology.Appearance; + private _overrideAppearance?: Map; + + /** + * Establish active feature overrides to emphasize elements and apply color/transparency overrides. + * @see [[Viewport.featureOverrideProvider]] + */ + public addFeatureOverrides(overrides: FeatureSymbology.Overrides, vp: Viewport): void { + const emphasizedElements = this.getEmphasizedElements(vp); + if (undefined !== emphasizedElements) { + overrides.setDefaultOverrides(this._defaultAppearance!); + const app = FeatureSymbology.Appearance.defaults; + emphasizedElements.forEach((id) => { overrides.overrideElement(id, app); }); + } + const overriddenElements = this.getOverriddenElements(); + if (undefined !== overriddenElements) { + for (const [key, ids] of overriddenElements) { + const ovrApp = this.createAppearanceFromKey(key); + ids.forEach((id) => { overrides.overrideElement(id, ovrApp); }); + } + } + } + + /** @hidden */ + protected createAppearanceFromKey(key: number): FeatureSymbology.Appearance { + if (key < 0) + return FeatureSymbology.Appearance.fromTransparency(Math.abs(key)); + const color = ColorDef.fromJSON(key); + if (0 === color.getAlpha()) + return FeatureSymbology.Appearance.fromRgb(color); // Fully transparent signifies to use color only... + return FeatureSymbology.Appearance.fromRgba(color); + } + + /** @hidden */ + protected createOverrideKey(color: ColorDef, override: FeatureOverrideType): number | undefined { + const colorValues = color.colors; + switch (override) { + case FeatureOverrideType.ColorAndAlpha: + return 255 === colorValues.t ? undefined : color.tbgr; // Hiding elements should be done using neverDrawn, not transparency... + case FeatureOverrideType.ColorOnly: + return ColorDef.from(colorValues.r, colorValues.g, colorValues.b, 255).tbgr; + case FeatureOverrideType.AlphaOnly: + return -(colorValues.t / 255); + } + } + + /** Get color and override type for the given key. */ + public getOverrideFromKey(key: number, color: ColorDef): FeatureOverrideType { + if (key < 0) { + color.setFrom(ColorDef.from(0, 0, 0, 255 * Math.abs(key))); + return FeatureOverrideType.AlphaOnly; + } + color.setFrom(ColorDef.fromJSON(key)); + if (0 === color.getAlpha()) { + color.setAlpha(255); + return FeatureOverrideType.ColorOnly; + } + return FeatureOverrideType.ColorAndAlpha; + } + + /** Get the current default appearance as established by emphasizeElements. */ + public get defaultAppearance(): FeatureSymbology.Appearance | undefined { return this._defaultAppearance; } + + /** Create default appearance to use for emphasizeElements when not supplied by caller. */ + public createDefaultAppearance(): FeatureSymbology.Appearance { + return FeatureSymbology.Appearance.fromJSON({ + rgb: new RgbColor(0xe4, 0xe4, 0xe4), + transparency: 0.8, + nonLocatable: true, + }); + } + + /** @hidden Get the IDs of the currently never drawn elements. */ + public getNeverDrawnElements(vp: Viewport): Id64Set | undefined { return (undefined !== vp.neverDrawn && 0 !== vp.neverDrawn.size ? vp.neverDrawn : undefined); } + /** @hidden Get the IDs of the currently always drawn elements. */ + public getAlwaysDrawnElements(vp: Viewport): Id64Set | undefined { return (undefined !== vp.alwaysDrawn && 0 !== vp.alwaysDrawn.size ? vp.alwaysDrawn : undefined); } + /** Get the IDs of the currently hidden elements. */ + public getHiddenElements(vp: Viewport): Id64Set | undefined { return this.getNeverDrawnElements(vp); } + /** Get the IDs of the currently isolated elements. */ + public getIsolatedElements(vp: Viewport): Id64Set | undefined { return (vp.isAlwaysDrawnExclusive ? this.getAlwaysDrawnElements(vp) : undefined); } + /** Get the IDs of the currently emphasized elements. */ + public getEmphasizedElements(vp: Viewport): Id64Set | undefined { return (!vp.isAlwaysDrawnExclusive && undefined !== this._defaultAppearance ? this.getAlwaysDrawnElements(vp) : undefined); } + /** Get the map of current elements with color/transparency overrides. */ + public getOverriddenElements(): Map | undefined { return (undefined !== this._overrideAppearance && 0 !== this._overrideAppearance.size ? this._overrideAppearance : undefined); } + /** @hidden Get the IDs of current elements with the specified color/tranparency override. */ + public getOverriddenElementsByKey(key: number): Id64Set | undefined { return (undefined !== this._overrideAppearance ? this._overrideAppearance.get(key) : undefined); } + + /** @hidden Clear never drawn elements. + * @return false if nothing to clear. + */ + public clearNeverDrawnElements(vp: Viewport): boolean { + if (undefined === this.getNeverDrawnElements(vp)) + return false; + vp.clearNeverDrawn(); + return true; + } + + /** @hidden Clear always drawn elements. + * @return false if nothing to clear. + */ + public clearAlwaysDrawnElements(vp: Viewport): boolean { + if (undefined === this.getAlwaysDrawnElements(vp)) + return false; + this._defaultAppearance = undefined; + vp.clearAlwaysDrawn(); + return true; + } + + /** Clear hidden elements. + * @return false if nothing to clear. + */ + public clearHiddenElements(vp: Viewport): boolean { + return this.clearNeverDrawnElements(vp); + } + + /** Clear isolated elements. + * @return false if nothing to clear. + */ + public clearIsolatedElements(vp: Viewport): boolean { + if (undefined === this.getIsolatedElements(vp)) + return false; + return this.clearAlwaysDrawnElements(vp); + } + + /** Clear emphasized elements. + * @return false if nothing to clear. + */ + public clearEmphasizedElements(vp: Viewport): boolean { + if (undefined === this.getEmphasizedElements(vp)) + return false; + return this.clearAlwaysDrawnElements(vp); + } + + /** Clear elements with color/transparency overrides. Specify key to clear only a single override. + * @return false if nothing to clear. + */ + public clearOverriddenElements(vp: Viewport, key?: number): boolean { + if (undefined === this._overrideAppearance) + return false; + if (undefined !== key) { + if (!this._overrideAppearance.delete(key)) + return false; + } else { + this._overrideAppearance = undefined; + } + vp.view.setFeatureOverridesDirty(true); + return true; + } + + /** @hidden Set the element IDs to be never drawn. + * @param ids The IDs of the elements to never draw. + * @param vp The viewport. + * @param replace true to replace currently hidden elements (if any) or false to add to the existing set. + * @return true if overrides were changed. + * @see [[Viewport.neverDrawn]] + */ + public setNeverDrawnElements(ids: Id64Arg, vp: Viewport, replace: boolean = true): boolean { + const hiddenIds = new Set(); + Id64.toIdSet(ids).forEach((id) => { hiddenIds.add(id); }); + if (0 === hiddenIds.size) + return false; + const oldSize = (!replace && undefined !== vp.neverDrawn ? vp.neverDrawn.size : 0); + if (0 !== oldSize && undefined !== vp.neverDrawn) + for (const id of vp.neverDrawn) + hiddenIds.add(id); + if (oldSize === hiddenIds.size) + return false; + vp.setNeverDrawn(hiddenIds); + return true; + } + + /** @hidden Set the element IDs to be always drawn. + * @param ids The IDs of the elements to always draw. + * @param vp The viewport. + * @param exclusive If true, *only* the specified elements will be drawn. + * @param replace true to replace currently always drawn elements (if any) or false to add to the existing set. + * @return true if overrides were changed. + * @see [[Viewport.alwaysDrawn]] + * @see [[Viewport.isAlwaysDrawnExclusive]] + */ + public setAlwaysDrawnElements(ids: Id64Arg, vp: Viewport, exclusive: boolean = true, replace: boolean = true): boolean { + const visibleIds = new Set(); + Id64.toIdSet(ids).forEach((id) => { visibleIds.add(id); }); + if (0 === visibleIds.size) + return false; + const oldSize = (!replace && undefined !== vp.alwaysDrawn ? vp.alwaysDrawn.size : 0); + if (0 !== oldSize && undefined !== vp.alwaysDrawn) + for (const id of vp.alwaysDrawn) + visibleIds.add(id); + if (oldSize === visibleIds.size) + return false; + this._defaultAppearance = undefined; + vp.setAlwaysDrawn(visibleIds, exclusive); + return true; + } + + /** Set the element IDs to be never drawn. + * @param ids The IDs of the elements to never draw. + * @param vp The viewport. + * @param replace true to replace currently hidden elements (if any) or false to add to the existing set. + * @return true if overrides were changed. + * @see [[Viewport.neverDrawn]] + */ + public hideElements(ids: Id64Arg, vp: Viewport, replace: boolean = false): boolean { + return this.setNeverDrawnElements(ids, vp, replace); + } + + /** Set the currently selected elements to be never drawn. + * @param vp The viewport. + * @param replace true to replace currently hidden elements (if any) or false to add to the existing set. + * @param clearSelection true to clear current selection after setting appearance override, false to leave selected. + * @return true if overrides were changed. + * @see [[Viewport.neverDrawn]] + */ + public hideSelectedElements(vp: Viewport, replace: boolean = false, clearSelection: boolean = true): boolean { + const selection = vp.view.iModel.selectionSet; + if (!selection.isActive || !this.hideElements(selection.elements, vp, replace)) + return false; + if (clearSelection) + selection.emptyAll(); + return true; + } + + /** Set the element IDs to be always drawn exclusively. + * @param ids The IDs of the elements to always draw. + * @param vp The viewport. + * @param replace true to replace currently isolated elements (if any) or false to add to the existing set. + * @return true if overrides were changed. + * @see [[Viewport.alwaysDrawn]] + * @see [[Viewport.isAlwaysDrawnExclusive]] + */ + public isolateElements(ids: Id64Arg, vp: Viewport, replace: boolean = true): boolean { + return this.setAlwaysDrawnElements(ids, vp, true, replace); + } + + /** Set the currently selected elements to be always drawn exclusively. + * @param vp The viewport. + * @param replace true to replace currently isolated elements (if any) or false to add to the existing set. + * @param clearSelection true to clear current selection after setting appearance override, false to leave selected. + * @return true if overrides were changed. + * @see [[Viewport.alwaysDrawn]] + * @see [[Viewport.isAlwaysDrawnExclusive]] + */ + public isolateSelectedElements(vp: Viewport, replace: boolean = true, clearSelection: boolean = true): boolean { + const selection = vp.view.iModel.selectionSet; + if (!selection.isActive || !this.isolateElements(selection.elements, vp, replace)) + return false; + if (clearSelection) + selection.emptyAll(); + return true; + } + + /** Set the element IDs to be always drawn normally with all other elements in the view overridden to draw using a default appearance.. + * @param ids The IDs of the elements to always draw. + * @param vp The viewport. + * @param defaultAppearance Optional default appearance, uses non-locatable transparent grey if not specified. + * @param replace true to replace currently overridden elements (if any) or false to add to the existing set. + * @return true if overrides were changed. + * @see [[Viewport.alwaysDrawn]] + * @see [[Viewport.isAlwaysDrawnExclusive]] + */ + public emphasizeElements(ids: Id64Arg, vp: Viewport, defaultAppearance?: FeatureSymbology.Appearance, replace: boolean = true): boolean { + if (!this.setAlwaysDrawnElements(ids, vp, false, replace)) + return false; + this._defaultAppearance = (undefined === defaultAppearance ? this.createDefaultAppearance() : defaultAppearance); + return true; + } + + /** Set the currently selected elements to be always drawn normally with all other elements in the view overridden to draw using a default appearance. + * @param vp The viewport. + * @param defaultAppearance Optional default appearance, uses transparent grey if not specified. + * @param replace true to replace currently overridden elements (if any) or false to add to the existing set. + * @param clearSelection true to clear current selection after setting appearance override, false to leave selected. + * @return true if overrides were changed. + * @see [[Viewport.alwaysDrawn]] + * @see [[Viewport.isAlwaysDrawnExclusive]] + */ + public emphasizeSelectedElements(vp: Viewport, defaultAppearance?: FeatureSymbology.Appearance, replace: boolean = true, clearSelection: boolean = true): boolean { + const selection = vp.view.iModel.selectionSet; + if (!selection.isActive || !this.emphasizeElements(selection.elements, vp, defaultAppearance, replace)) + return false; + if (clearSelection) + selection.emptyAll(); + return true; + } + + /** Set the element IDs to display with a color/transparency override. + * @param ids The IDs of the elements. + * @param vp The viewport. + * @param color ColorDef to specify override rgb and alpha. + * @param override Whether to use color and alpha, only color, or only alpha from the supplied ColorDef. + * @param replace true to replace currently overridden elements (if any) or false to add to the existing set. + * @return true if overrides were changed. + * @see [[Viewport.featureOverrideProvider]] + */ + public overrideElements(ids: Id64Arg, vp: Viewport, color: ColorDef, override: FeatureOverrideType = FeatureOverrideType.ColorOnly, replace: boolean = false): boolean { + const ovrKey = this.createOverrideKey(color, override); + if (undefined === ovrKey) + return false; + const overrideIds = new Set(); + Id64.toIdSet(ids).forEach((id) => { overrideIds.add(id); }); + if (0 === overrideIds.size) + return false; + const existingIds = (!replace ? this.getOverriddenElementsByKey(ovrKey) : undefined); + const oldSize = (undefined !== existingIds ? existingIds.size : 0); + if (0 !== oldSize && undefined !== existingIds) + for (const id of existingIds) + overrideIds.add(id); + if (oldSize === overrideIds.size) + return false; + if (undefined === this._overrideAppearance) { + this._overrideAppearance = new Map(); + } else { + for (const [key, otherIds] of this._overrideAppearance) { + if (key === ovrKey) // Make sure these ids are unique to this color/transparency key... + continue; + Id64.toIdSet(ids).forEach((id) => { otherIds.delete(id); }); + if (0 !== otherIds.size) + continue; + this._overrideAppearance.delete(key); + } + } + this._overrideAppearance.set(ovrKey, overrideIds); + vp.view.setFeatureOverridesDirty(true); + return true; + } + + /** Set the currently selected elements to display with a color/transparency override. + * @param vp The viewport. + * @param color ColorDef to specify override rgb and alpha. + * @param override Whether to use color and alpha, only color, or only alpha from the supplied ColorDef. + * @param replace true to replace currently overridden elements (if any) or false to add to the existing set. + * @param clearSelection true to clear current selection after setting appearance override, false to leave selected. + * @return true if overrides were changed. + * @see [[Viewport.featureOverrideProvider]] + */ + public overrideSelectedElements(vp: Viewport, color: ColorDef, override: FeatureOverrideType = FeatureOverrideType.ColorOnly, replace: boolean = false, clearSelection: boolean = true): boolean { + const selection = vp.view.iModel.selectionSet; + if (!selection.isActive || !this.overrideElements(selection.elements, vp, color, override, replace)) + return false; + if (clearSelection) + selection.emptyAll(); + return true; + } + + /** @return true if provider is currently overriding the display of any elements. */ + public isActive(vp: Viewport): boolean { return (undefined !== this.getNeverDrawnElements(vp) || undefined !== this.getAlwaysDrawnElements(vp) || undefined !== this.getOverriddenElements()); } + + public toJSON(vp: Viewport): EmphasizeElementsProps { + const props: EmphasizeElementsProps = {}; + const neverDrawn = this.getNeverDrawnElements(vp); + if (undefined !== neverDrawn) + props.neverDrawn = new Set(neverDrawn); + const alwaysDrawn = this.getAlwaysDrawnElements(vp); + if (undefined !== alwaysDrawn) + props.alwaysDrawn = new Set(alwaysDrawn); + if (vp.isAlwaysDrawnExclusive) + props.isAlwaysDrawnExclusive = true; // isolate + else if (undefined !== this.defaultAppearance) + props.defaultAppearance = this.defaultAppearance; // emphasize + const overriddenElements = this.getOverriddenElements(); + if (undefined !== overriddenElements) { + const appearanceOverride: AppearanceOverrideProps[] = []; + const color = new ColorDef(); + for (const [key, ovrIds] of overriddenElements) { + const overrideType = this.getOverrideFromKey(key, color); + const ids = new Set(ovrIds); + appearanceOverride.push({ overrideType, color, ids }); + } + props.appearanceOverride = appearanceOverride; + } + return props; + } + + public fromJSON(props: EmphasizeElementsProps, vp: Viewport): boolean { + let changed = false; + if (undefined !== props.neverDrawn) { + if (this.setNeverDrawnElements(props.neverDrawn, vp, true)) + changed = true; + } + if (undefined !== props.alwaysDrawn) { + if (undefined !== props.defaultAppearance) { + if (this.emphasizeElements(props.alwaysDrawn, vp, FeatureSymbology.Appearance.fromJSON(props.defaultAppearance), true)) + changed = true; + } else { + if (this.setAlwaysDrawnElements(props.alwaysDrawn, vp, props.isAlwaysDrawnExclusive)) + changed = true; + } + } + if (undefined !== props.appearanceOverride) { + for (const ovrApp of props.appearanceOverride) { + if (undefined === ovrApp.ids) + continue; + if (this.overrideElements(ovrApp.ids, vp, ColorDef.fromJSON(ovrApp.color), ovrApp.overrideType, true)) + changed = true; + } + } + return changed; + } + + /** Get current [[Viewport.featureOverrideProvider]] if it's an instance of EmphasizeElements. */ + public static get(vp: Viewport): EmphasizeElements | undefined { + return (vp.featureOverrideProvider instanceof EmphasizeElements ? vp.featureOverrideProvider : undefined); + } + + /** Get or replace current [[Viewport.featureOverrideProvider]] with an instance of EmphasizeElements. */ + public static getOrCreate(vp: Viewport): EmphasizeElements { + let provider = vp.featureOverrideProvider instanceof EmphasizeElements ? vp.featureOverrideProvider : undefined; + if (undefined === provider) { + provider = new EmphasizeElements(); + vp.featureOverrideProvider = provider; + } + return provider; + } + + /** Clear current [[Viewport.featureOverrideProvider]] if it's an instance of EmphasizeElements. */ + public static clear(vp: Viewport, inactiveOnly: boolean = false) { + const provider = vp.featureOverrideProvider instanceof EmphasizeElements ? vp.featureOverrideProvider : undefined; + if (undefined === provider || (inactiveOnly && provider.isActive)) + return; + vp.clearNeverDrawn(); + vp.clearAlwaysDrawn(); + vp.featureOverrideProvider = undefined; + } + + // ###TODO - Implement toJSON/fromJSON... +} diff --git a/core/frontend/src/EntityState.ts b/core/frontend/src/EntityState.ts index dda1d00..6d1d395 100644 --- a/core/frontend/src/EntityState.ts +++ b/core/frontend/src/EntityState.ts @@ -8,7 +8,9 @@ import { Id64, Id64String, GuidString } from "@bentley/bentleyjs-core"; import { EntityProps, Code, ElementProps, RelatedElement } from "@bentley/imodeljs-common"; import { IModelConnection } from "./IModelConnection"; -/** The "state" of an Entity as represented in a web browser. */ +/** The "state" of an Entity as represented in a web browser. + * @public + */ export class EntityState implements EntityProps { public readonly id: Id64String; public readonly iModel: IModelConnection; @@ -16,8 +18,7 @@ export class EntityState implements EntityProps { public readonly jsonProperties: { [key: string]: any }; public static schemaName = "BisCore"; - /** - * constructor for EntityState + /** Constructor for EntityState * @param props the properties of the Entity for this EntityState * @param iModel the iModel from which this EntityState is to be constructed * @param _state source EntityState for clone @@ -39,28 +40,28 @@ export class EntityState implements EntityProps { return val; } - public equals(other: EntityState): boolean { return JSON.stringify(this.toJSON()) === JSON.stringify(other.toJSON()); } + public equals(other: this): boolean { return JSON.stringify(this.toJSON()) === JSON.stringify(other.toJSON()); } /** Make an independent copy of this EntityState */ - public clone() { return new (this.constructor as typeof EntityState)(this.toJSON(), this.iModel, this) as T; } + public clone(iModel?: IModelConnection): this { return new (this.constructor as typeof EntityState)(this.toJSON(), iModel ? iModel : this.iModel, this) as this; } - /** - * Get full class name of this Entity in the form "SchemaName:ClassName". + /** Get full class name of this Entity in the form "SchemaName:ClassName". * @note Subclasses from other than the BisCore domain should override their static member "schemaName" with their schema name. */ public static getClassFullName(): string { return this.schemaName + ":" + this.className; } public static get sqlName(): string { return this.schemaName + "." + this.className; } - /** - * Get the ECClass name for this EntityState. + /** Get the ECClass name for this EntityState. * @note This default implementation relies on all EntityState subclasses using their ECClass name as their JavaScript class name, with "State" appended to the end . * If this is not true, you must override this method. */ public static get className(): string { return this.name.slice(0, this.name.lastIndexOf("State")); } } -/** The "state" of an Element as represented in a web browser. */ +/** The "state" of an Element as represented in a web browser. + * @public + */ export class ElementState extends EntityState implements ElementProps { public readonly model: Id64String; public readonly code: Code; diff --git a/core/frontend/src/GeoServices.ts b/core/frontend/src/GeoServices.ts index 96eed61..65a442a 100644 --- a/core/frontend/src/GeoServices.ts +++ b/core/frontend/src/GeoServices.ts @@ -2,8 +2,6 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -/** @module GeoServices */ - import { IModelConnection } from "./IModelConnection"; import { IModelReadRpcInterface, @@ -153,6 +151,11 @@ class IMCtoGCResultCache { } } +/** + * The GeoConverter class communicates with the backend to convert longitude/latitude coordinates to iModel coordinates and vice-versa + * @internal + * @hidden + */ export class GeoConverter { private _datum: string; private _gCtoIMCResultCache: GCtoIMCResultCache; @@ -174,12 +177,14 @@ export class GeoConverter { } } -/** The Geographic Services available for an [[IModelConnection]]. */ +/** + * The Geographic Services available for an [[IModelConnection]]. + * @internal + * @hidden + */ export class GeoServices { - /** @hidden */ private _iModel: IModelConnection; - /** @hidden */ constructor(iModel: IModelConnection) { this._iModel = iModel; } diff --git a/core/frontend/src/HitDetail.ts b/core/frontend/src/HitDetail.ts index db79b32..96f597b 100644 --- a/core/frontend/src/HitDetail.ts +++ b/core/frontend/src/HitDetail.ts @@ -12,6 +12,7 @@ import { DecorateContext } from "./ViewContext"; import { GraphicType } from "./render/GraphicBuilder"; import { LinePixels, GeometryClass } from "@bentley/imodeljs-common"; +/** @public */ export const enum SnapMode { Nearest = 1, NearestKeypoint = 1 << 1, @@ -22,13 +23,16 @@ export const enum SnapMode { Intersection = 1 << 6, } +/** @public */ export const enum SnapHeat { None = 0, NotInRange = 1, // "of interest", but out of range InRange = 2, } -/** The procedure that generated this Hit. */ +/** The procedure that generated this Hit. + * @public + */ export const enum HitSource { None = 0, FromUser = 1, @@ -41,10 +45,9 @@ export const enum HitSource { EditActionSS = 8, } -/** - * What was being tested to generate this hit. This is not the element or - * GeometricPrimitive that generated the Hit, it is an indication of whether it is an - * edge or interior hit. +/** What was being tested to generate this hit. This is not the element or + * GeometricPrimitive that generated the Hit, it is an indication of whether it is an edge or interior hit. + * @public */ export const enum HitGeomType { None = 0, @@ -55,8 +58,8 @@ export const enum HitGeomType { Surface = 5, } -/** - * Classification of GeometricPrimitive that generated the Hit. +/** Classification of GeometricPrimitive that generated the Hit. + * @public */ export const enum HitParentGeomType { None = 0, @@ -67,6 +70,7 @@ export const enum HitParentGeomType { Text = 5, } +/** @public */ export const enum HitPriority { WireEdge = 0, PlanarEdge = 1, @@ -77,19 +81,19 @@ export const enum HitPriority { Unknown = 6, } +/** @public */ export const enum HitDetailType { Hit = 1, Snap = 2, Intersection = 3, } -/** - * A HitDetail stores the result when locating geometry displayed in a view. +/** A HitDetail stores the result when locating geometry displayed in a view. * It holds an approximate location on an element (or decoration) from a *pick*. + * @public */ export class HitDetail { - /** - * Create a new HitDetail from the inputs to and results of a locate operation. + /** Create a new HitDetail from the inputs to and results of a locate operation. * @param testPoint The world coordinate space point that was used as the locate point. * @param viewport The view the locate operation was performed in. * @param hitSource The procedure that requested the locate operation. @@ -127,8 +131,7 @@ export class HitDetail { /** Draw this HitDetail as a Decoration. Causes the picked element to *flash* */ public draw(_context: DecorateContext) { this.viewport.setFlashed(this.sourceId, 0.25); } - /** - * Get the tooltip content for this HitDetail. + /** Get the tooltip content for this HitDetail. * Calls the backend method [Element.getToolTipMessage]($backend), and replaces all instances of `${localizeTag}` with localized string from IModelApp.i18n. */ public async getToolTip(): Promise { @@ -146,6 +149,7 @@ export class HitDetail { /** A SnapDetail is generated from the result of [IModelDb.requestSnap]($backend) call. In addition to the HitDetail about the reason the element was *picked*, * it holds the *exact* point on the element from the snapping logic, plus additional information that varies with the type of element and snap mode. + * @public */ export class SnapDetail extends HitDetail { /** A sprite to show the user the type of snap performed */ @@ -284,7 +288,6 @@ export class SnapDetail extends HitDetail { super.draw(context); } - /** */ private static getSnapSpriteUrl(snapType: SnapMode): string { switch (snapType) { case SnapMode.Nearest: return "sprites/SnapPointOn.png"; @@ -299,6 +302,7 @@ export class SnapDetail extends HitDetail { } } +/** @public */ export class IntersectDetail extends SnapDetail { public constructor(from: SnapDetail, heat: SnapHeat = SnapHeat.None, snapPoint: XYZProps, public readonly otherPrimitive: CurvePrimitive, public readonly otherId: string) { super(from, SnapMode.Intersection, heat, snapPoint); @@ -330,9 +334,9 @@ export class IntersectDetail extends SnapDetail { } } -/** - * The result of a "locate" is a sorted list of objects that satisfied the search criteria (a HitList). Earlier hits in the list - * are somehow *better* than those later on. +/** The result of a "locate" is a sorted list of objects that satisfied the search criteria (a HitList). Earlier hits in the list + * are somehow *better* than those later on. + * @public */ export class HitList { public hits: T[] = []; @@ -341,8 +345,7 @@ export class HitList { public empty(): void { this.hits.length = 0; this.currHit = -1; } public resetCurrentHit(): void { this.currHit = -1; } - /** - * get a hit from a particular index into a HitList + /** Get a hit from a particular index into a HitList * return the requested hit from the HitList or undefined */ public getHit(hitNum: number): T | undefined { diff --git a/core/frontend/src/IModelApp.ts b/core/frontend/src/IModelApp.ts index d755155..a421da8 100644 --- a/core/frontend/src/IModelApp.ts +++ b/core/frontend/src/IModelApp.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ /** @module IModelApp */ -import { dispose } from "@bentley/bentleyjs-core"; +import { dispose, Guid, GuidString } from "@bentley/bentleyjs-core"; import { AccessToken, ConnectSettingsClient, IModelClient, IModelHubClient, SettingsAdmin } from "@bentley/imodeljs-clients"; import { FeatureGates, IModelError, IModelStatus } from "@bentley/imodeljs-common"; import { I18N, I18NOptions } from "@bentley/imodeljs-i18n"; @@ -26,14 +26,14 @@ import * as pluginTool from "./tools/PluginTool"; import * as viewTool from "./tools/ViewTool"; import * as measureTool from "./tools/MeasureTool"; -/** - * Creates an *Application* to show an iModel in a web browser. +/** Creates an *Application* to show an iModel in a web browser. * It connects the user interface with the iModel.js services. There can be only one IModelApp active in a session. * * Applications may customize the behavior of their application by subclassing this class and supplying different * implementations of the members. * * Before any interactive operations may be performed, [[IModelApp.startup]] must be called (typically on a subclass of IModelApp) + * @public */ export class IModelApp { /** @hidden */ @@ -65,6 +65,8 @@ export class IModelApp { public static settings: SettingsAdmin; /** The name of this application. */ public static applicationId: string; + /** A uniqueId for this session */ + public static sessionId: GuidString; /** @hidden */ public static readonly features = new FeatureGates(); /** The [[ToolRegistry]] for this session. */ @@ -89,8 +91,7 @@ export class IModelApp { /** @hidden This is to be refactored...for now it holds the AccessToken for the current session. Must be set by the application. */ public static accessToken?: AccessToken; - /** - * This method must be called before any iModel.js frontend services are used. Typically, an application will make a subclass of IModelApp + /** This method must be called before any iModel.js frontend services are used. Typically, an application will make a subclass of IModelApp * and call this method on that subclass. E.g: * ``` ts * MyApp extends IModelApp { @@ -107,7 +108,7 @@ export class IModelApp { throw new IModelError(IModelStatus.AlreadyLoaded, "startup may only be called once"); IModelApp._initialized = true; - + IModelApp.sessionId = Guid.createValue(); if (imodelClient !== undefined) this._imodelClient = imodelClient; @@ -156,8 +157,7 @@ export class IModelApp { IModelApp._initialized = false; } - /** - * Implement this method to register your app's tools, override implementation of managers, and initialize your app-specific members. + /** Implement this method to register your app's tools, override implementation of managers, and initialize your app-specific members. * @note The default tools will already be registered, so if you register tools with the same toolId, your tools will override the defaults. */ protected static onStartup(): void { } diff --git a/core/frontend/src/IModelConnection.ts b/core/frontend/src/IModelConnection.ts index 96291d2..ba18ffd 100644 --- a/core/frontend/src/IModelConnection.ts +++ b/core/frontend/src/IModelConnection.ts @@ -4,24 +4,27 @@ *--------------------------------------------------------------------------------------------*/ /** @module IModelConnection */ -import { ActivityLoggingContext, assert, BeEvent, BentleyStatus, Guid, Id64, Id64Arg, Id64Set, Id64String, Logger, OpenMode, TransientIdSequence } from "@bentley/bentleyjs-core"; +import { ActivityLoggingContext, assert, BeEvent, BentleyStatus, DbResult, Guid, Id64, Id64Arg, Id64Set, Id64String, Logger, OpenMode, TransientIdSequence } from "@bentley/bentleyjs-core"; +import { Angle, Point3d, Range3dProps, XYAndZ } from "@bentley/geometry-core"; import { AccessToken } from "@bentley/imodeljs-clients"; import { - AxisAlignedBox3d, CodeSpec, ElementProps, EntityQueryParams, FontMap, ImageSourceFormat, IModel, IModelError, IModelNotFoundResponse, - IModelReadRpcInterface, IModelStatus, IModelTileRpcInterface, IModelToken, IModelUnitTestRpcInterface, IModelVersion, IModelWriteRpcInterface, - ModelProps, ModelQueryParams, RpcNotFoundResponse, RpcOperation, RpcRequest, RpcRequestEvent, SnapRequestProps, SnapResponseProps, + AxisAlignedBox3d, Cartographic, CodeSpec, ElementProps, EntityQueryParams, FontMap, GeoCoordStatus, ImageSourceFormat, IModel, IModelError, + IModelNotFoundResponse, IModelReadRpcInterface, IModelStatus, IModelToken, IModelVersion, IModelWriteRpcInterface, kPagingDefaultOptions, + ModelProps, ModelQueryParams, PageOptions, RpcNotFoundResponse, RpcOperation, RpcRequest, RpcRequestEvent, SnapRequestProps, SnapResponseProps, StandaloneIModelRpcInterface, ThumbnailProps, TileTreeProps, ViewDefinitionProps, ViewQueryParams, WipRpcInterface, } from "@bentley/imodeljs-common"; import { EntityState } from "./EntityState"; +import { GeoServices } from "./GeoServices"; import { IModelApp } from "./IModelApp"; import { ModelState } from "./ModelState"; import { HilitedSet, SelectionSet } from "./SelectionSet"; -import { GeoServices } from "./GeoServices"; import { ViewState } from "./ViewState"; const loggingCategory = "imodeljs-frontend.IModelConnection"; -/** A connection to an iModel database hosted on the backend. */ +/** A connection to an iModel database hosted on the backend. + * @public + */ export class IModelConnection extends IModel { /** The [[OpenMode]] used for this IModelConnection. */ public readonly openMode: OpenMode; @@ -43,18 +46,17 @@ export class IModelConnection extends IModel { public readonly tiles: IModelConnection.Tiles; /** Generator for unique Ids of transient graphics for this IModelConnection. */ public readonly transientIds = new TransientIdSequence(); - /** A unique Id of this IModelConnection. */ - public readonly connectionId = Guid.createValue(); /** The Geographic location services available for this iModelConnection */ public readonly geoServices: GeoServices; + /** @hidden Whether it has already been determined that this iModelConnection does not have a map projection. */ + protected _noGcsDefined?: boolean; /** The maximum time (in milliseconds) to wait before timing out the request to open a connection to a new iModel */ - private static _connectionTimeout: number = 10 * 60 * 1000; + public static connectionTimeout: number = 10 * 60 * 1000; /** Check the [[openMode]] of this IModelConnection to see if it was opened read-only. */ public get isReadonly(): boolean { return this.openMode === OpenMode.Readonly; } - /** - * Event called immediately before an IModelConnection is closed. + /** Event called immediately before an IModelConnection is closed. * @note Be careful not to perform any asynchronous operations on the IModelConnection because it will close before they are processed. */ public static readonly onClose = new BeEvent<(_imodel: IModelConnection) => void>(); @@ -62,8 +64,7 @@ export class IModelConnection extends IModel { /** The font map for this IModelConnection. Only valid after calling #loadFontMap and waiting for the returned promise to be fulfilled. */ public fontMap?: FontMap; - /** - * Load the FontMap for this IModelConnection. + /** Load the FontMap for this IModelConnection. * @returns Returns a Promise that is fulfilled when the FontMap member of this IModelConnection is valid. */ public async loadFontMap(): Promise { @@ -77,8 +78,7 @@ export class IModelConnection extends IModel { public static registerClass(className: string, classType: typeof EntityState) { this._registry.set(className.toLowerCase(), classType); } private static lookupClass(className: string) { return this._registry.get(className.toLowerCase()); } - /** - * Find the first registered base class of the given EntityState className. This class will "handle" the State for the supplied className. + /** Find the first registered base class of the given EntityState className. This class will "handle" the State for the supplied className. * @param className The full name of the class of interest. * @param defaultClass If no base class of the className is registered, return this value. * @note this method is async since it may have to query the server to get the class hierarchy. @@ -135,11 +135,10 @@ export class IModelConnection extends IModel { } private static async callOpen(accessToken: AccessToken, iModelToken: IModelToken, openMode: OpenMode): Promise { - /* Try opening the iModel repeatedly accommodating any pending responses from the backend. - * Waits for an increasing amount of time (but within a range) before checking on the pending request again. - */ + // Try opening the iModel repeatedly accommodating any pending responses from the backend. + // Waits for an increasing amount of time (but within a range) before checking on the pending request again. const connectionRetryIntervalRange = { min: 100, max: 5000 }; // in milliseconds - let connectionRetryInterval = Math.min(connectionRetryIntervalRange.min, IModelConnection._connectionTimeout); + let connectionRetryInterval = Math.min(connectionRetryIntervalRange.min, IModelConnection.connectionTimeout); let openForReadOperation: RpcOperation | undefined; let openForWriteOperation: RpcOperation | undefined; @@ -169,12 +168,12 @@ export class IModelConnection extends IModel { Logger.logTrace(loggingCategory, "Received pending open notification in IModelConnection.open", () => ({ ...iModelToken, openMode })); const connectionTimeElapsed = Date.now() - startTime; - if (connectionTimeElapsed > IModelConnection._connectionTimeout) { - Logger.logError(loggingCategory, `Timed out opening connection in IModelConnection.open (took longer than ${IModelConnection._connectionTimeout} milliseconds)`, () => ({ ...iModelToken, openMode })); + if (connectionTimeElapsed > IModelConnection.connectionTimeout) { + Logger.logError(loggingCategory, `Timed out opening connection in IModelConnection.open (took longer than ${IModelConnection.connectionTimeout} milliseconds)`, () => ({ ...iModelToken, openMode })); throw new IModelError(BentleyStatus.ERROR, "Opening a connection was timed out"); // NEEDS_WORK: More specific error status } - connectionRetryInterval = Math.min(connectionRetryIntervalRange.max, connectionRetryInterval * 2, IModelConnection._connectionTimeout - connectionTimeElapsed); + connectionRetryInterval = Math.min(connectionRetryIntervalRange.max, connectionRetryInterval * 2, IModelConnection.connectionTimeout - connectionTimeElapsed); if (request.retryInterval !== connectionRetryInterval) { request.retryInterval = connectionRetryInterval; Logger.logTrace(loggingCategory, `Adjusted open connection retry interval to ${request.retryInterval} milliseconds in IModelConnection.open`, () => ({ ...iModelToken, openMode })); @@ -194,7 +193,7 @@ export class IModelConnection extends IModel { IModelApp.accessToken = accessToken; // ###TODO to be refactored later... Logger.logTrace(loggingCategory, "Completed open request in IModelConnection.open", () => ({ ...iModelToken, openMode })); - return openResponse; + return openResponse!; } private _reopenConnectionHandler = async (request: RpcRequest, response: IModelNotFoundResponse, resubmit: () => void, reject: (reason: any) => void) => { @@ -220,7 +219,10 @@ export class IModelConnection extends IModel { resubmit(); } - /** Close this IModelConnection */ + /** Close this IModelConnection + * In the case of ReadWrite connections ensure all changes are pushed to the iModelHub before making this call - + * any un-pushed changes are lost after the close. + */ public async close(accessToken: AccessToken): Promise { if (!this.iModelToken) return; @@ -234,8 +236,7 @@ export class IModelConnection extends IModel { } } - /** - * Open an IModelConnection to a standalone iModel (not managed by iModelHub) from a file name that is resolved by the backend. + /** Open an IModelConnection to a standalone iModel (not managed by iModelHub) from a file name that is resolved by the backend. * This method is intended for desktop or mobile applications and should not be used for web applications. */ public static async openStandalone(fileName: string, openMode = OpenMode.Readonly): Promise { @@ -258,40 +259,101 @@ export class IModelConnection extends IModel { } } - /** Load a file from the native asset directory of the backend. - * @param assetName Name of the asset file, with path relative to the *Assets* directory + /** Compute number of rows that would be returned by the ECSQL. + * + * See also: + * - [ECSQL Overview]($docs/learning/backend/ExecutingECSQL) + * - [Code Examples]($docs/learning/backend/ECSQLCodeExamples) + * + * @param ecsql The ECSQL statement to execute + * @param bindings The values to bind to the parameters (if the ECSQL has any). + * Pass an *array* of values if the parameters are *positional*. + * Pass an *object of the values keyed on the parameter name* for *named parameters*. + * The values in either the array or object must match the respective types of the parameters. + * See "[iModel.js Types used in ECSQL Parameter Bindings]($docs/learning/ECSQLParameterTypes)" for details. + * @returns Return row count. + * @throws [IModelError]($common) If the statement is invalid */ - public async loadNativeAsset(assetName: string): Promise { return IModelReadRpcInterface.getClient().loadNativeAsset(this.iModelToken, assetName); } + public async queryRowCount(ecsql: string, bindings?: any[] | object): Promise { + Logger.logTrace(loggingCategory, "IModelConnection.queryRowCount", () => ({ iModelId: this.iModelToken.iModelId, ecsql, bindings })); + return IModelReadRpcInterface.getClient().queryRowCount(this.iModelToken, ecsql, bindings); + } - /** - * Execute an ECSQL query against the iModel. + /** Execute a query agaisnt this ECDb * The result of the query is returned as an array of JavaScript objects where every array element represents an * [ECSQL row]($docs/learning/ECSQLRowFormat). * * See also: - * - [ECSQL Overview]($docs/learning/frontend/ExecutingECSQL) - * - [Code Examples]($docs/learning/frontend/ECSQLCodeExamples) + * - [ECSQL Overview]($docs/learning/backend/ExecutingECSQL) + * - [Code Examples]($docs/learning/backend/ECSQLCodeExamples) * - * @param ecsql The ECSQL to execute + * @param ecsql The ECSQL statement to execute * @param bindings The values to bind to the parameters (if the ECSQL has any). - * The section "[iModel.js Types used in ECSQL Parameter Bindings]($docs/learning/ECSQLParameterTypes)" describes the - * iModel.js types to be used for the different ECSQL parameter types. * Pass an *array* of values if the parameters are *positional*. * Pass an *object of the values keyed on the parameter name* for *named parameters*. * The values in either the array or object must match the respective types of the parameters. - * @returns Returns the query result as an array of the resulting rows or an empty array if the query has returned no rows - * @throws [IModelError]($common) if the ECSQL is invalid + * See "[iModel.js Types used in ECSQL Parameter Bindings]($docs/learning/ECSQLParameterTypes)" for details. + * @param options Provide paging option. This allow set page size and page number from which to grab rows from. + * @returns Returns the query result as an array of the resulting rows or an empty array if the query has returned no rows. + * See [ECSQL row format]($docs/learning/ECSQLRowFormat) for details about the format of the returned rows. + * @throws [IModelError]($common) If the statement is invalid */ - public async executeQuery(ecsql: string, bindings?: any[] | object): Promise { - Logger.logTrace(loggingCategory, "IModelConnection.executeQuery", () => ({ iModelId: this.iModelToken.iModelId, ecsql, bindings })); - return IModelReadRpcInterface.getClient().executeQuery(this.iModelToken, ecsql, bindings); + public async queryPage(ecsql: string, bindings?: any[] | object, options?: PageOptions): Promise { + Logger.logTrace(loggingCategory, "IModelConnection.queryPage", () => ({ iModelId: this.iModelToken.iModelId, ecsql, options, bindings })); + return IModelReadRpcInterface.getClient().queryPage(this.iModelToken, ecsql, bindings, options); + } + + /** Execute a pagable query. + * The result of the query is async iterator over the rows. The iterator will get next page automatically once rows in current page has been read. + * [ECSQL row]($docs/learning/ECSQLRowFormat). + * + * See also: + * - [ECSQL Overview]($docs/learning/backend/ExecutingECSQL) + * - [Code Examples]($docs/learning/backend/ECSQLCodeExamples) + * + * @param ecsql The ECSQL statement to execute + * @param bindings The values to bind to the parameters (if the ECSQL has any). + * Pass an *array* of values if the parameters are *positional*. + * Pass an *object of the values keyed on the parameter name* for *named parameters*. + * The values in either the array or object must match the respective types of the parameters. + * See "[iModel.js Types used in ECSQL Parameter Bindings]($docs/learning/ECSQLParameterTypes)" for details. + * @param options Provide paging option. Which allow page to start iterating from and also size of the page to use. + * @returns Returns the query result as an array of the resulting rows or an empty array if the query has returned no rows. + * See [ECSQL row format]($docs/learning/ECSQLRowFormat) for details about the format of the returned rows. + * @throws [IModelError]($common) If the statement is invalid + */ + public async * query(ecsql: string, bindings?: any[] | object, options?: PageOptions): AsyncIterableIterator { + if (!options) { + options = kPagingDefaultOptions; + } + + let pageNo = options.start || kPagingDefaultOptions.start!; + const pageSize = options.size || kPagingDefaultOptions.size!; + + // verify if correct options was provided. + if (pageNo < 0) + throw new IModelError(DbResult.BE_SQLITE_ERROR, "options.start must be positive integer"); + + if (pageSize < 0) + throw new IModelError(DbResult.BE_SQLITE_ERROR, "options.size must be positive integer starting from 1"); + + do { + const page = await this.queryPage(ecsql, bindings, { start: pageNo, size: pageSize }); + if (page.length > 0) { + for (const row of page) { + yield row; + } + pageNo = pageNo + 1; + } else { + pageNo = -1; + } + } while (pageNo >= 0); } /** Query for a set of element ids that satisfy the supplied query params */ public async queryEntityIds(params: EntityQueryParams): Promise { return IModelReadRpcInterface.getClient().queryEntityIds(this.iModelToken, params); } - /** - * Update the project extents of this iModel. + /** Update the project extents of this iModel. * @param newExtents The new project extents as an AxisAlignedBox3d * @throws [[IModelError]] if the IModelConnection is read-only or there is a problem updating the extents. */ @@ -302,8 +364,7 @@ export class IModelConnection extends IModel { return IModelWriteRpcInterface.getClient().updateProjectExtents(this.iModelToken, newExtents); } - /** - * Commit pending changes to this iModel + /** Commit pending changes to this iModel * @param description Optional description of the changes * @throws [[IModelError]] if the IModelConnection is read-only or there is a problem saving changes. */ @@ -314,16 +375,14 @@ export class IModelConnection extends IModel { return IModelWriteRpcInterface.getClient().saveChanges(this.iModelToken, description); } - /** - * WIP - Determines whether the *Change Cache file* is attached to this iModel or not. + /** WIP - Determines whether the *Change Cache file* is attached to this iModel or not. * See also [Change Summary Overview]($docs/learning/ChangeSummaries) * @returns Returns true if the *Change Cache file* is attached to the iModel. false otherwise * @hidden */ public async changeCacheAttached(): Promise { return WipRpcInterface.getClient().isChangeCacheAttached(this.iModelToken); } - /** - * WIP - Attaches the *Change Cache file* to this iModel if it hasn't been attached yet. + /** WIP - Attaches the *Change Cache file* to this iModel if it hasn't been attached yet. * A new *Change Cache file* will be created for the iModel if it hasn't existed before. * See also [Change Summary Overview]($docs/learning/ChangeSummaries) * @throws [IModelError]($common) if a Change Cache file has already been attached before. @@ -331,8 +390,7 @@ export class IModelConnection extends IModel { */ public async attachChangeCache(): Promise { return WipRpcInterface.getClient().attachChangeCache(this.iModelToken); } - /** - * WIP - Detaches the *Change Cache file* to this iModel if it had been attached before. + /** WIP - Detaches the *Change Cache file* to this iModel if it had been attached before. * > You do not have to check whether a Change Cache file had been attached before. The * > method does not do anything, if no Change Cache is attached. * See also [Change Summary Overview]($docs/learning/ChangeSummaries) @@ -340,21 +398,108 @@ export class IModelConnection extends IModel { */ public async detachChangeCache(): Promise { return WipRpcInterface.getClient().detachChangeCache(this.iModelToken); } - /** - * Execute a test by name - * @param testName The name of the test to execute - * @param params A JSON string containing all parameters the test requires - * @hidden - */ - public async executeTest(testName: string, params: any): Promise { return IModelUnitTestRpcInterface.getClient().executeTest(this.iModelToken, testName, params); } - /** Request a snap from the backend. */ - public async requestSnap(props: SnapRequestProps): Promise { return IModelReadRpcInterface.getClient().requestSnap(this.iModelToken, this.connectionId, props); } + public async requestSnap(props: SnapRequestProps): Promise { return IModelReadRpcInterface.getClient().requestSnap(this.iModelToken, IModelApp.sessionId, props); } /** Request a tooltip from the backend. */ public async getToolTipMessage(id: string): Promise { return IModelReadRpcInterface.getClient().getToolTipMessage(this.iModelToken, id); } + + /** Convert a point in this iModel's Spatial coordinates to a [[Cartographic]] using the Geographic location services for this IModelConnection. + * @param spatial A point in the iModel's spatial coordinates + * @param result If defined, use this for output + * @returns A Cartographic location + * @throws IModelError if [[isGeoLocated]] is false or point could not be converted. + */ + public async spatialToCartographicFromGcs(spatial: XYAndZ, result?: Cartographic): Promise { + if (undefined === this._noGcsDefined && !this.isGeoLocated) + this._noGcsDefined = true; + + if (this._noGcsDefined) + throw new IModelError(IModelStatus.NoGeoLocation, "iModel does not have a Geographic Coordinate system. It may be Geolocated with an EcefTransform"); + + const geoConverter = this.geoServices.getConverter(); + const coordResponse = await geoConverter.getGeoCoordinatesFromIModelCoordinates([spatial]); + + if (this._noGcsDefined = (1 !== coordResponse.geoCoords.length || GeoCoordStatus.NoGCSDefined === coordResponse.geoCoords[0].s)) + throw new IModelError(IModelStatus.NoGeoLocation, "iModel does not have a Geographic Coordinate system. It may be Geolocated with an EcefTransform"); + + if (GeoCoordStatus.Success !== coordResponse.geoCoords[0].s) + throw new IModelError(IModelStatus.BadRequest, "Error converting spatial to cartographic"); + + const longLatHeight = Point3d.fromJSON(coordResponse.geoCoords[0].p); // x is longitude in degrees, y is latitude in degrees, z is height in meters... + return Cartographic.fromDegrees(longLatHeight.x, longLatHeight.y, longLatHeight.z, result); + } + + /** Convert a point in this iModel's Spatial coordinates to a [[Cartographic]] using the Geographic location services for this IModelConnection or [[IModel.ecefLocation]]. + * @param spatial A point in the iModel's spatial coordinates + * @param result If defined, use this for output + * @returns A Cartographic location + * @throws IModelError if [[isGeoLocated]] is false or point could not be converted. + * @see [[spatialToCartographicFromGcs]] + * @see [[spatialToCartographicFromEcef]] + */ + public async spatialToCartographic(spatial: XYAndZ, result?: Cartographic): Promise { + if (undefined === this._noGcsDefined) { + try { + return await this.spatialToCartographicFromGcs(spatial, result); + } catch (error) { + if (!this._noGcsDefined) + throw error; + } + } + return (this._noGcsDefined ? this.spatialToCartographicFromEcef(spatial, result) : this.spatialToCartographicFromGcs(spatial, result)); + } + + /** Convert a [[Cartographic]] to a point in this iModel's Spatial coordinates using the Geographic location services for this IModelConnection. + * @param cartographic A cartographic location + * @param result If defined, use this for output + * @returns A point in this iModel's spatial coordinates + * @throws IModelError if [[isGeoLocated]] is false or cartographic location could not be converted. + */ + public async cartographicToSpatialFromGcs(cartographic: Cartographic, result?: Point3d): Promise { + if (undefined === this._noGcsDefined && !this.isGeoLocated) + this._noGcsDefined = true; + + if (this._noGcsDefined) + throw new IModelError(IModelStatus.NoGeoLocation, "iModel does not have a Geographic Coordinate system. It may be Geolocated with an EcefTransform"); + + const geoConverter = this.geoServices.getConverter(); + const geoCoord = Point3d.create(Angle.radiansToDegrees(cartographic.longitude), Angle.radiansToDegrees(cartographic.latitude), cartographic.height); // x is longitude in degrees, y is latitude in degrees, z is height in meters... + const coordResponse = await geoConverter.getIModelCoordinatesFromGeoCoordinates([geoCoord]); + + if (this._noGcsDefined = (1 !== coordResponse.iModelCoords.length || GeoCoordStatus.NoGCSDefined === coordResponse.iModelCoords[0].s)) + throw new IModelError(IModelStatus.NoGeoLocation, "iModel does not have a Geographic Coordinate system. It may be Geolocated with an EcefTransform"); + + if (GeoCoordStatus.Success !== coordResponse.iModelCoords[0].s) + throw new IModelError(IModelStatus.BadRequest, "Error converting cartographic to spatial"); + + result = result ? result : Point3d.createZero(); + result.setFromJSON(coordResponse.iModelCoords[0].p); + return result; + } + + /** Convert a [[Cartographic]] to a point in this iModel's Spatial coordinates using the Geographic location services for this IModelConnection or [[IModel.ecefLocation]]. + * @param cartographic A cartographic location + * @param result If defined, use this for output + * @returns A point in this iModel's spatial coordinates + * @throws IModelError if [[isGeoLocated]] is false or cartographic location could not be converted. + * @see [[cartographicToSpatialFromGcs]] + * @see [[cartographicToSpatialFromEcef]] + */ + public async cartographicToSpatial(cartographic: Cartographic, result?: Point3d): Promise { + if (undefined === this._noGcsDefined) { + try { + return await this.cartographicToSpatialFromGcs(cartographic, result); + } catch (error) { + if (!this._noGcsDefined) + throw error; + } + } + return (this._noGcsDefined ? this.cartographicToSpatialFromEcef(cartographic, result) : this.cartographicToSpatialFromGcs(cartographic, result)); + } } +/** @public */ export namespace IModelConnection { /** The id/name/class of a ViewDefinition. Returned by [[IModelConnection.Views.getViewList]] */ @@ -407,6 +552,11 @@ export namespace IModelConnection { } catch (err) { } // ignore error, we had nothing to do. } + /** Query for a set of model ranges by ModelIds. */ + public async queryModelRanges(modelIds: Id64Arg): Promise { + return IModelReadRpcInterface.getClient().queryModelRanges(this._iModel.iModelToken, Id64.toIdSet(modelIds)); + } + /** Query for a set of ModelProps of the specified ModelQueryParams. */ public async queryProps(queryParams: ModelQueryParams): Promise { const params: ModelQueryParams = Object.assign({}, queryParams); // make a copy @@ -451,11 +601,6 @@ export namespace IModelConnection { public async queryProps(params: EntityQueryParams): Promise { return IModelReadRpcInterface.getClient().queryElementProps(this._iModel.iModelToken, params); } - - /** Ask the backend to format (for presentation) the specified list of element ids. */ - public async formatElements(elementIds: Id64Arg): Promise { - return IModelReadRpcInterface.getClient().formatElements(this._iModel.iModelToken, Id64.toIdSet(elementIds)); - } } /** The collection of [[CodeSpec]] entities for an [[IModelConnection]]. */ @@ -514,8 +659,7 @@ export namespace IModelConnection { /** @hidden */ constructor(private _iModel: IModelConnection) { } - /** - * Query for an array of ViewDefinitionProps + /** Query for an array of ViewDefinitionProps * @param queryParams Query parameters specifying the views to return */ public async queryProps(queryParams: ViewQueryParams): Promise { @@ -531,8 +675,7 @@ export namespace IModelConnection { return viewProps as ViewDefinitionProps[]; } - /** - * Get an array of the ViewSpecs for all views in this IModel that satisfy a ViewQueryParams. + /** Get an array of the ViewSpecs for all views in this IModel that satisfy a ViewQueryParams. * * This is typically used to create a list for UI. * @@ -549,8 +692,7 @@ export namespace IModelConnection { return views; } - /** - * Query the ID of the default view associated with this iModel. Applications can choose to use this as the default view to which to open a viewport upon startup, or the initial selection + /** Query the ID of the default view associated with this iModel. Applications can choose to use this as the default view to which to open a viewport upon startup, or the initial selection * within a view selection dialog, or similar purposes. * @returns the ID of the default view, or an invalid ID if no default view is defined. */ @@ -610,7 +752,7 @@ export namespace IModelConnection { /** @hidden */ constructor(iModel: IModelConnection) { this._iModel = iModel; } - public async getTileTreeProps(id: string): Promise { return IModelTileRpcInterface.getClient().getTileTreeProps(this._iModel.iModelToken, id); } - public async getTileContent(treeId: string, contentId: string): Promise { return IModelTileRpcInterface.getClient().getTileContent(this._iModel.iModelToken, treeId, contentId); } + public async getTileTreeProps(id: string): Promise { return IModelApp.tileAdmin.requestTileTreeProps(this._iModel, id); } + public async getTileContent(treeId: string, contentId: string): Promise { return IModelApp.tileAdmin.requestTileContent(this._iModel, treeId, contentId); } } } diff --git a/core/frontend/src/Marker.ts b/core/frontend/src/Marker.ts index a4fd5e5..10aeed2 100644 --- a/core/frontend/src/Marker.ts +++ b/core/frontend/src/Marker.ts @@ -19,10 +19,10 @@ export type MarkerFillStyle = string | CanvasGradient | CanvasPattern; export type MarkerTextAlign = "left" | "right" | "center" | "start" | "end"; export type MarkerTextBaseline = "top" | "hanging" | "middle" | "alphabetic" | "ideographic" | "bottom"; -/** - * A Marker is a [[CanvasDecoration]], whose position follows a fixed location in world space. +/** A Marker is a [[CanvasDecoration]], whose position follows a fixed location in world space. * Markers draw on top of all scene graphics, and show visual cues about locations of interest. * @see [Markers]($docs/learning/frontend/Markers) + * @public */ export class Marker implements CanvasDecoration { protected _scaleFactor?: Point2d; @@ -108,8 +108,7 @@ export class Marker implements CanvasDecoration { this.position = new Point3d(); } - /** - * Make a new Marker at the same position and size as this Marker. + /** Make a new Marker at the same position and size as this Marker. * Thew new Marker will share the world location and size objects, but will be otherwise blank. */ public static makeFrom(other: Marker, ...args: any[]): T { @@ -122,8 +121,7 @@ export class Marker implements CanvasDecoration { return out; } - /** - * When a Marker is displayed in its hilited state, this method is called first. If it returns true, no further action is taken. + /** When a Marker is displayed in its hilited state, this method is called first. If it returns true, no further action is taken. * Otherwise the Marker's normal drawing operations are also called. By default, this method adds a shadowBlur effect and increases * the size of the Marker by 25%. * @return true to stop drawing this Marker @@ -228,9 +226,9 @@ export class Marker implements CanvasDecoration { } } -/** - * A cluster of one or more Markers that overlap one another in the view. The cluster's screen position is taken from its first entry. +/** A cluster of one or more Markers that overlap one another in the view. The cluster's screen position is taken from its first entry. * Clusters also have a Marker themselves, that represents the whole group. The cluster marker isn't created until all entries have been added. + * @public */ export class Cluster { public readonly rect: ViewRect; @@ -244,6 +242,7 @@ export class Cluster { /** A *set* of Markers that are logically related, such that they *cluster* when they overlap. In that case, a *cluster marker* * is drawn instead of the overlapping Markers. + * @public */ export abstract class MarkerSet { /** @hidden */ @@ -256,16 +255,14 @@ export abstract class MarkerSet { /** The set of Markers in this MarkerSet. Add your [[Marker]]s into this. */ public readonly markers = new Set(); - /** - * Implement this method to create a new Marker that is shown as a *stand-in* for a Cluster of Markers that overlap one another. + /** Implement this method to create a new Marker that is shown as a *stand-in* for a Cluster of Markers that overlap one another. * @param cluster The [[Cluster]] that the new Marker will represent. * @returns The Marker that will be displayed to represent the Cluster. * @note You must create a new Marker each time this method is called. */ protected abstract getClusterMarker(cluster: Cluster): Marker; - /** - * This method should be called from [[Decorator.decorate]]. It will add this this MarkerSet to the supplied DecorateContext. + /** This method should be called from [[Decorator.decorate]]. It will add this this MarkerSet to the supplied DecorateContext. * This method implements the logic that turns overlapping Markers into a Cluster. * @param context The DecorateContext for the Markers */ diff --git a/core/frontend/src/ModelSelectorState.ts b/core/frontend/src/ModelSelectorState.ts index 2305fcf..801f4c1 100644 --- a/core/frontend/src/ModelSelectorState.ts +++ b/core/frontend/src/ModelSelectorState.ts @@ -11,6 +11,7 @@ import { ModelSelectorProps } from "@bentley/imodeljs-common"; /** The state of a [ModelSelector]($backend). It holds a set of ids of GeometricModels for a [[SpatialViewState]]. * It defines the set of [[ModelState]]s drawn within the view as a set of IDs. + * @public */ export class ModelSelectorState extends ElementState { /** The set of ModelIds of this ModelSelectorState */ diff --git a/core/frontend/src/ModelState.ts b/core/frontend/src/ModelState.ts index 3ff5164..8976e60 100644 --- a/core/frontend/src/ModelState.ts +++ b/core/frontend/src/ModelState.ts @@ -4,16 +4,18 @@ *--------------------------------------------------------------------------------------------*/ /** @module ModelState */ -import { Id64String, Id64, JsonUtils, dispose } from "@bentley/bentleyjs-core"; +import { dispose, Id64, Id64String, JsonUtils } from "@bentley/bentleyjs-core"; +import { Point2d, Range3d } from "@bentley/geometry-core"; +import { AxisAlignedBox3d, BatchType, GeometricModel2dProps, ModelProps, RelatedElement, ServerTimeoutError, TileTreeProps } from "@bentley/imodeljs-common"; import { EntityState } from "./EntityState"; -import { Point2d } from "@bentley/geometry-core"; -import { ModelProps, GeometricModel2dProps, AxisAlignedBox3d, RelatedElement, TileTreeProps, BatchType } from "@bentley/imodeljs-common"; -import { IModelConnection } from "./IModelConnection"; import { IModelApp } from "./IModelApp"; -import { TileTree, TileTreeState, IModelTileLoader } from "./tile/TileTree"; +import { IModelConnection } from "./IModelConnection"; import { RealityModelTileTree } from "./tile/RealityModelTileTree"; +import { IModelTileLoader, TileTree, TileTreeState } from "./tile/TileTree"; -/** Represents the front-end state of a [Model]($backend). */ +/** Represents the front-end state of a [Model]($backend). + * @public + */ export class ModelState extends EntityState implements ModelProps { public readonly modeledElement: RelatedElement; public readonly name: string; @@ -42,7 +44,7 @@ export class ModelState extends EntityState implements ModelProps { val.isTemplate = this.isTemplate; return val; } - public getExtents(): AxisAlignedBox3d { return new AxisAlignedBox3d(); } // NEEDS_WORK + public getExtents(): AxisAlignedBox3d { return new Range3d(); } // NEEDS_WORK /** Determine whether this is a GeometricModel */ public get isGeometricModel(): boolean { return false; } @@ -61,12 +63,16 @@ export class ModelState extends EntityState implements ModelProps { export interface TileTreeModelState { readonly tileTree: TileTree | undefined; readonly loadStatus: TileTree.LoadStatus; - loadTileTree(asClassifier?: boolean, classifierExpansion?: number): TileTree.LoadStatus; + readonly treeModelId: Id64String; // Model Id, or transient Id if not a model (context reality model) + loadTileTree(edgesRequired: boolean, animationId?: Id64String, asClassifier?: boolean, classifierExpansion?: number): TileTree.LoadStatus; } + /** Represents the front-end state of a [GeometricModel]($backend). * The contents of a GeometricModelState can be rendered inside a [[Viewport]]. + * @public */ export abstract class GeometricModelState extends ModelState implements TileTreeModelState { + private _modelRange?: Range3d; /** @hidden */ protected _tileTreeState: TileTreeState = new TileTreeState(this.iModel, !this.is2d, this.id); /** @hidden */ @@ -89,16 +95,21 @@ export abstract class GeometricModelState extends ModelState implements TileTree /** @hidden */ public get isGeometricModel(): boolean { return true; } /** @hidden */ - public getOrLoadTileTree(): TileTree | undefined { + public get treeModelId(): Id64String { return this.id; } + /** @hidden */ + public getOrLoadTileTree(edgesRequired: boolean): TileTree | undefined { if (undefined === this.tileTree) - this.loadTileTree(); + this.loadTileTree(edgesRequired); return this.tileTree; } /** @hidden */ - public loadTileTree(asClassifier?: boolean, classifierExpansion?: number): TileTree.LoadStatus { + public loadTileTree(edgesRequired: boolean, animationId?: Id64String, asClassifier?: boolean, classifierExpansion?: number): TileTree.LoadStatus { const tileTreeState = asClassifier ? this._classifierTileTreeState : this._tileTreeState; + if (tileTreeState.edgesOmitted && edgesRequired) + tileTreeState.clearTileTree(); + if (TileTree.LoadStatus.NotLoaded !== tileTreeState.loadStatus) return tileTreeState.loadStatus; @@ -109,17 +120,24 @@ export abstract class GeometricModelState extends ModelState implements TileTree return tileTreeState.loadStatus; } - return this.loadIModelTileTree(tileTreeState, asClassifier, classifierExpansion); + return this.loadIModelTileTree(tileTreeState, edgesRequired, animationId, asClassifier, classifierExpansion); } - private loadIModelTileTree(tileTreeState: TileTreeState, asClassifier?: boolean, classifierExpansion?: number): TileTree.LoadStatus { - const id = asClassifier ? ("C:" + classifierExpansion as string + "_" + this.id) : this.id; + private loadIModelTileTree(tileTreeState: TileTreeState, edgesRequired: boolean, animationId?: Id64String, asClassifier?: boolean, classifierExpansion?: number): TileTree.LoadStatus { + const id = (asClassifier ? ("C:" + classifierExpansion as string + "_") : "") + (animationId ? ("A:" + animationId + "_") : "") + this.id; this.iModel.tiles.getTileTreeProps(id).then((result: TileTreeProps) => { - tileTreeState.setTileTree(result, new IModelTileLoader(this.iModel, asClassifier ? BatchType.Classifier : BatchType.Primary)); + tileTreeState.setTileTree(result, new IModelTileLoader(this.iModel, asClassifier ? BatchType.Classifier : BatchType.Primary, edgesRequired)); + this._tileTreeState.edgesOmitted = !edgesRequired; IModelApp.viewManager.onNewTilesReady(); }).catch((_err) => { - this._tileTreeState.loadStatus = TileTree.LoadStatus.NotFound; // on separate line because stupid chrome debugger. + // Retry in case of timeout; otherwise fail. + if (_err instanceof ServerTimeoutError) + this._tileTreeState.loadStatus = TileTree.LoadStatus.NotLoaded; + else + this._tileTreeState.loadStatus = TileTree.LoadStatus.NotFound; + + IModelApp.viewManager.onNewTilesReady(); }); return tileTreeState.loadStatus; @@ -130,9 +148,20 @@ export abstract class GeometricModelState extends ModelState implements TileTree dispose(this._tileTreeState.tileTree); // we do not track if we are disposed...catch this at the tiletree level super.onIModelConnectionClose(); } + + /** Query for the union of the ranges of all the elements in this GeometricModel. */ + public async queryModelRange(): Promise { + if (undefined === this._modelRange) { + const ranges = await this.iModel.models.queryModelRanges(this.id); + this._modelRange = Range3d.fromJSON(ranges[0]); + } + return this._modelRange!; + } } -/** Represents the front-end state of a [GeometricModel2d]($backend). */ +/** Represents the front-end state of a [GeometricModel2d]($backend). + * @public + */ export class GeometricModel2dState extends GeometricModelState implements GeometricModel2dProps { public readonly globalOrigin: Point2d; constructor(props: GeometricModel2dProps, iModel: IModelConnection) { @@ -152,7 +181,9 @@ export class GeometricModel2dState extends GeometricModelState implements Geomet } } -/** Represents the front-end state of a [GeometricModel3d]($backend). */ +/** Represents the front-end state of a [GeometricModel3d]($backend). + * @public + */ export class GeometricModel3dState extends GeometricModelState { /** Returns true. */ public get is3d(): boolean { return true; } @@ -160,14 +191,22 @@ export class GeometricModel3dState extends GeometricModelState { public get asGeometricModel3d(): GeometricModel3dState { return this; } } -/** Represents the front-end state of a [SheetModel]($backend). */ +/** Represents the front-end state of a [SheetModel]($backend). + * @public + */ export class SheetModelState extends GeometricModel2dState { } -/** Represents the front-end state of a [SpatialModel]($backend). */ +/** Represents the front-end state of a [SpatialModel]($backend). + * @public + */ export class SpatialModelState extends GeometricModel3dState { } -/** Represents the front-end state of a [DrawingModel]($backend). */ +/** Represents the front-end state of a [DrawingModel]($backend). + * @public + */ export class DrawingModelState extends GeometricModel2dState { } -/** Represents the front-end state of a [SectionDrawingModel]($backend). */ +/** Represents the front-end state of a [SectionDrawingModel]($backend). + * @public + */ export class SectionDrawingModelState extends DrawingModelState { } diff --git a/core/frontend/src/NotificationManager.ts b/core/frontend/src/NotificationManager.ts index 7cd57a5..3a7e711 100644 --- a/core/frontend/src/NotificationManager.ts +++ b/core/frontend/src/NotificationManager.ts @@ -192,7 +192,7 @@ export class NotificationManager { */ public endActivityMessage(_reason: ActivityMessageEndReason) { return true; } - /** Return true if _showTooltip has an implemention and will display a tooltip. */ + /** Return true if _showTooltip has an implementation and will display a tooltip. */ public get isToolTipSupported(): boolean { return false; } /** Return true if the tooltip is currently open. */ diff --git a/core/frontend/src/Plugin.ts b/core/frontend/src/Plugin.ts new file mode 100644 index 0000000..8dd2bc1 --- /dev/null +++ b/core/frontend/src/Plugin.ts @@ -0,0 +1,202 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module Plugins */ +import * as semver from "semver"; +import { IModelApp } from "./IModelApp"; +import { NotifyMessageDetails, OutputMessageAlert, OutputMessagePriority, OutputMessageType } from "./NotificationManager"; + +/** + * Base Plugin class for writing a demand-loaded module. + * @see [[PluginAdmin]] for a description of how Plugins are loaded. + * @see [Plugins]($docs/learning/frontend/plugins.md) + */ +export abstract class Plugin { + /** + * Constructor for base Plugin class + * @param name - the name of the plugin. When you use the buildIModelJsModule build script, this argument is filled in as the PLUGIN_NAME constant by webpack + * @param versionsRequired - the versions of iModel.js system modules that this Plugin requires. When you use the buildIModelJsModule build script, this argument + * is filled in as the IMODELJS_VERSIONS_REQUIRED constant by webpack. + * @note Typically, a Plugin subclass is instantiated and registered with top-level JavaScript statements like these: + * ```ts + * const myPlugin = new MyPlugin(PLUGIN_NAME, IMODELJS_VERSIONS_REQUIRED); + * PluginAdmin.register(myPlugin); + * ``` + */ + public constructor(public name: string, public versionsRequired: string) { + } + + /** + * Method called when the Plugin is first loaded. + * @param _args arguments that were passed to PluginAdmin.loadPlugin. The first argument is the plugin name. + */ + public onLoad(_args: string[]): void { + } + + /** + * Method called immediately following the call to onLoad when the Plugin is first loaded, and also once for + * each additional call to PluginAdmin.loadPlugin for the same Plugin. + * @param _args arguments that were passed to PluginAdmin.loadPlugin. The first argument is the plugin name. + */ + public abstract onExecute(_args: string[]): void; +} + +/** + * Controls loading of Plugins and calls methods on newly loaded or reloaded Plugins + */ +export class PluginAdmin { + private static _loadedPlugins: Map> = new Map>(); + private static _registeredPlugins: Map = new Map(); + + // returns an array of strings with version mismatch errors, or undefined if the versions of all modules are usable. + private static checkIModelJsVersions(versionsRequired: string): string[] | undefined { + // make sure we're in a browser-like environment + if ((typeof window === "undefined") || !window) { + return [IModelApp.i18n.translate("iModelJs:PluginErrors.FrontEndOnly")]; + } + const versionsLoaded: Map = (window as any).iModelJsVersions; + if (!versionsLoaded) { + return [IModelApp.i18n.translate("iModelJs:PluginErrors.NoVersionsLoaded")]; + } + + // make sure the versionsRequired string isn't empty. + if (!versionsRequired || (0 === versionsRequired.length)) { + return [IModelApp.i18n.translate("iModelJs:PluginErrors.WebpackedIncorrectly")]; + } + + // make sure versionsRequired is a JSON string. + const errorMessages: string[] = []; + try { + const versionsRequiredObject: any = JSON.parse(versionsRequired); + for (const moduleName of Object.getOwnPropertyNames(versionsRequiredObject)) { + // bwc doesn't set its version, so we have to ignore it for now. + if (moduleName === "bwc") { + continue; + } + const versionRequired: string = versionsRequiredObject[moduleName]; + if (!versionRequired || "string" !== typeof (versionRequired)) { + errorMessages.push(IModelApp.i18n.translate("iModelJs:PluginErrors.NoVersionSpecified", { moduleName })); + } else { + const versionLoaded = versionsLoaded.get(moduleName); + if (!versionLoaded) { + errorMessages.push(IModelApp.i18n.translate("iModelJs:PluginErrors.ModuleNotLoaded", { moduleName })); + } else { + // check version required vs. version loaded. + if (!semver.satisfies(versionLoaded, versionRequired)) { + errorMessages.push(IModelApp.i18n.translate("iModelJs:PluginErrors.VersionMismatch", { versionLoaded, moduleName, versionRequired })); + } + } + } + } + } catch (err) { + return [IModelApp.i18n.translate("iModelJs:PluginErrors.WebpackedIncorrectly")]; + } + return (errorMessages.length > 0) ? errorMessages : undefined; + } + + private static getPluginName(packageName: string) { + if (packageName.endsWith(".js")) + return packageName.substr(0, packageName.length - 3); + return packageName; + } + + /** + * Loads a Plugin + * @param packageName the name of the JavaScript file to be loaded from the web server. + * @param args arguments that will be passed to the Plugin.onLoaded and Plugin.onExecute methods. If the first argument is not the plugin name, the plugin name will be prepended to the args array. + */ + public static async loadPlugin(packageName: string, args?: string[]): Promise { + // see if it is already loaded. + const pluginName: string = PluginAdmin.getPluginName(packageName); + + // make sure there's an args and make sure the first element is the plugin name. + if (!args) { + args = [pluginName]; + } else { + if ((args.length < 1) || (args[0] !== pluginName)) { + const newArray: string[] = [pluginName]; + args = newArray.concat(args); + } + } + + const loadPromise = PluginAdmin._loadedPlugins.get(pluginName); + if (undefined !== loadPromise) { + // it has been loaded (or at least we have started to load it) already. If it is registered, call its reload method. (Otherwise reload called when we're done the initial load) + const registeredPlugin = PluginAdmin._registeredPlugins.get(pluginName); + if (registeredPlugin) { + registeredPlugin.onExecute(args); + } + return loadPromise; + } + + // set it up to load. + const thisPromise: Promise = new Promise((resolve, reject) => { + const head = document.getElementsByTagName("head")[0]; + if (!head) + reject(new Error("no head element found")); + + // create the script element. handle onload and onerror. + const scriptElement = document.createElement("script"); + scriptElement.onload = () => { + scriptElement.onload = null; + resolve(); + }; + scriptElement.onerror = (ev) => { + scriptElement.onload = null; + reject(new Error("can't load " + packageName + " : " + ev)); + }; + scriptElement.async = true; + scriptElement.src = packageName; + head.insertBefore(scriptElement, head.lastChild); + }); + + // Javascript-ish saving of the arguments in the promise, so we can call reload with them. + (thisPromise as any).args = args; + PluginAdmin._loadedPlugins.set(pluginName, thisPromise); + return thisPromise; + } + + /** + * Registers a Plugin with the PluginAdmin. This method is called by the Plugin when it is first loaded. + * This method verifies that the required versions of the iModel.js system modules are loaded. If those + * requirements are met, then the onLoad and onExecute methods of the Plugin will be called (@see [[Plugin]]). + * If not, no further action is taken and the Plugin is not active. + * @param plugin a newly instantiated subclass of Plugin. + * @returns an array of error messages. The array will be empty if the load is successful, otherwise it is a list of one or more problems. + */ + public static register(plugin: Plugin): string[] | undefined { + const errorMessages = PluginAdmin.checkIModelJsVersions(plugin.versionsRequired); + if (errorMessages) { + // report load errors to the user. + let allDetails: string = ""; + for (const thisMessage of errorMessages) { + allDetails = allDetails.concat(thisMessage, "\n"); + } + const briefMessage = IModelApp.i18n.translate("iModelJs:PluginErrors.VersionErrors", { pluginName: plugin.name }); + const errorDetails: NotifyMessageDetails = new NotifyMessageDetails(OutputMessagePriority.Info, briefMessage, allDetails, OutputMessageType.Alert, OutputMessageAlert.Balloon); + IModelApp.notifications.outputMessage(errorDetails); + return errorMessages; + } + PluginAdmin._registeredPlugins.set(plugin.name, plugin); + + // announce successful load after plugin is registered. + const messageDetail: NotifyMessageDetails = new NotifyMessageDetails(OutputMessagePriority.Info, IModelApp.i18n.translate("iModelJs:PluginErrors.Success", { pluginName: plugin.name })); + IModelApp.notifications.outputMessage(messageDetail); + + // retrieve the args we saved in the promise. + let args: string[] | undefined; + const loadedPluginPromise = PluginAdmin._loadedPlugins.get(plugin.name); + if (loadedPluginPromise) { + args = (loadedPluginPromise as any).args; + } + + if (!args) + args = [plugin.name]; + + plugin.onLoad(args); + plugin.onExecute(args); + return undefined; + } + +} diff --git a/core/frontend/src/RenderScheduleState.ts b/core/frontend/src/RenderScheduleState.ts index 7acd6ec..24e5bc6 100644 --- a/core/frontend/src/RenderScheduleState.ts +++ b/core/frontend/src/RenderScheduleState.ts @@ -4,14 +4,11 @@ *--------------------------------------------------------------------------------------------*/ /** @module Views */ -import { RenderSchedule, RgbColor, TileTreeProps, BatchType } from "@bentley/imodeljs-common"; +import { RenderSchedule, RgbColor } from "@bentley/imodeljs-common"; import { Range1d, Transform, Point3d, Vector3d, Matrix3d, Plane3dByOriginAndUnitNormal, ClipPlane, ConvexClipPlaneSet, UnionOfConvexClipPlaneSets, Point4d } from "@bentley/geometry-core"; import { Id64String, Id64 } from "@bentley/bentleyjs-core"; import { FeatureSymbology } from "./render/FeatureSymbology"; -import { TileTreeModelState } from "./ModelState"; import { IModelConnection } from "./IModelConnection"; -import { TileTree, TileTreeState, IModelTileLoader } from "./tile/TileTree"; -import { IModelApp } from "./IModelApp"; import { ClipPlanesVolume } from "./render/webgl/ClipVolume"; import { AnimationBranchStates, AnimationBranchState } from "./render/System"; @@ -63,17 +60,18 @@ export namespace RenderScheduleState { export class ElementTimeline implements RenderSchedule.ElementTimelineProps { public currentClip: ClipPlanesVolume | undefined = undefined; public elementIds: Id64String[]; + public batchId: number; public visibilityTimeline?: VisibilityEntry[]; public colorTimeline?: ColorEntry[]; public transformTimeline?: TransformEntry[]; public cuttingPlaneTimeline?: CuttingPlaneEntry[]; public get isValid() { return this.elementIds.length > 0 && (Array.isArray(this.visibilityTimeline) && this.visibilityTimeline.length > 0) || (Array.isArray(this.colorTimeline) && this.colorTimeline.length > 0); } - private constructor(elementIds: Id64String[]) { this.elementIds = elementIds; } + private constructor(elementIds: Id64String[], batchId: number) { this.elementIds = elementIds; this.batchId = batchId; } public static fromJSON(json?: RenderSchedule.ElementTimelineProps): ElementTimeline { if (!json) - return new ElementTimeline([]); + return new ElementTimeline([], 0); - const val = new ElementTimeline(json.elementIds); + const val = new ElementTimeline(json.elementIds, json.batchId); if (json.visibilityTimeline) { val.visibilityTimeline = []; json.visibilityTimeline.forEach((entry) => val.visibilityTimeline!.push(new VisibilityEntry(entry))); @@ -126,22 +124,31 @@ export namespace RenderScheduleState { return true; } + public getVisibilityOverride(time: number, interval: Interval): number { + if (!ElementTimeline.findTimelineInterval(interval, time, this.visibilityTimeline) && this.visibilityTimeline![interval.index0].value !== null) + return 100.0; + const timeline = this.visibilityTimeline!; + let visibility = timeline[interval.index0].value; + if (visibility === undefined || visibility === null) + return 100.0; + + if (interval.fraction > 0) + visibility = interpolate(visibility, timeline[interval.index1].value, interval.fraction); + + return visibility; + } + public getSymbologyOverrides(overrides: FeatureSymbology.Overrides, time: number, interval: Interval, batchId: number) { let colorOverride, transparencyOverride; - if (ElementTimeline.findTimelineInterval(interval, time, this.visibilityTimeline) && this.visibilityTimeline![interval.index0].value !== null) { - const timeline = this.visibilityTimeline!; - let visibility = timeline[interval.index0].value; - if (interval.fraction > 0) - visibility = interpolate(visibility, timeline[interval.index1].value, interval.fraction); - - if (visibility <= 0) { - overrides.setBatchNeverDrawn(batchId); - return; - } - if (visibility <= 100) - transparencyOverride = 1.0 - visibility / 100.0; + const visibility = this.getVisibilityOverride(time, interval); + if (visibility <= 0) { + overrides.setAnimationNodeNeverDrawn(batchId); + return; } + if (visibility <= 100) + transparencyOverride = 1.0 - visibility / 100.0; + if (ElementTimeline.findTimelineInterval(interval, time, this.colorTimeline) && this.colorTimeline![interval.index0].value !== null) { const entry0 = this.colorTimeline![interval.index0].value; if (interval.fraction > 0) { @@ -152,7 +159,7 @@ export namespace RenderScheduleState { } if (colorOverride || transparencyOverride) - overrides.overrideBatch(batchId, FeatureSymbology.Appearance.fromJSON({ rgb: colorOverride, transparency: transparencyOverride })); + overrides.overrideAnimationNode(batchId, FeatureSymbology.Appearance.fromJSON({ rgb: colorOverride, transparency: transparencyOverride })); } public getAnimationTransform(time: number, interval: Interval): Transform | undefined { if (!ElementTimeline.findTimelineInterval(interval, time, this.transformTimeline) || this.transformTimeline![interval.index0].value === null) @@ -208,6 +215,9 @@ export namespace RenderScheduleState { const value1 = timeline[interval.index1].value; position.interpolate(interval.fraction, Point3d.fromJSON(value1.position), position); direction.interpolate(interval.fraction, Vector3d.fromJSON(value1.direction), direction); + } else { + if (value.hidden || value.visible) + return undefined; } direction.normalizeInPlace(); @@ -219,39 +229,11 @@ export namespace RenderScheduleState { } } - class AnimationModelState implements TileTreeModelState { - private _iModel: IModelConnection; - private _modelId: Id64String; - private _displayStyleId: Id64String; - protected _tileTreeState: TileTreeState; - constructor(modelId: Id64String, displayStyleId: Id64String, iModel: IModelConnection) { this._modelId = modelId; this._displayStyleId = displayStyleId, this._iModel = iModel; this._tileTreeState = new TileTreeState(iModel, true, modelId); } - - public get tileTree(): TileTree | undefined { return this._tileTreeState.tileTree; } - public get loadStatus(): TileTree.LoadStatus { return this._tileTreeState.loadStatus; } - /** @hidden */ - public loadTileTree(_asClassifier?: boolean, _classifierExpansion?: number): TileTree.LoadStatus { - if (TileTree.LoadStatus.NotLoaded !== this._tileTreeState.loadStatus) - return this._tileTreeState.loadStatus; - - this._tileTreeState.loadStatus = TileTree.LoadStatus.Loading; - const id = "A:" + this._displayStyleId + "_" + this._modelId; - - this._iModel.tiles.getTileTreeProps(id).then((result: TileTreeProps) => { - this._tileTreeState.setTileTree(result, new IModelTileLoader(this._iModel, BatchType.Primary)); - IModelApp.viewManager.onNewTilesReady(); - }).catch((_err) => { - this._tileTreeState.loadStatus = TileTree.LoadStatus.NotFound; // on separate line because stupid chrome debugger. - }); - - return this._tileTreeState.loadStatus; - } - } export class ModelTimeline implements RenderSchedule.ModelTimelineProps { public modelId: Id64String; public elementTimelines: ElementTimeline[] = []; public containsFeatureOverrides: boolean = false; public containsAnimation: boolean = false; - public animationModel?: AnimationModelState; private constructor(modelId: Id64String) { this.modelId = modelId; } public get duration() { const duration = Range1d.createNull(); @@ -276,7 +258,7 @@ export namespace RenderScheduleState { return value; } - public getSymbologyOverrides(overrides: FeatureSymbology.Overrides, time: number, nextBatchId: number) { const interval = new Interval(); this.elementTimelines.forEach((entry) => entry.getSymbologyOverrides(overrides, time, interval, nextBatchId++)); } + public getSymbologyOverrides(overrides: FeatureSymbology.Overrides, time: number) { const interval = new Interval(); this.elementTimelines.forEach((entry) => entry.getSymbologyOverrides(overrides, time, interval, entry.batchId)); } public forEachAnimatedId(idFunction: (id: Id64String) => void): void { if (this.containsAnimation) { for (const timeline of this.elementTimelines) @@ -288,18 +270,15 @@ export namespace RenderScheduleState { public getAnimationBranches(branches: AnimationBranchStates, scheduleTime: number) { const interval = new Interval(); for (let i = 0; i < this.elementTimelines.length; i++) { - const transform = this.elementTimelines[i].getAnimationTransform(scheduleTime, interval); - const clip = this.elementTimelines[i].getAnimationClip(scheduleTime, interval); - if (transform || clip) - branches.set(this.modelId + "_Node_" + (i + 1).toString(), new AnimationBranchState(transform, clip)); - } - } - public initBatchMap(batchMap: Id64.Uint32Map, nextBatchId: number) { - for (const timeline of this.elementTimelines) { - for (const id of timeline.elementIds) - batchMap.setById(id, nextBatchId); - - nextBatchId++; + const elementTimeline = this.elementTimelines[i]; + if (elementTimeline.getVisibilityOverride(scheduleTime, interval) <= 0.0) { + branches.set(this.modelId + "_Node_" + (i + 1).toString(), new AnimationBranchState(undefined, undefined, true)); + } else { + const transform = elementTimeline.getAnimationTransform(scheduleTime, interval); + const clip = elementTimeline.getAnimationClip(scheduleTime, interval); + if (transform || clip) + branches.set(this.modelId + "_Node_" + (i + 1).toString(), new AnimationBranchState(transform, clip)); + } } } } @@ -308,19 +287,13 @@ export namespace RenderScheduleState { public modelTimelines: ModelTimeline[] = []; public iModel: IModelConnection; public displayStyleId: Id64String; - private _batchMap = new Id64.Uint32Map(); constructor(displayStyleId: Id64String, iModel: IModelConnection) { this.displayStyleId = displayStyleId; this.iModel = iModel; } public static fromJSON(displayStyleId: Id64String, iModel: IModelConnection, modelTimelines: RenderSchedule.ModelTimelineProps[]): Script | undefined { const value = new Script(displayStyleId, iModel); modelTimelines.forEach((entry) => value.modelTimelines.push(ModelTimeline.fromJSON(entry))); - value.initBatchMap(); return value; } - public initBatchMap() { - const nextBatchId = 0; - this.modelTimelines.forEach((modelTimeline) => modelTimeline.initBatchMap(this._batchMap, nextBatchId)); - } public get containsAnimation() { for (const modelTimeline of this.modelTimelines) if (modelTimeline.containsAnimation) @@ -349,19 +322,17 @@ export namespace RenderScheduleState { } public getSymbologyOverrides(overrides: FeatureSymbology.Overrides, time: number) { - overrides.batchMap = this._batchMap; // Is it necessary to clone this?? - const batchId = 0; - this.modelTimelines.forEach((entry) => entry.getSymbologyOverrides(overrides, time, batchId)); + this.modelTimelines.forEach((entry) => entry.getSymbologyOverrides(overrides, time)); } + public getModelAnimationId(modelId: Id64String): Id64String | undefined { + if (Id64.isTransient(modelId)) + return undefined; - public forEachAnimationModel(tileTreeFunction: (model: TileTreeModelState) => void): void { - for (const modelTimeline of this.modelTimelines) { - if (modelTimeline.containsAnimation) { - if (!modelTimeline.animationModel) - modelTimeline.animationModel = new AnimationModelState(modelTimeline.modelId, this.displayStyleId, this.iModel); - tileTreeFunction(modelTimeline.animationModel); - } - } + for (const modelTimeline of this.modelTimelines) + if (modelTimeline.modelId === modelId && modelTimeline.containsAnimation) + return this.displayStyleId; + + return undefined; } } } diff --git a/core/frontend/src/SelectionSet.ts b/core/frontend/src/SelectionSet.ts index be579f3..62b691d 100644 --- a/core/frontend/src/SelectionSet.ts +++ b/core/frontend/src/SelectionSet.ts @@ -132,11 +132,13 @@ export class SelectionSet { public addAndRemove(adds: Id64Arg, removes: Id64Arg): boolean { const added = this.add(adds, false); const removed = this.remove(removes, false); - if (added || removed) { - this.sendChangedEvent(SelectEventType.Replace, Id64.toIdSet(adds)); - return true; - } - return false; + if (added && removed) + this.sendChangedEvent(SelectEventType.Replace, this.elements); + else if (added) + this.sendChangedEvent(SelectEventType.Add, Id64.toIdSet(adds)); + else if (removed) + this.sendChangedEvent(SelectEventType.Remove, Id64.toIdSet(removes)); + return (added || removed); } /** Invert the state of a set of Ids in the SelectionSet */ diff --git a/core/frontend/src/Sheet.ts b/core/frontend/src/Sheet.ts index 1a4e19b..d916054 100644 --- a/core/frontend/src/Sheet.ts +++ b/core/frontend/src/Sheet.ts @@ -270,7 +270,7 @@ export namespace Attachments { super(new Tile.Params( root, "", - new ElementAlignedBox3d(0, 0, -RenderTarget.frustumDepth2d, range.high.x, range.high.y, RenderTarget.frustumDepth2d), + new Range3d(0, 0, -RenderTarget.frustumDepth2d, range.high.x, range.high.y, RenderTarget.frustumDepth2d), 512, // does not matter... have no children true, )); @@ -320,7 +320,7 @@ export namespace Attachments { fullRange = root.getRootRange(); const mid = fullRange.low.interpolate(0.5, fullRange.high); - const range = new ElementAlignedBox3d(); + const range = new Range3d(); switch (placement) { case Tile3dPlacement.UpperLeft: range.extend(mid); @@ -662,7 +662,7 @@ export namespace Attachments { if (!viewedModel) return State.Empty; - viewedModel.getOrLoadTileTree(); + viewedModel.getOrLoadTileTree(true); const loadStatus = viewedModel.loadStatus; if (loadStatus === TileTree.LoadStatus.Loaded) { attachment.tree = new Tree2d(viewedModel.iModel, attachment, view, viewedModel.tileTree!); @@ -866,7 +866,7 @@ export namespace Attachments { /** Given a view and an origin point, compute a placement for an attachment. */ private static computePlacement(view: ViewState, origin: Point2d, scale: number): Placement2d { const viewExtents = view.getExtents(); - const box = new ElementAlignedBox2d(); + const box = new Range2d(); box.low.setZero(); box.high.x = viewExtents.x / scale; box.high.y = viewExtents.y / scale; diff --git a/core/frontend/src/Sprites.ts b/core/frontend/src/Sprites.ts index fc81f4e..7dfca7b 100644 --- a/core/frontend/src/Sprites.ts +++ b/core/frontend/src/Sprites.ts @@ -11,8 +11,7 @@ import { CanvasDecoration } from "./render/System"; import { DecorateContext } from "./ViewContext"; import { ScreenViewport } from "./Viewport"; -/** - * Sprites are small raster images that are drawn *on top* of Viewports by a ViewDecoration. +/** Sprites are small raster images that are drawn *on top* of Viewports by a ViewDecoration. * Their purpose is to draw the user's attention to something of importance. * * There are two classes in the Sprites subsystem: Sprite (a Sprite image) and SpriteLocation. @@ -24,6 +23,7 @@ import { ScreenViewport } from "./Viewport"; * (an x,y point) and a Sprite to draw at that point. A Sprite * can be used many times by many SpriteLocations and a single SpriteLocation can * change both position and which Sprite is shown at that position over time. + * @public */ export class Sprite { /** The image for this Sprite. If undefined, the Spite is not valid. */ @@ -71,11 +71,11 @@ export class IconSprites { public static emptyAll() { this._sprites.clear(); } } -/** - * A Sprite location. Sprites generally move around on the screen and this object holds the current location +/** A Sprite location. Sprites generally move around on the screen and this object holds the current location * and current Sprite within a ScreenViewport. SpriteLocations can be either inactive (not visible) or active. * * A SpriteLocation can also specify that a Sprite should be drawn partially transparent. + * @public */ export class SpriteLocation implements CanvasDecoration { private _viewport?: ScreenViewport; @@ -87,8 +87,7 @@ export class SpriteLocation implements CanvasDecoration { public readonly position = new Point3d(); public get isActive(): boolean { return this._viewport !== undefined; } - /** - * Activate this SpriteLocation to show a Sprite at a location in a single ScreenViewport. + /** Activate this SpriteLocation to show a Sprite at a location in a single ScreenViewport. * This call does not display the Sprite. Rather, subsequent calls to * [[decorate]] from will show the Sprite. * This SpriteLocation remains active until [[deactivate]] is called. diff --git a/core/frontend/src/StandardView.ts b/core/frontend/src/StandardView.ts index 95e31be..242c331 100644 --- a/core/frontend/src/StandardView.ts +++ b/core/frontend/src/StandardView.ts @@ -5,12 +5,11 @@ /** @module Views */ import { Matrix3d } from "@bentley/geometry-core"; -/** - * Describes a set of commonly-used view rotations. +/** Describes a set of commonly-used view rotations. + * @public */ export const enum StandardViewId { - /** - * Any rotation which does not match one of the standard rotations. + /** Any rotation which does not match one of the standard rotations. * Invalid as an argument to StandardView.getStandardRotation() - used as a return value only. */ NotStandard = -1, @@ -51,8 +50,8 @@ function getMatrices(): Matrix3d[] { return standardViewMatrices; } -/** - * Supplies access to a set of commonly-used view rotations. +/** Supplies access to a set of commonly-used view rotations. + * @public */ export class StandardView { public static get top(): Matrix3d { return this.getStandardRotation(StandardViewId.Top); } @@ -64,8 +63,7 @@ export class StandardView { public static get iso(): Matrix3d { return this.getStandardRotation(StandardViewId.Iso); } public static get rightIso(): Matrix3d { return this.getStandardRotation(StandardViewId.RightIso); } - /** - * Obtain a [[Matrix3d]] corresponding to the specified [[StandardViewId]]. + /** Obtain a [[Matrix3d]] corresponding to the specified [[StandardViewId]]. * @param id The ID of the desired rotation. * @return A rotation matrix corresponding to the requested standard view ID, or a "top" view rotation if the input does not correspond to a standard rotation. */ @@ -76,8 +74,7 @@ export class StandardView { return getMatrices()[id]; } - /** - * Attempts to adjust the supplied rotation matrix to match the standard view rotation it most closely matches. + /** Attempts to adjust the supplied rotation matrix to match the standard view rotation it most closely matches. * If a matching standard rotation exists, the input matrix will be modified in-place to precisely match it. * Otherwise, the input matrix will be unmodified. * @param matrix The rotation matrix to adjust. diff --git a/core/frontend/src/ViewContext.ts b/core/frontend/src/ViewContext.ts index f4cb607..6fbe612 100644 --- a/core/frontend/src/ViewContext.ts +++ b/core/frontend/src/ViewContext.ts @@ -17,7 +17,9 @@ import { IModelApp } from "./IModelApp"; const gridConstants = { maxPoints: 50, maxRefs: 25, maxDotsInRow: 250, maxHorizon: 500, dotTransparency: 100, lineTransparency: 200, planeTransparency: 225 }; -/** Provides context for producing [[RenderGraphic]]s for drawing within a [[Viewport]]. */ +/** Provides context for producing [[RenderGraphic]]s for drawing within a [[Viewport]]. + * @public + */ export class RenderContext { /** ViewFlags extracted from the context's [[Viewport]]. */ public readonly viewFlags: ViewFlags; @@ -62,6 +64,7 @@ export class RenderContext { /** Provides context for an [[InteractiveTool]] to display decorations representing its current state. * @see [[InteractiveTool.onDynamicFrame]] + * @public */ export class DynamicsContext extends RenderContext { private _dynamics?: GraphicList; @@ -77,7 +80,9 @@ export class DynamicsContext extends RenderContext { public changeDynamics(): void { this.viewport!.changeDynamics(this._dynamics); } } -/** Provides context for a [[Decorator]] to add [[Decorations]] to be rendered within a [[Viewport]]. */ +/** Provides context for a [[Decorator]] to add [[Decorations]] to be rendered within a [[Viewport]]. + * @public + */ export class DecorateContext extends RenderContext { /** The HTMLDivElement which overlays the [[Viewport]]'s HTMLCanvasElement, to which HTML decorations are added. */ public decorationDiv: HTMLDivElement; diff --git a/core/frontend/src/ViewManager.ts b/core/frontend/src/ViewManager.ts index 70de575..d86b5a1 100644 --- a/core/frontend/src/ViewManager.ts +++ b/core/frontend/src/ViewManager.ts @@ -18,6 +18,7 @@ import { SheetViewState } from "./Sheet"; /** Interface for drawing "decorations" into, or on top of, the active [[Viewport]]s. * Decorators generate [[Decorations]]. + * @public */ export interface Decorator { /** Implement this method to add Decorations into the supplied DecorateContext. */ @@ -57,13 +58,13 @@ export interface SelectedViewportChangedArgs { previous?: ScreenViewport; } -/** - * The ViewManager holds the list of opened views, plus the *selected view*. It also provides notifications of view open/close and suspend/resume. +/** The ViewManager holds the list of opened views, plus the *selected view*. It also provides notifications of view open/close and suspend/resume. * Applications must call [[addViewport]] when new Viewports that should be associated with user events are created. * * A single ViewManager is created when [[IModelApp.startup]] is called. It can be accessed via the static member [[IModelApp.viewManager]]. * * The ViewManager controls the render loop, which causes the contents of each registered [[Viewport]] to update on the screen. + * @public */ export class ViewManager { public inDynamicsMode = false; @@ -118,22 +119,19 @@ export class ViewManager { */ public readonly onViewSuspend = new BeUiEvent(); - /** - * Called after a suspended view is resumed. This can happen when a minimized application is restored + /** Called after a suspended view is resumed. This can happen when a minimized application is restored * or, on a tablet, when the application is moved to the foreground. */ public readonly onViewResume = new BeUiEvent(); - /** - * Called at the beginning of each tick of the render loop, before any viewports have been updated. + /** Called at the beginning of each tick of the render loop, before any viewports have been updated. * The render loop is typically invoked by a requestAnimationFrame() callback. It will not be invoked if the ViewManager is tracking no viewports. * @note Due to the frequency of this event, avoid performing expensive work inside event listeners. * @see [[ViewManager.onFinishRender]] */ public readonly onBeginRender = new BeEvent<() => void>(); - /** - * Called at the end of each tick of the render loop, after all viewports have been updated. + /** Called at the end of each tick of the render loop, after all viewports have been updated. * The render loop is typically invoked by a requestAnimationFrame() callback. It will not be invoked if the ViewManager is tracking no viewports. * @note Due to the frequency of this event, avoid performing expensive work inside event listeners. * @see [[ViewManager.onBeginRender]] @@ -205,8 +203,7 @@ export class ViewManager { /** Get the first opened view. */ public getFirstOpenView(): ScreenViewport | undefined { return this._viewports.length > 0 ? this._viewports[0] : undefined; } - /** - * Add a new Viewport to the list of opened views and create an EventController for it. + /** Add a new Viewport to the list of opened views and create an EventController for it. * @param newVp the Viewport to add * @returns SUCCESS if vp was successfully added, ERROR if it was already present. * @note raises onViewOpen event with newVp. @@ -229,8 +226,7 @@ export class ViewManager { return BentleyStatus.SUCCESS; } - /** - * Remove a Viewport from the list of opened views, and optionally dispose of it. + /** Remove a Viewport from the list of opened views, and optionally dispose of it. * Typically a Viewport is dropped when it is no longer of any use to the application, in which case it should also be * disposed of as it may hold significant GPU resources. * However in some cases a Viewport may be temporarily dropped to suspend rendering; and subsequently re-added to @@ -325,7 +321,6 @@ export class ViewManager { /** Drop (remove) a [[Decorator]] so it is no longer active. * @param decorator The Decorator to drop. * @note Does nothing if decorator is not currently active. - * */ public dropDecorator(decorator: Decorator) { const index = this.decorators.indexOf(decorator); @@ -335,7 +330,7 @@ export class ViewManager { } /** Get the tooltip for a pickable decoration. - * @hidden + * @hidden */ public async getDecorationToolTip(hit: HitDetail): Promise { for (const decorator of this.decorators) { @@ -346,7 +341,7 @@ export class ViewManager { } /** Allow a pickable decoration to handle a button event that identified it for the SelectTool. - * @hidden + * @hidden */ public async onDecorationButtonEvent(hit: HitDetail, ev: BeButtonEvent): Promise { for (const decorator of IModelApp.viewManager.decorators) { @@ -357,7 +352,7 @@ export class ViewManager { } /** Allow a pickable decoration to be snapped to by AccuSnap or TentativePoint. - * @hidden + * @hidden */ public getDecorationGeometry(hit: HitDetail): GeometryStreamProps | undefined { for (const decorator of IModelApp.viewManager.decorators) { @@ -383,6 +378,5 @@ export class ViewManager { if (undefined !== this.selectedView) { this.selectedView.setCursor(cursor); } - } } diff --git a/core/frontend/src/ViewState.ts b/core/frontend/src/ViewState.ts index a486d00..a91586b 100644 --- a/core/frontend/src/ViewState.ts +++ b/core/frontend/src/ViewState.ts @@ -30,7 +30,9 @@ import { TileTree } from "./tile/TileTree"; import { DecorateContext, SceneContext } from "./ViewContext"; import { Viewport } from "./Viewport"; -/** Describes the orientation of the grid displayed within a [[Viewport]]. */ +/** Describes the orientation of the grid displayed within a [[Viewport]]. + * @public + */ export const enum GridOrientationType { /** Oriented with the view. */ View = 0, @@ -44,7 +46,9 @@ export const enum GridOrientationType { AuxCoord = 4, } -/** Describes the result of a viewing operation such as those exposed by [[ViewState]] and [[Viewport]]. */ +/** Describes the result of a viewing operation such as those exposed by [[ViewState]] and [[Viewport]]. + * @public + */ export const enum ViewStatus { Success = 0, ViewNotInitialized, @@ -64,13 +68,13 @@ export const enum ViewStatus { InvalidViewport, } -/** - * Margins for white space to be left around view volumes for [[ViewState.lookAtVolume]]. +/** Margins for white space to be left around view volumes for [[ViewState.lookAtVolume]]. * Values mean "fraction of view size" and must be between 0 and .25. + * @public */ export class MarginPercent { constructor(public left: number, public top: number, public right: number, public bottom: number) { - const limitMargin = (val: number) => (val < 0.0) ? 0.0 : (val > .25) ? .25 : val; + const limitMargin = (val: number) => Geometry.clamp(val, 0.0, 0.25); this.left = limitMargin(left); this.top = limitMargin(top); this.right = limitMargin(right); @@ -78,12 +82,85 @@ export class MarginPercent { } } -/** - * Stores information about sub-categories specific to a ViewState. Functions as a lazily-populated cache. +/** A cancelable paginated request for subcategory information. + * @see ViewSubCategories + * @hidden + */ +export class SubCategoriesRequest { + private static readonly _LIMIT = 1000; + + private readonly _imodel: IModelConnection; + private readonly _subcategories: ViewSubCategories; + private readonly _ecsql: string; + private readonly _pages: any[][] = []; + private _canceled = false; + + public constructor(subcategories: ViewSubCategories, categoryIds: Set, imodel: IModelConnection) { + this._imodel = imodel; + this._subcategories = subcategories; + + const where = [...categoryIds].join(","); + this._ecsql = "SELECT ECInstanceId as id, Parent.Id as parentId, Properties as appearance FROM BisCore.SubCategory WHERE Parent.Id IN (" + where + ")"; + } + + public cancel() { this._canceled = true; } + + public async dispatch(): Promise { + if (this._canceled) + return Promise.resolve(); + + let rows: any[] | undefined; + try { + const pageToGet = this._pages.length; + const pageSize = SubCategoriesRequest._LIMIT; + // we can actully use following async iterator here which would grab pages and iterator over rows automatically. + // at any time inside the loop user can break and cancel or stop reading the results. With catch that async iterator work on all browser except ie/edge and starting of safari 12/ ios 12 + // rows = []; + // for await (const row of this._imodel.query(this._ecsql)) { + // if (this._canceled) + // return Promise.resolve(); + // else + // rows.push(row); + // } + rows = Array.from(await this._imodel.queryPage(this._ecsql, undefined, { start: pageToGet, size: pageSize })); + } catch (_) { + // ###TODO: detect cases in which retry is warranted + // Note that currently, if we succeed in obtaining some pages of results and fail to retrieve another page, we will end up processing the + // incomplete results. Since we're not retrying, that's the best we can do. + rows = undefined; + } + + if (undefined !== rows && rows.length > 0) { + this._pages.push(rows); + // More rows exist. If we're canceled, we can't process the partial results we've obtained thus far. + if (this._canceled) + return Promise.resolve(); + + // Query the next page of results. + return this.dispatch(); + } + + // Even if we were canceled, we've retrieved all the rows. Might as well process them to prevent another request for some of the same rows from being enqueued. + this.processResults(); + return Promise.resolve(); + } + + // Because this request can be canceled before all rows are obtained and processed, we must wait to populate the ViewSubCategories until we have all of the data. + private processResults(): void { + for (const rows of this._pages) + this._subcategories.loadFromRows(rows); + + this._pages.length = 0; + } +} + +/** Stores information about sub-categories specific to a ViewState. Functions as a lazily-populated cache. + * @hidden */ export class ViewSubCategories { private readonly _byCategoryId = new Map(); private readonly _appearances = new Map(); + private _request?: SubCategoriesRequest; /** Get the Ids of all subcategories belonging to the category with the specified Id, or undefined if no such information is present. */ public getSubCategories(categoryId: string): Id64Set | undefined { return this._byCategoryId.get(categoryId); } @@ -97,12 +174,7 @@ export class ViewSubCategories { * categories in the ViewState's [[CategorySelector]]. */ public async load(categoryIds: Set, iModel: IModelConnection): Promise { - const where = [...categoryIds].join(","); - if (0 === where.length) - return Promise.resolve(); - - const ecsql = "SELECT ECInstanceId as id, Parent.Id as parentId, Properties as appearance FROM BisCore.SubCategory WHERE Parent.Id IN (" + where + ")"; - return iModel.executeQuery(ecsql).then((rows: any[]) => this.loadFromRows(rows)); + return 0 < categoryIds.size ? this.request(categoryIds, iModel) : Promise.resolve(); } /** @@ -111,6 +183,8 @@ export class ViewSubCategories { * categories. */ public async update(addedCategoryIds: Set, iModel: IModelConnection): Promise { + this.cancelRequest(); // Just in case current request has obtained (some of) the data we want + let missing: Set | undefined; for (const catId of addedCategoryIds) { if (undefined === this._byCategoryId.get(catId)) { @@ -121,10 +195,20 @@ export class ViewSubCategories { } } - if (undefined !== missing) - return this.load(missing, iModel); - else - return Promise.resolve(); + return undefined !== missing ? this.request(missing, iModel) : Promise.resolve(); + } + + private async request(categoryIds: Set, iModel: IModelConnection): Promise { + this.cancelRequest(); + this._request = new SubCategoriesRequest(this, categoryIds, iModel); + return this._request.dispatch(); + } + + private cancelRequest(): void { + if (undefined !== this._request) { + this._request.cancel(); + this._request = undefined; + } } private static createSubCategoryAppearance(json?: any) { @@ -135,7 +219,7 @@ export class ViewSubCategories { return new SubCategoryAppearance(props); } - private loadFromRows(rows: any[]): void { + public loadFromRows(rows: any[]): void { for (const row of rows) this.add(row.parentId as string, row.id as string, ViewSubCategories.createSubCategoryAppearance(row.appearance)); } @@ -150,10 +234,10 @@ export class ViewSubCategories { } } -/** - * The front-end state of a [[ViewDefinition]] element. +/** The front-end state of a [[ViewDefinition]] element. * A ViewState is typically associated with a [[Viewport]] to display the contents of the view on the screen. * * @see [Views]($docs/learning/frontend/Views.md) + * @public */ export abstract class ViewState extends ElementState { protected _featureOverridesDirty = true; @@ -166,6 +250,7 @@ export abstract class ViewState extends ElementState { public undoTime?: BeTimePoint; /** A cache of information about subcategories belonging to categories present in this view's [[CategorySelectorState]]. * It is populated on-demand as new categories are added to the selector. + * @hidden */ public readonly subCategories = new ViewSubCategories(); public static get className() { return "ViewDefinition"; } @@ -182,8 +267,7 @@ export abstract class ViewState extends ElementState { } } - /** - * Create a new ViewState object from a set of properties. Generally this is called internally by [[IModelConnection.Views.load]] after the properties + /** Create a new ViewState object from a set of properties. Generally this is called internally by [[IModelConnection.Views.load]] after the properties * have been read from an iModel. But, it can also be used to create a ViewState in memory, from scratch or from properties stored elsewhere. */ public static createFromProps(_props: ViewStateProps, _iModel: IModelConnection): ViewState | undefined { return undefined; } @@ -206,9 +290,9 @@ export abstract class ViewState extends ElementState { } } /** Get the AnalysisDisplayProperties from the displayStyle of this ViewState. */ - public get AnalysisStyle(): AnalysisStyle | undefined { return this.displayStyle.analysisStyle; } + public get analysisStyle(): AnalysisStyle | undefined { return this.displayStyle.analysisStyle; } - /** Get the RenderSchedule.Script from the displayStyle ofthis viewState */ + /** Get the RenderSchedule.Script from the displayStyle of this viewState */ public get scheduleScript(): RenderScheduleState.Script | undefined { return this.displayStyle.scheduleScript; } public get scheduleTime() { return this._scheduleTime; } public set scheduleTime(time: number) { @@ -222,7 +306,7 @@ export abstract class ViewState extends ElementState { /** Determine whether this ViewState exactly matches another. * @see [[ViewState.equalState]] for determining broader equivalence of two ViewStates. */ - public equals(other: ViewState): boolean { return super.equals(other) && this.categorySelector.equals(other.categorySelector) && this.displayStyle.equals(other.displayStyle); } + public equals(other: this): boolean { return super.equals(other) && this.categorySelector.equals(other.categorySelector) && this.displayStyle.equals(other.displayStyle); } /** Determine whether this ViewState is equivalent to another for the purposes of display. * @see [[ViewState.equals]] for determining exact equality. @@ -275,8 +359,8 @@ public cancelAllTileLoads(): void { public get areAllTileTreesLoaded(): boolean { let allLoaded = true; this.forEachTileTreeModel((model) => { - const loadStatus = model.loadStatus; - if (loadStatus !== TileTree.LoadStatus.Loaded) + // Loaded or NotFound qualify as "loaded" - either the load succeeded or failed. + if (model.loadStatus < TileTree.LoadStatus.Loaded) allLoaded = false; }); return allLoaded; @@ -288,68 +372,8 @@ public cancelAllTileLoads(): void { /** Get this view's background color. */ public get backgroundColor(): ColorDef { return this.displayStyle.backgroundColor; } - private _neverDrawn?: Id64Set; - private _alwaysDrawn?: Id64Set; - private _alwaysDrawnExclusive: boolean = false; - - /** - * IDs of a set of elements which should not be rendered within this view. - * @note Do not modify this set directly - use [[setNeverDrawn]] or [[clearNeverDrawn]] instead. - * @note This set takes precedence over the [[alwaysDrawn]] set - if an element is present in both sets, it is never drawn. - */ - public get neverDrawn(): Id64Set | undefined { return this._neverDrawn; } - - /** - * IDs of a set of elements which should always be rendered within this view, regardless of category and subcategory visibility. - * If the [[isAlwaysDrawnExclusive]] flag is also set, *only* those elements in this set will be drawn. - * @note Do not modify this set directly - use [[setAlwaysDrawn]] or [[clearAlwaysDrawn]] instead. - * @note The [[neverDrawn]] set takes precedence - if an element is present in both sets, it is never drawn. - */ - public get alwaysDrawn(): Id64Set | undefined { return this._alwaysDrawn; } - - /** Clear the set of always-drawn elements. - * @see [[alwaysDrawn]] - */ - public clearAlwaysDrawn(): void { - if (undefined !== this.alwaysDrawn && 0 < this.alwaysDrawn.size) { - this.alwaysDrawn.clear(); - this._alwaysDrawnExclusive = false; - this.setFeatureOverridesDirty(); - } - } - - /** Clear the set of never-drawn elements. - * @see [[neverDrawn]] - */ - public clearNeverDrawn(): void { - if (undefined !== this.neverDrawn && 0 < this.neverDrawn.size) { - this.neverDrawn.clear(); - this.setFeatureOverridesDirty(); - } - } - - /** Specify the IDs of a set of elements which should never be rendered within this view. - * @see [[neverDrawn]]. - */ - public setNeverDrawn(ids: Id64Set): void { - this._neverDrawn = ids; - this.setFeatureOverridesDirty(); - } - - /** Specify the IDs of a set of elements which should always be rendered within this view, regardless of category and subcategory visibility. - * @param ids The IDs of the elements to always draw. - * @param exclusive If true, *only* the specified elements will be drawn. - * @see [[alwaysDrawn]] - * @see [[isAlwaysDrawnExclusive]] - */ - public setAlwaysDrawn(ids: Id64Set, exclusive: boolean = false): void { - this._alwaysDrawn = ids; - this._alwaysDrawnExclusive = exclusive; - this.setFeatureOverridesDirty(); - } - /** Remove any [[SubCategoryOverride]] for the specified subcategory. - * @param id The ID of the subcategory. + * @param id The Id of the subcategory. * @see [[overrideSubCategory]] */ public dropSubCategoryOverride(id: Id64String) { @@ -358,7 +382,7 @@ public cancelAllTileLoads(): void { } /** Override the symbology of geometry belonging to a specific subcategory when rendered within this view. - * @param id The ID of the subcategory. + * @param id The Id of the subcategory. * @param ovr The symbology overrides to apply to all geometry belonging to the specified subcategory. * @see [[dropSubCategoryOverride]] */ @@ -368,7 +392,7 @@ public cancelAllTileLoads(): void { } /** Query the symbology overrides applied to geometry belonging to a specific subcategory when rendered within this view. - * @param id The ID of the subcategory. + * @param id The Id of the subcategory. * @return The symbology overrides applied to all geometry belonging to the specified subcategory, or undefined if no such overrides exist. * @see [[overrideSubCategory]] */ @@ -377,7 +401,7 @@ public cancelAllTileLoads(): void { /** Query the symbology with which geometry belonging to a specific subcategory is rendered within this view. * Every [[SubCategory]] defines a base symbology independent of any [[ViewState]]. * If a [[SubCategoryOverride]] has been applied to the subcategory within the context of this [[ViewState]], it will be applied to the subcategory's base symbology. - * @param id The ID of the subcategory. + * @param id The Id of the subcategory. * @return The symbology of the subcategory within this view, including any overrides. * @see [[overrideSubCategory]] */ @@ -403,14 +427,10 @@ public cancelAllTileLoads(): void { return !ovr.invisible; } - /** Returns true if the set of elements in the [[alwaysDrawn]] set are the *only* elements rendered within this view. */ - public get isAlwaysDrawnExclusive(): boolean { return this._alwaysDrawnExclusive; } - - /** - * Enable or disable display of elements belonging to a set of categories specified by ID. + /** Enable or disable display of elements belonging to a set of categories specified by Id. * Visibility of individual subcategories belonging to a category can be controlled separately through the use of [[SubCategoryOverride]]s. * By default, enabling display of a category does not affect display of subcategories thereof which have been overridden to be invisible. - * @param categories The ID(s) of the categories to which the change should be applied. No other categories will be affected. + * @param categories The Id(s) of the categories to which the change should be applied. No other categories will be affected. * @param display Whether or not elements on the specified categories should be displayed in the view. * @param enableAllSubCategories Specifies that when enabling display for a category, all of its subcategories should also be displayed even if they are overridden to be invisible. */ @@ -418,6 +438,7 @@ public cancelAllTileLoads(): void { if (display) { this.categorySelector.addCategories(categories); const categoryIds = Id64.toIdSet(categories); + this.subCategories.update(categoryIds, this.iModel).then(() => { // tslint:disable-line:no-floating-promises this.setFeatureOverridesDirty(); if (enableAllSubCategories) { @@ -487,7 +508,7 @@ public cancelAllTileLoads(): void { */ public abstract onRenderFrame(_viewport: Viewport): void; - /** Returns true if this view displays the contents of a [[Model]] specified by ID. */ + /** Returns true if this view displays the contents of a [[Model]] specified by Id. */ public abstract viewsModel(modelId: Id64String): boolean; /** Get the origin of this view in [[CoordSystem.World]] coordinates. */ @@ -649,8 +670,7 @@ public cancelAllTileLoads(): void { return { map: Map4d.createVectorFrustum(origin, xExtent, yExtent, zExtent, frustFraction), frustFraction }; } - /** - * Calculate the world coordinate Frustum from the parameters of this ViewState. + /** Calculate the world coordinate Frustum from the parameters of this ViewState. * @param result Optional Frustum to hold result. If undefined a new Frustum is created. * @returns The 8-point Frustum with the corners of this ViewState, or undefined if the parameters are invalid. */ @@ -664,8 +684,7 @@ public cancelAllTileLoads(): void { return box; } - /** - * Initialize the origin, extents, and rotation from an existing Frustum + /** Initialize the origin, extents, and rotation from an existing Frustum * This function is commonly used in the implementation of [[ViewTool]]s as follows: * 1. Obtain the ViewState's initial frustum. * 2. Modify the frustum based on user input. @@ -829,7 +848,7 @@ public cancelAllTileLoads(): void { return this._auxCoordSystem; } - /** Get the ID of the auxiliary coordinate system for this ViewState */ + /** Get the Id of the auxiliary coordinate system for this ViewState */ public getAuxiliaryCoordinateSystemId(): Id64String { return Id64.fromJSON(this.getDetail("acs")); } /** Set or clear the AuxiliaryCoordinateSystem for this view. @@ -959,8 +978,8 @@ public cancelAllTileLoads(): void { const x = JsonUtils.asInt(this.getDetail("gridSpaceX"), 1.0); return { x, y: JsonUtils.asInt(this.getDetail("gridSpaceY"), x) }; } - /** - * Change the volume that this view displays, keeping its current rotation. + + /** Change the volume that this view displays, keeping its current rotation. * @param volume The new volume, in world-coordinates, for the view. The resulting view will show all of worldVolume, by fitting a * view-axis-aligned bounding box around it. For views that are not aligned with the world coordinate system, this will sometimes * result in a much larger volume than worldVolume. @@ -976,8 +995,7 @@ public cancelAllTileLoads(): void { return this.lookAtViewAlignedVolume(Range3d.createArray(rangeBox), aspect, margin); } - /** - * look at a volume of space defined by a range in view local coordinates, keeping its current rotation. + /** Look at a volume of space defined by a range in view local coordinates, keeping its current rotation. * @param volume The new volume, in view-coordinates, for the view. The resulting view will show all of volume. * @param aspect The X/Y aspect ratio of the view into which the result will be displayed. If the aspect ratio of the volume does not * match aspect, the shorter axis is lengthened and the volume is centered. If aspect is undefined, no adjustment is made. @@ -1067,7 +1085,7 @@ public cancelAllTileLoads(): void { } private addModelToScene(model: TileTreeModelState, context: SceneContext): void { - model.loadTileTree(); + model.loadTileTree(context.viewFlags.edgesRequired(), this.scheduleScript ? this.scheduleScript.getModelAnimationId(model.treeModelId) : undefined); const tileTree = model.tileTree; if (undefined !== tileTree) { tileTree.drawScene(context); @@ -1080,7 +1098,7 @@ public cancelAllTileLoads(): void { if (classifier.isActive) { const classifierModel = this.iModel.models.getLoaded(classifier.modelId) as GeometricModelState; if (undefined !== classifierModel) { - classifierModel.loadTileTree(true, classifier.expand); + classifierModel.loadTileTree(false, undefined, true, classifier.expand); if (undefined !== classifierModel.classifierTileTree) classifierModel.classifierTileTree.drawScene(context); } @@ -1088,8 +1106,7 @@ public cancelAllTileLoads(): void { } } - /** - * Set the rotation of this ViewState to the supplied rotation, by rotating it about a point. + /** Set the rotation of this ViewState to the supplied rotation, by rotating it about a point. * @param rotation The new rotation matrix for this ViewState. * @param point The point to rotate about. If undefined, use the [[getTargetPoint]]. */ @@ -1113,6 +1130,7 @@ public cancelAllTileLoads(): void { /** Defines the state of a view of 3d models. * @see [ViewState Parameters]($docs/learning/frontend/views#viewstate-parameters) + * @public */ export abstract class ViewState3d extends ViewState { /** True if the camera is valid. */ @@ -1234,8 +1252,7 @@ export abstract class ViewState3d extends ViewState { public getDisplayStyle3d() { return this.displayStyle as DisplayStyle3dState; } - /** - * Turn the camera off for this view. After this call, the camera parameters in this view definition are ignored and views that use it will + /** Turn the camera off for this view. After this call, the camera parameters in this view definition are ignored and views that use it will * display with an orthographic (infinite focal length) projection of the view volume from the view direction. * @note To turn the camera back on, call #lookAt */ @@ -1259,8 +1276,7 @@ export abstract class ViewState3d extends ViewState { return this.getEyePoint().plusScaled(viewZ, -1.0 * this.getFocusDistance(), result); } - /** - * Position the camera for this view and point it at a new target point. + /** Position the camera for this view and point it at a new target point. * @param eyePoint The new location of the camera. * @param targetPoint The new location to which the camera should point. This becomes the center of the view on the focus plane. * @param upVector A vector that orients the camera's "up" (view y). This vector must not be parallel to the vector from eye to target. @@ -1336,15 +1352,14 @@ export abstract class ViewState3d extends ViewState { return ViewStatus.Success; } - /** - * Position the camera for this view and point it at a new target point, using a specified lens angle. + /** Position the camera for this view and point it at a new target point, using a specified lens angle. * @param eyePoint The new location of the camera. * @param targetPoint The new location to which the camera should point. This becomes the center of the view on the focus plane. * @param upVector A vector that orients the camera's "up" (view y). This vector must not be parallel to the vector from eye to target. * @param fov The angle, in radians, that defines the field-of-view for the camera. Must be between .0001 and pi. * @param frontDistance The distance from the eyePoint to the front plane. If undefined, the existing front distance is used. * @param backDistance The distance from the eyePoint to the back plane. If undefined, the existing back distance is used. - * @returns Status indicating whether the camera was successfully positioned. See values at [[ViewStatus]] for possible errors. + * @returns [[ViewStatus]] indicating whether the camera was successfully positioned. * @note The aspect ratio of the view remains unchanged. */ public lookAtUsingLensAngle(eyePoint: Point3d, targetPoint: Point3d, upVector: Vector3d, fov: Angle, frontDistance?: number, backDistance?: number): ViewStatus { @@ -1364,8 +1379,7 @@ export abstract class ViewState3d extends ViewState { return this.lookAt(eyePoint, targetPoint, upVector, delta, frontDistance, backDistance); } - /** - * Move the camera relative to its current location by a distance in camera coordinates. + /** Move the camera relative to its current location by a distance in camera coordinates. * @param distance to move camera. Length is in world units, direction relative to current camera orientation. * @returns Status indicating whether the camera was successfully positioned. See values at [[ViewStatus]] for possible errors. */ @@ -1374,8 +1388,7 @@ export abstract class ViewState3d extends ViewState { return this.moveCameraWorld(distWorld); } - /** - * Move the camera relative to its current location by a distance in world coordinates. + /** Move the camera relative to its current location by a distance in world coordinates. * @param distance in world units. * @returns Status indicating whether the camera was successfully positioned. See values at [[ViewStatus]] for possible errors. */ @@ -1390,8 +1403,7 @@ export abstract class ViewState3d extends ViewState { return this.lookAt(newEyePt, newTarget, this.getYVector()); } - /** - * Rotate the camera from its current location about an axis relative to its current orientation. + /** Rotate the camera from its current location about an axis relative to its current orientation. * @param angle The angle to rotate the camera. * @param axis The axis about which to rotate the camera. The axis is a direction relative to the current camera orientation. * @param aboutPt The point, in world coordinates, about which the camera is rotated. If aboutPt is undefined, the camera rotates in place @@ -1404,8 +1416,7 @@ export abstract class ViewState3d extends ViewState { return this.rotateCameraWorld(angle, axisWorld, aboutPt); } - /** - * Rotate the camera from its current location about an axis in world coordinates. + /** Rotate the camera from its current location about an axis in world coordinates. * @param angle The angle to rotate the camera. * @param axis The world-based axis (direction) about which to rotate the camera. * @param aboutPt The point, in world coordinates, about which the camera is rotated. If aboutPt is undefined, the camera rotates in place @@ -1434,8 +1445,7 @@ export abstract class ViewState3d extends ViewState { return eyeOrg.z; } - /** - * Place the eyepoint of the camera so it is aligned with the center of the view. This removes any 1-point perspective skewing that may be + /** Place the eyepoint of the camera so it is aligned with the center of the view. This removes any 1-point perspective skewing that may be * present in the current view. * @param backDistance If defined, the new the distance from the eyepoint to the back plane. Otherwise the distance from the * current eyepoint is used. @@ -1553,7 +1563,7 @@ export abstract class ViewState3d extends ViewState { /** Return the ground extents, which will originate either from the viewport frustum or the extents of the imodel. */ public getGroundExtents(vp?: Viewport): AxisAlignedBox3d { const displayStyle = this.getDisplayStyle3d(); - const extents = new AxisAlignedBox3d(); + const extents = new Range3d(); if (!displayStyle.environment.ground.display) return extents; // Ground plane is not enabled @@ -1591,9 +1601,8 @@ export abstract class ViewState3d extends ViewState { /** @hidden */ protected drawGroundPlane(context: DecorateContext): void { const extents = this.getGroundExtents(context.viewport); - if (extents.isNull) { + if (extents.isNull) return; - } const ground = this.getDisplayStyle3d().environment.ground; if (!ground.display) @@ -1649,6 +1658,7 @@ export abstract class ViewState3d extends ViewState { /** Defines a view of one or more SpatialModels. * The list of viewed models is stored by the ModelSelector. + * @public */ export class SpatialViewState extends ViewState3d { public modelSelector: ModelSelectorState; @@ -1669,7 +1679,7 @@ export class SpatialViewState extends ViewState3d { this.modelSelector = arg3.modelSelector.clone(); } } - public equals(other: SpatialViewState): boolean { return super.equals(other) && this.modelSelector.equals(other.modelSelector); } + public equals(other: this): boolean { return super.equals(other) && this.modelSelector.equals(other.modelSelector); } public equalState(other: SpatialViewState): boolean { if (!super.equalState(other)) @@ -1687,11 +1697,16 @@ export class SpatialViewState extends ViewState3d { public computeFitRange(): AxisAlignedBox3d { // Loop over the current models in the model selector with loaded tile trees and union their ranges - const range = new AxisAlignedBox3d(); + const range = new Range3d(); this.forEachTileTreeModel((model: TileTreeModelState) => { // ...if we don't want to fit context reality mdoels this should cal forEachSpatialTileTreeModel... const tileTree = model.tileTree; - if (tileTree !== undefined && tileTree.rootTile !== undefined) { // can we assume that a loaded model - range.extendRange(tileTree.rootTile.computeWorldContentRange()); + if (tileTree !== undefined && tileTree.rootTile !== undefined) { + const contentRange = tileTree.rootTile.computeWorldContentRange(); + assert(!contentRange.isNull); + assert(contentRange.intersectsRange(this.iModel.projectExtents)); + + range.extendRange(contentRange); + } }); @@ -1704,7 +1719,7 @@ export class SpatialViewState extends ViewState3d { } public getViewedExtents(): AxisAlignedBox3d { - const extents = AxisAlignedBox3d.fromJSON(this.iModel.projectExtents); + const extents = Range3d.fromJSON(this.iModel.projectExtents); extents.scaleAboutCenterInPlace(1.0001); // projectExtents. lying smack up against the extents is not excluded by frustum... extents.extendRange(this.getGroundExtents()); return extents; @@ -1731,18 +1746,13 @@ export class SpatialViewState extends ViewState3d { } public forEachTileTreeModel(func: (model: TileTreeModelState) => void): void { this.displayStyle.forEachContextRealityModel((model: TileTreeModelState) => func(model)); - this.forEachSpatialTileTreeModel((model: TileTreeModelState) => func(model)); - } - - public forEachSpatialTileTreeModel(func: (model: TileTreeModelState) => void): void { - if (this.scheduleScript && this.scheduleScript.containsAnimation) - this.scheduleScript.forEachAnimationModel((model: TileTreeModelState) => func(model)); - else - this.forEachModel((model: GeometricModelState) => func(model)); + this.forEachModel((model: TileTreeModelState) => func(model)); } } -/** Defines a spatial view that displays geometry on the image plane using a parallel orthographic projection. */ +/** Defines a spatial view that displays geometry on the image plane using a parallel orthographic projection. + * @public + */ export class OrthographicViewState extends SpatialViewState { public static get className() { return "OrthographicViewDefinition"; } @@ -1751,7 +1761,9 @@ export class OrthographicViewState extends SpatialViewState { public supportsCamera(): boolean { return false; } } -/** Defines the state of a view of a single 2d model. */ +/** Defines the state of a view of a single 2d model. + * @public + */ export abstract class ViewState2d extends ViewState { public readonly origin: Point2d; public readonly delta: Point2d; @@ -1799,15 +1811,15 @@ export abstract class ViewState2d extends ViewState { if (undefined === this._viewedExtents) { const model = this.iModel.models.getLoaded(this.baseModelId); if (undefined !== model && model.isGeometricModel) { - const tree = (model as GeometricModelState).getOrLoadTileTree(); + const tree = (model as GeometricModelState).getOrLoadTileTree(true); if (undefined !== tree) { - this._viewedExtents = new AxisAlignedBox3d(tree.range.low, tree.range.high); + this._viewedExtents = Range3d.create(tree.range.low, tree.range.high); tree.location.multiplyRange(this._viewedExtents, this._viewedExtents); } } } - return undefined !== this._viewedExtents ? this._viewedExtents : new AxisAlignedBox3d(); + return undefined !== this._viewedExtents ? this._viewedExtents : new Range3d(); } public onRenderFrame(_viewport: Viewport): void { } @@ -1833,7 +1845,9 @@ export abstract class ViewState2d extends ViewState { public createAuxCoordSystem(acsName: string): AuxCoordSystemState { return AuxCoordSystem2dState.createNew(acsName, this.iModel); } } -/** A view of a DrawingModel */ +/** A view of a DrawingModel + * @public + */ export class DrawingViewState extends ViewState2d { public static createFromProps(props: ViewStateProps, iModel: IModelConnection): ViewState | undefined { const cat = new CategorySelectorState(props.categorySelectorProps, iModel); diff --git a/core/frontend/src/Viewport.ts b/core/frontend/src/Viewport.ts index bf41cd7..796eb47 100644 --- a/core/frontend/src/Viewport.ts +++ b/core/frontend/src/Viewport.ts @@ -4,15 +4,14 @@ *--------------------------------------------------------------------------------------------*/ /** @module Views */ -import { assert, BeDuration, BeEvent, BeTimePoint, dispose, Id64, Id64Arg, IDisposable, StopWatch } from "@bentley/bentleyjs-core"; +import { assert, BeDuration, BeEvent, BeTimePoint, dispose, Id64, Id64Arg, IDisposable, StopWatch, Id64Set } from "@bentley/bentleyjs-core"; import { - Angle, AngleSweep, Arc3d, AxisOrder, Constant, LowAndHighXY, LowAndHighXYZ, Map4d, Matrix3d, - Plane3dByOriginAndUnitNormal, Point2d, Point3d, Point4d, Range3d, Ray3d, Transform, Vector3d, XAndY, - XYAndZ, XYZ, Geometry, + Angle, AngleSweep, Arc3d, AxisOrder, Constant, Geometry, LowAndHighXY, LowAndHighXYZ, Map4d, Matrix3d, + Plane3dByOriginAndUnitNormal, Point2d, Point3d, Point4d, Range3d, Ray3d, Transform, Vector3d, XAndY, XYAndZ, XYZ, SmoothTransformBetweenFrusta, } from "@bentley/geometry-core"; import { - AnalysisStyle, AntiAliasPref, Camera, ColorDef, ElementProps, Frustum, Hilite, ImageBuffer, Npc, - NpcCenter, NpcCorners, Placement2d, Placement2dProps, Placement3d, PlacementProps, ViewFlags, + AnalysisStyle, AntiAliasPref, Camera, ColorDef, ElementProps, Frustum, Hilite, ImageBuffer, Npc, NpcCenter, + NpcCorners, Placement2d, Placement2dProps, Placement3d, PlacementProps, ViewFlags, } from "@bentley/imodeljs-common"; import { AuxCoordSystemState } from "./AuxCoordSys"; import { ElementPicker, LocateOptions } from "./ElementLocateManager"; @@ -24,16 +23,19 @@ import { FeatureSymbology } from "./render/FeatureSymbology"; import { GraphicType } from "./render/GraphicBuilder"; import { Decorations, GraphicList, Pixel, RenderPlan, RenderTarget } from "./render/System"; import { StandardView, StandardViewId } from "./StandardView"; +import { Tile } from "./tile/TileTree"; import { EventController } from "./tools/EventController"; import { ToolSettings } from "./tools/ToolAdmin"; import { DecorateContext, SceneContext } from "./ViewContext"; import { GridOrientationType, MarginPercent, ViewState, ViewStatus } from "./ViewState"; -import { Tile } from "./tile/TileTree"; -/** A function which customizes the appearance of Features within a Viewport. - * @see [[Viewport.addFeatureOverrides]] +/** An object which customizes the appearance of Features within a [[Viewport]]. + * Only one FeatureOverrideProvider may be associated with a viewport at a time. Setting a new FeatureOverrideProvider replaces any existing provider. + * @see [[Viewport.featureOverrideProvider]] */ -export type AddFeatureOverrides = (overrides: FeatureSymbology.Overrides, viewport: Viewport) => void; +export interface FeatureOverrideProvider { + addFeatureOverrides(overrides: FeatureSymbology.Overrides, viewport: Viewport): void; +} /** Viewport synchronization flags. Synchronization is handled internally - do not use directly. * @hidden @@ -54,7 +56,7 @@ export class SyncFlags { public get isValidAnimationFraction(): boolean { return this._animationFraction; } public get isRedrawPending(): boolean { return this._redrawPending; } public invalidateDecorations(): void { this._decorations = false; } - public invalidateScene(): void { this._scene = false; this.invalidateDecorations(); } + public invalidateScene(): void { this._scene = false; this.invalidateDecorations(); this.invalidateAnimationFraction(); } public invalidateRenderPlan(): void { this._renderPlan = false; this.invalidateScene(); } public invalidateController(): void { this._controller = false; this.invalidateRenderPlan(); } public invalidateRotatePoint(): void { this._rotatePoint = false; } @@ -73,6 +75,7 @@ export class SyncFlags { /** A rectangle in integer view coordinates with (0,0) corresponding to the top-left corner of the view. * * Increasing **x** moves from left to right, and increasing **y** moves from top to bottom. + * @public */ export class ViewRect { private _left!: number; @@ -212,8 +215,7 @@ export class ViewRect { } } -/** - * The minimum and maximum values for the z-depth of a rectangle of screen space. +/** The minimum and maximum values for the z-depth of a rectangle of screen space. * * Values are in [[CoordSystem.Npc]] so they will be between 0 and 1.0. */ @@ -228,23 +230,22 @@ export class DepthRangeNpc { public middle(): number { return this.minimum + ((this.maximum - this.minimum) / 2.0); } } -/** Coordinate system types */ +/** Coordinate system types + * @public + */ export const enum CoordSystem { - /** - * Coordinates are relative to the origin of the viewing rectangle. + /** Coordinates are relative to the origin of the viewing rectangle. * x and y values correspond to pixels within that rectangle, with (x=0,y=0) corresponding to the top-left corner. */ View, - /** - * Coordinates are in [Normalized Plane Coordinates]($docs/learning/glossary.md#npc). NPC is a coordinate system + /** Coordinates are in [Normalized Plane Coordinates]($docs/learning/glossary.md#npc). NPC is a coordinate system * for frustums in which each dimension [x,y,z] is normalized to hold values between 0.0 and 1.0. * [0,0,0] corresponds to the left-bottom-rear and [1,1,1] to the right-top-front of the frustum. */ Npc, - /** - * Coordinates are in the coordinate system of the models in the view. For SpatialViews, this is the iModel's spatial coordinate system. + /** Coordinates are in the coordinate system of the models in the view. For SpatialViews, this is the iModel's spatial coordinate system. * For 2d views, it is the coordinate system of the GeometricModel2d that the view shows. */ World, @@ -256,6 +257,7 @@ export const enum CoordSystem { class Animator { private readonly _currFrustum = new Frustum(); private _startTime?: BeTimePoint; + private _interpolator?: SmoothTransformBetweenFrusta; private moveToTime(time: number) { this.interpolateFrustum(time / this.totalTime.milliseconds); } /** Construct a new Animator. @@ -264,11 +266,12 @@ class Animator { * @param startFrustum The Viewport's starting Frustum at the beginning of the animation. * @param endFrustum The Viewport's ending Frustum after the animation. */ - public constructor(public totalTime: BeDuration, public viewport: Viewport, public startFrustum: Frustum, public endFrustum: Frustum) { } + public constructor(public totalTime: BeDuration, public viewport: Viewport, public startFrustum: Frustum, public endFrustum: Frustum) { + this._interpolator = SmoothTransformBetweenFrusta.create(startFrustum.points, endFrustum.points); + } private interpolateFrustum(fraction: number): void { - for (let i = 0; i < Npc.CORNER_COUNT; ++i) - this.startFrustum.points[i].interpolate(fraction, this.endFrustum.points[i], this._currFrustum.points[i]); + this._interpolator!.fractionToWorldCorners(fraction, this._currFrustum.points); this.viewport.setupViewFromFrustum(this._currFrustum); } @@ -277,6 +280,11 @@ class Animator { * @return true when finished to terminate the animation. */ public animate(): boolean { + if (!this._interpolator) { + this.viewport.setupViewFromFrustum(this.endFrustum); + return true; + } + const currTime = BeTimePoint.now(); if (!this._startTime) this._startTime = currTime; @@ -310,8 +318,7 @@ class Animator { /** Status for [[ViewportAnimator.animate]]. */ export const enum RemoveMe { No = 0, Yes = 1 } -/** - * An object to animate a transition of a [[Viewport]]. +/** An object to animate a transition of a [[Viewport]]. * Only one animator may be associated with a viewport at a time. Registering a new * animator replaces any existing animator. * The animator's animate() function will be invoked just prior to the rendering of each frame. @@ -323,15 +330,13 @@ export interface ViewportAnimator { /** Apply animation to the viewport. Return `RemoveMe.Yes` when animation is completed, causing the animator to be removed from the viewport. */ animate(viewport: Viewport): RemoveMe; - /** - * Invoked when this ViewportAnimator is removed from the viewport, e.g. because it was replaced by a new animator, the viewport was closed - + /** Invoked when this ViewportAnimator is removed from the viewport, e.g. because it was replaced by a new animator, the viewport was closed - * that is, for any reason other than returning RemoveMe.Yes from animate() */ onInterrupted(viewport: Viewport): void; } -/** - * A ViewportAnimator that animates decorations. While the animator is +/** A ViewportAnimator that animates decorations. While the animator is * active, decorations will be invalidated on each frame. The animator's * animateDecorations() function will be invoked to update any animation state; then * decorations will be re-requested and rendered. @@ -345,8 +350,7 @@ export class DecorationAnimator implements ViewportAnimator { this._stop = this._start.plus(duration); } - /** - * Override to update animation state, which can then be used on the next call to produce decorations. + /** Override to update animation state, which can then be used on the next call to produce decorations. * @param viewport The viewport being animated * @param durationPercent The ratio of duration elapsed, in [0.0,1.0] * @returns RemoveMe.Yes to immediately remove this animator, RemoveMe::No to continue animating until duration elapsed or animator interrupted. @@ -734,8 +738,7 @@ export class ViewFrustum { for (const p of pts) corners.fractionToPoint(p.x, p.y, p.z, p); } - /** - * Convert a point from CoordSystem.View to CoordSystem.Npc + /** Convert a point from CoordSystem.View to CoordSystem.Npc * @param pt the point to convert * @param out optional location for result. If undefined, a new Point3d is created. */ @@ -745,8 +748,7 @@ export class ViewFrustum { Transform.initFromRange(corners.low, corners.high, undefined, scrToNpcTran); return scrToNpcTran.multiplyPoint3d(pt, out); } - /** - * Convert a point from CoordSystem.Npc to CoordSystem.View + /** Convert a point from CoordSystem.Npc to CoordSystem.View * @param pt the point to convert * @param out optional location for result. If undefined, a new Point3d is created. */ @@ -803,8 +805,7 @@ export class ViewFrustum { */ public view4dToWorld(input: Point4d, out?: Point3d): Point3d { return this.worldToViewMap.transform1.multiplyXYZWQuietRenormalize(input.x, input.y, input.z, input.w, out); } - /** - * Get an 8-point Frustum corresponding to the 8 corners of the Viewport in the specified coordinate system. + /** Get an 8-point Frustum corresponding to the 8 corners of the Viewport in the specified coordinate system. * * There are two sets of corners that may be of interest. * The "adjusted" box is the one that is computed by examining the "viewed extents" and moving @@ -857,8 +858,7 @@ export class ViewFrustum { } } -/** - * A Viewport renders the contents of one or more Models onto an `HTMLCanvasElement`. +/** A Viewport renders the contents of one or more Models onto an `HTMLCanvasElement`. * * It holds a [[ViewState]] object that defines its viewing parameters. [[ViewTool]]s may * modify the ViewState object. Changes to the ViewState are only reflected in a Viewport after the @@ -868,10 +868,15 @@ export class ViewFrustum { * for undo/redo (i.e. *View Previous* and *View Next*) of viewing tools. * * @see [[ViewManager]] + * @public */ export abstract class Viewport implements IDisposable { /** Event called whenever this viewport is synchronized with its ViewState. */ public readonly onViewChanged = new BeEvent<(vp: Viewport) => void>(); + /** Event called whenever this viewport's set of always-drawn elements changes. */ + public readonly onAlwaysDrawnChanged = new BeEvent<(vp: Viewport) => void>(); + /** Event called whenever this viewport's set of never-drawn elements changes. */ + public readonly onNeverDrawnChanged = new BeEvent<(vp: Viewport) => void>(); private readonly _viewportId: number; private _animationFraction = 0.0; @@ -922,11 +927,15 @@ export abstract class Viewport implements IDisposable { public static undoDelay = BeDuration.fromSeconds(.5); private static _nextViewportId = 1; - private _addFeatureOverrides?: AddFeatureOverrides; private _debugBoundingBoxes: Tile.DebugBoundingBoxes = Tile.DebugBoundingBoxes.None; private _freezeScene = false; private _viewFrustum!: ViewFrustum; private _target?: RenderTarget; + private _fadeOutActive = false; + private _neverDrawn?: Id64Set; + private _alwaysDrawn?: Id64Set; + private _alwaysDrawnExclusive: boolean = false; + private _featureOverrideProvider?: FeatureOverrideProvider; /** @hidden */ public get viewFrustum(): ViewFrustum { return this._viewFrustum; } @@ -964,8 +973,7 @@ export abstract class Viewport implements IDisposable { /** The settings that control how elements are hilited in this Viewport. */ public hilite = new Hilite.Settings(); - /** - * Determine whether the Grid display is currently enabled in this Viewport. + /** Determine whether the Grid display is currently enabled in this Viewport. * @return true if the grid display is on. */ public get isGridOn(): boolean { return this.viewFlags.grid; } @@ -976,8 +984,7 @@ export abstract class Viewport implements IDisposable { /** @hidden */ public get wantAntiAliasText(): AntiAliasPref { return AntiAliasPref.Detect; } - /** - * Determines what type (if any) of debug graphics will be displayed to visualize [[Tile]] volumes. + /** Determines what type (if any) of debug graphics will be displayed to visualize [[Tile]] volumes. * @see [[Tile.DebugBoundingBoxes]] */ public get debugBoundingBoxes(): Tile.DebugBoundingBoxes { return this._debugBoundingBoxes; } @@ -999,7 +1006,7 @@ export abstract class Viewport implements IDisposable { } /** @hidden */ - public get AnalysisStyle(): AnalysisStyle | undefined { return this.view.AnalysisStyle; } + public get analysisStyle(): AnalysisStyle | undefined { return this.view.analysisStyle; } /** The iModel of this Viewport */ public get iModel(): IModelConnection { return this.view.iModel; } /** @hidden */ @@ -1009,6 +1016,18 @@ export abstract class Viewport implements IDisposable { /** @hidden */ public get isContextRotationRequired(): boolean { return IModelApp.toolAdmin.acsContextLock; } + /** Enables or disables "fade-out" mode. When this mode is enabled, transparent graphics are rendered with a flat alpha weight, + * causing them to appear de-emphasized. This is typically used in contexts in which a handful of elements are to be emphasized in the view, + * while the rest of the graphics are drawn transparently. + */ + public get isFadeOutActive(): boolean { return this._fadeOutActive; } + public set isFadeOutActive(active: boolean) { + if (active !== this._fadeOutActive) { + this._fadeOutActive = active; + this.invalidateRenderPlan(); + } + } + /** @hidden */ protected constructor(target: RenderTarget) { this._target = target; @@ -1037,19 +1056,86 @@ export abstract class Viewport implements IDisposable { /** @hidden */ public get backgroundMapPlane() { return this.view.displayStyle.backgroundMapPlane; } - /** - * Sets a function which can customize the appearance of [[Feature]]s within a viewport. - * If defined, this function will be invoked whenever the overrides are determined to need updating. + /** IDs of a set of elements which should not be rendered within this view. + * @note Do not modify this set directly - use [[setNeverDrawn]] or [[clearNeverDrawn]] instead. + * @note This set takes precedence over the [[alwaysDrawn]] set - if an element is present in both sets, it is never drawn. + */ + public get neverDrawn(): Id64Set | undefined { return this._neverDrawn; } + + /** IDs of a set of elements which should always be rendered within this view, regardless of category and subcategory visibility. + * If the [[isAlwaysDrawnExclusive]] flag is also set, *only* those elements in this set will be drawn. + * @note Do not modify this set directly - use [[setAlwaysDrawn]] or [[clearAlwaysDrawn]] instead. + * @note The [[neverDrawn]] set takes precedence - if an element is present in both sets, it is never drawn. + */ + public get alwaysDrawn(): Id64Set | undefined { return this._alwaysDrawn; } + + /** Clear the set of always-drawn elements. + * @see [[alwaysDrawn]] + */ + public clearAlwaysDrawn(): void { + if ((undefined !== this.alwaysDrawn && 0 < this.alwaysDrawn.size) || this._alwaysDrawnExclusive) { + if (undefined !== this.alwaysDrawn) + this.alwaysDrawn.clear(); + + this._alwaysDrawnExclusive = false; + this.view.setFeatureOverridesDirty(); + this.onAlwaysDrawnChanged.raiseEvent(this); + } + } + + /** Clear the set of never-drawn elements. + * @see [[neverDrawn]] + */ + public clearNeverDrawn(): void { + if (undefined !== this.neverDrawn && 0 < this.neverDrawn.size) { + this.neverDrawn.clear(); + this.view.setFeatureOverridesDirty(); + this.onNeverDrawnChanged.raiseEvent(this); + } + } + + /** Specify the IDs of a set of elements which should never be rendered within this view. + * @see [[neverDrawn]]. + */ + public setNeverDrawn(ids: Id64Set): void { + this._neverDrawn = ids; + this.view.setFeatureOverridesDirty(); + this.onNeverDrawnChanged.raiseEvent(this); + } + + /** Specify the IDs of a set of elements which should always be rendered within this view, regardless of category and subcategory visibility. + * @param ids The IDs of the elements to always draw. + * @param exclusive If true, *only* the specified elements will be drawn. + * @see [[alwaysDrawn]] + * @see [[isAlwaysDrawnExclusive]] + */ + public setAlwaysDrawn(ids: Id64Set, exclusive: boolean = false): void { + this._alwaysDrawn = ids; + this._alwaysDrawnExclusive = exclusive; + this.view.setFeatureOverridesDirty(); + this.onAlwaysDrawnChanged.raiseEvent(this); + } + + /** Returns true if the set of elements in the [[alwaysDrawn]] set are the *only* elements rendered within this view. */ + public get isAlwaysDrawnExclusive(): boolean { return this._alwaysDrawnExclusive; } + + /** Sets an object which can customize the appearance of [[Feature]]s within a viewport. + * If defined, the provider will be invoked whenever the overrides are determined to need updating. * The overrides can be explicitly marked as needing a refresh by calling [[ViewState.setFeatureOverridesDirty]]. * @see [[FeatureSymbology.Overrides]] */ - public set addFeatureOverrides(addFeatureOverrides: AddFeatureOverrides | undefined) { - if (addFeatureOverrides !== this._addFeatureOverrides) { - this._addFeatureOverrides = addFeatureOverrides; + public set featureOverrideProvider(provider: FeatureOverrideProvider | undefined) { + if (provider !== this._featureOverrideProvider) { + this._featureOverrideProvider = provider; this.view.setFeatureOverridesDirty(true); } } + /** Get the current FeatureOverrideProvider for this viewport if defined. */ + public get featureOverrideProvider(): FeatureOverrideProvider | undefined { + return this._featureOverrideProvider; + } + /** True if this is a 3d view with the camera turned on. */ public get isCameraOn(): boolean { return this.view.is3d() && this.view.isCameraOn; } /** @hidden */ @@ -1089,8 +1175,7 @@ export abstract class Viewport implements IDisposable { /** @hidden */ public fromView(from: XYZ, to?: XYZ) { this._viewFrustum.fromView(from, to); } - /** - * Change the ViewState of this Viewport + /** Change the ViewState of this Viewport * @param view a fully loaded (see discussion at [[ViewState.load]] ) ViewState */ public changeView(view: ViewState) { @@ -1103,8 +1188,7 @@ export abstract class Viewport implements IDisposable { /** @hidden */ public invalidateScene(): void { this.sync.invalidateScene(); } - /** - * Computes the range of npc depth values for a region of the screen + /** Computes the range of npc depth values for a region of the screen * @param rect the rectangle to test. If undefined, test entire view * @param result optional DepthRangeNpc to store the result * @returns the minimum and maximum depth values within the region, or undefined. @@ -1232,14 +1316,12 @@ export abstract class Viewport implements IDisposable { public viewToNpcArray(pts: Point3d[]): void { this._viewFrustum.viewToNpcArray(pts); } /** Convert an array of points from CoordSystem.Npc to CoordSystem.View */ public npcToViewArray(pts: Point3d[]): void { this._viewFrustum.npcToViewArray(pts); } - /** - * Convert a point from CoordSystem.View to CoordSystem.Npc + /** Convert a point from CoordSystem.View to CoordSystem.Npc * @param pt the point to convert * @param out optional location for result. If undefined, a new Point3d is created. */ public viewToNpc(pt: Point3d, out?: Point3d): Point3d { return this._viewFrustum.viewToNpc(pt, out); } - /** - * Convert a point from CoordSystem.Npc to CoordSystem.View + /** Convert a point from CoordSystem.Npc to CoordSystem.View * @param pt the point to convert * @param out optional location for result. If undefined, a new Point3d is created. */ @@ -1256,38 +1338,32 @@ export abstract class Viewport implements IDisposable { public viewToWorldArray(pts: Point3d[]) { this._viewFrustum.viewToWorldArray(pts); } /** Convert an array of points from CoordSystem.View as Point4ds to CoordSystem.World */ public view4dToWorldArray(viewPts: Point4d[], worldPts: Point3d[]): void { this._viewFrustum.view4dToWorldArray(viewPts, worldPts); } - /** - * Convert a point from CoordSystem.World to CoordSystem.Npc + /** Convert a point from CoordSystem.World to CoordSystem.Npc * @param pt the point to convert * @param out optional location for result. If undefined, a new Point3d is created. */ public worldToNpc(pt: XYAndZ, out?: Point3d): Point3d { return this._viewFrustum.worldToNpc(pt, out); } - /** - * Convert a point from CoordSystem.Npc to CoordSystem.World + /** Convert a point from CoordSystem.Npc to CoordSystem.World * @param pt the point to convert * @param out optional location for result. If undefined, a new Point3d is created. */ public npcToWorld(pt: XYAndZ, out?: Point3d): Point3d { return this._viewFrustum.npcToWorld(pt, out); } - /** - * Convert a point from CoordSystem.World to CoordSystem.View + /** Convert a point from CoordSystem.World to CoordSystem.View * @param pt the point to convert * @param out optional location for result. If undefined, a new Point3d is created. */ public worldToView(input: XYAndZ, out?: Point3d): Point3d { return this._viewFrustum.worldToView(input, out); } - /** - * Convert a point from CoordSystem.World to CoordSystem.View as Point4d + /** Convert a point from CoordSystem.World to CoordSystem.View as Point4d * @param input the point to convert * @param out optional location for result. If undefined, a new Point4d is created. */ public worldToView4d(input: XYAndZ, out?: Point4d): Point4d { return this._viewFrustum.worldToView4d(input, out); } - /** - * Convert a point from CoordSystem.View to CoordSystem.World + /** Convert a point from CoordSystem.View to CoordSystem.World * @param pt the point to convert * @param out optional location for result. If undefined, a new Point3d is created. */ public viewToWorld(input: XYAndZ, out?: Point3d): Point3d { return this._viewFrustum.viewToWorld(input, out); } - /** - * Convert a point from CoordSystem.View as a Point4d to CoordSystem.View + /** Convert a point from CoordSystem.View as a Point4d to CoordSystem.View * @param input the point to convert * @param out optional location for result. If undefined, a new Point3d is created. */ @@ -1300,8 +1376,7 @@ export abstract class Viewport implements IDisposable { */ public pixelsFromInches(inches: number): number { return inches * this.pixelsPerInch; } - /** - * Get an 8-point Frustum corresponding to the 8 corners of the Viewport in the specified coordinate system. + /** Get an 8-point Frustum corresponding to the 8 corners of the Viewport in the specified coordinate system. * * There are two sets of corners that may be of interest. * The "adjusted" box is the one that is computed by examining the "viewed extents" and moving @@ -1325,8 +1400,7 @@ export abstract class Viewport implements IDisposable { this.animateFrustumChange(startFrust, this.getFrustum(), options.animationTime); } - /** - * Scroll the view by a given number of pixels. + /** Scroll the view by a given number of pixels. * @param screenDist distance to scroll, in pixels */ public scroll(screenDist: Point2d, options?: ViewChangeOptions) { @@ -1353,8 +1427,7 @@ export abstract class Viewport implements IDisposable { this.finishViewChange(startFrust, options); } - /** - * Zoom the view by a scale factor, placing the new center at the projection of the given point (world coordinates) + /** Zoom the view by a scale factor, placing the new center at the projection of the given point (world coordinates) * on the focal plane. * Updates ViewState and re-synchs Viewport. Does not save in view undo buffer. */ @@ -1410,8 +1483,7 @@ export abstract class Viewport implements IDisposable { this.finishViewChange(startFrust, options); } - /** - * Zoom the view to a show the tightest box around a given set of PlacementProps. Optionally, change view rotation. + /** Zoom the view to a show the tightest box around a given set of PlacementProps. Optionally, change view rotation. * @param props array of PlacementProps. Will zoom to the union of the placements. * @param options options that control how the view change works and whether to change view rotation. */ @@ -1446,8 +1518,7 @@ export abstract class Viewport implements IDisposable { this.finishViewChange(this.getFrustum().clone(), options); } - /** - * Zoom the view to a show the tightest box around a given set of ElementProps. Optionally, change view rotation. + /** Zoom the view to a show the tightest box around a given set of ElementProps. Optionally, change view rotation. * @param props element props. Will zoom to the union of the placements. * @param options options that control how the view change works and whether to change view rotation. */ @@ -1462,8 +1533,7 @@ export abstract class Viewport implements IDisposable { this.zoomToPlacementProps(placementProps, options); } - /** - * Zoom the view to a show the tightest box around a given set of elements. Optionally, change view rotation. + /** Zoom the view to a show the tightest box around a given set of elements. Optionally, change view rotation. * @param ids the element id(s) to include. Will zoom to the union of the placements. * @param options options that control how the view change works and whether to change view rotation. */ @@ -1471,8 +1541,7 @@ export abstract class Viewport implements IDisposable { this.zoomToElementProps(await this.iModel.elements.getProps(ids), options); } - /** - * Zoom the view to a volume of space in world coordinates. + /** Zoom the view to a volume of space in world coordinates. * @param volume The low and high corners, in world coordinates. * @param options options that control how the view change works */ @@ -1481,8 +1550,7 @@ export abstract class Viewport implements IDisposable { this.finishViewChange(this.getFrustum().clone(), options); } - /** - * Shortcut to call view.setupFromFrustum and then [[setupFromView]] + /** Shortcut to call view.setupFromFrustum and then [[setupFromView]] * @param inFrustum the new viewing frustum * @returns true if both steps were successful */ @@ -1523,7 +1591,7 @@ export abstract class Viewport implements IDisposable { /** @hidden */ public applyViewState(val: ViewState, animationTime?: BeDuration) { const startFrust = this.getFrustum(); - this._viewFrustum.view = val.clone(); + this._viewFrustum.view = val.clone(this.view.iModel); // preserve our iModel in case val is coming from a different connection this.synchWithView(false); if (animationTime) this.animateFrustumChange(startFrust, this.getFrustum(), animationTime); @@ -1613,8 +1681,7 @@ export abstract class Viewport implements IDisposable { this.pointToStandardGrid(point, rMatrix, origin); } - /** - * Get the width of a pixel (a unit vector in the x direction in view coordinates) at a given point in world coordinates, returning the result in meters (world units). + /** Get the width of a pixel (a unit vector in the x direction in view coordinates) at a given point in world coordinates, returning the result in meters (world units). * * This is most useful to determine how large something is in a view. In particular, in a perspective view * the result of this method will be a larger number for points closer to the back of the view Frustum (that is, @@ -1669,8 +1736,7 @@ export abstract class Viewport implements IDisposable { /** @hidden */ public createSceneContext(): SceneContext { return new SceneContext(this); } - /** - * Called when the visible contents of the viewport are redrawn. + /** Called when the visible contents of the viewport are redrawn. * @note Due to the frequency of this event, avoid performing expensive work inside event listeners. */ public readonly onRender = new BeEvent<(vp: Viewport) => void>(); @@ -1714,8 +1780,12 @@ export abstract class Viewport implements IDisposable { if (view.areFeatureOverridesDirty) { const ovr = new FeatureSymbology.Overrides(view); - if (undefined !== this._addFeatureOverrides) - this._addFeatureOverrides(ovr, this); + if (undefined !== this._neverDrawn) + ovr.setNeverDrawnSet(this._neverDrawn); + if (undefined !== this._alwaysDrawn) + ovr.setAlwaysDrawnSet(this._alwaysDrawn, this._alwaysDrawnExclusive); + if (undefined !== this._featureOverrideProvider) + this._featureOverrideProvider.addFeatureOverrides(ovr, this); target.overrideFeatureSymbology(ovr); view.setFeatureOverridesDirty(false); @@ -1783,23 +1853,22 @@ export abstract class Viewport implements IDisposable { /** @hidden */ public addDecorations(_decorations: Decorations): void { } - /** - * Read selected data about each pixel within a rectangular region of this Viewport. + /** Read selected data about each pixel within a rectangular region of this Viewport. * @param rect The area of the viewport's contents to read. The origin specifies the upper-left corner. Must lie entirely within the viewport's dimensions. * @param selector Specifies which aspect(s) of data to read. * @param receiver A function accepting a [[Pixel.Buffer]] object from which the selected data can be retrieved, or receiving undefined if the viewport is not active, the rect is out of bounds, or some other error. + * @param excludeNonLocatable If true, geometry with the "non-locatable" flag set will not be drawn. * @note The [[Pixel.Buffer]] supplied to the `receiver` function becomes invalid once that function exits. Do not store a reference to it. */ - public readPixels(rect: ViewRect, selector: Pixel.Selector, receiver: Pixel.Receiver): void { + public readPixels(rect: ViewRect, selector: Pixel.Selector, receiver: Pixel.Receiver, excludeNonLocatable = false): void { const viewRect = this.viewRect; if (!rect.isContained(viewRect)) receiver(undefined); else - this.target.readPixels(rect, selector, receiver); + this.target.readPixels(rect, selector, receiver, excludeNonLocatable); } - /** - * Read the current image from this viewport from the rendering system. If a view rectangle outside the actual view is specified, the entire view is captured. + /** Read the current image from this viewport from the rendering system. If a view rectangle outside the actual view is specified, the entire view is captured. * @param rect The area of the view to read. The origin of a viewRect must specify the upper left corner. * @param targetSize The size of the image to be returned. The size can be larger or smaller than the original view. * @param flipVertically If true, the image is flipped along the x-axis. @@ -1840,8 +1909,7 @@ export abstract class Viewport implements IDisposable { } } -/** - * An interactive Viewport that exists within an HTMLDivElement. ScreenViewports can receive HTML events. +/** An interactive Viewport that exists within an HTMLDivElement. ScreenViewports can receive HTML events. * To render the contents of a ScreenViewport, it must be added to the [[ViewManager]] via ViewManager.addViewport(). * Every frame, the ViewManager will update the Viewport's state and re-render its contents if anything has changed. * To halt this loop, use ViewManager.dropViewport() to remove the viewport from the ViewManager. @@ -1866,6 +1934,7 @@ export abstract class Viewport implements IDisposable { * 5a. If it is currently registered with the ViewManager, it is dropped and disposed of via ViewManager.dropViewport() * 5b. Otherwise, it is disposed of by invoking its dispose() method directly. * ``` + * @public */ export class ScreenViewport extends Viewport { private _evController?: EventController; @@ -1885,8 +1954,7 @@ export class ScreenViewport extends Viewport { /** The HTMLDivElement used for toolTips. May be referenced from the DOM by class "overlay-tooltip". */ public readonly toolTipDiv: HTMLDivElement; - /** - * Create a new ScreenViewport that shows a View of an iModel into an HTMLDivElement. This method will create a new HTMLCanvasElement as a child of the supplied parentDiv. + /** Create a new ScreenViewport that shows a View of an iModel into an HTMLDivElement. This method will create a new HTMLCanvasElement as a child of the supplied parentDiv. * It also creates two new child HTMLDivElements: one of class "overlay-decorators" for HTML overlay decorators, and one of class * "overlay-tooltip" for ToolTips. All the new child HTMLElements are the same size as the parentDiv. * @param parentDiv The HTMLDivElement to contain the ScreenViewport. @@ -1907,6 +1975,31 @@ export class ScreenViewport extends Viewport { while (el.lastChild) el.removeChild(el.lastChild); } + /** add a child element to this.parentDiv and set its size and position the same as the parent. + * @hidden + */ + public addChildDiv(element: HTMLElement, zIndex: number) { + // get the (computed) z-index value of the parent, as an integer. + const parentZ = parseInt(window.getComputedStyle(this.parentDiv).zIndex || "0", 10); + const style = element.style; + style.position = "absolute"; + style.top = "0"; + style.left = "0"; + style.height = "100%"; + style.width = "100%"; + style.zIndex = (parentZ + zIndex).toString(); + this.parentDiv.appendChild(element); + } + + /** @hidden */ + public addNewDiv(className: string, overflowHidden: boolean, z: number): HTMLDivElement { + const div = document.createElement("div"); + div.className = className; + div.style.pointerEvents = "none"; + div.style.overflow = overflowHidden ? "hidden" : "visible"; + this.addChildDiv(div, z); + return div; + } /** @hidden */ constructor(canvas: HTMLCanvasElement, parentDiv: HTMLDivElement, target: RenderTarget) { @@ -1914,44 +2007,18 @@ export class ScreenViewport extends Viewport { this.canvas = canvas; this.parentDiv = parentDiv; - // function to add a child element to this.parentDiv and set its size and position the same as the parent. - const addChild = (element: HTMLElement, zIndex: number) => { - const style = element.style; - style.position = "absolute"; - style.top = "0"; - style.left = "0"; - style.height = "100%"; - style.width = "100%"; - style.zIndex = zIndex.toString(); - this.parentDiv.appendChild(element); - }; - - // first remove all children of supplied element + // first remove all children of the parent Div ScreenViewport.removeAllChildren(parentDiv); - // get the (computed) z-index value of the parent, as an integer. - const parentZ = parseInt(window.getComputedStyle(parentDiv).zIndex || "0", 10); - - addChild(canvas, parentZ + 10); + this.addChildDiv(canvas, 10); this.target.updateViewRect(); - this.decorationDiv = document.createElement("div"); - this.decorationDiv.className = "overlay-decorators"; - this.decorationDiv.style.pointerEvents = "none"; - this.decorationDiv.style.overflow = "hidden"; - addChild(this.decorationDiv, parentZ + 20); - - this.toolTipDiv = document.createElement("div"); - this.toolTipDiv.className = "overlay-tooltip"; - this.toolTipDiv.style.pointerEvents = "none"; - this.toolTipDiv.style.overflow = "visible"; - addChild(this.toolTipDiv, parentZ + 30); - + this.decorationDiv = this.addNewDiv("overlay-decorators", true, 30); + this.toolTipDiv = this.addNewDiv("overlay-tooltip", false, 40); this.setCursor(); } - /** - * Open the toolTip window in this ScreenViewport with the supplied message and location. The tooltip will be a child of [[ScreenViewport.toolTipDiv]]. + /** Open the toolTip window in this ScreenViewport with the supplied message and location. The tooltip will be a child of [[ScreenViewport.toolTipDiv]]. * @param message The message to display * @param location The position of the toolTip, in view coordinates. If undefined, use center of view. * @param options the ToolTip options @@ -1964,16 +2031,18 @@ export class ScreenViewport extends Viewport { /** Set the event controller for this Viewport. Destroys previous controller, if one was defined. */ public setEventController(controller: EventController | undefined) { if (this._evController) { this._evController.destroy(); } this._evController = controller; } - /** - * Find a point on geometry visible in this Viewport, within a radius of supplied pick point. + /** Find a point on geometry visible in this Viewport, within a radius of supplied pick point. * @param pickPoint Point to search about, in world coordinates * @param radius Radius, in pixels, of the circular area to search. + * @param allowNonLocatable If true, include geometry with non-locatable flag set. * @param out Optional Point3d to hold the result. If undefined, a new Point3d is returned. * @returns The point, in world coordinates, on the element closest to `pickPoint`, or undefined if no elements within `radius`. */ - public pickNearestVisibleGeometry(pickPoint: Point3d, radius: number, out?: Point3d): Point3d | undefined { + public pickNearestVisibleGeometry(pickPoint: Point3d, radius: number, allowNonLocatable = true, out?: Point3d): Point3d | undefined { const picker = new ElementPicker(); - if (0 !== picker.doPick(this, pickPoint, radius, new LocateOptions())) { + const options = new LocateOptions(); + options.allowNonLocatable = allowNonLocatable; + if (0 !== picker.doPick(this, pickPoint, radius, options)) { const result = undefined !== out ? out : new Point3d(); result.setFrom(picker.getHit(0)!.getPoint()); return result; @@ -2030,8 +2099,7 @@ export class ScreenViewport extends Viewport { this.saveViewUndo(); } - /** - * Change the ViewState of this Viewport + /** Change the ViewState of this Viewport * @param view a fully loaded (see discussion at [[ViewState.load]] ) ViewState */ public changeView(view: ViewState) { @@ -2064,7 +2132,7 @@ export class ScreenViewport extends Viewport { // the first time we're called we need to establish the baseline if (!this._currentBaseline) - this._currentBaseline = this.view.clone(); + this._currentBaseline = this.view.clone(); if (this.view.equalState(this._currentBaseline!)) // this does a deep compare of the ViewState plus DisplayStyle, CategorySelector, and ModelSelector return; // nothing changed, we're done @@ -2084,11 +2152,9 @@ export class ScreenViewport extends Viewport { this._forwardStack.length = 0; // not possible to do redo after this } - this._currentBaseline = this.view.clone(); + this._currentBaseline = this.view.clone(); } - /** - * Reverses the most recent change to the Viewport from the undo stack. - */ + /** Reverses the most recent change to the Viewport from the undo stack. */ public doUndo(animationTime?: BeDuration) { if (0 === this._backStack.length) return; @@ -2098,9 +2164,7 @@ export class ScreenViewport extends Viewport { this.applyViewState(this._currentBaseline, animationTime); } - /** - * Re-applies the most recently un-done change to the Viewport from the redo stack. - */ + /** Re-applies the most recently un-done change to the Viewport from the redo stack. */ public doRedo(animationTime?: BeDuration) { if (0 === this._forwardStack.length) return; @@ -2182,8 +2246,7 @@ export class ScreenViewport extends Viewport { } } -/** - * Forms a 2-way connection between 2 Viewports of the same iModel, such that any change of the parameters in one will be reflected in the other. +/** Forms a 2-way connection between 2 Viewports of the same iModel, such that any change of the parameters in one will be reflected in the other. * For example, Navigator uses this class to synchronize two views for revision comparison. * @note It is possible to synchronize two Viewports from two different [[IModelConnection]]s of the same iModel. */ diff --git a/core/frontend/src/WebWorkerManager.ts b/core/frontend/src/WebWorkerManager.ts new file mode 100644 index 0000000..ff8dd25 --- /dev/null +++ b/core/frontend/src/WebWorkerManager.ts @@ -0,0 +1,177 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +// This file contains the code that manages a pool of We bWorkers, starting and dispatching work to them. +// All the WebWorkers for a given instance of WebWorkerManager are the same, running the specified javascript +// code. The Web Worker javascript must be set up to handle the set of WorkerOperation's that are sent by +// calls to queueOperation on WebWorkerManager. + +type resolveFunc = ((arg: any) => void); +type rejectFunc = ((arg: Error) => void); + +/** Class that manages Web Workers. The number of Web Worker threads can be specified. + * Each Web Worker maintains a queue of requests, and queueOperation method selects + * the thread with the fewest entries in the queue. Operations are represented as + * subclasses of the abstract class WorkerOperation. + * @alpha + * @hidden + */ +export class WebWorkerManager { + private _workerProxys: WebWorkerProxy[]; + private _maxWebWorkers: number; + private _workerJsFile: string; + + public constructor(workerJsFile: string, maxWebWorkers?: number) { + this._workerJsFile = workerJsFile; + this._maxWebWorkers = maxWebWorkers ? maxWebWorkers : 3; + this._workerProxys = new Array(); + } + + // queues an operation to one of the WebWorker threads. The workerJsFile must have a + // handler method with the name of WorkerOperation.operation, and must return a result. + public async queueOperation(operation: WorkerOperation): Promise { + const wwProxy: WebWorkerProxy = this.getBestQueue(); + return operation.sendMessage(wwProxy); + } + + // selects the queue to which the request is directed. + public getBestQueue(): WebWorkerProxy { + let shortestExisting: number = Number.MAX_SAFE_INTEGER; + let selectedProxy: WebWorkerProxy | undefined; + let selectedLength: number = 0; + if (this._workerProxys.length > 0) { + for (const proxy of this._workerProxys) { + if (proxy.queueLength < shortestExisting) { + selectedProxy = proxy; + selectedLength = proxy.queueLength; + shortestExisting = selectedLength; + if (selectedLength === 0) + break; + } + } + } + + // if we have no proxys yet, or if none of them have an empty queue, start a new WebWorker proxy. + if (!selectedProxy || ((selectedLength > 0) && (this._workerProxys.length < this._maxWebWorkers))) { + const length: number = this._workerProxys.push(new WebWorkerProxy(this._workerJsFile)); + selectedProxy = this._workerProxys[length - 1]; + } + return selectedProxy; + } +} + +// the message sent to the webWorker. +class RequestMessage { + constructor(public msgId: number, public operation: string, public operands: any) { } +} + +/** + * Abstract base class for requests handled by a Web Worker. + * @note - To direct a request to a Web Worker, create a subclass of WorkerOperation in the main thread, + * instantiate that subclass, and then call the queueOperation method of the corresponding WebWorkerManager. + * The javascript loaded by the WebWorkerManager must be configured to handle the request by implementing + * a method equal to the operation argument of the WorkerOperation constructor. + * @alpha + * @hidden + */ +export abstract class WorkerOperation { + private _resolve: resolveFunc | undefined = undefined; + private _reject: rejectFunc | undefined = undefined; + private _proxy: WebWorkerProxy | undefined = undefined; + public msgId: number = 0; + + constructor(public operation: string, public operands: any[], public transferable?: any[]) { + } + + // This is the executor method that is called immediately when you instantiate a Promise. + // Here, we store the resolve and reject functions for use when we handle the message from the worker (see handleMessage). + public executor(resolve: resolveFunc, reject: rejectFunc) { + // save the resolve and reject functions to dispatch when we get reply back from the web worker. + this._resolve = resolve; + this._reject = reject; + + // start the operation in the web worker. + this._proxy!.enqueue(this); + } + + // This method puts together the request, sends it, and returns the Promise. + public async sendMessage(proxy: WebWorkerProxy): Promise { + this._proxy = proxy; + return new Promise(this.executor.bind(this)); + } + + // Called when the correct message is returned from the Web Worker. + // This should be called only from the handleMessage method of WebWorkerProxy + public doResolve(event: MessageEvent): void { + // the return value is in event.data.result. + this._resolve!(event.data.result); + } + + // This should be called only from the handleMessage method of WebWorkerProxy. + public doReject(errorEvent: ErrorEvent): void { + this._reject!(new Error(`Error ${errorEvent.message} at line number ${errorEvent.lineno} of file ${errorEvent.filename}, in the webworker thread`)); + } +} + +class WebWorkerProxy { + private _queue: Map; + private _worker: Worker; + private _nextMsgId: number; + + public constructor(workerFile: string) { + this._worker = new Worker(workerFile); + this._worker.onmessage = this.handleMessage.bind(this); + this._worker.onerror = this.handleError.bind(this); + this._queue = new Map(); + this._nextMsgId = 1; + } + + // this is the method that gets responses (that worked) from the webWorker. + private handleMessage(event: MessageEvent) { + const msgId: number = event.data.msgId; + const wo = this._queue.get(msgId); + if (wo) { + wo.doResolve(event); + this._queue.delete(msgId); + } + } + + // this is the method that gets errors from the webworker. + // Try to get the msgId out of the error we got back. That doesn't always work, because + // sometimes we don't throw the error if it happens while the worker is loading. In that case + // we find the message with the lowest msgId, since that's the order we're processing them. + private handleError(error: ErrorEvent) { + let errorMsgId = 0; + let rejectError = error; + if (error.hasOwnProperty("msgId") && error.hasOwnProperty("originalError")) { + errorMsgId = (error as any).msgId; + rejectError = (error as any).originalError; + } else { + // find the lowest number msgId. + errorMsgId = Number.MAX_SAFE_INTEGER; + for (const msgId of this._queue.keys()) { + errorMsgId = Math.min(msgId, errorMsgId); + } + } + // reject the promise and remove the message from the queue. + const wo = this._queue.get(errorMsgId); + if (wo) { + wo.doReject(rejectError); + this._queue.delete(errorMsgId); + } + } + + public enqueue(wo: WorkerOperation): void { + wo.msgId = this._nextMsgId++; + const message = new RequestMessage(wo.msgId, wo.operation, wo.operands); + this._queue.set(wo.msgId, wo); + this._worker.postMessage(message, wo.transferable); + } + + // gets the queue size. + public get queueLength(): number { + return this._queue.size; + } +} diff --git a/core/frontend/src/imodeljs-frontend.ts b/core/frontend/src/imodeljs-frontend.ts index 402761a..6fcb755 100644 --- a/core/frontend/src/imodeljs-frontend.ts +++ b/core/frontend/src/imodeljs-frontend.ts @@ -21,6 +21,7 @@ export * from "./CategorySelectorState"; export * from "./ContextRealityModelState"; export * from "./DisplayStyleState"; export * from "./ElementLocateManager"; +export * from "./EmphasizeElements"; export * from "./EntityState"; export * from "./FenceParams"; export * from "./FuzzySearch"; @@ -32,6 +33,7 @@ export * from "./Marker"; export * from "./ModelSelectorState"; export * from "./ModelState"; export * from "./NotificationManager"; +export * from "./Plugin"; export * from "./SelectionSet"; export * from "./Sheet"; export * from "./Sprites"; @@ -48,11 +50,28 @@ export * from "./tile/TileAdmin"; export * from "./tile/TileTree"; export * from "./render/FeatureSymbology"; export * from "./render/GraphicBuilder"; +export * from "./render/MockRender"; export * from "./render/System"; export * from "./render/webgl/Target"; export * from "./oidc/OidcBrowserClient"; export * from "./oidc/OidcClientWrapper"; +export * from "./properties/Description"; +export * from "./properties/EditorParams"; +export * from "./properties/Record"; +export * from "./properties/Value"; +export * from "./properties/ToolSettingsValue"; +import * as Primitives from "./properties/PrimitiveTypes"; +export { Primitives }; + +// Set the version number so it can be found at runtime. BUILD_SEMVER is replaced at build time by the webpack DefinePlugin. +declare var BUILD_SEMVER: string; +if ((typeof (BUILD_SEMVER) !== "undefined") && (typeof window !== "undefined") && window) { + if (!(window as any).iModelJsVersions) + (window as any).iModelJsVersions = new Map(); + (window as any).iModelJsVersions.set("imodeljs-frontend", BUILD_SEMVER); +} + /** @docs-package-description * The ($frontend) package always runs in a web browser. It contains classes for [querying iModels and showing views]($docs/learning/frontend/index.md). */ @@ -100,14 +119,22 @@ export * from "./oidc/OidcClientWrapper"; * See [the learning articles]($docs/learning/frontend/index.md). */ /** - * @docs-group-description SelectionSet - * Classes for working with the set of selected elements. - * See [the learning articles]($docs/learning/frontend/index.md). + * @docs-group-description Plugins + * Classes for creating and managing runtime [Plugins]($docs/learning/frontend/Plugins.md) + */ +/** + * @docs-group-description Properties + * Classes for working with property records and descriptions. */ /** * @docs-group-description Rendering * Classes for rendering the contents of views. */ +/** + * @docs-group-description SelectionSet + * Classes for working with the set of selected elements. + * See [the learning articles]($docs/learning/frontend/index.md). + */ /** * @docs-group-description Tile * Classes for selecting and drawing tiles in views. diff --git a/core/frontend/src/loader/IModelJsLoader.ts b/core/frontend/src/loader/IModelJsLoader.ts index 780c374..4f06cf4 100644 --- a/core/frontend/src/loader/IModelJsLoader.ts +++ b/core/frontend/src/loader/IModelJsLoader.ts @@ -2,7 +2,6 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -/** @module ModuleLoader */ // The purpose of this file (which is standalone, and not webpacked into the imodeljs-frontend module) // is to load all of the iModelJs modules. The application's main entry point should be webpacked @@ -67,23 +66,51 @@ class ScriptLoader { } } -// Load Options. Loading the UiComponents and UiFramework are optiona. +// Load Options. Loading the UiComponents and UiFramework are optional. class IModelJsLoadOptions { - public iModelJsVersion: string; + private _iModelJsVersions: any; public loadUiComponents: boolean; public loadUiFramework: boolean; + public loadECPresentation: boolean; - constructor(iModelJsVersion: string, loadUiComponents: boolean, loadUiFramework: boolean) { - this.iModelJsVersion = "v".concat(iModelJsVersion, "/"); - this.loadUiComponents = loadUiComponents || loadUiFramework; - this.loadUiFramework = loadUiFramework; + // IModelJsVersionString is a JSON string. The object properties are the module names, and the values are the versions. + constructor(iModelJsVersionString: string | null) { + this.loadUiComponents = true; + this.loadUiFramework = true; + this.loadECPresentation = true; + + if (iModelJsVersionString) { + this._iModelJsVersions = JSON.parse(iModelJsVersionString); + this.loadUiComponents = (undefined !== this._iModelJsVersions["ui-core"]) || (undefined !== this._iModelJsVersions["ui-components"]); + this.loadUiFramework = (undefined !== this._iModelJsVersions["ui-ninezone"]) || (undefined !== this._iModelJsVersions["ui-framework"]); + this.loadECPresentation = (undefined !== this._iModelJsVersions["presentation-common"]) || (undefined !== this._iModelJsVersions["presentation-frontend"]) || (undefined !== this._iModelJsVersions["presentation-components"]); + + // we need the uiComponents for either ECPresentation or uiFramework. + if (this.loadECPresentation || this.loadUiFramework) { + this.loadUiComponents = true; + } + } else { + this._iModelJsVersions = {}; + } } + public prefixVersion(packageName: string): string { - return this.iModelJsVersion.concat(packageName); + // find the version from the package name. + let versionNumberString: string; + // remove the ".js" from the packageName to get the key + const key = packageName.substr(0, packageName.length - 3); + if (undefined === (versionNumberString = this._iModelJsVersions[key])) { + // tslint:disable-next-line:no-console + console.log("No version specified for ", packageName); + return "v-latest".concat("/", packageName); + } + return "v".concat(versionNumberString, "/", packageName); } } // loads the iModelJs modules, and the external modules that they depend on. +/** @internal */ +/** @hidden */ export async function loadIModelJs(options: IModelJsLoadOptions): Promise { // if we are going to load the ui modules, get the third party stuff started now. They don't depend on any of our modules so can be loaded at any time. let thirdPartyRootPromise; @@ -100,37 +127,29 @@ export async function loadIModelJs(options: IModelJsLoadOptions): Promise if (options.loadUiComponents) { await thirdPartyRootPromise; // load the rest of the third party modules that depend on react and redux. - await ScriptLoader.loadPackagesParallel(["bwc.js", "react-dom.js", "inspire-tree.js", "react-dnd.js", "react-dnd-html5-backend.js", "react-redux.js"]); + await ScriptLoader.loadPackagesParallel(["react-dom.js", "inspire-tree.js", "react-dnd.js", "react-dnd-html5-backend.js", "react-redux.js"]); await ScriptLoader.loadPackage(options.prefixVersion("ui-core.js")); await ScriptLoader.loadPackage(options.prefixVersion("ui-components.js")); - if (options.loadUiFramework) { - await ScriptLoader.loadPackage(options.prefixVersion("ui-ninezone.js")); + if (options.loadECPresentation) { await ScriptLoader.loadPackage(options.prefixVersion("presentation-common.js")); await ScriptLoader.loadPackage(options.prefixVersion("presentation-frontend.js")); await ScriptLoader.loadPackage(options.prefixVersion("presentation-components.js")); + } + if (options.loadUiFramework) { + await ScriptLoader.loadPackage(options.prefixVersion("ui-ninezone.js")); await ScriptLoader.loadPackage(options.prefixVersion("ui-framework.js")); } } await ScriptLoader.loadPackage("main.js"); } -// interprets string from options specification -function stringToBoolean(value: string | null, defaultValue: boolean): boolean { - if (!value) - return defaultValue; - return (value !== "false") && (value !== "FALSE") && (value !== "0"); -} - -// retrieves the options from the script tag, using the "data-" attribute capability. function getOptions(): IModelJsLoadOptions { const loaderScriptElement: HTMLScriptElement | SVGScriptElement | null = document.currentScript; if (!loaderScriptElement) - return new IModelJsLoadOptions("-latest", true, true); + return new IModelJsLoadOptions(null); - const iModelJsVersion = loaderScriptElement.getAttribute("data-imjsversion"); - const loadComponentsString = loaderScriptElement.getAttribute("data-uicomponents"); - const loadFrameworkString = loaderScriptElement.getAttribute("data-uiframework"); - return new IModelJsLoadOptions(iModelJsVersion ? iModelJsVersion : "-latest", stringToBoolean(loadComponentsString, true), stringToBoolean(loadFrameworkString, true)); + const iModelJsVersionString = loaderScriptElement.getAttribute("data-imjsversions"); + return new IModelJsLoadOptions(iModelJsVersionString); } // execute the loader diff --git a/core/frontend/src/oidc/OidcBrowserClient.ts b/core/frontend/src/oidc/OidcBrowserClient.ts index d47e826..2b46ccb 100644 --- a/core/frontend/src/oidc/OidcBrowserClient.ts +++ b/core/frontend/src/oidc/OidcBrowserClient.ts @@ -50,7 +50,7 @@ export class OidcBrowserClient extends OidcClient implements IOidcFrontendClient * The application should use this method whenever a redirection happens - redirection typically causes * the re-initialization of a Single Page Application. */ - private async handleRedirectCallback(): Promise { + public async handleRedirectCallback(): Promise { if (window.location.pathname !== this._redirectPath) return false; diff --git a/core/frontend/src/oidc/OidcClientWrapper.ts b/core/frontend/src/oidc/OidcClientWrapper.ts index 51d1463..59ca9ef 100644 --- a/core/frontend/src/oidc/OidcClientWrapper.ts +++ b/core/frontend/src/oidc/OidcClientWrapper.ts @@ -8,13 +8,16 @@ import { IOidcFrontendClient, OidcFrontendClientConfiguration } from "@bentley/i import { OidcBrowserClient } from "./OidcBrowserClient"; import { ActivityLoggingContext } from "@bentley/bentleyjs-core"; -import { ElectronRpcConfiguration } from "@bentley/imodeljs-common"; +import { ElectronRpcConfiguration, MobileRpcConfiguration } from "@bentley/imodeljs-common"; +import { OidcIOSClient } from "./OidcIOSClient"; let OidcClient: any; // tslint:disable-line:variable-name if (ElectronRpcConfiguration.isElectron) { // TODO: Need to figure a way to load a module that contains OidcDeviceClient, and // eventually migrate that to a separate imodeljs-clients-device package. OidcClient = OidcBrowserClient; // eval("require")("@bentley/imodeljs-clients-backend").OidcDeviceClient; // tslint:disable-line:no-eval +} else if (MobileRpcConfiguration.isIOSFrontend) { + OidcClient = OidcIOSClient; } else { OidcClient = OidcBrowserClient; } diff --git a/core/frontend/src/oidc/OidcIOSClient.ts b/core/frontend/src/oidc/OidcIOSClient.ts new file mode 100644 index 0000000..863fbfa --- /dev/null +++ b/core/frontend/src/oidc/OidcIOSClient.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { IOidcFrontendClient, AccessToken, OidcClient, UserInfo } from "@bentley/imodeljs-clients"; +import { BeEvent } from "@bentley/bentleyjs-core"; + +/** Utility to provide OIDC/OAuth tokens from native ios app to frontend */ +export class OidcIOSClient extends OidcClient implements IOidcFrontendClient { + private _accessToken: AccessToken | undefined; + public constructor() { + super(); + } + + /** Initialize client by hooking to notifOidcClient handler called by native side */ + public async initialize(): Promise { + return new Promise((resolve) => { + (window as any).notifyOidcClient = () => { + this.realodInfo(); + this.onUserStateChanged.raiseEvent(this._accessToken); + }; + resolve(); + }); + } + + /** Load oidc info that is set by native side and set access_token */ + private realodInfo() { + const settings = window.localStorage.getItem("ios:oidc_info"); + const info = JSON.parse(settings!); + const startsAt: Date = new Date(info!.expires_at - info!.expires_in); + const expiresAt: Date = new Date(info!.expires_at); + const userInfo = UserInfo.fromJson(info.user_info); + this._accessToken = AccessToken.fromJsonWebTokenString(info.access_token, startsAt, expiresAt, userInfo); + } + + /** Should be call to begin signIn process from native side */ + public signIn(): void { + (window as any).webkit.messageHandlers.signIn.postMessage(""); + } + + /** Should be call to begin signOut process from native side */ + public signOut(): void { + (window as any).webkit.messageHandlers.signOut.postMessage(""); + } + + /** return accessToken */ + public async getAccessToken(): Promise { + return new Promise((resolve) => { + resolve(this._accessToken); + }); + } + + public dispose(): void { + } + + public readonly onUserStateChanged = new BeEvent<(token: AccessToken | undefined) => void>(); +} diff --git a/ui/components/src/ui-components/properties/Description.ts b/core/frontend/src/properties/Description.ts similarity index 84% rename from ui/components/src/ui-components/properties/Description.ts rename to core/frontend/src/properties/Description.ts index d30e9d5..8f04eb2 100644 --- a/ui/components/src/ui-components/properties/Description.ts +++ b/core/frontend/src/properties/Description.ts @@ -5,6 +5,7 @@ /** @module Properties */ import { PropertyEditorParams } from "./EditorParams"; +import { QuantityType } from "../QuantityFormatter"; /** * Information about an enumeration choice @@ -27,8 +28,8 @@ export interface EnumerationChoicesInfo { * Information about a Property Editor */ export interface PropertyEditorInfo { - name: string; - params: PropertyEditorParams[]; + name?: string; + params?: PropertyEditorParams[]; } /** @@ -40,7 +41,8 @@ export interface PropertyDescription { typename: string; enum?: EnumerationChoicesInfo; editor?: PropertyEditorInfo; - + /** QuantityType or name KOQ full name - used by quantity formatter */ + quantityType?: QuantityType | string; /** Get the custom DataController by this name and register it with the property editor */ dataController?: string; } diff --git a/ui/components/src/ui-components/properties/EditorParams.ts b/core/frontend/src/properties/EditorParams.ts similarity index 70% rename from ui/components/src/ui-components/properties/EditorParams.ts rename to core/frontend/src/properties/EditorParams.ts index f8826a8..33cd7bf 100644 --- a/ui/components/src/ui-components/properties/EditorParams.ts +++ b/core/frontend/src/properties/EditorParams.ts @@ -8,13 +8,16 @@ * Enumeration for Property Editor Param Types */ export enum PropertyEditorParamTypes { + ButtonGroupData, + CheckBoxIcons, + Icon, + InputEditorSize, JSON, + MultilineText, Range, Slider, - MultilineText, - Icon, - CheckBoxIcons, SuppressUnitLabel, + SuppressEditorLabel, } /** @@ -29,6 +32,7 @@ export interface BasePropertyEditorParams { */ export interface JsonEditorParams extends BasePropertyEditorParams { type: PropertyEditorParamTypes.JSON; + json: any; } /** @@ -42,6 +46,17 @@ export interface RangeEditorParams extends BasePropertyEditorParams { maximum?: number; } +/** + * Parameters used by PropertyEditors that use HTML element. + */ +export interface InputEditorSizeParams extends BasePropertyEditorParams { + type: PropertyEditorParamTypes.InputEditorSize; + /** Optionally define the width in characters. */ + size?: number; + /** Optionally define the maximum number of characters allowed. */ + maxLength?: number; +} + /** * Parameters used to indicate that a Slider should be presented for the property * and to specify the values needed by the slider. @@ -75,10 +90,17 @@ export interface MultilineTextEditorParams extends BasePropertyEditorParams { * Information about an icon displayed next to a property editor. */ export interface IconDefinition { - /** relative path to the image file. */ - iconPath: string; - /** icon color. */ - iconColor: number; + /** icon class name. */ + iconClass: string; + isEnabledFunction?: () => boolean; +} + +/** + * Parameters used by EnumButtonGroupEditor to define icons in button group. + */ +export interface ButtonGroupEditorParams extends BasePropertyEditorParams { + type: PropertyEditorParamTypes.ButtonGroupData; + buttons: IconDefinition[]; } /** @@ -99,14 +121,24 @@ export interface CheckBoxIconsEditorParams extends BasePropertyEditorParams { } /** - * Parameters used to suppress Unit labels + * Parameter used to suppress Unit labels */ export interface SuppressUnitLabelEditorParams extends BasePropertyEditorParams { type: PropertyEditorParamTypes.SuppressUnitLabel; } +/** + * Parameters used to suppress the label for a type editor in the ToolSettings widget. + */ +export interface SuppressLabelEditorParams extends BasePropertyEditorParams { + type: PropertyEditorParamTypes.SuppressEditorLabel; + /** if false then an empty placeholder label is created. This is sometimes necessary to align editor in proper column */ + suppressLabelPlaceholder?: boolean; +} + /** * Type definition for all Property Editor params */ -export type PropertyEditorParams = JsonEditorParams | RangeEditorParams | SliderEditorParams - | MultilineTextEditorParams | IconEditorParams | CheckBoxIconsEditorParams | SuppressUnitLabelEditorParams; +export type PropertyEditorParams = JsonEditorParams | RangeEditorParams | SliderEditorParams | ButtonGroupEditorParams + | MultilineTextEditorParams | IconEditorParams | CheckBoxIconsEditorParams | SuppressUnitLabelEditorParams + | SuppressLabelEditorParams | InputEditorSizeParams; diff --git a/ui/components/src/ui-components/converters/valuetypes/PrimitiveTypes.ts b/core/frontend/src/properties/PrimitiveTypes.ts similarity index 100% rename from ui/components/src/ui-components/converters/valuetypes/PrimitiveTypes.ts rename to core/frontend/src/properties/PrimitiveTypes.ts diff --git a/ui/components/src/ui-components/properties/Record.ts b/core/frontend/src/properties/Record.ts similarity index 63% rename from ui/components/src/ui-components/properties/Record.ts rename to core/frontend/src/properties/Record.ts index b32b915..99385e6 100644 --- a/ui/components/src/ui-components/properties/Record.ts +++ b/core/frontend/src/properties/Record.ts @@ -3,10 +3,22 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ /** @module Properties */ - import { PropertyDescription } from "./Description"; import { PropertyValue } from "./Value"; +/** Properties for the [[PropertyRecord]] with link info supplied */ +export interface LinkElementsInfo { + /** Callback to link click event */ + onClick: (record: PropertyRecord, text: string) => void; + /** + * Function that specifies which parts of display value need to be clickable. + * + * Letters will be picked from __start__ index to __end__ index. __end__ index is not included. + * For a string _"example"_ and a match ```{ start: 1, end: 3 }```, _"xa"_ will be clickable. + */ + matcher?: (displayValue: string) => Array<{ start: number, end: number }>; +} + /** * PropertyRecord contains instance information about a Property, including a * value that can be edited using a PropertyEditor and converted using a TypeConverter. @@ -16,9 +28,11 @@ export class PropertyRecord { public readonly property: PropertyDescription; public description?: string; public isReadonly?: boolean; + public isDisabled?: boolean; public isMerged?: boolean; - // unit?: string; // [grigas] should this be in the PropertyValue? - // KOQ?: ECKindOfQuantityInfo; // [grigas] should this be in the PropertyValue or PropertyDescription? + + /** Properties for link logic */ + public links?: LinkElementsInfo; public constructor(value: PropertyValue, property: PropertyDescription) { this.value = value; diff --git a/core/frontend/src/properties/ToolSettingsValue.ts b/core/frontend/src/properties/ToolSettingsValue.ts new file mode 100644 index 0000000..81e89c8 --- /dev/null +++ b/core/frontend/src/properties/ToolSettingsValue.ts @@ -0,0 +1,88 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module Properties */ + +// import { HorizontalAlignment, VerticalAlignment } from "@bentley/ui-core"; +import { PropertyDescription } from "./Description"; +import { PrimitiveValue, PropertyValue, PropertyValueFormat } from "./Value"; +import { PropertyRecord } from "./Record"; + +/** Primitive ToolSettings Value. */ +export class ToolSettingsValue implements PrimitiveValue { + public readonly valueFormat = PropertyValueFormat.Primitive; + public value?: number | string | boolean | Date; + public displayValue?: string; + + public constructor(value?: number | string | boolean | Date, displayValue?: string) { + this.value = value; + this.displayValue = displayValue; + } + + public get isNullValue(): boolean { + return undefined === this.value; + } + + public get hasDisplayValue(): boolean { + return undefined !== this.displayValue; + } + + public update(newValue: ToolSettingsValue): boolean { + if (newValue.valueFormat !== this.valueFormat) + throw new Error("ToolSettingsValue.update requires both values to be of the same format"); + + if (this.value === newValue.value && this.displayValue === newValue.displayValue) + return false; + + this.value = newValue.value; + this.displayValue = newValue.displayValue; + return true; + } + + public clone(): ToolSettingsValue { + return new ToolSettingsValue(this.value, this.displayValue); + } +} + +/** Interface used to identify the location of the UI control to manipulate a ToolSettings property value. */ +export interface EditorPosition { + /** Determine the order the row is shown in UI */ + rowPriority: number; + /** Determines the column position for the type editor */ + columnIndex: number; + /** Number of columns to occupy. Defaults to 1 */ + columnSpan?: number; +} + +/** Class used to identify a specific ToolSettings property value. */ +export class ToolSettingsPropertySyncItem { + public value: ToolSettingsValue; + public propertyName: string; + /** used to pass enable state to Ui from Tool so property record can be updated */ + public isDisabled?: boolean; + + public constructor(value: ToolSettingsValue, propertyName: string, isDisabled?: boolean) { + this.value = value; + this.propertyName = propertyName; + this.isDisabled = isDisabled; + } +} + +/** Property Record to specify an editor in Tool Settings zone. */ +export class ToolSettingsPropertyRecord extends PropertyRecord { + public editorPosition: EditorPosition; + + public constructor(value: PropertyValue, property: PropertyDescription, editorPosition: EditorPosition, isReadonly = false) { + super(value, property); + this.editorPosition = editorPosition; + this.isReadonly = isReadonly; + } + + public static clone(record: ToolSettingsPropertyRecord, newValue?: ToolSettingsValue): ToolSettingsPropertyRecord { + const value = Object.assign({}, newValue ? newValue : record.value); + const newRecord = new ToolSettingsPropertyRecord(value, record.property, record.editorPosition, record.isReadonly); + newRecord.isDisabled = record.isDisabled; + return newRecord; + } +} diff --git a/ui/components/src/ui-components/properties/Value.ts b/core/frontend/src/properties/Value.ts similarity index 93% rename from ui/components/src/ui-components/properties/Value.ts rename to core/frontend/src/properties/Value.ts index 36343cd..d52fe54 100644 --- a/ui/components/src/ui-components/properties/Value.ts +++ b/core/frontend/src/properties/Value.ts @@ -5,7 +5,7 @@ /** @module Properties */ import { PropertyRecord } from "./Record"; -import * as Primitives from "../converters/valuetypes/PrimitiveTypes"; +import * as Primitives from "./PrimitiveTypes"; /** * Enumeration for Format of the property value. @@ -25,7 +25,7 @@ export interface BasePropertyValue { export interface PrimitiveValue extends BasePropertyValue { valueFormat: PropertyValueFormat.Primitive; value?: Primitives.Value; - displayValue: string; + displayValue?: string; } /** Struct property value */ diff --git a/core/frontend/src/public/locales/en/CoreTools.json b/core/frontend/src/public/locales/en/CoreTools.json index d08d1b9..31afc4e 100644 --- a/core/frontend/src/public/locales/en/CoreTools.json +++ b/core/frontend/src/public/locales/en/CoreTools.json @@ -85,8 +85,13 @@ "description": "Fit all graphics in the chosen model" } }, - "SelectView": "Select view" + "Standard": { + "keyin": "Rotate to Standard View", + "description": "align view with standard rotation", + "flyover": "Standard View" + } }, + "SelectView": "Select view", "Select": { "keyin": "Select Elements", "description": "Selects Element for inspection or manipulation", @@ -138,7 +143,9 @@ "IdentifyLineRemove": "Identify elements to remove by crossing line", "IdentifyBoxRemove": "Identify elements to remove by box corner points", "AcceptSelection": "Accept/Reject Selection", - "AcceptFence": "Accept/Reject Fence Contents" + "AcceptFence": "Accept/Reject Fence Contents", + "Mode": "Mode", + "Scope": "Scope" }, "Error": { "NoFence": "No fence defined", @@ -147,6 +154,17 @@ "NoSSElems": "No elements selected.", "NotSuportedElmType": "Element type not valid for this tool", "ProjectExtents": "Invalid location. All elements must fit within the volume defined by the project extents." + }, + "SelectionOptions": { + "Pick": "Select single", + "Line": "Select all crossing line", + "Box": "Select by range", + "Add": "Add to selection", + "Remove": "Remove from selection" + }, + "SelectionScope": { + "Element": "Element", + "Assembly": "Assembly" } }, "Plugin": { diff --git a/core/frontend/src/public/locales/en/iModelJs.json b/core/frontend/src/public/locales/en/iModelJs.json index 6ec7fe5..fa0d454 100644 --- a/core/frontend/src/public/locales/en/iModelJs.json +++ b/core/frontend/src/public/locales/en/iModelJs.json @@ -7,7 +7,8 @@ "InvalidWindow": "Invalid window", "MinWindow": "Minimum window", "MaxWindow": "Maximum window", - "MaxZoom": "Maximum Zoom" + "MaxZoom": "Maximum Zoom", + "NotDuringMarkup": "Cannot change view during markup" }, "ElementUndo": { "NothingToUndo": "No actions to undo", @@ -39,5 +40,17 @@ "BingDataClickTarget": "Data Attribution", "MapBoxCopyright": "(c) Mapbox, (c) OpenStreetMap contributors", "BingDataAttribution": "Data Provided by: " + }, + "PluginErrors": { + "Success": "Plugin '{{pluginName}}' loaded", + "VersionErrors": "Plugin '{{pluginName}}' version problems encountered", + "FrontendOnly": "Plugins can be loaded in a browser or Electron only", + "NoVersionsLoaded": "iModel.js versions loaded not found", + "ModuleNotLoaded": "Module {{moduleName}} is not loaded", + "WebpackedIncorrectly": "The Plugin has not been webpacked correctly", + "NoVersionSpecified": "No version specified for module {{moduleName}}", + "NotLoaded": "Required module {{moduleName}} is not loaded", + "VersionMismatch": "Version {{versionLoaded}} of {{moduleName}} is not compatible with {{versionRequired}}", + "UnableToLoad": "Unable to load Plugin {{pluginName}}" } } \ No newline at end of file diff --git a/core/frontend/src/render/FeatureSymbology.ts b/core/frontend/src/render/FeatureSymbology.ts index 3efbcf9..92ed4a6 100644 --- a/core/frontend/src/render/FeatureSymbology.ts +++ b/core/frontend/src/render/FeatureSymbology.ts @@ -5,7 +5,7 @@ /** @module Rendering */ import { LinePixels, ColorDef, RgbColor, Feature, GeometryClass, SubCategoryOverride, BatchType } from "@bentley/imodeljs-common"; -import { Id64, Id64String } from "@bentley/bentleyjs-core"; +import { Id64, Id64String, Id64Set } from "@bentley/bentleyjs-core"; import { ViewState } from "../ViewState"; function copyIdSetToUint32Set(dst: Id64.Uint32Set, src?: Set): void { @@ -30,6 +30,8 @@ export namespace FeatureSymbology { linePixels?: LinePixels; /** If true, ignore the [[RenderMaterial]] associated with surfaces. */ ignoresMaterial?: true | undefined; + /** If true, the associated [[Feature]]s will not be drawn when using [[Viewport.readPixels]]. */ + nonLocatable?: true | undefined; } /** Defines overrides for selected aspects of a [[Feature]]'s symbology. @@ -47,12 +49,14 @@ export namespace FeatureSymbology { public readonly linePixels?: LinePixels; /** If true, ignore the [[RenderMaterial]] associated with surfaces. */ public readonly ignoresMaterial?: true | undefined; + /** If true, ignore the [[Feature]] when using [[Viewport.readPixels]]. */ + public readonly nonLocatable?: true | undefined; /** An Appearance which overrides nothing. */ public static readonly defaults = new Appearance({}); public static fromJSON(props?: AppearanceProps) { - if (undefined === props || (undefined === props.rgb && undefined === props.weight && undefined === props.transparency && undefined === props.linePixels && !props.ignoresMaterial)) + if (undefined === props || (undefined === props.rgb && undefined === props.weight && undefined === props.transparency && undefined === props.linePixels && !props.ignoresMaterial && !props.nonLocatable)) return this.defaults; else return new Appearance(props); @@ -102,6 +106,7 @@ export namespace FeatureSymbology { transparency: this.transparency, linePixels: this.linePixels, ignoresMaterial: this.ignoresMaterial ? true : undefined, + nonLocatable: this.nonLocatable ? true : undefined, }; } @@ -116,6 +121,7 @@ export namespace FeatureSymbology { if (undefined === props.linePixels) props.linePixels = this.linePixels; if (undefined === props.weight) props.weight = this.weight; if (undefined === props.ignoresMaterial && this.ignoresMaterial) props.ignoresMaterial = true; + if (undefined === props.nonLocatable && this.nonLocatable) props.nonLocatable = true; return Appearance.fromJSON(props); } @@ -126,6 +132,7 @@ export namespace FeatureSymbology { this.transparency = props.transparency; this.linePixels = props.linePixels; this.ignoresMaterial = props.ignoresMaterial; + this.nonLocatable = props.nonLocatable; if (undefined !== this.weight) this.weight = Math.max(1, Math.min(this.weight, 32)); @@ -199,20 +206,14 @@ export namespace FeatureSymbology { /** The set of displayed subcategories. Geometry belonging to subcategories not included in this set will not be drawn. @hidden */ protected readonly _visibleSubCategories = new Id64.Uint32Set(); - /** Mapping of elements IDs to batch ID. When a large number of elements have the same neverDrawn or appearance overrides - * as is the case with schedule simulation, setting these values once rather than for each element is is much - * more efficient. Overrides of a specific element always take precedence over batch overrides. Chiefly used for schedule simulation animation. + /** IDs of animation nodes which should never be drawn. * @hidden */ - public batchMap: Id64.Uint32Map | undefined = undefined; - /** IDs of batch which should never be drawn. + public readonly neverDrawnAnimationNodes = new Set(); + /** Mapping of animation node IDS to overrides applied to the corresponding animation nodes. * @hidden */ - public readonly batchNeverDrawn = new Set(); - /** Mapping of batch IDS to overrides applied to the corresponding batch. - * @hidden - */ - public readonly batchOverrides = new Map(); + public readonly animationNodeOverrides = new Map(); /** Overrides applied to features for which no other overrides are defined */ public get defaultOverrides(): Appearance { return this._defaultOverrides; } @@ -220,14 +221,11 @@ export namespace FeatureSymbology { public get lineWeights(): boolean { return this._lineWeights; } /** @hidden */ - protected isNeverDrawn(idLo: number, idHi: number): boolean { - if (this._neverDrawn.has(idLo, idHi)) + protected isNeverDrawn(elemIdLo: number, elemIdHi: number, animationNodeId: number): boolean { + if (this._neverDrawn.has(elemIdLo, elemIdHi)) return true; - else if (undefined === this.batchMap) - return false; - - const batchId = this.batchMap.get(idLo, idHi); - return undefined !== batchId && this.batchNeverDrawn.has(batchId); + else + return 0 !== animationNodeId && this.neverDrawnAnimationNodes.has(animationNodeId); } /** @hidden */ protected isAlwaysDrawn(idLo: number, idHi: number): boolean { return this._alwaysDrawn.has(idLo, idHi); } @@ -237,13 +235,12 @@ export namespace FeatureSymbology { /** @hidden */ protected getModelOverrides(idLo: number, idHi: number): Appearance | undefined { return this._modelOverrides.get(idLo, idHi); } /** @hidden */ - protected getElementOverrides(idLo: number, idHi: number): Appearance | undefined { + protected getElementOverrides(idLo: number, idHi: number, animationNodeId: number): Appearance | undefined { const app = this._elementOverrides.get(idLo, idHi); - if (app !== undefined || undefined === this.batchMap) + if (app !== undefined || 0 === animationNodeId) return app; - const batchId = this.batchMap.get(idLo, idHi); - return undefined !== batchId ? this.batchOverrides.get(batchId) : undefined; + return this.animationNodeOverrides.get(animationNodeId); } /** @hidden */ protected getSubCategoryOverrides(idLo: number, idHi: number): Appearance | undefined { return this._subCategoryOverrides.get(idLo, idHi); } @@ -254,8 +251,12 @@ export namespace FeatureSymbology { public setNeverDrawn(id: Id64String): void { this._neverDrawn.addId(id); } /** Specify the ID of an element which should always be drawn in this view. */ public setAlwaysDrawn(id: Id64String): void { this._alwaysDrawn.addId(id); } - /** Specify the ID of a batch which should never be drawn in this view. */ - public setBatchNeverDrawn(id: number): void { this.batchNeverDrawn.add(id); } + /** Specify the ID of a animation node which should never be drawn in this view. */ + public setAnimationNodeNeverDrawn(id: number): void { this.neverDrawnAnimationNodes.add(id); } + /** Specify the IDs of elements which should never be drawn in this view. */ + public setNeverDrawnSet(ids: Id64Set) { copyIdSetToUint32Set(this._neverDrawn, ids); } + /** Specify the IDs of elements which should always be drawn in this view. */ + public setAlwaysDrawnSet(ids: Id64Set, exclusive: boolean) { copyIdSetToUint32Set(this._alwaysDrawn, ids); this.isAlwaysDrawnExclusive = exclusive; } /** Returns the feature's Appearance overrides, or undefined if the feature is not visible. */ public getFeatureAppearance(feature: Feature, modelId: Id64String, type: BatchType = BatchType.Primary): Appearance | undefined { @@ -264,7 +265,7 @@ export namespace FeatureSymbology { Id64.getLowerUint32(feature.subCategoryId), Id64.getUpperUint32(feature.subCategoryId), feature.geometryClass, Id64.getLowerUint32(modelId), Id64.getUpperUint32(modelId), - type); + type, 0); } private static readonly _weight1Appearance = Appearance.fromJSON({ weight: 1 }); @@ -274,7 +275,7 @@ export namespace FeatureSymbology { * This API is much uglier but also much more efficient. * @hidden */ - public getAppearance(elemLo: number, elemHi: number, subcatLo: number, subcatHi: number, geomClass: GeometryClass, modelLo: number, modelHi: number, type: BatchType): Appearance | undefined { + public getAppearance(elemLo: number, elemHi: number, subcatLo: number, subcatHi: number, geomClass: GeometryClass, modelLo: number, modelHi: number, type: BatchType, animationNodeId: number): Appearance | undefined { if (BatchType.Classifier === type) return this.getClassifierAppearance(elemLo, elemHi, subcatLo, subcatHi, modelLo, modelHi); @@ -287,7 +288,7 @@ export namespace FeatureSymbology { let elemApp, alwaysDrawn = false; if (Id64.isValidUint32Pair(elemLo, elemHi)) { - if (this.isNeverDrawn(elemLo, elemHi)) + if (this.isNeverDrawn(elemLo, elemHi, animationNodeId)) return undefined; alwaysDrawn = this.isAlwaysDrawn(elemLo, elemHi); @@ -295,7 +296,7 @@ export namespace FeatureSymbology { return undefined; // Element overrides take precedence - elemApp = this.getElementOverrides(elemLo, elemHi); + elemApp = this.getElementOverrides(elemLo, elemHi, animationNodeId); if (undefined !== elemApp) app = undefined !== modelApp ? elemApp.extendAppearance(app) : elemApp; } @@ -328,7 +329,7 @@ export namespace FeatureSymbology { if (undefined !== modelApp) app = modelApp.extendAppearance(app); - const elemApp = this.getElementOverrides(elemLo, elemHi); + const elemApp = this.getElementOverrides(elemLo, elemHi, 0); if (undefined !== elemApp) app = undefined !== modelApp ? elemApp.extendAppearance(app) : elemApp; @@ -399,19 +400,19 @@ export namespace FeatureSymbology { public overrideElement(id: Id64String, app: Appearance, replaceExisting: boolean = true): void { const idLo = Id64.getLowerUint32(id); const idHi = Id64.getUpperUint32(id); - if (this.isNeverDrawn(idLo, idHi)) + if (this.isNeverDrawn(idLo, idHi, 0)) return; // NB: Appearance may specify no overridden symbology - this means "don't apply the default overrides to this element" - if (replaceExisting || undefined === this.getElementOverrides(idLo, idHi)) + if (replaceExisting || undefined === this.getElementOverrides(idLo, idHi, 0)) this._elementOverrides.set(idLo, idHi, app); } - /** Specify overrides for all geometry originating from the specified batch. - * @param id The ID of the batch. + /** Specify overrides for all geometry originating from the specified animation node. + * @param id The ID of the animation node. * @param app The symbology overrides. * @note These overides do not take precedence over element overrides. */ - public overrideBatch(id: number, app: Appearance): void { this.batchOverrides.set(id, app); } + public overrideAnimationNode(id: number, app: Appearance): void { this.animationNodeOverrides.set(id, app); } /** Defines a default Appearance to be applied to any [[Feature]] *not* explicitly overridden. * @param appearance The symbology overides. @@ -426,17 +427,12 @@ export namespace FeatureSymbology { * @hidden */ public initFromView(view: ViewState) { - const { alwaysDrawn, neverDrawn, viewFlags } = view; + const { viewFlags } = view; const { constructions, dimensions, patterns } = viewFlags; - copyIdSetToUint32Set(this._alwaysDrawn, alwaysDrawn); - copyIdSetToUint32Set(this._neverDrawn, neverDrawn); - - this.batchMap = undefined; - this.batchNeverDrawn.clear(); - this.batchOverrides.clear(); + this.neverDrawnAnimationNodes.clear(); + this.animationNodeOverrides.clear(); - this.isAlwaysDrawnExclusive = view.isAlwaysDrawnExclusive; this._constructions = constructions; this._dimensions = dimensions; this._patterns = patterns; @@ -473,7 +469,7 @@ export namespace FeatureSymbology { /** Returns the overrides applied to geometry belonging to the specified model, if any such are defined. */ public getModelOverridesById(id: Id64String): Appearance | undefined { return this.getModelOverrides(Id64.getLowerUint32(id), Id64.getUpperUint32(id)); } /** Returns the overrides applied to geometry belonging to the specified element, if any such are defined. */ - public getElementOverridesById(id: Id64String): Appearance | undefined { return this.getElementOverrides(Id64.getLowerUint32(id), Id64.getUpperUint32(id)); } + public getElementOverridesById(id: Id64String): Appearance | undefined { return this.getElementOverrides(Id64.getLowerUint32(id), Id64.getUpperUint32(id), 0); } /** Returns the overrides applied to geometry belonging to the specified subcategory, if any such are defined. */ public getSubCategoryOverridesById(id: Id64String): Appearance | undefined { return this.getSubCategoryOverrides(Id64.getLowerUint32(id), Id64.getUpperUint32(id)); } @@ -483,7 +479,7 @@ export namespace FeatureSymbology { const isValidElemId = !Id64.isInvalid(elementId); const elemIdParts = isValidElemId ? Id64.getUint32Pair(elementId) : undefined; - if (undefined !== elemIdParts && this.isNeverDrawn(elemIdParts.lower, elemIdParts.upper)) + if (undefined !== elemIdParts && this.isNeverDrawn(elemIdParts.lower, elemIdParts.upper, 0)) return false; const alwaysDrawn = undefined !== elemIdParts && this.isAlwaysDrawn(elemIdParts.lower, elemIdParts.upper); diff --git a/core/frontend/src/render/GraphicBuilder.ts b/core/frontend/src/render/GraphicBuilder.ts index 6c00a33..79235a9 100644 --- a/core/frontend/src/render/GraphicBuilder.ts +++ b/core/frontend/src/render/GraphicBuilder.ts @@ -18,8 +18,10 @@ import { } from "@bentley/geometry-core"; import { ColorDef, + Frustum, GraphicParams, LinePixels, + Npc, } from "@bentley/imodeljs-common"; import { Viewport } from "../Viewport"; import { RenderGraphic } from "./System"; @@ -157,16 +159,6 @@ export abstract class GraphicBuilder { */ public abstract addLineString(points: Point3d[]): void; - /** Helper for adding a series of line strings - * @hidden - */ - public addLineStrings(...lines: Array<[number, Point3d[]]>): void { this.convertToLineStringParams(...lines).forEach((l) => this.addLineString(l.points)); } - - /** Helper for converting an array of string param data each of which are stored as array into an array of line string params. - * @hidden - */ - public convertToLineStringParams(...lines: Array<[number, Point3d[]]>): Array<{ numPoints: number, points: Point3d[] }> { return lines.map((l) => ({ numPoints: l[0], points: l[1] })); } - /** * Appends a 2d line string to the builder. * @param points Array of vertices in the line string. @@ -233,22 +225,25 @@ export abstract class GraphicBuilder { * @hidden */ public addRangeBox(range: Range3d) { - const p: Point3d[] = []; - for (let i = 0; i < 8; ++i) p[i] = new Point3d(); - - p[0].x = p[3].x = p[4].x = p[5].x = range.low.x; - p[1].x = p[2].x = p[6].x = p[7].x = range.high.x; - p[0].y = p[1].y = p[4].y = p[7].y = range.low.y; - p[2].y = p[3].y = p[5].y = p[6].y = range.high.y; - p[0].z = p[1].z = p[2].z = p[3].z = range.low.z; - p[4].z = p[5].z = p[6].z = p[7].z = range.high.z; + const frustum = Frustum.fromRange(range); + const p = frustum.points; - const tmpPts: Point3d[] = []; - tmpPts[0] = p[0]; tmpPts[1] = p[1]; tmpPts[2] = p[2]; - tmpPts[3] = p[3]; tmpPts[4] = p[5]; tmpPts[5] = p[6]; - tmpPts[6] = p[7]; tmpPts[7] = p[4]; tmpPts[8] = p[0]; + this.addLineString([ + p[Npc.LeftBottomFront], + p[Npc.LeftTopFront], + p[Npc.RightTopFront], + p[Npc.RightBottomFront], + p[Npc.RightBottomRear], + p[Npc.RightTopRear], + p[Npc.LeftTopRear], + p[Npc.LeftBottomRear], + p[Npc.LeftBottomFront], + p[Npc.RightBottomFront], + ]); - this.addLineStrings([9, tmpPts], [2, [p[0], p[3]]], [2, [p[4], p[5]]], [2, [p[1], p[7]]], [2, [p[2], p[6]]]); + this.addLineString([p[Npc.LeftTopFront], p[Npc.LeftTopRear]]); + this.addLineString([p[Npc.RightTopFront], p[Npc.RightTopRear]]); + this.addLineString([p[Npc.LeftBottomRear], p[Npc.RightBottomRear]]); } /** Sets the current active symbology for this builder. Any new geometry subsequently added will be drawn using the specified symbology. diff --git a/core/frontend/src/render/MockRender.ts b/core/frontend/src/render/MockRender.ts new file mode 100644 index 0000000..760e0fb --- /dev/null +++ b/core/frontend/src/render/MockRender.ts @@ -0,0 +1,157 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { Viewport, ViewRect } from "../Viewport"; +import { + Decorations, + GraphicBranch, + GraphicList, + PackedFeatureTable, + Pixel, + RenderClipVolume, + RenderGraphic, + RenderMemory, + RenderPlan, + RenderSystem, + RenderTarget, +} from "./System"; +import { GraphicType } from "./GraphicBuilder"; +import { IModelApp } from "../IModelApp"; +import { IModelConnection } from "../IModelConnection"; +import { PrimitiveBuilder } from "./primitives/geometry/GeometryListBuilder"; +import { MeshParams, PolylineParams, PointStringParams } from "./primitives/VertexTable"; +import { PointCloudArgs } from "./primitives/PointCloudPrimitive"; +import { ElementAlignedBox3d } from "@bentley/imodeljs-common"; +import { Transform } from "@bentley/geometry-core"; +import { Id64String, dispose } from "@bentley/bentleyjs-core"; + +/** Contains extensible mock implementations of the various components of a RenderSystem, intended for use in tests. + * Use these for tests instead of the default RenderSystem wherever possible because: + * (1) Electron has a bug on Windows in which it fails to obtain a WebGLRenderingContext when running inside a VM (e.g., during CI job); and + * (2) To decouple the logic which uses aspects of the RenderSystem from the full implementation. + * Any and all of these types can be extended for the purposes of specific tests. + * To use this: + * (1) If overriding anything in the implementation supplied herein, pass a SystemFactory function to MockRender.App.systemFactory. + * (2) Call MockRender.App.startup() instead of IModelApp.startup() or MaybeRenderApp.startup() before tests begin. + * (3) Likewise call MockRender.App.shutdown() when finished. This resets the SystemFactory to its default. + * @hidden + */ +export namespace MockRender { + /** @hidden */ + export abstract class Target extends RenderTarget { + protected constructor(private readonly _system: System) { super(); } + + public get renderSystem(): RenderSystem { return this._system; } + public get cameraFrustumNearScaleLimit() { return 0; } + public get wantInvertBlackBackground() { return false; } + public get animationFraction() { return 0; } + public set animationFraction(_fraction: number) { } + public changeScene(_scene: GraphicList) { } + public changeTerrain(_terrain: GraphicList) { } + public changeDynamics(_dynamics?: GraphicList) { } + public changeDecorations(_decs: Decorations) { } + public changeRenderPlan(_plan: RenderPlan) { } + public drawFrame(_sceneTime?: number) { } + public updateViewRect() { return false; } + public readPixels(_rect: ViewRect, _selector: Pixel.Selector, receiver: Pixel.Receiver, _excludeNonLocatable: boolean) { receiver(undefined); } + } + + /** @hidden */ + export class OnScreenTarget extends Target { + public constructor(system: System, private readonly _canvas: HTMLCanvasElement) { super(system); } + + public get viewRect() { return new ViewRect(0, 0, this._canvas.clientWidth, this._canvas.clientHeight); } + public setViewRect(_rect: ViewRect, _temp: boolean) { } + } + + /** @hidden */ + export class OffScreenTarget extends Target { + public constructor(system: System, private readonly _viewRect: ViewRect) { super(system); } + + public get viewRect() { return this._viewRect; } + public setViewRect(rect: ViewRect, _temp: boolean) { this._viewRect.setFrom(rect); } + } + + /** @hidden */ + export class Builder extends PrimitiveBuilder { + public constructor(system: System, placement: Transform = Transform.identity, type: GraphicType, viewport: Viewport, pickId?: Id64String) { + super(system, type, viewport, placement, pickId); + } + } + + /** @hidden */ + export class Graphic extends RenderGraphic { + public constructor() { super(); } + + public dispose() { } + public collectStatistics(_stats: RenderMemory.Statistics): void { } + } + + /** @hidden */ + export class List extends Graphic { + public constructor(public readonly graphics: RenderGraphic[]) { super(); } + + public dispose() { + for (const graphic of this.graphics) + dispose(graphic); + + this.graphics.length = 0; + } + } + + /** @hidden */ + export class Branch extends Graphic { + public constructor(public readonly branch: GraphicBranch, public readonly transform: Transform, public readonly clips?: RenderClipVolume) { super(); } + + public dispose() { this.branch.dispose(); } + } + + /** @hidden */ + export class Batch extends Graphic { + public constructor(public readonly graphic: RenderGraphic, public readonly featureTable: PackedFeatureTable, public readonly range: ElementAlignedBox3d) { super(); } + + public dispose() { + dispose(this.graphic); + } + } + + /** @hidden */ + export class System extends RenderSystem { + public get isValid() { return true; } + public dispose(): void { } + public get maxTextureSize() { return 4096; } + + public createTarget(canvas: HTMLCanvasElement) { return new OnScreenTarget(this, canvas); } + public createOffscreenTarget(rect: ViewRect): RenderTarget { return new OffScreenTarget(this, rect); } + + public createGraphicBuilder(placement: Transform, type: GraphicType, viewport: Viewport, pickableId?: Id64String) { return new Builder(this, placement, type, viewport, pickableId); } + public createGraphicList(primitives: RenderGraphic[]) { return new List(primitives); } + public createBranch(branch: GraphicBranch, transform: Transform, clips?: RenderClipVolume) { return new Branch(branch, transform, clips); } + public createBatch(graphic: RenderGraphic, features: PackedFeatureTable, range: ElementAlignedBox3d) { return new Batch(graphic, features, range); } + + public createMesh(_params: MeshParams) { return new Graphic(); } + public createPolyline(_params: PolylineParams) { return new Graphic(); } + public createPointString(_params: PointStringParams) { return new Graphic(); } + public createPointCloud(_args: PointCloudArgs, _imodel: IModelConnection) { return new Graphic(); } + } + + /** @hidden */ + export type SystemFactory = () => RenderSystem; + + /** An implementation of IModelApp which uses a MockRender.System by default. + * @hidden + */ + export class App extends IModelApp { + public static systemFactory: SystemFactory = () => App.createDefaultRenderSystem(); + + protected static supplyRenderSystem(): RenderSystem { return this.systemFactory(); } + public static shutdown(): void { + this.systemFactory = () => App.createDefaultRenderSystem(); + super.shutdown(); + } + + protected static createDefaultRenderSystem() { return new System(); } + } +} diff --git a/core/frontend/src/render/System.ts b/core/frontend/src/render/System.ts index e718476..e3f6ff2 100644 --- a/core/frontend/src/render/System.ts +++ b/core/frontend/src/render/System.ts @@ -55,6 +55,7 @@ export namespace RenderMemory { Polylines, PointStrings, PointClouds, + Instances, COUNT, } @@ -79,6 +80,7 @@ export namespace RenderMemory { public get polylines() { return this.consumers[BufferType.Polylines]; } public get pointStrings() { return this.consumers[BufferType.PointStrings]; } public get pointClouds() { return this.consumers[BufferType.PointClouds]; } + public get instances() { return this.consumers[BufferType.Instances]; } public clear(): void { for (const consumer of this.consumers) @@ -152,6 +154,7 @@ export namespace RenderMemory { public addPolyline(numBytes: number) { this.addBuffer(BufferType.Polylines, numBytes); } public addPointString(numBytes: number) { this.addBuffer(BufferType.PointStrings, numBytes); } public addPointCloud(numBytes: number) { this.addBuffer(BufferType.PointClouds, numBytes); } + public addInstances(numBytes: number) { this.addBuffer(BufferType.Instances, numBytes); } } /** @hidden */ @@ -178,6 +181,7 @@ export class RenderPlan { public readonly lights?: SceneLights; public readonly analysisStyle?: AnalysisStyle; public readonly ao?: AmbientOcclusion.Settings; + public readonly isFadeOutActive: boolean; public analysisTexture?: RenderTexture; private _curFrustum: ViewFrustum; @@ -187,7 +191,7 @@ export class RenderPlan { public selectTerrainFrustum() { if (undefined !== this.terrainFrustum) this._curFrustum = this.terrainFrustum; } public selectViewFrustum() { this._curFrustum = this.viewFrustum; } - private constructor(is3d: boolean, viewFlags: ViewFlags, bgColor: ColorDef, monoColor: ColorDef, hiliteSettings: Hilite.Settings, aaLines: AntiAliasPref, aaText: AntiAliasPref, viewFrustum: ViewFrustum, terrainFrustum: ViewFrustum | undefined, activeVolume?: RenderClipVolume, hline?: HiddenLine.Settings, lights?: SceneLights, analysisStyle?: AnalysisStyle, ao?: AmbientOcclusion.Settings) { + private constructor(is3d: boolean, viewFlags: ViewFlags, bgColor: ColorDef, monoColor: ColorDef, hiliteSettings: Hilite.Settings, aaLines: AntiAliasPref, aaText: AntiAliasPref, viewFrustum: ViewFrustum, isFadeOutActive: boolean, terrainFrustum: ViewFrustum | undefined, activeVolume?: RenderClipVolume, hline?: HiddenLine.Settings, lights?: SceneLights, analysisStyle?: AnalysisStyle, ao?: AmbientOcclusion.Settings) { this.is3d = is3d; this.viewFlags = viewFlags; this.bgColor = bgColor; @@ -202,6 +206,7 @@ export class RenderPlan { this.terrainFrustum = terrainFrustum; this.analysisStyle = analysisStyle; this.ao = ao; + this.isFadeOutActive = isFadeOutActive; } public static createFromViewport(vp: Viewport): RenderPlan { @@ -214,7 +219,7 @@ export class RenderPlan { const clipVec = view.getViewClip(); const activeVolume = clipVec !== undefined ? IModelApp.renderSystem.getClipVolume(clipVec, view.iModel) : undefined; const terrainFrustum = (undefined === vp.backgroundMapPlane) ? undefined : ViewFrustum.createFromViewportAndPlane(vp, vp.backgroundMapPlane as Plane3dByOriginAndUnitNormal); - const rp = new RenderPlan(view.is3d(), style.viewFlags, view.backgroundColor, style.monochromeColor, vp.hilite, vp.wantAntiAliasLines, vp.wantAntiAliasText, vp.viewFrustum, terrainFrustum!, activeVolume, hline, lights, style.analysisStyle, ao); + const rp = new RenderPlan(view.is3d(), style.viewFlags, view.backgroundColor, style.monochromeColor, vp.hilite, vp.wantAntiAliasLines, vp.wantAntiAliasText, vp.viewFrustum, vp.isFadeOutActive, terrainFrustum!, activeVolume, hline, lights, style.analysisStyle, ao); if (rp.analysisStyle !== undefined && rp.analysisStyle.scalarThematicSettings !== undefined) rp.analysisTexture = vp.target.renderSystem.getGradientTexture(Gradient.Symb.createThematic(rp.analysisStyle.scalarThematicSettings), vp.iModel); @@ -469,6 +474,7 @@ export interface PackedFeature { elementId: Id64.Uint32Pair; subCategoryId: Id64.Uint32Pair; geometryClass: GeometryClass; + animationNodeId: number; } /** @@ -482,6 +488,7 @@ export class PackedFeatureTable { public readonly numFeatures: number; public readonly anyDefined: boolean; public readonly type: BatchType; + private readonly _animationNodeIds?: Uint8Array | Uint16Array | Uint32Array; public get byteLength(): number { return this._data.byteLength; } @@ -489,12 +496,13 @@ export class PackedFeatureTable { * This is used internally when deserializing Tiles in iMdl format. * @hidden */ - public constructor(data: Uint32Array, modelId: Id64String, numFeatures: number, maxFeatures: number, type: BatchType) { + public constructor(data: Uint32Array, modelId: Id64String, numFeatures: number, maxFeatures: number, type: BatchType, animationNodeIds?: Uint8Array | Uint16Array | Uint32Array) { this._data = data; this.modelId = modelId; this.maxFeatures = maxFeatures; this.numFeatures = numFeatures; this.type = type; + this._animationNodeIds = animationNodeIds; switch (this.numFeatures) { case 0: @@ -510,6 +518,7 @@ export class PackedFeatureTable { assert(this._data.length >= this._subCategoriesOffset); assert(this.maxFeatures >= this.numFeatures); + assert(undefined === this._animationNodeIds || this._animationNodeIds.length === this.numFeatures); } /** Create a packed feature table from a [[FeatureTable]]. */ @@ -575,6 +584,11 @@ export class PackedFeatureTable { }; } + /** @hidden */ + public getAnimationNodeId(featureIndex: number): number { + return undefined !== this._animationNodeIds ? this._animationNodeIds[featureIndex] : 0; + } + /** @hidden */ public getPackedFeature(featureIndex: number): PackedFeature { assert(featureIndex < this.numFeatures); @@ -589,7 +603,8 @@ export class PackedFeatureTable { subCatIndex = subCatIndex * 2 + this._subCategoriesOffset; const subCategoryId = { lower: this._data[subCatIndex], upper: this._data[subCatIndex + 1] }; - return { elementId, subCategoryId, geometryClass }; + const animationNodeId = this.getAnimationNodeId(featureIndex); + return { elementId, subCategoryId, geometryClass, animationNodeId }; } /** Returns the element ID of the Feature associated with the specified index, or undefined if the index is out of range. */ @@ -674,7 +689,7 @@ export abstract class RenderTarget implements IDisposable { /** @hidden */ public reset(): void { } /** @hidden */ - public abstract changeScene(scene: GraphicList, activeVolume?: RenderClipVolume): void; + public abstract changeScene(scene: GraphicList): void; /** @hidden */ public abstract changeTerrain(_scene: GraphicList): void; /** @hidden */ @@ -698,7 +713,7 @@ export abstract class RenderTarget implements IDisposable { /** @hidden */ public abstract updateViewRect(): boolean; // force a RenderTarget viewRect to resize if necessary since last draw /** @hidden */ - public abstract readPixels(rect: ViewRect, selector: Pixel.Selector, receiver: Pixel.Receiver): void; + public abstract readPixels(rect: ViewRect, selector: Pixel.Selector, receiver: Pixel.Receiver, excludeNonLocatable: boolean): void; /** @hidden */ public readImage(_rect: ViewRect, _targetSize: Point2d, _flipVertically: boolean): ImageBuffer | undefined { return undefined; } } @@ -711,6 +726,34 @@ export interface TextureImage { format: ImageSourceFormat | undefined; } +/** @hidden */ +export const enum RenderDiagnostics { + /** No diagnostics enabled. */ + None = 0, + /** Debugging output to browser console enabled. */ + DebugOutput = 1 << 1, + /** Potentially expensive checks of WebGL state enabled. */ + WebGL = 1 << 2, + /** All diagnostics enabled. */ + All = DebugOutput | WebGL, +} + +/** Parameters for creating a [[RenderGraphic]] representing a collection of instances of shared geometry. + * Each instance is drawn using the same graphics, but with its own transform and (optionally) [[Feature]] Id. + */ +export interface InstancedGraphicParams { + /** The number of instances. + * Must be greater than zero. + * Must be equal to (transforms.length / 12) + * If featureIds is defined, must be equal to (featureIds.length / 3) + */ + readonly count: number; + /** An array of instance-to-model transforms. Each transform consists of 3 rows of 4 columns where the 4th column holds the translation. */ + readonly transforms: Float32Array; + /** If defined, an array of little-endian 24-bit unsigned integers containing the feature ID of each instance. */ + readonly featureIds?: Uint8Array; +} + /** A RenderSystem provides access to resources used by the internal WebGL-based rendering system. * @see [[IModelApp.renderSystem]]. */ @@ -758,28 +801,28 @@ export abstract class RenderSystem implements IDisposable { public abstract createGraphicBuilder(placement: Transform, type: GraphicType, viewport: Viewport, pickableId?: Id64String): GraphicBuilder; /** @hidden */ - public createTriMesh(args: MeshArgs): RenderGraphic | undefined { + public createTriMesh(args: MeshArgs, instances?: InstancedGraphicParams): RenderGraphic | undefined { const params = MeshParams.create(args); - return this.createMesh(params); + return this.createMesh(params, instances); } /** @hidden */ - public createIndexedPolylines(args: PolylineArgs): RenderGraphic | undefined { + public createIndexedPolylines(args: PolylineArgs, instances?: InstancedGraphicParams): RenderGraphic | undefined { if (args.flags.isDisjoint) { const pointStringParams = PointStringParams.create(args); - return undefined !== pointStringParams ? this.createPointString(pointStringParams) : undefined; + return undefined !== pointStringParams ? this.createPointString(pointStringParams, instances) : undefined; } else { const polylineParams = PolylineParams.create(args); - return undefined !== polylineParams ? this.createPolyline(polylineParams) : undefined; + return undefined !== polylineParams ? this.createPolyline(polylineParams, instances) : undefined; } } /** @hidden */ - public createMesh(_params: MeshParams): RenderGraphic | undefined { return undefined; } + public createMesh(_params: MeshParams, _instances?: InstancedGraphicParams): RenderGraphic | undefined { return undefined; } /** @hidden */ - public createPolyline(_params: PolylineParams): RenderGraphic | undefined { return undefined; } + public createPolyline(_params: PolylineParams, _instances?: InstancedGraphicParams): RenderGraphic | undefined { return undefined; } /** @hidden */ - public createPointString(_params: PointStringParams): RenderGraphic | undefined { return undefined; } + public createPointString(_params: PointStringParams, _instances?: InstancedGraphicParams): RenderGraphic | undefined { return undefined; } /** @hidden */ public createPointCloud(_args: PointCloudArgs, _imodel: IModelConnection): RenderGraphic | undefined { return undefined; } /** @hidden */ @@ -925,12 +968,18 @@ export abstract class RenderSystem implements IDisposable { /** @hidden */ public onInitialized(): void { } + + /** @hidden */ + public enableDiagnostics(_enable: RenderDiagnostics): void { } } + /** Clip/Transform for a branch that are varied over time. */ export class AnimationBranchState { + public readonly omit?: boolean; public readonly transform?: Transform; public readonly clip?: ClipPlanesVolume; - constructor(transform?: Transform, clip?: ClipPlanesVolume) { this.transform = transform; this.clip = clip; } + constructor(transform?: Transform, clip?: ClipPlanesVolume, omit?: boolean) { this.transform = transform; this.clip = clip; this.omit = omit; } } + /** Mapping from node/branch IDs to animation branch state */ export type AnimationBranchStates = Map; diff --git a/core/frontend/src/render/primitives/AuxChannelTable.ts b/core/frontend/src/render/primitives/AuxChannelTable.ts new file mode 100644 index 0000000..a7f1312 --- /dev/null +++ b/core/frontend/src/render/primitives/AuxChannelTable.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module Rendering */ + +export interface AuxChannelProps { + readonly name: string; + readonly inputs: number[]; + readonly indices: number[]; +} + +export interface QuantizedAuxChannelProps extends AuxChannelProps { + readonly qOrigin: number[]; + readonly qScale: number[]; +} + +export class AuxChannel implements AuxChannelProps { + public readonly name: string; + public readonly inputs: number[]; + public readonly indices: number[]; + + public constructor(props: AuxChannelProps) { + this.name = props.name; + this.inputs = props.inputs; + this.indices = props.indices; + } +} + +export class AuxDisplacementChannel extends AuxChannel { + public readonly qOrigin: Float32Array; + public readonly qScale: Float32Array; + + public constructor(props: QuantizedAuxChannelProps) { + super(props); + this.qOrigin = Float32Array.from(props.qOrigin); + this.qScale = Float32Array.from(props.qScale); + } +} + +export class AuxParamChannel extends AuxChannel { + public readonly qOrigin: number; + public readonly qScale: number; + + public constructor(props: QuantizedAuxChannelProps) { + super(props); + this.qOrigin = props.qOrigin[0]; + this.qScale = props.qScale[0]; + } +} + +export interface AuxChannelTableProps { + /** Rectangular array of per-vertex data, of size width * height * numBytesPerVertex bytes. */ + readonly data: Uint8Array; + /** The number of 4-byte RGBA columns in each row of the array. */ + readonly width: number; + /** The number of rows in the array. */ + readonly height: number; + /** The number of vertices in the array. Must be no more than (width * height) / numBytesPerVertex. */ + readonly count: number; + /** The number of bytes allocated for each vertex. Must be a multiple of two. */ + readonly numBytesPerVertex: number; + /** Displacements used for animations. */ + readonly displacements?: QuantizedAuxChannelProps[]; + /** Normals used for animations. */ + readonly normals?: AuxChannelProps[]; + /** Scalar params used for animations. */ + readonly params?: QuantizedAuxChannelProps[]; +} + +/** + * Represents one or more channels of auxiliary per-vertex data which can be used to animate and resymbolize a mesh in various ways. + * Each channel holds a fixed number of bytes for each vertex (typically 2 bytes for normals and params, 6 bytes for displacements). + * The channels are interleaved in a rectangular array such that the data for each vertex is stored contiguously; that is, if a displacement and + * a normal channel exist, then the first vertex's displacement is followed by the first vertex's normal, which is followed by the second + * vertex's displacement and normal; and so on. + */ +export class AuxChannelTable { + /** Rectangular array of per-vertex data, of size width * height * numBytesPerVertex bytes. */ + public readonly data: Uint8Array; + /** The number of 4-byte RGBA columns in each row of the array. */ + public readonly width: number; + /** The number of rows in the array. */ + public readonly height: number; + /** The number of vertices in the array. Must be no more than (width * height) / numBytesPerVertex. */ + public readonly numVertices: number; + /** The number of bytes allocated for each vertex. Must be a multiple of two. */ + public readonly numBytesPerVertex: number; + /** Displacements used for animations. */ + public readonly displacements?: AuxDisplacementChannel[]; + /** Normals used for animations. */ + public readonly normals?: AuxChannel[]; + /** Scalar params used for animations. */ + public readonly params?: AuxParamChannel[]; + + private constructor(props: AuxChannelTableProps, displacements?: AuxDisplacementChannel[], normals?: AuxChannel[], params?: AuxParamChannel[]) { + this.data = props.data; + this.width = props.width; + this.height = props.height; + this.numVertices = props.count; + this.numBytesPerVertex = props.numBytesPerVertex; + this.displacements = displacements; + this.normals = normals; + this.params = params; + } + + public static fromJSON(props: AuxChannelTableProps): AuxChannelTable | undefined { + let displacements: AuxDisplacementChannel[] | undefined; + let normals: AuxChannel[] | undefined; + let params: AuxParamChannel[] | undefined; + + if (undefined !== props.displacements && 0 < props.displacements.length) { + displacements = []; + for (const displacement of props.displacements) + displacements.push(new AuxDisplacementChannel(displacement)); + } + + if (undefined !== props.normals && 0 < props.normals.length) { + normals = []; + for (const normal of props.normals) + normals.push(new AuxChannel(normal)); + } + + if (undefined !== props.params && 0 < props.params.length) { + params = []; + for (const param of props.params) + params.push(new AuxParamChannel(param)); + } + + return undefined !== displacements || undefined !== normals || undefined !== params ? new AuxChannelTable(props, displacements, normals, params) : undefined; + } +} diff --git a/core/frontend/src/render/primitives/ColorMap.ts b/core/frontend/src/render/primitives/ColorMap.ts index fcb10f9..0190f29 100644 --- a/core/frontend/src/render/primitives/ColorMap.ts +++ b/core/frontend/src/render/primitives/ColorMap.ts @@ -27,7 +27,7 @@ export class ColorMap extends IndexMap { public get hasTransparency(): boolean { return this._hasTransparency; } public get isUniform(): boolean { return 1 === this.length; } - public toColorIndex(index: ColorIndex, indices: Uint16Array): void { + public toColorIndex(index: ColorIndex, indices: number[]): void { index.reset(); if (0 === this.length) { assert(false, "empty color map"); diff --git a/core/frontend/src/render/primitives/VertexTable.ts b/core/frontend/src/render/primitives/VertexTable.ts index db4e909..38e2613 100644 --- a/core/frontend/src/render/primitives/VertexTable.ts +++ b/core/frontend/src/render/primitives/VertexTable.ts @@ -24,6 +24,7 @@ import { PolylineTypeFlags, MeshEdge, } from "@bentley/imodeljs-common"; +import { AuxChannelTable } from "./AuxChannelTable"; import { PolylineArgs, MeshArgs } from "./mesh/MeshPrimitives"; import { IModelApp } from "../../IModelApp"; /** @@ -100,65 +101,6 @@ function computeDimensions(nEntries: number, nRgbaPerEntry: number, nExtraRgba: const scratchColorDef = new ColorDef(); -/** Describes a auxilliary channel */ -export interface AuxChannelProps { - readonly name: string; - readonly qOrigin: number[]; - readonly qScale: number[]; - readonly inputs: number[]; - readonly indices: number[]; -} -export interface AuxChannelBaseProps { - readonly name: string; - readonly inputs: number[]; - readonly indices: number[]; -} -export class AuxChannelBase { - public readonly name: string; - public readonly inputs: number[]; - public readonly indices: number[]; - - public constructor(props: AuxChannelBaseProps) { - this.name = props.name; - this.inputs = props.inputs; - this.indices = props.indices; - } -} -export class AuxDisplacement extends AuxChannelBase { - - public readonly qOrigin: Float32Array; - public readonly qScale: Float32Array; - - public constructor(props: AuxChannelProps) { - super(props); - this.qOrigin = new Float32Array(3); - this.qScale = new Float32Array(3); - for (let i = 0; i < 3; i++) { - this.qOrigin[i] = props.qOrigin[i]; - this.qScale[i] = props.qScale[i]; - } - } -} -export class AuxParam extends AuxChannelBase { - public readonly qOrigin: number; - public readonly qScale: number; - - public constructor(props: AuxChannelProps) { - super(props); - this.qOrigin = props.qOrigin[0]; - this.qScale = props.qScale[0]; - } -} -export interface AuxNormalProps { - readonly name: string; - readonly inputs: number[]; - readonly indices: number[]; -} -export class AuxNormal extends AuxChannelBase { - public constructor(props: AuxNormalProps) { - super(props); - } -} /** Describes a VertexTable. */ export interface VertexTableProps { /** The rectangular array of vertex data, of size width*height*numRgbaPerVertex bytes. */ @@ -183,12 +125,6 @@ export interface VertexTableProps { readonly numRgbaPerVertex: number; /** If vertex data include texture UV coordinates, the quantization params for those coordinates. */ readonly uvParams?: QParams2d; - // The auxilliary displacements for animations. - readonly auxDisplacements?: AuxDisplacement[]; - // The auxilliary normals for animations. - readonly auxNormals?: AuxNormal[]; - // The auxilliary parameters for animations. - readonly auxParams?: AuxParam[]; } /** @@ -221,12 +157,6 @@ export class VertexTable implements VertexTableProps { public readonly numRgbaPerVertex: number; /** If vertex data include texture UV coordinates, the quantization params for those coordinates. */ public readonly uvParams?: QParams2d; - // The auxilliary displacements for animations. - public readonly auxDisplacements?: AuxDisplacement[]; - // The auxilliary normals for animations. - public readonly auxNormals?: AuxNormal[]; - // The auxilliary parameters for animations. - public readonly auxParams?: AuxParam[]; /** Construct a VertexTable. The VertexTable takes ownership of all input data - it must not be later modified by the caller. */ public constructor(props: VertexTableProps) { @@ -241,29 +171,6 @@ export class VertexTable implements VertexTableProps { this.numVertices = props.numVertices; this.numRgbaPerVertex = props.numRgbaPerVertex; this.uvParams = props.uvParams; - this.auxDisplacements = props.auxDisplacements; - this.auxNormals = props.auxNormals; - this.auxParams = props.auxParams; - - // ###TODO: Fix add-on to put uniform feature index into vertex table... - if (undefined !== this.uniformFeatureID) - this.fixUpUniformFeatureId(this.uniformFeatureID); - } - - private fixUpUniformFeatureId(id: number): void { - if (0 === id) - return; // already set to zero - - const u32 = new Uint32Array(1); - u32[0] = id; - const u8 = new Uint8Array(u32.buffer); - for (let i = 0; i < this.numVertices; i++) { - const u32Index = (i * this.numRgbaPerVertex) + 2; - const u8Index = u32Index * 4; - for (let j = 0; j < 4; j++) { - this.data[u8Index + j] = u8[j]; - } - } } public static buildFrom(builder: VertexTableBuilder, colorIndex: ColorIndex, featureIndex: FeatureIndex): VertexTable { @@ -728,13 +635,15 @@ export class MeshParams { public readonly surface: SurfaceParams; public readonly edges?: EdgeParams; public readonly isPlanar: boolean; + public readonly auxChannels?: AuxChannelTable; /** Directly construct a MeshParams. The MeshParams takes ownership of all input data. */ - public constructor(vertices: VertexTable, surface: SurfaceParams, edges?: EdgeParams, isPlanar?: boolean) { + public constructor(vertices: VertexTable, surface: SurfaceParams, edges?: EdgeParams, isPlanar?: boolean, auxChannels?: AuxChannelTable) { this.vertices = vertices; this.surface = surface; this.edges = edges; - this.isPlanar = true === isPlanar; + this.isPlanar = !!isPlanar; + this.auxChannels = auxChannels; } /** Construct from a MeshArgs. */ diff --git a/core/frontend/src/render/primitives/geometry/GeometryListBuilder.ts b/core/frontend/src/render/primitives/geometry/GeometryListBuilder.ts index 45b94c4..e96cf05 100644 --- a/core/frontend/src/render/primitives/geometry/GeometryListBuilder.ts +++ b/core/frontend/src/render/primitives/geometry/GeometryListBuilder.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ /** @module Rendering */ -import { Transform, Arc3d, LineSegment3d, CurvePrimitive, Loop, Path, Point2d, Point3d, Polyface, IndexedPolyface, LineString3d } from "@bentley/geometry-core"; -import { GraphicParams, RenderTexture, Gradient, ElementAlignedBox3d, FeatureTable } from "@bentley/imodeljs-common"; +import { Transform, Arc3d, LineSegment3d, CurvePrimitive, Loop, Path, Point2d, Point3d, Polyface, IndexedPolyface, LineString3d, Range3d } from "@bentley/geometry-core"; +import { GraphicParams, RenderTexture, Gradient, FeatureTable } from "@bentley/imodeljs-common"; import { GraphicBuilder, GraphicType } from "../../GraphicBuilder"; import { Viewport } from "../../../Viewport"; import { GeometryOptions } from "../Primitives"; @@ -161,7 +161,7 @@ export class PrimitiveBuilder extends GeometryListBuilder { let graphic = (this.primitives.length !== 1) ? this.accum.system.createGraphicList(this.primitives) : this.primitives.pop() as RenderGraphic; if (undefined !== featureTable) { - const range = new ElementAlignedBox3d(); // ###TODO compute range... + const range = new Range3d(); // ###TODO compute range... graphic = this.accum.system.createBatch(graphic, PackedFeatureTable.pack(featureTable), range); } diff --git a/core/frontend/src/render/primitives/geometry/GeometryPrimitives.ts b/core/frontend/src/render/primitives/geometry/GeometryPrimitives.ts index 5eaa0dc..7922b91 100644 --- a/core/frontend/src/render/primitives/geometry/GeometryPrimitives.ts +++ b/core/frontend/src/render/primitives/geometry/GeometryPrimitives.ts @@ -221,7 +221,7 @@ export class PrimitivePolyfaceGeometry extends Geometry { protected _getPolyfaces(_facetOptions: StrokeOptions): PolyfacePrimitiveList | undefined { if (!this.hasTexture) { // clear parameters if (this.polyface.data.param) { - this.polyface.data.param = []; + this.polyface.data.param.clear(); } if (this.polyface.data.paramIndex) { this.polyface.data.paramIndex = []; diff --git a/core/frontend/src/render/primitives/mesh/MeshBuilder.ts b/core/frontend/src/render/primitives/mesh/MeshBuilder.ts index 7b9ff29..72d216b 100644 --- a/core/frontend/src/render/primitives/mesh/MeshBuilder.ts +++ b/core/frontend/src/render/primitives/mesh/MeshBuilder.ts @@ -143,7 +143,7 @@ export class MeshBuilder { const vertices = []; for (let i = 0; i < 3; ++i) { const vertexIndex = 0 === i ? 0 : triangleIndex + i; - const position = QPoint3d.create(point.getPoint3dAt(vertexIndex), qPointParams); + const position = QPoint3d.create(point.getPoint3dAtUncheckedPointIndex(vertexIndex), qPointParams); const normal = requireNormals ? OctEncodedNormal.fromVector(visitor.getNormal(vertexIndex)!) : undefined; const uvParam: Point2d | undefined = params ? params[vertexIndex] : undefined; vertices[i] = { position, fillColor, normal, uvParam }; diff --git a/core/frontend/src/render/primitives/mesh/MeshPrimitives.ts b/core/frontend/src/render/primitives/mesh/MeshPrimitives.ts index af81559..90f88e7 100644 --- a/core/frontend/src/render/primitives/mesh/MeshPrimitives.ts +++ b/core/frontend/src/render/primitives/mesh/MeshPrimitives.ts @@ -31,7 +31,7 @@ import { } from "@bentley/imodeljs-common"; import { DisplayParams } from "../DisplayParams"; import { ColorMap } from "../ColorMap"; -import { RenderGraphic, RenderSystem } from "../../System"; +import { InstancedGraphicParams, RenderGraphic, RenderSystem } from "../../System"; import { Triangle, TriangleList } from "../Primitives"; import { VertexKeyProps } from "../VertexKey"; @@ -210,7 +210,7 @@ export class Mesh { public readonly normals: OctEncodedNormal[] = []; public readonly uvParams: Point2d[] = []; public readonly colorMap: ColorMap = new ColorMap(); // used to be called ColorTable - public colors: Uint16Array = new Uint16Array(); + public colors: number[] = []; public edges?: MeshEdges; public readonly features?: Mesh.Features; public readonly type: Mesh.PrimitiveType; @@ -243,12 +243,12 @@ export class Mesh { this.features.toFeatureIndex(index); } - public getGraphics(args: MeshGraphicArgs, system: RenderSystem): RenderGraphic | undefined { + public getGraphics(args: MeshGraphicArgs, system: RenderSystem, instances?: InstancedGraphicParams): RenderGraphic | undefined { if (undefined !== this.triangles && this.triangles.length !== 0) { if (args.meshArgs.init(this)) - return system.createTriMesh(args.meshArgs); + return system.createTriMesh(args.meshArgs, instances); } else if (undefined !== this.polylines && this.polylines.length !== 0 && args.polylineArgs.init(this)) { - return system.createIndexedPolylines(args.polylineArgs); + return system.createIndexedPolylines(args.polylineArgs, instances); } return undefined; @@ -288,10 +288,22 @@ export class Mesh { if (undefined !== uvParam) this.uvParams.push(uvParam); - this.colorMap.insert(fillColor); + // Don't allocate color indices until we have non-uniform colors + if (0 === this.colorMap.length) { + this.colorMap.insert(fillColor); + assert(this.colorMap.isUniform); + assert(0 === this.colorMap.indexOf(fillColor)); + } else if (!this.colorMap.isUniform || !this.colorMap.hasColor(fillColor)) { + // Back-fill uniform value (index=0) for existing vertices if previously uniform + if (0 === this.colors.length) + this.colors.length = this.points.length - 1; + + this.colors.push(this.colorMap.insert(fillColor)); + assert(!this.colorMap.isUniform); + } + return this.points.length - 1; } - } export namespace Mesh { diff --git a/core/frontend/src/render/webgl/BranchState.ts b/core/frontend/src/render/webgl/BranchState.ts index f2ee8eb..1792474 100644 --- a/core/frontend/src/render/webgl/BranchState.ts +++ b/core/frontend/src/render/webgl/BranchState.ts @@ -23,7 +23,7 @@ export class BranchState { public static fromBranch(prev: BranchState, branch: Branch) { const vf = branch.branch.getViewFlags(prev.viewFlags); - const transform = branch.localToWorldTransform.multiplyTransformTransform(prev.transform); + const transform = prev.transform.multiplyTransformTransform(branch.localToWorldTransform); const ovrs = undefined !== branch.branch.symbologyOverrides ? branch.branch.symbologyOverrides : prev.symbologyOverrides; return new BranchState(vf, transform, ovrs, branch.clips); } diff --git a/core/frontend/src/render/webgl/CachedGeometry.ts b/core/frontend/src/render/webgl/CachedGeometry.ts index 88ab026..c178e11 100644 --- a/core/frontend/src/render/webgl/CachedGeometry.ts +++ b/core/frontend/src/render/webgl/CachedGeometry.ts @@ -23,9 +23,25 @@ import { VertexLUT } from "./VertexLUT"; import { TextureHandle } from "./Texture"; import { Material } from "./Material"; import { SkyBox } from "../../DisplayStyleState"; +import { InstancedGeometry } from "./InstancedGeometry"; +import { SurfaceGeometry, MeshGeometry, EdgeGeometry, SilhouetteEdgeGeometry } from "./Mesh"; /** Represents a geometric primitive ready to be submitted to the GPU for rendering. */ export abstract class CachedGeometry implements IDisposable, RenderMemory.Consumer { + /** + * Functions for obtaining a subclass of CachedGeometry. + * IMPORTANT: Do NOT use code like `const surface = cachedGeom as SurfaceGeometry`. + * Instanced geometry holds a reference to the shared geometry rendered for each instance - such casts will fail, + * while the casting `functions` will forward to the shared geometry. + */ + public get asLUT(): LUTGeometry | undefined { return undefined; } + public get asSurface(): SurfaceGeometry | undefined { return undefined; } + public get asMesh(): MeshGeometry | undefined { return undefined; } + public get asEdge(): EdgeGeometry | undefined { return undefined; } + public get asSilhouette(): SilhouetteEdgeGeometry | undefined { return undefined; } + public get asInstanced(): InstancedGeometry | undefined { return undefined; } + public get isInstanced() { return undefined !== this.asInstanced; } + // Returns true if white portions of this geometry should render as black on white background protected abstract _wantWoWReversal(_target: Target): boolean; // Returns the edge/line weight used to render this geometry @@ -60,9 +76,8 @@ export abstract class CachedGeometry implements IDisposable, RenderMemory.Consum // Intended to be overridden by specific subclasses public get material(): Material | undefined { return undefined; } public get polylineBuffers(): PolylineBuffers | undefined { return undefined; } - public set uniformFeatureIndices(value: number) { assert(undefined !== value); } // silence 'unused variable' warning... + public set uniformFeatureIndices(_value: number) { } public get featuresInfo(): FeaturesInfo | undefined { return undefined; } - public get debugString(): string { return ""; } public get isEdge(): boolean { switch (this.renderOrder) { @@ -90,9 +105,9 @@ export abstract class CachedGeometry implements IDisposable, RenderMemory.Consum let weight = this._getLineWeight(params); weight = Math.max(weight, minWeight); weight = Math.min(weight, 31.0); - assert(Math.floor(weight) === weight); return weight; } + // Returns true if flashing this geometry should mix its color with the hilite color. If not, the geometry color will be brightened instead. public wantMixHiliteColorForFlash(vf: ViewFlags, target: Target): boolean { // By default only surfaces rendered with lighting get brightened. Overridden for reality meshes since they have lighting baked-in. @@ -113,13 +128,18 @@ export abstract class CachedGeometry implements IDisposable, RenderMemory.Consum export abstract class LUTGeometry extends CachedGeometry { // The texture containing the vertex data. public abstract get lut(): VertexLUT; + public get asLUT() { return this; } + + protected abstract _draw(_numInstances: number): void; + public draw(): void { this._draw(0); } + public drawInstanced(numInstances: number): void { this._draw(numInstances); } // Override this if your color varies based on the target public getColor(_target: Target): ColorInfo { return this.lut.colorInfo; } public get qOrigin(): Float32Array { return this.lut.qOrigin; } public get qScale(): Float32Array { return this.lut.qScale; } - public get hasAnimation(): boolean { return undefined !== this.lut.auxDisplacements || undefined !== this.lut.auxParams || undefined !== this.lut.auxNormals; } + public get hasAnimation() { return this.lut.hasAnimation; } protected constructor() { super(); } } @@ -139,12 +159,9 @@ export class IndexedGeometryParams implements IDisposable { public static create(positions: Uint16Array, qparams: QParams3d, indices: Uint32Array) { const posBuf = QBufferHandle3d.create(qparams, positions); const indBuf = BufferHandle.createBuffer(GL.Buffer.Target.ElementArrayBuffer, indices); - if (undefined === posBuf || undefined === indBuf) { - assert(false); + if (undefined === posBuf || undefined === indBuf) return undefined; - } - assert(!posBuf.isDisposed && !indBuf.isDisposed); return new IndexedGeometryParams(posBuf, indBuf, indices.length); } public static createFromList(positions: QPoint3dList, indices: Uint32Array) { @@ -189,8 +206,7 @@ export class ClipMaskGeometry extends IndexedGeometry { } public collectStatistics(stats: RenderMemory.Statistics): void { - stats.addClipVolume(this._params.positions.bytesUsed); - stats.addClipVolume(this._params.indices.bytesUsed); + stats.addClipVolume(this._params.positions.bytesUsed + this._params.indices.bytesUsed); } public getTechniqueId(_target: Target): TechniqueId { return TechniqueId.ClipMask; } @@ -280,12 +296,9 @@ export class SkyBoxGeometryParams implements IDisposable { public static create(positions: Uint16Array, qparams: QParams3d) { const posBuf = QBufferHandle3d.create(qparams, positions); - if (undefined === posBuf) { - assert(false); + if (undefined === posBuf) return undefined; - } - assert(!posBuf.isDisposed); return new SkyBoxGeometryParams(posBuf); } @@ -324,7 +337,7 @@ export class SkyBoxQuadsGeometry extends CachedGeometry { } public collectStatistics(_stats: RenderMemory.Statistics): void { - // ###TODO, maybe. + // Not interested in tracking this. } public getTechniqueId(_target: Target) { return this._techniqueId; } @@ -425,9 +438,7 @@ export class TexturedViewportQuadGeometry extends ViewportQuadGeometry { // TypeScript compiler will happily accept TextureHandle (or any other type) in place of WebGLTexture. // There is no such 'type' as WebGLTexture at run-time. - for (const texture of this._textures) { - assert(!(texture instanceof TextureHandle)); - } + assert(this._textures.every((tx) => !(tx instanceof TextureHandle))); } } @@ -572,20 +583,29 @@ export class BlurGeometry extends TexturedViewportQuadGeometry { // Geometry used during the 'composite' pass to apply transparency and/or hilite effects. export class CompositeGeometry extends TexturedViewportQuadGeometry { - public static createGeometry(opaque: WebGLTexture, accum: WebGLTexture, reveal: WebGLTexture, hilite: WebGLTexture, occlusion: WebGLTexture) { + public static createGeometry(opaque: WebGLTexture, accum: WebGLTexture, reveal: WebGLTexture, hilite: WebGLTexture) { const params = ViewportQuad.getInstance().createParams(); - if (undefined === params) { + if (undefined === params) return undefined; - } - return new CompositeGeometry(params, [opaque, accum, reveal, hilite, occlusion]); + const textures = [opaque, accum, reveal, hilite]; + return new CompositeGeometry(params, textures); } public get opaque() { return this._textures[0]; } public get accum() { return this._textures[1]; } public get reveal() { return this._textures[2]; } public get hilite() { return this._textures[3]; } - public get occlusion() { return this._textures[4]; } + public get occlusion(): WebGLTexture | undefined { + return this._textures.length > 4 ? this._textures[4] : undefined; + } + public set occlusion(occlusion: WebGLTexture | undefined) { + assert((undefined === occlusion) === (undefined !== this.occlusion)); + if (undefined !== occlusion) + this._textures[4] = occlusion; + else + this._textures.length = 4; + } // Invoked each frame to determine the appropriate Technique to use. public update(flags: CompositeFlags): void { this._techniqueId = this.determineTechnique(flags); } @@ -596,7 +616,7 @@ export class CompositeGeometry extends TexturedViewportQuadGeometry { private constructor(params: IndexedGeometryParams, textures: WebGLTexture[]) { super(params, TechniqueId.CompositeHilite, textures); - assert(5 === this._textures.length); + assert(4 <= this._textures.length); } } @@ -656,9 +676,7 @@ export class PolylineBuffers implements IDisposable { } public collectStatistics(stats: RenderMemory.Statistics, type: RenderMemory.BufferType): void { - stats.addBuffer(type, this.indices.bytesUsed); - stats.addBuffer(type, this.prevIndices.bytesUsed); - stats.addBuffer(type, this.nextIndicesAndParams.bytesUsed); + stats.addBuffer(type, this.indices.bytesUsed + this.prevIndices.bytesUsed + this.nextIndicesAndParams.bytesUsed); } public dispose() { diff --git a/core/frontend/src/render/webgl/ClipVolume.ts b/core/frontend/src/render/webgl/ClipVolume.ts index fbb68b7..75b1a67 100644 --- a/core/frontend/src/render/webgl/ClipVolume.ts +++ b/core/frontend/src/render/webgl/ClipVolume.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ /** @module WebGL */ -import { assert, dispose } from "@bentley/bentleyjs-core"; +import { dispose, assert } from "@bentley/bentleyjs-core"; import { ClipVector, Point3d, ClipUtilities, Triangulator, PolyfaceBuilder, IndexedPolyfaceVisitor, UnionOfConvexClipPlaneSets, Vector3d, StrokeOptions } from "@bentley/geometry-core"; import { QPoint3dList, Frustum, QParams3d } from "@bentley/imodeljs-common"; import { ShaderProgramExecutor } from "./ShaderProgram"; @@ -248,7 +248,7 @@ export class ClipMaskVolume extends RenderClipVolume implements RenderMemory.Con assert(nPoints !== 0); for (let i = 0; i < nPoints; i++) - vertices.add(pPoints.getPoint3dAt(i)); + vertices.add(pPoints.getPoint3dAtUncheckedPointIndex(i)); const visitor = IndexedPolyfaceVisitor.create(polyface, 0); while (visitor.moveToNextFacet()) diff --git a/core/frontend/src/render/webgl/ColorInfo.ts b/core/frontend/src/render/webgl/ColorInfo.ts index 9d5b264..88a54b4 100644 --- a/core/frontend/src/render/webgl/ColorInfo.ts +++ b/core/frontend/src/render/webgl/ColorInfo.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ /** @module WebGL */ +import { ColorIndex, ColorDef } from "@bentley/imodeljs-common"; import { assert } from "@bentley/bentleyjs-core"; import { FloatPreMulRgba } from "./FloatRGBA"; -import { ColorIndex, ColorDef } from "@bentley/imodeljs-common"; import { VertexTable } from "../primitives/VertexTable"; /* Describes a primitive's basic color properties */ diff --git a/core/frontend/src/render/webgl/Diagnostics.ts b/core/frontend/src/render/webgl/Diagnostics.ts new file mode 100644 index 0000000..8d0ec76 --- /dev/null +++ b/core/frontend/src/render/webgl/Diagnostics.ts @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module WebGL */ + +import { GL } from "./GL"; +import { System } from "./System"; + +/** Provides facilities for conditionally executing diagnostic/debug code. By default, all facilities are disabled - they must be explicitly enabled. */ +export class Debug { + /** Whether [[Debug.print]] will actually produce output. */ + public static printEnabled = false; + /** Whether [[Debug.evaluate]] will actually evaluate an expression. */ + public static evaluateEnabled = false; + + /** If [[Debug.printEnabled]] is true, outputs a message using `console.log`. + * @param message A function which returns a string. If [[Debug.printEnabled]] is false, the function is never evaluated. + */ + public static print(message: () => string): void { + if (this.printEnabled) + console.log(message()); // tslint:disable-line:no-console + } + + /** If [[Debug.evaluate]] is true, executes the supplied function and returns its result; otherwise returns the supplied default value. + * @param evaluate The function to execute + * @param defaultValue The value to return if [[Debug.evaluate]] is false + * @returns The return value of `evaluate` if [[Debug.evaluate]] is true; otherwise, the `defaultValue`. + */ + public static evaluate(evaluate: () => T, defaultValue: T): T { + return this.evaluateEnabled ? evaluate() : defaultValue; + } + + /** If [[Debug.evaluateEnabled]] is true, returns whether the currently-bound framebuffer is complete. */ + public static get isValidFrameBuffer(): boolean { return GL.FrameBuffer.Status.Complete === this.checkFrameBufferStatus(); } + + /** If [[Debug.evaluateEnabled]] is true, returns the status of the currently-bound framebuffer. */ + public static checkFrameBufferStatus(): GL.FrameBuffer.Status { + return this.evaluate(() => System.instance.context.checkFramebufferStatus(GL.FrameBuffer.TARGET), GL.FrameBuffer.Status.Complete); + } +} diff --git a/core/frontend/src/render/webgl/DrawCommand.ts b/core/frontend/src/render/webgl/DrawCommand.ts index 92a2b89..9565c2e 100644 --- a/core/frontend/src/render/webgl/DrawCommand.ts +++ b/core/frontend/src/render/webgl/DrawCommand.ts @@ -6,9 +6,9 @@ import { Matrix4 } from "./Matrix"; import { CachedGeometry } from "./CachedGeometry"; -import { Transform } from "@bentley/geometry-core"; -import { assert, Id64, Id64String } from "@bentley/bentleyjs-core"; -import { FeatureIndexType, RenderMode, ViewFlags, Frustum, FrustumPlanes, ElementAlignedBox3d } from "@bentley/imodeljs-common"; +import { Transform, Range3d } from "@bentley/geometry-core"; +import { Id64, Id64String, assert } from "@bentley/bentleyjs-core"; +import { FeatureIndexType, RenderMode, ViewFlags, Frustum, FrustumPlanes } from "@bentley/imodeljs-common"; import { System } from "./System"; import { Batch, Branch, Graphic, GraphicsArray } from "./Graphic"; import { Primitive } from "./Primitive"; @@ -16,9 +16,8 @@ import { ShaderProgramExecutor } from "./ShaderProgram"; import { RenderPass, RenderOrder, CompositeFlags } from "./RenderFlags"; import { Target } from "./Target"; import { BranchStack, BatchState } from "./BranchState"; -import { GraphicList, Decorations, RenderGraphic } from "../System"; +import { GraphicList, Decorations, RenderGraphic, AnimationBranchState } from "../System"; import { TechniqueId } from "./TechniqueId"; -import { SurfacePrimitive, SurfaceGeometry } from "./Surface"; import { SurfaceType } from "../primitives/VertexTable"; import { MeshGraphic } from "./Mesh"; @@ -26,10 +25,12 @@ export class ShaderProgramParams { private _target?: Target; private _renderPass: RenderPass = RenderPass.None; private readonly _projectionMatrix: Matrix4 = new Matrix4(); + private readonly _viewMatrix = new Matrix4(); public get target(): Target { assert(undefined !== this._target); return this._target!; } public get renderPass() { return this._renderPass; } public get projectionMatrix() { return this._projectionMatrix; } + public get viewMatrix() { return this._viewMatrix; } public get isViewCoords() { return RenderPass.ViewOverlay === this.renderPass || RenderPass.Background === this.renderPass; } public get isOverlayPass() { return RenderPass.WorldOverlay === this.renderPass || RenderPass.ViewOverlay === this.renderPass; } @@ -44,6 +45,8 @@ export class ShaderProgramParams { } else { Matrix4.fromMatrix4d(target.projectionMatrix, this._projectionMatrix); } + + Matrix4.fromTransform(this.target.viewMatrix, this._viewMatrix); } } @@ -54,17 +57,16 @@ export class DrawParams { private _geometry?: CachedGeometry; private readonly _modelViewMatrix = new Matrix4(); private readonly _modelMatrix = new Matrix4(); - private readonly _viewMatrix = new Matrix4(); public get geometry(): CachedGeometry { assert(undefined !== this._geometry); return this._geometry!; } public get programParams(): ShaderProgramParams { assert(undefined !== this._programParams); return this._programParams!; } public get modelViewMatrix() { return this._modelViewMatrix; } public get modelMatrix() { return this._modelMatrix; } - public get viewMatrix() { return this._viewMatrix; } public get target() { return this.programParams.target; } public get renderPass() { return this.programParams.renderPass; } public get projectionMatrix() { return this.programParams.projectionMatrix; } + public get viewMatrix() { return this.programParams.viewMatrix; } public get isViewCoords() { return this.programParams.isViewCoords; } public get isOverlayPass() { return this.programParams.isOverlayPass; } public get context() { return this.programParams.context; } @@ -74,7 +76,7 @@ export class DrawParams { if (undefined === pass) pass = programParams.renderPass; else - assert(pass === this.programParams.renderPass); // ###TODO remove this once confirmed it's redundant... + assert(pass === this.programParams.renderPass); this._geometry = geometry; Matrix4.fromTransform(modelMatrix, this._modelMatrix); @@ -88,8 +90,6 @@ export class DrawParams { modelViewMatrix = modelViewMatrix.multiplyTransformTransform(modelMatrix, modelViewMatrix); Matrix4.fromTransform(modelViewMatrix, this._modelViewMatrix); } - - Matrix4.fromTransform(this.target.viewMatrix, this._viewMatrix); } } @@ -104,6 +104,12 @@ export const enum PushOrPop { * to draw a primitive; others involve state changes such as pushing/popping transforms * and symbology overrides, which require that commands be executed in order. */ +export const enum OmitStatus { + Neutral = 0, + Begin = 1, + End = -1, +} + export abstract class DrawCommand { public preExecute(_exec: ShaderProgramExecutor): void { } public abstract execute(_exec: ShaderProgramExecutor): void; @@ -118,8 +124,10 @@ export abstract class DrawCommand { public get hasFeatureOverrides(): boolean { return FeatureIndexType.Empty !== this.featureIndexType; } public get renderOrder(): RenderOrder { return undefined !== this.primitive ? this.primitive.renderOrder : RenderOrder.BlankingRegion; } public get hasAnimation(): boolean { return undefined !== this.primitive ? this.primitive.hasAnimation : false; } + public get isInstanced(): boolean { return undefined !== this.primitive ? this.primitive.isInstanced : false; } public getRenderPass(target: Target): RenderPass { return undefined !== this.primitive ? this.primitive.getRenderPass(target) : RenderPass.None; } public getTechniqueId(target: Target): TechniqueId { return undefined !== this.primitive ? this.primitive.getTechniqueId(target) : TechniqueId.Invalid; } + public getOmitStatus(_target: Target) { return OmitStatus.Neutral; } public isPushCommand(branch?: Branch) { return PushOrPop.Push === this.pushOrPop && (undefined === branch || this.branch === branch); @@ -146,16 +154,28 @@ class BranchCommand extends DrawCommand { this._branch = branch; this._pushOrPop = pushOrPop; } + private getAnimationBranch(target: Target): AnimationBranchState | undefined { + return (this._branch.branch.animationId && target.animationBranches) ? target.animationBranches.get(this._branch.branch.animationId) : undefined; + } + + public getOmitStatus(target: Target): OmitStatus { + const animationBranch = this.getAnimationBranch(target); + return (animationBranch && animationBranch.omit) ? (this._pushOrPop === PushOrPop.Push ? OmitStatus.Begin : OmitStatus.End) : OmitStatus.Neutral; + } public preExecute(_exec: ShaderProgramExecutor): void { - if (this._branch.branch.animationId && _exec.target.animationBranches) { - const animationBranch = _exec.target.animationBranches.get(this._branch.branch.animationId); - if (animationBranch) { - if (animationBranch.transform) - this._branch.localToWorldTransform = animationBranch.transform; - if (animationBranch.clip) - this._branch.clips = animationBranch.clip; + const animationBranch = this.getAnimationBranch(_exec.target); + if (animationBranch) { + if (animationBranch.transform) { + let branchTransform = animationBranch.transform; + const prevLocalToWorld = _exec.target.currentTransform; + const prevWorldToLocal = prevLocalToWorld.inverse(); + if (prevLocalToWorld && prevWorldToLocal) + branchTransform = prevWorldToLocal.multiplyTransformTransform(branchTransform.multiplyTransformTransform(prevLocalToWorld)); + this._branch.localToWorldTransform = branchTransform; } + if (animationBranch.clip) + this._branch.clips = animationBranch.clip; } } @@ -199,15 +219,13 @@ export class BatchPrimitiveCommand extends PrimitiveCommand { } public computeIsFlashed(flashedId: Id64String): boolean { - if (this.primitive instanceof SurfacePrimitive) { - const sp = this.primitive as SurfacePrimitive; - if (undefined !== sp.meshData.features && sp.meshData.features.isUniform) { - const fi = sp.meshData.features.uniform!; - const featureElementId = this._batch.featureTable.findElementId(fi); - if (undefined !== featureElementId) { - return featureElementId.toString() === flashedId.toString(); - } - } + // ###TODO Can this be done in a less-ugly way? It's trying to determine if the batch's graphic is a classification primitive. + const sp = this.primitive.cachedGeometry.asSurface; + if (undefined !== sp && undefined !== sp.mesh.features && sp.mesh.features.isUniform) { + const fi = sp.mesh.features.uniform!; + const featureElementId = this._batch.featureTable.findElementId(fi); + if (undefined !== featureElementId) + return featureElementId.toString() === flashedId.toString(); } return Id64.isInvalid(flashedId); @@ -221,7 +239,7 @@ export type DrawCommands = DrawCommand[]; export class RenderCommands { private _frustumPlanes?: FrustumPlanes; private readonly _scratchFrustum = new Frustum(); - private readonly _scratchRange = new ElementAlignedBox3d(); + private readonly _scratchRange = new Range3d(); private readonly _commands: DrawCommands[]; private readonly _stack: BranchStack; // refers to the Target's BranchStack private readonly _batchState: BatchState; // refers to the Target's BatchState @@ -317,8 +335,6 @@ export class RenderCommands { } }); } - - // ###TODO: overlays } public addBackground(gf?: Graphic): void { @@ -479,7 +495,7 @@ export class RenderCommands { } public clear(): void { - assert(this._batchState.isEmpty, "BatchState should be cleared at end of frame"); + assert(this._batchState.isEmpty); this._clearCommands(); } private _clearCommands(): void { @@ -556,10 +572,9 @@ export class RenderCommands { public addPrimitive(prim: Primitive): void { if (undefined !== this._frustumPlanes) { // See if we can cull this primitive. - if (prim instanceof SurfacePrimitive && RenderPass.Classification === prim.getRenderPass(this.target)) { - const surf = prim as SurfacePrimitive; - if (surf.cachedGeometry instanceof SurfaceGeometry) { - const geom = surf.cachedGeometry as SurfaceGeometry; + if (RenderPass.Classification === prim.getRenderPass(this.target)) { + const geom = prim.cachedGeometry.asSurface; + if (undefined !== geom) { this._scratchRange.setNull(); const lowX = geom.qOrigin[0]; const lowY = geom.qOrigin[1]; diff --git a/core/frontend/src/render/webgl/FeatureDimensions.ts b/core/frontend/src/render/webgl/FeatureDimensions.ts index 547319b..a471092 100644 --- a/core/frontend/src/render/webgl/FeatureDimensions.ts +++ b/core/frontend/src/render/webgl/FeatureDimensions.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ /** @module WebGL */ -import { assert } from "@bentley/bentleyjs-core"; import { FeatureIndexType } from "@bentley/imodeljs-common"; import { System } from "./System"; +import { assert } from "@bentley/bentleyjs-core"; /** Describes the dimensionality of a texture used as a look-up table. */ export const enum LUTDimension { diff --git a/core/frontend/src/render/webgl/FloatRGBA.ts b/core/frontend/src/render/webgl/FloatRGBA.ts index 2a75003..f644166 100644 --- a/core/frontend/src/render/webgl/FloatRGBA.ts +++ b/core/frontend/src/render/webgl/FloatRGBA.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ /** @module WebGL */ -import { ColorDef } from "@bentley/imodeljs-common"; import { assert } from "@bentley/bentleyjs-core"; +import { ColorDef } from "@bentley/imodeljs-common"; import { UniformHandle } from "./Handle"; function assertComponent(c: number) { assert(1.0 >= c && 0.0 <= c); } diff --git a/core/frontend/src/render/webgl/FrameBuffer.ts b/core/frontend/src/render/webgl/FrameBuffer.ts index 3a15aac..3de81b6 100644 --- a/core/frontend/src/render/webgl/FrameBuffer.ts +++ b/core/frontend/src/render/webgl/FrameBuffer.ts @@ -4,12 +4,11 @@ *--------------------------------------------------------------------------------------------*/ /** @module WebGL */ -import { assert, IDisposable } from "@bentley/bentleyjs-core"; +import { IDisposable, assert } from "@bentley/bentleyjs-core"; import { TextureHandle } from "./Texture"; import { RenderBuffer } from "./RenderBuffer"; import { GL } from "./GL"; import { System } from "./System"; -import { debugPrint } from "./debugPrint"; export type DepthBuffer = RenderBuffer | TextureHandle; @@ -29,7 +28,6 @@ export class FrameBuffer implements IDisposable { public get isDisposed(): boolean { return this._fbo === undefined; } - public get isValid(): boolean { return System.instance.context.FRAMEBUFFER_COMPLETE === this.checkStatus(); } public get isBound(): boolean { return FrameBufferBindState.Bound === this._bindState || FrameBufferBindState.BoundWithAttachments === this._bindState; } public get isSuspended(): boolean { return FrameBufferBindState.Suspended === this._bindState; } public getColor(ndx: number): TextureHandle { @@ -87,14 +85,6 @@ export class FrameBuffer implements IDisposable { } } - private getDebugAttachmentsString(): string { - let str = "["; - for (const tx of this._colorTextures) - str += " " + (tx.getHandle()! as any)._debugId; - - return str + " ]"; - } - public bind(bindAttachments: boolean = false): boolean { assert(undefined !== this._fbo); assert(!this.isBound); @@ -109,9 +99,6 @@ export class FrameBuffer implements IDisposable { if (bindAttachments) { System.instance.setDrawBuffers(this._colorAttachments); this._bindState = FrameBufferBindState.BoundWithAttachments; - - if (TextureHandle.wantDebugIds) - debugPrint("Bound attachments " + this.getDebugAttachmentsString()); } else { this._bindState = FrameBufferBindState.Bound; } @@ -122,15 +109,9 @@ export class FrameBuffer implements IDisposable { assert(this.isBound); System.instance.context.bindFramebuffer(GL.FrameBuffer.TARGET, null); this._bindState = FrameBufferBindState.Unbound; - if (TextureHandle.wantDebugIds) - debugPrint("Unbound attachments " + this.getDebugAttachmentsString()); } public suspend() { assert(this.isBound); this._bindState = FrameBufferBindState.Suspended; } - public checkStatus(): GLenum { - const status: GLenum = System.instance.context.checkFramebufferStatus(GL.FrameBuffer.TARGET); - return status; - } // Chiefly for debugging currently - assumes RGBA, unsigned byte, want all pixels. public get debugPixels(): Uint8Array | undefined { diff --git a/core/frontend/src/render/webgl/GL.ts b/core/frontend/src/render/webgl/GL.ts index 048217c..c3ce6c1 100644 --- a/core/frontend/src/render/webgl/GL.ts +++ b/core/frontend/src/render/webgl/GL.ts @@ -216,6 +216,13 @@ export namespace GL { export namespace FrameBuffer { export const TARGET = WebGLRenderingContext.FRAMEBUFFER; + export enum Status { + Complete = WebGLRenderingContext.FRAMEBUFFER_COMPLETE, + IncompleteAttachment = WebGLRenderingContext.FRAMEBUFFER_INCOMPLETE_ATTACHMENT, + IncompleteMissingAttachment = WebGLRenderingContext.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT, + IncompleteDimensions = WebGLRenderingContext.FRAMEBUFFER_INCOMPLETE_DIMENSIONS, + Unsupported = WebGLRenderingContext.FRAMEBUFFER_UNSUPPORTED, + } } export enum BufferBit { diff --git a/core/frontend/src/render/webgl/Graphic.ts b/core/frontend/src/render/webgl/Graphic.ts index ccefe78..a119107 100644 --- a/core/frontend/src/render/webgl/Graphic.ts +++ b/core/frontend/src/render/webgl/Graphic.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ /** @module WebGL */ -import { assert, Id64, Id64String, BeTimePoint, IDisposable, dispose } from "@bentley/bentleyjs-core"; +import { Id64, Id64String, BeTimePoint, IDisposable, dispose, assert } from "@bentley/bentleyjs-core"; import { ViewFlags, ElementAlignedBox3d } from "@bentley/imodeljs-common"; import { Transform } from "@bentley/geometry-core"; import { Primitive } from "./Primitive"; @@ -85,7 +85,7 @@ export class FeatureOverrides implements IDisposable { // R = override flags (see FeatureOverrides::Flags) // G = line weight // B = line code - // A = unused + // A = 1 if no-locatable // [1] // RGB = rgb // A = alpha @@ -97,7 +97,7 @@ export class FeatureOverrides implements IDisposable { feature.elementId.lower, feature.elementId.upper, feature.subCategoryId.lower, feature.subCategoryId.upper, feature.geometryClass, - modelIdParts.lower, modelIdParts.upper, map.type); + modelIdParts.lower, modelIdParts.upper, map.type, feature.animationNodeId); if (undefined === app || app.isFullyTransparent) { // The feature is not visible. We don't care about any of the other overrides, because we're not going to render it. @@ -153,8 +153,10 @@ export class FeatureOverrides implements IDisposable { if (undefined !== flashedIdParts && feature.elementId.lower === flashedIdParts.lower && feature.elementId.upper === flashedIdParts.upper) flags |= OvrFlags.Flashed; + data.setByteAtIndex(dataIndex + 3, app.nonLocatable ? 1 : 0); + data.setOvrFlagsAtIndex(dataIndex, flags); - if (OvrFlags.None !== flags) + if (OvrFlags.None !== flags || app.nonLocatable) nOverridden++; } @@ -302,7 +304,6 @@ export class Batch extends Graphic { ret = FeatureOverrides.createFromTarget(target); this._overrides.push(ret); target.addBatch(this); - // ###TODO target.addBatch(*this); ret.initFromMap(this.featureTable); } diff --git a/core/frontend/src/render/webgl/Handle.ts b/core/frontend/src/render/webgl/Handle.ts index dc20e22..bafd1b5 100644 --- a/core/frontend/src/render/webgl/Handle.ts +++ b/core/frontend/src/render/webgl/Handle.ts @@ -191,12 +191,12 @@ export class AttributeHandle { System.instance.context.vertexAttribPointer(this._glId, size, type, normalized, stride, offset); } - public enableVertexAttribArray(): void { System.instance.enableVertexAttribArray(this._glId); } + public enableVertexAttribArray(instanced = false): void { System.instance.enableVertexAttribArray(this._glId, instanced); } - public enableArray(buffer: BufferHandle, size: number, type: GL.DataType, normalized: boolean, stride: number, offset: number): void { + public enableArray(buffer: BufferHandle, size: number, type: GL.DataType, normalized: boolean, stride: number, offset: number, instanced = false): void { buffer.bind(GL.Buffer.Target.ArrayBuffer); this.setVertexAttribPointer(size, type, normalized, stride, offset); - this.enableVertexAttribArray(); + this.enableVertexAttribArray(instanced); BufferHandle.unbind(GL.Buffer.Target.ArrayBuffer); } } diff --git a/core/frontend/src/render/webgl/InstancedGeometry.ts b/core/frontend/src/render/webgl/InstancedGeometry.ts new file mode 100644 index 0000000..23886ab --- /dev/null +++ b/core/frontend/src/render/webgl/InstancedGeometry.ts @@ -0,0 +1,94 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module WebGL */ + +import { assert, dispose } from "@bentley/bentleyjs-core"; +import { InstancedGraphicParams, RenderMemory } from "../System"; +import { CachedGeometry, LUTGeometry } from "./CachedGeometry"; +import { Target } from "./Target"; +import { ShaderProgramParams } from "./DrawCommand"; +import { AttributeHandle, BufferHandle } from "./Handle"; + +export class InstancedGeometry extends CachedGeometry { + public readonly numInstances: number; + public readonly transforms: BufferHandle; + public readonly featureIds?: BufferHandle; + private readonly _repr: LUTGeometry; + private readonly _ownsRepr: boolean; + + public get asInstanced() { return this; } + public get asLUT() { return this._repr.asLUT; } + public get asMesh() { return this._repr.asMesh; } + public get asSurface() { return this._repr.asSurface; } + public get asEdge() { return this._repr.asEdge; } + public get asSilhouette() { return this._repr.asSilhouette; } + + public get renderOrder() { return this._repr.renderOrder; } + public get isLitSurface() { return this._repr.isLitSurface; } + public get hasBakedLighting() { return this._repr.hasBakedLighting; } + public get hasAnimation() { return this._repr.hasAnimation; } + public get qOrigin() { return this._repr.qOrigin; } + public get qScale() { return this._repr.qScale; } + public get material() { return this._repr.material; } + public get polylineBuffers() { return this._repr.polylineBuffers; } + public set uniformFeatureIndices(value: number) { this._repr.uniformFeatureIndices = value; } + public get isEdge() { return this._repr.isEdge; } + public get featuresInfo() { return this._repr.featuresInfo; } + + public getTechniqueId(target: Target) { return this._repr.getTechniqueId(target); } + public getRenderPass(target: Target) { return this._repr.getRenderPass(target); } + public wantWoWReversal(params: ShaderProgramParams) { return this._repr.wantWoWReversal(params); } + public getLineCode(params: ShaderProgramParams) { return this._repr.getLineCode(params); } + public getLineWeight(params: ShaderProgramParams) { return this._repr.getLineWeight(params); } + + public static create(repr: LUTGeometry, ownsRepr: boolean, params: InstancedGraphicParams): InstancedGeometry | undefined { + const { count, transforms, featureIds } = params; + assert(count > 0 && Math.floor(count) === count); + assert(count === transforms.length / 12); + assert(undefined === featureIds || count === featureIds.length / 3); + + let idBuf: BufferHandle | undefined; + if (undefined !== featureIds && undefined === (idBuf = BufferHandle.createArrayBuffer(featureIds))) + return undefined; + + const tfBuf = BufferHandle.createArrayBuffer(transforms); + return undefined !== tfBuf ? new InstancedGeometry(repr, ownsRepr, count, tfBuf, idBuf) : undefined; + } + + private constructor(repr: LUTGeometry, ownsRepr: boolean, count: number, transforms: BufferHandle, featureIds?: BufferHandle) { + super(); + this._repr = repr; + this._ownsRepr = ownsRepr; + this.numInstances = count; + this.transforms = transforms; + this.featureIds = featureIds; + } + + public dispose() { + dispose(this.transforms); + dispose(this.featureIds); + if (this._ownsRepr) + this._repr.dispose(); + } + + public bindVertexArray(handle: AttributeHandle) { + this._repr.bindVertexArray(handle); + } + + protected _wantWoWReversal(_target: Target) { + assert(false, "Should never be called"); + return false; + } + + public draw() { + this._repr.drawInstanced(this.numInstances); + } + + public collectStatistics(stats: RenderMemory.Statistics) { + this._repr.collectStatistics(stats); + const bytesUsed = this.transforms.bytesUsed + (undefined !== this.featureIds ? this.featureIds.bytesUsed : 0); + stats.addInstances(bytesUsed); + } +} diff --git a/core/frontend/src/render/webgl/Mesh.ts b/core/frontend/src/render/webgl/Mesh.ts index d327175..239a9f4 100644 --- a/core/frontend/src/render/webgl/Mesh.ts +++ b/core/frontend/src/render/webgl/Mesh.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ /** @module WebGL */ -import { assert, IDisposable, dispose } from "@bentley/bentleyjs-core"; -import { RenderPass, RenderOrder } from "./RenderFlags"; -import { LUTGeometry, PolylineBuffers } from "./CachedGeometry"; -import { SurfaceType, MeshParams, SegmentEdgeParams, SilhouetteParams, TesselatedPolyline } from "../primitives/VertexTable"; +import { IDisposable, dispose } from "@bentley/bentleyjs-core"; +import { SurfaceFlags, RenderPass, RenderOrder } from "./RenderFlags"; +import { LUTGeometry, PolylineBuffers, CachedGeometry } from "./CachedGeometry"; +import { VertexIndices, SurfaceType, MeshParams, SegmentEdgeParams, SilhouetteParams, TesselatedPolyline } from "../primitives/VertexTable"; import { LineCode } from "./EdgeOverrides"; import { ColorInfo } from "./ColorInfo"; import { Graphic, Batch } from "./Graphic"; @@ -17,15 +17,14 @@ import { Primitive } from "./Primitive"; import { FloatPreMulRgba } from "./FloatRGBA"; import { ShaderProgramParams, RenderCommands } from "./DrawCommand"; import { Target } from "./Target"; -import { SurfacePrimitive } from "./Surface"; import { Material } from "./Material"; import { Texture } from "./Texture"; -import { FillFlags, RenderMode, LinePixels } from "@bentley/imodeljs-common"; +import { FillFlags, RenderMode, LinePixels, ViewFlags } from "@bentley/imodeljs-common"; import { System } from "./System"; import { BufferHandle, AttributeHandle } from "./Handle"; import { GL } from "./GL"; import { TechniqueId } from "./TechniqueId"; -import { RenderMemory } from "../System"; +import { InstancedGraphicParams, RenderMemory } from "../System"; export class MeshData implements IDisposable { public readonly edgeWidth: number; @@ -54,7 +53,7 @@ export class MeshData implements IDisposable { } public static create(params: MeshParams): MeshData | undefined { - const lut = VertexLUT.createFromVertexTable(params.vertices); + const lut = VertexLUT.createFromVertexTable(params.vertices, params.auxChannels); return undefined !== lut ? new MeshData(lut, params) : undefined; } @@ -67,36 +66,38 @@ export class MeshData implements IDisposable { export class MeshGraphic extends Graphic { public readonly meshData: MeshData; - private readonly _primitives: MeshPrimitive[] = []; + private readonly _primitives: Primitive[] = []; - public static create(params: MeshParams): MeshGraphic | undefined { + public static create(params: MeshParams, instances?: InstancedGraphicParams): MeshGraphic | undefined { const data = MeshData.create(params); - return undefined !== data ? new MeshGraphic(data, params) : undefined; + return undefined !== data ? new MeshGraphic(data, params, instances) : undefined; } - private addPrimitive(primitive?: MeshPrimitive) { + private addPrimitive(createGeom: () => CachedGeometry | undefined, instances?: InstancedGraphicParams) { + const primitive = Primitive.create(createGeom, instances); if (undefined !== primitive) this._primitives.push(primitive); } - private constructor(data: MeshData, params: MeshParams) { + private constructor(data: MeshData, params: MeshParams, instances?: InstancedGraphicParams) { super(); this.meshData = data; - this.addPrimitive(SurfacePrimitive.create(params.surface, this)); + this.addPrimitive(() => SurfaceGeometry.create(this.meshData, params.surface.indices), instances); // Classifiers are surfaces only...no edges. if (this.surfaceType === SurfaceType.Classifier || undefined === params.edges) return; - if (undefined !== params.edges.silhouettes) - this.addPrimitive(SilhouetteEdgePrimitive.create(params.edges.silhouettes, this)); + const edges = params.edges; + if (undefined !== edges.silhouettes) + this.addPrimitive(() => SilhouetteEdgeGeometry.createSilhouettes(this.meshData, edges.silhouettes!), instances); - if (undefined !== params.edges.segments) - this.addPrimitive(EdgePrimitive.create(params.edges.segments, this)); + if (undefined !== edges.segments) + this.addPrimitive(() => EdgeGeometry.create(this.meshData, edges.segments!), instances); - if (undefined !== params.edges.polylines) - this.addPrimitive(PolylineEdgePrimitive.create(params.edges.polylines, this)); + if (undefined !== edges.polylines) + this.addPrimitive(() => PolylineEdgeGeometry.create(this.meshData, edges.polylines!), instances); } public dispose() { @@ -123,27 +124,29 @@ export class MeshGraphic extends Graphic { // Defines one aspect of the geometry of a mesh (surface or edges) export abstract class MeshGeometry extends LUTGeometry { - protected readonly _mesh: MeshData; + public readonly mesh: MeshData; protected readonly _numIndices: number; + public get asMesh() { return this; } + // Convenience accessors... - public get edgeWidth() { return this._mesh.edgeWidth; } - public get edgeLineCode() { return this._mesh.edgeLineCode; } - public get featuresInfo(): FeaturesInfo | undefined { return this._mesh.features; } - public get surfaceType() { return this._mesh.type; } - public get fillFlags() { return this._mesh.fillFlags; } - public get isPlanar() { return this._mesh.isPlanar; } - public get colorInfo(): ColorInfo { return this._mesh.lut.colorInfo; } + public get edgeWidth() { return this.mesh.edgeWidth; } + public get edgeLineCode() { return this.mesh.edgeLineCode; } + public get featuresInfo(): FeaturesInfo | undefined { return this.mesh.features; } + public get surfaceType() { return this.mesh.type; } + public get fillFlags() { return this.mesh.fillFlags; } + public get isPlanar() { return this.mesh.isPlanar; } + public get colorInfo(): ColorInfo { return this.mesh.lut.colorInfo; } public get uniformColor(): FloatPreMulRgba | undefined { return this.colorInfo.isUniform ? this.colorInfo.uniform : undefined; } - public get texture() { return this._mesh.texture; } - public get hasBakedLighting() { return this._mesh.hasBakedLighting; } - public get lut() { return this._mesh.lut; } - public get hasScalarAnimation() { return this._mesh.lut.auxParams !== undefined; } + public get texture() { return this.mesh.texture; } + public get hasBakedLighting() { return this.mesh.hasBakedLighting; } + public get lut() { return this.mesh.lut; } + public get hasScalarAnimation() { return this.mesh.lut.hasScalarAnimation; } protected constructor(mesh: MeshData, numIndices: number) { super(); this._numIndices = numIndices; - this._mesh = mesh; + this.mesh = mesh; } protected computeEdgeWeight(params: ShaderProgramParams): number { return params.target.getEdgeWeight(params, this.edgeWidth); } @@ -161,23 +164,14 @@ export abstract class MeshGeometry extends LUTGeometry { } } -export abstract class MeshPrimitive extends Primitive { - public readonly mesh: MeshGraphic; // is not owned (mesh is the owner of THIS MeshPrimitive) - - public get meshData(): MeshData { return this.mesh.meshData; } - - protected constructor(cachedGeom: MeshGeometry, mesh: MeshGraphic) { - super(cachedGeom); - this.mesh = mesh; - } - - public assignUniformFeatureIndices(_index: number) { assert(false); } // handled by MeshGraphic... -} - export class EdgeGeometry extends MeshGeometry { protected readonly _indices: BufferHandle; protected readonly _endPointAndQuadIndices: BufferHandle; + public get asSurface() { return undefined; } + public get asEdge() { return this; } + public get asSilhouette(): SilhouetteEdgeGeometry | undefined { return undefined; } + public static create(mesh: MeshData, edges: SegmentEdgeParams): EdgeGeometry | undefined { const indexBuffer = BufferHandle.createArrayBuffer(edges.indices.data); const endPointBuffer = BufferHandle.createArrayBuffer(edges.endPointAndQuadIndices); @@ -190,18 +184,16 @@ export class EdgeGeometry extends MeshGeometry { } public collectStatistics(stats: RenderMemory.Statistics): void { - stats.addVisibleEdges(this._indices.bytesUsed); - stats.addVisibleEdges(this._endPointAndQuadIndices.bytesUsed); + stats.addVisibleEdges(this._indices.bytesUsed + this._endPointAndQuadIndices.bytesUsed); } public bindVertexArray(attr: AttributeHandle): void { attr.enableArray(this._indices, 3, GL.DataType.UnsignedByte, false, 0, 0); } - public draw(): void { - const gl = System.instance.context; + protected _draw(numInstances: number): void { this._indices.bind(GL.Buffer.Target.ArrayBuffer); - gl.drawArrays(GL.PrimitiveType.Triangles, 0, this._numIndices); + System.instance.drawArrays(GL.PrimitiveType.Triangles, 0, this._numIndices, numInstances); } protected _wantWoWReversal(_target: Target): boolean { return true; } @@ -220,21 +212,11 @@ export class EdgeGeometry extends MeshGeometry { } } -export class EdgePrimitive extends MeshPrimitive { - public static create(params: SegmentEdgeParams, mesh: MeshGraphic): EdgePrimitive | undefined { - const geom = EdgeGeometry.create(mesh.meshData, params); - return undefined !== geom ? new EdgePrimitive(geom, mesh) : undefined; - } - - private constructor(cachedGeom: EdgeGeometry, mesh: MeshGraphic) { super(cachedGeom, mesh); } - - public get renderOrder(): RenderOrder { return this.meshData.isPlanar ? RenderOrder.PlanarEdge : RenderOrder.Edge; } - public get isEdge(): boolean { return true; } -} - export class SilhouetteEdgeGeometry extends EdgeGeometry { private readonly _normalPairs: BufferHandle; + public get asSilhouette() { return this; } + public static createSilhouettes(mesh: MeshData, params: SilhouetteParams): SilhouetteEdgeGeometry | undefined { const indexBuffer = BufferHandle.createArrayBuffer(params.indices.data); const endPointBuffer = BufferHandle.createArrayBuffer(params.endPointAndQuadIndices); @@ -248,9 +230,7 @@ export class SilhouetteEdgeGeometry extends EdgeGeometry { } public collectStatistics(stats: RenderMemory.Statistics): void { - stats.addSilhouetteEdges(this._indices.bytesUsed); - stats.addSilhouetteEdges(this._endPointAndQuadIndices.bytesUsed); - stats.addSilhouetteEdges(this._normalPairs.bytesUsed); + stats.addSilhouetteEdges(this._indices.bytesUsed + this._endPointAndQuadIndices.bytesUsed + this._normalPairs.bytesUsed); } public getTechniqueId(_target: Target): TechniqueId { return TechniqueId.SilhouetteEdge; } @@ -262,19 +242,6 @@ export class SilhouetteEdgeGeometry extends EdgeGeometry { this._normalPairs = normalPairs; } } - -export class SilhouetteEdgePrimitive extends MeshPrimitive { - public static create(params: SilhouetteParams, mesh: MeshGraphic): SilhouetteEdgePrimitive | undefined { - const geom = SilhouetteEdgeGeometry.createSilhouettes(mesh.meshData, params); - return undefined !== geom ? new SilhouetteEdgePrimitive(geom, mesh) : undefined; - } - - private constructor(cachedGeom: SilhouetteEdgeGeometry, mesh: MeshGraphic) { super(cachedGeom, mesh); } - - public get renderOrder(): RenderOrder { return this.meshData.isPlanar ? RenderOrder.PlanarSilhouette : RenderOrder.Silhouette; } - public get isEdge(): boolean { return true; } -} - export class PolylineEdgeGeometry extends MeshGeometry { private _buffers: PolylineBuffers; @@ -303,10 +270,10 @@ export class PolylineEdgeGeometry extends MeshGeometry { attr.enableArray(this._buffers.indices, 3, GL.DataType.UnsignedByte, false, 0, 0); } - public draw(): void { - const gl = System.instance.context; + protected _draw(numInstances: number): void { + const gl = System.instance; this._buffers.indices.bind(GL.Buffer.Target.ArrayBuffer); - gl.drawArrays(GL.PrimitiveType.Triangles, 0, this._numIndices); + gl.drawArrays(GL.PrimitiveType.Triangles, 0, this._numIndices, numInstances); } private constructor(numIndices: number, buffers: PolylineBuffers, mesh: MeshData) { @@ -315,14 +282,198 @@ export class PolylineEdgeGeometry extends MeshGeometry { } } -export class PolylineEdgePrimitive extends MeshPrimitive { - public static create(polyline: TesselatedPolyline, mesh: MeshGraphic): PolylineEdgePrimitive | undefined { - const geom = PolylineEdgeGeometry.create(mesh.meshData, polyline); - return undefined !== geom ? new PolylineEdgePrimitive(geom, mesh) : undefined; +function wantMaterials(vf: ViewFlags) { return vf.materials && RenderMode.SmoothShade === vf.renderMode; } +function wantLighting(vf: ViewFlags) { + return RenderMode.SmoothShade === vf.renderMode && (vf.sourceLights || vf.cameraLights || vf.solarLight); +} + +export class SurfaceGeometry extends MeshGeometry { + private readonly _indices: BufferHandle; + + public static create(mesh: MeshData, indices: VertexIndices): SurfaceGeometry | undefined { + const indexBuffer = BufferHandle.createArrayBuffer(indices.data); + return undefined !== indexBuffer ? new SurfaceGeometry(indexBuffer, indices.length, mesh) : undefined; + } + + public dispose() { + dispose(this._indices); + } + + public collectStatistics(stats: RenderMemory.Statistics): void { + stats.addSurface(this._indices.bytesUsed); + } + + public get isLit() { return SurfaceType.Lit === this.surfaceType || SurfaceType.TexturedLit === this.surfaceType; } + public get isTextured() { return SurfaceType.Textured === this.surfaceType || SurfaceType.TexturedLit === this.surfaceType; } + public get isGlyph() { return undefined !== this.texture && this.texture.isGlyph; } + public get isTileSection() { return undefined !== this.texture && this.texture.isTileSection; } + public get isClassifier() { return SurfaceType.Classifier === this.surfaceType; } + + public get asSurface() { return this; } + public get asEdge() { return undefined; } + public get asSilhouette() { return undefined; } + + public bindVertexArray(attr: AttributeHandle): void { + attr.enableArray(this._indices, 3, GL.DataType.UnsignedByte, false, 0, 0); + } + + protected _draw(numInstances: number): void { + const system = System.instance; + const gl = system.context; + const offset = RenderOrder.BlankingRegion === this.renderOrder; + if (offset) { + gl.enable(GL.POLYGON_OFFSET_FILL); + gl.polygonOffset(1.0, 1.0); + } + + this._indices.bind(GL.Buffer.Target.ArrayBuffer); + system.drawArrays(GL.PrimitiveType.Triangles, 0, this._numIndices, numInstances); + + if (offset) { + gl.disable(GL.POLYGON_OFFSET_FILL); + } + } + + public getTechniqueId(_target: Target) { return TechniqueId.Surface; } + public get isLitSurface() { return this.isLit; } + public get hasBakedLighting() { return this.mesh.hasBakedLighting; } + public get renderOrder(): RenderOrder { + if (FillFlags.Behind === (this.fillFlags & FillFlags.Behind)) + return RenderOrder.BlankingRegion; + else + return this.isPlanar ? RenderOrder.PlanarSurface : RenderOrder.Surface; + } + + public getColor(target: Target) { + if (FillFlags.Background === (this.fillFlags & FillFlags.Background)) + return ColorInfo.createFromColorDef(target.bgColor); + else + return this.colorInfo; + } + + public getRenderPass(target: Target): RenderPass { + if (this.isClassifier) + return RenderPass.Classification; + + const mat = this.isLit ? this.mesh.material : undefined; + const opaquePass = this.isPlanar ? RenderPass.OpaquePlanar : RenderPass.OpaqueGeneral; + const fillFlags = this.fillFlags; + + if (this.isGlyph && target.isReadPixelsInProgress) + return opaquePass; + + const vf = target.currentViewFlags; + if (RenderMode.Wireframe === vf.renderMode) { + const showFill = FillFlags.Always === (fillFlags & FillFlags.Always) || (vf.fill && FillFlags.ByView === (fillFlags & FillFlags.ByView)); + if (!showFill) { + return RenderPass.None; + } + } + if (!this.isGlyph) { + if (!vf.transparency || RenderMode.SolidFill === vf.renderMode || RenderMode.HiddenLine === vf.renderMode) { + return opaquePass; + } + } + if (undefined !== this.texture && this.wantTextures(target, true)) { + if (this.texture.hasTranslucency) + return RenderPass.Translucent; + + // material may have texture weight < 1 - if so must account for material or element alpha below + if (undefined === mat || (mat.textureMapping !== undefined && mat.textureMapping.params.weight >= 1)) + return opaquePass; + } + + const hasAlpha = (undefined !== mat && wantMaterials(vf) && mat.hasTranslucency) || this.getColor(target).hasTranslucency; + return hasAlpha ? RenderPass.Translucent : opaquePass; } - private constructor(cachedGeom: PolylineEdgeGeometry, mesh: MeshGraphic) { super(cachedGeom, mesh); } + protected _wantWoWReversal(target: Target): boolean { + const fillFlags = this.fillFlags; + if (FillFlags.None !== (fillFlags & FillFlags.Background)) + return false; // fill color explicitly from background + + if (FillFlags.None !== (fillFlags & FillFlags.Always)) + return true; // fill displayed even in wireframe + + const vf = target.currentViewFlags; + if (RenderMode.Wireframe === vf.renderMode || vf.visibleEdges) + return false; // never invert surfaces when edges are displayed + + if (this.isLit && wantLighting(vf)) + return false; - public get renderOrder(): RenderOrder { return this.meshData.isPlanar ? RenderOrder.PlanarEdge : RenderOrder.Edge; } - public get isEdge(): boolean { return true; } + // Don't invert white pixels of textures... + return !this.wantTextures(target, this.isTextured); + } + public get material(): Material | undefined { return this.mesh.material; } + + public computeSurfaceFlags(params: ShaderProgramParams): SurfaceFlags { + const target = params.target; + const vf = target.currentViewFlags; + + let flags = wantMaterials(vf) ? SurfaceFlags.None : SurfaceFlags.IgnoreMaterial; + if (this.isLit) { + flags |= SurfaceFlags.HasNormals; + if (wantLighting(vf)) { + flags |= SurfaceFlags.ApplyLighting; + } + + // Textured meshes store normal in place of color index. + // Untextured lit meshes store normal where textured meshes would store UV coords. + // Tell shader where to find normal. + if (!this.isTextured) { + flags |= SurfaceFlags.HasColorAndNormal; + } + } + + if (this.wantTextures(target, this.isTextured)) { + flags |= SurfaceFlags.HasTexture; + } + + switch (params.renderPass) { + // NB: We need this for opaque pass due to SolidFill (must compute transparency, discard below threshold, render opaque at or above threshold) + case RenderPass.OpaqueLinear: + case RenderPass.OpaquePlanar: + case RenderPass.OpaqueGeneral: + case RenderPass.Translucent: { + const mode = vf.renderMode; + if (!this.isGlyph && (RenderMode.HiddenLine === mode || RenderMode.SolidFill === mode)) { + flags |= SurfaceFlags.TransparencyThreshold; + if (RenderMode.HiddenLine === mode && FillFlags.Always !== (this.fillFlags & FillFlags.Always)) { + // fill flags test for text - doesn't render with bg fill in hidden line mode. + flags |= SurfaceFlags.BackgroundFill; + } + break; + } + } + } + + return flags; + } + + private constructor(indices: BufferHandle, numIndices: number, mesh: MeshData) { + super(mesh, numIndices); + this._indices = indices; + } + + private wantTextures(target: Target, surfaceTextureExists: boolean): boolean { + if (this.hasScalarAnimation && undefined !== target.analysisTexture) + return true; + + if (!surfaceTextureExists) + return false; + + if (this.isGlyph) { + return true; + } + const fill = this.fillFlags; + const flags = target.currentViewFlags; + + // ###TODO need to distinguish between gradient fill and actual textures... + switch (flags.renderMode) { + case RenderMode.SmoothShade: return flags.textures; + case RenderMode.Wireframe: return FillFlags.Always === (fill & FillFlags.Always) || (flags.fill && FillFlags.ByView === (fill & FillFlags.ByView)); + default: return FillFlags.Always === (fill & FillFlags.Always); + } + } } diff --git a/core/frontend/src/render/webgl/PointCloud.ts b/core/frontend/src/render/webgl/PointCloud.ts index e06b4ea..965f5c7 100644 --- a/core/frontend/src/render/webgl/PointCloud.ts +++ b/core/frontend/src/render/webgl/PointCloud.ts @@ -5,8 +5,6 @@ /** @module WebGL */ import { PointCloudArgs } from "../primitives/PointCloudPrimitive"; import { FeaturesInfo } from "./FeaturesInfo"; -import { RenderCommands } from "./DrawCommand"; -import { Primitive } from "./Primitive"; import { CachedGeometry } from "./CachedGeometry"; import { AttributeHandle, QBufferHandle3d, BufferHandle } from "./Handle"; import { TechniqueId } from "./TechniqueId"; @@ -17,19 +15,6 @@ import { System } from "./System"; import { dispose } from "@bentley/bentleyjs-core"; import { RenderMemory } from "../System"; -export class PointCloudPrimitive extends Primitive { - public get renderOrder(): RenderOrder { return RenderOrder.Surface; } - public addCommands(commands: RenderCommands): void { commands.addPrimitive(this); } - public dispose(): void { - } - public static create(args: PointCloudArgs) { - return new PointCloudPrimitive(args); - } - - private constructor(args: PointCloudArgs) { - super(new PointCloudGeometry(args)); - } -} export class PointCloudGeometry extends CachedGeometry { private _vertices: QBufferHandle3d; private _vertexCount: number; @@ -48,9 +33,8 @@ export class PointCloudGeometry extends CachedGeometry { } public collectStatistics(stats: RenderMemory.Statistics): void { - stats.addPointCloud(this._vertices.bytesUsed); - if (undefined !== this._colorHandle) - stats.addPointCloud(this._colorHandle.bytesUsed); + const bytesUsed = this._vertices.bytesUsed + (undefined !== this._colorHandle ? this._colorHandle.bytesUsed : 0); + stats.addPointCloud(bytesUsed); } protected _wantWoWReversal(_target: Target): boolean { return false; } diff --git a/core/frontend/src/render/webgl/PointString.ts b/core/frontend/src/render/webgl/PointString.ts index b80d1e1..88e20d1 100644 --- a/core/frontend/src/render/webgl/PointString.ts +++ b/core/frontend/src/render/webgl/PointString.ts @@ -6,9 +6,8 @@ import { dispose } from "@bentley/bentleyjs-core"; import { QParams3d } from "@bentley/imodeljs-common"; -import { Primitive } from "./Primitive"; import { Target } from "./Target"; -import { CachedGeometry, LUTGeometry } from "./CachedGeometry"; +import { LUTGeometry } from "./CachedGeometry"; import { RenderPass, RenderOrder } from "./RenderFlags"; import { TechniqueId } from "./TechniqueId"; import { PointStringParams } from "../primitives/VertexTable"; @@ -50,10 +49,10 @@ export class PointStringGeometry extends LUTGeometry { protected _getLineWeight(_params: ShaderProgramParams): number { return this.weight; } - public draw(): void { - const gl = System.instance.context; + protected _draw(numInstances: number): void { + const gl = System.instance; this.indices.bind(GL.Buffer.Target.ArrayBuffer); - gl.drawArrays(GL.PrimitiveType.Points, 0, this.numIndices); + gl.drawArrays(GL.PrimitiveType.Points, 0, this.numIndices, numInstances); } public static create(params: PointStringParams): PointStringGeometry | undefined { @@ -78,14 +77,3 @@ export class PointStringGeometry extends LUTGeometry { stats.addPointString(this.indices.bytesUsed); } } - -export class PointStringPrimitive extends Primitive { - public static create(params: PointStringParams): PointStringPrimitive | undefined { - const geom = PointStringGeometry.create(params); - return undefined !== geom ? new PointStringPrimitive(geom) : undefined; - } - - private constructor(cachedGeom: CachedGeometry) { super(cachedGeom); } - - public get renderOrder(): RenderOrder { return RenderOrder.Linear; } -} diff --git a/core/frontend/src/render/webgl/Polyline.ts b/core/frontend/src/render/webgl/Polyline.ts index fc41c38..2d6d026 100644 --- a/core/frontend/src/render/webgl/Polyline.ts +++ b/core/frontend/src/render/webgl/Polyline.ts @@ -6,9 +6,8 @@ import { QParams3d, RenderMode, PolylineTypeFlags } from "@bentley/imodeljs-common"; import { PolylineParams } from "../primitives/VertexTable"; -import { Primitive } from "./Primitive"; import { Target } from "./Target"; -import { CachedGeometry, LUTGeometry, PolylineBuffers } from "./CachedGeometry"; +import { LUTGeometry, PolylineBuffers } from "./CachedGeometry"; import { RenderPass, RenderOrder } from "./RenderFlags"; import { TechniqueId } from "./TechniqueId"; import { AttributeHandle } from "./Handle"; @@ -76,7 +75,7 @@ export class PolylineGeometry extends LUTGeometry { if (RenderMode.SmoothShade === vf.renderMode && !vf.visibleEdges) return RenderPass.None; - // Only want to return Translucent for edges if rendering in Wireframe mode TODO: what about overrides? + // Only want to return Translucent for edges if rendering in Wireframe mode ###TODO: what about overrides? const isTranslucent: boolean = RenderMode.Wireframe === vf.renderMode && vf.transparency && colorInfo.hasTranslucency; return isTranslucent ? RenderPass.Translucent : RenderPass.OpaqueLinear; } @@ -114,10 +113,10 @@ export class PolylineGeometry extends LUTGeometry { attr.enableArray(this._buffers!.indices, 3, GL.DataType.UnsignedByte, false, 0, 0); } - public draw(): void { - const gl = System.instance.context; + protected _draw(numInstances: number): void { + const gl = System.instance; this._buffers!.indices.bind(GL.Buffer.Target.ArrayBuffer); - gl.drawArrays(GL.PrimitiveType.Triangles, 0, this.numIndices); + gl.drawArrays(GL.PrimitiveType.Triangles, 0, this.numIndices, numInstances); } public static create(params: PolylineParams): PolylineGeometry | undefined { @@ -132,16 +131,3 @@ export class PolylineGeometry extends LUTGeometry { return new PolylineGeometry(lut, buffers, params); } } - -export class PolylinePrimitive extends Primitive { - private constructor(cachedGeom: CachedGeometry) { super(cachedGeom); } - - public static create(params: PolylineParams): PolylinePrimitive | undefined { - const geom = PolylineGeometry.create(params); - return undefined !== geom ? new PolylinePrimitive(geom) : undefined; - } - - public get renderOrder(): RenderOrder { return (this.cachedGeometry as PolylineGeometry).renderOrder; } - public get isPlanar(): boolean { return (this.cachedGeometry as PolylineGeometry).isPlanar; } - public get isEdge(): boolean { return (this.cachedGeometry as PolylineGeometry).isEdge; } -} diff --git a/core/frontend/src/render/webgl/Primitive.ts b/core/frontend/src/render/webgl/Primitive.ts index 2df9668..848f1f9 100644 --- a/core/frontend/src/render/webgl/Primitive.ts +++ b/core/frontend/src/render/webgl/Primitive.ts @@ -7,21 +7,47 @@ import { FeatureIndexType } from "@bentley/imodeljs-common"; import { Target } from "./Target"; import { Graphic, Batch } from "./Graphic"; -import { CachedGeometry } from "./CachedGeometry"; +import { CachedGeometry, LUTGeometry } from "./CachedGeometry"; import { RenderPass, RenderOrder } from "./RenderFlags"; import { ShaderProgramExecutor } from "./ShaderProgram"; import { DrawParams, RenderCommands, DrawCommand } from "./DrawCommand"; import { TechniqueId } from "./TechniqueId"; -import { FeaturesInfo } from "./FeaturesInfo"; -import { dispose } from "@bentley/bentleyjs-core"; +import { assert, dispose } from "@bentley/bentleyjs-core"; import { System } from "./System"; -import { RenderMemory } from "../System"; +import { InstancedGraphicParams, RenderMemory } from "../System"; +import { InstancedGeometry } from "./InstancedGeometry"; -export abstract class Primitive extends Graphic { +export class Primitive extends Graphic { public cachedGeometry: CachedGeometry; public isPixelMode: boolean = false; - public constructor(cachedGeom: CachedGeometry) { super(); this.cachedGeometry = cachedGeom; } + protected constructor(cachedGeom: CachedGeometry) { super(); this.cachedGeometry = cachedGeom; } + + // ###TODO_INSTANCING: Remove me when feature complete... + private static _forceInstancing = false; + private static _fakeInstanceParams = { + transforms: new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0]), + featureIds: new Uint8Array([0, 0, 0]), + count: 1, + }; + + public static create(createGeom: () => CachedGeometry | undefined, instances?: InstancedGraphicParams): Primitive | undefined { + let geom = createGeom(); + if (undefined === geom) + return undefined; + + const doInstancing = undefined !== instances || (this._forceInstancing && geom instanceof LUTGeometry); + assert(geom instanceof LUTGeometry || !doInstancing, "Invalid geometry type for instancing"); + if (doInstancing) { + assert(geom instanceof LUTGeometry, "Invalid geometry type for instancing"); + if (undefined === instances) + instances = this._fakeInstanceParams; + + geom = InstancedGeometry.create(geom as LUTGeometry, true, instances); + } + + return undefined !== geom ? new this(geom) : undefined; + } public dispose() { dispose(this.cachedGeometry); @@ -39,9 +65,7 @@ export abstract class Primitive extends Graphic { public get featureIndexType(): FeatureIndexType { const feature = this.cachedGeometry.featuresInfo; - if (feature instanceof FeaturesInfo) - return feature.type; - return FeatureIndexType.Empty; + return undefined !== feature ? feature.type : FeatureIndexType.Empty; } public get usesMaterialColor(): boolean { @@ -49,10 +73,6 @@ export abstract class Primitive extends Graphic { return undefined !== materialData && (materialData.overridesRgb || materialData.overridesAlpha); } - public get isLit(): boolean { return this.cachedGeometry.isLitSurface; } - - public get hasAnimation(): boolean { return this.cachedGeometry.hasAnimation; } - public addCommands(commands: RenderCommands): void { commands.addPrimitive(this); } public addHiliteCommands(commands: RenderCommands, batch: Batch, pass: RenderPass): void { @@ -64,13 +84,14 @@ export abstract class Primitive extends Graphic { } public setUniformFeatureIndices(featId: number): void { this.cachedGeometry.uniformFeatureIndices = featId; } - - public get isEdge(): boolean { return false; } + public get hasAnimation(): boolean { return this.cachedGeometry.hasAnimation; } + public get isInstanced(): boolean { return this.cachedGeometry.isInstanced; } + public get isLit(): boolean { return this.cachedGeometry.isLitSurface; } + public get isEdge(): boolean { return this.cachedGeometry.isEdge; } + public get renderOrder(): RenderOrder { return this.cachedGeometry.renderOrder; } public toPrimitive(): Primitive { return this; } - public abstract get renderOrder(): RenderOrder; - private static _drawParams?: DrawParams; public draw(shader: ShaderProgramExecutor): void { @@ -84,8 +105,6 @@ export abstract class Primitive extends Graphic { } public getTechniqueId(target: Target): TechniqueId { return this.cachedGeometry.getTechniqueId(target); } - - public get debugString(): string { return this.cachedGeometry.debugString; } } export class SkyBoxPrimitive extends Primitive { @@ -104,16 +123,4 @@ export class SkyBoxPrimitive extends Primitive { System.instance.context.viewport(0, 0, vw, vh); // Restore viewport } - - public get renderOrder(): RenderOrder { return RenderOrder.Surface; } -} - -export class SkySpherePrimitive extends Primitive { - public constructor(cachedGeom: CachedGeometry) { super(cachedGeom); } - public get renderOrder(): RenderOrder { return RenderOrder.Surface; } -} - -export class PointCloudPrimitive extends Primitive { - public constructor(cachedGeom: CachedGeometry) { super(cachedGeom); } - public get renderOrder(): RenderOrder { return RenderOrder.Surface; } } diff --git a/core/frontend/src/render/webgl/RenderFlags.ts b/core/frontend/src/render/webgl/RenderFlags.ts index 3b40284..1ad8d07 100644 --- a/core/frontend/src/render/webgl/RenderFlags.ts +++ b/core/frontend/src/render/webgl/RenderFlags.ts @@ -52,6 +52,7 @@ export enum TextureUnit { PickDepthAndOrder = Four, VertexLUT = Five, + AuxChannelLUT = Six, } /** @@ -112,11 +113,6 @@ export const enum SurfaceFlags { // For textured meshes, the color index in the vertex LUT is unused - we place the normal there instead. // For untextured lit meshes, the normal is placed after the feature ID. HasColorAndNormal = 1 << 6, - - // For materials with reflectivity. - EnvironmentMap = 1 << 7, - - // NB: No more available unless we increase size of SurfaceFlags } export const enum OvrFlags { diff --git a/core/frontend/src/render/webgl/SceneCompositor.ts b/core/frontend/src/render/webgl/SceneCompositor.ts index 2439925..b3b2e2e 100644 --- a/core/frontend/src/render/webgl/SceneCompositor.ts +++ b/core/frontend/src/render/webgl/SceneCompositor.ts @@ -21,6 +21,7 @@ import { CompositeFlags, RenderPass, RenderOrder } from "./RenderFlags"; import { FloatRgba } from "./FloatRGBA"; import { BatchState } from "./BranchState"; import { Feature } from "@bentley/imodeljs-common"; +import { Debug } from "./Diagnostics"; let progParams: ShaderProgramParams | undefined; let drawParams: DrawParams | undefined; @@ -92,17 +93,25 @@ class Textures implements IDisposable { this.featureId = TextureHandle.createForAttachment(width, height, GL.Texture.Format.Rgba, GL.Texture.DataType.UnsignedByte); this.depthAndOrder = TextureHandle.createForAttachment(width, height, GL.Texture.Format.Rgba, GL.Texture.DataType.UnsignedByte); - this.occlusion = TextureHandle.createForAttachment(width, height, GL.Texture.Format.Rgba, GL.Texture.DataType.UnsignedByte); - this.occlusionBlur = TextureHandle.createForAttachment(width, height, GL.Texture.Format.Rgba, GL.Texture.DataType.UnsignedByte); - return undefined !== this.accumulation && undefined !== this.revealage && undefined !== this.color && undefined !== this.featureId && undefined !== this.depthAndOrder - && undefined !== this.hilite - && undefined !== this.occlusion - && undefined !== this.occlusionBlur; + && undefined !== this.hilite; + } + + public enableOcclusion(width: number, height: number): boolean { + assert(undefined === this.occlusion && undefined === this.occlusionBlur); + this.occlusion = TextureHandle.createForAttachment(width, height, GL.Texture.Format.Rgba, GL.Texture.DataType.UnsignedByte); + this.occlusionBlur = TextureHandle.createForAttachment(width, height, GL.Texture.Format.Rgba, GL.Texture.DataType.UnsignedByte); + return undefined !== this.occlusion && undefined !== this.occlusionBlur; + } + + public disableOcclusion(): void { + assert(undefined !== this.occlusion && undefined !== this.occlusionBlur); + this.occlusion = dispose(this.occlusion); + this.occlusionBlur = dispose(this.occlusionBlur); } } @@ -127,19 +136,27 @@ class FrameBuffers implements IDisposable { this.depthAndOrder = FrameBuffer.create([textures.depthAndOrder!], depth); this.hilite = FrameBuffer.create([textures.hilite!]); this.hiliteUsingStencil = FrameBuffer.create([textures.hilite!], depth); - if (DepthType.TextureUnsignedInt24Stencil8 === System.instance.capabilities.maxDepthType) { + + if (DepthType.TextureUnsignedInt24Stencil8 === System.instance.capabilities.maxDepthType) this.stencilSet = FrameBuffer.create([], depth); - } - this.occlusion = FrameBuffer.create([textures.occlusion!]); - this.occlusionBlur = FrameBuffer.create([textures.occlusionBlur!]); return undefined !== this.opaqueColor && undefined !== this.opaqueAndCompositeColor && undefined !== this.depthAndOrder && undefined !== this.hilite - && undefined !== this.hiliteUsingStencil - && undefined !== this.occlusion - && undefined !== this.occlusionBlur; + && undefined !== this.hiliteUsingStencil; + } + + public toggleOcclusion(textures: Textures): void { + if (undefined !== textures.occlusion) { + assert(undefined !== textures.occlusionBlur); + this.occlusion = FrameBuffer.create([textures.occlusion]); + this.occlusionBlur = FrameBuffer.create([textures.occlusionBlur!]); + } else { + assert(undefined === textures.occlusionBlur); + this.occlusion = dispose(this.occlusion); + this.occlusionBlur = dispose(this.occlusionBlur); + } } public dispose() { @@ -164,15 +181,32 @@ class Geometry implements IDisposable { public init(textures: Textures): boolean { assert(undefined === this.composite); - this.composite = CompositeGeometry.createGeometry(textures.color!.getHandle()!, textures.accumulation!.getHandle()!, textures.revealage!.getHandle()!, textures.hilite!.getHandle()!, textures.occlusion!.getHandle()!); + this.composite = CompositeGeometry.createGeometry( + textures.color!.getHandle()!, + textures.accumulation!.getHandle()!, + textures.revealage!.getHandle()!, textures.hilite!.getHandle()!); + this.stencilCopy = ViewportQuadGeometry.create(TechniqueId.CopyStencil); - assert(textures.depthAndOrder !== undefined); - this.occlusion = AmbientOcclusionGeometry.createGeometry(textures.depthAndOrder!.getHandle()!); - this.occlusionXBlur = BlurGeometry.createGeometry(textures.occlusion!.getHandle()!, textures.depthAndOrder!.getHandle()!, new Vector2d(1.0, 0.0)); - this.occlusionYBlur = BlurGeometry.createGeometry(textures.occlusionBlur!.getHandle()!, textures.depthAndOrder!.getHandle()!, new Vector2d(0.0, 1.0)); + return undefined !== this.composite; } + public toggleOcclusion(textures: Textures): void { + if (undefined !== textures.occlusion) { + assert(undefined !== textures.occlusionBlur); + this.composite!.occlusion = textures.occlusion.getHandle(); + this.occlusion = AmbientOcclusionGeometry.createGeometry(textures.depthAndOrder!.getHandle()!); + this.occlusionXBlur = BlurGeometry.createGeometry(textures.occlusion!.getHandle()!, textures.depthAndOrder!.getHandle()!, new Vector2d(1.0, 0.0)); + this.occlusionYBlur = BlurGeometry.createGeometry(textures.occlusionBlur!.getHandle()!, textures.depthAndOrder!.getHandle()!, new Vector2d(0.0, 1.0)); + } else { + assert(undefined === textures.occlusionBlur); + this.composite!.occlusion = undefined; + this.occlusion = dispose(this.occlusion); + this.occlusionXBlur = dispose(this.occlusionXBlur); + this.occlusionYBlur = dispose(this.occlusionYBlur); + } + } + public dispose() { this.composite = dispose(this.composite); this.stencilCopy = dispose(this.stencilCopy); @@ -368,6 +402,7 @@ export abstract class SceneCompositor implements IDisposable { abstract class Compositor extends SceneCompositor { protected _width: number = -1; protected _height: number = -1; + protected _includeOcclusion: boolean = false; protected _textures = new Textures(); protected _depth?: DepthBuffer; protected _frameBuffers: FrameBuffers; @@ -402,8 +437,8 @@ abstract class Compositor extends SceneCompositor { System.instance.applyRenderState(RenderState.defaults); const params = getDrawParams(this.target, this._geom.occlusion!); this.target.techniques.draw(params); - }); + this.target.recordPerformanceMetric("Compute AO"); // Render the X-blurred ambient occlusion based on unblurred ambient occlusion fbo = this._frameBuffers.occlusionBlur!; @@ -412,6 +447,7 @@ abstract class Compositor extends SceneCompositor { const params = getDrawParams(this.target, this._geom.occlusionXBlur!); this.target.techniques.draw(params); }); + this.target.recordPerformanceMetric("Blur AO X"); // Render the Y-blurred ambient occlusion based on X-blurred ambient occlusion (render into original occlusion framebuffer) fbo = this._frameBuffers.occlusion!; @@ -420,6 +456,7 @@ abstract class Compositor extends SceneCompositor { const params = getDrawParams(this.target, this._geom.occlusionYBlur!); this.target.techniques.draw(params); }); + this.target.recordPerformanceMetric("Blur AO Y"); } protected constructor(target: Target, fbos: FrameBuffers, geometry: Geometry) { @@ -429,6 +466,7 @@ abstract class Compositor extends SceneCompositor { this._geom = geometry; this._opaqueRenderState.flags.depthTest = true; + // this._opaqueRenderState.flags.cull = true; ###TODO: Want backface culling but breaks edge display (presumably incorrect winding order) this._translucentRenderState.flags.depthMask = false; this._translucentRenderState.flags.blend = this._translucentRenderState.flags.depthTest = true; @@ -482,17 +520,37 @@ abstract class Compositor extends SceneCompositor { const rect = this.target.viewRect; const width = rect.width; const height = rect.height; + const includeOcclusion = this.target.wantAmbientOcclusion; - if (this._textures.accumulation !== undefined && width === this._width && height === this._height) { - return true; + // If not yet initialized, or dimensions changed, initialize. + if (undefined === this._textures.accumulation || width !== this._width || height !== this._height) { + this._width = width; + this._height = height; + + // init() first calls dispose(), which releases all of our fbos, textures, etc, and resets the _includeOcclusion flag. + if (!this.init()) { + assert(false, "Failed to initialize scene compositor"); + return false; + } } - this._width = width; - this._height = height; + // Allocate or free ambient occlusion-related resources if necessary + if (includeOcclusion !== this._includeOcclusion) { + this._includeOcclusion = includeOcclusion; + if (includeOcclusion) { + if (!this._textures.enableOcclusion(width, height)) { + assert(false, "Failed to initialize occlusion textures"); + return false; + } + } else { + this._textures.disableOcclusion(); + } - const result = this.init(); - assert(result); - return result; + this._frameBuffers.toggleOcclusion(this._textures); + this._geom.toggleOcclusion(this._textures); + } + + return true; } public draw(commands: RenderCommands) { @@ -509,37 +567,37 @@ abstract class Compositor extends SceneCompositor { // Render the background this.renderBackground(commands, needComposite); - if (this.target.performanceMetrics) this.target.performanceMetrics.recordTime("Render Background"); + this.target.recordPerformanceMetric("Render Background"); // Render the sky box this.renderSkyBox(commands, needComposite); - if (this.target.performanceMetrics) this.target.performanceMetrics.recordTime("Render SkyBox"); + this.target.recordPerformanceMetric("Render SkyBox"); // Render the terrain this.renderTerrain(commands, needComposite); - if (this.target.performanceMetrics) this.target.performanceMetrics.recordTime("Render Terrain"); + this.target.recordPerformanceMetric("Render Terrain"); // Enable clipping this.target.pushActiveVolume(); - if (this.target.performanceMetrics) this.target.performanceMetrics.recordTime("Enable Clipping"); + this.target.recordPerformanceMetric("Enable Clipping"); // Render opaque geometry this.renderOpaque(commands, compositeFlags, false); - if (this.target.performanceMetrics) this.target.performanceMetrics.recordTime("Render Opaque"); + this.target.recordPerformanceMetric("Render Opaque"); // Render stencil volumes this.renderClassification(commands, needComposite, false); - if (this.target.performanceMetrics) this.target.performanceMetrics.recordTime("Render Stencils"); + this.target.recordPerformanceMetric("Render Stencils"); if (needComposite) { this._geom.composite!.update(compositeFlags); this.clearTranslucent(); this.renderTranslucent(commands); - if (this.target.performanceMetrics) this.target.performanceMetrics.recordTime("Render Translucent"); + this.target.recordPerformanceMetric("Render Translucent"); this.renderHilite(commands); - if (this.target.performanceMetrics) this.target.performanceMetrics.recordTime("Render Hilite"); + this.target.recordPerformanceMetric("Render Hilite"); this.composite(); - if (this.target.performanceMetrics) this.target.performanceMetrics.recordTime("Composite"); + this.target.recordPerformanceMetric("Composite"); } this.target.popActiveVolume(); } @@ -607,7 +665,7 @@ abstract class Compositor extends SceneCompositor { } private readFrameBuffer(rect: ViewRect, fbo?: FrameBuffer): Uint8Array | undefined { - if (undefined === fbo || !fbo.isValid) + if (undefined === fbo || !Debug.isValidFrameBuffer) return undefined; // NB: ViewRect origin at top-left; GL origin at bottom-left @@ -628,6 +686,7 @@ abstract class Compositor extends SceneCompositor { public dispose() { this._depth = dispose(this._depth); + this._includeOcclusion = false; dispose(this._textures); dispose(this._frameBuffers); dispose(this._geom); @@ -637,7 +696,9 @@ abstract class Compositor extends SceneCompositor { this.dispose(); this._depth = System.instance.createDepthBuffer(this._width, this._height); if (this._depth !== undefined) { - return this._textures.init(this._width, this._height) && this._frameBuffers.init(this._textures, this._depth) && this._geom.init(this._textures); + return this._textures.init(this._width, this._height) + && this._frameBuffers.init(this._textures, this._depth) + && this._geom.init(this._textures); } return false; } @@ -1182,7 +1243,6 @@ class MPCompositor extends Compositor { } protected renderOpaque(commands: RenderCommands, compositeFlags: CompositeFlags, renderForReadPixels: boolean): void { - // ###TODO: Support ambient occlusion. // Output the first 2 passes to color and pick data buffers. (All 3 in the case of rendering for readPixels()). this._readPickDataFromPingPong = true; const needComposite = CompositeFlags.None !== compositeFlags; diff --git a/core/frontend/src/render/webgl/ShaderBuilder.ts b/core/frontend/src/render/webgl/ShaderBuilder.ts index b511691..dba314e 100644 --- a/core/frontend/src/render/webgl/ShaderBuilder.ts +++ b/core/frontend/src/render/webgl/ShaderBuilder.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ /** @module WebGL */ -import { assert } from "@bentley/bentleyjs-core"; import { ShaderProgram } from "./ShaderProgram"; -import { GLSLVertex, addPosition } from "./glsl/Vertex"; +import { GLSLVertex, addPosition, addInstancedModelMatrix } from "./glsl/Vertex"; import { System } from "./System"; import { addClipping } from "./glsl/Clipping"; import { ClipDef } from "./TechniqueFlags"; import { ClippingType } from "../System"; +import { assert } from "@bentley/bentleyjs-core"; /** Describes the data type of a shader program variable. */ export const enum VariableType { @@ -316,6 +316,16 @@ export class SourceBuilder { public addMain(implementation: string): void { this.addFunction("void main()", implementation); } } +export const enum ShaderBuilderFlags { + // No special flags. Vertex data comes from attributes, geometry is not instanced. + None = 0, + // Vertex data comes from a texture. + VertexTable = 1 << 0, + // Geometry is instanced. + Instanced = 1 << 1, + InstancedVertexTable = VertexTable | Instanced, +} + /* * Represents a fragment or vertex shader under construction. The shader consists of a set of defined variables, * plus a set of code snippets which can be concatenated together to form the shader source. @@ -325,10 +335,15 @@ export class ShaderBuilder extends ShaderVariables { public readonly functions: string[] = new Array(); public readonly extensions: string[] = new Array(); public headerComment: string = ""; + protected readonly _flags: ShaderBuilderFlags; + + public get usesVertexTable() { return ShaderBuilderFlags.None !== (this._flags & ShaderBuilderFlags.VertexTable); } + public get usesInstancedGeometry() { return ShaderBuilderFlags.None !== (this._flags & ShaderBuilderFlags.Instanced); } - protected constructor(maxComponents: number) { - super(); // dumb but required. superclass has no explicit constructor. + protected constructor(maxComponents: number, flags: ShaderBuilderFlags) { + super(); this.components.length = maxComponents; + this._flags = flags; } protected addComponent(index: number, component: string): void { @@ -414,7 +429,17 @@ export class ShaderBuilder extends ShaderVariables { // Variable declarations src.add(this.buildDeclarations()); - if (isFrag) { + if (!isFrag) { + if (!this.usesInstancedGeometry) { + src.addline("#define MAT_MV u_mv"); + src.addline("#define MAT_MVP u_mvp"); + src.addline("#define MAT_NORM u_nmx"); + } else { + src.addline("#define MAT_MV g_mv"); + src.addline("#define MAT_MVP g_mvp"); + src.addline("#define MAT_NORM g_nmx"); + } + } else { src.addline("#define FragColor gl_FragColor"); if (needMultiDrawBuffers) { src.addline("#define FragColor0 gl_FragData[0]"); @@ -424,17 +449,7 @@ export class ShaderBuilder extends ShaderVariables { } if (isLit) { - /* ###TODO: Source Lighting - // ###TODO: May end up needing to change this to 8 for unrolled lighting loop...see ShaderBuilder.cpp... - const maxShaderLights = 64; - src.addline("const int kMaxShaderLights = " + maxShaderLights); - src.addline("#define LightColor(i) u_lightData[i*3+0].rgb"); - src.addline("#define LightAtten1(i) u_lightData[i*3+0].a"); - src.addline("#define LightPos(i) u_lightData[i*3+1].xyz"); - src.addline("#define cosHTheta(i) u_lightData[i*3+1].w"); - src.addline("#define LightDir(i) u_lightData[i*3+2].xyz"); - src.addline("#define cosHPhi(i) u_lightData[i*3+2].w"); - */ + // ###TODO: Source Lighting } } @@ -451,6 +466,9 @@ export class ShaderBuilder extends ShaderVariables { // Describes the optional and required components which can be assembled into complete export const enum VertexShaderComponent { + // (Optional) Adjust the result of unquantizeVertexPosition(). + // vec4 adjustRawPosition(vec4 rawPosition) + AdjustRawPosition, // (Optional) Return true to discard this vertex before evaluating feature overrides etc, given the model-space position. // bool checkForEarlyDiscard(vec4 rawPos) CheckForEarlyDiscard, @@ -481,9 +499,12 @@ export class VertexShaderBuilder extends ShaderBuilder { private buildPrelude(): SourceBuilder { return this.buildPreludeCommon(); } - public constructor(positionFromLUT: boolean, private _isAnimated: boolean) { - super(VertexShaderComponent.COUNT); - addPosition(this, positionFromLUT); + public constructor(flags: ShaderBuilderFlags) { + super(VertexShaderComponent.COUNT, flags); + if (this.usesInstancedGeometry) + addInstancedModelMatrix(this); + + addPosition(this, this.usesVertexTable); } public get(id: VertexShaderComponent): string | undefined { return this.getComponent(id); } @@ -514,8 +535,11 @@ export class VertexShaderBuilder extends ShaderBuilder { } main.addline(" vec4 rawPosition = unquantizeVertexPosition(a_pos, u_qOrigin, u_qScale);"); - if (this._isAnimated) - main.addline(" rawPosition.xyz += computeAnimationDisplacement(g_vertexLUTIndex, u_animDispParams.x, u_animDispParams.y, u_animDispParams.z, u_qAnimDispOrigin, u_qAnimDispScale);"); + const adjustRawPosition = this.get(VertexShaderComponent.AdjustRawPosition); + if (undefined !== adjustRawPosition) { + prelude.addFunction("vec4 adjustRawPosition(vec4 rawPos)", adjustRawPosition); + main.addline(" rawPosition = adjustRawPosition(rawPosition);"); + } const checkForEarlyDiscard = this.get(VertexShaderComponent.CheckForEarlyDiscard); if (undefined !== checkForEarlyDiscard) { @@ -604,8 +628,8 @@ export const enum FragmentShaderComponent { export class FragmentShaderBuilder extends ShaderBuilder { public maxClippingPlanes: number = 0; - public constructor() { - super(FragmentShaderComponent.COUNT); + public constructor(flags: ShaderBuilderFlags) { + super(FragmentShaderComponent.COUNT, flags); } public get(id: FragmentShaderComponent): string | undefined { return this.getComponent(id); } @@ -789,10 +813,12 @@ export const enum ShaderType { export class ProgramBuilder { public readonly vert: VertexShaderBuilder; public readonly frag: FragmentShaderBuilder; + private readonly _flags: ShaderBuilderFlags; - public constructor(positionFromLUT: boolean, isAnimated: boolean = false) { - this.vert = new VertexShaderBuilder(positionFromLUT, isAnimated); - this.frag = new FragmentShaderBuilder(); + public constructor(flags = ShaderBuilderFlags.None) { + this.vert = new VertexShaderBuilder(flags); + this.frag = new FragmentShaderBuilder(flags); + this._flags = flags; // only needed for clone - though could loook up from vert or frag shader. } private addVariable(v: ShaderVariable, which: ShaderType) { @@ -852,7 +878,7 @@ export class ProgramBuilder { /** Returns a deep copy of this program builder. */ public clone(): ProgramBuilder { - const clone = new ProgramBuilder(false); + const clone = new ProgramBuilder(this._flags); // Copy from vertex builder clone.vert.headerComment = this.vert.headerComment; diff --git a/core/frontend/src/render/webgl/ShaderProgram.ts b/core/frontend/src/render/webgl/ShaderProgram.ts index 333f7d0..c2ec549 100644 --- a/core/frontend/src/render/webgl/ShaderProgram.ts +++ b/core/frontend/src/render/webgl/ShaderProgram.ts @@ -21,6 +21,7 @@ export const enum ShaderFlags { NonUniformColor = 1 << 1, OITFlatAlphaWeight = 1 << 2, OITScaleOutput = 1 << 3, + IgnoreNonLocatable = 1 << 4, } /** Describes the location of a uniform variable within a shader program. */ @@ -152,7 +153,7 @@ export class ShaderProgram implements IDisposable { const glProgram = gl.createProgram(); this._glProgram = (null === glProgram) ? undefined : glProgram; - // ###TODO: Silencing 'unused variable' warnings temporarily... + // Silencing 'unused variable' warnings temporarily... assert(undefined !== this._description); } diff --git a/core/frontend/src/render/webgl/Surface.ts b/core/frontend/src/render/webgl/Surface.ts deleted file mode 100644 index e961739..0000000 --- a/core/frontend/src/render/webgl/Surface.ts +++ /dev/null @@ -1,232 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ -/** @module WebGL */ - -import { dispose } from "@bentley/bentleyjs-core"; -import { FillFlags, ViewFlags, RenderMode } from "@bentley/imodeljs-common"; -import { SurfaceFlags, RenderPass, RenderOrder } from "./RenderFlags"; -import { SurfaceType, SurfaceParams, VertexIndices } from "../primitives/VertexTable"; -import { MeshData, MeshGeometry, MeshPrimitive, MeshGraphic } from "./Mesh"; -import { System } from "./System"; -import { BufferHandle, AttributeHandle } from "./Handle"; -import { GL } from "./GL"; -import { TechniqueId } from "./TechniqueId"; -import { Target } from "./Target"; -import { ColorInfo } from "./ColorInfo"; -import { ShaderProgramParams } from "./DrawCommand"; -import { Material } from "./Material"; -import { RenderMemory } from "../System"; - -function wantMaterials(vf: ViewFlags) { return vf.materials && RenderMode.SmoothShade === vf.renderMode; } -function wantLighting(vf: ViewFlags) { - return RenderMode.SmoothShade === vf.renderMode && (vf.sourceLights || vf.cameraLights || vf.solarLight); -} - -export class SurfaceGeometry extends MeshGeometry { - private readonly _indices: BufferHandle; - - public static create(mesh: MeshData, indices: VertexIndices): SurfaceGeometry | undefined { - const indexBuffer = BufferHandle.createArrayBuffer(indices.data); - return undefined !== indexBuffer ? new SurfaceGeometry(indexBuffer, indices.length, mesh) : undefined; - } - - public dispose() { - dispose(this._indices); - } - - public collectStatistics(stats: RenderMemory.Statistics): void { - stats.addSurface(this._indices.bytesUsed); - } - - public get isLit() { return SurfaceType.Lit === this.surfaceType || SurfaceType.TexturedLit === this.surfaceType; } - public get isTextured() { return SurfaceType.Textured === this.surfaceType || SurfaceType.TexturedLit === this.surfaceType; } - public get isGlyph() { return undefined !== this.texture && this.texture.isGlyph; } - public get isTileSection() { return undefined !== this.texture && this.texture.isTileSection; } - public get isClassifier() { return SurfaceType.Classifier === this.surfaceType; } - - public bindVertexArray(attr: AttributeHandle): void { - attr.enableArray(this._indices, 3, GL.DataType.UnsignedByte, false, 0, 0); - } - - public draw(): void { - const gl = System.instance.context; - const offset = RenderOrder.BlankingRegion === this.renderOrder; - if (offset) { - gl.enable(GL.POLYGON_OFFSET_FILL); - gl.polygonOffset(1.0, 1.0); - } - - this._indices.bind(GL.Buffer.Target.ArrayBuffer); - gl.drawArrays(GL.PrimitiveType.Triangles, 0, this._numIndices); - - if (offset) { - gl.disable(GL.POLYGON_OFFSET_FILL); - } - } - - public getTechniqueId(_target: Target) { return TechniqueId.Surface; } - public get isLitSurface() { return this.isLit; } - public get hasBakedLighting() { return this._mesh.hasBakedLighting; } - public get renderOrder(): RenderOrder { - if (FillFlags.Behind === (this.fillFlags & FillFlags.Behind)) - return RenderOrder.BlankingRegion; - else - return this.isPlanar ? RenderOrder.PlanarSurface : RenderOrder.Surface; - } - - public getColor(target: Target) { - if (FillFlags.Background === (this.fillFlags & FillFlags.Background)) - return ColorInfo.createFromColorDef(target.bgColor); - else - return this.colorInfo; - } - - public getRenderPass(target: Target): RenderPass { - if (this.isClassifier) - return RenderPass.Classification; - - const mat = this.isLit ? this._mesh.material : undefined; - const opaquePass = this.isPlanar ? RenderPass.OpaquePlanar : RenderPass.OpaqueGeneral; - const fillFlags = this.fillFlags; - - if (this.isGlyph && target.isReadPixelsInProgress) - return opaquePass; - - const vf = target.currentViewFlags; - if (RenderMode.Wireframe === vf.renderMode) { - const showFill = FillFlags.Always === (fillFlags & FillFlags.Always) || (vf.fill && FillFlags.ByView === (fillFlags & FillFlags.ByView)); - if (!showFill) { - return RenderPass.None; - } - } - if (!this.isGlyph) { - if (!vf.transparency || RenderMode.SolidFill === vf.renderMode || RenderMode.HiddenLine === vf.renderMode) { - return opaquePass; - } - } - if (undefined !== this.texture && this.wantTextures(target, true)) { - if (this.texture.hasTranslucency) - return RenderPass.Translucent; - - // material may have texture weight < 1 - if so must account for material or element alpha below - if (undefined === mat || (mat.textureMapping !== undefined && mat.textureMapping.params.weight >= 1)) - return opaquePass; - } - - const hasAlpha = (undefined !== mat && wantMaterials(vf) && mat.hasTranslucency) || this.getColor(target).hasTranslucency; - return hasAlpha ? RenderPass.Translucent : opaquePass; - } - - protected _wantWoWReversal(target: Target): boolean { - const fillFlags = this.fillFlags; - if (FillFlags.None !== (fillFlags & FillFlags.Background)) - return false; // fill color explicitly from background - - if (FillFlags.None !== (fillFlags & FillFlags.Always)) - return true; // fill displayed even in wireframe - - const vf = target.currentViewFlags; - if (RenderMode.Wireframe === vf.renderMode || vf.visibleEdges) - return false; // never invert surfaces when edges are displayed - - if (this.isLit && wantLighting(vf)) - return false; - - // Don't invert white pixels of textures... - return !this.wantTextures(target, this.isTextured); - } - public get material(): Material | undefined { return this._mesh.material; } - - public computeSurfaceFlags(params: ShaderProgramParams): SurfaceFlags { - const target = params.target; - const vf = target.currentViewFlags; - - let flags = wantMaterials(vf) ? SurfaceFlags.None : SurfaceFlags.IgnoreMaterial; - if (this.isLit) { - flags |= SurfaceFlags.HasNormals; - if (wantLighting(vf)) { - flags |= SurfaceFlags.ApplyLighting; - if (undefined !== target.environmentMap) { - flags |= SurfaceFlags.EnvironmentMap; - } - } - - // Textured meshes store normal in place of color index. - // Untextured lit meshes store normal where textured meshes would store UV coords. - // Tell shader where to find normal. - if (!this.isTextured) { - flags |= SurfaceFlags.HasColorAndNormal; - } - } - - if (this.wantTextures(target, this.isTextured)) { - flags |= SurfaceFlags.HasTexture; - } - - switch (params.renderPass) { - // NB: We need this for opaque pass due to SolidFill (must compute transparency, discard below threshold, render opaque at or above threshold) - case RenderPass.OpaqueLinear: - case RenderPass.OpaquePlanar: - case RenderPass.OpaqueGeneral: - case RenderPass.Translucent: { - const mode = vf.renderMode; - if (!this.isGlyph && (RenderMode.HiddenLine === mode || RenderMode.SolidFill === mode)) { - flags |= SurfaceFlags.TransparencyThreshold; - if (RenderMode.HiddenLine === mode && FillFlags.Always !== (this.fillFlags & FillFlags.Always)) { - // fill flags test for text - doesn't render with bg fill in hidden line mode. - flags |= SurfaceFlags.BackgroundFill; - } - break; - } - } - } - - return flags; - } - - private constructor(indices: BufferHandle, numIndices: number, mesh: MeshData) { - super(mesh, numIndices); - this._indices = indices; - } - - private wantTextures(target: Target, surfaceTextureExists: boolean): boolean { - if (this.hasScalarAnimation && undefined !== target.analysisTexture) - return true; - - if (!surfaceTextureExists) - return false; - - if (this.isGlyph) { - return true; - } - const fill = this.fillFlags; - const flags = target.currentViewFlags; - - // ###TODO need to distinguish between gradient fill and actual textures... - switch (flags.renderMode) { - case RenderMode.SmoothShade: return flags.textures; - case RenderMode.Wireframe: return FillFlags.Always === (fill & FillFlags.Always) || (flags.fill && FillFlags.ByView === (fill & FillFlags.ByView)); - default: return FillFlags.Always === (fill & FillFlags.Always); - } - } -} - -export class SurfacePrimitive extends MeshPrimitive { - public static create(params: SurfaceParams, mesh: MeshGraphic): SurfacePrimitive | undefined { - const geom = SurfaceGeometry.create(mesh.meshData, params.indices); - return undefined !== geom ? new SurfacePrimitive(geom, mesh) : undefined; - } - - private constructor(cachedGeom: SurfaceGeometry, mesh: MeshGraphic) { - super(cachedGeom, mesh); - } - - public get renderOrder(): RenderOrder { - if (FillFlags.Behind === (this.meshData.fillFlags & FillFlags.Behind)) - return RenderOrder.BlankingRegion; - else - return this.meshData.isPlanar ? RenderOrder.PlanarSurface : RenderOrder.Surface; - } -} diff --git a/core/frontend/src/render/webgl/System.ts b/core/frontend/src/render/webgl/System.ts index b7765fc..7377b52 100644 --- a/core/frontend/src/render/webgl/System.ts +++ b/core/frontend/src/render/webgl/System.ts @@ -9,7 +9,17 @@ import { ClipVector, Transform, Point3d, ClipUtilities, PolyfaceBuilder, Point2d, IndexedPolyface, Range3d, IndexedPolyfaceVisitor, Triangulator, StrokeOptions, HalfEdgeGraph, HalfEdge, HalfEdgeMask, } from "@bentley/geometry-core"; -import { RenderGraphic, GraphicBranch, RenderSystem, RenderTarget, RenderClipVolume, GraphicList, PackedFeatureTable } from "../System"; +import { + InstancedGraphicParams, + RenderGraphic, + GraphicBranch, + RenderSystem, + RenderDiagnostics, + RenderTarget, + RenderClipVolume, + GraphicList, + PackedFeatureTable, +} from "../System"; import { SkyBox } from "../../DisplayStyleState"; import { OnScreenTarget, OffScreenTarget } from "./Target"; import { GraphicBuilder, GraphicType } from "../GraphicBuilder"; @@ -19,7 +29,7 @@ import { PointStringParams, MeshParams, PolylineParams } from "../primitives/Ver import { MeshArgs } from "../primitives/mesh/MeshPrimitives"; import { Branch, Batch, GraphicsArray } from "./Graphic"; import { IModelConnection } from "../../IModelConnection"; -import { BentleyStatus, assert, Dictionary, IDisposable, dispose, Id64String } from "@bentley/bentleyjs-core"; +import { assert, BentleyStatus, Dictionary, IDisposable, dispose, Id64String } from "@bentley/bentleyjs-core"; import { Techniques } from "./Technique"; import { IModelApp } from "../../IModelApp"; import { ViewRect, Viewport } from "../../Viewport"; @@ -28,21 +38,18 @@ import { FrameBufferStack, DepthBuffer } from "./FrameBuffer"; import { RenderBuffer } from "./RenderBuffer"; import { TextureHandle, Texture, TextureMonitor } from "./Texture"; import { GL } from "./GL"; -import { PolylinePrimitive } from "./Polyline"; -import { PointStringPrimitive } from "./PointString"; +import { PolylineGeometry } from "./Polyline"; +import { PointStringGeometry } from "./PointString"; import { MeshGraphic } from "./Mesh"; -import { PointCloudPrimitive } from "./PointCloud"; +import { PointCloudGeometry } from "./PointCloud"; import { LineCode } from "./EdgeOverrides"; import { Material } from "./Material"; import { SkyBoxQuadsGeometry, SkySphereViewportQuadGeometry } from "./CachedGeometry"; -import { SkyBoxPrimitive, SkySpherePrimitive } from "./Primitive"; +import { SkyBoxPrimitive, Primitive } from "./Primitive"; import { ClipPlanesVolume, ClipMaskVolume } from "./ClipVolume"; import { TextureUnit } from "./RenderFlags"; import { UniformHandle } from "./Handle"; - -function debugPrint(_str: string): void { - // console.log(_str); // tslint:disable-line:no-console -} +import { Debug } from "./Diagnostics"; export const enum ContextState { Uninitialized, @@ -106,6 +113,7 @@ export class Capabilities { /** These getters check for existence of extension objects to determine availability of features. In WebGL2, could just return true for some. */ public get supportsNonPowerOf2Textures(): boolean { return false; } public get supportsDrawBuffers(): boolean { return this.queryExtensionObject("WEBGL_draw_buffers") !== undefined; } + public get supportsInstancing(): boolean { return this.queryExtensionObject("ANGLE_instanced_arrays") !== undefined; } public get supports32BitElementIndex(): boolean { return this.queryExtensionObject("OES_element_index_uint") !== undefined; } public get supportsTextureFloat(): boolean { return this.queryExtensionObject("OES_texture_float") !== undefined; } public get supportsTextureHalfFloat(): boolean { return this.queryExtensionObject("OES_texture_half_float") !== undefined; } @@ -135,7 +143,7 @@ export class Capabilities { for (const ext of extensions) { if ((!forceNoDrawBuffers && ext === "WEBGL_draw_buffers") || ext === "OES_element_index_uint" || (!forceHalfFloat && ext === "OES_texture_float") || ext === "OES_texture_half_float" || ext === "WEBGL_depth_texture" || ext === "EXT_color_buffer_float" || - ext === "EXT_shader_texture_lod") { + ext === "EXT_shader_texture_lod" || ext === "ANGLE_instanced_arrays") { const extObj: any = gl.getExtension(ext); // This call enables the extension and returns a WebGLObject containing extension instance. if (null !== extObj) this._extensionMap[ext] = extObj; @@ -159,7 +167,7 @@ export class Capabilities { // this._maxDepthType = this.queryExtensionObject("WEBGL_depth_texture") !== undefined ? DepthType.TextureUnsignedInt32 : DepthType.RenderBufferUnsignedShort16; this._maxDepthType = this.queryExtensionObject("WEBGL_depth_texture") !== undefined ? DepthType.TextureUnsignedInt24Stencil8 : DepthType.RenderBufferUnsignedShort16; - this.debugPrint(gl); // uses debugPrint at top of file; uncomment console.log in there to activate this. + this.debugPrint(gl); // Return based on currently-required features. This must change if the amount used is increased or decreased. return this._hasRequiredFeatures && this._hasRequiredTextureUnits; @@ -201,50 +209,54 @@ export class Capabilities { } private debugPrint(gl: WebGLRenderingContext) { - debugPrint("GLES Capabilities Information:"); - debugPrint(" hasRequiredFeatures : " + this._hasRequiredFeatures); - debugPrint(" hasRequiredTextureUnits : " + this._hasRequiredTextureUnits); - debugPrint(" GL_VERSION : " + gl.getParameter(gl.VERSION)); - debugPrint(" GL_VENDOR : " + gl.getParameter(gl.VENDOR)); - debugPrint(" GL_RENDERER : " + gl.getParameter(gl.RENDERER)); - debugPrint(" maxTextureSize : " + this.maxTextureSize); - debugPrint(" maxColorAttachments : " + this.maxColorAttachments); - debugPrint(" maxDrawBuffers : " + this.maxDrawBuffers); - debugPrint(" maxFragTextureUnits : " + this.maxFragTextureUnits); - debugPrint(" maxVertTextureUnits : " + this.maxVertTextureUnits); - debugPrint(" nonPowerOf2Textures : " + (this.supportsNonPowerOf2Textures ? "yes" : "no")); - debugPrint(" drawBuffers : " + (this.supportsDrawBuffers ? "yes" : "no")); - debugPrint(" 32BitElementIndex : " + (this.supports32BitElementIndex ? "yes" : "no")); - debugPrint(" textureFloat : " + (this.supportsTextureFloat ? "yes" : "no")); - debugPrint(" textureHalfFloat : " + (this.supportsTextureHalfFloat ? "yes" : "no")); - debugPrint(" shaderTextureLOD : " + (this.supportsShaderTextureLOD ? "yes" : "no")); + if (!Debug.printEnabled) + return; + + Debug.print(() => "GLES Capabilities Information:"); + Debug.print(() => " hasRequiredFeatures : " + this._hasRequiredFeatures); + Debug.print(() => " hasRequiredTextureUnits : " + this._hasRequiredTextureUnits); + Debug.print(() => " GL_VERSION : " + gl.getParameter(gl.VERSION)); + Debug.print(() => " GL_VENDOR : " + gl.getParameter(gl.VENDOR)); + Debug.print(() => " GL_RENDERER : " + gl.getParameter(gl.RENDERER)); + Debug.print(() => " maxTextureSize : " + this.maxTextureSize); + Debug.print(() => " maxColorAttachments : " + this.maxColorAttachments); + Debug.print(() => " maxDrawBuffers : " + this.maxDrawBuffers); + Debug.print(() => " maxFragTextureUnits : " + this.maxFragTextureUnits); + Debug.print(() => " maxVertTextureUnits : " + this.maxVertTextureUnits); + Debug.print(() => " nonPowerOf2Textures : " + (this.supportsNonPowerOf2Textures ? "yes" : "no")); + Debug.print(() => " drawBuffers : " + (this.supportsDrawBuffers ? "yes" : "no")); + Debug.print(() => " instancing : " + (this.supportsInstancing ? "yes" : "no")); + Debug.print(() => " 32BitElementIndex : " + (this.supports32BitElementIndex ? "yes" : "no")); + Debug.print(() => " textureFloat : " + (this.supportsTextureFloat ? "yes" : "no")); + Debug.print(() => " textureHalfFloat : " + (this.supportsTextureHalfFloat ? "yes" : "no")); + Debug.print(() => " shaderTextureLOD : " + (this.supportsShaderTextureLOD ? "yes" : "no")); switch (this.maxRenderType) { case RenderType.TextureUnsignedByte: - debugPrint(" maxRenderType : TextureUnsigedByte"); + Debug.print(() => " maxRenderType : TextureUnsigedByte"); break; case RenderType.TextureHalfFloat: - debugPrint(" maxRenderType : TextureHalfFloat"); + Debug.print(() => " maxRenderType : TextureHalfFloat"); break; case RenderType.TextureFloat: - debugPrint(" maxRenderType : TextureFloat"); + Debug.print(() => " maxRenderType : TextureFloat"); break; default: - debugPrint(" maxRenderType : Unknown"); + Debug.print(() => " maxRenderType : Unknown"); } switch (this.maxDepthType) { case DepthType.RenderBufferUnsignedShort16: - debugPrint(" maxDepthType : RenderBufferUnsignedShort16"); + Debug.print(() => " maxDepthType : RenderBufferUnsignedShort16"); break; case DepthType.TextureUnsignedInt24Stencil8: - debugPrint(" maxDepthType : TextureUnsignedInt24Stencil8"); + Debug.print(() => " maxDepthType : TextureUnsignedInt24Stencil8"); break; case DepthType.TextureUnsignedInt32: - debugPrint(" maxDepthType : TextureUnsignedInt32"); + Debug.print(() => " maxDepthType : TextureUnsignedInt32"); break; default: - debugPrint(" maxDepthType : Unknown"); + Debug.print(() => " maxDepthType : Unknown"); } } } @@ -420,6 +432,13 @@ class TextureStats implements TextureMonitor { export type TextureBinding = WebGLTexture | undefined; +const enum VertexAttribState { + Disabled = 0, + Enabled = 1 << 0, + Instanced = 1 << 2, + InstancedEnabled = Instanced | Enabled, +} + export class System extends RenderSystem { public readonly canvas: HTMLCanvasElement; public readonly currentRenderState = new RenderState(); @@ -428,10 +447,15 @@ export class System extends RenderSystem { public readonly capabilities: Capabilities; public readonly resourceCache: Map; private readonly _drawBuffersExtension?: WEBGL_draw_buffers; + private readonly _instancingExtension?: ANGLE_instanced_arrays; private readonly _textureStats?: TextureStats; private readonly _textureBindings: TextureBinding[] = []; - private readonly _curVertexAttribStates: boolean[] = [false, false, false, false]; - private readonly _nextVertexAttribStates: boolean[] = [false, false, false, false]; + + // NB: Increase the size of these arrays when the maximum number of attributes used by any one shader increases. + private readonly _curVertexAttribStates: VertexAttribState[] = [VertexAttribState.Disabled, VertexAttribState.Disabled, VertexAttribState.Disabled, VertexAttribState.Disabled, + VertexAttribState.Disabled, VertexAttribState.Disabled, VertexAttribState.Disabled, VertexAttribState.Disabled]; + private readonly _nextVertexAttribStates: VertexAttribState[] = [VertexAttribState.Disabled, VertexAttribState.Disabled, VertexAttribState.Disabled, VertexAttribState.Disabled, + VertexAttribState.Disabled, VertexAttribState.Disabled, VertexAttribState.Disabled, VertexAttribState.Disabled]; // The following are initialized immediately after the System is constructed. private _lineCodeTexture?: TextureHandle; @@ -504,10 +528,10 @@ export class System extends RenderSystem { public createOffscreenTarget(rect: ViewRect): RenderTarget { return new OffScreenTarget(rect); } public createGraphicBuilder(placement: Transform, type: GraphicType, viewport: Viewport, pickableId?: Id64String): GraphicBuilder { return new PrimitiveBuilder(this, type, viewport, placement, pickableId); } - public createMesh(params: MeshParams): RenderGraphic | undefined { return MeshGraphic.create(params); } - public createPolyline(params: PolylineParams): RenderGraphic | undefined { return PolylinePrimitive.create(params); } - public createPointString(params: PointStringParams): RenderGraphic | undefined { return PointStringPrimitive.create(params); } - public createPointCloud(args: PointCloudArgs): RenderGraphic | undefined { return PointCloudPrimitive.create(args); } + public createMesh(params: MeshParams, instances?: InstancedGraphicParams): RenderGraphic | undefined { return MeshGraphic.create(params, instances); } + public createPolyline(params: PolylineParams, instances?: InstancedGraphicParams): RenderGraphic | undefined { return Primitive.create(() => PolylineGeometry.create(params), instances); } + public createPointString(params: PointStringParams, instances?: InstancedGraphicParams): RenderGraphic | undefined { return Primitive.create(() => PointStringGeometry.create(params), instances); } + public createPointCloud(args: PointCloudArgs): RenderGraphic | undefined { return Primitive.create(() => new PointCloudGeometry(args)); } public createGraphicList(primitives: RenderGraphic[]): RenderGraphic { return new GraphicsArray(primitives); } public createBranch(branch: GraphicBranch, transform: Transform, clips?: ClipPlanesVolume | ClipMaskVolume): RenderGraphic { return new Branch(branch, transform, clips); } @@ -515,12 +539,10 @@ export class System extends RenderSystem { public createSkyBox(params: SkyBox.CreateParams): RenderGraphic | undefined { if (undefined !== params.cube) { - const cachedGeom = SkyBoxQuadsGeometry.create(params.cube); - return cachedGeom !== undefined ? new SkyBoxPrimitive(cachedGeom) : undefined; + return SkyBoxPrimitive.create(() => SkyBoxQuadsGeometry.create(params.cube!)); } else { assert(undefined !== params.sphere || undefined !== params.gradient); - const cachedGeom = SkySphereViewportQuadGeometry.createGeometry(params); - return cachedGeom !== undefined ? new SkySpherePrimitive(cachedGeom) : undefined; + return SkyBoxPrimitive.create(() => SkySphereViewportQuadGeometry.createGeometry(params)); } } @@ -631,6 +653,7 @@ export class System extends RenderSystem { this.context = context; this.capabilities = capabilities; this._drawBuffersExtension = capabilities.queryExtensionObject("WEBGL_draw_buffers"); + this._instancingExtension = capabilities.queryExtensionObject("ANGLE_instanced_arrays"); this.resourceCache = new Map(); // Make this System a subscriber to the the IModelConnection onClose event @@ -735,9 +758,7 @@ export class System extends RenderSystem { for (const point of pts) meshArgs.points.push(QPoint3d.create(point, meshArgs.points.params)); - const uvs: Point2d[] = []; // temporary uv storage - will be rearranged below - for (const param of rawParams) - uvs.push(param.clone()); + const uvs: Point2d[] = rawParams.getPoint2dArray(); const pointIndices: number[] = []; const uvIndices: number[] = []; @@ -807,24 +828,62 @@ export class System extends RenderSystem { // System keeps track of current enabled state of vertex attribute arrays. // This prevents errors caused by leaving a vertex attrib array enabled after disposing of the buffer bound to it; // also prevents unnecessarily 'updating' the enabled state of a vertex attrib array when it hasn't actually changed. - public enableVertexAttribArray(id: number): void { this._nextVertexAttribStates[id] = true; } + public enableVertexAttribArray(id: number, instanced: boolean): void { + assert(id < this._nextVertexAttribStates.length, "if you add new vertex attributes you must update array length"); + assert(id < this._curVertexAttribStates.length, "if you add new vertex attributes you must update array length"); + + this._nextVertexAttribStates[id] = instanced ? VertexAttribState.InstancedEnabled : VertexAttribState.Enabled; + } + public updateVertexAttribArrays(): void { const cur = this._curVertexAttribStates; const next = this._nextVertexAttribStates; const context = this.context; + for (let i = 0; i < next.length; i++) { - const wasEnabled = cur[i]; - const nowEnabled = next[i]; - if (wasEnabled !== nowEnabled) { - if (wasEnabled) - context.disableVertexAttribArray(i); - else - context.enableVertexAttribArray(i); - - cur[i] = nowEnabled; + const oldState = cur[i]; + const newState = next[i]; + if (oldState !== newState) { + // Update the enabled state if it changed. + const wasEnabled = 0 !== (VertexAttribState.Enabled & oldState); + const nowEnabled = 0 !== (VertexAttribState.Enabled & newState); + if (wasEnabled !== nowEnabled) { + if (nowEnabled) { + context.enableVertexAttribArray(i); + } else { + context.disableVertexAttribArray(i); + } + } + + // Only update the divisor if the attribute is enabled. + if (nowEnabled) { + const wasInstanced = 0 !== (VertexAttribState.Instanced & oldState); + const nowInstanced = 0 !== (VertexAttribState.Instanced & newState); + if (wasInstanced !== nowInstanced) { + assert(undefined !== this._instancingExtension); + this._instancingExtension!.vertexAttribDivisorANGLE(i, nowInstanced ? 1 : 0); + } + } + + cur[i] = newState; } - next[i] = false; + // Set the attribute back to disabled, but preserve the divisor. + next[i] &= ~VertexAttribState.Enabled; } } + + public drawArrays(type: GL.PrimitiveType, first: number, count: number, numInstances: number): void { + if (0 !== numInstances) { + if (undefined !== this._instancingExtension) + this._instancingExtension.drawArraysInstancedANGLE(type, first, count, numInstances); + } else { + this.context.drawArrays(type, first, count); + } + } + + public enableDiagnostics(enable: RenderDiagnostics): void { + Debug.printEnabled = RenderDiagnostics.None !== (enable & RenderDiagnostics.DebugOutput); + Debug.evaluateEnabled = RenderDiagnostics.None !== (enable & RenderDiagnostics.WebGL); + } } diff --git a/core/frontend/src/render/webgl/Target.ts b/core/frontend/src/render/webgl/Target.ts index 1b575a6..f4f2010 100644 --- a/core/frontend/src/render/webgl/Target.ts +++ b/core/frontend/src/render/webgl/Target.ts @@ -5,9 +5,9 @@ /** @module WebGL */ import { Transform, Vector3d, Point3d, Matrix4d, Point2d, XAndY } from "@bentley/geometry-core"; -import { BeTimePoint, assert, Id64String, Id64, StopWatch, dispose, disposeArray } from "@bentley/bentleyjs-core"; +import { assert, BeTimePoint, Id64String, Id64, StopWatch, dispose, disposeArray } from "@bentley/bentleyjs-core"; import { RenderTarget, RenderSystem, Decorations, GraphicList, RenderPlan, ClippingType, CanvasDecoration, Pixel, AnimationBranchStates } from "../System"; -import { ViewFlags, Frustum, Hilite, ColorDef, Npc, RenderMode, ImageLight, ImageBuffer, ImageBufferFormat, AnalysisStyle, RenderTexture, AmbientOcclusion } from "@bentley/imodeljs-common"; +import { ViewFlags, Frustum, Hilite, ColorDef, Npc, RenderMode, ImageBuffer, ImageBufferFormat, AnalysisStyle, RenderTexture, AmbientOcclusion } from "@bentley/imodeljs-common"; import { FeatureSymbology } from "../FeatureSymbology"; import { Techniques } from "./Technique"; import { TechniqueId } from "./TechniqueId"; @@ -207,9 +207,6 @@ export abstract class Target extends RenderTarget { public readonly nearPlaneCenter = new Point3d(); public readonly viewMatrix = Transform.createIdentity(); public readonly projectionMatrix = Matrix4d.createIdentity(); - private _environmentMap?: TextureHandle; // ###TODO: for IBL - private _diffuseMap?: TextureHandle; // ###TODO: for IBL - public readonly imageSolar?: ImageLight.Solar; // ###TODO: for IBL private readonly _visibleEdgeOverrides = new EdgeOverrides(); private readonly _hiddenEdgeOverrides = new EdgeOverrides(); public analysisStyle?: AnalysisStyle; @@ -221,6 +218,8 @@ export abstract class Target extends RenderTarget { public plan?: RenderPlan; private _animationBranches?: AnimationBranchStates; private _isReadPixelsInProgress = false; + private _drawNonLocatable = true; + public isFadeOutActive = false; protected constructor(rect?: ViewRect) { super(); @@ -234,9 +233,9 @@ export abstract class Target extends RenderTarget { } public get isReadPixelsInProgress(): boolean { return this._isReadPixelsInProgress; } + public get drawNonLocatable(): boolean { return this._drawNonLocatable; } public get currentOverrides(): FeatureOverrides | undefined { return this._currentOverrides; } - // public get currentOverrides(): FeatureOverrides | undefined { return this._currentOverrides ? undefined : undefined; } // ###TODO remove this - for testing purposes only (forces overrides off) public set currentOverrides(ovr: FeatureOverrides | undefined) { // Don't bother setting up overrides if they don't actually override anything - wastes time doing texture lookups in shaders. this._currentOverrides = (undefined !== ovr && ovr.anyOverridden) ? ovr : undefined; @@ -306,9 +305,6 @@ export abstract class Target extends RenderTarget { this._clipMask = mask; } - public get environmentMap(): TextureHandle | undefined { return this._environmentMap; } - public get diffuseMap(): TextureHandle | undefined { return this._diffuseMap; } - public get is2d(): boolean { return this.frustumUniforms.is2d; } public get is3d(): boolean { return !this.is2d; } @@ -316,8 +312,6 @@ export abstract class Target extends RenderTarget { this.reset(); dispose(this.compositor); - this._environmentMap = dispose(this._environmentMap); - this._diffuseMap = dispose(this._diffuseMap); this._dcAssigned = false; // necessary to reassign to OnScreenTarget fbo member when re-validating render plan } @@ -390,9 +384,8 @@ export abstract class Target extends RenderTarget { this._decorations = dispose(this._decorations); this._decorations = decs; } - public changeScene(scene: GraphicList, _activeVolume: ClipPlanesVolume | ClipMaskVolume) { + public changeScene(scene: GraphicList) { this._scene = scene; - this._activeClipVolume = _activeVolume; } public changeTerrain(terrain: GraphicList) { this._terrain = terrain; @@ -522,6 +515,7 @@ export abstract class Target extends RenderTarget { this.bgColor.setFrom(plan.bgColor); this.monoColor.setFrom(plan.monoColor); this.hiliteSettings = plan.hiliteSettings; + this.isFadeOutActive = plan.isFadeOutActive; this._transparencyThreshold = 0.0; this.analysisStyle = plan.analysisStyle === undefined ? undefined : plan.analysisStyle.clone(); this.analysisTexture = plan.analysisTexture; @@ -646,7 +640,7 @@ export abstract class Target extends RenderTarget { this._batches = []; - // ###TODO this._activeVolume = undefined; + dispose(this._activeClipVolume); } public get wantInvertBlackBackground(): boolean { return false; } @@ -690,6 +684,11 @@ export abstract class Target extends RenderTarget { private _doDebugPaint: boolean = false; protected debugPaint(): void { } + public recordPerformanceMetric(operation: string): void { + if (this.performanceMetrics) + this.performanceMetrics.recordTime(operation); + } + private paintScene(sceneMilSecElapsed?: number): void { if (this._doDebugPaint) { this.debugPaint(); @@ -712,7 +711,7 @@ export abstract class Target extends RenderTarget { if (drawForReadPixels) { this._isReadPixelsInProgress = true; - if (this.performanceMetrics) this.performanceMetrics.recordTime("Begin Paint"); + this.recordPerformanceMetric("Begin Paint"); const vf = this.currentViewFlags.clone(this._scratchViewFlags); vf.transparency = false; vf.textures = false; @@ -730,18 +729,18 @@ export abstract class Target extends RenderTarget { this.pushState(state); this._renderCommands.init(this._scene, this._terrain, this._decorations, this._dynamics, true); - if (this.performanceMetrics) this.performanceMetrics.recordTime("Init Commands"); + this.recordPerformanceMetric("Init Commands"); this.compositor.drawForReadPixels(this._renderCommands); - if (this.performanceMetrics) this.performanceMetrics.recordTime("Draw Read Pixels"); + this.recordPerformanceMetric("Draw Read Pixels"); this._stack.pop(); this._isReadPixelsInProgress = false; } else { - if (this.performanceMetrics) this.performanceMetrics.recordTime("Begin Paint"); + this.recordPerformanceMetric("Begin Paint"); this._renderCommands.init(this._scene, this._terrain, this._decorations, this._dynamics); - if (this.performanceMetrics) this.performanceMetrics.recordTime("Init Commands"); + this.recordPerformanceMetric("Init Commands"); this.compositor.draw(this._renderCommands); // scene compositor gets disposed and then re-initialized... target remains undisposed this._stack.pushState(this.decorationState); @@ -749,14 +748,14 @@ export abstract class Target extends RenderTarget { this.drawPass(RenderPass.ViewOverlay); this._stack.pop(); - if (this.performanceMetrics) this.performanceMetrics.recordTime("Overlay Draws"); + this.recordPerformanceMetric("Overlay Draws"); } // Reset the batch IDs in all batches drawn for this call. this._batchState.reset(); this._endPaint(); - if (this.performanceMetrics) this.performanceMetrics.recordTime("End Paint"); + this.recordPerformanceMetric("End Paint"); if (this.performanceMetrics) { if (this.performanceMetrics.gatherCurPerformanceMetrics) { @@ -814,7 +813,7 @@ export abstract class Target extends RenderTarget { return this._dcAssigned; } - public readPixels(rect: ViewRect, selector: Pixel.Selector, receiver: Pixel.Receiver): void { + public readPixels(rect: ViewRect, selector: Pixel.Selector, receiver: Pixel.Receiver, excludeNonLocatable: boolean): void { // We can't reuse the previous frame's data for a variety of reasons, chief among them that some types of geometry (surfaces, translucent stuff) don't write // to the pick buffers and others we don't want - such as non-pickable decorations - do. // Render to an offscreen buffer so that we don't destroy the current color buffer. @@ -828,7 +827,9 @@ export abstract class Target extends RenderTarget { const fbo = FrameBuffer.create([texture]); if (undefined !== fbo) { System.instance.frameBufferStack.execute(fbo, true, () => { + this._drawNonLocatable = !excludeNonLocatable; result = this.readPixelsFromFbo(rect, selector); + this._drawNonLocatable = true; }); dispose(fbo); diff --git a/core/frontend/src/render/webgl/Technique.ts b/core/frontend/src/render/webgl/Technique.ts index 54d8329..6b16729 100644 --- a/core/frontend/src/render/webgl/Technique.ts +++ b/core/frontend/src/render/webgl/Technique.ts @@ -7,9 +7,9 @@ import { assert, using, IDisposable, dispose } from "@bentley/bentleyjs-core"; import { ShaderProgram, ShaderProgramExecutor } from "./ShaderProgram"; import { TechniqueId, computeCompositeTechniqueId } from "./TechniqueId"; -import { TechniqueFlags, FeatureMode, ClipDef } from "./TechniqueFlags"; +import { IsInstanced, IsAnimated, TechniqueFlags, FeatureMode, ClipDef } from "./TechniqueFlags"; import { ProgramBuilder, FragmentShaderComponent, ClippingShaders } from "./ShaderBuilder"; -import { DrawParams, DrawCommands } from "./DrawCommand"; +import { DrawParams, DrawCommands, OmitStatus } from "./DrawCommand"; import { Target } from "./Target"; import { RenderPass } from "./RenderFlags"; import { createClearTranslucentProgram } from "./glsl/ClearTranslucent"; @@ -59,9 +59,10 @@ export class SingularTechnique implements Technique { } function numFeatureVariants(numBaseShaders: number) { return numBaseShaders * 3; } -const numHiliteVariants = 1; +const numHiliteVariants = 2; // instanced and non-instanced const featureModes = [FeatureMode.None, FeatureMode.Pick, FeatureMode.Overrides]; const scratchTechniqueFlags = new TechniqueFlags(); +const scratchHiliteFlags = new TechniqueFlags(); // A rendering technique implemented using multiple shader programs, selected based on TechniqueFlags. export abstract class VariedTechnique implements Technique { @@ -117,10 +118,10 @@ export abstract class VariedTechnique implements Technique { this._basicPrograms[index] = program; } - protected addHiliteShader(gl: WebGLRenderingContext, create: () => ProgramBuilder): void { - const builder = create(); - scratchTechniqueFlags.initForHilite(new ClipDef()); - this.addShader(builder, scratchTechniqueFlags, gl); + protected addHiliteShader(gl: WebGLRenderingContext, instanced: IsInstanced, create: (instanced: IsInstanced) => ProgramBuilder): void { + const builder = create(instanced); + scratchHiliteFlags.initForHilite(new ClipDef(), instanced); + this.addShader(builder, scratchHiliteFlags, gl); } protected addTranslucentShader(builder: ProgramBuilder, flags: TechniqueFlags, gl: WebGLRenderingContext): void { @@ -172,27 +173,30 @@ class SurfaceTechnique extends VariedTechnique { private static readonly _kOpaque = 0; private static readonly _kTranslucent = 1; private static readonly _kAnimated = 2; - private static readonly _kFeature = 4; + private static readonly _kInstanced = 4; + private static readonly _kFeature = 8; private static readonly _kHilite = numFeatureVariants(SurfaceTechnique._kFeature); public constructor(gl: WebGLRenderingContext) { - super((numFeatureVariants(4) + numHiliteVariants)); + super((SurfaceTechnique._kHilite + numHiliteVariants)); const flags = scratchTechniqueFlags; - this.addHiliteShader(gl, createSurfaceHiliter); - for (let iAnimate = 0; iAnimate < 2; iAnimate++) { - for (const featureMode of featureModes) { - flags.reset(featureMode); - flags.isAnimated = iAnimate !== 0; - const builder = createSurfaceBuilder(featureMode, flags.isAnimated); - addMonochrome(builder.frag); - addMaterial(builder.frag); - - addSurfaceDiscardByAlpha(builder.frag); - this.addShader(builder, flags, gl); - - builder.frag.unset(FragmentShaderComponent.DiscardByAlpha); - this.addTranslucentShader(builder, flags, gl); + for (let instanced = IsInstanced.No; instanced <= IsInstanced.Yes; instanced++) { + this.addHiliteShader(gl, instanced, createSurfaceHiliter); + for (let iAnimate = IsAnimated.No; iAnimate <= IsAnimated.Yes; iAnimate++) { + for (const featureMode of featureModes) { + flags.reset(featureMode, instanced); + flags.isAnimated = iAnimate; + const builder = createSurfaceBuilder(featureMode, flags.isInstanced, flags.isAnimated); + addMonochrome(builder.frag); + addMaterial(builder.frag); + + addSurfaceDiscardByAlpha(builder.frag); + this.addShader(builder, flags, gl); + + builder.frag.unset(FragmentShaderComponent.DiscardByAlpha); + this.addTranslucentShader(builder, flags, gl); + } } } } @@ -201,13 +205,15 @@ class SurfaceTechnique extends VariedTechnique { public computeShaderIndex(flags: TechniqueFlags): number { if (flags.isHilite) { assert(flags.hasFeatures); - return SurfaceTechnique._kHilite; + return SurfaceTechnique._kHilite + flags.isInstanced; } let index = flags.isTranslucent ? SurfaceTechnique._kTranslucent : SurfaceTechnique._kOpaque; index += SurfaceTechnique._kFeature * flags.featureMode; if (flags.isAnimated) index += SurfaceTechnique._kAnimated; + if (flags.isInstanced) + index += SurfaceTechnique._kInstanced; return index; } @@ -216,33 +222,37 @@ class SurfaceTechnique extends VariedTechnique { class PolylineTechnique extends VariedTechnique { private static readonly _kOpaque = 0; private static readonly _kTranslucent = 1; - private static readonly _kFeature = 2; + private static readonly _kInstanced = 2; + private static readonly _kFeature = 4; private static readonly _kHilite = numFeatureVariants(PolylineTechnique._kFeature); public constructor(gl: WebGLRenderingContext) { - super((numFeatureVariants(2) + numHiliteVariants)); + super(PolylineTechnique._kHilite + numHiliteVariants); const flags = scratchTechniqueFlags; - this.addHiliteShader(gl, createPolylineHiliter); - for (const featureMode of featureModes) { - flags.reset(featureMode); - const builder = createPolylineBuilder(); - addMonochrome(builder.frag); - - // The translucent shaders do not need the element IDs. - const builderTrans = createPolylineBuilder(); - addMonochrome(builderTrans.frag); - if (FeatureMode.Overrides === featureMode) { - addFeatureSymbology(builderTrans, featureMode, FeatureSymbologyOptions.Linear); - addFeatureSymbology(builder, featureMode, FeatureSymbologyOptions.Linear); - this.addTranslucentShader(builderTrans, flags, gl); - } else { - this.addTranslucentShader(builderTrans, flags, gl); - addFeatureSymbology(builder, featureMode, FeatureSymbologyOptions.None); + for (let instanced = IsInstanced.No; instanced <= IsInstanced.Yes; instanced++) { + this.addHiliteShader(gl, instanced, createPolylineHiliter); + for (const featureMode of featureModes) { + flags.reset(featureMode, instanced); + const builder = createPolylineBuilder(instanced); + addMonochrome(builder.frag); + + // The translucent shaders do not need the element IDs. + const builderTrans = createPolylineBuilder(instanced); + addMonochrome(builderTrans.frag); + if (FeatureMode.Overrides === featureMode) { + addFeatureSymbology(builderTrans, featureMode, FeatureSymbologyOptions.Linear); + addFeatureSymbology(builder, featureMode, FeatureSymbologyOptions.Linear); + this.addTranslucentShader(builderTrans, flags, gl); + } else { + this.addTranslucentShader(builderTrans, flags, gl); + addFeatureSymbology(builder, featureMode, FeatureSymbologyOptions.None); + } + + this.addFeatureId(builder, featureMode); + flags.reset(featureMode, instanced); + this.addShader(builder, flags, gl); } - this.addFeatureId(builder, featureMode); - flags.reset(featureMode); - this.addShader(builder, flags, gl); } } @@ -251,11 +261,12 @@ class PolylineTechnique extends VariedTechnique { public computeShaderIndex(flags: TechniqueFlags): number { if (flags.isHilite) { assert(flags.hasFeatures); - return PolylineTechnique._kHilite; + return PolylineTechnique._kHilite + flags.isInstanced; } let index = flags.isTranslucent ? PolylineTechnique._kTranslucent : PolylineTechnique._kOpaque; index += PolylineTechnique._kFeature * flags.featureMode; + index += PolylineTechnique._kInstanced * flags.isInstanced; return index; } } @@ -264,35 +275,40 @@ class EdgeTechnique extends VariedTechnique { private static readonly _kOpaque = 0; private static readonly _kTranslucent = 1; private static readonly _kAnimated = 2; - private static readonly _kFeature = 4; + private static readonly _kInstanced = 4; + private static readonly _kFeature = 8; private readonly _isSilhouette: boolean; public constructor(gl: WebGLRenderingContext, isSilhouette: boolean = false) { - super(numFeatureVariants(4)); + super(numFeatureVariants(EdgeTechnique._kFeature)); this._isSilhouette = isSilhouette; const flags = scratchTechniqueFlags; - for (let iAnimate = 0; iAnimate < 2; iAnimate++) { - for (const featureMode of featureModes) { - flags.reset(featureMode); - flags.isAnimated = iAnimate !== 0; - const builder = createEdgeBuilder(isSilhouette, flags.isAnimated); - addMonochrome(builder.frag); + for (let instanced = IsInstanced.No; instanced <= IsInstanced.Yes; instanced++) { + for (let iAnimate = IsAnimated.No; iAnimate <= IsAnimated.Yes; iAnimate++) { + for (const featureMode of featureModes) { + flags.reset(featureMode, instanced); + flags.isAnimated = iAnimate; + const builder = createEdgeBuilder(isSilhouette, flags.isInstanced, flags.isAnimated); + addMonochrome(builder.frag); + + // The translucent shaders do not need the element IDs. + const builderTrans = createEdgeBuilder(isSilhouette, flags.isInstanced, flags.isAnimated); + addMonochrome(builderTrans.frag); + if (FeatureMode.Overrides === featureMode) { + addFeatureSymbology(builderTrans, featureMode, FeatureSymbologyOptions.Linear); + addFeatureSymbology(builder, featureMode, FeatureSymbologyOptions.Linear); + this.addTranslucentShader(builderTrans, flags, gl); + } else { + this.addTranslucentShader(builderTrans, flags, gl); + addFeatureSymbology(builder, featureMode, FeatureSymbologyOptions.None); + } - // The translucent shaders do not need the element IDs. - const builderTrans = createEdgeBuilder(isSilhouette, flags.isAnimated); - addMonochrome(builderTrans.frag); - if (FeatureMode.Overrides === featureMode) { - addFeatureSymbology(builderTrans, featureMode, FeatureSymbologyOptions.Linear); - addFeatureSymbology(builder, featureMode, FeatureSymbologyOptions.Linear); - this.addTranslucentShader(builderTrans, flags, gl); - } else { - this.addTranslucentShader(builderTrans, flags, gl); - addFeatureSymbology(builder, featureMode, FeatureSymbologyOptions.None); + this.addFeatureId(builder, featureMode); + flags.reset(featureMode, instanced); + flags.isAnimated = iAnimate; + this.addShader(builder, flags, gl); } - this.addFeatureId(builder, featureMode); - flags.reset(featureMode); - this.addShader(builder, flags, gl); } } } @@ -304,6 +320,8 @@ class EdgeTechnique extends VariedTechnique { index += EdgeTechnique._kFeature * flags.featureMode; if (flags.isAnimated) index += EdgeTechnique._kAnimated; + if (flags.isInstanced) + index += EdgeTechnique._kInstanced; return index; } @@ -312,33 +330,37 @@ class EdgeTechnique extends VariedTechnique { class PointStringTechnique extends VariedTechnique { private static readonly _kOpaque = 0; private static readonly _kTranslucent = 1; - private static readonly _kFeature = 2; + private static readonly _kInstanced = 2; + private static readonly _kFeature = 4; private static readonly _kHilite = numFeatureVariants(PointStringTechnique._kFeature); public constructor(gl: WebGLRenderingContext) { - super((numFeatureVariants(2) + numHiliteVariants)); + super((PointStringTechnique._kHilite + numHiliteVariants)); const flags = scratchTechniqueFlags; - this.addHiliteShader(gl, createPointStringHiliter); - for (const featureMode of featureModes) { - flags.reset(featureMode); - const builder = createPointStringBuilder(); - addMonochrome(builder.frag); - - // The translucent shaders do not need the element IDs. - const builderTrans = createPointStringBuilder(); - addMonochrome(builderTrans.frag); - if (FeatureMode.Overrides === featureMode) { - addFeatureSymbology(builderTrans, featureMode, FeatureSymbologyOptions.Point); - addFeatureSymbology(builder, featureMode, FeatureSymbologyOptions.Point); - this.addTranslucentShader(builderTrans, flags, gl); - } else { - this.addTranslucentShader(builderTrans, flags, gl); - addFeatureSymbology(builder, featureMode, FeatureSymbologyOptions.None); + for (let instanced = IsInstanced.No; instanced <= IsInstanced.Yes; instanced++) { + this.addHiliteShader(gl, instanced, createPointStringHiliter); + for (const featureMode of featureModes) { + flags.reset(featureMode, instanced); + const builder = createPointStringBuilder(instanced); + addMonochrome(builder.frag); + + // The translucent shaders do not need the element IDs. + const builderTrans = createPointStringBuilder(instanced); + addMonochrome(builderTrans.frag); + if (FeatureMode.Overrides === featureMode) { + addFeatureSymbology(builderTrans, featureMode, FeatureSymbologyOptions.Point); + addFeatureSymbology(builder, featureMode, FeatureSymbologyOptions.Point); + this.addTranslucentShader(builderTrans, flags, gl); + } else { + this.addTranslucentShader(builderTrans, flags, gl); + addFeatureSymbology(builder, featureMode, FeatureSymbologyOptions.None); + } + + this.addFeatureId(builder, featureMode); + flags.reset(featureMode, instanced); + this.addShader(builder, flags, gl); } - this.addFeatureId(builder, featureMode); - flags.reset(featureMode); - this.addShader(builder, flags, gl); } } @@ -347,11 +369,12 @@ class PointStringTechnique extends VariedTechnique { public computeShaderIndex(flags: TechniqueFlags): number { if (flags.isHilite) { assert(flags.hasFeatures); - return PointStringTechnique._kHilite; + return PointStringTechnique._kHilite + flags.isInstanced; } let index = flags.isTranslucent ? PointStringTechnique._kTranslucent : PointStringTechnique._kOpaque; index += PointStringTechnique._kFeature * flags.featureMode; + index += PointStringTechnique._kInstanced * flags.isInstanced; return index; } } @@ -363,7 +386,7 @@ class PointCloudTechnique extends VariedTechnique { public constructor(gl: WebGLRenderingContext) { super(3); - this.addHiliteShader(gl, createPointCloudHiliter); + this.addHiliteShader(gl, IsInstanced.No, () => createPointCloudHiliter()); const flags = scratchTechniqueFlags; const pointCloudFeatureModes = [FeatureMode.None, FeatureMode.Overrides]; @@ -426,14 +449,19 @@ export class Techniques implements IDisposable { const flags = this._scratchTechniqueFlags; using(new ShaderProgramExecutor(target, renderPass), (executor: ShaderProgramExecutor) => { + let omitCounter = 0; for (const command of commands) { + const omitStatus = command.getOmitStatus(target); + if ((omitCounter += omitStatus) !== 0 || omitStatus !== OmitStatus.Neutral) + continue; command.preExecute(executor); - const techniqueId = command.getTechniqueId(target); if (TechniqueId.Invalid !== techniqueId) { // A primitive command. assert(command.isPrimitiveCommand, "expected primitive command"); - flags.init(target, renderPass, command.hasAnimation); + flags.init(target, renderPass, IsInstanced.No); + flags.setAnimated(command.hasAnimation); + flags.setInstanced(command.isInstanced); const tech = this.getTechnique(techniqueId); const program = tech.getShader(flags); if (executor.setProgram(program)) { @@ -479,7 +507,8 @@ export class Techniques implements IDisposable { assert(TechniqueId.Invalid !== techniqueId); // A primitive command. assert(primCmd.isPrimitiveCommand, "expected primitive command"); - flags.init(target, renderPass, primCmd.hasAnimation); + flags.init(target, renderPass, IsInstanced.No); + flags.setAnimated(primCmd.hasAnimation); const tech = this.getTechnique(undefined !== techId ? techId : techniqueId); const program = tech.getShader(flags); if (executor.setProgram(program)) { diff --git a/core/frontend/src/render/webgl/TechniqueFlags.ts b/core/frontend/src/render/webgl/TechniqueFlags.ts index 2e983a0..4509381 100644 --- a/core/frontend/src/render/webgl/TechniqueFlags.ts +++ b/core/frontend/src/render/webgl/TechniqueFlags.ts @@ -25,63 +25,72 @@ export class ClipDef { public static forPlanes(numPlanes: number) { return new ClipDef(ClippingType.Planes, numPlanes); } } +export const enum IsInstanced { No, Yes } +export const enum IsAnimated { No, Yes } + /** Flags used to control which shader program is used by a rendering Technique. */ export class TechniqueFlags { - public clip: ClipDef; - public featureMode: FeatureMode; + public clip: ClipDef = new ClipDef(); + public featureMode = FeatureMode.None; public isTranslucent: boolean; - public isAnimated: boolean; - private _isHilite: boolean; + public isAnimated: IsAnimated = IsAnimated.No; + public isInstanced: IsInstanced = IsInstanced.No; + private _isHilite = false; public constructor(translucent: boolean = false) { this.isTranslucent = translucent; - this._isHilite = false; - this.isAnimated = false; - this.featureMode = FeatureMode.None; - this.clip = new ClipDef(); } public get hasClip(): boolean { return this.clip.type !== ClippingType.None; } - public init(target: Target, pass: RenderPass, isAnimated: boolean = false): void { + public init(target: Target, pass: RenderPass, instanced: IsInstanced, animated: IsAnimated = IsAnimated.No): void { if (RenderPass.Hilite === pass || RenderPass.HiliteClassification === pass) { - this.initForHilite(target.clipDef); + this.initForHilite(target.clipDef, instanced); } else { this._isHilite = false; this.isTranslucent = RenderPass.Translucent === pass; this.clip = target.clipDef; - this.isAnimated = isAnimated; + this.isAnimated = animated; + this.isInstanced = instanced; - if (undefined !== target.currentOverrides) { + if (undefined !== target.currentOverrides) this.featureMode = FeatureMode.Overrides; - } else if (0 !== target.currentBatchId) { + else if (0 !== target.currentBatchId) this.featureMode = FeatureMode.Pick; - } else { + else this.featureMode = FeatureMode.None; - } } } - public reset(mode: FeatureMode, isTranslucent: boolean = false) { + public reset(mode: FeatureMode, instanced: IsInstanced = IsInstanced.No, isTranslucent: boolean = false) { this._isHilite = false; this.featureMode = mode; this.isTranslucent = isTranslucent; + this.isAnimated = IsAnimated.No; + this.isInstanced = instanced; this.clip.type = ClippingType.None; this.clip.numberOfPlanes = 0; } public get hasFeatures() { return FeatureMode.None !== this.featureMode; } + public setAnimated(animated: boolean) { this.isAnimated = animated ? IsAnimated.Yes : IsAnimated.No; } + public setInstanced(instanced: boolean) { this.isInstanced = instanced ? IsInstanced.Yes : IsInstanced.No; } + public get isHilite() { return this._isHilite; } - public initForHilite(clip: ClipDef) { + public initForHilite(clip: ClipDef, instanced: IsInstanced) { this.featureMode = FeatureMode.Overrides; this._isHilite = true; this.isTranslucent = false; + this.isAnimated = IsAnimated.No; + this.isInstanced = instanced; this.clip = clip; } public buildDescription(): string { const parts = [this.isTranslucent ? "Translucent" : "Opaque"]; + if (this.isInstanced) parts.push("instanced"); + if (this.isAnimated) parts.push("animated"); if (this.isHilite) parts.push("hilite"); if (this.hasClip) parts.push("clip"); if (this.hasFeatures) parts.push(FeatureMode.Pick === this.featureMode ? "pick" : "overrides"); diff --git a/core/frontend/src/render/webgl/TechniqueId.ts b/core/frontend/src/render/webgl/TechniqueId.ts index 5dd51d6..7e94fd5 100644 --- a/core/frontend/src/render/webgl/TechniqueId.ts +++ b/core/frontend/src/render/webgl/TechniqueId.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ /** @module WebGL */ -import { assert } from "@bentley/bentleyjs-core"; import { CompositeFlags } from "./RenderFlags"; +import { assert } from "@bentley/bentleyjs-core"; /** Technique enumeration */ export const enum TechniqueId { diff --git a/core/frontend/src/render/webgl/Texture.ts b/core/frontend/src/render/webgl/Texture.ts index c7e802a..ba96cf9 100644 --- a/core/frontend/src/render/webgl/Texture.ts +++ b/core/frontend/src/render/webgl/Texture.ts @@ -10,7 +10,6 @@ import { GL } from "./GL"; import { System } from "./System"; import { UniformHandle } from "./Handle"; import { TextureUnit, OvrFlags } from "./RenderFlags"; -import { debugPrint } from "./debugPrint"; type CanvasOrImage = HTMLCanvasElement | HTMLImageElement; @@ -306,17 +305,8 @@ export abstract class TextureHandle implements IDisposable { return TextureCubeHandle.createForCubeImages(posX, negX, posY, negY, posZ, negZ); } - // Set following to true to assign sequential numeric identifiers to WebGLTexture objects. - // This helps in debugging issues in which e.g. the same texture is bound as an input and output. - public static wantDebugIds: boolean = false; - private static _debugId: number = 0; - private static readonly _maxDebugId = 0xffffff; protected constructor(glTexture: WebGLTexture) { this._glTexture = glTexture; - if (TextureHandle.wantDebugIds) { - (glTexture as any)._debugId = ++TextureHandle._debugId; - TextureHandle._debugId %= TextureHandle._maxDebugId; - } } } @@ -337,8 +327,6 @@ export class Texture2DHandle extends TextureHandle { public static bindTexture(texUnit: TextureUnit, glTex: WebGLTexture | undefined) { assert(!(glTex instanceof TextureHandle)); System.instance.bindTexture2d(texUnit, glTex); - if (this.wantDebugIds) - debugPrint("Texture Unit " + (texUnit - TextureUnit.Zero) + " = " + (glTex ? (glTex as any)._debugId : "null")); } /** Bind the specified texture to a uniform sampler2D */ @@ -443,8 +431,6 @@ export class TextureCubeHandle extends TextureHandle { public static bindTexture(texUnit: TextureUnit, glTex: WebGLTexture | undefined) { assert(!(glTex instanceof TextureHandle)); System.instance.bindTextureCubeMap(texUnit, glTex); - if (this.wantDebugIds) - debugPrint("Texture Unit " + (texUnit - TextureUnit.Zero) + " = " + (glTex ? (glTex as any)._debugId : "null")); } /** Bind the specified texture to a uniform sampler2D */ diff --git a/core/frontend/src/render/webgl/VertexLUT.ts b/core/frontend/src/render/webgl/VertexLUT.ts index b21a9dd..950301d 100644 --- a/core/frontend/src/render/webgl/VertexLUT.ts +++ b/core/frontend/src/render/webgl/VertexLUT.ts @@ -9,7 +9,54 @@ import { QParams2d, QParams3d } from "@bentley/imodeljs-common"; import { ColorInfo } from "./ColorInfo"; import { TextureHandle } from "./Texture"; import { qparams2dToArray, qorigin3dToArray, qscale3dToArray } from "./Handle"; -import { VertexTable, AuxDisplacement, AuxNormal, AuxParam } from "../primitives/VertexTable"; +import { VertexTable } from "../primitives/VertexTable"; +import { AuxChannelTable, AuxChannel, AuxDisplacementChannel, AuxParamChannel } from "../primitives/AuxChannelTable"; + +type ChannelPropName = "normals" | "displacements" | "params"; + +export class AuxChannelLUT implements IDisposable { + public readonly texture: TextureHandle; + public readonly numVertices: number; + public readonly numBytesPerVertex: number; + public displacements?: Map; + public normals?: Map; + public params?: Map; + + private constructor(texture: TextureHandle, table: AuxChannelTable) { + this.texture = texture; + this.numVertices = table.numVertices; + this.numBytesPerVertex = table.numBytesPerVertex; + + this.initChannels(table, "displacements"); + this.initChannels(table, "normals"); + this.initChannels(table, "params"); + } + + private initChannels(table: AuxChannelTable, name: ChannelPropName): void { + const channels = table[name]; + if (undefined === channels) + return; + + const map = (this[name] = new Map()); + for (const channel of channels) { + // Compiler doesn't appear to deduce specific T here? 'as any' to work around... + // error TS2345: Argument of type 'AuxChannel | AuxDisplacementChannel | AuxParamChannel' is not assignable to parameter of type 'T'. + map.set(channel.name, channel as any); + } + } + + public get bytesUsed(): number { return this.texture.bytesUsed; } + public get hasScalarAnimation() { return undefined !== this.params; } + + public dispose() { + dispose(this.texture); + } + + public static create(table: AuxChannelTable): AuxChannelLUT | undefined { + const texture = TextureHandle.createForData(table.width, table.height, table.data); + return undefined !== texture ? new AuxChannelLUT(texture, table) : undefined; + } +} /** Represents the finished lookup table ready for submittal to GPU. */ export class VertexLUT implements IDisposable { @@ -20,42 +67,39 @@ export class VertexLUT implements IDisposable { public readonly qOrigin: Float32Array; // Origin of quantized positions public readonly qScale: Float32Array; // Scale of quantized positions public readonly uvQParams?: Float32Array; // If vertices contain texture UV params, quantization parameters as [origin.x, origin.y, scale.x, scale.y ] - public readonly auxDisplacements?: Map; // Auxilliary displacements. - public readonly auxNormals?: Map; // Auxilliary displacements. - public readonly auxParams?: Map; // Auxilliary displacements. + public readonly auxChannels?: AuxChannelLUT; - public get bytesUsed(): number { return this.texture.bytesUsed; } + public get hasAnimation() { return undefined !== this.auxChannels; } + public get hasScalarAnimation() { return undefined !== this.auxChannels && this.auxChannels.hasScalarAnimation; } - public static createFromVertexTable(vt: VertexTable): VertexLUT | undefined { + public get bytesUsed(): number { + let bytesUsed = this.texture.bytesUsed; + if (undefined !== this.auxChannels) + bytesUsed += this.auxChannels.bytesUsed; + + return bytesUsed; + } + + public static createFromVertexTable(vt: VertexTable, aux?: AuxChannelTable): VertexLUT | undefined { const texture = TextureHandle.createForData(vt.width, vt.height, vt.data); - return undefined !== texture ? new VertexLUT(texture, vt, ColorInfo.createFromVertexTable(vt), vt.qparams, vt.uvParams) : undefined; + if (undefined === texture) + return undefined; + + const auxLUT = undefined !== aux ? AuxChannelLUT.create(aux) : undefined; + return new VertexLUT(texture, vt, ColorInfo.createFromVertexTable(vt), vt.qparams, vt.uvParams, auxLUT); } - private constructor(texture: TextureHandle, table: VertexTable, colorInfo: ColorInfo, qparams: QParams3d, uvParams?: QParams2d) { + private constructor(texture: TextureHandle, table: VertexTable, colorInfo: ColorInfo, qparams: QParams3d, uvParams?: QParams2d, auxChannels?: AuxChannelLUT) { this.texture = texture; this.numVertices = table.numVertices; this.numRgbaPerVertex = table.numRgbaPerVertex; this.colorInfo = colorInfo; this.qOrigin = qorigin3dToArray(qparams.origin); this.qScale = qscale3dToArray(qparams.scale); + this.auxChannels = auxChannels; + if (undefined !== uvParams) this.uvQParams = qparams2dToArray(uvParams); - - if (undefined !== table.auxDisplacements) { - this.auxDisplacements = new Map(); - for (const auxDisplacement of table.auxDisplacements) - this.auxDisplacements.set(auxDisplacement.name, auxDisplacement); - } - if (undefined !== table.auxParams) { - this.auxParams = new Map(); - for (const auxParam of table.auxParams) - this.auxParams.set(auxParam.name, auxParam); - } - if (undefined !== table.auxNormals) { - this.auxNormals = new Map(); - for (const auxNormal of table.auxNormals) - this.auxNormals.set(auxNormal.name, auxNormal); - } } public dispose() { diff --git a/core/frontend/src/render/webgl/glsl/Animation.ts b/core/frontend/src/render/webgl/glsl/Animation.ts new file mode 100644 index 0000000..f38f334 --- /dev/null +++ b/core/frontend/src/render/webgl/glsl/Animation.ts @@ -0,0 +1,296 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module WebGL */ + +import { AuxChannel, AuxParamChannel, AuxDisplacementChannel } from "../../primitives/AuxChannelTable"; +import { VertexShaderComponent, VertexShaderBuilder, VariableType } from "../ShaderBuilder"; +import { DrawParams } from "../DrawCommand"; +import { octDecodeNormal } from "./Surface"; +import { AnalysisStyle, Gradient } from "@bentley/imodeljs-common"; +import { assert } from "@bentley/bentleyjs-core"; +import { TextureUnit } from "../RenderFlags"; + +const initialize = ` + g_anim_step = vec2(1.0) / u_animLUTParams.xy; + g_anim_center = g_anim_step * 0.5; +`; + +// The vertex index is an integer in [0..numVertices]. +// The frame index is an integer in [0..numBytesPerVertex/2]. +// Therefore each frame index points at 2 bytes within the texture. +// The third component of the return value is 0.0 if the input index points to the first 2 bytes of the texel, or 1.0 if pointing to the second 2 bytes +const computeAnimLUTCoords = ` +vec3 computeAnimLUTCoords(float vertIndex, float frameIndex) { + // float baseIndex = (vertIndex * 2.0) + frameIndex; + float baseIndex = (vertIndex * u_animLUTParams.z) + frameIndex; + float halfIndex = baseIndex * 0.5; + float index = floor(halfIndex); + + float epsilon = 0.5 / u_animLUTParams.x; + float yId = floor(index / u_animLUTParams.x + epsilon); + float xId = index - u_animLUTParams.x * yId; + + vec2 texCoord = g_anim_center + vec2(xId / u_animLUTParams.x, yId / u_animLUTParams.y); + return vec3(texCoord, 2.0 * (halfIndex - index)); +}`; + +// Sample 2 bytes at the specified index. +const sampleAnimVec2 = ` +vec2 sampleAnimVec2(float vertIndex, float frameIndex) { + vec3 tc = computeAnimLUTCoords(vertIndex, frameIndex); + vec4 texel = floor(TEXTURE(u_animLUT, tc.xy) * 255.0 + 0.5); + return texel.xy * (1.0 - tc.z) + texel.zw * tc.z; +}`; + +// Position is quantized to 6 bytes (2 bytes per component). So we always must sample two adjacent texels. We discard two bytes based on whether the index is even or odd. +const computeAnimationFrameDisplacement = ` +vec3 computeAnimationFrameDisplacement(float vertIndex, float frameIndex, vec3 origin, vec3 scale) { + vec3 tc = computeAnimLUTCoords(vertIndex, frameIndex); + vec4 enc1 = floor(TEXTURE(u_animLUT, tc.xy) * 255.0 + 0.5); + tc.x += g_anim_step.x; + vec4 enc2 = floor(TEXTURE(u_animLUT, tc.xy) * 255.0 + 0.5); + + vec2 ex = enc1.xy * (1.0 - tc.z) + enc1.zw * tc.z; + vec2 ey = enc1.zw * (1.0 - tc.z) + enc2.xy * tc.z; + vec2 ez = enc2.xy * (1.0 - tc.z) + enc2.zw * tc.z; + + vec3 qpos = vec3(decodeUInt16(ex), decodeUInt16(ey), decodeUInt16(ez)); + return unquantizePosition(qpos, origin, scale).xyz; +}`; + +const computeAnimationDisplacement = ` +vec3 computeAnimationDisplacement(float vertIndex, float frameIndex0, float frameIndex1, float fraction, vec3 origin, vec3 scale) { + if (frameIndex0 < 0.0) + return vec3(0.0, 0.0, 0.0); + + vec3 displacement = computeAnimationFrameDisplacement(vertIndex, frameIndex0, origin, scale); + if (fraction > 0.0) { + vec3 displacement1 = computeAnimationFrameDisplacement(vertIndex, frameIndex1, origin, scale); + displacement += fraction * (displacement1 - displacement); + } + + return displacement; +}`; + +const adjustRawPosition = ` + rawPos.xyz += computeAnimationDisplacement(g_vertexLUTIndex, u_animDispParams.x, u_animDispParams.y, u_animDispParams.z, u_qAnimDispOrigin, u_qAnimDispScale); + return rawPos; +`; + +const computeAnimationFrameNormal = ` +vec3 computeAnimationFrameNormal(float frameIndex) { + vec2 enc = sampleAnimVec2(g_vertexLUTIndex, frameIndex); + return octDecodeNormal(enc); +}`; + +const computeAnimationNormal = ` +vec3 computeAnimationNormal(float frameIndex0, float frameIndex1, float fraction) { +vec3 normal = computeAnimationFrameNormal(frameIndex0); +if (fraction > 0.0) { + vec3 normal1 = computeAnimationFrameNormal(frameIndex1); + normal += fraction * (normal1 - normal); + } + +return normal; +}`; + +const computeAnimationFrameParam = ` +float computeAnimationFrameParam(float frameIndex, float origin, float scale) { + vec2 enc = sampleAnimVec2(g_vertexLUTIndex, frameIndex); + return clamp((origin + scale * decodeUInt16(enc)), 0.0, 1.0); +}`; + +const computeAnimationParam = ` +vec2 computeAnimationParam(float frameIndex0, float frameIndex1, float fraction, float origin, float scale) { +float param = computeAnimationFrameParam(frameIndex0, origin, scale); +if (fraction > 0.0) { + float param1 = computeAnimationFrameParam(frameIndex1, origin, scale); + param += fraction * (param1 - param); + } + + return vec2(.5, param); +}`; + +const scratchAnimParams = [ + undefined, + undefined, + new Float32Array(2), // origin, scale + new Float32Array(3), // index0, index1, fraction +]; + +function getAnimParams(size: 2 | 3, initialValue?: number): Float32Array { + const array = scratchAnimParams[size]!; + if (undefined !== initialValue) + for (let i = 0; i < array.length; i++) + array[i] = initialValue; + + return array; +} + +function getDisplacementChannel(params: DrawParams): { channel: AuxDisplacementChannel, style: AnalysisStyle } | undefined { + const style = params.target.analysisStyle; + if (undefined === style || undefined === style.displacementChannelName) + return undefined; + + const lutGeom = params.geometry.asLUT!; + const displacements = undefined !== lutGeom.lut.auxChannels ? lutGeom.lut.auxChannels.displacements : undefined; + const channel = undefined !== displacements ? displacements.get(style.displacementChannelName) : undefined; + return undefined !== channel ? { channel, style } : undefined; +} + +function getNormalChannel(params: DrawParams): AuxChannel | undefined { + const style = params.target.analysisStyle; + if (undefined === style || undefined === style.normalChannelName) + return undefined; + + const lutGeom = params.geometry.asLUT!; + const normals = undefined !== lutGeom.lut.auxChannels ? lutGeom.lut.auxChannels.normals : undefined; + return undefined !== normals ? normals.get(style.normalChannelName) : undefined; +} + +function getScalarChannel(params: DrawParams): { channel: AuxParamChannel, style: AnalysisStyle } | undefined { + const style = params.target.analysisStyle; + if (undefined === style || undefined === style.scalarChannelName) + return undefined; + + const geom = params.geometry.asMesh!; + const scalars = undefined !== geom.lut.auxChannels ? geom.lut.auxChannels.params : undefined; + const channel = undefined !== scalars ? scalars.get(style.scalarChannelName) : undefined; + return undefined !== channel ? { channel, style } : undefined; +} + +function computeAnimParams(params: Float32Array, channel: AuxChannel, fraction: number): void { + const { inputs, indices } = channel; + const inputValue = fraction * inputs[inputs.length - 1]; + for (let i = 0; i < inputs.length - 1; i++) { + if (inputValue >= inputs[i] && inputValue < inputs[i + 1]) { + params[0] = indices[i]; + params[1] = indices[i + 1]; + params[2] = inputValue - inputs[i] / (inputs[i + 1] - inputs[i]); + return; + } + } + params[0] = params[1] = indices[inputs.length - 1]; + params[2] = 0.0; +} + +export function addAnimation(vert: VertexShaderBuilder, isSurface: boolean): void { + // Lookup table + vert.addGlobal("g_anim_step", VariableType.Vec2); + vert.addGlobal("g_anim_center", VariableType.Vec2); + vert.addInitializer(initialize); + + vert.addUniform("u_animLUT", VariableType.Sampler2D, (prog) => { + prog.addGraphicUniform("u_animLUT", (uniform, params) => { + const channels = (params.geometry.asLUT!).lut.auxChannels!; + assert(undefined !== channels); + channels.texture.bindSampler(uniform, TextureUnit.AuxChannelLUT); + }); + }); + + vert.addUniform("u_animLUTParams", VariableType.Vec3, (prog) => { + prog.addGraphicUniform("u_animLUTParams", (uniform, params) => { + const geom = params.geometry.asLUT!; + assert(undefined !== geom && undefined !== geom.lut.auxChannels); + const tex = geom.lut.auxChannels!.texture; + const array = getAnimParams(3); + array[0] = tex.width; + array[1] = tex.height; + array[2] = geom.lut.auxChannels!.numBytesPerVertex / 2; + uniform.setUniform3fv(array); + }); + }); + + vert.addFunction(computeAnimLUTCoords); + vert.addFunction(sampleAnimVec2); + + // Displacement + vert.addFunction(computeAnimationFrameDisplacement); + vert.addFunction(computeAnimationDisplacement); + vert.set(VertexShaderComponent.AdjustRawPosition, adjustRawPosition); + + vert.addUniform("u_animDispParams", VariableType.Vec3, (prog) => { + prog.addGraphicUniform("u_animDispParams", (uniform, params) => { + const animParams = getAnimParams(3, 0.0); + const disp = getDisplacementChannel(params); + if (undefined !== disp) + computeAnimParams(animParams, disp.channel, params.target.animationFraction); + + uniform.setUniform3fv(animParams); + }); + }); + vert.addUniform("u_qAnimDispScale", VariableType.Vec3, (prog) => { + prog.addGraphicUniform("u_qAnimDispScale", (uniform, params) => { + const animParams = getAnimParams(3, 0.0); + const disp = getDisplacementChannel(params); + if (undefined !== disp) { + const displacementScale = disp.style.displacementScale ? disp.style.displacementScale : 1.0; + for (let i = 0; i < 3; i++) + animParams[i] = disp.channel.qScale[i] * displacementScale; // Apply displacement scale. + } + + uniform.setUniform3fv(animParams); + }); + }); + vert.addUniform("u_qAnimDispOrigin", VariableType.Vec3, (prog) => { + prog.addGraphicUniform("u_qAnimDispOrigin", (uniform, params) => { + const animParams = getAnimParams(3, 0.0); + const disp = getDisplacementChannel(params); + if (undefined !== disp) { + const displacementScale = disp.style.displacementScale ? disp.style.displacementScale : 1.0; + for (let i = 0; i < 3; i++) + animParams[i] = disp.channel.qOrigin[i] * displacementScale; // Apply displacement scale + } + + uniform.setUniform3fv(animParams); + }); + }); + + // Normal and param + if (isSurface) { + vert.addFunction(octDecodeNormal); + vert.addFunction(computeAnimationFrameNormal); + vert.addFunction(computeAnimationNormal); + + vert.addFunction(computeAnimationFrameParam); + vert.addFunction(computeAnimationParam); + + vert.addUniform("u_animNormalParams", VariableType.Vec3, (prog) => { + prog.addGraphicUniform("u_animNormalParams", (uniform, params) => { + const animParams = getAnimParams(3, -1.0); + const channel = getNormalChannel(params); + if (undefined !== channel) + computeAnimParams(animParams, channel, params.target.animationFraction); + + uniform.setUniform3fv(animParams); + }); + }); + + vert.addUniform("u_animScalarParams", VariableType.Vec3, (prog) => { + prog.addGraphicUniform("u_animScalarParams", (uniform, params) => { + const scalars = getScalarChannel(params); + const animParams = getAnimParams(3, -1.0); + if (undefined !== scalars) + computeAnimParams(animParams, scalars.channel, params.target.animationFraction); + + uniform.setUniform3fv(animParams); + }); + }); + + vert.addUniform("u_animScalarQParams", VariableType.Vec2, (prog) => { + prog.addGraphicUniform("u_animScalarQParams", (uniform, params) => { + const scalars = getScalarChannel(params); + const animParams = getAnimParams(2, 1.0); + if (undefined !== scalars) { + const rangeScale = scalars.style.scalarRange!.high - scalars.style.scalarRange!.low; + animParams[0] = Gradient.ThematicSettings.margin + (scalars.channel.qOrigin - scalars.style.scalarRange!.low) / rangeScale; + animParams[1] = Gradient.ThematicSettings.contentRange * scalars.channel.qScale / rangeScale; + } + + uniform.setUniform2fv(animParams); + }); + }); + } +} diff --git a/core/frontend/src/render/webgl/glsl/ClipMask.ts b/core/frontend/src/render/webgl/glsl/ClipMask.ts index d9bbe40..3fe7fbd 100644 --- a/core/frontend/src/render/webgl/glsl/ClipMask.ts +++ b/core/frontend/src/render/webgl/glsl/ClipMask.ts @@ -9,12 +9,12 @@ import { ShaderProgram } from "../ShaderProgram"; import { GLSLFragment } from "./Fragment"; import { addModelViewProjectionMatrix } from "./Vertex"; -const computePosition = "return u_mvp * rawPos;"; +const computePosition = "return MAT_MVP * rawPos;"; const computeBaseColor = "return vec4(1.0);"; export function createClipMaskProgram(context: WebGLRenderingContext): ShaderProgram { - const builder = new ProgramBuilder(false); + const builder = new ProgramBuilder(); addModelViewProjectionMatrix(builder.vert); builder.vert.set(VertexShaderComponent.ComputePosition, computePosition); diff --git a/core/frontend/src/render/webgl/glsl/Clipping.ts b/core/frontend/src/render/webgl/glsl/Clipping.ts index 3145fbf..1c1b80a 100644 --- a/core/frontend/src/render/webgl/glsl/Clipping.ts +++ b/core/frontend/src/render/webgl/glsl/Clipping.ts @@ -38,9 +38,9 @@ const unpackFloat = ` } `; +// ###TODO: oct-encode the normal to reduce # of samples from 4 to 2 const unpackClipPlane = ` vec4 getClipPlane(int index) { - // ###TODO: oct-encode the normal to reduce # of samples from 4 to 2 float y = (float(index) + 0.5) / float(u_numClips); float sx = 0.25; vec2 tc = vec2(0.125, y); diff --git a/core/frontend/src/render/webgl/glsl/Color.ts b/core/frontend/src/render/webgl/glsl/Color.ts index 52893ec..8b80872 100644 --- a/core/frontend/src/render/webgl/glsl/Color.ts +++ b/core/frontend/src/render/webgl/glsl/Color.ts @@ -9,7 +9,6 @@ import { VariableType, FragmentShaderComponent, } from "../ShaderBuilder"; -import { LUTGeometry } from "../CachedGeometry"; import { GLSLFragment } from "./Fragment"; import { addRenderPass } from "./RenderPass"; @@ -41,7 +40,7 @@ export function addColor(builder: ProgramBuilder, forwardBaseAlpha: boolean = fa // ShaderSource::AddRenderPass builder.vert.addUniform("u_color", VariableType.Vec4, (prog) => { prog.addGraphicUniform("u_color", (uniform, params) => { - const lutGeom = params.geometry as LUTGeometry; + const lutGeom = params.geometry.asLUT!; const color = lutGeom.getColor(params.target); if (color.isUniform) { const rgba = color.uniform; diff --git a/core/frontend/src/render/webgl/glsl/Common.ts b/core/frontend/src/render/webgl/glsl/Common.ts index 6894f07..add5a2b 100644 --- a/core/frontend/src/render/webgl/glsl/Common.ts +++ b/core/frontend/src/render/webgl/glsl/Common.ts @@ -7,11 +7,9 @@ import { ShaderBuilder, ProgramBuilder, VariableType, ShaderType } from "../ShaderBuilder"; import { UniformHandle } from "../Handle"; import { DrawParams } from "../DrawCommand"; -import { LUTGeometry } from "../CachedGeometry"; import { ShaderFlags } from "../ShaderProgram"; import { System, RenderType } from "../System"; import { assert } from "@bentley/bentleyjs-core"; -import { SurfaceGeometry } from "../Surface"; const extractShaderBit = ` float extractShaderBit(float flag) { return extractNthBit(floor(u_shaderFlags + 0.5), flag); } @@ -25,6 +23,7 @@ function addShaderFlagsLookup(shader: ShaderBuilder) { shader.addConstant("kShaderBit_NonUniformColor", VariableType.Float, "1.0"); shader.addConstant("kShaderBit_OITFlatAlphaWeight", VariableType.Float, "2.0"); shader.addConstant("kShaderBit_OITScaleOutput", VariableType.Float, "3.0"); + shader.addConstant("kShaderBit_IgnoreNonLocatable", VariableType.Float, "4.0"); shader.addFunction(GLSLCommon.extractNthBit); shader.addFunction(extractShaderBit); @@ -40,8 +39,8 @@ export function addViewMatrix(vert: ShaderBuilder): void { } function setShaderFlags(uniform: UniformHandle, params: DrawParams) { - assert(params.geometry instanceof LUTGeometry); - const geom = params.geometry as LUTGeometry; + assert(params.geometry.asLUT !== undefined); + const geom = params.geometry.asLUT!; let flags = params.target.currentShaderFlags; const color = geom.getColor(params.target); @@ -56,9 +55,16 @@ function setShaderFlags(uniform: UniformHandle, params: DrawParams) { // Otherwise, the very tiny Z range makes things fade to black as the precision limit is encountered. This workaround disregards Z // in calculating the color, so it means that transparency is less accurate based on Z-ordering, but it is the best we can do with // this algorithm on low-end hardware. + + // Finally, the application can put the viewport into "fadeout mode", which explicitly enables flat alpha weight in order to de-emphasize transparent geometry. const maxRenderType = System.instance.capabilities.maxRenderType; - const surface = params.geometry instanceof SurfaceGeometry ? params.geometry as SurfaceGeometry : undefined; - if ((undefined !== surface && (surface.isGlyph || surface.isTileSection)) || RenderType.TextureUnsignedByte === maxRenderType) + let flatAlphaWeight = RenderType.TextureUnsignedByte === maxRenderType || params.target.isFadeOutActive; + if (!flatAlphaWeight) { + const surface = params.geometry.asSurface; + flatAlphaWeight = undefined !== surface && (surface.isGlyph || surface.isTileSection); + } + + if (flatAlphaWeight) flags |= ShaderFlags.OITFlatAlphaWeight; // If Cesium-style transparency is being used with non-float texture targets, we must scale the output in the shaders to 0-1 range. @@ -66,6 +72,9 @@ function setShaderFlags(uniform: UniformHandle, params: DrawParams) { if (maxRenderType < RenderType.TextureHalfFloat) flags |= ShaderFlags.OITScaleOutput; + if (!params.target.drawNonLocatable) + flags |= ShaderFlags.IgnoreNonLocatable; + uniform.setUniform1f(flags); } @@ -90,7 +99,7 @@ export function addFrustum(builder: ProgramBuilder) { builder.addGlobal("kFrustumType_Perspective", VariableType.Float, ShaderType.Both, "2.0", true); } -const computeEyeSpace = "v_eyeSpace = (u_mv * rawPosition);"; +const computeEyeSpace = "v_eyeSpace = (MAT_MV * rawPosition);"; export function addEyeSpace(builder: ProgramBuilder) { builder.addInlineComputedVarying("v_eyeSpace", VariableType.Vec4, computeEyeSpace); diff --git a/core/frontend/src/render/webgl/glsl/Composite.ts b/core/frontend/src/render/webgl/glsl/Composite.ts index 8ee28dc..82b183b 100644 --- a/core/frontend/src/render/webgl/glsl/Composite.ts +++ b/core/frontend/src/render/webgl/glsl/Composite.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ /** @module WebGL */ -import { assert } from "@bentley/bentleyjs-core"; import { TextureUnit, CompositeFlags } from "../RenderFlags"; import { VariableType, FragmentShaderComponent } from "../ShaderBuilder"; import { ShaderProgram } from "../ShaderProgram"; @@ -13,6 +12,7 @@ import { Texture2DHandle } from "../Texture"; import { createViewportQuadBuilder } from "./ViewportQuad"; import { GLSLFragment, addWindowToTexCoords } from "./Fragment"; import { addHiliteSettings } from "./FeatureSymbology"; +import { assert } from "@bentley/bentleyjs-core"; const isEdgePixel = ` bool isEdgePixel(float xOffset, float yOffset) { @@ -142,7 +142,7 @@ export function createCompositeProgram(flags: CompositeFlags, context: WebGLRend if (wantOcclusion) { frag.addUniform("u_occlusion", VariableType.Sampler2D, (prog) => { prog.addGraphicUniform("u_occlusion", (uniform, params) => { - Texture2DHandle.bindSampler(uniform, (params.geometry as CompositeGeometry).occlusion, TextureUnit.Four); + Texture2DHandle.bindSampler(uniform, (params.geometry as CompositeGeometry).occlusion!, TextureUnit.Four); }); }); diff --git a/core/frontend/src/render/webgl/glsl/Edge.ts b/core/frontend/src/render/webgl/glsl/Edge.ts index d94239f..1dc2159 100644 --- a/core/frontend/src/render/webgl/glsl/Edge.ts +++ b/core/frontend/src/render/webgl/glsl/Edge.ts @@ -4,21 +4,23 @@ *--------------------------------------------------------------------------------------------*/ /** @module WebGL */ -import { assert } from "@bentley/bentleyjs-core"; import { ProgramBuilder, + ShaderBuilderFlags, VariableType, VertexShaderComponent, } from "../ShaderBuilder"; -import { addModelViewMatrix, addProjectionMatrix, addAnimation, GLSLVertex, addNormalMatrix } from "./Vertex"; +import { addModelViewMatrix, addProjectionMatrix, GLSLVertex, addNormalMatrix } from "./Vertex"; +import { addAnimation } from "./Animation"; import { addViewport, addModelToWindowCoordinates } from "./Viewport"; import { GL } from "../GL"; import { addColor } from "./Color"; import { addWhiteOnWhiteReversal } from "./Fragment"; import { addShaderFlags } from "./Common"; import { addLineCode, adjustWidth } from "./Polyline"; -import { EdgeGeometry, SilhouetteEdgeGeometry } from "../Mesh"; import { octDecodeNormal } from "./Surface"; +import { assert } from "@bentley/bentleyjs-core"; +import { IsInstanced, IsAnimated } from "../TechniqueFlags"; const decodeEndPointAndQuadIndices = ` g_otherIndex = decodeUInt32(a_endPointAndQuadIndices.xyz); @@ -34,13 +36,13 @@ const animateEndPoint = `g_otherPos.xyz += computeAnimationDisplacement(g_otherI `; const checkForSilhouetteDiscard = ` - vec3 n0 = u_nmx * octDecodeNormal(a_normals.xy); - vec3 n1 = u_nmx * octDecodeNormal(a_normals.zw); + vec3 n0 = MAT_NORM * octDecodeNormal(a_normals.xy); + vec3 n1 = MAT_NORM * octDecodeNormal(a_normals.zw); - if (0.0 == u_mvp[0].w) { + if (0.0 == MAT_MVP[0].w) { return n0.z * n1.z > 0.0; // orthographic. } else { - vec4 viewPos = u_mv * rawPos; // perspective + vec4 viewPos = MAT_MV * rawPos; // perspective vec3 toEye = normalize(viewPos.xyz); float dot0 = dot(n0, toEye); float dot1 = dot(n1, toEye); @@ -50,7 +52,7 @@ const checkForSilhouetteDiscard = ` // Need to discard if either is non-silhouette. vec4 otherPosition = g_otherPos; - viewPos = u_mv * otherPosition; + viewPos = MAT_MV * otherPosition; toEye = normalize(viewPos.xyz); dot0 = dot(n0, toEye); dot1 = dot(n1, toEye); @@ -61,7 +63,7 @@ const checkForSilhouetteDiscard = ` const computePosition = ` v_lnInfo = vec4(0.0, 0.0, 0.0, 0.0); // init and set flag to false - vec4 pos = u_mvp * rawPos; + vec4 pos = MAT_MVP * rawPos; vec4 other = g_otherPos; vec3 modelDir = other.xyz - pos.xyz; float miterAdjust = 0.0; @@ -96,8 +98,8 @@ const computePosition = ` `; const lineCodeArgs = "g_windowDir, g_windowPos, 0.0"; -function createBase(isSilhouette: boolean, isAnimated: boolean): ProgramBuilder { - const builder = new ProgramBuilder(true, isAnimated); +function createBase(isSilhouette: boolean, instanced: IsInstanced, isAnimated: IsAnimated): ProgramBuilder { + const builder = new ProgramBuilder(instanced ? ShaderBuilderFlags.InstancedVertexTable : ShaderBuilderFlags.VertexTable); const vert = builder.vert; vert.addGlobal("g_otherPos", VariableType.Vec4); @@ -125,13 +127,13 @@ function createBase(isSilhouette: boolean, isAnimated: boolean): ProgramBuilder addModelViewMatrix(vert); if (isAnimated) - addAnimation(vert, false, false); + addAnimation(vert, false); vert.addAttribute("a_endPointAndQuadIndices", VariableType.Vec4, (shaderProg) => { shaderProg.addAttribute("a_endPointAndQuadIndices", (attr, params) => { const geom = params.geometry; - assert(geom instanceof EdgeGeometry); - const edgeGeom = geom as EdgeGeometry; + assert(undefined !== geom.asEdge); + const edgeGeom = geom.asEdge!; attr.enableArray(edgeGeom.endPointAndQuadIndices, 4, GL.DataType.UnsignedByte, false, 0, 0); }); }); @@ -149,8 +151,8 @@ function createBase(isSilhouette: boolean, isAnimated: boolean): ProgramBuilder vert.addAttribute("a_normals", VariableType.Vec4, (shaderProg) => { shaderProg.addAttribute("a_normals", (attr, params) => { const geom = params.geometry; - assert(geom instanceof SilhouetteEdgeGeometry); - const silhouetteGeom = geom as SilhouetteEdgeGeometry; + assert(undefined !== geom.asSilhouette); + const silhouetteGeom = geom.asSilhouette!; attr.enableArray(silhouetteGeom.normalPairs, 4, GL.DataType.UnsignedByte, false, 0, 0); }); }); @@ -159,8 +161,8 @@ function createBase(isSilhouette: boolean, isAnimated: boolean): ProgramBuilder return builder; } -export function createEdgeBuilder(isSilhouette: boolean, isAnimated: boolean): ProgramBuilder { - const builder = createBase(isSilhouette, isAnimated); +export function createEdgeBuilder(isSilhouette: boolean, instanced: IsInstanced, isAnimated: IsAnimated): ProgramBuilder { + const builder = createBase(isSilhouette, instanced, isAnimated); addShaderFlags(builder); addColor(builder); addWhiteOnWhiteReversal(builder.frag); diff --git a/core/frontend/src/render/webgl/glsl/FeatureSymbology.ts b/core/frontend/src/render/webgl/glsl/FeatureSymbology.ts index 0130462..05d942e 100644 --- a/core/frontend/src/render/webgl/glsl/FeatureSymbology.ts +++ b/core/frontend/src/render/webgl/glsl/FeatureSymbology.ts @@ -23,11 +23,11 @@ import { GLSLFragment, addWindowToTexCoords } from "./Fragment"; import { GLSLCommon, addEyeSpace, addUInt32s } from "./Common"; import { GLSLDecode } from "./Decode"; import { addLookupTable } from "./LookupTable"; -import { assert } from "@bentley/bentleyjs-core"; import { addRenderPass } from "./RenderPass"; -import { SurfaceGeometry } from "../Surface"; import { UniformHandle } from "../Handle"; +import { GL } from "../GL"; import { DrawParams } from "../DrawCommand"; +import { assert } from "@bentley/bentleyjs-core"; export const enum FeatureSymbologyOptions { None = 0, @@ -54,16 +54,18 @@ function addFlagConstants(builder: ShaderBuilder): void { builder.addConstant("kOvrBit_IgnoreMaterial", VariableType.Float, "7.0"); } -const computeFeatureIndex = ` - g_featureIndex = floor(TEXTURE(u_vertLUT, g_featureIndexCoords) * 255.0 + 0.5); -`; - -const getFeatureIndex = ` -float getFeatureIndex() { -` + computeFeatureIndex + ` - return decodeUInt32(g_featureIndex.xyz); +const computeLUTFeatureIndex = `floor(TEXTURE(u_vertLUT, g_featureIndexCoords) * 255.0 + 0.5)`; +const computeInstanceFeatureIndex = `vec4(a_featureId, 0.0)`; +function computeFeatureIndex(instanced: boolean): string { + return `g_featureIndex = ` + (instanced ? computeInstanceFeatureIndex : computeLUTFeatureIndex) + `;`; +} +function getFeatureIndex(instanced: boolean): string { + return ` + float getFeatureIndex() { + g_featureIndex = ` + computeFeatureIndex(instanced) + `; + return decodeUInt32(g_featureIndex.xyz); + }`; } -`; // Returns 1.0 if the specified flag is not globally overridden and is set in flags const extractNthFeatureBit = ` @@ -105,7 +107,18 @@ float ComputeLineCode() { function addFeatureIndex(vert: VertexShaderBuilder): void { vert.addGlobal("g_featureIndex", VariableType.Vec4); - vert.addFunction(getFeatureIndex); + vert.addFunction(getFeatureIndex(vert.usesInstancedGeometry)); + if (vert.usesInstancedGeometry) { + vert.addAttribute("a_featureId", VariableType.Vec3, (prog) => { + prog.addAttribute("a_featureId", (attr, params) => { + const geom = params.geometry.asInstanced!; + assert(undefined !== geom); + assert(undefined !== geom.featureIds, "Cannot use feature shaders if no features"); + if (undefined !== geom.featureIds) + attr.enableArray(geom.featureIds, 3, GL.DataType.UnsignedByte, false, 0, 0, true); + }); + }); + } } // Discards vertex if feature is invisible; or rendering opaque during translucent pass or vice-versa @@ -137,7 +150,7 @@ function addCommon(builder: ProgramBuilder, mode: FeatureMode, opts: FeatureSymb if (!haveOverrides) { // For pick output we must compute g_featureIndex... if (FeatureMode.Pick === mode) - vert.set(VertexShaderComponent.ComputeFeatureOverrides, computeFeatureIndex); + vert.set(VertexShaderComponent.ComputeFeatureOverrides, computeFeatureIndex(vert.usesInstancedGeometry)); return true; } @@ -383,7 +396,7 @@ const checkForEarlySurfaceDiscardWithFeatureID = ` function addEdgeWidth(builder: ShaderBuilder) { builder.addUniform("u_lineWeight", VariableType.Float, (prog) => { prog.addGraphicUniform("u_lineWeight", (uniform, params) => { - const mesh = params.geometry as SurfaceGeometry; + const mesh = params.geometry.asSurface!; const width = params.target.getEdgeWeight(params.programParams, mesh.edgeWidth); uniform.setUniform1f(width < 1.0 ? 1.0 : width); }); @@ -531,13 +544,15 @@ const computeFeatureOverrides = ` v_feature_alpha_flashed = vec2(-1.0, 0.0); vec4 value = getFirstFeatureRgba(); - // 2 RGBA values per feature - first R is override flags mask - if (0.0 == value.r) { + // 2 RGBA values per feature - first R is override flags mask, first A is 1.0 for non-locatable feature. + // The latter makes the feature invisible only if the "ignore non-locatable" shader flag is set. + float nonLocatable = value.a * extractShaderBit(kShaderBit_IgnoreNonLocatable); + if (0.0 == value.r + nonLocatable) return; // nothing overridden for this feature - } float flags = value.r * 256.0; - feature_invisible = 1.0 == extractNthFeatureBit(flags, kOvrBit_Visibility); + float invisible = extractNthFeatureBit(flags, kOvrBit_Visibility); + feature_invisible = 0.0 != (invisible + nonLocatable); if (feature_invisible) return; diff --git a/core/frontend/src/render/webgl/glsl/Fragment.ts b/core/frontend/src/render/webgl/glsl/Fragment.ts index 0c56451..5596d7c 100644 --- a/core/frontend/src/render/webgl/glsl/Fragment.ts +++ b/core/frontend/src/render/webgl/glsl/Fragment.ts @@ -9,10 +9,6 @@ import { ColorDef } from "@bentley/imodeljs-common"; import { GLSLDecode } from "./Decode"; import { System } from "../System"; -/* ###TODO: IBL -import { Matrix3 } from "../Matrix"; -*/ - export function addWindowToTexCoords(frag: FragmentShaderBuilder) { const windowCoordsToTexCoords = `\nvec2 windowCoordsToTexCoords(vec2 wc) { return wc * u_invScreenSize; }\n`; frag.addFunction(windowCoordsToTexCoords); @@ -49,18 +45,6 @@ export function addRenderTargetIndex(frag: FragmentShaderBuilder) { }); } -/* ###TODO: IBL -export function addNormalMatrixF(frag: FragmentShaderBuilder) { - frag.addUniform("u_nmx", VariableType.Mat3, (prog) => { - prog.addGraphicUniform("u_nmx", (uniform, params) => { - const rotMat: Matrix3 | undefined = params.modelViewMatrix.getRotation(); - if (undefined !== rotMat) - uniform.setMatrix3(rotMat); - }); - }); -} -*/ - const reverseWhiteOnWhite = ` const vec3 white = vec3(1.0); const vec3 epsilon = vec3(0.0001); diff --git a/core/frontend/src/render/webgl/glsl/Lighting.ts b/core/frontend/src/render/webgl/glsl/Lighting.ts index bbeff84..8a9c811 100644 --- a/core/frontend/src/render/webgl/glsl/Lighting.ts +++ b/core/frontend/src/render/webgl/glsl/Lighting.ts @@ -8,9 +8,6 @@ import { ProgramBuilder, VariableType, FragmentShaderComponent, - /* ###TODO: IBL - FragmentShaderBuilder, - */ } from "../ShaderBuilder"; import { addFrustum } from "./Common"; import { Material } from "../Material"; diff --git a/core/frontend/src/render/webgl/glsl/PointCloud.ts b/core/frontend/src/render/webgl/glsl/PointCloud.ts index a3b6400..0b194ae 100644 --- a/core/frontend/src/render/webgl/glsl/PointCloud.ts +++ b/core/frontend/src/render/webgl/glsl/PointCloud.ts @@ -4,19 +4,19 @@ *--------------------------------------------------------------------------------------------*/ /** @module WebGL */ -import { assert } from "@bentley/bentleyjs-core"; import { addModelViewProjectionMatrix } from "./Vertex"; import { addUniformHiliter } from "./FeatureSymbology"; import { ProgramBuilder, VertexShaderComponent, FragmentShaderComponent, VariableType } from "../ShaderBuilder"; import { PointCloudGeometry } from "../PointCloud"; import { GL } from "../GL"; +import { assert } from "@bentley/bentleyjs-core"; -const computePosition = "gl_PointSize = 1.0; return u_mvp * rawPos;"; +const computePosition = "gl_PointSize = 1.0; return MAT_MVP * rawPos;"; const computeColor = "return vec4(a_color, 1.0);"; const computeBaseColor = "return v_color;"; function createBuilder(): ProgramBuilder { - const builder = new ProgramBuilder(false); + const builder = new ProgramBuilder(); const vert = builder.vert; vert.set(VertexShaderComponent.ComputePosition, computePosition); addModelViewProjectionMatrix(vert); diff --git a/core/frontend/src/render/webgl/glsl/PointString.ts b/core/frontend/src/render/webgl/glsl/PointString.ts index df6f7a1..715d9f0 100644 --- a/core/frontend/src/render/webgl/glsl/PointString.ts +++ b/core/frontend/src/render/webgl/glsl/PointString.ts @@ -9,20 +9,21 @@ import { addModelViewProjectionMatrix, GLSLVertex } from "./Vertex"; import { addShaderFlags } from "./Common"; import { addColor } from "./Color"; import { addWhiteOnWhiteReversal } from "./Fragment"; -import { ProgramBuilder, VertexShaderComponent, VariableType, FragmentShaderComponent } from "../ShaderBuilder"; +import { ShaderBuilderFlags, ProgramBuilder, VertexShaderComponent, VariableType, FragmentShaderComponent } from "../ShaderBuilder"; +import { IsInstanced } from "../TechniqueFlags"; const computePosition = ` float lineWeight = ComputeLineWeight(); lineWeight += 0.5 * float(lineWeight > 4.0); // fudge factor for rounding fat points... gl_PointSize = lineWeight; - return u_mvp * rawPos; + return MAT_MVP * rawPos; `; +// gl_PointSize specifies coordinates of this fragment within the point in range [0,1]. +// This should be the most precise of the many approaches we've tried, but it still yields some asymmetry... +// Discarding if it meets radius precisely seems to reduce that slightly... +// ###TODO try point sprites? const roundCorners = ` - // gl_PointSize specifies coordinates of this fragment within the point in range [0,1]. - // This should be the most precise of the many approaches we've tried, but it still yields some asymmetry... - // Discarding if it meets radius precisely seems to reduce that slightly... - // ###TODO try point sprites? const vec2 center = vec2(0.5, 0.5); vec2 vt = gl_PointCoord - center; return dot(vt, vt) * v_roundCorners >= 0.25; // meets or exceeds radius of circle @@ -30,9 +31,8 @@ const roundCorners = ` const computeRoundCorners = " v_roundCorners = gl_PointSize > 4.0 ? 1.0 : 0.0;"; -function createBase(): ProgramBuilder { - const builder = new ProgramBuilder(true); - // addShaderFlags(builder); // Commented out in the c++ code +function createBase(instanced: IsInstanced): ProgramBuilder { + const builder = new ProgramBuilder(instanced ? ShaderBuilderFlags.InstancedVertexTable : ShaderBuilderFlags.VertexTable); const vert = builder.vert; vert.set(VertexShaderComponent.ComputePosition, computePosition); addModelViewProjectionMatrix(vert); @@ -49,14 +49,14 @@ function createBase(): ProgramBuilder { return builder; } -export function createPointStringHiliter(): ProgramBuilder { - const builder = createBase(); +export function createPointStringHiliter(instanced: IsInstanced): ProgramBuilder { + const builder = createBase(instanced); addHiliter(builder, true); return builder; } -export function createPointStringBuilder(): ProgramBuilder { - const builder = createBase(); +export function createPointStringBuilder(instanced: IsInstanced): ProgramBuilder { + const builder = createBase(instanced); addShaderFlags(builder); addColor(builder); addWhiteOnWhiteReversal(builder.frag); diff --git a/core/frontend/src/render/webgl/glsl/Polyline.ts b/core/frontend/src/render/webgl/glsl/Polyline.ts index d9f774d..d596934 100644 --- a/core/frontend/src/render/webgl/glsl/Polyline.ts +++ b/core/frontend/src/render/webgl/glsl/Polyline.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ /** @module WebGL */ -import { assert } from "@bentley/bentleyjs-core"; import { ProgramBuilder, + ShaderBuilderFlags, VariableType, FragmentShaderComponent, VertexShaderComponent, @@ -22,6 +22,8 @@ import { addWhiteOnWhiteReversal } from "./Fragment"; import { System } from "../System"; import { TextureUnit } from "../RenderFlags"; import { addHiliter } from "./FeatureSymbology"; +import { assert } from "@bentley/bentleyjs-core"; +import { IsInstanced } from "../TechniqueFlags"; const checkForDiscard = "return discardByLineCode;"; @@ -249,7 +251,7 @@ const computePosition = ` v_lnInfo = vec4(0.0, 0.0, 0.0, 0.0); // init and set flag to false - vec4 pos = u_mvp * rawPos; + vec4 pos = MAT_MVP * rawPos; vec4 next = g_nextPos; g_windowPos = modelToWindowCoordinates(rawPos, next); @@ -347,8 +349,8 @@ const computePosition = ` const lineCodeArgs = "g_windowDir, g_windowPos, miterAdjust"; -export function createPolylineBuilder(): ProgramBuilder { - const builder = new ProgramBuilder(true); +export function createPolylineBuilder(instanced: IsInstanced): ProgramBuilder { + const builder = new ProgramBuilder(instanced ? ShaderBuilderFlags.InstancedVertexTable : ShaderBuilderFlags.VertexTable); addShaderFlags(builder); addCommon(builder); @@ -361,8 +363,8 @@ export function createPolylineBuilder(): ProgramBuilder { return builder; } -export function createPolylineHiliter(): ProgramBuilder { - const builder = new ProgramBuilder(true); +export function createPolylineHiliter(instanced: IsInstanced): ProgramBuilder { + const builder = new ProgramBuilder(instanced ? ShaderBuilderFlags.InstancedVertexTable : ShaderBuilderFlags.VertexTable); addCommon(builder); addFrustum(builder); addHiliter(builder); diff --git a/core/frontend/src/render/webgl/glsl/SkyBox.ts b/core/frontend/src/render/webgl/glsl/SkyBox.ts index 66d20d7..b069366 100644 --- a/core/frontend/src/render/webgl/glsl/SkyBox.ts +++ b/core/frontend/src/render/webgl/glsl/SkyBox.ts @@ -17,7 +17,7 @@ const computePosition = `vec3 pos = u_rot * vec3(rawPos.x, rawPos.z, -rawPos.y); const computeTexDir = `v_texDir = rawPosition.xyz;`; export function createSkyBoxProgram(context: WebGLRenderingContext): ShaderProgram { - const prog = new ProgramBuilder(false); + const prog = new ProgramBuilder(); prog.frag.set(FragmentShaderComponent.ComputeBaseColor, computeBaseColor); prog.frag.set(FragmentShaderComponent.AssignFragData, assignFragData); diff --git a/core/frontend/src/render/webgl/glsl/SkySphere.ts b/core/frontend/src/render/webgl/glsl/SkySphere.ts index 6946ca8..6d66b5d 100644 --- a/core/frontend/src/render/webgl/glsl/SkySphere.ts +++ b/core/frontend/src/render/webgl/glsl/SkySphere.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ /** @module WebGL */ -import { assert } from "@bentley/bentleyjs-core"; import { VariableType, FragmentShaderComponent, ShaderType } from "../ShaderBuilder"; import { ShaderProgram } from "../ShaderProgram"; import { GLSLFragment } from "./Fragment"; @@ -17,6 +16,7 @@ import { GL } from "../GL"; import { Texture } from "../Texture"; import { TextureUnit } from "../RenderFlags"; import { System } from "../System"; +import { assert } from "@bentley/bentleyjs-core"; const computeGradientValue = ` // For the gradient sky it's good enough to calculate these in the vertex shader. diff --git a/core/frontend/src/render/webgl/glsl/Surface.ts b/core/frontend/src/render/webgl/glsl/Surface.ts index 13f5e7e..102f1de 100644 --- a/core/frontend/src/render/webgl/glsl/Surface.ts +++ b/core/frontend/src/render/webgl/glsl/Surface.ts @@ -12,22 +12,23 @@ import { FragmentShaderComponent, VertexShaderComponent, ShaderBuilder, + ShaderBuilderFlags, } from "../ShaderBuilder"; -import { FeatureMode } from "../TechniqueFlags"; +import { IsInstanced, IsAnimated, FeatureMode } from "../TechniqueFlags"; import { GLSLFragment, addWhiteOnWhiteReversal, addPickBufferOutputs } from "./Fragment"; -import { addProjectionMatrix, addModelViewMatrix, addNormalMatrix, addAnimation } from "./Vertex"; +import { addProjectionMatrix, addModelViewMatrix, addNormalMatrix } from "./Vertex"; +import { addAnimation } from "./Animation"; import { GLSLDecode } from "./Decode"; import { addColor } from "./Color"; import { addLighting } from "./Lighting"; import { FloatPreMulRgba } from "../FloatRGBA"; import { addSurfaceDiscard, FeatureSymbologyOptions, addFeatureSymbology, addSurfaceHiliter } from "./FeatureSymbology"; import { addShaderFlags, GLSLCommon } from "./Common"; -import { SurfaceGeometry } from "../Surface"; import { SurfaceFlags, TextureUnit } from "../RenderFlags"; import { Texture } from "../Texture"; -import { assert } from "@bentley/bentleyjs-core"; import { Material } from "../Material"; import { System } from "../System"; +import { assert } from "@bentley/bentleyjs-core"; const sampleSurfaceTexture = ` vec4 sampleSurfaceTexture() { @@ -75,17 +76,17 @@ export function addMaterial(frag: FragmentShaderBuilder): void { } const computePosition = ` - vec4 pos = u_mv * rawPos; + vec4 pos = MAT_MV * rawPos; v_pos = pos.xyz; return u_proj * pos; `; -function createCommon(animated: boolean): ProgramBuilder { - const builder = new ProgramBuilder(true, animated); +function createCommon(instanced: IsInstanced, animated: IsAnimated): ProgramBuilder { + const builder = new ProgramBuilder(instanced ? ShaderBuilderFlags.InstancedVertexTable : ShaderBuilderFlags.VertexTable); const vert = builder.vert; if (animated) - addAnimation(vert, true, true); + addAnimation(vert, true); addProjectionMatrix(vert); addModelViewMatrix(vert); @@ -95,11 +96,11 @@ function createCommon(animated: boolean): ProgramBuilder { return builder; } -export function createSurfaceHiliter(): ProgramBuilder { - const builder = createCommon(false); +export function createSurfaceHiliter(instanced: IsInstanced): ProgramBuilder { + const builder = createCommon(instanced, IsAnimated.No); addSurfaceFlags(builder, true); - addTexture(builder, false); + addTexture(builder, IsAnimated.No); addSurfaceHiliter(builder); return builder; } @@ -121,7 +122,6 @@ function addSurfaceFlagsLookup(builder: ShaderBuilder) { builder.addConstant("kSurfaceBit_TransparencyThreshold", VariableType.Float, "4.0"); builder.addConstant("kSurfaceBit_BackgroundFill", VariableType.Float, "5.0"); builder.addConstant("kSurfaceBit_HasColorAndNormal", VariableType.Float, "6.0"); - builder.addConstant("kSurfaceBit_EnvironmentMap", VariableType.Float, "7.0"); builder.addConstant("kSurfaceMask_None", VariableType.Float, "0.0"); builder.addConstant("kSurfaceMask_HasTexture", VariableType.Float, "1.0"); @@ -131,7 +131,6 @@ function addSurfaceFlagsLookup(builder: ShaderBuilder) { builder.addConstant("kSurfaceMask_TransparencyThreshold", VariableType.Float, "16.0"); builder.addConstant("kSurfaceMask_BackgroundFill", VariableType.Float, "32.0"); builder.addConstant("kSurfaceMask_HasColorAndNormal", VariableType.Float, "64.0"); - builder.addConstant("kSurfaceMask_EnvironmentMap", VariableType.Float, "128.0"); builder.addFunction(GLSLCommon.extractNthBit); builder.addFunction(extractSurfaceBit); @@ -171,12 +170,12 @@ const computeNormal = ` tc.x += 3.0 * g_vert_stepX; vec4 enc = floor(TEXTURE(u_vertLUT, tc) * 255.0 + 0.5); vec2 normal = mix(g_vertexData2, enc.xy, extractSurfaceBit(kSurfaceBit_HasColorAndNormal)); - return mix(vec3(0.0), normalize(u_nmx * octDecodeNormal(normal)), extractSurfaceBit(kSurfaceBit_HasNormals)); + return mix(vec3(0.0), normalize(MAT_NORM * octDecodeNormal(normal)), extractSurfaceBit(kSurfaceBit_HasNormals)); `; const computeAnimatedNormal = ` if (u_animNormalParams.x >= 0.0) - return normalize(u_nmx * computeAnimationNormal(u_animNormalParams.x, u_animNormalParams.y, u_animNormalParams.z)); + return normalize(MAT_NORM * computeAnimationNormal(u_animNormalParams.x, u_animNormalParams.y, u_animNormalParams.z)); ` + computeNormal; const applyBackgroundColor = ` @@ -230,27 +229,27 @@ function addSurfaceFlags(builder: ProgramBuilder, withFeatureOverrides: boolean) addSurfaceFlagsLookup(builder.frag); builder.addUniform("u_surfaceFlags", VariableType.Float, (prog) => { prog.addGraphicUniform("u_surfaceFlags", (uniform, params) => { - assert(params.geometry instanceof SurfaceGeometry); - const mesh = params.geometry as SurfaceGeometry; + assert(undefined !== params.geometry.asSurface); + const mesh = params.geometry.asSurface!; const surfFlags = mesh.computeSurfaceFlags(params.programParams); uniform.setUniform1f(surfFlags); }); }); } -function addNormal(builder: ProgramBuilder, animated: boolean) { +function addNormal(builder: ProgramBuilder, animated: IsAnimated) { addNormalMatrix(builder.vert); builder.vert.addFunction(octDecodeNormal); builder.addFunctionComputedVarying("v_n", VariableType.Vec3, "computeLightingNormal", animated ? computeAnimatedNormal : computeNormal); } -function addTexture(builder: ProgramBuilder, animated: boolean) { +function addTexture(builder: ProgramBuilder, animated: IsAnimated) { builder.vert.addFunction(GLSLDecode.unquantize2d); builder.addFunctionComputedVarying("v_texCoord", VariableType.Vec2, "computeTexCoord", animated ? computeAnimatedTexCoord : computeTexCoord); builder.vert.addUniform("u_qTexCoordParams", VariableType.Vec4, (prog) => { prog.addGraphicUniform("u_qTexCoordParams", (uniform, params) => { - const surfGeom: SurfaceGeometry = params.geometry as SurfaceGeometry; + const surfGeom = params.geometry.asSurface!; const surfFlags: SurfaceFlags = surfGeom.computeSurfaceFlags(params.programParams); if (SurfaceFlags.None !== (SurfaceFlags.HasTexture & surfFlags)) { const uvQParams = surfGeom.lut.uvQParams; @@ -265,7 +264,7 @@ function addTexture(builder: ProgramBuilder, animated: boolean) { builder.frag.addFunction(sampleSurfaceTexture); builder.frag.addUniform("s_texture", VariableType.Sampler2D, (prog) => { prog.addGraphicUniform("s_texture", (uniform, params) => { - const surfGeom = params.geometry as SurfaceGeometry; + const surfGeom = params.geometry.asSurface!; const surfFlags = surfGeom.computeSurfaceFlags(params.programParams); if (SurfaceFlags.None !== (SurfaceFlags.HasTexture & surfFlags)) { const texture = (params.target.analysisTexture ? params.target.analysisTexture : surfGeom.texture) as Texture; @@ -278,14 +277,14 @@ function addTexture(builder: ProgramBuilder, animated: boolean) { }); } -export function createSurfaceBuilder(feat: FeatureMode, animated: boolean): ProgramBuilder { - const builder = createCommon(animated); +export function createSurfaceBuilder(feat: FeatureMode, isInstanced: IsInstanced, isAnimated: IsAnimated): ProgramBuilder { + const builder = createCommon(isInstanced, isAnimated); addShaderFlags(builder); addFeatureSymbology(builder, feat, FeatureMode.Overrides === feat ? FeatureSymbologyOptions.Surface : FeatureSymbologyOptions.None); addSurfaceFlags(builder, FeatureMode.Overrides === feat); addSurfaceDiscard(builder, feat); - addNormal(builder, animated); + addNormal(builder, isAnimated); // In HiddenLine mode, we must compute the base color (plus feature overrides etc) in order to get the alpha, then replace with background color (preserving alpha for the transparency threshold test). builder.frag.set(FragmentShaderComponent.FinalizeBaseColor, applyBackgroundColor); @@ -297,11 +296,11 @@ export function createSurfaceBuilder(feat: FeatureMode, animated: boolean): Prog }); }); - addTexture(builder, animated); + addTexture(builder, isAnimated); builder.frag.addUniform("u_applyGlyphTex", VariableType.Float, (prog) => { prog.addGraphicUniform("u_applyGlyphTex", (uniform, params) => { - const surfGeom = params.geometry as SurfaceGeometry; + const surfGeom = params.geometry.asSurface!; const surfFlags: SurfaceFlags = surfGeom.computeSurfaceFlags(params.programParams); let isGlyph = false; if (SurfaceFlags.None !== (SurfaceFlags.HasTexture & surfFlags)) diff --git a/core/frontend/src/render/webgl/glsl/Vertex.ts b/core/frontend/src/render/webgl/glsl/Vertex.ts index 9c271a6..7a6c964 100644 --- a/core/frontend/src/render/webgl/glsl/Vertex.ts +++ b/core/frontend/src/render/webgl/glsl/Vertex.ts @@ -4,15 +4,13 @@ *--------------------------------------------------------------------------------------------*/ /** @module WebGL */ +import { assert } from "@bentley/bentleyjs-core"; import { VertexShaderBuilder, VariableType } from "../ShaderBuilder"; import { Matrix3, Matrix4 } from "../Matrix"; -import { LUTGeometry } from "../CachedGeometry"; -import { MeshGeometry } from "../Mesh"; import { TextureUnit, RenderPass } from "../RenderFlags"; +import { GL } from "../GL"; import { GLSLDecode } from "./Decode"; import { addLookupTable } from "./LookupTable"; -import { octDecodeNormal } from "./Surface"; -import { Gradient } from "@bentley/imodeljs-common"; const initializeVertLUTCoords = ` g_vertexLUTIndex = decodeUInt32(a_pos); @@ -27,93 +25,42 @@ const unquantizeVertexPosition = ` vec4 unquantizeVertexPosition(vec3 pos, vec3 origin, vec3 scale) { return unquantizePosition(pos, origin, scale); } `; -const unquantizeVertexPositionFromLUT = ` +const unquantizeVertexPositionFromLUTPrelude = ` vec4 unquantizeVertexPosition(vec3 encodedIndex, vec3 origin, vec3 scale) { // Need to read 2 rgba values to obtain 6 16-bit integers for position vec2 tc = g_vertexBaseCoords; vec4 enc1 = floor(TEXTURE(u_vertLUT, tc) * 255.0 + 0.5); tc.x += g_vert_stepX; vec4 enc2 = floor(TEXTURE(u_vertLUT, tc) * 255.0 + 0.5); +`; +const computeFeatureIndexCoords = ` tc.x += g_vert_stepX; g_featureIndexCoords = tc; - +`; +const unquantizeVertexPositionFromLUTPostlude = ` vec3 qpos = vec3(decodeUInt16(enc1.xy), decodeUInt16(enc1.zw), decodeUInt16(enc2.xy)); - - // Might as well decode the color index since we already read it...may not end up being used. - // (NOTE = If this is a textured mesh, the normal is stored where the color index would otherwise be...) g_vertexData2 = enc2.zw; - return unquantizePosition(qpos, origin, scale); } `; -const computeAnimationFrameDisplacement = ` -vec3 computeAnimationFrameDisplacement(float vertexLUTIndex, float frameIndex, vec3 origin, vec3 scale) { - vec2 tc = computeLUTCoords(frameIndex + g_vertexLUTIndex * 2.0, u_vertParams.xy, g_vert_center, 1.0); - vec4 enc1 = floor(TEXTURE(u_vertLUT, tc) * 255.0 + 0.5); - tc.x += g_vert_stepX; - vec4 enc2 = floor(TEXTURE(u_vertLUT, tc) * 255.0 + 0.5); - vec3 qpos = vec3(decodeUInt16(enc1.xy), decodeUInt16(enc1.zw), decodeUInt16(enc2.xy)); - return unquantizePosition(qpos, origin, scale).xyz; -}`; - -const computeAnimationDisplacement = ` -vec3 computeAnimationDisplacement(float vertexLUTIndex, float frameIndex0, float frameIndex1, float fraction, vec3 origin, vec3 scale) { -if (frameIndex0 < 0.0) - return vec3(0.0, 0.0, 0.0); -vec3 displacement = computeAnimationFrameDisplacement(vertexLUTIndex, frameIndex0, origin, scale); -if (fraction > 0.0) { - vec3 displacement1 = computeAnimationFrameDisplacement(vertexLUTIndex, frameIndex1, origin, scale); - displacement += fraction * (displacement1 - displacement); - } -return displacement; -}`; -const computeAnimationFrameNormal = ` -vec3 computeAnimationFrameNormal(float frameIndex) { - float vertexIndex = floor(g_vertexLUTIndex/2.0); - vec2 tc = computeLUTCoords(frameIndex + vertexIndex, u_vertParams.xy, g_vert_center, 1.0); - vec4 enc = floor(TEXTURE(u_vertLUT, tc) * 255.0 + 0.5); - vec2 param = (vertexIndex * 2.0 == g_vertexLUTIndex) ? enc.xy : enc.zw; - return octDecodeNormal(param); -}`; -const computeAnimationNormal = ` -vec3 computeAnimationNormal(float frameIndex0, float frameIndex1, float fraction) { -vec3 normal = computeAnimationFrameNormal(frameIndex0); -if (fraction > 0.0) { - vec3 normal1 = computeAnimationFrameNormal(frameIndex1); - normal += fraction * (normal1 - normal); - } -return normal; -}`; -const computeAnimationFrameParam = ` -float computeAnimationFrameParam(float frameIndex, float origin, float scale) { - float vertexIndex = floor(g_vertexLUTIndex/2.0); - vec2 tc = computeLUTCoords(frameIndex + vertexIndex, u_vertParams.xy, g_vert_center, 1.0); - vec4 enc = floor(TEXTURE(u_vertLUT, tc) * 255.0 + 0.5); - vec2 param = (vertexIndex * 2.0 == g_vertexLUTIndex) ? enc.xy : enc.zw; - return clamp((origin + scale * decodeUInt16(param)), 0.0, 1.0); -}`; - -const computeAnimationParam = ` -vec2 computeAnimationParam(float frameIndex0, float frameIndex1, float fraction, float origin, float scale) { -float param = computeAnimationFrameParam(frameIndex0, origin, scale); -if (fraction > 0.0) { - float param1 = computeAnimationFrameParam(frameIndex1, origin, scale); - param += fraction * (param1 - param); - } -return vec2(.5, param); -}`; - const scratchMVPMatrix = new Matrix4(); export function addModelViewProjectionMatrix(vert: VertexShaderBuilder): void { - vert.addUniform("u_mvp", VariableType.Mat4, (prog) => { - prog.addGraphicUniform("u_mvp", (uniform, params) => { - const mvp = params.projectionMatrix.clone(scratchMVPMatrix); - mvp.multiplyBy(params.modelViewMatrix); - uniform.setMatrix4(mvp); + if (vert.usesInstancedGeometry) { + addModelViewMatrix(vert); + addProjectionMatrix(vert); + vert.addGlobal("g_mvp", VariableType.Mat4); + vert.addInitializer("g_mvp = u_proj * g_mv;"); + } else { + vert.addUniform("u_mvp", VariableType.Mat4, (prog) => { + prog.addGraphicUniform("u_mvp", (uniform, params) => { + const mvp = params.projectionMatrix.clone(scratchMVPMatrix); + mvp.multiplyBy(params.modelViewMatrix); + uniform.setMatrix4(mvp); + }); }); - }); + } } export function addProjectionMatrix(vert: VertexShaderBuilder): void { @@ -125,140 +72,82 @@ export function addProjectionMatrix(vert: VertexShaderBuilder): void { } export function addModelViewMatrix(vert: VertexShaderBuilder): void { - vert.addUniform("u_mv", VariableType.Mat4, (prog) => { - prog.addGraphicUniform("u_mv", (uniform, params) => { - uniform.setMatrix4(params.modelViewMatrix); + if (vert.usesInstancedGeometry) { + // ###TODO_INSTANCING: We only need 3 rows, not 4... + vert.addUniform("u_viewMatrix", VariableType.Mat4, (prog) => { + prog.addProgramUniform("u_viewMatrix", (uniform, params) => { + uniform.setMatrix4(params.viewMatrix); + }); }); - }); -} -export function addNormalMatrix(vert: VertexShaderBuilder) { - vert.addUniform("u_nmx", VariableType.Mat3, (prog) => { - prog.addGraphicUniform("u_nmx", (uniform, params) => { - const rotMat: Matrix3 | undefined = params.modelViewMatrix.getRotation(); - if (undefined !== rotMat) - uniform.setMatrix3(rotMat); + vert.addGlobal("g_mv", VariableType.Mat4); + vert.addInitializer("g_mv = u_viewMatrix * g_modelMatrix;"); + } else { + vert.addUniform("u_mv", VariableType.Mat4, (prog) => { + // ###TODO: We only need 3 rows, not 4... + prog.addGraphicUniform("u_mv", (uniform, params) => { + uniform.setMatrix4(params.modelViewMatrix); + }); }); - }); + } } -const scratchAnimDisplacementParams = new Float32Array(3); // index0, index1, fraction. -const scratchAnimScalarParams = new Float32Array(3); // index0, index1, fraction. -const scratchAnimNormalParams = new Float32Array(3); // index0, index1, fraction. -const scratchAnimScalarQParams = new Float32Array(2); // origin, scale. - -function computeAnimParams(params: Float32Array, inputs: number[], indices: number[], fraction: number): void { - const inputValue = fraction * inputs[inputs.length - 1]; - for (let i = 0; i < inputs.length - 1; i++) { - if (inputValue >= inputs[i] && inputValue < inputs[i + 1]) { - params[0] = indices[i]; - params[1] = indices[i + 1]; - params[2] = inputValue - inputs[i] / (inputs[i + 1] - inputs[i]); - return; - } +export function addNormalMatrix(vert: VertexShaderBuilder) { + if (vert.usesInstancedGeometry) { + vert.addGlobal("g_nmx", VariableType.Mat3); + vert.addInitializer("g_nmx = mat3(MAT_MV);"); + } else { + vert.addUniform("u_nmx", VariableType.Mat3, (prog) => { + prog.addGraphicUniform("u_nmx", (uniform, params) => { + const rotMat: Matrix3 | undefined = params.modelViewMatrix.getRotation(); + if (undefined !== rotMat) + uniform.setMatrix3(rotMat); + }); + }); } - params[0] = params[1] = indices[inputs.length - 1]; - params[2] = 0.0; } -export function addAnimation(vert: VertexShaderBuilder, includeTexture: boolean, includeNormal: boolean): void { - scratchAnimDisplacementParams[0] = scratchAnimDisplacementParams[1] = scratchAnimDisplacementParams[2] = -1.0; - vert.addFunction(computeAnimationFrameDisplacement); - vert.addFunction(computeAnimationDisplacement); - if (includeNormal) { - vert.addFunction(octDecodeNormal); - vert.addFunction(computeAnimationFrameNormal); - vert.addFunction(computeAnimationNormal); - } - if (includeTexture) { - vert.addFunction(computeAnimationFrameParam); - vert.addFunction(computeAnimationParam); - } - - vert.addUniform("u_animDispParams", VariableType.Vec3, (prog) => { - prog.addGraphicUniform("u_animDispParams", (uniform, params) => { - scratchAnimDisplacementParams[0] = scratchAnimDisplacementParams[1] = scratchAnimDisplacementParams[2] = 0.0; - const lutGeom: LUTGeometry = params.geometry as LUTGeometry; - const analysisStyle = params.target.analysisStyle; - let channel: any; - if (lutGeom.lut.auxDisplacements && analysisStyle && analysisStyle.displacementChannelName && undefined !== (channel = lutGeom.lut.auxDisplacements.get(analysisStyle.displacementChannelName))) - computeAnimParams(scratchAnimDisplacementParams, channel.inputs, channel.indices, params.target.animationFraction); - uniform.setUniform3fv(scratchAnimDisplacementParams); - }); - }); - vert.addUniform("u_qAnimDispScale", VariableType.Vec3, (prog) => { - prog.addGraphicUniform("u_qAnimDispScale", (uniform, params) => { - const lutGeom: LUTGeometry = params.geometry as LUTGeometry; - const analysisStyle = params.target.analysisStyle; - let channel: any; - scratchAnimDisplacementParams[0] = scratchAnimDisplacementParams[1] = scratchAnimDisplacementParams[2] = 0.0; - if (lutGeom.lut.auxDisplacements && analysisStyle && analysisStyle.displacementChannelName && undefined !== (channel = lutGeom.lut.auxDisplacements.get(analysisStyle.displacementChannelName))) { - const displacementScale = analysisStyle.displacementScale ? analysisStyle.displacementScale : 1.0; - for (let i = 0; i < 3; i++) - scratchAnimDisplacementParams[i] = channel.qScale[i] * displacementScale; // Apply displacement scale. - } - uniform.setUniform3fv(scratchAnimDisplacementParams); +function addInstanceMatrixRow(vert: VertexShaderBuilder, row: number) { + // 3 rows per instance; 4 floats per row; 4 bytes per float. + const floatsPerRow = 4; + const bytesPerVertex = floatsPerRow * 4; + const offset = row * bytesPerVertex; + const stride = 3 * bytesPerVertex; + const name = "a_instanceMatrixRow" + row; + vert.addAttribute(name, VariableType.Vec4, (prog) => { + prog.addAttribute(name, (attr, params) => { + const geom = params.geometry.asInstanced!; + assert(undefined !== geom); + attr.enableArray(geom.transforms, floatsPerRow, GL.DataType.Float, false, stride, offset, true); }); }); - vert.addUniform("u_qAnimDispOrigin", VariableType.Vec3, (prog) => { - prog.addGraphicUniform("u_qAnimDispOrigin", (uniform, params) => { - const lutGeom: LUTGeometry = params.geometry as LUTGeometry; - const analysisStyle = params.target.analysisStyle; - scratchAnimDisplacementParams[0] = scratchAnimDisplacementParams[1] = scratchAnimDisplacementParams[2] = 0.0; - let channel: any; - if (lutGeom.lut.auxDisplacements && analysisStyle && analysisStyle.displacementChannelName && undefined !== (channel = lutGeom.lut.auxDisplacements.get(analysisStyle.displacementChannelName))) { - const displacementScale = analysisStyle.displacementScale ? analysisStyle.displacementScale : 1.0; - for (let i = 0; i < 3; i++) - scratchAnimDisplacementParams[i] = channel.qOrigin[i] * displacementScale; // Apply displacement scale - } - uniform.setUniform3fv(scratchAnimDisplacementParams); +} + +const computeInstancedModelMatrix = ` + mat4 instanceMatrix = mat4( + a_instanceMatrixRow0.x, a_instanceMatrixRow1.x, a_instanceMatrixRow2.x, 0.0, + a_instanceMatrixRow0.y, a_instanceMatrixRow1.y, a_instanceMatrixRow2.y, 0.0, + a_instanceMatrixRow0.z, a_instanceMatrixRow1.z, a_instanceMatrixRow2.z, 0.0, + a_instanceMatrixRow0.w, a_instanceMatrixRow1.w, a_instanceMatrixRow2.w, 1.0); + g_modelMatrix = instanceMatrix * u_rootModelMatrix; +`; + +export function addInstancedModelMatrix(vert: VertexShaderBuilder) { + // ###TODO_INSTANCING: We can make this more efficient, reduce amount of data sent as uniforms and attributes, and reduce computation on the GPU. + // Get it working the straightforward way first. + vert.addUniform("u_rootModelMatrix", VariableType.Mat4, (prog) => { + // ###TODO_INSTANCING: We only need 3 rows, not 4... + prog.addGraphicUniform("u_rootModelMatrix", (uniform, params) => { + uniform.setMatrix4(params.modelMatrix); }); }); - if (includeNormal) { - vert.addUniform("u_animNormalParams", VariableType.Vec3, (prog) => { - prog.addGraphicUniform("u_animNormalParams", (uniform, params) => { - scratchAnimNormalParams[0] = scratchAnimNormalParams[1] = scratchAnimNormalParams[2] = -1.0; - const lutGeom: LUTGeometry = params.geometry as LUTGeometry; - const analysisStyle = params.target.analysisStyle; - let channel: any; - if (lutGeom.lut.auxNormals && analysisStyle && analysisStyle.normalChannelName && undefined !== (channel = lutGeom.lut.auxNormals.get(analysisStyle.normalChannelName))) - computeAnimParams(scratchAnimNormalParams, channel.inputs, channel.indices, params.target.animationFraction); - uniform.setUniform3fv(scratchAnimNormalParams); - }); - }); - } - if (includeTexture) { - vert.addUniform("u_animScalarParams", VariableType.Vec3, (prog) => { - prog.addGraphicUniform("u_animScalarParams", (uniform, params) => { - const meshGeom: MeshGeometry = params.geometry as MeshGeometry; - const analysisStyle = params.target.analysisStyle; - scratchAnimScalarParams[0] = scratchAnimScalarParams[1] = scratchAnimScalarParams[2] = -1.0; - let channel: any; - if (meshGeom.lut.auxParams !== undefined && - analysisStyle !== undefined && - analysisStyle.scalarChannelName !== undefined && - (channel = meshGeom.lut.auxParams.get(analysisStyle.scalarChannelName)) !== undefined) - computeAnimParams(scratchAnimScalarParams, channel.inputs, channel.indices, params.target.animationFraction); - uniform.setUniform3fv(scratchAnimScalarParams); - }); - }); - vert.addUniform("u_animScalarQParams", VariableType.Vec2, (prog) => { - prog.addGraphicUniform("u_animScalarQParams", (uniform, params) => { - const meshGeom: MeshGeometry = params.geometry as MeshGeometry; - const analysisStyle = params.target.analysisStyle; - scratchAnimScalarQParams[0] = scratchAnimScalarQParams[1] = -1.0; - let channel: any; - if (meshGeom.lut.auxParams && analysisStyle && analysisStyle.scalarChannelName && analysisStyle.scalarRange && - (channel = meshGeom.lut.auxParams.get(analysisStyle.scalarChannelName)) !== undefined) { - const rangeScale = analysisStyle.scalarRange.high - analysisStyle.scalarRange.low; - scratchAnimScalarQParams[0] = Gradient.ThematicSettings.margin + (channel.qOrigin - analysisStyle.scalarRange.low) / rangeScale; - scratchAnimScalarQParams[1] = Gradient.ThematicSettings.contentRange * channel.qScale / rangeScale; - } - uniform.setUniform2fv(scratchAnimScalarQParams); - }); - }); - } + addInstanceMatrixRow(vert, 0); + addInstanceMatrixRow(vert, 1); + addInstanceMatrixRow(vert, 2); + + vert.addGlobal("g_modelMatrix", VariableType.Mat4); + vert.addInitializer(computeInstancedModelMatrix); } const scratchLutParams = new Float32Array(4); @@ -266,22 +155,26 @@ function addPositionFromLUT(vert: VertexShaderBuilder) { vert.addGlobal("g_vertexLUTIndex", VariableType.Float); vert.addGlobal("g_vertexBaseCoords", VariableType.Vec2); vert.addGlobal("g_vertexData2", VariableType.Vec2); - vert.addGlobal("g_featureIndexCoords", VariableType.Vec2); vert.addFunction(GLSLDecode.uint32); vert.addFunction(GLSLDecode.uint16); - vert.addFunction(unquantizeVertexPositionFromLUT); + if (vert.usesInstancedGeometry) { + vert.addFunction(unquantizeVertexPositionFromLUTPrelude + unquantizeVertexPositionFromLUTPostlude); + } else { + vert.addGlobal("g_featureIndexCoords", VariableType.Vec2); + vert.addFunction(unquantizeVertexPositionFromLUTPrelude + computeFeatureIndexCoords + unquantizeVertexPositionFromLUTPostlude); + } vert.addUniform("u_vertLUT", VariableType.Sampler2D, (prog) => { prog.addGraphicUniform("u_vertLUT", (uniform, params) => { - (params.geometry as LUTGeometry).lut.texture.bindSampler(uniform, TextureUnit.VertexLUT); + (params.geometry.asLUT!).lut.texture.bindSampler(uniform, TextureUnit.VertexLUT); }); }); vert.addUniform("u_vertParams", VariableType.Vec4, (prog) => { prog.addGraphicUniform("u_vertParams", (uniform, params) => { - const lutGeom: LUTGeometry = params.geometry as LUTGeometry; - const lut = lutGeom.lut; + assert(undefined !== params.geometry.asLUT); + const lut = params.geometry.asLUT!.lut; const lutParams = scratchLutParams; lutParams[0] = lut.texture.width; lutParams[1] = lut.texture.height; diff --git a/core/frontend/src/render/webgl/glsl/Viewport.ts b/core/frontend/src/render/webgl/glsl/Viewport.ts index f466b7f..4336366 100644 --- a/core/frontend/src/render/webgl/glsl/Viewport.ts +++ b/core/frontend/src/render/webgl/glsl/Viewport.ts @@ -68,7 +68,7 @@ export function addViewportTransformation(shader: ShaderBuilder) { const modelToWindowCoordinates = ` vec4 modelToWindowCoordinates(vec4 position, vec4 next) { if (kRenderPass_ViewOverlay == u_renderPass || kRenderPass_Background == u_renderPass) { - vec4 q = u_mvp * position; + vec4 q = MAT_MVP * position; q.xyz /= q.w; q.xyz = (u_viewportTransformation * vec4(q.xyz, 1.0)).xyz; return q; @@ -76,8 +76,8 @@ vec4 modelToWindowCoordinates(vec4 position, vec4 next) { // Negative values are in front of the camera (visible). float s_maxZ = -u_frustum.x; // use -near (front) plane for segment drop test since u_frustum's near & far are pos. - vec4 q = u_mv * position; // eye coordinates. - vec4 n = u_mv * next; + vec4 q = MAT_MV * position; // eye coordinates. + vec4 n = MAT_MV * next; if (q.z > s_maxZ) { if (n.z > s_maxZ) diff --git a/core/frontend/src/render/webgl/glsl/ViewportQuad.ts b/core/frontend/src/render/webgl/glsl/ViewportQuad.ts index 0288866..84edfee 100644 --- a/core/frontend/src/render/webgl/glsl/ViewportQuad.ts +++ b/core/frontend/src/render/webgl/glsl/ViewportQuad.ts @@ -15,7 +15,7 @@ function addTexture(prog: ProgramBuilder) { } export function createViewportQuadBuilder(textured: boolean): ProgramBuilder { - const prog = new ProgramBuilder(false); + const prog = new ProgramBuilder(); prog.vert.set(VertexShaderComponent.ComputePosition, computePosition); if (textured) { addTexture(prog); diff --git a/core/frontend/src/tile.ts b/core/frontend/src/tile.ts index 441d2bd..8743a15 100644 --- a/core/frontend/src/tile.ts +++ b/core/frontend/src/tile.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ export * from "./tile/GltfTileIO"; -export * from "./tile/DgnTileIO"; export * from "./tile/IModelTileIO"; export * from "./tile/TileIO"; export * from "./tile/TileTree"; diff --git a/core/frontend/src/tile/B3dmTileIO.ts b/core/frontend/src/tile/B3dmTileIO.ts index cf61b10..730467c 100644 --- a/core/frontend/src/tile/B3dmTileIO.ts +++ b/core/frontend/src/tile/B3dmTileIO.ts @@ -5,13 +5,12 @@ /** @module Tile */ import { TileIO } from "./TileIO"; import { GltfTileIO } from "./GltfTileIO"; -import { DisplayParams } from "../render/primitives/DisplayParams"; -import { ElementAlignedBox3d, ColorDef, LinePixels, FillFlags, FeatureTable, Feature, TextureMapping, BatchType } from "@bentley/imodeljs-common"; -import { Id64String, JsonUtils } from "@bentley/bentleyjs-core"; +import { ElementAlignedBox3d, FeatureTable, Feature, BatchType } from "@bentley/imodeljs-common"; +import { Id64String, utf8ToString } from "@bentley/bentleyjs-core"; import { RenderSystem } from "../render/System"; -import { ColorMap } from "../render/primitives/ColorMap"; import { Mesh } from "../render/primitives/mesh/MeshPrimitives"; import { IModelConnection } from "../IModelConnection"; +import { Transform } from "@bentley/geometry-core"; /** * Provides facilities for deserializing Batched 3D Model (B3dm) tiles. @@ -24,6 +23,7 @@ export namespace B3dmTileIO { public readonly featureTableBinaryLength: number; public readonly batchTableJsonLength: number; public readonly batchTableBinaryLength: number; + public readonly featureTableJson: any; public get isValid(): boolean { return TileIO.Format.B3dm === this.format; } public constructor(stream: TileIO.StreamBuffer) { @@ -33,7 +33,35 @@ export namespace B3dmTileIO { this.featureTableBinaryLength = stream.nextUint32; this.batchTableJsonLength = stream.nextUint32; this.batchTableBinaryLength = stream.nextUint32; - stream.advance(this.featureTableJsonLength); + + // Keep this legacy check in for now since a lot of tilesets are still using the old header. + // Legacy header #1: [batchLength] [batchTableByteLength] + // Legacy header #2: [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength] + // Current header: [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] + // If the header is in the first legacy format 'batchTableJsonByteLength' will be the start of the JSON string (a quotation mark) or the glTF magic. + // Accordingly its first byte will be either 0x22 or 0x67, and so the minimum uint32 expected is 0x22000000 = 570425344 = 570MB. It is unlikely that the feature table Json will exceed this length. + // The check for the second legacy format is similar, except it checks 'batchTableBinaryByteLength' instead + if (this.batchTableJsonLength >= 570425344) { + // First legacy check + stream.curPos = 20; + // batchLength = this.featureTableJsonLength; + this.batchTableJsonLength = this.featureTableBinaryLength; + this.batchTableBinaryLength = 0; + this.featureTableJsonLength = 0; + this.featureTableBinaryLength = 0; + } else if (this.batchTableBinaryLength >= 570425344) { + // Second legacy check + stream.curPos = 24; + this.batchTableJsonLength = this.featureTableJsonLength; + this.batchTableBinaryLength = this.featureTableBinaryLength; + this.featureTableJsonLength = 0; + this.featureTableBinaryLength = 0; + } + if (0 !== this.featureTableJsonLength) { + const sceneStrData = stream.nextBytes(this.featureTableJsonLength); + const sceneStr = utf8ToString(sceneStrData); + if (sceneStr) this.featureTableJson = JSON.parse(sceneStr); + } stream.advance(this.featureTableBinaryLength); stream.advance(this.batchTableJsonLength); stream.advance(this.batchTableBinaryLength); @@ -48,15 +76,20 @@ export namespace B3dmTileIO { * @hidden */ export class Reader extends GltfTileIO.Reader { - public static create(stream: TileIO.StreamBuffer, iModel: IModelConnection, modelId: Id64String, is3d: boolean, range: ElementAlignedBox3d, system: RenderSystem, yAxisUp: boolean, isLeaf: boolean, isCanceled?: GltfTileIO.IsCanceled): Reader | undefined { + public static create(stream: TileIO.StreamBuffer, iModel: IModelConnection, modelId: Id64String, is3d: boolean, range: ElementAlignedBox3d, system: RenderSystem, yAxisUp: boolean, isLeaf: boolean, transformToRoot?: Transform, isCanceled?: GltfTileIO.IsCanceled): Reader | undefined { const header = new Header(stream); if (!header.isValid) return undefined; + if (header.featureTableJson && Array.isArray(header.featureTableJson.RTC_CENTER)) { + const returnToCenterTransform = Transform.createTranslationXYZ(header.featureTableJson.RTC_CENTER[0], header.featureTableJson.RTC_CENTER[1], header.featureTableJson.RTC_CENTER[2]); + transformToRoot = transformToRoot ? transformToRoot.multiplyTransformTransform(returnToCenterTransform) : returnToCenterTransform; + } + const props = GltfTileIO.ReaderProps.create(stream, yAxisUp); - return undefined !== props ? new Reader(props, iModel, modelId, is3d, system, range, isLeaf, isCanceled) : undefined; + return undefined !== props ? new Reader(props, iModel, modelId, is3d, system, range, isLeaf, transformToRoot, isCanceled) : undefined; } - private constructor(props: GltfTileIO.ReaderProps, iModel: IModelConnection, modelId: Id64String, is3d: boolean, system: RenderSystem, private _range: ElementAlignedBox3d, private _isLeaf: boolean, isCanceled?: GltfTileIO.IsCanceled) { + private constructor(props: GltfTileIO.ReaderProps, iModel: IModelConnection, modelId: Id64String, is3d: boolean, system: RenderSystem, private _range: ElementAlignedBox3d, private _isLeaf: boolean, private _transformToRoot?: Transform, isCanceled?: GltfTileIO.IsCanceled) { super(props, iModel, modelId, is3d, system, BatchType.Primary, isCanceled); } public async read(): Promise { @@ -71,7 +104,7 @@ export namespace B3dmTileIO { if (this._isCanceled) return Promise.resolve({ readStatus: TileIO.ReadStatus.Canceled, isLeaf: this._isLeaf }); - return Promise.resolve(this.readGltfAndCreateGraphics(this._isLeaf, false, true, featureTable, this._range)); + return Promise.resolve(this.readGltfAndCreateGraphics(this._isLeaf, featureTable, this._range, this._transformToRoot)); } protected readFeatures(features: Mesh.Features, _json: any): boolean { const feature = new Feature(this._modelId); @@ -79,25 +112,5 @@ export namespace B3dmTileIO { features.add(feature, 1); return true; } - protected readColorTable(colorTable: ColorMap, _json: any): boolean | undefined { - colorTable.insert(0x777777); - return true; - } - protected createDisplayParams(materialJson: any, hasBakedLighting: boolean): DisplayParams | undefined { - let textureMapping: TextureMapping | undefined; - if (undefined !== materialJson && - undefined !== materialJson.values.tex) { - textureMapping = this.findTextureMapping(materialJson.values.tex); - } - const grey: ColorDef = new ColorDef(0x77777777); - return new DisplayParams(DisplayParams.Type.Mesh, grey, grey, 1, LinePixels.Solid, FillFlags.Always, undefined, undefined, hasBakedLighting, textureMapping); - } - protected extractReturnToCenter(extensions: any): number[] | undefined { - if (extensions === undefined) { return undefined; } - const cesiumRtc = JsonUtils.asObject(extensions.CESIUM_RTC); - return (cesiumRtc === undefined) ? undefined : JsonUtils.asArray(cesiumRtc.center); - } - - protected get _hasBakedLighting(): boolean { return true; } // ###TODO? currently always desired (3mx, 3sm) - may change in future. } } diff --git a/core/frontend/src/tile/CompositeTileIO.ts b/core/frontend/src/tile/CompositeTileIO.ts new file mode 100644 index 0000000..371c815 --- /dev/null +++ b/core/frontend/src/tile/CompositeTileIO.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module Tile */ +import { TileIO } from "./TileIO"; + +/** + * Provides facilities for deserializing Composite (cmpt) tiles. + * @hidden + */ +export namespace CompositeTileIO { + export class Header extends TileIO.Header { + public readonly length: number; + public readonly tileCount: number; + public readonly tilePosition: number; + + public get isValid(): boolean { return TileIO.Format.Cmpt === this.format; } + + public constructor(stream: TileIO.StreamBuffer) { + super(stream); + this.length = stream.nextUint32; + this.tileCount = stream.nextUint32; + this.tilePosition = stream.curPos; + + if (stream.isPastTheEnd) + this.invalidate(); + } + } +} diff --git a/core/frontend/src/tile/DgnTileIO.ts b/core/frontend/src/tile/DgnTileIO.ts deleted file mode 100644 index a3426f3..0000000 --- a/core/frontend/src/tile/DgnTileIO.ts +++ /dev/null @@ -1,334 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ -/** @module Tile */ - -import { TileIO } from "./TileIO"; -import { GltfTileIO } from "./GltfTileIO"; -import { DisplayParams } from "../render/primitives/DisplayParams"; -import { ColorMap } from "../render/primitives/ColorMap"; -import { Id64String, JsonUtils, assert } from "@bentley/bentleyjs-core"; -import { RenderSystem } from "../render/System"; -import { imageElementFromImageSource } from "../ImageUtil"; -import { - Feature, - FeatureTable, - ElementAlignedBox3d, - GeometryClass, - FillFlags, - ColorDef, - LinePixels, - TextureMapping, - ImageSource, - ImageSourceFormat, - RenderTexture, - RenderMaterial, - Gradient, - BatchType, -} from "@bentley/imodeljs-common"; -import { IModelConnection } from "../IModelConnection"; - -/** - * Provides facilities for deserializing tiles in 'dgn' format. Such tiles contain element geometry. - * @hidden - */ -export namespace DgnTileIO { - export const enum Flags { - None = 0, - ContainsCurves = 1 << 0, - Incomplete = 1 << 1, - IsLeaf = 1 << 2, - HasSizeMultiplier = 1 << 3, - } - - /** @hidden */ - export class Header extends TileIO.Header { - public readonly flags: Flags; - public readonly contentRange: ElementAlignedBox3d; - public readonly sizeMultiplier: number; - public readonly length: number; - - public get isValid(): boolean { return TileIO.Format.Dgn === this.format; } - - public constructor(stream: TileIO.StreamBuffer) { - super(stream); - this.flags = stream.nextUint32; - this.contentRange = ElementAlignedBox3d.createFromPoints(stream.nextPoint3d64, stream.nextPoint3d64); - this.sizeMultiplier = stream.nextFloat64; - this.length = stream.nextUint32; - - if (stream.isPastTheEnd) - this.invalidate(); - } - } - - /** @hidden */ - class FeatureTableHeader { - public static readFrom(stream: TileIO.StreamBuffer) { - const length = stream.nextUint32; - const maxFeatures = stream.nextUint32; - const count = stream.nextUint32; - return stream.isPastTheEnd ? undefined : new FeatureTableHeader(length, maxFeatures, count); - } - - private constructor(public readonly length: number, - public readonly maxFeatures: number, - public readonly count: number) { } - } - - /** - * Deserializes a dgn tile. - * @hidden - */ - export class Reader extends GltfTileIO.Reader { - public static create(stream: TileIO.StreamBuffer, iModel: IModelConnection, modelId: Id64String, is3d: boolean, system: RenderSystem, type: BatchType = BatchType.Primary, isCanceled?: GltfTileIO.IsCanceled): Reader | undefined { - const header = new Header(stream); - if (!header.isValid) - return undefined; - - // The feature table follows the dgnT header - if (!this.skipFeatureTable(stream)) - return undefined; - - // A glTF header follows the feature table - const props = GltfTileIO.ReaderProps.create(stream); - return undefined !== props ? new Reader(props, iModel, modelId, is3d, system, type, isCanceled) : undefined; - } - - protected extractReturnToCenter(_extensions: any): number[] | undefined { return undefined; } // Original IModel Tile creator set RTC unnecessarily and incorrectly. - private static skipFeatureTable(stream: TileIO.StreamBuffer): boolean { - const startPos = stream.curPos; - const header = FeatureTableHeader.readFrom(stream); - if (undefined !== header) - stream.curPos = startPos + header.length; - - return undefined !== header; - } - - public async read(): Promise { - // ###TODO don't re-read the headers... - this._buffer.reset(); - const header = new Header(this._buffer); - let isLeaf = true; - if (!header.isValid) - return { readStatus: TileIO.ReadStatus.InvalidHeader, isLeaf }; - - isLeaf = Flags.None !== (header.flags & Flags.IsLeaf); - const hasSizeMultiplier = Flags.None !== (header.flags & Flags.HasSizeMultiplier); - const sizeMultiplier = hasSizeMultiplier ? header.sizeMultiplier : undefined; - const featureTable = this.readFeatureTable(); - if (undefined === featureTable) - return { readStatus: TileIO.ReadStatus.InvalidFeatureTable, isLeaf, sizeMultiplier }; - - const isComplete = Flags.None === (header.flags & Flags.Incomplete); - const isCurved = Flags.None !== (header.flags & Flags.ContainsCurves); - - // Textures must be loaded asynchronously first... - await this.loadNamedTextures(); - if (this._isCanceled) - return Promise.resolve({ readStatus: TileIO.ReadStatus.Canceled, isLeaf, sizeMultiplier }); - else - return Promise.resolve(this.readGltfAndCreateGraphics(isLeaf, isCurved, isComplete, featureTable, header.contentRange, sizeMultiplier)); - } - - private constructor(props: GltfTileIO.ReaderProps, iModel: IModelConnection, modelId: Id64String, is3d: boolean, system: RenderSystem, type: BatchType = BatchType.Primary, isCanceled?: GltfTileIO.IsCanceled) { - super(props, iModel, modelId, is3d, system, type, isCanceled); - } - - protected readFeatureTable(): FeatureTable | undefined { - const startPos = this._buffer.curPos; - const header = FeatureTableHeader.readFrom(this._buffer); - if (undefined === header) - return undefined; - - const featureTable = new FeatureTable(header.maxFeatures, this._modelId, this._type); - for (let i = 0; i < header.count; i++) { - const elementId = this._buffer.nextId64; - const subCategoryId = this._buffer.nextId64; - const geometryClass = this._buffer.nextUint32 as GeometryClass; - const index = this._buffer.nextUint32; - - if (this._buffer.isPastTheEnd) - return undefined; - - featureTable.insertWithIndex(new Feature(elementId, subCategoryId, geometryClass), index); - } - - this._buffer.curPos = startPos + header.length; - return featureTable; - } - - protected readFeatureIndices(json: any): number[] | undefined { - const featureId = json.featureID; - if (undefined !== featureId) - return [featureId as number]; - else - return this.readIndices(json, "featureIDs"); - } - - protected readColorTable(colorTable: ColorMap, meshJson: any): boolean { - const json = JsonUtils.asArray(meshJson.colorTable); - if (undefined !== json) { - for (const color of json) - colorTable.insert(color as number); - } - - return 0 < colorTable.length; - } - - protected createDisplayParams(json: any): DisplayParams | undefined { - const type = JsonUtils.asInt(json.type, DisplayParams.Type.Mesh); - const lineColor = new ColorDef(JsonUtils.asInt(json.lineColor)); - const fillColor = new ColorDef(JsonUtils.asInt(json.fillColor)); - const width = JsonUtils.asInt(json.lineWidth); - const linePixels = JsonUtils.asInt(json.linePixels, LinePixels.Solid); - const fillFlags = JsonUtils.asInt(json.fillFlags, FillFlags.None); - const ignoreLighting = JsonUtils.asBool(json.ignoreLighting); - - // Material will always contain its own texture if it has one - const materialKey = json.materialId; - const material = undefined !== materialKey ? this.materialFromJson(materialKey) : undefined; - - // We will only attempt to include the texture if material is undefined - let textureMapping; - if (!material) { - const textureJson = json.texture; - textureMapping = undefined !== textureJson ? this.textureMappingFromJson(textureJson) : undefined; - - if (undefined === textureMapping) { - // Look for a gradient. If defined, create a texture mapping. No reason to pass the Gradient.Symb to the DisplayParams once we have the texture. - const gradientProps = json.gradient as Gradient.SymbProps; - const gradient = undefined !== gradientProps ? Gradient.Symb.fromJSON(gradientProps) : undefined; - if (undefined !== gradient) { - const texture = this._system.getGradientTexture(gradient, this._iModel); - if (undefined !== texture) { - // ###TODO: would be better if DisplayParams created the TextureMapping - but that requires an IModelConnection and a RenderSystem... - textureMapping = new TextureMapping(texture, new TextureMapping.Params({ textureMat2x3: new TextureMapping.Trans2x3(0, 1, 0, 1, 0, 0) })); - } - } - } - } - - return new DisplayParams(type, lineColor, fillColor, width, linePixels, fillFlags, material, undefined, ignoreLighting, textureMapping); - } - - protected colorDefFromMaterialJson(json: any): ColorDef | undefined { - return undefined !== json ? ColorDef.from(json[0] * 255 + 0.5, json[1] * 255 + 0.5, json[2] * 255 + 0.5) : undefined; - } - - protected materialFromJson(key: string): RenderMaterial | undefined { - if (this._renderMaterials === undefined || this._renderMaterials[key] === undefined) - return undefined; - - let material = this._system.findMaterial(key, this._iModel); - if (!material) { - const materialJson = this._renderMaterials[key]; - - const materialParams = new RenderMaterial.Params(key); - materialParams.diffuseColor = this.colorDefFromMaterialJson(materialJson.diffuseColor); - if (materialJson.diffuse !== undefined) - materialParams.diffuse = JsonUtils.asDouble(materialJson.diffuse); - materialParams.specularColor = this.colorDefFromMaterialJson(materialJson.specularColor); - if (materialJson.specular !== undefined) - materialParams.specular = JsonUtils.asDouble(materialJson.specular); - materialParams.reflectColor = this.colorDefFromMaterialJson(materialJson.reflectColor); - if (materialJson.reflect !== undefined) - materialParams.reflect = JsonUtils.asDouble(materialJson.reflect); - - if (materialJson.specularExponent !== undefined) - materialParams.specularExponent = materialJson.specularExponent; - if (materialJson.transparency !== undefined) - materialParams.transparency = materialJson.transparency; - materialParams.refract = JsonUtils.asDouble(materialJson.refract); - materialParams.shadows = JsonUtils.asBool(materialJson.shadows); - materialParams.ambient = JsonUtils.asDouble(materialJson.ambient); - - if (undefined !== materialJson.textureMapping) - materialParams.textureMapping = this.textureMappingFromJson(materialJson.textureMapping.texture); - - material = this._system.createMaterial(materialParams, this._iModel); - } - - return material; - } - - private textureMappingFromJson(json: any): TextureMapping | undefined { - if (undefined === json) - return undefined; - - const name = JsonUtils.asString(json.name); - const namedTex = 0 !== name.length ? this._namedTextures[name] : undefined; - const texture = undefined !== namedTex ? namedTex.renderTexture as RenderTexture : undefined; - if (undefined === texture) - return undefined; - - const paramsJson = json.params; - const tf = paramsJson.transform; - const paramProps: TextureMapping.ParamProps = { - textureMat2x3: new TextureMapping.Trans2x3(tf[0][0], tf[0][1], tf[0][2], tf[1][0], tf[1][1], tf[1][2]), - textureWeight: JsonUtils.asDouble(paramsJson.weight, 1.0), - mapMode: JsonUtils.asInt(paramsJson.mode), - worldMapping: JsonUtils.asBool(paramsJson.worldMapping), - }; - - return new TextureMapping(texture, new TextureMapping.Params(paramProps)); - } - - private async loadNamedTextures(): Promise { - if (undefined === this._namedTextures) - return Promise.resolve(); - - const promises = new Array>(); - for (const name of Object.keys(this._namedTextures)) - promises.push(this.loadNamedTexture(name)); - - return promises.length > 0 ? Promise.all(promises).then((_) => undefined) : Promise.resolve(); - } - - private async loadNamedTexture(name: string): Promise { - if (this._isCanceled) - return Promise.resolve(); - - const namedTex = this._namedTextures[name]; - assert(undefined !== namedTex); // we got here by iterating the keys of this.namedTextures... - if (undefined === namedTex) - return Promise.resolve(); - - const texture = this._system.findTexture(name, this._iModel); - if (undefined !== texture) - return Promise.resolve(); - - return this.readNamedTexture(namedTex).then((result) => { namedTex.renderTexture = result; }); - } - private async readNamedTexture(namedTex: any): Promise { - const bufferViewId = JsonUtils.asString(namedTex.bufferView); - const bufferViewJson = 0 !== bufferViewId.length ? this._bufferViews[bufferViewId] : undefined; - if (undefined === bufferViewJson) - return Promise.resolve(undefined); - - const byteOffset = JsonUtils.asInt(bufferViewJson.byteOffset); - const byteLength = JsonUtils.asInt(bufferViewJson.byteLength); - if (0 === byteLength) - return Promise.resolve(undefined); - - const bytes = this._binaryData.subarray(byteOffset, byteOffset + byteLength); - const format = namedTex.format; - const imageSource = new ImageSource(bytes, format); - - return imageElementFromImageSource(imageSource).then((image) => { - if (this._isCanceled) - return undefined; - - let textureType = RenderTexture.Type.Normal; - if (JsonUtils.asBool(namedTex.isGlyph)) - textureType = RenderTexture.Type.Glyph; - else if (JsonUtils.asBool(namedTex.isTileSection)) - textureType = RenderTexture.Type.TileSection; - - const params = new RenderTexture.Params(name, textureType); - return this._system.createTextureFromImage(image, ImageSourceFormat.Png === format, this._iModel, params); - }); - } - } -} diff --git a/core/frontend/src/tile/DracoDecoder.ts b/core/frontend/src/tile/DracoDecoder.ts new file mode 100644 index 0000000..8c1ed0d --- /dev/null +++ b/core/frontend/src/tile/DracoDecoder.ts @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module Tile */ +import { createDecoderModule } from "draco3d"; +import { Mesh } from "../render/primitives/mesh/MeshPrimitives"; +import { QPoint3dList/*, QParams3d, QParams2d, QPoint2dList */ } from "@bentley/imodeljs-common"; +import { Point2d, Point3d } from "@bentley/geometry-core"; +import { Triangle } from "../render/primitives/Primitives"; + +export class DracoDecoder { + private static _dracoDecoderModule: any; /** @hidden */ + + public static readDracoMesh(mesh: Mesh, _primitive: any, bufferData: Uint8Array): Mesh | undefined { + if (!DracoDecoder._dracoDecoderModule) + DracoDecoder._dracoDecoderModule = createDecoderModule(undefined); + + const dracoModule = DracoDecoder._dracoDecoderModule; + const dracoDecoder = new dracoModule.Decoder(); + + const buffer = new dracoModule.DecoderBuffer(); + buffer.Init(bufferData, bufferData.length); + + const geometryType = dracoDecoder.GetEncodedGeometryType(buffer); + if (geometryType !== dracoModule.TRIANGULAR_MESH) + return undefined; + + const dracoGeometry = new dracoModule.Mesh(); + const decodingStatus = dracoDecoder.DecodeBufferToMesh(buffer, dracoGeometry); + dracoModule.destroy(buffer); + if (!decodingStatus.ok() || dracoGeometry.ptr === 0) + return undefined; + + if (!DracoDecoder.decodeTriangles(mesh, dracoGeometry, dracoDecoder) || + !DracoDecoder.decodeVertices(mesh.points, dracoGeometry, dracoDecoder)) + return undefined; + DracoDecoder.decodeUVParams(mesh.uvParams, dracoGeometry, dracoDecoder); + dracoModule.destroy(dracoGeometry); + dracoModule.destroy(dracoDecoder); + + return mesh; + } + private static decodeVertices(qPoints: QPoint3dList, dracoGeometry: any, dracoDecoder: any): boolean { + const dracoAttribute = dracoDecoder.GetAttributeByUniqueId(dracoGeometry, dracoDecoder.GetAttributeId(dracoGeometry, DracoDecoder._dracoDecoderModule.POSITION)); + if (undefined === dracoAttribute) return false; + + const numPoints = dracoGeometry.num_points(); + + /* Quantization WIP... + const transform = new DracoDecoder._dracoDecoderModule.AttributeQuantizationTransform(); + const vertexArrayLength = 3 * numPoints; + + const isQuantized = transform.InitFromAttribute(dracoAttribute); + if (isQuantized && 4 === dracoAttribute.data_type()) { // If the data is already unsigned 16 bit (what our shaders support) then use it directly -- else get floats and we'll have to requantize. + dracoDecoder.SkipAttributeTransform(DracoDecoder._dracoDecoderModule.POSITION); + const quantizedValues = new DracoDecoder._dracoDecoderModule.DracoUInt16Array(); + dracoDecoder.GetAttributeUInt16ForAllPoints(dracoGeometry, dracoAttribute, quantizedValues); + const typedArray = new Uint16Array(vertexArrayLength); + const transformRange = transform.range(); + const transformMin = new Point3d(transform.min_value(0), transform.min_value(1), transform.min_value(2)); + for (let i = 0; i < vertexArrayLength; i++) + typedArray[i] = quantizedValues.GetValue(i); + + const range = new Range3d(transformMin.x, transformMin.y, transformMin.z, transformMin.x + transformRange, transformMin.y + transformRange, transformMin.z + transformRange); + QPoint3dList.fromTypedArray(typedArray, range, qPoints); + } else { */ + const unquantizedValues = new DracoDecoder._dracoDecoderModule.DracoFloat32Array(); + dracoDecoder.GetAttributeFloatForAllPoints(dracoGeometry, dracoAttribute, unquantizedValues); + const points = []; + for (let i = 0, j = 0; i < numPoints; i++) + points.push(new Point3d(unquantizedValues.GetValue(j++), unquantizedValues.GetValue(j++), unquantizedValues.GetValue(j++))); + + QPoint3dList.fromPoints(points, qPoints); + // } + return true; + } + private static decodeUVParams(points: Point2d[], dracoGeometry: any, dracoDecoder: any): boolean { + const dracoAttribute = dracoDecoder.GetAttributeByUniqueId(dracoGeometry, dracoDecoder.GetAttributeId(dracoGeometry, DracoDecoder._dracoDecoderModule.TEX_COORD)); + if (undefined === dracoAttribute) return false; + + const numPoints = dracoGeometry.num_points(); + + /* Quantization WIP. + const vertexArrayLength = 2 * numPoints; + const transform = new DracoDecoder._dracoDecoderModule.AttributeQuantizationTransform(); + const isQuantized = transform.InitFromAttribute(dracoAttribute); + if (isQuantized && 4 === dracoAttribute.data_type()) { // If the data is already unsigned 16 bit (what our shaders support) then use it directly -- else get floats and we'll have to requantize. + dracoDecoder.SkipAttributeTransform(DracoDecoder._dracoDecoderModule.TEX_COORD); + const quantizedValues = new DracoDecoder._dracoDecoderModule.DracoUInt16Array(); + dracoDecoder.GetAttributeUInt16ForAllPoints(dracoGeometry, dracoAttribute, quantizedValues); + const typedArray = new Uint16Array(vertexArrayLength); + const transformRange = transform.range(); + const transformMin = new Point2d(transform.min_value(0), transform.min_value(1)); + for (let i = 0; i < vertexArrayLength; i++) + typedArray[i] = quantizedValues.GetValue(i); + + const range = new Range2d(transformMin.x, transformMin.y, transformMin.x + transformRange, transformMin.y + transformRange); + return QPoint2dList.fromTypedArray(typedArray, range); + } else { */ + const unquantizedValues = new DracoDecoder._dracoDecoderModule.DracoFloat32Array(); + dracoDecoder.GetAttributeFloatForAllPoints(dracoGeometry, dracoAttribute, unquantizedValues); + for (let i = 0, j = 0; i < numPoints; i++) + points.push(new Point2d(unquantizedValues.GetValue(j++), unquantizedValues.GetValue(j++))); + // } + return true; + } + private static decodeTriangles(mesh: Mesh, dracoGeometry: any, dracoDecoder: any) { + const numFaces = dracoGeometry.num_faces(); + const faceIndices = new DracoDecoder._dracoDecoderModule.DracoInt32Array(); + const numIndices = numFaces * 3; + const indexArray = new Uint32Array(); + const triangle = new Triangle(); + + for (let i = 0; i < numFaces; ++i) { + dracoDecoder.GetFaceFromMesh(dracoGeometry, i, faceIndices); + triangle.setIndices(faceIndices.GetValue(0), faceIndices.GetValue(1), faceIndices.GetValue(2)); + mesh.addTriangle(triangle); + } + + DracoDecoder._dracoDecoderModule.destroy(faceIndices); + + return { + typedArray: indexArray, + numberOfIndices: numIndices, + }; + } +} diff --git a/core/frontend/src/tile/GltfTileIO.ts b/core/frontend/src/tile/GltfTileIO.ts index 1562972..4ef92cc 100644 --- a/core/frontend/src/tile/GltfTileIO.ts +++ b/core/frontend/src/tile/GltfTileIO.ts @@ -8,7 +8,6 @@ import { TileIO } from "./TileIO"; import { DisplayParams } from "../render/primitives/DisplayParams"; import { Triangle } from "../render/primitives/Primitives"; import { Mesh, MeshList, MeshGraphicArgs } from "../render/primitives/mesh/MeshPrimitives"; -import { ColorMap } from "../render/primitives/ColorMap"; import { FeatureTable, QPoint3d, @@ -17,21 +16,27 @@ import { OctEncodedNormal, MeshPolyline, MeshPolylineList, - MeshEdges, - MeshEdge, - OctEncodedNormalPair, ElementAlignedBox3d, TextureMapping, ImageSource, ImageSourceFormat, RenderTexture, BatchType, + ColorDef, + LinePixels, + FillFlags, } from "@bentley/imodeljs-common"; import { Id64String, assert, JsonUtils, utf8ToString } from "@bentley/bentleyjs-core"; import { Range3d, Point2d, Point3d, Vector3d, Transform, Matrix3d, Angle } from "@bentley/geometry-core"; -import { RenderSystem, RenderGraphic, GraphicBranch, PackedFeatureTable } from "../render/System"; +import { InstancedGraphicParams, RenderSystem, RenderGraphic, GraphicBranch, PackedFeatureTable } from "../render/System"; import { imageElementFromImageSource, getImageSourceFormatForMimeType } from "../ImageUtil"; import { IModelConnection } from "../IModelConnection"; +/* ----------------------------------- + * To restore the use of web workers to decode jpeg, locate and uncomment the three sections by searching for "webworker". + import { WorkerOperation, WebWorkerManager } from "../WebWorkerManager"; + ------------------------------------ */ + +// Defer Draco for now. import { DracoDecoder } from "./DracoDecoder"; /** Provides facilities for deserializing tiles in the [glTF tile format](https://www.khronos.org/gltf/). */ export namespace GltfTileIO { @@ -40,7 +45,11 @@ export namespace GltfTileIO { Version1 = 1, Version2 = 2, CurrentVersion = Version1, - SceneFormat = 0, + Gltf1SceneFormat = 0, + } + export const enum V2ChunkTypes { + JSON = 0x4E4F534a, + Binary = 0x004E4942, } /** The result of [[GltfTileIO.Reader.read]]. */ export interface ReaderResult { @@ -54,23 +63,49 @@ export namespace GltfTileIO { /** Header preceding glTF tile data. */ export class Header extends TileIO.Header { public readonly gltfLength: number; - public readonly sceneStrLength: number; - public readonly gltfSceneFormat: number; + public readonly scenePosition: number = 0; + public readonly sceneStrLength: number = 0; + public readonly binaryPosition: number = 0; public get isValid(): boolean { return TileIO.Format.Gltf === this.format; } public constructor(stream: TileIO.StreamBuffer) { super(stream); this.gltfLength = stream.nextUint32; this.sceneStrLength = stream.nextUint32; - this.gltfSceneFormat = stream.nextUint32; - - if ((Versions.Version1 !== this.version && Versions.Version2 !== this.version) || Versions.SceneFormat !== this.gltfSceneFormat) + const value5 = stream.nextUint32; + + // Early versions of the reality data tile publisher incorrectly put version 2 into header - handle these old tiles + // validating the chunk type. + if (this.version === Versions.Version2 && value5 === Versions.Gltf1SceneFormat) + this.version = Versions.Version1; + + if (this.version === Versions.Version1) { + const gltfSceneFormat = value5; + if (Versions.Gltf1SceneFormat !== gltfSceneFormat) { + this.invalidate(); + return; + } + this.scenePosition = stream.curPos; + this.binaryPosition = stream.curPos + this.sceneStrLength; + } else if (this.version === Versions.Version2) { + const sceneChunkType = value5; + this.scenePosition = stream.curPos; + stream.curPos = stream.curPos + this.sceneStrLength; + const binaryLength = stream.nextUint32; + const binaryChunkType = stream.nextUint32; + if (V2ChunkTypes.JSON !== sceneChunkType || V2ChunkTypes.Binary !== binaryChunkType || 0 === binaryLength) { + this.invalidate(); + return; + } + this.binaryPosition = stream.curPos; + } else { this.invalidate(); + } } } /** @hidden */ - export const enum PrimitiveType { + export const enum MeshMode { Lines = 1, LineStrip = 3, Triangles = 4, @@ -214,6 +249,7 @@ export namespace GltfTileIO { public readonly materials: any, public readonly extensions: any, public readonly samplers: any, + public readonly techniques: any, public readonly yAxisUp: boolean) { } /** Attempt to construct a new ReaderProps from the binary data beginning at the supplied stream's current read position. */ @@ -222,8 +258,8 @@ export namespace GltfTileIO { if (!header.isValid) return undefined; - const binaryData = new Uint8Array(buffer.arrayBuffer, buffer.curPos + header.sceneStrLength); - + const binaryData = new Uint8Array(buffer.arrayBuffer, header.binaryPosition); + buffer.curPos = header.scenePosition; const sceneStrData = buffer.nextBytes(header.sceneStrLength); const sceneStr = utf8ToString(sceneStrData); if (undefined === sceneStr) @@ -238,11 +274,12 @@ export namespace GltfTileIO { const bufferViews = JsonUtils.asObject(sceneValue.bufferViews); const extensions = JsonUtils.asObject(sceneValue.extensions); const samplers = JsonUtils.asObject(sceneValue.samplers); + const techniques = JsonUtils.asObject(sceneValue.techniques); if (undefined === meshes) return undefined; - return new ReaderProps(buffer, binaryData, accessors, bufferViews, sceneValue, nodes, meshes, materialValues, extensions, samplers, yAxisUp); + return new ReaderProps(buffer, binaryData, accessors, bufferViews, sceneValue, nodes, meshes, materialValues, extensions, samplers, techniques, yAxisUp); } catch (e) { return undefined; } @@ -252,11 +289,26 @@ export namespace GltfTileIO { /** A function that returns true if Reader.read() should abort because the tile data is no longer needed. */ export type IsCanceled = (reader: Reader) => boolean; + /* ----------------------------------- + This is part of the webworker option. + + // input is Uint8Array, the result is an ImageBitMap. + class ImageDecodeWorkerOperation extends WorkerOperation { + constructor(imageBytes: ArrayBuffer, imageMimeType: string) { + super("imageBytesToImageBitmap", [imageBytes, imageMimeType], [imageBytes]); + } + } + + declare var BUILD_SEMVER: string; + -------------------------------------- */ + /** Deserializes [(glTF tile data](https://www.khronos.org/gltf/). */ export abstract class Reader { /** @hidden */ protected readonly _buffer: TileIO.StreamBuffer; /** @hidden */ + protected readonly _scene: any; + /** @hidden */ protected readonly _accessors: any; /** @hidden */ protected readonly _bufferViews: any; @@ -278,6 +330,8 @@ export namespace GltfTileIO { protected readonly _images: any; /** @hidden */ protected readonly _samplers: any; + /** @hidden */ + protected readonly _techniques: any; /** @hidden */ protected readonly _binaryData: Uint8Array; /** @hidden */ @@ -294,8 +348,20 @@ export namespace GltfTileIO { protected readonly _yAxisUp: boolean; /** @hidden */ protected readonly _type: BatchType; + private readonly _canceled?: IsCanceled; + /* ----------------------------------- + private static _webWorkerManager: WebWorkerManager; + + private static get webWorkerManager() { + if (!Reader._webWorkerManager) { + Reader._webWorkerManager = new WebWorkerManager("v" + BUILD_SEMVER + "/frontend-webworker.js", 4); + } + return Reader._webWorkerManager; + } + ------------------------------------- */ + /** Asynchronously deserialize the tile data and return the result. */ public async abstract read(): Promise; @@ -305,37 +371,42 @@ export namespace GltfTileIO { protected get _isClassifier(): boolean { return BatchType.Classifier === this._type; } /** @hidden */ - protected readGltfAndCreateGraphics(isLeaf: boolean, isCurved: boolean, isComplete: boolean, featureTable: FeatureTable, contentRange: ElementAlignedBox3d, sizeMultiplier?: number): GltfTileIO.ReaderResult { + protected readGltfAndCreateGraphics(isLeaf: boolean, featureTable: FeatureTable, contentRange: ElementAlignedBox3d, transformToRoot?: Transform, sizeMultiplier?: number, instances?: InstancedGraphicParams): GltfTileIO.ReaderResult { if (this._isCanceled) return { readStatus: TileIO.ReadStatus.Canceled, isLeaf, sizeMultiplier }; - const geometry = new TileIO.GeometryCollection(new MeshList(featureTable), isComplete, isCurved); - const readStatus = this.readGltf(geometry); + const childNodes = new Set(); + for (const key of Object.keys(this._nodes)) { + const node = this._nodes[key]; + if (node.children) + for (const child of node.children) + childNodes.add(child.toString()); + } + + const renderGraphicList: RenderGraphic[] = []; + let readStatus: TileIO.ReadStatus = TileIO.ReadStatus.InvalidTileData; + for (const nodeKey of Object.keys(this._nodes)) + if (!childNodes.has(nodeKey)) + if (TileIO.ReadStatus.Success !== (readStatus = this.readNodeAndCreateGraphics(renderGraphicList, this._nodes[nodeKey], featureTable, undefined, instances))) + return { readStatus, isLeaf }; + + if (0 === renderGraphicList.length) + return { readStatus: TileIO.ReadStatus.InvalidTileData, isLeaf }; let renderGraphic: RenderGraphic | undefined; - if (!geometry.isEmpty) { - const meshGraphicArgs = new MeshGraphicArgs(); - if (1 === geometry.meshes.length) { - renderGraphic = geometry.meshes[0].getGraphics(meshGraphicArgs, this._system); - } else { - const renderGraphicList: RenderGraphic[] = []; - for (const mesh of geometry.meshes) { - renderGraphic = mesh.getGraphics(meshGraphicArgs, this._system); - if (undefined !== renderGraphic) - renderGraphicList.push(renderGraphic); - } - renderGraphic = this._system.createGraphicList(renderGraphicList); - } - if (undefined !== renderGraphic) { - renderGraphic = this._system.createBatch(renderGraphic, PackedFeatureTable.pack(featureTable), contentRange); - if (undefined !== this._returnToCenter || this._yAxisUp) { - const branch = new GraphicBranch(); - branch.add(renderGraphic); - let transform = (undefined === this._returnToCenter) ? Transform.createIdentity() : Transform.createTranslationXYZ(this._returnToCenter[0], this._returnToCenter[1], this._returnToCenter[2]); - if (this._yAxisUp) transform = transform.multiplyTransformMatrix3d(Matrix3d.createRotationAroundVector(Vector3d.create(1.0, 0.0, 0.0), Angle.createRadians(Angle.piOver2Radians)) as Matrix3d); - renderGraphic = this._system.createBranch(branch, transform); - } - } + if (1 === renderGraphicList.length) + renderGraphic = renderGraphicList[0]; + else + renderGraphic = this._system.createGraphicList(renderGraphicList); + + renderGraphic = this._system.createBatch(renderGraphic, PackedFeatureTable.pack(featureTable), contentRange); + if (undefined !== this._returnToCenter || this._yAxisUp || undefined !== transformToRoot) { + const branch = new GraphicBranch(); + branch.add(renderGraphic); + let transform = (undefined === this._returnToCenter) ? Transform.createIdentity() : Transform.createTranslationXYZ(this._returnToCenter[0], this._returnToCenter[1], this._returnToCenter[2]); + if (this._yAxisUp) transform = transform.multiplyTransformMatrix3d(Matrix3d.createRotationAroundVector(Vector3d.create(1.0, 0.0, 0.0), Angle.createRadians(Angle.piOver2Radians)) as Matrix3d); + if (undefined !== transformToRoot) transform = transformToRoot.multiplyTransformTransform(transform); + renderGraphic = this._system.createBranch(branch, transform); } return { @@ -346,6 +417,59 @@ export namespace GltfTileIO { renderGraphic, }; } + private readNodeAndCreateGraphics(renderGraphicList: RenderGraphic[], node: any, featureTable: FeatureTable, parentTransform: Transform | undefined, instances?: InstancedGraphicParams): TileIO.ReadStatus { + if (undefined === node) + return TileIO.ReadStatus.InvalidTileData; + + let thisTransform = parentTransform; + if (Array.isArray(node.matrix)) { + const jTrans = node.matrix; + const nodeTransform = Transform.createOriginAndMatrix(Point3d.create(jTrans[12], jTrans[13], jTrans[14]), Matrix3d.createRowValues(jTrans[0], jTrans[4], jTrans[8], jTrans[1], jTrans[5], jTrans[9], jTrans[2], jTrans[6], jTrans[10])); + thisTransform = thisTransform ? thisTransform.multiplyTransformTransform(nodeTransform) : nodeTransform; + } + const meshKey = node.meshes ? node.meshes : node.mesh; + if (undefined !== meshKey) { + const nodeMesh = this._meshes[meshKey]; + if (nodeMesh) { + const meshGraphicArgs = new MeshGraphicArgs(); + const geometryCollection = new TileIO.GeometryCollection(new MeshList(featureTable), true, false); + for (const primitive of nodeMesh.primitives) { + const geometry = this.readMeshPrimitive(primitive, featureTable); + if (undefined !== geometry) + geometryCollection.meshes.push(geometry); + } + + let renderGraphic: RenderGraphic | undefined; + if (!geometryCollection.isEmpty) { + if (1 === geometryCollection.meshes.length) { + renderGraphic = geometryCollection.meshes[0].getGraphics(meshGraphicArgs, this._system, instances); + } else { + const thisList: RenderGraphic[] = []; + for (const mesh of geometryCollection.meshes) { + renderGraphic = mesh.getGraphics(meshGraphicArgs, this._system, instances); + if (undefined !== renderGraphic) + thisList.push(renderGraphic!); + } + if (0 !== thisList.length) + renderGraphic = this._system.createGraphicList(thisList); + } + if (renderGraphic) { + if (thisTransform && !thisTransform.isIdentity) { + const branch = new GraphicBranch(); + branch.add(renderGraphic); + renderGraphic = this._system.createBranch(branch, thisTransform); + } + renderGraphicList.push(renderGraphic); + } + } + } + } + if (node.children) { + for (const child of node.children) + this.readNodeAndCreateGraphics(renderGraphicList, this._nodes[child], featureTable, thisTransform, instances); + } + return TileIO.ReadStatus.Success; + } /** @hidden */ public getBufferView(json: any, accessorName: string): BufferView | undefined { @@ -355,7 +479,7 @@ export namespace GltfTileIO { const bufferViewAccessorValue = undefined !== accessor ? JsonUtils.asString(accessor.bufferView) : ""; const bufferView = 0 < bufferViewAccessorValue.length ? JsonUtils.asObject(this._bufferViews[bufferViewAccessorValue]) : undefined; - if (undefined === bufferView || undefined === accessor) + if (undefined === accessor) return undefined; const type = accessor.componentType as DataType; @@ -374,11 +498,21 @@ export namespace GltfTileIO { default: return undefined; } + let componentCount = 1; + switch (accessor.type) { + case "VEC3": + componentCount = 3; + break; + case "VEC2": + componentCount = 2; + break; + } - const offset = bufferView.byteOffset + accessor.byteOffset; + const offset = ((bufferView && bufferView.byteOffset) ? bufferView.byteOffset : 0) + (accessor.byteOffset ? accessor.byteOffset : 0); + const length = componentCount * dataSize * accessor.count; // If the data is misaligned (Scalable mesh tile publisher) use slice to copy -- else use subarray. // assert(0 === offset % dataSize); - const bytes = (0 === (this._binaryData.byteOffset + offset) % dataSize) ? this._binaryData.subarray(offset, offset + bufferView.byteLength) : this._binaryData.slice(offset, offset + bufferView.byteLength); + const bytes = (0 === (this._binaryData.byteOffset + offset) % dataSize) ? this._binaryData.subarray(offset, offset + length) : this._binaryData.slice(offset, offset + length); return new BufferView(bytes, accessor.count as number, type, accessor); } catch (e) { return undefined; @@ -397,6 +531,7 @@ export namespace GltfTileIO { /** @hidden */ protected constructor(props: ReaderProps, iModel: IModelConnection, modelId: Id64String, is3d: boolean, system: RenderSystem, type: BatchType = BatchType.Classifier, isCanceled?: IsCanceled) { this._buffer = props.buffer; + this._scene = props.scene; this._binaryData = props.binaryData; this._accessors = props.accessors; this._bufferViews = props.bufferViews; @@ -404,6 +539,7 @@ export namespace GltfTileIO { this._nodes = props.nodes; this._materialValues = props.materials; this._samplers = props.samplers; + this._techniques = props.techniques; this._yAxisUp = props.yAxisUp; this._returnToCenter = this.extractReturnToCenter(props.extensions); this._textures = props.scene.textures; @@ -428,30 +564,44 @@ export namespace GltfTileIO { /** @hidden */ protected readFeatureIndices(_json: any): number[] | undefined { return undefined; } - /** @hidden */ - protected abstract readColorTable(_colorTable: ColorMap, _json: any): boolean | undefined; - /** @hidden */ - protected abstract createDisplayParams(_json: any, hasBakedLighting: boolean): DisplayParams | undefined; - /** @hidden */ - protected abstract extractReturnToCenter(extensions: any): number[] | undefined; - /** @hidden */ - protected readGltf(geometry: TileIO.GeometryCollection): TileIO.ReadStatus { - for (const meshKey of Object.keys(this._meshes)) { - const meshValue = this._meshes[meshKey]; - const primitives = JsonUtils.asArray(meshValue.primitives); - if (undefined === primitives) - continue; + private colorFromJson(values: number[]): ColorDef { return ColorDef.from(values[0] * 255, values[1] * 255, values[2] * 255, (1.0 - values[3]) * 255); } - for (const primitive of primitives) { - const mesh = this.readMeshPrimitive(primitive, geometry.meshes.features); - assert(undefined !== mesh); - if (undefined !== mesh) - geometry.meshes.push(mesh); - } + private colorFromMaterial(materialJson: any): ColorDef { + if (materialJson) { + if (materialJson.values && Array.isArray(materialJson.values.color)) + return this.colorFromJson(materialJson.values.color); + else if (materialJson.pbrMetallicRoughness && Array.isArray(materialJson.pbrMetallicRoughness.baseColorFactor)) + return this.colorFromJson(materialJson.pbrMetallicRoughness.baseColorFactor); + else if (materialJson.extensions && materialJson.extensions.KHR_techniques_webgl && materialJson.extensions.KHR_techniques_webgl.values && materialJson.extensions.KHR_techniques_webgl.values.u_color) + return this.colorFromJson(materialJson.extensions.KHR_techniques_webgl.values.u_color); } + return ColorDef.white.clone(); + } - return TileIO.ReadStatus.Success; + protected createDisplayParams(materialJson: any, hasBakedLighting: boolean): DisplayParams | undefined { + let textureMapping: TextureMapping | undefined; + + if (undefined !== materialJson) { + if (materialJson.values && materialJson.values.tex) + textureMapping = this.findTextureMapping(materialJson.values.tex); // Bimiums shader value. + else if (materialJson.extensions && materialJson.extensions.KHR_techniques_webgl && materialJson.extensions.KHR_techniques_webgl.values && materialJson.extensions.KHR_techniques_webgl.values.u_tex) + textureMapping = this.findTextureMapping(materialJson.extensions.KHR_techniques_webgl.values.u_tex.index); // Bimiums colorIndex. + else if (materialJson.diffuseTexture) + textureMapping = this.findTextureMapping(materialJson.diffuseTexture.index); // TBD -- real map support with PBR + else if (materialJson.emissiveTexture) + textureMapping = this.findTextureMapping(materialJson.emissiveTexture.index); // TBD -- real map support with PBR + } + + const color = this.colorFromMaterial(materialJson); + return new DisplayParams(DisplayParams.Type.Mesh, color, color, 1, LinePixels.Solid, FillFlags.Always, undefined, undefined, hasBakedLighting, textureMapping); + } + protected extractReturnToCenter(extensions: any): number[] | undefined { + if (extensions === undefined) { return undefined; } + const cesiumRtc = JsonUtils.asObject(extensions.CESIUM_RTC); + if (cesiumRtc === undefined) return undefined; + const rtc = JsonUtils.asArray(cesiumRtc.center); + return (rtc[0] === 0.0 && rtc[1] === 0.0 && rtc[2] === 0.0) ? undefined : rtc; } /** @hidden */ @@ -463,7 +613,20 @@ export namespace GltfTileIO { if (undefined === displayParams) return undefined; - const primitiveType = JsonUtils.asInt(primitive.type, Mesh.PrimitiveType.Mesh); + let primitiveType: number = -1; + const meshMode = JsonUtils.asInt(primitive.mode, GltfTileIO.MeshMode.Triangles); + switch (meshMode) { + case GltfTileIO.MeshMode.Lines: + primitiveType = Mesh.PrimitiveType.Polyline; + return undefined; // Needs work... + break; + case GltfTileIO.MeshMode.Triangles: + primitiveType = Mesh.PrimitiveType.Mesh; + break; + default: + assert(false); + return undefined; + } const isPlanar = JsonUtils.asBool(primitive.isPlanar); const asClassifier = this._isClassifier; @@ -477,21 +640,38 @@ export namespace GltfTileIO { hasBakedLighting, asClassifier, }); - - if (!this.readVertices(mesh.points, primitive)) - return undefined; - - if (!this.readColorTable(mesh.colorMap, primitive)) - return undefined; - - const colorIndices = this.readColorIndices(primitive); - if (undefined !== colorIndices) - mesh.colors = colorIndices; - else if (mesh.colorMap.length !== 1) - return undefined; + // We don't have real colormap - just load material color. This will be used if non-Bentley + // tile or fit the color table is uniform. For a non-Bentley, non-Uniform, we'll set the + // uv parameters to pick the colors out of the color map texture. + mesh.colorMap.insert(displayParams.fillColor.tbgr); // White... + + const colorIndices = this.readBufferData16(primitive.attributes, "_COLORINDEX"); + if (undefined !== colorIndices) { + let texStep; + if (materialValue.values !== undefined && Array.isArray(materialValue.values.texStep)) + texStep = materialValue.values.texStep; + else if (materialValue.extensions && materialValue.extensions.KHR_techniques_webgl && materialValue.extensions.KHR_techniques_webgl.values && Array.isArray(materialValue.extensions.KHR_techniques_webgl.values.u_texStep)) + texStep = materialValue.extensions.KHR_techniques_webgl.values.u_texStep; + + if (texStep) + for (let i = 0; i < colorIndices.count; i++) + mesh.uvParams.push(new Point2d(texStep[1] + texStep[0] * colorIndices.buffer[i], .5)); + } if (undefined !== mesh.features && !this.readFeatures(mesh.features, primitive)) return undefined; + if (primitive.extensions && primitive.extensions.KHR_draco_mesh_compression) { + return undefined; // Defer Draco support until moved to web worker. + /* + const dracoExtension = primitive.extensions.KHR_draco_mesh_compression; + const bufferView = this._bufferViews[dracoExtension.bufferView]; + if (undefined === bufferView) return undefined; + const bufferData = this._binaryData.subarray(bufferView.byteOffset, bufferView.byteOffset + bufferView.byteLength); + + return DracoDecoder.readDracoMesh(mesh, primitive, bufferData); */ + } + if (!this.readVertices(mesh.points, primitive)) + return undefined; switch (primitiveType) { case Mesh.PrimitiveType.Mesh: { @@ -501,10 +681,11 @@ export namespace GltfTileIO { if (!displayParams.ignoreLighting && !this.readNormals(mesh.normals, primitive.attributes, "NORMAL")) return undefined; - this.readUVParams(mesh.uvParams, primitive.attributes, "TEXCOORD_0"); - this.readMeshEdges(mesh, primitive.edges); + if (0 === mesh.uvParams.length) + this.readUVParams(mesh.uvParams, primitive.attributes, "TEXCOORD_0"); break; } + case Mesh.PrimitiveType.Polyline: case Mesh.PrimitiveType.Point: { if (undefined !== mesh.polylines && !this.readPolylines(mesh.polylines, primitive, "indices", Mesh.PrimitiveType.Point === primitiveType)) @@ -516,6 +697,8 @@ export namespace GltfTileIO { return undefined; } } + if (displayParams.textureMapping && 0 === mesh.uvParams.length) + return undefined; return mesh; } @@ -593,19 +776,6 @@ export namespace GltfTileIO { return true; } - /** @hidden */ - protected readColorIndices(json: any): Uint16Array | undefined { - const data = this.readBufferData16(json.attributes, "_COLORINDEX"); - if (undefined === data) - return undefined; - - const colors = new Uint16Array(data.count); - for (let i = 0; i < data.count; i++) - colors[i] = data.buffer[i]; - - return colors; - } - /** @hidden */ protected readMeshIndices(mesh: Mesh, json: any): boolean { const data = this.readBufferData32(json, "indices"); @@ -704,64 +874,6 @@ export namespace GltfTileIO { return true; } - /** @hidden */ - protected readMeshEdges(mesh: Mesh, json: any): boolean { - if (undefined === json || undefined === mesh) - return false; - - mesh.edges = new MeshEdges(); - const visEdges = this.readEdgeIndices(json, "visibles"); - if (undefined !== visEdges) - mesh.edges.visible = visEdges; - - if (undefined !== json.silhouettes) { - const normPairs = this.readNormalPairs(json.silhouettes, "normalPairs"); - if (undefined !== normPairs) - mesh.edges.silhouetteNormals = normPairs; - const silEdges = this.readEdgeIndices(json.silhouettes, "indices"); - if (undefined !== silEdges) - mesh.edges.silhouette = silEdges; - } - - mesh.edges.polylines = new MeshPolylineList(); - return this.readPolylines(mesh.edges.polylines, json, "polylines", false); - } - - /** @hidden */ - protected readEdgeIndices(json: any, accessorName: string): MeshEdge[] | undefined { - const data = this.readBufferData32(json, accessorName); - if (undefined === data) - return undefined; - - const edges = new Array(data.count / 2); - - let e = 0; - for (let i = 0; i < data.count; i += 2) - edges[e++] = new MeshEdge(data.buffer[i], data.buffer[i + 1]); - - return edges; - } - - /** @hidden */ - protected readNormalPairs(json: any, accessorName: string): OctEncodedNormalPair[] | undefined { - const data = this.readBufferData8(json, accessorName); - if (undefined === data) - return undefined; - - const normPairs = new Array(data.count); - - // ###TODO: we shouldn't have to allocate OctEncodedNormal objects...just use uint16s / numbers... - for (let i = 0; i < data.count; i++) { - // ###TODO? not clear why ray writes these as pairs of uint8... - const index = i * 4; - const normal0 = data.buffer[index] | (data.buffer[index + 1] << 8); - const normal1 = data.buffer[index + 2] | (data.buffer[index + 3] << 8); - normPairs[i] = new OctEncodedNormalPair(new OctEncodedNormal(normal0), new OctEncodedNormal(normal1)); - } - - return normPairs; - } - /** @hidden */ protected readPolylines(polylines: MeshPolylineList, json: any, accessorName: string, disjoint: boolean): boolean { const view = this.getBufferView(json, accessorName); @@ -813,34 +925,59 @@ export namespace GltfTileIO { if (undefined === this._textures) return Promise.resolve(); + const transparentTextures: Set = new Set(); + for (const name of Object.keys(this._materialValues)) { + const materialValue = this._materialValues[name]; + let technique; + if (undefined !== materialValue.values && + undefined !== materialValue.values.tex && + undefined !== materialValue.technique && + undefined !== (technique = this._techniques[materialValue.technique]) && + undefined !== technique.states && + Array.isArray(technique.states.enable)) { + for (const enable of technique.states.enable) + if (enable === 3042) + transparentTextures.add(materialValue.values.tex); + } + } + const promises = new Array>(); for (const name of Object.keys(this._textures)) - promises.push(this.loadTexture(name)); + promises.push(this.loadTexture(name, transparentTextures.has(name))); return promises.length > 0 ? Promise.all(promises).then((_) => undefined) : Promise.resolve(); } /** @hidden */ - protected async loadTextureImage(imageJson: any, samplerJson: any): Promise { + protected async loadTextureImage(imageJson: any, samplerJson: any, isTransparent: boolean): Promise { try { - const binaryImageJson = JsonUtils.asObject(imageJson.extensions.KHR_binary_glTF); + const binaryImageJson = (imageJson.extensions && imageJson.extensions.KHR_binary_glTF) ? JsonUtils.asObject(imageJson.extensions.KHR_binary_glTF) : imageJson; const bufferView = this._bufferViews[binaryImageJson.bufferView]; const mimeType = JsonUtils.asString(binaryImageJson.mimeType); const format = getImageSourceFormatForMimeType(mimeType); if (undefined === format) return undefined; - const offset = bufferView.byteOffset; - const bytes = this._binaryData.subarray(offset, offset + bufferView.byteLength); - const imageSource = new ImageSource(bytes, format); let textureType = RenderTexture.Type.Normal; if (undefined !== samplerJson && (undefined !== samplerJson.wrapS || undefined !== samplerJson.wrapS)) textureType = RenderTexture.Type.TileSection; - const textureParams = new RenderTexture.Params(undefined, textureType); + const offset = bufferView.byteOffset; + + /* ----------------------------------- + const jpegArray = this._binaryData.slice(offset, offset + bufferView.byteLength); + const jpegArrayBuffer = jpegArray.buffer; + const workerOp = new ImageDecodeWorkerOperation(jpegArrayBuffer, mimeType); + return Reader.webWorkerManager.queueOperation(workerOp) + .then((imageBitmap) => this._isCanceled ? undefined : this._system.createTextureFromImage(imageBitmap, isTransparent && ImageSourceFormat.Png === format, this._iModel, textureParams)) + .catch((_) => undefined); + ------------------------------------- */ + + const bytes = this._binaryData.subarray(offset, offset + bufferView.byteLength); + const imageSource = new ImageSource(bytes, format); return imageElementFromImageSource(imageSource) - .then((image) => this._isCanceled ? undefined : this._system.createTextureFromImage(image, ImageSourceFormat.Png === format, this._iModel, textureParams)) + .then((image) => this._isCanceled ? undefined : this._system.createTextureFromImage(image, isTransparent && ImageSourceFormat.Png === format, this._iModel, textureParams)) .catch((_) => undefined); } catch (e) { return undefined; @@ -848,12 +985,12 @@ export namespace GltfTileIO { } /** @hidden */ - protected async loadTexture(textureId: string): Promise { + protected async loadTexture(textureId: string, isTransparent: boolean): Promise { const textureJson = JsonUtils.asObject(this._textures[textureId]); if (undefined === textureJson) return Promise.resolve(); - return this.loadTextureImage(this._images[textureJson.source], undefined === this._samplers ? undefined : this._samplers[textureJson.sampler]).then((texture) => { + return this.loadTextureImage(this._images[textureJson.source], undefined === this._samplers ? undefined : this._samplers[textureJson.sampler], isTransparent).then((texture) => { textureJson.renderTexture = texture; }); } diff --git a/core/frontend/src/tile/I3dmTileIO.ts b/core/frontend/src/tile/I3dmTileIO.ts new file mode 100644 index 0000000..e58dfb9 --- /dev/null +++ b/core/frontend/src/tile/I3dmTileIO.ts @@ -0,0 +1,167 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module Tile */ +import { TileIO } from "./TileIO"; +import { GltfTileIO } from "./GltfTileIO"; +import { ElementAlignedBox3d, FeatureTable, Feature, BatchType } from "@bentley/imodeljs-common"; +import { Id64String, utf8ToString, JsonUtils } from "@bentley/bentleyjs-core"; +import { InstancedGraphicParams, RenderSystem } from "../render/System"; +import { Mesh } from "../render/primitives/mesh/MeshPrimitives"; +import { IModelConnection } from "../IModelConnection"; +import { Point3d, Vector3d, Matrix3d, AxisOrder } from "@bentley/geometry-core"; + +function setTransform(transforms: Float32Array, index: number, rotation: Matrix3d, origin: Point3d): void { + const i = index * 12; + let rot = rotation.coffs; + + const ignoreRotation = false; + if (ignoreRotation) + rot = new Float64Array([1, 0, 0, 0, 1, 0, 0, 0, 1]); + + const ignoreOrigin = false; + if (ignoreOrigin) + origin.x = origin.y = origin.z = 0; + + transforms[i + 0] = rot[0]; + transforms[i + 1] = rot[1]; + transforms[i + 2] = rot[2]; + transforms[i + 3] = origin.x; + + transforms[i + 4] = rot[3]; + transforms[i + 5] = rot[4]; + transforms[i + 6] = rot[5]; + transforms[i + 7] = origin.y; + + transforms[i + 8] = rot[6]; + transforms[i + 9] = rot[7]; + transforms[i + 10] = rot[8]; + transforms[i + 11] = origin.z; +} + +/** + * Provides facilities for deserializing Batched 3D Model (B3dm) tiles. + * @hidden + */ +export namespace I3dmTileIO { + export class Header extends TileIO.Header { + public readonly length: number; + public readonly featureTableJsonPosition: number; + public readonly featureTableJsonLength: number; + public readonly featureTableBinaryLength: number; + public readonly batchTableJsonLength: number; + public readonly batchTableBinaryLength: number; + public readonly gltfVersion: number; + public get isValid(): boolean { return TileIO.Format.I3dm === this.format; } + + public constructor(stream: TileIO.StreamBuffer) { + super(stream); + this.length = stream.nextUint32; + this.featureTableJsonLength = stream.nextUint32; + this.featureTableBinaryLength = stream.nextUint32; + this.batchTableJsonLength = stream.nextUint32; + this.batchTableBinaryLength = stream.nextUint32; + this.gltfVersion = stream.nextUint32; + this.featureTableJsonPosition = stream.curPos; + stream.advance(this.featureTableJsonLength); + stream.advance(this.featureTableBinaryLength); + stream.advance(this.batchTableJsonLength); + stream.advance(this.batchTableBinaryLength); + + if (stream.isPastTheEnd) + this.invalidate(); + } + } + + /** + * Deserializes a I3DM tile. + * @hidden + */ + export class Reader extends GltfTileIO.Reader { + public static create(stream: TileIO.StreamBuffer, iModel: IModelConnection, modelId: Id64String, is3d: boolean, range: ElementAlignedBox3d, system: RenderSystem, yAxisUp: boolean, isLeaf: boolean, isCanceled?: GltfTileIO.IsCanceled): Reader | undefined { + const header = new Header(stream); + if (!header.isValid) + return undefined; + + const props = GltfTileIO.ReaderProps.create(stream, yAxisUp); + stream.curPos = header.featureTableJsonPosition; + const featureStr = utf8ToString(stream.nextBytes(header.featureTableJsonLength)); + if (undefined === featureStr) + return undefined; + const featureBinary = new Uint8Array(stream.arrayBuffer, header.featureTableJsonPosition + header.featureTableJsonLength, header.featureTableBinaryLength); + return undefined !== props ? new Reader(featureBinary, JSON.parse(featureStr), props, iModel, modelId, is3d, system, range, isLeaf, isCanceled) : undefined; + } + + private constructor(private _featureBinary: Uint8Array, private _featureJson: any, props: GltfTileIO.ReaderProps, iModel: IModelConnection, modelId: Id64String, is3d: boolean, system: RenderSystem, private _range: ElementAlignedBox3d, private _isLeaf: boolean, isCanceled?: GltfTileIO.IsCanceled) { + super(props, iModel, modelId, is3d, system, BatchType.Primary, isCanceled); + } + + public async read(): Promise { + + // TBD... Create an actual feature table if one exists. For now we are only reading tiles from scalable mesh which have no features. + // NB: For reality models with no batch table, we want the model ID in the feature table + const featureTable: FeatureTable = new FeatureTable(1, this._modelId, this._type); + const feature = new Feature(this._modelId); + featureTable.insert(feature); + + await this.loadTextures(); + if (this._isCanceled) + return Promise.resolve({ readStatus: TileIO.ReadStatus.Canceled, isLeaf: this._isLeaf }); + + const instances = this.readInstances(); + if (undefined === instances) + return Promise.resolve({ readStatus: TileIO.ReadStatus.InvalidTileData, isLeaf: this._isLeaf }); + + return this.readGltfAndCreateGraphics(this._isLeaf, featureTable, this._range, undefined, undefined, instances); + } + + protected readFeatures(features: Mesh.Features, _json: any): boolean { + const feature = new Feature(this._modelId); + + features.add(feature, 1); + return true; + } + + private readInstances(): InstancedGraphicParams | undefined { + const count = JsonUtils.asInt(this._featureJson.INSTANCES_LENGTH, 0); + if (count <= 0) + return undefined; + + const json = this._featureJson; + const binary = this._featureBinary; + + const positions = json.POSITION ? new Float32Array(binary.buffer, binary.byteOffset + json.POSITION.byteOffset, count * 3) : undefined; + const upNormals = json.NORMAL_UP ? new Float32Array(binary.buffer, binary.byteOffset + json.NORMAL_UP.byteOffset, count * 3) : undefined; + const rightNormals = json.NORMAL_RIGHT ? new Float32Array(binary.buffer, binary.byteOffset + json.NORMAL_RIGHT.byteOffset, count * 3) : undefined; + + const matrix = Matrix3d.createIdentity(); + const position = Point3d.createZero(); + const upNormal = Vector3d.create(0, 0, 1); + const rightNormal = Vector3d.create(1, 0, 0); + + const transforms = new Float32Array(12 * count); + for (let i = 0; i < count; i++) { + const index = i * 3; + if (positions) + position.set(positions[index], positions[index + 1], positions[index + 2]); + + if (upNormals || rightNormals) { + if (upNormals) + upNormal.set(upNormals[index], upNormals[index + 1], upNormals[index + 2]); + + if (rightNormals) + rightNormal.set(rightNormals[index], rightNormals[index + 1], rightNormals[index + 2]); + + Matrix3d.createRigidFromColumns(rightNormal, upNormal, AxisOrder.XYZ, matrix); + setTransform(transforms, i, matrix, position); + } + } + + // ###TODO: Use actual feature IDs if feature table exists + const featureIds = new Uint8Array(3 * count); + + return { count, transforms, featureIds }; + } + } +} diff --git a/core/frontend/src/tile/IModelTileIO.ts b/core/frontend/src/tile/IModelTileIO.ts index adde9f1..b863f80 100644 --- a/core/frontend/src/tile/IModelTileIO.ts +++ b/core/frontend/src/tile/IModelTileIO.ts @@ -20,11 +20,11 @@ import { SegmentEdgeParams, SilhouetteParams, EdgeParams, - AuxDisplacement, - AuxNormal, - AuxParam, } from "../render/primitives/VertexTable"; -import { ColorMap } from "../render/primitives/ColorMap"; +import { + AuxChannelTable, + AuxChannelTableProps, +} from "../render/primitives/AuxChannelTable"; import { Id64String, JsonUtils, assert } from "@bentley/bentleyjs-core"; import { RenderSystem, RenderGraphic, PackedFeatureTable, GraphicBranch } from "../render/System"; import { imageElementFromImageSource } from "../ImageUtil"; @@ -64,10 +64,27 @@ export namespace IModelTileIO { Incomplete = 1 << 2, } + /** Describes the major and minor version of the tile format supported by this front-end package. */ + export const enum CurrentVersion { + /** The unsigned 16-bit major version number. If the major version specified in the tile header is greater than this value, then this + * front-end is not capable of reading the tile content. Otherwise, this front-end can read the tile content even if the header specifies a + * greater minor version than CurrentVersion.Minor, although some data may be skipped. + */ + Major = 1, + /** The unsigned 16-bit minor version number. If the major version in the tile header is equal to CurrentVersion.Major, then this front-end can + * read the tile content even if the minor version in the tile header is greater than this value, although some data may be skipped. + */ + Minor = 4, + /** The unsigned 32-bit version number derived from the 16-bit major and minor version numbers. */ + Combined = (Major << 0x10) | Minor, + } + /** Header embedded at the beginning of the binary tile data describing its contents. * @hidden */ export class Header extends TileIO.Header { + /** The size of this header in bytes. */ + public readonly headerLength: number; /** Flags describing the geometry contained within the tile */ public readonly flags: Flags; /** A bounding box no larger than the tile's range, tightly enclosing the tile's geometry; or a null range if the tile is emtpy */ @@ -79,21 +96,37 @@ export namespace IModelTileIO { /** The number of elements within the tile range which contributed no geometry to the tile content */ public readonly numElementsExcluded: number; /** The total number of bytes in the binary tile data, including this header */ - public readonly length: number; + public readonly tileLength: number; + + public get versionMajor(): number { return this.version >>> 0x10; } + public get versionMinor(): number { return (this.version & 0xffff) >>> 0; } public get isValid(): boolean { return TileIO.Format.IModel === this.format; } + public get isReadableVersion(): boolean { return this.versionMajor <= IModelTileIO.CurrentVersion.Major; } /** Deserialize a header from the binary data at the stream's current position. * If the binary data does not contain a valid header, the Header will be marked 'invalid'. */ public constructor(stream: TileIO.StreamBuffer) { super(stream); + this.headerLength = stream.nextUint32; this.flags = stream.nextUint32; - this.contentRange = ElementAlignedBox3d.createFromPoints(stream.nextPoint3d64, stream.nextPoint3d64); + + // NB: Cannot use any of the static create*() functions because they all want to compute a range to contain the supplied points. + // (If contentRange is null, this will produce maximum range). + this.contentRange = new Range3d(); + this.contentRange.low = stream.nextPoint3d64; + this.contentRange.high = stream.nextPoint3d64; + this.tolerance = stream.nextFloat64; this.numElementsIncluded = stream.nextUint32; this.numElementsExcluded = stream.nextUint32; - this.length = stream.nextUint32; + this.tileLength = stream.nextUint32; + + // Skip any unprocessed bytes in header + const remainingHeaderBytes = this.headerLength - stream.curPos; + assert(remainingHeaderBytes >= 0); + stream.advance(remainingHeaderBytes); if (stream.isPastTheEnd) this.invalidate(); @@ -124,11 +157,12 @@ export namespace IModelTileIO { */ export class Reader extends GltfTileIO.Reader { private readonly _sizeMultiplier?: number; + private readonly _loadEdges: boolean; /** Attempt to initialize a Reader to deserialize iModel tile data beginning at the stream's current position. */ - public static create(stream: TileIO.StreamBuffer, iModel: IModelConnection, modelId: Id64String, is3d: boolean, system: RenderSystem, type: BatchType = BatchType.Primary, isCanceled?: GltfTileIO.IsCanceled, sizeMultiplier?: number): Reader | undefined { + public static create(stream: TileIO.StreamBuffer, iModel: IModelConnection, modelId: Id64String, is3d: boolean, system: RenderSystem, type: BatchType = BatchType.Primary, loadEdges: boolean = true, isCanceled?: GltfTileIO.IsCanceled, sizeMultiplier?: number): Reader | undefined { const header = new Header(stream); - if (!header.isValid) + if (!header.isValid || !header.isReadableVersion) return undefined; // The feature table follows the iMdl header @@ -137,7 +171,7 @@ export namespace IModelTileIO { // A glTF header follows the feature table const props = GltfTileIO.ReaderProps.create(stream, false); - return undefined !== props ? new Reader(props, iModel, modelId, is3d, system, type, isCanceled, sizeMultiplier) : undefined; + return undefined !== props ? new Reader(props, iModel, modelId, is3d, system, type, loadEdges, isCanceled, sizeMultiplier) : undefined; } /** Attempt to deserialize the tile data */ @@ -147,6 +181,8 @@ export namespace IModelTileIO { let isLeaf = true; if (!header.isValid) return { readStatus: TileIO.ReadStatus.InvalidHeader, isLeaf }; + else if (!header.isReadableVersion) + return { readStatus: TileIO.ReadStatus.NewerMajorVersion, isLeaf }; const featureTable = this.readFeatureTable(); if (undefined === featureTable) @@ -185,9 +221,6 @@ export namespace IModelTileIO { /** @hidden */ protected extractReturnToCenter(_extensions: any): number[] | undefined { return undefined; } - /** @hidden */ - protected readColorTable(_colorTable: ColorMap, _json: any): boolean | undefined { assert(false); return false; } - /** @hidden */ protected createDisplayParams(json: any): DisplayParams | undefined { const type = JsonUtils.asInt(json.type, DisplayParams.Type.Mesh); @@ -363,14 +396,41 @@ export namespace IModelTileIO { if (this._buffer.isPastTheEnd) return undefined; + let animNodesArray: Uint8Array | Uint16Array | Uint32Array | undefined; + const animationNodes = JsonUtils.asObject(this._scene.animationNodes); + if (undefined !== animationNodes) { + const bytesPerId = JsonUtils.asInt(animationNodes.bytesPerId); + const bufferViewId = JsonUtils.asString(animationNodes.bufferView); + const bufferViewJson = this._bufferViews[bufferViewId]; + if (undefined !== bufferViewJson) { + const byteOffset = JsonUtils.asInt(bufferViewJson.byteOffset); + const byteLength = JsonUtils.asInt(bufferViewJson.byteLength); + const bytes = this._binaryData.subarray(byteOffset, byteOffset + byteLength); + switch (bytesPerId) { + case 1: + animNodesArray = new Uint8Array(bytes); + break; + case 2: + // NB: A *copy* of the subarray. + animNodesArray = Uint16Array.from(new Uint16Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 2)); + break; + case 4: + // NB: A *copy* of the subarray. + animNodesArray = Uint32Array.from(new Uint32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 4)); + break; + } + } + } + this._buffer.curPos = startPos + header.length; - return new PackedFeatureTable(packedFeatureArray, this._modelId, header.count, header.maxFeatures, this._type); + return new PackedFeatureTable(packedFeatureArray, this._modelId, header.count, header.maxFeatures, this._type, animNodesArray); } - private constructor(props: GltfTileIO.ReaderProps, iModel: IModelConnection, modelId: Id64String, is3d: boolean, system: RenderSystem, type: BatchType, isCanceled?: GltfTileIO.IsCanceled, sizeMultiplier?: number) { + private constructor(props: GltfTileIO.ReaderProps, iModel: IModelConnection, modelId: Id64String, is3d: boolean, system: RenderSystem, type: BatchType, loadEdges: boolean, isCanceled?: GltfTileIO.IsCanceled, sizeMultiplier?: number) { super(props, iModel, modelId, is3d, system, type, isCanceled); this._sizeMultiplier = sizeMultiplier; + this._loadEdges = loadEdges; } private static skipFeatureTable(stream: TileIO.StreamBuffer): boolean { @@ -399,7 +459,7 @@ export namespace IModelTileIO { const primitiveType = JsonUtils.asInt(primitive.type, Mesh.PrimitiveType.Mesh); switch (primitiveType) { case Mesh.PrimitiveType.Mesh: - return this.createMeshGraphic(primitive, displayParams, vertices, isPlanar); + return this.createMeshGraphic(primitive, displayParams, vertices, isPlanar, this.readAuxChannelTable(primitive)); case Mesh.PrimitiveType.Polyline: return this.createPolylineGraphic(primitive, displayParams, vertices, isPlanar); case Mesh.PrimitiveType.Point: @@ -455,43 +515,7 @@ export namespace IModelTileIO { const uvRange = new Range2d(uvMin[0], uvMin[1], uvMax[0], uvMax[1]); uvParams = QParams2d.fromRange(uvRange); } - let auxDisplacements: undefined | AuxDisplacement[]; - if (undefined !== json.auxDisplacements) { - auxDisplacements = []; - for (const displacementJson of json.auxDisplacements) { - auxDisplacements.push(new AuxDisplacement({ - name: displacementJson.name, - qOrigin: displacementJson.qOrigin, - qScale: displacementJson.qScale, - inputs: displacementJson.inputs, - indices: displacementJson.indices, - })); - } - } - let auxNormals: undefined | AuxNormal[]; - if (undefined !== json.auxNormals) { - auxNormals = []; - for (const normalJson of json.auxNormals) { - auxNormals.push(new AuxNormal({ - name: normalJson.name, - inputs: normalJson.inputs, - indices: normalJson.indices, - })); - } - } - let auxParams: undefined | AuxParam[]; - if (undefined !== json.auxParams) { - auxParams = []; - for (const paramJson of json.auxParams) { - auxParams.push(new AuxParam({ - name: paramJson.name, - qOrigin: paramJson.qOrigin, - qScale: paramJson.qScale, - inputs: paramJson.inputs, - indices: paramJson.indices, - })); - } - } + return new VertexTable({ data: bytes, qparams, @@ -504,12 +528,32 @@ export namespace IModelTileIO { numVertices: json.count, numRgbaPerVertex: json.numRgbaPerVertex, uvParams, - auxDisplacements, - auxNormals, - auxParams, }); } + private readAuxChannelTable(primitive: any): AuxChannelTable | undefined { + const json = primitive.auxChannels; + if (undefined === json) + return undefined; + + const bytes = this.findBuffer(JsonUtils.asString(json.bufferView)); + if (undefined === bytes) + return undefined; + + const props: AuxChannelTableProps = { + data: bytes, + width: json.width, + height: json.height, + count: json.count, + numBytesPerVertex: json.numBytesPerVertex, + displacements: json.displacements, + normals: json.normals, + params: json.params, + }; + + return AuxChannelTable.fromJSON(props); + } + private readVertexIndices(json: any): VertexIndices | undefined { const bytes = this.findBuffer(json as string); return undefined !== bytes ? new VertexIndices(bytes) : undefined; @@ -571,7 +615,7 @@ export namespace IModelTileIO { type, indices, fillFlags: displayParams.fillFlags, - hasBakedLighting: displayParams.ignoreLighting, + hasBakedLighting: false, material: displayParams.material, texture, }; @@ -601,8 +645,7 @@ export namespace IModelTileIO { if (undefined !== json.silhouettes && undefined === (silhouettes = this.readSilhouettes(json.silhouettes))) return { succeeded }; - const ignorePolylineEdges = true; // ###TODO: Fix add-on - it duplicates these with the segment edges, and these waste tons of memory... - if (!ignorePolylineEdges && undefined !== json.polylines && undefined === (polylines = this.readTesselatedPolyline(json.polylines))) + if (undefined !== json.polylines && undefined === (polylines = this.readTesselatedPolyline(json.polylines))) return { succeeded }; succeeded = true; @@ -620,14 +663,14 @@ export namespace IModelTileIO { return { succeeded, params }; } - private createMeshGraphic(primitive: any, displayParams: DisplayParams, vertices: VertexTable, isPlanar: boolean): RenderGraphic | undefined { + private createMeshGraphic(primitive: any, displayParams: DisplayParams, vertices: VertexTable, isPlanar: boolean, auxChannels: AuxChannelTable | undefined): RenderGraphic | undefined { const surface = this.readSurface(primitive, displayParams); if (undefined === surface) return undefined; // ###TODO: Tile generator shouldn't bother producing edges for classification meshes in the first place... let edgeParams: EdgeParams | undefined; - if (undefined !== primitive.edges && SurfaceType.Classifier !== surface.type) { + if (this._loadEdges && undefined !== primitive.edges && SurfaceType.Classifier !== surface.type) { const edgeResult = this.readEdges(primitive.edges, displayParams); if (!edgeResult.succeeded) return undefined; @@ -635,7 +678,7 @@ export namespace IModelTileIO { edgeParams = edgeResult.params; } - const params = new MeshParams(vertices, surface, edgeParams, isPlanar); + const params = new MeshParams(vertices, surface, edgeParams, isPlanar, auxChannels); return this._system.createMesh(params); } diff --git a/core/frontend/src/tile/RealityModelTileTree.ts b/core/frontend/src/tile/RealityModelTileTree.ts index 994a1fa..1861864 100644 --- a/core/frontend/src/tile/RealityModelTileTree.ts +++ b/core/frontend/src/tile/RealityModelTileTree.ts @@ -4,18 +4,20 @@ *--------------------------------------------------------------------------------------------*/ /** @module Tile */ -import { IModelError, TileTreeProps, TileProps, ViewFlag, ViewFlags, RenderMode, Cartographic, EcefLocation } from "@bentley/imodeljs-common"; +import { IModelError, TileTreeProps, TileProps, ViewFlag, ViewFlags, RenderMode, Cartographic } from "@bentley/imodeljs-common"; import { IModelConnection } from "../IModelConnection"; import { BentleyStatus, assert, Guid, ActivityLoggingContext } from "@bentley/bentleyjs-core"; import { TransformProps, Range3dProps, Range3d, Transform, Point3d, Vector3d, Matrix3d } from "@bentley/geometry-core"; -import { RealityDataServicesClient, AccessToken, getArrayBuffer, getJson } from "@bentley/imodeljs-clients"; +import { RealityDataServicesClient, AccessToken, getArrayBuffer, getJson, RealityData } from "@bentley/imodeljs-clients"; import { TileTree, TileTreeState, Tile, TileLoader } from "./TileTree"; import { TileRequest } from "./TileRequest"; import { IModelApp } from "../IModelApp"; +/** @hidden */ +function getUrl(content: any) { return content ? (content.url ? content.url : content.uri) : undefined; } /** @hidden */ export class RealityModelTileUtils { - public static rangeFromBoundingVolume(boundingVolume: any, ecefLocation: EcefLocation | undefined): Range3d | undefined { + public static rangeFromBoundingVolume(boundingVolume: any): Range3d | undefined { if (undefined === boundingVolume) return undefined; if (undefined !== boundingVolume.box) { @@ -33,11 +35,15 @@ export class RealityModelTileUtils { } } return Range3d.createArray(corners); - } else if (Array.isArray(boundingVolume.region) && undefined !== ecefLocation) { + } else if (Array.isArray(boundingVolume.sphere)) { + const sphere: number[] = boundingVolume.sphere; + const center = Point3d.create(sphere[0], sphere[1], sphere[2]); + const radius = sphere[3]; + return Range3d.createXYZXYZ(center.x - radius, center.y - radius, center.z - radius, center.x + radius, center.y + radius, center.z + radius); + } else if (Array.isArray(boundingVolume.region)) { const ecefLow = (new Cartographic(boundingVolume.region[0], boundingVolume.region[1], boundingVolume.region[4])).toEcef(); const ecefHigh = (new Cartographic(boundingVolume.region[2], boundingVolume.region[3], boundingVolume.region[5])).toEcef(); - const ecefRange = Range3d.create(ecefLow, ecefHigh); - return ecefLocation.getTransform().inverse()!.multiplyRange(ecefRange); + return Range3d.create(ecefLow, ecefHigh); } else return undefined; } @@ -48,11 +54,6 @@ export class RealityModelTileUtils { public static transformFromJson(jTrans: number[] | undefined): Transform { return (jTrans === undefined) ? Transform.createIdentity() : Transform.createOriginAndMatrix(Point3d.create(jTrans[12], jTrans[13], jTrans[14]), Matrix3d.createRowValues(jTrans[0], jTrans[4], jTrans[8], jTrans[1], jTrans[5], jTrans[9], jTrans[2], jTrans[6], jTrans[10])); } - public static ecefTransformFromRegion(region: number[]) { - const cartoCenter = new Cartographic((region[0] + region[2]) / 2.0, (region[1] + region[3]) / 2.0, (region[4] + region[5]) / 2.0); - const ecefLocation = EcefLocation.createFromCartographicOrigin(cartoCenter!); - return ecefLocation.getTransform(); - } } /** @hidden */ @@ -62,12 +63,11 @@ class RealityModelTileTreeProps implements TileTreeProps { public location: TransformProps; public tilesetJson: object; public yAxisUp: boolean = false; - public ecefLocation?: EcefLocation; - constructor(json: any, public client: RealityModelTileClient, tileToDb: Transform, ecefLocation: EcefLocation | undefined) { + constructor(json: any, public client: RealityModelTileClient, tilesetTransform: Transform) { this.tilesetJson = json.root; - this.rootTile = new RealityModelTileProps(json.root, "", ecefLocation); - this.location = tileToDb.toJSON(); - if (json.asset.gltfUpAxis === undefined || json.asset.gltfUpAxis === "y") + this.rootTile = new RealityModelTileProps(json.root, ""); + this.location = tilesetTransform.toJSON(); + if (json.asset.gltfUpAxis === undefined || json.asset.gltfUpAxis === "y" || json.asset.gltfUpAxis === "Y") this.yAxisUp = true; } } @@ -79,15 +79,17 @@ class RealityModelTileProps implements TileProps { public readonly contentRange?: Range3dProps; public readonly maximumSize: number; public readonly isLeaf: boolean; + public readonly transformToRoot?: TransformProps; public geometry?: string | ArrayBuffer; public hasContents: boolean; - constructor(json: any, thisId: string, ecefLocation: EcefLocation | undefined) { + constructor(json: any, thisId: string, transformToRoot?: Transform) { this.contentId = thisId; - this.range = RealityModelTileUtils.rangeFromBoundingVolume(json.boundingVolume, ecefLocation)!; + this.range = RealityModelTileUtils.rangeFromBoundingVolume(json.boundingVolume)!; this.isLeaf = !Array.isArray(json.children) || 0 === json.children.length; - this.hasContents = undefined !== json.content && (undefined !== json.content.url || undefined !== json.content.uri); + this.hasContents = undefined !== getUrl(json.content); + this.transformToRoot = transformToRoot; if (this.hasContents) { - this.contentRange = json.content.boundingVolume && RealityModelTileUtils.rangeFromBoundingVolume(json.content.boundingVolume, ecefLocation); + this.contentRange = RealityModelTileUtils.rangeFromBoundingVolume(json.content.boundingVolume); this.maximumSize = RealityModelTileUtils.maximumSizeFromGeometricTolerance(Range3d.fromJSON(this.range), json.geometricError); } else { this.maximumSize = 0.0; @@ -97,7 +99,7 @@ class RealityModelTileProps implements TileProps { /** @hidden */ class FindChildResult { - constructor(public id: string, public json: any) { } + constructor(public id: string, public json: any, public transformToRoot?: Transform) { } } /** @hidden */ @@ -113,13 +115,13 @@ class RealityModelTileLoader extends TileLoader { const thisId = parent.contentId; const prefix = thisId.length ? thisId + "_" : ""; - const json = await this.findTileInJson(this._tree.tilesetJson, thisId, ""); - if (undefined !== json && Array.isArray(json.json.children)) { - for (let i = 0; i < json.json.children.length; i++) { + const findResult = await this.findTileInJson(this._tree.tilesetJson, thisId, "", undefined, true); + if (undefined !== findResult && Array.isArray(findResult.json.children)) { + for (let i = 0; i < findResult.json.children.length; i++) { const childId = prefix + i; - const foundChild = await this.findTileInJson(this._tree.tilesetJson, childId, ""); + const foundChild = await this.findTileInJson(this._tree.tilesetJson, childId, "", undefined, true); if (undefined !== foundChild) - props.push(new RealityModelTileProps(foundChild.json, foundChild.id, this._tree.ecefLocation)); + props.push(new RealityModelTileProps(foundChild.json, foundChild.id, foundChild.transformToRoot)); } } @@ -131,12 +133,26 @@ class RealityModelTileLoader extends TileLoader { if (undefined === foundChild) return undefined; - return this._tree.client.getTileContent(foundChild.json.content.url ? foundChild.json.content.url : foundChild.json.content.uri); + return this._tree.client.getTileContent(getUrl(foundChild.json.content)); + } + private addUrlPrefix(subTree: any, prefix: string) { + if (undefined === subTree) + return; + if (undefined !== subTree.content && undefined !== subTree.content.url) + subTree.content.url = prefix + subTree.content.url; + + if (undefined !== subTree.children) + for (const child of subTree.children) + this.addUrlPrefix(child, prefix); } - private async findTileInJson(tilesetJson: any, id: string, parentId: string): Promise { + private async findTileInJson(tilesetJson: any, id: string, parentId: string, transformToRoot?: Transform, isRoot: boolean = false): Promise { + if (!isRoot && tilesetJson.transform) { // Child tiles may have their own transform. + const thisTransform = RealityModelTileUtils.transformFromJson(tilesetJson.transform); + transformToRoot = transformToRoot ? transformToRoot.multiplyTransformTransform(thisTransform) : thisTransform; + } if (id.length === 0) - return new FindChildResult(id, tilesetJson); // Root. + return new FindChildResult(id, tilesetJson, transformToRoot); // Root. const separatorIndex = id.indexOf("_"); const childId = (separatorIndex < 0) ? id : id.substring(0, separatorIndex); const childIndex = parseInt(childId, 10); @@ -148,13 +164,21 @@ class RealityModelTileLoader extends TileLoader { let foundChild = tilesetJson.children[childIndex]; const thisParentId = parentId.length ? (parentId + "_" + childId) : childId; - if (separatorIndex >= 0) { return this.findTileInJson(foundChild, id.substring(separatorIndex + 1), thisParentId); } - if (undefined !== foundChild.content && foundChild.content.url.endsWith("json")) { // A child may contain a subTree... - const subTree = await this._tree.client.getTileJson(foundChild.json.content.url ? foundChild.json.content.url : foundChild.json.content.uri); + if (foundChild.transform) { + const thisTransform = RealityModelTileUtils.transformFromJson(foundChild.transform); + transformToRoot = transformToRoot ? transformToRoot.multiplyTransformTransform(thisTransform) : thisTransform; + } + if (separatorIndex >= 0) { return this.findTileInJson(foundChild, id.substring(separatorIndex + 1), thisParentId, transformToRoot); } + const childUrl = getUrl(foundChild.content); + if (undefined !== childUrl && childUrl.endsWith("json")) { // A child may contain a subTree... + const subTree = await this._tree.client.getTileJson(childUrl); + const prefixIndex = childUrl.lastIndexOf("/"); + if (prefixIndex > 0) + this.addUrlPrefix(subTree.root, childUrl.substring(0, prefixIndex + 1)); foundChild = subTree.root; tilesetJson.children[childIndex] = subTree.root; } - return new FindChildResult(thisParentId, foundChild); + return new FindChildResult(thisParentId, foundChild, transformToRoot); } } @@ -173,22 +197,16 @@ export class RealityModelTileTree { const tileClient = new RealityModelTileClient(url, IModelApp.accessToken); const json = await tileClient.getRootDocument(url); const ecefLocation = iModel.ecefLocation; - let rootTransform: Transform; - if (undefined !== json.root.transform) { - rootTransform = RealityModelTileUtils.transformFromJson(json.root.transform); - } else if (undefined !== json.root.boundingVolume && Array.isArray(json.root.boundingVolume.region)) { - rootTransform = RealityModelTileUtils.ecefTransformFromRegion(json.root.boundingVolume.region); - } else { - rootTransform = Transform.createIdentity(); - } - let tilesetToDb = Transform.createIdentity(); + let rootTransform = ecefLocation ? ecefLocation.getTransform().inverse()! : Transform.createIdentity(); + if (json.root.transform) + rootTransform = rootTransform.multiplyTransformTransform(RealityModelTileUtils.transformFromJson(json.root.transform)); + else if (json.root.boundingVolume && Array.isArray(json.root.boundingVolume.region)) + rootTransform = Transform.createTranslationXYZ(0, 0, (json.root.boundingVolume.region[4] + json.root.boundingVolume.region[5]) / 2.0).multiplyTransformTransform(rootTransform); - if (undefined !== tilesetToDbJson) { - tilesetToDb.setFromJSON(tilesetToDbJson); - } else if (ecefLocation !== undefined) { - tilesetToDb = ecefLocation.getTransform().inverse()!; - } - return new RealityModelTileTreeProps(json, tileClient, tilesetToDb.multiplyTransformTransform(rootTransform), ecefLocation); + if (undefined !== tilesetToDbJson) + rootTransform = Transform.fromJSON(tilesetToDbJson).multiplyTransformTransform(rootTransform); + + return new RealityModelTileTreeProps(json, tileClient, rootTransform); } else { throw new IModelError(BentleyStatus.ERROR, "Unable to read reality data"); } @@ -203,20 +221,41 @@ interface RDSClientProps { /** * ###TODO temporarly here for testing, needs to be moved to the clients repo * @hidden + * This class encapsulates access to a reality data wether it be from local access, http or RDS + * The url provided at the creation is parsed to determine if this is a RDS (ProjectWise Context Share) reference. + * If not then it is considered local (ex: C:\temp\TileRoot.json) or plain http access (http://someserver.com/data/TileRoot.json) + * There is a one to one relationship between a reality data and the instances of present class. */ export class RealityModelTileClient { - public readonly rdsProps?: RDSClientProps; - private _baseUrl: string = ""; - private readonly _token?: AccessToken; - private static _client = new RealityDataServicesClient(); + public readonly rdsProps?: RDSClientProps; // For reality data stored on PW Context Share only. If undefined then Reality Data is not on Context Share. + private _realityData?: RealityData; // For reality data stored on PW Context Share only. + private _baseUrl: string = ""; // For use by all Reality Data. For RD stored on PW Context Share, represents the portion from the root of the Azure Blob Container + private readonly _token?: AccessToken; // Only used for accessing PW Context Share. + private static _client = new RealityDataServicesClient(); // WSG Client for accessing Reality Data on PW Context Share // ###TODO we should be able to pass the projectId / tileId directly, instead of parsing the url + // But if the present can also be used by non PW Context Share stored data then the url is required and token is not. Possibly two classes inheriting from common interface. constructor(url: string, accessToken?: AccessToken) { - this.rdsProps = this.parseUrl(url); + this.rdsProps = this.parseUrl(url); // Note that returned is undefined if url does not refer to a PW Context Share reality data. this._token = accessToken; } + private async initializeRDSRealityData(alctx: ActivityLoggingContext): Promise { + if (undefined !== this.rdsProps && undefined !== this._token) { + if (!this._realityData) { + // TODO Temporary fix ... the root document may not be located at the root. We need to set the base URL even for RD stored on server + // though this base URL is only the part relative to the root of the blob contining the data. + this._realityData = await RealityModelTileClient._client.getRealityData(alctx, this._token, this.rdsProps.projectId, this.rdsProps.tilesId); + + // A reality data that has not root document set should not be considered. + const rootDocument: string = (this._realityData!.rootDocument ? this._realityData!.rootDocument as string : ""); + this.setBaseUrl(rootDocument); + } + } + } + // ###TODO temporary means of extracting the tileId and projectId from the given url + // This is the method that determines if the url refers to Reality Data stored on PW Context Share. If not then undefined is returned. private parseUrl(url: string): RDSClientProps | undefined { const urlParts = url.split("/").map((entry: string) => entry.replace(/%2D/g, "-")); const tilesId = urlParts.find(Guid.isGuid); @@ -234,40 +273,70 @@ export class RealityModelTileClient { return props; } - // this is only used for accessing locally served reality tiles. + // This is to set the root url fromt he provided root document path. + // If the root document is stored on PW Context Share then the root document property of the Reality Data is provided, + // otherwise the full path to root document is given. + // The base URL contains the base URL from which tile relative path are constructed. // The tile's path root will need to be reinserted for child tiles to return a 200 private setBaseUrl(url: string): void { const urlParts = url.split("/"); urlParts.pop(); - this._baseUrl = urlParts.join("/") + "/"; + if (urlParts.length === 0) + this._baseUrl = ""; + else + this._baseUrl = urlParts.join("/") + "/"; } + // ### TODO. Technically the url should not be required. If the reality data encapsulated is stored on PW Context Share then + // the relative path to root document is extracted from the reality data. Otherwise the full url to root document should have been provided at + // the construction of the instance. public async getRootDocument(url: string): Promise { const alctx = new ActivityLoggingContext(Guid.createValue()); + await this.initializeRDSRealityData(alctx); // Only needed for PW Context Share data ... return immediately otherwise. + if (undefined !== this.rdsProps && undefined !== this._token) - return RealityModelTileClient._client.getRootDocumentJson(alctx, this._token, this.rdsProps.projectId, this.rdsProps.tilesId); + return this._realityData!.getRootDocumentJson(alctx, this._token); + // The following is only if the reality data is not stored on PW Context Share. this.setBaseUrl(url); return getJson(alctx, url); } + /** + * Returns the tile content. The path to the tile is relative to the base url of present reality data whatever the type. + */ public async getTileContent(url: string): Promise { const alctx = new ActivityLoggingContext(Guid.createValue()); - if (undefined !== this.rdsProps && undefined !== this._token) - return RealityModelTileClient._client.getTileContent(alctx, this._token, this.rdsProps.projectId, this.rdsProps.tilesId, url); + + await this.initializeRDSRealityData(alctx); // Only needed for PW Context Share data ... return immediately otherwise. + + let tileUrl: string = url; if (undefined !== this._baseUrl) { - const tileUrl = this._baseUrl + url; + tileUrl = this._baseUrl + url; + + if (undefined !== this.rdsProps && undefined !== this._token) + return this._realityData!.getTileContent(alctx, this._token, tileUrl); + return getArrayBuffer(alctx, tileUrl); } throw new IModelError(BentleyStatus.ERROR, "Unable to determine reality data content url"); } + /** + * Returns the tile content in json format. The path to the tile is relative to the base url of present reality data whatever the type. + */ public async getTileJson(url: string): Promise { const alctx = new ActivityLoggingContext(Guid.createValue()); - if (undefined !== this.rdsProps && undefined !== this._token) - return RealityModelTileClient._client.getTileJson(alctx, this._token, this.rdsProps.projectId, this.rdsProps.tilesId, url); + + await this.initializeRDSRealityData(alctx); // Only needed for PW Context Share data ... return immediately otherwise. + + let tileUrl: string = url; if (undefined !== this._baseUrl) { - const tileUrl = this._baseUrl + url; + tileUrl = this._baseUrl + url; + + if (undefined !== this.rdsProps && undefined !== this._token) + return this._realityData!.getTileJson(alctx, this._token, tileUrl); + return getJson(alctx, tileUrl); } throw new IModelError(BentleyStatus.ERROR, "Unable to determine reality data json url"); diff --git a/core/frontend/src/tile/TileAdmin.ts b/core/frontend/src/tile/TileAdmin.ts index 4a9c22e..994b335 100644 --- a/core/frontend/src/tile/TileAdmin.ts +++ b/core/frontend/src/tile/TileAdmin.ts @@ -7,6 +7,7 @@ import { TileRequest } from "./TileRequest"; import { Tile } from "./TileTree"; import { Dictionary, SortedArray, PriorityQueue, assert } from "@bentley/bentleyjs-core"; +import { RpcOperation, IModelTileRpcInterface, TileTreeProps } from "@bentley/imodeljs-common"; import { Viewport } from "../Viewport"; import { IModelConnection } from "../IModelConnection"; @@ -32,6 +33,9 @@ export abstract class TileAdmin { public abstract set maxActiveRequests(max: number); public abstract get maxActiveRequests(): number; + /** @internal */ + public abstract get enableInstancing(): boolean; + /** Returns the union of the input set and the input viewport. */ public abstract getViewportSet(vp: Viewport, vps?: TileAdmin.ViewportSet): TileAdmin.ViewportSet; /** Invoked from the [[ToolAdmin]] event loop to process any pending or active requests for tiles. */ @@ -49,6 +53,9 @@ export abstract class TileAdmin { public abstract forgetViewport(vp: Viewport): void; public abstract onShutDown(): void; + public abstract async requestTileTreeProps(iModel: IModelConnection, treeId: string): Promise; + public abstract async requestTileContent(iModel: IModelConnection, treeId: string, contentId: string): Promise; + public static create(props?: TileAdmin.Props): TileAdmin { return new Admin(props); } @@ -83,6 +90,15 @@ export namespace TileAdmin { * @note If this is defined and true, `maxActiveRequests` is ignored. */ disableThrottling?: boolean; + + /** If true, tiles may represent repeated geometry as sets of instances. This can reduce tile size and tile generation time, and improve performance. + * + * Default value: false + */ + enableInstancing?: boolean; + + /** If defined, requests for tile content or tile tree properties will be memoized and retried at the specified interval in milliseconds */ + retryInterval?: number; } /** A set of [[Viewport]]s. @@ -214,12 +230,16 @@ class Admin extends TileAdmin { private readonly _uniqueViewportSets = new UniqueViewportSets(); private _maxActiveRequests: number; private readonly _throttle: boolean; + private readonly _retryInterval: number; + private readonly _enableInstancing: boolean; private readonly _removeIModelConnectionOnCloseListener: () => void; private _activeRequests = new Set(); private _swapActiveRequests = new Set(); private _pendingRequests = new Queue(); private _swapPendingRequests = new Queue(); private _numCanceled = 0; + private _retryIntervalInitialized = false; + private get _memoizeRequests() { return this._retryInterval > 0; } public get emptyViewportSet(): TileAdmin.ViewportSet { return this._uniqueViewportSets.emptySet; } public get statistics(): TileAdmin.Statistics { @@ -238,10 +258,14 @@ class Admin extends TileAdmin { this._throttle = !options.disableThrottling; this._maxActiveRequests = undefined !== options.maxActiveRequests ? options.maxActiveRequests : 10; + this._retryInterval = undefined !== options.retryInterval ? options.retryInterval : 0; + this._enableInstancing = !!options.enableInstancing; this._removeIModelConnectionOnCloseListener = IModelConnection.onClose.addListener((iModel) => this.onIModelClosed(iModel)); } + public get enableInstancing() { return this._enableInstancing; } + public get maxActiveRequests() { return this._maxActiveRequests; } public set maxActiveRequests(max: number) { if (max > 0) @@ -298,7 +322,9 @@ class Admin extends TileAdmin { private processRequests(vp: Viewport, tiles: Set): void { for (const tile of tiles) { if (undefined === tile.request) { - assert(tile.loadStatus === Tile.LoadStatus.NotLoaded); + // ###TODO: This assertion triggers for AttachmentViewports used for rendering 3d sheet attachments. + // Determine why and fix. + // assert(tile.loadStatus === Tile.LoadStatus.NotLoaded); if (Tile.LoadStatus.NotLoaded === tile.loadStatus) { const request = new TileRequest(tile, vp); tile.request = request; @@ -386,4 +412,29 @@ class Admin extends TileAdmin { public getViewportSet(vp: Viewport, vps?: TileAdmin.ViewportSet): TileAdmin.ViewportSet { return this._uniqueViewportSets.getViewportSet(vp, vps); } + + public async requestTileTreeProps(iModel: IModelConnection, treeId: string): Promise { + this.initializeRetryInterval(); + const intfc = IModelTileRpcInterface.getClient(); + return this._memoizeRequests ? intfc.requestTileTreeProps(iModel.iModelToken, treeId) : intfc.getTileTreeProps(iModel.iModelToken, treeId); + } + + public async requestTileContent(iModel: IModelConnection, treeId: string, contentId: string): Promise { + this.initializeRetryInterval(); + const intfc = IModelTileRpcInterface.getClient(); + return this._memoizeRequests ? intfc.requestTileContent(iModel.iModelToken, treeId, contentId) : intfc.getTileContent(iModel.iModelToken, treeId, contentId); + } + + private initializeRetryInterval(): void { + // Would prefer to do this in constructor - but nothing enforces that the app initializes the rpc interfaces before it creates the TileAdmin (via IModelApp.startup()) - so do it on first request instead. + if (!this._retryIntervalInitialized) { + this._retryIntervalInitialized = true; + + if (this._memoizeRequests) { + const retryInterval = this._retryInterval; + RpcOperation.lookup(IModelTileRpcInterface, "requestTileTreeProps").policy.retryInterval = () => retryInterval; + RpcOperation.lookup(IModelTileRpcInterface, "requestTileContent").policy.retryInterval = () => retryInterval; + } + } + } } diff --git a/core/frontend/src/tile/TileIO.ts b/core/frontend/src/tile/TileIO.ts index ba4f427..10c1e1c 100644 --- a/core/frontend/src/tile/TileIO.ts +++ b/core/frontend/src/tile/TileIO.ts @@ -18,6 +18,7 @@ export namespace TileIO { InvalidBatchTable, InvalidScene, InvalidFeatureTable, + NewerMajorVersion, Canceled, } @@ -26,9 +27,10 @@ export namespace TileIO { Unknown = 0, B3dm = 0x6d643362, // "b3dm" Gltf = 0x46546c67, // "glTF" - Dgn = 0x546e6764, // "dgnT" Pnts = 0x73746e70, // "pnts" IModel = 0x6c644d69, // "iMdl" + Cmpt = 0x74706d63, // cmpt + I3dm = 0x6d643369, // i3dm } /** Given a magic number, return whether it identifies a known tile format. */ @@ -38,8 +40,9 @@ export namespace TileIO { case Format.B3dm: case Format.Gltf: case Format.IModel: - case Format.Dgn: case Format.Pnts: + case Format.Cmpt: + case Format.I3dm: return true; default: return false; @@ -128,7 +131,7 @@ export namespace TileIO { */ export abstract class Header { private _format: Format; - public readonly version: number; + public version: number; /** Construct a Header from the binary data at the supplied stream's current read position */ public constructor(stream: StreamBuffer) { diff --git a/core/frontend/src/tile/TileRequest.ts b/core/frontend/src/tile/TileRequest.ts index 7c4acba..2e11316 100644 --- a/core/frontend/src/tile/TileRequest.ts +++ b/core/frontend/src/tile/TileRequest.ts @@ -5,7 +5,7 @@ /** @module Tile */ import { assert, base64StringToUint8Array } from "@bentley/bentleyjs-core"; -import { ImageSource, ElementAlignedBox3d } from "@bentley/imodeljs-common"; +import { ImageSource, ElementAlignedBox3d, ServerTimeoutError } from "@bentley/imodeljs-common"; import { RenderGraphic } from "../render/System"; import { Tile, TileTree, TileLoader } from "./TileTree"; import { TileAdmin } from "./TileAdmin"; @@ -57,7 +57,15 @@ export class TileRequest { return this.handleResponse(response); } catch (_err) { - this.setFailed(); + if (_err instanceof ServerTimeoutError) { + // Invalidate scene - if tile is re-selected, it will be re-requested. + this.notifyAndClear(); + this._state = TileRequest.State.Failed; + } else { + // Unknown error - not retryable. + this.setFailed(); + } + return Promise.resolve(); } } @@ -108,7 +116,7 @@ export class TileRequest { try { const graphic = await this.loader.loadTileGraphic(this.tile, data); if (this.isCanceled) - return Promise.resolve(); + return Promise.resolve(); this._state = TileRequest.State.Completed; this.tile.setGraphic(graphic.renderGraphic, graphic.isLeaf, graphic.contentRange, graphic.sizeMultiplier); diff --git a/core/frontend/src/tile/TileTree.ts b/core/frontend/src/tile/TileTree.ts index d929795..a3e0a79 100644 --- a/core/frontend/src/tile/TileTree.ts +++ b/core/frontend/src/tile/TileTree.ts @@ -4,42 +4,22 @@ *--------------------------------------------------------------------------------------------*/ /** @module Tile */ -import { - assert, - Id64, - Id64String, - BeTimePoint, - BeDuration, - JsonUtils, - dispose, - IDisposable, - base64StringToUint8Array, -} from "@bentley/bentleyjs-core"; -import { - ElementAlignedBox3d, - ViewFlag, - ViewFlags, - RenderMode, - Frustum, - FrustumPlanes, - TileProps, - TileTreeProps, - ColorDef, - BatchType, -} from "@bentley/imodeljs-common"; -import { Range3d, Point3d, Transform, ClipVector, ClipPlaneContainment } from "@bentley/geometry-core"; -import { SceneContext } from "../ViewContext"; -import { RenderGraphic, GraphicBranch, RenderMemory } from "../render/System"; -import { IModelConnection } from "../IModelConnection"; +import { assert, BeDuration, BeTimePoint, dispose, Id64, Id64String, IDisposable, JsonUtils } from "@bentley/bentleyjs-core"; +import { ClipPlaneContainment, ClipVector, Point3d, Range3d, Transform } from "@bentley/geometry-core"; +import { BatchType, ColorDef, ElementAlignedBox3d, Frustum, FrustumPlanes, RenderMode, TileProps, TileTreeProps, ViewFlag, ViewFlags, BoundingSphere } from "@bentley/imodeljs-common"; import { IModelApp } from "../IModelApp"; -import { TileIO } from "./TileIO"; -import { TileRequest } from "./TileRequest"; -import { GltfTileIO } from "./GltfTileIO"; +import { IModelConnection } from "../IModelConnection"; +import { GraphicBranch, RenderGraphic, RenderMemory } from "../render/System"; +import { SceneContext } from "../ViewContext"; +import { ViewFrustum } from "../Viewport"; import { B3dmTileIO } from "./B3dmTileIO"; -import { PntsTileIO } from "./PntsTileIO"; -import { DgnTileIO } from "./DgnTileIO"; +import { CompositeTileIO } from "./CompositeTileIO"; +import { GltfTileIO } from "./GltfTileIO"; +import { I3dmTileIO } from "./I3dmTileIO"; import { IModelTileIO } from "./IModelTileIO"; -import { ViewFrustum } from "../Viewport"; +import { PntsTileIO } from "./PntsTileIO"; +import { TileIO } from "./TileIO"; +import { TileRequest } from "./TileRequest"; /** @hidden */ export class Tile implements IDisposable, RenderMemory.Consumer { @@ -50,6 +30,7 @@ export class Tile implements IDisposable, RenderMemory.Consumer { public contentId: string; public readonly center: Point3d; public readonly radius: number; + public readonly transformToRoot?: Transform; protected _maximumSize: number; protected _isLeaf: boolean; protected _childrenLastUsed: BeTimePoint; @@ -61,6 +42,9 @@ export class Tile implements IDisposable, RenderMemory.Consumer { protected _rangeGraphicType: Tile.DebugBoundingBoxes = Tile.DebugBoundingBoxes.None; protected _sizeMultiplier?: number; protected _request?: TileRequest; + protected _transformToRoot?: Transform; + protected _localRange?: ElementAlignedBox3d; + protected _localContentRange?: ElementAlignedBox3d; private _state: TileState; public constructor(props: Tile.Params) { @@ -75,7 +59,14 @@ export class Tile implements IDisposable, RenderMemory.Consumer { this._childrenLastUsed = BeTimePoint.now(); this._contentRange = props.contentRange; this._sizeMultiplier = props.sizeMultiplier; - + if (undefined !== (this.transformToRoot = props.transformToRoot)) { + this.transformToRoot.multiplyRange(props.range, this.range); + this._localRange = this.range; + if (undefined !== props.contentRange) { + this.transformToRoot.multiplyRange(props.contentRange, this._contentRange); + this._localContentRange = props.contentRange; + } + } if (!this.root.loader.tileRequiresLoading(props)) { this.setIsReady(); // If no contents, this node is for structure only and no content loading is required. } @@ -126,11 +117,11 @@ export class Tile implements IDisposable, RenderMemory.Consumer { if (undefined === this.request) return Tile.LoadStatus.NotLoaded; else if (TileRequest.State.Loading === this.request.state) - return Tile.LoadStatus.Loading; + return Tile.LoadStatus.Loading; assert(TileRequest.State.Completed !== this.request.state && TileRequest.State.Failed !== this.request.state); // this.request should be undefined in these cases... return Tile.LoadStatus.Queued; - } + } case TileState.Ready: { assert(undefined === this.request); return Tile.LoadStatus.Ready; @@ -207,7 +198,7 @@ export class Tile implements IDisposable, RenderMemory.Consumer { public get loader(): TileLoader { return this.root.loader; } public get hasContentRange(): boolean { return undefined !== this._contentRange; } - public isRegionCulled(args: Tile.DrawArgs): boolean { return this.isCulled(this.range, args); } + public isRegionCulled(args: Tile.DrawArgs): boolean { return Tile._scratchRootSphere.init(this.center, this.radius), this.isCulled(this.range, args, Tile._scratchRootSphere); } public isContentCulled(args: Tile.DrawArgs): boolean { return this.isCulled(this.contentRange, args); } private getRangeGraphic(context: SceneContext): RenderGraphic | undefined { @@ -219,10 +210,20 @@ export class Tile implements IDisposable, RenderMemory.Consumer { this._rangeGraphic = dispose(this._rangeGraphic); if (Tile.DebugBoundingBoxes.None !== type) { const builder = context.createSceneGraphicBuilder(); - const color = this.hasSizeMultiplier ? ColorDef.red : (this.isLeaf ? ColorDef.blue : ColorDef.green); - builder.setSymbology(color, color, 1); - const range = Tile.DebugBoundingBoxes.Content === type ? this.contentRange : this.range; - builder.addRangeBox(range); + if (Tile.DebugBoundingBoxes.Both === type) { + builder.setSymbology(ColorDef.blue, ColorDef.blue, 1); + builder.addRangeBox(this.range); + if (this.hasContentRange) { + builder.setSymbology(ColorDef.red, ColorDef.red, 1); + builder.addRangeBox(this.contentRange); + } + } else { + const color = this.hasSizeMultiplier ? ColorDef.red : (this.isLeaf ? ColorDef.blue : ColorDef.green); + builder.setSymbology(color, color, 1); + const range = Tile.DebugBoundingBoxes.Content === type ? this.contentRange : this.range; + builder.addRangeBox(range); + } + this._rangeGraphic = builder.finish(); } @@ -231,7 +232,7 @@ export class Tile implements IDisposable, RenderMemory.Consumer { /** Returns the range of this tile's contents in world coordinates. */ public computeWorldContentRange(): ElementAlignedBox3d { - const range = new ElementAlignedBox3d(); + const range = new Range3d(); if (!this.contentRange.isNull) this.root.location.multiplyRange(this.contentRange, range); @@ -406,10 +407,13 @@ export class Tile implements IDisposable, RenderMemory.Consumer { private static _scratchWorldFrustum = new Frustum(); private static _scratchRootFrustum = new Frustum(); - private isCulled(range: ElementAlignedBox3d, args: Tile.DrawArgs) { + private static _scratchWorldSphere = new BoundingSphere(); + private static _scratchRootSphere = new BoundingSphere(); + private isCulled(range: ElementAlignedBox3d, args: Tile.DrawArgs, sphere?: BoundingSphere) { const box = Frustum.fromRange(range, Tile._scratchRootFrustum); const worldBox = box.transformBy(args.location, Tile._scratchWorldFrustum); - const isOutside = FrustumPlanes.Containment.Outside === args.frustumPlanes.computeFrustumContainment(worldBox); + const worldSphere = sphere ? sphere.transformBy(args.location, Tile._scratchWorldSphere) : undefined; + const isOutside = FrustumPlanes.Containment.Outside === args.frustumPlanes.computeFrustumContainment(worldBox, worldSphere); const isClipped = !isOutside && undefined !== args.clip && ClipPlaneContainment.StronglyOutside === args.clip.classifyPointContainment(box.points); const isCulled = isOutside || isClipped; return isCulled; @@ -423,9 +427,8 @@ export class Tile implements IDisposable, RenderMemory.Consumer { this._childrenLoadStatus = TileTree.LoadStatus.Loaded; if (undefined !== props) { // If this tile is undisplayable, update its content range based on children's content ranges. - const parentRange = this.hasContentRange ? undefined : new ElementAlignedBox3d(); + const parentRange = this.hasContentRange ? undefined : new Range3d(); for (const prop of props) { - // ###TODO if child is empty don't bother adding it to list... const child = new Tile(Tile.Params.fromJSON(prop, this.root, this)); // stick the corners on the Tile (used only by WebMercator Tiles) @@ -540,6 +543,8 @@ export namespace Tile { Volume, /** Display boxes representing the range of the tile's contents, which may be tighter than (but never larger than) the tile's full volume. */ Content, + /** Display both volume and content boxes. */ + Both, } /** @@ -583,7 +588,7 @@ export namespace Tile { private static _scratchRange = new Range3d(); public getTileRadius(tile: Tile): number { - let range = tile.range.clone(DrawArgs._scratchRange); + let range: Range3d = tile.range.clone(DrawArgs._scratchRange); range = this.location.multiplyRange(range, range); return 0.5 * (tile.root.is3d ? range.low.distance(range.high) : range.low.distanceXY(range.high)); } @@ -617,11 +622,13 @@ export namespace Tile { public readonly isLeaf?: boolean, public readonly parent?: Tile, public readonly contentRange?: ElementAlignedBox3d, + public readonly transformToRoot?: Transform, public readonly sizeMultiplier?: number) { } public static fromJSON(props: TileProps, root: TileTree, parent?: Tile) { - const contentRange = undefined !== props.contentRange ? ElementAlignedBox3d.fromJSON(props.contentRange) : undefined; - return new Params(root, props.contentId, ElementAlignedBox3d.fromJSON(props.range), props.maximumSize, props.isLeaf, parent, contentRange, props.sizeMultiplier); + const contentRange = undefined !== props.contentRange ? Range3d.fromJSON(props.contentRange) : undefined; + const transformToRoot = undefined !== props.transformToRoot ? Transform.fromJSON(props.transformToRoot) : undefined; + return new Params(root, props.contentId, Range3d.fromJSON(props.range), props.maximumSize, props.isLeaf, parent, contentRange, transformToRoot, props.sizeMultiplier); } } } @@ -675,7 +682,7 @@ export class TileTree implements IDisposable, RenderMemory.Consumer { } public get is2d(): boolean { return !this.is3d; } - public get range(): ElementAlignedBox3d { return this._rootTile !== undefined ? this._rootTile.range : new ElementAlignedBox3d(); } + public get range(): ElementAlignedBox3d { return this._rootTile !== undefined ? this._rootTile.range : new Range3d(); } public selectTilesForScene(context: SceneContext): Tile[] { return this.selectTiles(this.createDrawArgs(context)); } public selectTiles(args: Tile.DrawArgs): Tile[] { @@ -720,6 +727,7 @@ export abstract class TileLoader { public abstract get maxDepth(): number; public abstract get priority(): Tile.LoadPriority; protected get _batchType(): BatchType { return BatchType.Primary; } + protected get _loadEdges(): boolean { return true; } public abstract tileRequiresLoading(params: Tile.Params): boolean; /** Given two tiles of the same [[Tile.LoadPriority]], determine which should be prioritized. * A negative value indicates lhs should load first, positive indicates rhs should load first, and zero indicates no distinction in priority. @@ -734,10 +742,14 @@ export abstract class TileLoader { public async loadTileGraphic(tile: Tile, data: TileRequest.ResponseData, isCanceled?: () => boolean): Promise { assert(data instanceof Uint8Array); const blob = data as Uint8Array; - const streamBuffer: TileIO.StreamBuffer = new TileIO.StreamBuffer(blob.buffer); + return this.loadTileGraphicFromStream(tile, streamBuffer, isCanceled); + } + public async loadTileGraphicFromStream(tile: Tile, streamBuffer: TileIO.StreamBuffer, isCanceled?: () => boolean): Promise { + + const position = streamBuffer.curPos; const format = streamBuffer.nextUint32; - streamBuffer.rewind(4); + streamBuffer.curPos = position; if (undefined === isCanceled) isCanceled = () => !tile.isLoading; @@ -748,16 +760,29 @@ export abstract class TileLoader { return { renderGraphic: PntsTileIO.readPointCloud(streamBuffer, tile.root.iModel, tile.root.modelId, tile.root.is3d, tile.range, IModelApp.renderSystem, tile.yAxisUp) }; case TileIO.Format.B3dm: - reader = B3dmTileIO.Reader.create(streamBuffer, tile.root.iModel, tile.root.modelId, tile.root.is3d, tile.range, IModelApp.renderSystem, tile.yAxisUp, tile.isLeaf, isCanceled); - break; - - case TileIO.Format.Dgn: - reader = DgnTileIO.Reader.create(streamBuffer, tile.root.iModel, tile.root.modelId, tile.root.is3d, IModelApp.renderSystem, this._batchType, isCanceled); + reader = B3dmTileIO.Reader.create(streamBuffer, tile.root.iModel, tile.root.modelId, tile.root.is3d, tile.range, IModelApp.renderSystem, tile.yAxisUp, tile.isLeaf, tile.transformToRoot, isCanceled); break; - case TileIO.Format.IModel: - reader = IModelTileIO.Reader.create(streamBuffer, tile.root.iModel, tile.root.modelId, tile.root.is3d, IModelApp.renderSystem, this._batchType, isCanceled, tile.hasSizeMultiplier ? tile.sizeMultiplier : undefined); + reader = IModelTileIO.Reader.create(streamBuffer, tile.root.iModel, tile.root.modelId, tile.root.is3d, IModelApp.renderSystem, this._batchType, this._loadEdges, isCanceled, tile.hasSizeMultiplier ? tile.sizeMultiplier : undefined); + break; + case TileIO.Format.I3dm: + reader = I3dmTileIO.Reader.create(streamBuffer, tile.root.iModel, tile.root.modelId, tile.root.is3d, tile.range, IModelApp.renderSystem, tile.yAxisUp, tile.isLeaf, isCanceled); break; + case TileIO.Format.Cmpt: + const header = new CompositeTileIO.Header(streamBuffer); + if (!header.isValid) return {}; + const branch = new GraphicBranch(); + for (let i = 0; i < header.tileCount; i++) { + const tilePosition = streamBuffer.curPos; + streamBuffer.advance(8); // Skip magic and version. + const tileBytes = streamBuffer.nextUint32; + streamBuffer.curPos = tilePosition; + const result = await this.loadTileGraphicFromStream(tile, streamBuffer, isCanceled); + if (result.renderGraphic) + branch.add(result.renderGraphic); + streamBuffer.curPos = tilePosition + tileBytes; + } + return { renderGraphic: branch.isEmpty ? undefined : IModelApp.renderSystem.createBranch(branch, Transform.createIdentity()), isLeaf: tile.isLeaf, sizeMultiplier: tile.sizeMultiplier }; default: assert(false, "unknown tile format " + format); @@ -775,61 +800,6 @@ export abstract class TileLoader { return graphic; } - public loadGraphics(tile: Tile, geometry: any): void { - let blob: Uint8Array | undefined; - if (typeof geometry === "string") { - blob = base64StringToUint8Array(geometry as string); - } else if (geometry instanceof Uint8Array) { - blob = geometry; - } else if (geometry instanceof ArrayBuffer) { - blob = new Uint8Array(geometry as ArrayBuffer); - } else { - tile.setIsReady(); - return; - } - - const streamBuffer: TileIO.StreamBuffer = new TileIO.StreamBuffer(blob.buffer); - const format = streamBuffer.nextUint32; - const isCanceled = () => !tile.isLoading; - let reader: GltfTileIO.Reader | undefined; - streamBuffer.rewind(4); - switch (format) { - case TileIO.Format.B3dm: - reader = B3dmTileIO.Reader.create(streamBuffer, tile.root.iModel, tile.root.modelId, tile.root.is3d, tile.range, IModelApp.renderSystem, tile.yAxisUp, tile.isLeaf, isCanceled); - break; - - case TileIO.Format.Dgn: - reader = DgnTileIO.Reader.create(streamBuffer, tile.root.iModel, tile.root.modelId, tile.root.is3d, IModelApp.renderSystem, this._batchType, isCanceled); - break; - - case TileIO.Format.IModel: - reader = IModelTileIO.Reader.create(streamBuffer, tile.root.iModel, tile.root.modelId, tile.root.is3d, IModelApp.renderSystem, this._batchType, isCanceled, tile.hasSizeMultiplier ? tile.sizeMultiplier : undefined); - break; - - case TileIO.Format.Pnts: - tile.setGraphic(PntsTileIO.readPointCloud(streamBuffer, tile.root.iModel, tile.root.modelId, tile.root.is3d, tile.range, IModelApp.renderSystem, tile.yAxisUp)); - return; - - default: - assert(false, "unknown tile format " + format); - break; - } - - if (undefined === reader) { - tile.setNotFound(); - return; - } - - const read = reader.read(); - read.catch((_err) => tile.setNotFound()); - read.then((result) => { // tslint:disable-line:no-floating-promises - // Make sure we still want this tile - may been unloaded, imodel may have been closed, IModelApp may have shut down taking render system with it, etc. - if (tile.isLoading) { - tile.setGraphic(result.renderGraphic, result.isLeaf, result.contentRange, result.sizeMultiplier); - IModelApp.viewManager.onNewTilesReady(); - } - }); - } public get viewFlagOverrides(): ViewFlag.Overrides { return defaultViewFlagOverrides; } } @@ -858,12 +828,15 @@ function bisectRange2d(range: Range3d, takeUpper: boolean): void { export class IModelTileLoader extends TileLoader { private _iModel: IModelConnection; private _type: BatchType; + private _edgesRequired: boolean; protected get _batchType() { return this._type; } + protected get _loadEdges(): boolean { return this._edgesRequired; } - public constructor(iModel: IModelConnection, batchType: BatchType) { + public constructor(iModel: IModelConnection, batchType: BatchType, edgesRequired: boolean = true) { super(); this._iModel = iModel; this._type = batchType; + this._edgesRequired = edgesRequired; } public get maxDepth(): number { return 32; } // Can be removed when element tile selector is working. @@ -976,11 +949,25 @@ export namespace TileTree { export class TileTreeState { public tileTree?: TileTree; public loadStatus: TileTree.LoadStatus = TileTree.LoadStatus.NotLoaded; + public edgesOmitted: boolean = false; public get iModel() { return this._iModel; } constructor(private _iModel: IModelConnection, private _is3d: boolean, private _modelId: Id64String) { } public setTileTree(props: TileTreeProps, loader: TileLoader) { - this.tileTree = new TileTree(TileTree.Params.fromJSON(props, this._iModel, this._is3d, loader, this._modelId)); - this.loadStatus = TileTree.LoadStatus.Loaded; + const tileTree = new TileTree(TileTree.Params.fromJSON(props, this._iModel, this._is3d, loader, this._modelId)); + if (tileTree.rootTile.contentRange.isNull) { + // No elements within model's range - don't create a TileTree for this model. + assert(tileTree.rootTile.isLeaf); + this.loadStatus = TileTree.LoadStatus.NotFound; + } else { + this.tileTree = tileTree; + this.loadStatus = TileTree.LoadStatus.Loaded; + } + + } + public clearTileTree() { + dispose(this.tileTree); + this.tileTree = undefined; + this.loadStatus = TileTree.LoadStatus.NotLoaded; } } diff --git a/core/frontend/src/tile/WebMercatorTileTree.ts b/core/frontend/src/tile/WebMercatorTileTree.ts index d624639..9ecbfef 100644 --- a/core/frontend/src/tile/WebMercatorTileTree.ts +++ b/core/frontend/src/tile/WebMercatorTileTree.ts @@ -7,7 +7,7 @@ import { assert, ActivityLoggingContext, BentleyError, IModelStatus, JsonUtils } from "@bentley/bentleyjs-core"; import { TileTreeProps, TileProps, Cartographic, ImageSource, ImageSourceFormat, RenderTexture, EcefLocation, - BackgroundMapType, BackgroundMapProps, IModelCoordinatesResponseProps, + BackgroundMapType, BackgroundMapProps, IModelCoordinatesResponseProps, GeoCoordStatus, } from "@bentley/imodeljs-common"; import { Range3dProps, Range3d, TransformProps, Transform, Point3d, Point2d, Range2d, Vector3d, Angle, Plane3dByOriginAndUnitNormal, XYZProps } from "@bentley/geometry-core"; import { TileLoader, TileTree, Tile } from "./TileTree"; @@ -158,7 +158,6 @@ class GeoTransformChildCreator implements ChildCreator { // send to the backend to get the XY coordinates. const response: IModelCoordinatesResponseProps = await this._converter.getIModelCoordinatesFromGeoCoordinates(requestProps); - // tslint:disable:no-console // get the tileProps now that we have their geoCoords. const tileProps: WebMercatorTileProps[] = []; const level = parentLevel + 1; @@ -265,9 +264,9 @@ class WebMercatorTileLoader extends TileLoader { private _providerInitialized: boolean = false; private _childTileCreator: ChildCreator; - constructor(private _imageryProvider: ImageryProvider, private _iModel: IModelConnection, groundBias: number) { + constructor(private _imageryProvider: ImageryProvider, private _iModel: IModelConnection, groundBias: number, gcsConverterAvailable: boolean) { super(); - const useLinearTransform: boolean = WebMercatorTileLoader.selectChildCreator(_iModel); + const useLinearTransform: boolean = !gcsConverterAvailable || WebMercatorTileLoader.selectLinearChildCreator(_iModel); if (useLinearTransform) { this._childTileCreator = new LinearTransformChildCreator(_iModel, groundBias); } else { @@ -275,7 +274,7 @@ class WebMercatorTileLoader extends TileLoader { } } - private static selectChildCreator(_iModel: IModelConnection) { + private static selectLinearChildCreator(_iModel: IModelConnection) { const linearRangeSquared: number = _iModel.projectExtents.diagonal().magnitudeSquared(); return linearRangeSquared < 1000.0 * 1000.00; // if the range is greater than a kilometer, use the more exact but slower GCS method of generating the WebMercator tile corners. } @@ -697,6 +696,7 @@ class MapBoxProvider extends ImageryProvider { public async initialize(): Promise { } } +const enum GcsConverterStatus { Uninitialized, Pending, NotAvailable, Available } /** @hidden */ export class BackgroundMapState { private _tileTree?: TileTree; @@ -705,6 +705,7 @@ export class BackgroundMapState { public providerName: string; private _groundBias: number; public mapType: BackgroundMapType; + private _gcsConverterStatus: GcsConverterStatus = GcsConverterStatus.Uninitialized; public setTileTree(props: TileTreeProps, loader: TileLoader) { this._tileTree = new TileTree(TileTree.Params.fromJSON(props, this._iModel, true, loader, "")); @@ -731,6 +732,20 @@ export class BackgroundMapState { this.mapType = json.providerData ? JsonUtils.asInt(json.providerData.mapType, BackgroundMapType.Hybrid) : BackgroundMapType.Hybrid; } + private testGcsConverter() { + this._gcsConverterStatus = GcsConverterStatus.Pending; + const converter = this._iModel.geoServices.getConverter("WGS84"); + const requestProps = new Array(1); + requestProps[0] = { x: 0, y: 0, z: 0 }; + converter.getIModelCoordinatesFromGeoCoordinates(requestProps).then((responseProps) => { + this._gcsConverterStatus = (responseProps.iModelCoords.length !== 1 || responseProps.iModelCoords[0].s === GeoCoordStatus.NoGCSDefined) ? GcsConverterStatus.NotAvailable : GcsConverterStatus.Available; + IModelApp.viewManager.onNewTilesReady(); + }).catch((_) => { + this._gcsConverterStatus = GcsConverterStatus.NotAvailable; + IModelApp.viewManager.onNewTilesReady(); + }); + } + private loadTileTree(): TileTree.LoadStatus { if (TileTree.LoadStatus.NotLoaded !== this._loadStatus) return this._loadStatus; @@ -738,6 +753,11 @@ export class BackgroundMapState { if (this._iModel.ecefLocation === undefined) { return this._loadStatus; } + if (GcsConverterStatus.Uninitialized === this._gcsConverterStatus) + this.testGcsConverter(); + + if (GcsConverterStatus.Pending === this._gcsConverterStatus) + return this._loadStatus; if ("BingProvider" === this.providerName) { this._provider = new BingMapProvider(this.mapType); @@ -747,7 +767,7 @@ export class BackgroundMapState { if (this._provider === undefined) throw new BentleyError(IModelStatus.BadModel, "WebMercator provider invalid"); - const loader = new WebMercatorTileLoader(this._provider, this._iModel, this._groundBias); + const loader = new WebMercatorTileLoader(this._provider, this._iModel, this._groundBias, this._gcsConverterStatus === GcsConverterStatus.Available); const tileTreeProps = new WebMercatorTileTreeProps(); this.setTileTree(tileTreeProps, loader); return this._loadStatus; diff --git a/ui/framework/src/test/shared/IconLabelSupport.test.tsx b/core/frontend/src/tile/draco3d.d.ts similarity index 70% rename from ui/framework/src/test/shared/IconLabelSupport.test.tsx rename to core/frontend/src/tile/draco3d.d.ts index 382a554..c11d53d 100644 --- a/ui/framework/src/test/shared/IconLabelSupport.test.tsx +++ b/core/frontend/src/tile/draco3d.d.ts @@ -2,12 +2,10 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import TestUtils from "../TestUtils"; -describe("IconLabelProps", () => { +declare module 'draco3d' { - before(async () => { - await TestUtils.initializeUiFramework(); - }); + export function createDecoderModule(DracoDecoderModule: any, ...args: any[]): any; + +} -}); diff --git a/core/frontend/src/tools/AccuDrawTool.ts b/core/frontend/src/tools/AccuDrawTool.ts index c3dd59e..d755fb1 100644 --- a/core/frontend/src/tools/AccuDrawTool.ts +++ b/core/frontend/src/tools/AccuDrawTool.ts @@ -760,7 +760,7 @@ export class AccuDrawShortcuts { accudraw.flags.auxRotationPlane = RotationMode.Top; const currentACS = vp.view.auxiliaryCoordinateSystem; - const acs = currentACS.clone(); + const acs = currentACS.clone(); acs.setRotation(accudraw.getRotation()); AccuDraw.updateAuxCoordinateSystem(acs, vp); @@ -926,7 +926,7 @@ export class AccuDrawShortcuts { accudraw.axes.setFrom(axes); if (RotationMode.ACS === accudraw.flags.baseRotation) { - const acs = currentACS.clone(); + const acs = currentACS.clone(); const rMatrix = accudraw.getRotation(); acs.setRotation(rMatrix); @@ -1103,7 +1103,7 @@ class RotateElementTool extends AccuDrawTool { AccuDrawShortcuts.processPendingHints(); const currentACS = vp.view.auxiliaryCoordinateSystem; - const acs = currentACS.clone(); + const acs = currentACS.clone(); acs.setOrigin(accudraw.origin); acs.setRotation(accudraw.getRotation()); @@ -1123,7 +1123,7 @@ class RotateElementTool extends AccuDrawTool { const accudraw = IModelApp.accuDraw; const origin = accudraw.origin; const rMatrix = accudraw.getRotation(); - const acs = context.viewport!.view.auxiliaryCoordinateSystem.clone(); + const acs = context.viewport!.view.auxiliaryCoordinateSystem.clone(); acs.setOrigin(origin); acs.setRotation(rMatrix); acs.display(context, ACSDisplayOptions.Active | ACSDisplayOptions.Dynamics); @@ -1166,7 +1166,7 @@ class DefineACSByPointsTool extends AccuDrawTool { const vp = ev.viewport; if (!this._acs) - this._acs = vp.view.auxiliaryCoordinateSystem.clone(); + this._acs = vp.view.auxiliaryCoordinateSystem.clone(); if (AccuDrawShortcuts.updateACSByPoints(this._acs, vp, this._points, false)) { AccuDraw.updateAuxCoordinateSystem(this._acs, vp); @@ -1188,7 +1188,7 @@ class DefineACSByPointsTool extends AccuDrawTool { const vp = context.viewport!; if (!this._acs) - this._acs = vp.view.auxiliaryCoordinateSystem.clone(); + this._acs = vp.view.auxiliaryCoordinateSystem.clone(); AccuDrawShortcuts.updateACSByPoints(this._acs, vp, tmpPoints, true); this._acs.display(context, ACSDisplayOptions.Active | ACSDisplayOptions.Dynamics); diff --git a/core/frontend/src/tools/EventController.ts b/core/frontend/src/tools/EventController.ts index 8372a68..ec992ab 100644 --- a/core/frontend/src/tools/EventController.ts +++ b/core/frontend/src/tools/EventController.ts @@ -18,7 +18,7 @@ export class EventController { private readonly _removals: VoidFunction[] = []; constructor(public vp: ScreenViewport) { - const element = vp.canvas; + const element = vp.parentDiv; if (element === undefined) return; diff --git a/core/frontend/src/tools/IdleTool.ts b/core/frontend/src/tools/IdleTool.ts index 616f412..72cd441 100644 --- a/core/frontend/src/tools/IdleTool.ts +++ b/core/frontend/src/tools/IdleTool.ts @@ -142,7 +142,7 @@ export class IdleTool extends InteractiveTool { public async onTouchMoveStart(ev: BeTouchEvent, startEv: BeTouchEvent): Promise { const tool = new DefaultViewTouchTool(startEv, ev); - return (tool.run() ? EventHandled.Yes : EventHandled.No); + return tool.run() ? EventHandled.Yes : EventHandled.No; } public async onTouchTap(ev: BeTouchEvent): Promise { @@ -157,7 +157,7 @@ export class IdleTool extends InteractiveTool { } else if (ev.isDoubleTap) { // Fit view on single finger double tap. const tool = new FitViewTool(ev.viewport!, true); - return (tool.run() ? EventHandled.Yes : EventHandled.No); + return tool.run() ? EventHandled.Yes : EventHandled.No; } return EventHandled.No; } diff --git a/core/frontend/src/tools/MeasureTool.ts b/core/frontend/src/tools/MeasureTool.ts index 5ead29c..76e195f 100644 --- a/core/frontend/src/tools/MeasureTool.ts +++ b/core/frontend/src/tools/MeasureTool.ts @@ -12,7 +12,7 @@ import { Marker } from "../Marker"; import { PrimitiveTool } from "./PrimitiveTool"; import { IModelApp } from "../IModelApp"; import { HitDetail, HitGeomType } from "../HitDetail"; -import { GeometryStreamProps, ColorDef, GeoCoordStatus, Cartographic } from "@bentley/imodeljs-common"; +import { GeometryStreamProps, ColorDef } from "@bentley/imodeljs-common"; import { QuantityType } from "../QuantityFormatter"; import { BeButtonEvent, EventHandled } from "./Tool"; import { NotifyMessageDetails, OutputMessagePriority, OutputMessageType } from "../NotificationManager"; @@ -69,9 +69,10 @@ class MeasureMarker extends Marker { const markerDrawFunc = (ctx: CanvasRenderingContext2D) => { ctx.beginPath(); ctx.arc(0, 0, this.size.x * 0.5, 0, 2 * Math.PI); - ctx.lineWidth = 1; + ctx.lineWidth = 2; ctx.strokeStyle = "black"; - ctx.fillStyle = this.isSelected ? "rgba(255,200,200,.5)" : "rgba(255,255,255,.5)"; + const hilite = this.isSelected && this._hiliteColor ? this._hiliteColor.colors : undefined; + ctx.fillStyle = undefined !== hilite ? "rgba(" + (hilite.r | 0) + "," + (hilite.g | 0) + "," + (hilite.b | 0) + ", 0.5)" : "rgba(255,255,255,.5)"; ctx.fill(); ctx.stroke(); }; @@ -189,6 +190,10 @@ export class MeasureDistanceTool extends PrimitiveTool { builderAxes.addLineString(segPoints); } + const segGlow = context.viewport.hilite.color.clone(); segGlow.setAlpha(50); + builderAxes.setSymbology(segGlow, ColorDef.black, 8); + builderAxes.addLineString([seg.start, seg.end]); + context.addDecorationFromBuilder(builderAxes); } @@ -266,6 +271,14 @@ export class MeasureDistanceTool extends PrimitiveTool { public async onMouseMotion(ev: BeButtonEvent): Promise { if (this._locationData.length > 0 && undefined !== ev.viewport) ev.viewport.invalidateDecorations(); } + protected reportMeasurements(): void { + if (undefined === this._totalDistanceMarker) + return; + const briefMsg = IModelApp.i18n.translateKeys(this._acceptedSegments.length > 1 ? "%{CoreTools:tools.Measure.Labels.CumulativeDistance}: " : "%{CoreTools:tools.Measure.Labels.Distance}: ") + this._totalDistanceMarker.label; + const msgDetail = new NotifyMessageDetails(OutputMessagePriority.Info, briefMsg, undefined, OutputMessageType.InputField); + IModelApp.notifications.outputMessage(msgDetail); + } + protected async updateTotals(): Promise { this._totalDistance = 0.0; this._totalDistanceMarker = undefined; @@ -280,10 +293,7 @@ export class MeasureDistanceTool extends PrimitiveTool { const formattedTotalDistance = IModelApp.quantityFormatter.formatQuantity(this._totalDistance, formatterSpec); this._totalDistanceMarker = new MeasureLabel(this._acceptedSegments[this._acceptedSegments.length - 1].end, formattedTotalDistance); - - const briefMsg = IModelApp.i18n.translateKeys(this._acceptedSegments.length > 1 ? "%{CoreTools:tools.Measure.Labels.CumulativeDistance}: " : "%{CoreTools:tools.Measure.Labels.Distance}: ") + formattedTotalDistance; - const msgDetail = new NotifyMessageDetails(OutputMessagePriority.Info, briefMsg, undefined, OutputMessageType.InputField); - IModelApp.notifications.outputMessage(msgDetail); + this.reportMeasurements(); } protected async getMarkerToolTip(distance: number, slope: number, start: Point3d, end: Point3d, delta?: Vector3d): Promise { @@ -324,9 +334,9 @@ export class MeasureDistanceTool extends PrimitiveTool { } if (undefined !== delta) { - const formattedDeltaX = IModelApp.quantityFormatter.formatQuantity(delta.x, distanceFormatterSpec); - const formattedDeltaY = IModelApp.quantityFormatter.formatQuantity(delta.y, distanceFormatterSpec); - const formattedDeltaZ = IModelApp.quantityFormatter.formatQuantity(delta.z, distanceFormatterSpec); + const formattedDeltaX = IModelApp.quantityFormatter.formatQuantity(Math.abs(delta.x), distanceFormatterSpec); + const formattedDeltaY = IModelApp.quantityFormatter.formatQuantity(Math.abs(delta.y), distanceFormatterSpec); + const formattedDeltaZ = IModelApp.quantityFormatter.formatQuantity(Math.abs(delta.z), distanceFormatterSpec); toolTip += IModelApp.i18n.translateKeys("%{CoreTools:tools.Measure.Labels.Delta}: ") + formattedDeltaX + ", " + formattedDeltaY + ", " + formattedDeltaZ + "
"; } @@ -512,14 +522,11 @@ export class MeasureLocationTool extends PrimitiveTool { toolTip += IModelApp.i18n.translateKeys("%{CoreTools:tools.Measure.Labels.Coordinate}: ") + formattedPointX + ", " + formattedPointY + ", " + formattedPointZ + "
"; } - if (undefined !== this.targetView && this.targetView.view.isSpatialView() && undefined !== this.iModel.ecefLocation) { + if (undefined !== this.targetView && this.targetView.view.isSpatialView()) { const latLongFormatterSpec = await IModelApp.quantityFormatter.getFormatterSpecByQuantityType(QuantityType.LatLong); if (undefined !== latLongFormatterSpec && undefined !== coordFormatterSpec) { - const geoConverter = this.iModel.geoServices.getConverter(); - const coordResponse = await geoConverter.getGeoCoordinatesFromIModelCoordinates([point]); - if (1 === coordResponse.geoCoords.length && GeoCoordStatus.Success === coordResponse.geoCoords[0].s) { - const longLatHeight = Point3d.fromJSON(coordResponse.geoCoords[0].p); // x is longitude in degrees, y is latitude in degrees, z is height in meters... - const cartographic = Cartographic.fromDegrees(longLatHeight.x, longLatHeight.y, longLatHeight.z); + try { + const cartographic = await this.iModel.spatialToCartographic(point); const formattedLat = IModelApp.quantityFormatter.formatQuantity(Math.abs(cartographic.latitude), latLongFormatterSpec); const formattedLong = IModelApp.quantityFormatter.formatQuantity(Math.abs(cartographic.longitude), latLongFormatterSpec); const formattedHeight = IModelApp.quantityFormatter.formatQuantity(cartographic.height, coordFormatterSpec); @@ -527,7 +534,7 @@ export class MeasureLocationTool extends PrimitiveTool { const longDir = IModelApp.i18n.translateKeys(cartographic.longitude < 0 ? "%{CoreTools:tools.Measure.Labels.W}" : "%{CoreTools:tools.Measure.Labels.E}"); toolTip += IModelApp.i18n.translateKeys("%{CoreTools:tools.Measure.Labels.LatLong}: ") + formattedLat + latDir + ", " + formattedLong + longDir + "
"; toolTip += IModelApp.i18n.translateKeys("%{CoreTools:tools.Measure.Labels.Altitude}: ") + formattedHeight + "
"; - } + } catch { } } } @@ -537,6 +544,8 @@ export class MeasureLocationTool extends PrimitiveTool { public decorate(context: DecorateContext): void { if (!context.viewport.view.isSpatialView()) return; this._acceptedLocations.forEach((marker) => marker.addDecoration(context)); } public decorateSuspended(context: DecorateContext): void { this.decorate(context); } + protected reportMeasurements(): void { } + public async onDataButtonDown(ev: BeButtonEvent): Promise { const point = ev.point.clone(); const toolTip = await this.getMarkerToolTip(point); @@ -546,6 +555,7 @@ export class MeasureLocationTool extends PrimitiveTool { marker.onMouseButton = noOpButtonFunc; this._acceptedLocations.push(marker); + this.reportMeasurements(); this.setupAndPromptForNextAction(); if (undefined !== ev.viewport) ev.viewport.invalidateDecorations(); @@ -562,10 +572,12 @@ export class MeasureLocationTool extends PrimitiveTool { return false; this._acceptedLocations.pop(); - if (0 === this._acceptedLocations.length) + if (0 === this._acceptedLocations.length) { this.onReinitialize(); - else + } else { + this.reportMeasurements(); this.setupAndPromptForNextAction(); + } return true; } diff --git a/core/frontend/src/tools/PluginTool.ts b/core/frontend/src/tools/PluginTool.ts index 67419cd..1b44476 100644 --- a/core/frontend/src/tools/PluginTool.ts +++ b/core/frontend/src/tools/PluginTool.ts @@ -5,47 +5,17 @@ /** @module Tools */ import { Tool } from "./Tool"; +import { PluginAdmin } from "../Plugin"; +import { IModelApp } from "../IModelApp"; -async function loadPackage(packageName: string): Promise { - return new Promise((resolve, reject) => { - const head = document.getElementsByTagName("head")[0]; - if (!head) - reject(new Error("no head element found")); - - // create the script element. handle onload and onerror. - const scriptElement = document.createElement("script"); - scriptElement.onload = () => { - scriptElement.onload = null; - resolve(); - }; - scriptElement.onerror = (ev) => { - scriptElement.onload = null; - reject(new Error("can't load " + packageName + " : " + ev)); - }; - scriptElement.async = true; - scriptElement.src = packageName; - head.insertBefore(scriptElement, head.lastChild); - }); -} - -/** - * An Immediate Tool that allows an iModelJs plugin module to be loaded . - */ +/** An Immediate Tool that starts the process of loading an iModelJs plugin. */ export class PluginTool extends Tool { public static toolId = "Plugin"; public run(args: any[]): boolean { - // we can only use $script in a browser environment. - if (!typeof document) { - // tslint:disable:no-console - console.log("PluginTool is only available in browser environment"); - return false; - } - - // tslint:disable:no-console - console.log(args); if (args && args.length > 0 && args[0]) { - // tslint:disable-line:no-console - loadPackage(args[0]).then(() => { console.log("script", args[0], "loaded"); }).catch((_err) => { console.log("Unable to load plugin"); }); + PluginAdmin.loadPlugin(args[0], args.slice(1)) + .then(() => { }) + .catch((_err) => { IModelApp.notifications.outputMessage(IModelApp.i18n.translate("PluginErrors.UnableToLoad", { pluginName: args[0] })); }); } return true; } diff --git a/core/frontend/src/tools/PrimitiveTool.ts b/core/frontend/src/tools/PrimitiveTool.ts index 262e3ee..f3bce60 100644 --- a/core/frontend/src/tools/PrimitiveTool.ts +++ b/core/frontend/src/tools/PrimitiveTool.ts @@ -173,11 +173,12 @@ export abstract class PrimitiveTool extends InteractiveTool { public exitTool(): void { IModelApp.toolAdmin.startDefaultTool(); } /** - * Called to revert to a previous tool state (ex. undo last data button). + * Called to reverse to a previous tool state (ex. undo last data button). * @return false to instead reverse the most recent transaction. */ public async onUndoPreviousStep(): Promise { return false; } + /** @hidden */ public async undoPreviousStep(): Promise { if (!await this.onUndoPreviousStep()) return false; @@ -189,6 +190,23 @@ export abstract class PrimitiveTool extends InteractiveTool { return true; } + /** + * Called to reinstate to a previous tool state (ex. redo last data button). + * @return false to instead reinstate the most recent transaction. + */ + public async onRedoPreviousStep(): Promise { return false; } + + /** @hidden */ + public async redoPreviousStep(): Promise { + if (!await this.onRedoPreviousStep()) + return false; + + AccuDrawShortcuts.processPendingHints(); // Process any hints the active tool setup in _OnUndoPreviousStep now... + IModelApp.viewManager.invalidateDecorationsAllViews(); + IModelApp.toolAdmin.updateDynamics(); + + return true; + } /** * Tools need to call SaveChanges to commit any elements they have added/changes they have made. * This helper method supplies the tool name for the undo string to iModel.saveChanges. diff --git a/core/frontend/src/tools/SelectTool.ts b/core/frontend/src/tools/SelectTool.ts index b75e3af..75398bc 100644 --- a/core/frontend/src/tools/SelectTool.ts +++ b/core/frontend/src/tools/SelectTool.ts @@ -4,18 +4,22 @@ *--------------------------------------------------------------------------------------------*/ /** @module SelectionSet */ -import { Point3d, Point2d, Range2d } from "@bentley/geometry-core"; -import { PrimitiveTool } from "./PrimitiveTool"; +import { Id64, Id64Arg } from "@bentley/bentleyjs-core"; +import { Point2d, Point3d, Range2d } from "@bentley/geometry-core"; +import { ColorDef } from "@bentley/imodeljs-common"; +import { LocateFilterStatus, LocateResponse } from "../ElementLocateManager"; +import { HitDetail } from "../HitDetail"; import { IModelApp } from "../IModelApp"; -import { CoordinateLockOverrides, ManipulatorToolEvent } from "./ToolAdmin"; +import { PropertyDescription } from "../properties/Description"; +import { PropertyEditorParamTypes } from "../properties/EditorParams"; +import { ToolSettingsPropertyRecord, ToolSettingsPropertySyncItem, ToolSettingsValue } from "../properties/ToolSettingsValue"; +import { PrimitiveValue } from "../properties/Value"; +import { Pixel } from "../rendering"; import { DecorateContext } from "../ViewContext"; -import { BeButtonEvent, BeButton, BeModifierKeys, EventHandled, BeTouchEvent, InputSource } from "./Tool"; -import { LocateResponse, LocateFilterStatus } from "../ElementLocateManager"; -import { HitDetail } from "../HitDetail"; -import { Id64Arg, Id64 } from "@bentley/bentleyjs-core"; import { ViewRect } from "../Viewport"; -import { Pixel } from "../rendering"; -import { ColorDef } from "@bentley/imodeljs-common"; +import { PrimitiveTool } from "./PrimitiveTool"; +import { BeButton, BeButtonEvent, BeModifierKeys, BeTouchEvent, EventHandled, InputSource } from "./Tool"; +import { CoordinateLockOverrides, ManipulatorToolEvent } from "./ToolAdmin"; /** The method for choosing elements with the [[SelectionTool]] */ export const enum SelectionMethod { @@ -23,7 +27,7 @@ export const enum SelectionMethod { Pick, /** Identify elements by overlap with crossing line */ Line, - /** Identify elements by box selection (inside/overlap for box selection determined by point direction and shift ke) */ + /** Identify elements by box selection (inside/overlap for box selection determined by point direction and shift key) */ Box, } @@ -49,6 +53,22 @@ export const enum SelectionProcessing { ReplaceSelectionWithElement, } +/** The selection options to display in the tool settings. */ +export const enum SelectOptions { + PickAndReplace, + LineAndReplace, + BoxAndReplace, + PickAndAdd, + PickAndRemove, +} + +export const enum SelectionScope { + /** Identified elements are selected. */ + Element, + /** Identified elements as well as all members of assemblies that include the identified elements are selected. */ + Assembly, +} + /** Tool for picking a set of elements of interest, selected by the user. */ export class SelectionTool extends PrimitiveTool { public static hidden = false; @@ -56,6 +76,10 @@ export class SelectionTool extends PrimitiveTool { public isSelectByPoints = false; public isSuspended = false; public readonly points: Point3d[] = []; + private _selectionMode = SelectionMode.Replace; + private _selectionMethod = SelectionMethod.Pick; + private _selectionOptionValue = new ToolSettingsValue(SelectOptions.PickAndReplace); + protected _selectionScopeValue = new ToolSettingsValue(SelectionScope.Element); public requireWriteableTarget(): boolean { return false; } public autoLockTarget(): void { } // NOTE: For selecting elements we only care about iModel, so don't lock target model automatically. @@ -64,52 +88,109 @@ export class SelectionTool extends PrimitiveTool { protected wantEditManipulators(): boolean { return SelectionMethod.Pick === this.getSelectionMethod(); } // NEEDSWORK: Settings...send ManipulatorToolEvent.Stop/Start as appropriate when value changes... protected wantPickableDecorations(): boolean { return this.wantEditManipulators(); } // Allow pickable decorations selection to be independent of manipulators... - protected getSelectionMethod(): SelectionMethod { return SelectionMethod.Pick; } // NEEDSWORK: Setting... - protected getSelectionMode(): SelectionMode { return SelectionMode.Replace; } // NEEDSWORK: Settings... - protected wantToolSettings(): boolean { return true; } // NEEDSWORK: Settings... + protected getSelectionMethod(): SelectionMethod { return this._selectionMethod; } + protected setSelectionMethod(method: SelectionMethod): void { this._selectionMethod = method; } + protected getSelectionMode(): SelectionMode { return this._selectionMode; } + protected setSelectionMode(mode: SelectionMode): void { this._selectionMode = mode; } + protected wantToolSettings(): boolean { return false; } + protected wantSelectionScopeInToolSettings(): boolean { return true; } + + private static optionMessage(str: string) { return IModelApp.i18n.translate("CoreTools:tools.ElementSet.SelectionOptions." + str); } + private static _optionsName = "selectionOptions"; + /* The property descriptions used to generate ToolSettings UI. */ + private static _getOptionsDescription(): PropertyDescription { + return { + name: SelectionTool._optionsName, + displayLabel: IModelApp.i18n.translate("CoreTools:tools.ElementSet.Prompts.Mode"), + typename: "enum", + editor: { + name: "enum-buttongroup", + params: [ + { + type: PropertyEditorParamTypes.ButtonGroupData, + buttons: [ + { iconClass: "icon icon-select-single" }, + { iconClass: "icon icon-select-line" }, + { iconClass: "icon icon-select-box" }, + { iconClass: "icon icon-select-plus" }, + { + iconClass: "icon icon-select-minus", + isEnabledFunction: () => { const tool = IModelApp.toolAdmin.activeTool; return tool instanceof PrimitiveTool ? tool.iModel.selectionSet.isActive : false; }, + }, + ], + }, + ], + }, + enum: { + choices: [ + { label: SelectionTool.optionMessage("Pick"), value: SelectOptions.PickAndReplace }, + { label: SelectionTool.optionMessage("Line"), value: SelectOptions.LineAndReplace }, + { label: SelectionTool.optionMessage("Box"), value: SelectOptions.BoxAndReplace }, + { label: SelectionTool.optionMessage("Add"), value: SelectOptions.PickAndAdd }, + { label: SelectionTool.optionMessage("Remove"), value: SelectOptions.PickAndRemove }, + ], + }, + }; + } + + private static scopeMessage(str: string) { return IModelApp.i18n.translate("CoreTools:tools.ElementSet.SelectionScope." + str); } + private static _scopesName = "selectionScope"; + private static _getScopesDescription(): PropertyDescription { + return { + name: SelectionTool._scopesName, + displayLabel: IModelApp.i18n.translate("CoreTools:tools.ElementSet.Prompts.Scope"), + typename: "enum", + enum: { + choices: [ + { label: SelectionTool.scopeMessage("Element"), value: SelectionScope.Element }, + { label: SelectionTool.scopeMessage("Assembly"), value: SelectionScope.Assembly }, + ], + }, + }; + } protected showPrompt(mode: SelectionMode, method: SelectionMethod): void { + let msg = "IdentifyElement"; switch (mode) { case SelectionMode.Replace: switch (method) { - case SelectionMethod.Pick: - IModelApp.notifications.outputPromptByKey("CoreTools:tools.ElementSet.Prompts.IdentifyElement"); - break; case SelectionMethod.Line: - IModelApp.notifications.outputPromptByKey("CoreTools:tools.ElementSet.Prompts.IdentifyLine"); + msg = "IdentifyLine"; break; case SelectionMethod.Box: - IModelApp.notifications.outputPromptByKey("CoreTools:tools.ElementSet.Prompts.IdentifyBox"); + msg = "IdentifyBox"; break; } break; case SelectionMode.Add: switch (method) { case SelectionMethod.Pick: - IModelApp.notifications.outputPromptByKey("CoreTools:tools.ElementSet.Prompts.IdentifyElementAdd"); + msg = "IdentifyElementAdd"; break; case SelectionMethod.Line: - IModelApp.notifications.outputPromptByKey("CoreTools:tools.ElementSet.Prompts.IdentifyLineAdd"); + msg = "IdentifyLineAdd"; break; case SelectionMethod.Box: - IModelApp.notifications.outputPromptByKey("CoreTools:tools.ElementSet.Prompts.IdentifyBoxAdd"); + msg = "IdentifyBoxAdd"; break; } break; case SelectionMode.Remove: switch (method) { case SelectionMethod.Pick: - IModelApp.notifications.outputPromptByKey("CoreTools:tools.ElementSet.Prompts.IdentifyElementRemove"); + msg = "IdentifyElementRemove"; break; case SelectionMethod.Line: - IModelApp.notifications.outputPromptByKey("CoreTools:tools.ElementSet.Prompts.IdentifyLineRemove"); + msg = "IdentifyLineRemove"; break; case SelectionMethod.Box: - IModelApp.notifications.outputPromptByKey("CoreTools:tools.ElementSet.Prompts.IdentifyBoxRemove"); + msg = "IdentifyBoxRemove"; break; } break; } + + IModelApp.notifications.outputPromptByKey("CoreTools:tools.ElementSet.Prompts." + msg); } protected initSelectTool(): void { @@ -125,20 +206,45 @@ export class SelectionTool extends PrimitiveTool { this.showPrompt(mode, method); } - public processSelection(elementId: Id64Arg, process: SelectionProcessing): boolean { + public updateSelection(elementId: Id64Arg, process: SelectionProcessing): boolean { + let returnValue = false; switch (process) { case SelectionProcessing.AddElementToSelection: - return this.iModel.selectionSet.add(elementId); + returnValue = this.iModel.selectionSet.add(elementId); + break; case SelectionProcessing.RemoveElementFromSelection: - return this.iModel.selectionSet.remove(elementId); + returnValue = this.iModel.selectionSet.remove(elementId); + break; case SelectionProcessing.InvertElementInSelection: // (if element is in selection remove it else add it.) - return this.iModel.selectionSet.invert(elementId); + returnValue = this.iModel.selectionSet.invert(elementId); + break; case SelectionProcessing.ReplaceSelectionWithElement: this.iModel.selectionSet.replace(elementId); - return true; + returnValue = true; + break; default: return false; } + // always force UI to sync display of options since the select option of PickAndRemove should only be enabled if the selection set has elements. + if (returnValue) this.syncSelectionOption(); + return returnValue; + } + + public async processSelection(elementId: Id64Arg, process: SelectionProcessing): Promise { + if (SelectionScope.Assembly === this.selectionScope) { + const assemblyIds = new Set(); + Id64.toIdSet(elementId).forEach((id) => { assemblyIds.add(id); }); + if (0 === assemblyIds.size) + return false; + const where = [...assemblyIds].join(","); + const ecsql = "SELECT ECInstanceId as id, Parent.Id as parentId FROM BisCore.GeometricElement WHERE Parent.Id IN (SELECT Parent.Id as parentId FROM BisCore.GeometricElement WHERE parent.Id != 0 AND ECInstanceId IN (" + where + "))"; + for (const row of await this.iModel.queryPage(ecsql)) { + assemblyIds.add(row.parentId as string); + assemblyIds.add(row.id as string); + } + return this.updateSelection(assemblyIds, process); + } + return this.updateSelection(elementId, process); } protected useOverlapSelection(ev: BeButtonEvent): boolean { @@ -243,26 +349,28 @@ export class SelectionTool extends PrimitiveTool { contents.forEach((id) => { if (Id64.isTransient(id)) contents.delete(id); }); if (0 === contents.size) { - if (!ev.isControlKey && this.wantSelectionClearOnMiss(ev)) + if (!ev.isControlKey && this.wantSelectionClearOnMiss(ev)) { this.iModel.selectionSet.emptyAll(); + this.syncSelectionOption(); + } return; } switch (this.getSelectionMode()) { case SelectionMode.Replace: if (!ev.isControlKey) - this.processSelection(contents, SelectionProcessing.ReplaceSelectionWithElement); + this.processSelection(contents, SelectionProcessing.ReplaceSelectionWithElement); // tslint:disable-line:no-floating-promises else - this.processSelection(contents, SelectionProcessing.InvertElementInSelection); + this.processSelection(contents, SelectionProcessing.InvertElementInSelection); // tslint:disable-line:no-floating-promises break; case SelectionMode.Add: - this.processSelection(contents, SelectionProcessing.AddElementToSelection); + this.processSelection(contents, SelectionProcessing.AddElementToSelection); // tslint:disable-line:no-floating-promises break; case SelectionMode.Remove: - this.processSelection(contents, SelectionProcessing.RemoveElementFromSelection); + this.processSelection(contents, SelectionProcessing.RemoveElementFromSelection); // tslint:disable-line:no-floating-promises break; } - }); + }, true); } protected selectByPointsStart(ev: BeButtonEvent): boolean { @@ -333,8 +441,10 @@ export class SelectionTool extends PrimitiveTool { return EventHandled.Yes; if (SelectionMethod.Pick !== this.getSelectionMethod()) { - if (!ev.isControlKey && this.wantSelectionClearOnMiss(ev)) + if (!ev.isControlKey && this.wantSelectionClearOnMiss(ev)) { this.iModel.selectionSet.emptyAll(); + this.syncSelectionOption(); + } if (InputSource.Touch !== ev.inputSource) this.selectByPointsStart(ev); // Require touch move and not tap to start crossing line/box selection... return EventHandled.Yes; @@ -347,22 +457,24 @@ export class SelectionTool extends PrimitiveTool { switch (this.getSelectionMode()) { case SelectionMode.Replace: - this.processSelection(hit.sourceId, ev.isControlKey ? SelectionProcessing.InvertElementInSelection : SelectionProcessing.ReplaceSelectionWithElement); + this.processSelection(hit.sourceId, ev.isControlKey ? SelectionProcessing.InvertElementInSelection : SelectionProcessing.ReplaceSelectionWithElement); // tslint:disable-line:no-floating-promises break; case SelectionMode.Add: - this.processSelection(hit.sourceId, SelectionProcessing.AddElementToSelection); + this.processSelection(hit.sourceId, SelectionProcessing.AddElementToSelection); // tslint:disable-line:no-floating-promises break; case SelectionMode.Remove: - this.processSelection(hit.sourceId, SelectionProcessing.RemoveElementFromSelection); + this.processSelection(hit.sourceId, SelectionProcessing.RemoveElementFromSelection); // tslint:disable-line:no-floating-promises break; } return EventHandled.Yes; } - if (!ev.isControlKey && 0 !== this.iModel.selectionSet.size && this.wantSelectionClearOnMiss(ev)) + if (!ev.isControlKey && 0 !== this.iModel.selectionSet.size && this.wantSelectionClearOnMiss(ev)) { this.iModel.selectionSet.emptyAll(); + this.syncSelectionOption(); + } return EventHandled.Yes; } @@ -387,11 +499,11 @@ export class SelectionTool extends PrimitiveTool { // remove element(s) previously selected if in replace mode, or if we have a next element in add mode if (SelectionMode.Replace === this.getSelectionMode() || undefined !== nextHit) - this.processSelection(lastHit.sourceId, SelectionProcessing.RemoveElementFromSelection); + this.processSelection(lastHit.sourceId, SelectionProcessing.RemoveElementFromSelection); // tslint:disable-line:no-floating-promises // add element(s) located via reset button if (undefined !== nextHit) - this.processSelection(nextHit.sourceId, SelectionProcessing.AddElementToSelection); + this.processSelection(nextHit.sourceId, SelectionProcessing.AddElementToSelection); // tslint:disable-line:no-floating-promises return EventHandled.Yes; } } @@ -451,4 +563,111 @@ export class SelectionTool extends PrimitiveTool { } public static startTool(): boolean { return new SelectionTool().run(); } + + private syncSelectionOption(): void { + if (SelectOptions.PickAndRemove === this._selectionOptionValue.value && !this.iModel.selectionSet.isActive) { + // No selection active resetting selection option since PickAndRemove is no longer valid + this._selectionOptionValue.value = SelectOptions.PickAndReplace; + this.setSelectionMode(SelectionMode.Replace); + this.setSelectionMethod(SelectionMethod.Pick); + this.initSelectTool(); + } + + const syncItem: ToolSettingsPropertySyncItem = { value: this._selectionOptionValue.clone(), propertyName: SelectionTool._optionsName }; + this.syncToolSettingsProperties([syncItem]); + } + + public get selectionOption(): SelectOptions { + return this._selectionOptionValue.value as SelectOptions; + } + + public set selectionOption(option: SelectOptions) { + this._selectionOptionValue.value = option; + this.syncSelectionOption(); + } + + private applySelectionOption(options: SelectOptions): void { + let restartRequired = false; + switch (options) { + case SelectOptions.PickAndReplace: + restartRequired = (this.getSelectionMethod() !== SelectionMethod.Pick); + this.setSelectionMode(SelectionMode.Replace); + this.setSelectionMethod(SelectionMethod.Pick); + break; + case SelectOptions.LineAndReplace: + restartRequired = (this.getSelectionMethod() !== SelectionMethod.Line); + this.setSelectionMode(SelectionMode.Replace); + this.setSelectionMethod(SelectionMethod.Line); + break; + case SelectOptions.BoxAndReplace: + restartRequired = (this.getSelectionMethod() !== SelectionMethod.Box); + this.setSelectionMode(SelectionMode.Replace); + this.setSelectionMethod(SelectionMethod.Box); + break; + case SelectOptions.PickAndAdd: + restartRequired = (this.getSelectionMethod() !== SelectionMethod.Pick); + this.setSelectionMode(SelectionMode.Add); + this.setSelectionMethod(SelectionMethod.Pick); + break; + case SelectOptions.PickAndRemove: + if (this.iModel.selectionSet.size > 0) { + restartRequired = (this.getSelectionMethod() !== SelectionMethod.Pick); + this.setSelectionMode(SelectionMode.Remove); + this.setSelectionMethod(SelectionMethod.Pick); + } + break; + } + + if (restartRequired) + this.initSelectTool(); + } + + private syncSelectionScope(): void { + const syncItem: ToolSettingsPropertySyncItem = { value: this._selectionScopeValue.clone(), propertyName: SelectionTool._scopesName }; + this.syncToolSettingsProperties([syncItem]); + } + + public get selectionScope(): SelectionScope { + return this._selectionScopeValue.value as SelectionScope; + } + + public set selectionScope(scope: SelectionScope) { + this._selectionScopeValue.value = scope; + this.syncSelectionScope(); + } + + private applySelectionScope(scope: SelectionScope): void { + // if we change scopes clear the selection + if (scope !== this.selectionScope) { + this._selectionScopeValue.value = scope; + this.iModel.selectionSet.emptyAll(); + this.syncSelectionOption(); + } + } + + /** Used to supply DefaultToolSettingProvider with a list of properties to use to generate ToolSettings. If undefined then no ToolSettings will be displayed */ + public supplyToolSettingsProperties(): ToolSettingsPropertyRecord[] | undefined { + if (!this.wantToolSettings()) + return undefined; + const toolSettings = new Array(); + // generate 2 columns - label will be placed in column 0 and editor in column 1. + toolSettings.push(new ToolSettingsPropertyRecord(this._selectionOptionValue.clone() as PrimitiveValue, SelectionTool._getOptionsDescription(), { rowPriority: 0, columnIndex: 1 })); + if (this.wantSelectionScopeInToolSettings()) + toolSettings.push(new ToolSettingsPropertyRecord(this._selectionScopeValue.clone() as PrimitiveValue, SelectionTool._getScopesDescription(), { rowPriority: 10, columnIndex: 1 })); + + return toolSettings; + } + + /** Used to send changes from UI back to Tool */ + public applyToolSettingPropertyChange(updatedValue: ToolSettingsPropertySyncItem): boolean { + if (updatedValue.propertyName === SelectionTool._optionsName) { + if (this._selectionOptionValue.update(updatedValue.value)) + this.applySelectionOption(updatedValue.value.value as SelectOptions); + } else if (updatedValue.propertyName === SelectionTool._scopesName) { + this.applySelectionScope(updatedValue.value.value as SelectionScope); + } + + // return true is change is valid + return true; + } } diff --git a/core/frontend/src/tools/Tool.ts b/core/frontend/src/tools/Tool.ts index 8f77350..d80a809 100644 --- a/core/frontend/src/tools/Tool.ts +++ b/core/frontend/src/tools/Tool.ts @@ -14,6 +14,7 @@ import { IModelError, GeometryStreamProps } from "@bentley/imodeljs-common"; import { FuzzySearch, FuzzySearchResults } from "../FuzzySearch"; import { CoordinateLockOverrides } from "./ToolAdmin"; import { LocateResponse, LocateFilterStatus } from "../ElementLocateManager"; +import { ToolSettingsPropertySyncItem, ToolSettingsPropertyRecord } from "../properties/ToolSettingsValue"; export type ToolType = typeof Tool; export type ToolList = ToolType[]; @@ -501,6 +502,19 @@ export abstract class InteractiveTool extends Tool { IModelApp.locateManager.initToolLocate(); this.changeLocateState(enableLocate, enableSnap, cursor, coordLockOvr); } + + /** Used to supply list of properties that can be used to generate ToolSettings. If undefined is returned then no ToolSettings will be displayed */ + public supplyToolSettingsProperties(): ToolSettingsPropertyRecord[] | undefined { return undefined; } + + /** Used to receive property changes from UI. Return false if there was an error applying updatedValue. */ + public applyToolSettingPropertyChange(_updatedValue: ToolSettingsPropertySyncItem): boolean { return true; } + + /** Called by tool to synchronize the UI with property changes made by tool. This is typically used to provide user feedback during tool dynamics. + * If the syncData contains a quantity value and if the displayValue is not defined, the displayValue will be generated in the UI layer before displaying the value. + */ + public syncToolSettingsProperties(syncData: ToolSettingsPropertySyncItem[]) { + IModelApp.toolAdmin.syncToolSettingsProperties(this.toolId, syncData); + } } /** diff --git a/core/frontend/src/tools/ToolAdmin.ts b/core/frontend/src/tools/ToolAdmin.ts index 1c208a4..a10adb1 100644 --- a/core/frontend/src/tools/ToolAdmin.ts +++ b/core/frontend/src/tools/ToolAdmin.ts @@ -19,6 +19,7 @@ import { IdleTool } from "./IdleTool"; import { PrimitiveTool } from "./PrimitiveTool"; import { BeButton, BeButtonEvent, BeButtonState, BeModifierKeys, BeTouchEvent, BeWheelEvent, CoordSource, EventHandled, InputCollector, InputSource, InteractiveTool, Tool } from "./Tool"; import { ViewTool } from "./ViewTool"; +import { ToolSettingsPropertySyncItem } from "../properties/ToolSettingsValue"; export const enum CoordinateLockOverrides { None = 0, @@ -43,7 +44,7 @@ export class ToolSettings { /** Number of screen inches of movement allowed between clicks to still qualify as a double-click. */ public static doubleClickToleranceInches = 0.05; /** Duration without movement before a no-motion event is generated. */ - public static noMotionTimeout = BeDuration.fromMilliseconds(50); + public static noMotionTimeout = BeDuration.fromMilliseconds(10); /** If true, view rotation tool keeps the up vector (worldZ) aligned with screenY. */ public static preserveWorldUp = true; /** Delay with a touch on the surface before a move operation begins. */ @@ -294,6 +295,7 @@ interface ToolEvent { /** Controls operation of Tools. Administers the current view, primitive, and idle tools. Forwards events to the appropriate tool. */ export class ToolAdmin { + public markupView?: ScreenViewport; /** @hidden */ public readonly currentInputState = new CurrentInputState(); /** @hidden */ @@ -308,12 +310,30 @@ export class ToolAdmin { private _saveCursor?: string; private _saveLocateCircle = false; private _modifierKeyWentDown = false; + private _defaultToolId = "Select"; + private _defaultToolArgs?: any[]; private _modifierKey = BeModifierKeys.None; /** Return the name of the [[PrimitiveTool]] to use as the default tool, if any. * @see [[startDefaultTool]] * @hidden */ - public get defaultToolId(): string { return "Select"; } + public get defaultToolId(): string { return this._defaultToolId; } + /** Set the name of the [[PrimitiveTool]] to use as the default tool, if any. + * @see [[startDefaultTool]] + * @hidden + */ + public set defaultToolId(toolId: string) { this._defaultToolId = toolId; } + /** Return the default arguments to pass in when starting the default tool, if any. + * @see [[startDefaultTool]] + * @hidden + */ + public get defaultToolArgs(): any[] | undefined { return this._defaultToolArgs; } + + /** Set the default arguments to pass in when starting the default tool, if any. + * @see [[startDefaultTool]] + * @hidden + */ + public set defaultToolArgs(args: any[] | undefined) { this._defaultToolArgs = args; } /** Apply operations such as transform, copy or delete to all members of an assembly. */ public assemblyLock = false; /** If Grid Lock is on, project data points to grid. */ @@ -329,6 +349,20 @@ export class ToolAdmin { // Workaround for Edge Bug. private static _keysCurrentlyDown = new Set(); // The (small) set of keys that are currently pressed. + /** Handler that wants to process synching latest tool setting properties with UI. + * @hidden + */ + private _toolSettingsChangeHandler: ((toolId: string, syncProperties: ToolSettingsPropertySyncItem[]) => void) | undefined = undefined; + + /** @hidden */ + /** Set by object that will be provide UI for tool settings properties. */ + public set toolSettingsChangeHandler(handler: ((toolId: string, syncProperties: ToolSettingsPropertySyncItem[]) => void) | undefined) { + this._toolSettingsChangeHandler = handler; + } + + /** @hidden */ + public get toolSettingsChangeHandler() { return this._toolSettingsChangeHandler; } + /** Handler for keyboard events. */ private static _keyEventHandler = (ev: KeyboardEvent) => { if (ev.repeat) // we don't want repeated keyboard events. If we keep them they interfere with replacing mouse motion events, since they come as a stream. @@ -465,7 +499,7 @@ export class ToolAdmin { return EventHandled.Yes; const tool = this.activeTool; - if (undefined === tool || EventHandled.Yes !== await tool.onMouseWheel(wheelEvent)) + if (undefined === tool || EventHandled.Yes !== await tool.onMouseWheel(wheelEvent) && vp !== this.markupView) return this.idleTool.onMouseWheel(wheelEvent); return EventHandled.Yes; } @@ -1113,8 +1147,14 @@ export class ToolAdmin { if (BeModifierKeys.None !== modifierKey) return this.onModifierKeyTransition(wentDown, modifierKey, keyEvent); - if (wentDown && keyEvent.ctrlKey && "z" === keyEvent.key.toLowerCase()) - return this.doUndoOperation(); + if (wentDown && keyEvent.ctrlKey) { + switch (keyEvent.key) { + case "z": + return this.doUndoOperation(); + case "y": + return this.doRedoOperation(); + } + } return activeTool.onKeyTransition(wentDown, keyEvent); } @@ -1131,6 +1171,18 @@ export class ToolAdmin { return false; } + /** Called to redo previous data button for primitive tools or undo last write operation. */ + public async doRedoOperation(): Promise { + const activeTool = this.activeTool; + if (activeTool instanceof PrimitiveTool) { + // ### TODO Add method so UI can be showing string to inform user that undo of last data point is available... + if (await activeTool.redoPreviousStep()) + return true; + } + // ### TODO Request TxnManager undo and restart this.primitiveTool... + return false; + } + private onUnsuspendTool() { const tool = this.activeTool; if (tool === undefined) @@ -1220,6 +1272,7 @@ export class ToolAdmin { /** @hidden */ public startViewTool(newTool: ViewTool) { + IModelApp.notifications.outputPrompt(""); IModelApp.accuDraw.onViewToolInstall(); @@ -1283,6 +1336,12 @@ export class ToolAdmin { this.setPrimitiveTool(newTool); } + /** Method used by interactive tools to send updated values to UI components, typically showing tool settings. */ + public syncToolSettingsProperties(toolId: string, syncProperties: ToolSettingsPropertySyncItem[]): void { + if (this.toolSettingsChangeHandler) + this.toolSettingsChangeHandler(toolId, syncProperties); + } + /** * Starts the default tool, if any. Generally invoked automatically when other tools exit, so shouldn't be called directly. * @note The default tool is expected to be a subclass of [[PrimitiveTool]]. A call to startDefaultTool is required to terminate @@ -1290,7 +1349,7 @@ export class ToolAdmin { * @hidden */ public startDefaultTool() { - if (!IModelApp.tools.run(this.defaultToolId)) + if (!IModelApp.tools.run(this.defaultToolId, this.defaultToolArgs)) this.startPrimitiveTool(undefined); } @@ -1511,7 +1570,7 @@ export class WheelEventProcessor { vp.worldToNpc(lastEvent.point, newTarget); targetNpc.z = newTarget.z; lastEventWasValid = true; - } else if (undefined !== vp.pickNearestVisibleGeometry(target, vp.pixelsFromInches(ToolSettings.viewToolPickRadiusInches), newTarget)) { + } else if (undefined !== vp.pickNearestVisibleGeometry(target, vp.pixelsFromInches(ToolSettings.viewToolPickRadiusInches), true, newTarget)) { vp.worldToNpc(newTarget, newTarget); targetNpc.z = newTarget.z; } else { @@ -1522,7 +1581,7 @@ export class WheelEventProcessor { vp.npcToWorld(targetNpc, target); } - const cameraView = vp.view as ViewState3d; + const cameraView: ViewState3d = vp.view; const transform = Transform.createFixedPointAndMatrix(target, Matrix3d.createScale(zoomRatio, zoomRatio, zoomRatio)); const oldCameraPos = cameraView.getEyePoint(); const newCameraPos = transform.multiplyPoint3d(oldCameraPos); diff --git a/core/frontend/src/tools/ViewTool.ts b/core/frontend/src/tools/ViewTool.ts index ad0fabd..f3ba537 100644 --- a/core/frontend/src/tools/ViewTool.ts +++ b/core/frontend/src/tools/ViewTool.ts @@ -4,16 +4,18 @@ *--------------------------------------------------------------------------------------------*/ /** @module Tools */ -import { BeButtonEvent, BeWheelEvent, CoordSource, InteractiveTool, EventHandled, BeTouchEvent, BeButton, InputSource } from "./Tool"; -import { Viewport, CoordSystem, DepthRangeNpc, ViewRect, ScreenViewport } from "../Viewport"; -import { Angle, Point3d, Vector3d, YawPitchRollAngles, Point2d, Vector2d, Matrix3d, Transform, Range3d } from "@bentley/geometry-core"; -import { Frustum, NpcCenter, Npc, ColorDef } from "@bentley/imodeljs-common"; -import { MarginPercent, ViewStatus, ViewState3d } from "../ViewState"; -import { IModelApp } from "../IModelApp"; -import { DecorateContext } from "../ViewContext"; +import { Angle, Matrix3d, Point2d, Point3d, Range3d, Transform, Vector2d, Vector3d, YawPitchRollAngles } from "@bentley/geometry-core"; +import { ColorDef, Frustum, Npc, NpcCenter } from "@bentley/imodeljs-common"; import { TentativeOrAccuSnap } from "../AccuSnap"; +import { IModelApp } from "../IModelApp"; import { GraphicType } from "../rendering"; +import { DecorateContext } from "../ViewContext"; +import { CoordSystem, DepthRangeNpc, ScreenViewport, Viewport, ViewRect } from "../Viewport"; +import { MarginPercent, ViewState3d, ViewStatus } from "../ViewState"; +import { BeButton, BeButtonEvent, BeTouchEvent, BeWheelEvent, CoordSource, EventHandled, InputSource, InteractiveTool } from "./Tool"; import { ToolSettings } from "./ToolAdmin"; +import { AccuDraw } from "../AccuDraw"; +import { StandardViewId } from "../StandardView"; export const enum ViewHandleWeight { Thin = 1, @@ -58,6 +60,11 @@ export abstract class ViewTool extends InteractiveTool { public endDynamicUpdate() { this.inDynamicUpdate = false; } public run(): boolean { const toolAdmin = IModelApp.toolAdmin; + if (undefined !== this.viewport && this.viewport === toolAdmin.markupView) { + IModelApp.notifications.outputPromptByKey("Viewing.NotDuringMarkup"); + return false; + } + if (!toolAdmin.onInstallTool(this)) return false; @@ -66,10 +73,13 @@ export abstract class ViewTool extends InteractiveTool { return true; } + public constructor(public viewport?: ScreenViewport) { super(); } public async onResetButtonUp(_ev: BeButtonEvent) { this.exitTool(); return EventHandled.Yes; } /** Do not override. */ public exitTool(): void { IModelApp.toolAdmin.exitViewTool(); } + public static showPrompt(prompt: string) { IModelApp.notifications.outputPromptByKey("CoreTools:tools.View." + prompt); } + } /** @hidden */ @@ -200,7 +210,6 @@ export class ViewHandleArray { /** Base class for tools that manipulate the frustum of a Viewport. */ export abstract class ViewManip extends ViewTool { - public viewport?: ScreenViewport = undefined; public viewHandles: ViewHandleArray; public frustumValid = false; public readonly targetCenterWorld = new Point3d(); @@ -211,10 +220,9 @@ export abstract class ViewManip extends ViewTool { public targetCenterLocked = false; public nPts = 0; protected _forcedHandle = ViewHandleType.None; - public readonly lastFrustum = new Frustum(); constructor(viewport: ScreenViewport | undefined, public handleMask: number, public oneShot: boolean, public isDraggingRequired: boolean = false) { - super(); + super(viewport); this.viewHandles = new ViewHandleArray(this); this.changeViewport(viewport); } @@ -237,7 +245,7 @@ export abstract class ViewManip extends ViewTool { public async onDataButtonDown(ev: BeButtonEvent): Promise { // Tool was started in "drag required" mode, don't advance tool state and wait to see if we get the start drag event. - if (0 === this.nPts && this.isDraggingRequired && !this.isDragging) + if (0 === this.nPts && this.isDraggingRequired && !this.isDragging || undefined === ev.viewport) return EventHandled.No; switch (this.nPts) { @@ -389,17 +397,6 @@ export abstract class ViewManip extends ViewTool { vp.invalidateDecorations(); } this.viewHandles.empty(); - this.viewport = undefined; - } - - public frustumDidNotChange(): boolean { - const frust = this.viewport!.getWorldFrustum(); - if (this.frustumValid && frust.equals(this.lastFrustum)) - return true; - - this.lastFrustum.setFrom(frust); - this.frustumValid = true; - return false; } public updateTargetCenter(): void { @@ -586,21 +583,19 @@ export abstract class ViewManip extends ViewTool { return true; } - public changeViewport(vp: ScreenViewport | undefined): void { - // If viewport isn't really changing do nothing... - if (vp === this.viewport) + public changeViewport(vp?: ScreenViewport): void { + if (vp === this.viewport && 0 !== this.viewHandles.count) // If viewport isn't really changing do nothing... return; - if (undefined !== this.viewport) { + if (this.viewport) { this.viewport.invalidateDecorations(); // Remove decorations from current viewport... this.viewHandles.empty(); } - // Set m_viewport to new viewport and return if new viewport is undefined... - if (undefined === (this.viewport = vp)) + this.viewport = vp; + if (this.viewport === undefined) return; - // allocate and initialize handles array this.viewHandles.viewport = vp; this.targetCenterValid = false; this.updateTargetCenter(); @@ -755,7 +750,7 @@ class ViewPan extends ViewingToolHandle { } this.viewTool.beginDynamicUpdate(); - IModelApp.notifications.outputPromptByKey("CoreTools:tools.View.Pan.Prompts.NextPoint"); + ViewTool.showPrompt("Pan.Prompts.NextPoint"); return true; } @@ -825,7 +820,7 @@ class ViewRotate extends ViewingToolHandle { this._frustum.setFrom(this._activeFrustum); tool.beginDynamicUpdate(); - IModelApp.notifications.outputPromptByKey("CoreTools:tools.View.Rotate.Prompts.NextPoint"); + ViewTool.showPrompt("Rotate.Prompts.NextPoint"); return true; } @@ -930,7 +925,7 @@ class ViewLook extends ViewingToolHandle { vp.getWorldFrustum(this._frustum); tool.beginDynamicUpdate(); - IModelApp.notifications.outputPromptByKey("CoreTools:tools.View.Look.Prompts.NextPoint"); + ViewTool.showPrompt("Look.Prompts.NextPoint"); return true; } @@ -1032,7 +1027,7 @@ class ViewScroll extends ViewingToolHandle { this._anchorPtView.setFrom(ev.viewPoint); this._lastPtView.setFrom(ev.viewPoint); tool.beginDynamicUpdate(); - IModelApp.notifications.outputPromptByKey("CoreTools:tools.View.Scroll.Prompts.NextPoint"); + ViewTool.showPrompt("Scroll.Prompts.NextPoint"); return true; } @@ -1050,7 +1045,6 @@ class ViewScroll extends ViewingToolHandle { if (ev.viewport !== this.viewTool.viewport) return false; - this.viewTool.beginDynamicUpdate(); this.doScroll(ev); return false; } @@ -1147,7 +1141,7 @@ class ViewZoom extends ViewingToolHandle { this._lastPtView.setFrom(this._anchorPtView); tool.viewport!.viewToNpc(this._anchorPtView, this._anchorPtNpc); tool.beginDynamicUpdate(); - IModelApp.notifications.outputPromptByKey("CoreTools:tools.View.Zoom.Prompts.NextPoint"); + ViewTool.showPrompt("Zoom.Prompts.NextPoint"); return true; } } @@ -1159,7 +1153,7 @@ class ViewZoom extends ViewingToolHandle { this._lastPtView.setFrom(this._anchorPtView); tool.viewport!.viewToNpc(this._anchorPtView, this._anchorPtNpc); tool.beginDynamicUpdate(); - IModelApp.notifications.outputPromptByKey("CoreTools:tools.View.Zoom.Prompts.NextPoint"); + ViewTool.showPrompt("Zoom.Prompts.NextPoint"); return true; } @@ -1174,7 +1168,6 @@ class ViewZoom extends ViewingToolHandle { if (ev.viewport !== this.viewTool.viewport) return false; - this.viewTool.beginDynamicUpdate(); this.doZoom(ev); return false; } @@ -1485,7 +1478,6 @@ abstract class ViewNavigate extends ViewingToolHandle { if (ev.viewport !== this.viewTool.viewport) return false; - this.viewTool.beginDynamicUpdate(); this.doNavigate(ev); return false; } @@ -1559,7 +1551,7 @@ class ViewWalk extends ViewNavigate { this._navigateMotion = new NavigateMotion(this.viewTool.viewport!); } public get handleType(): ViewHandleType { return ViewHandleType.Walk; } - public firstPoint(ev: BeButtonEvent): boolean { IModelApp.notifications.outputPromptByKey("CoreTools:tools.View.Walk.Prompts.NextPoint"); return super.firstPoint(ev); } + public firstPoint(ev: BeButtonEvent): boolean { ViewTool.showPrompt("Walk.Prompts.NextPoint"); return super.firstPoint(ev); } protected getNavigateMotion(elapsedTime: number): NavigateMotion | undefined { const input = this.getInputVector(); @@ -1596,7 +1588,7 @@ class ViewFly extends ViewNavigate { this._navigateMotion = new NavigateMotion(this.viewTool.viewport!); } public get handleType(): ViewHandleType { return ViewHandleType.Fly; } - public firstPoint(ev: BeButtonEvent): boolean { IModelApp.notifications.outputPromptByKey("CoreTools:tools.View.Fly.Prompts.NextPoint"); return super.firstPoint(ev); } + public firstPoint(ev: BeButtonEvent): boolean { ViewTool.showPrompt("Fly.Prompts.NextPoint"); return super.firstPoint(ev); } protected getNavigateMotion(elapsedTime: number): NavigateMotion | undefined { const input = this.getInputVector(); @@ -1629,10 +1621,10 @@ class ViewFly extends ViewNavigate { /** The tool that performs a Pan view operation */ export class PanViewTool extends ViewManip { public static toolId = "View.Pan"; - constructor(vp: ScreenViewport, oneShot = false, isDraggingRequired = false) { + constructor(vp: ScreenViewport | undefined, oneShot = false, isDraggingRequired = false) { super(vp, ViewHandleType.Pan, oneShot, isDraggingRequired); } - public onReinitialize(): void { super.onReinitialize(); IModelApp.notifications.outputPromptByKey("CoreTools:tools.View.Pan.Prompts.FirstPoint"); } + public onReinitialize(): void { super.onReinitialize(); ViewTool.showPrompt("Pan.Prompts.FirstPoint"); } } /** A tool that performs a Rotate view operation */ @@ -1641,7 +1633,7 @@ export class RotateViewTool extends ViewManip { constructor(vp: ScreenViewport, oneShot = false, isDraggingRequired = false) { super(vp, ViewHandleType.Rotate | ViewHandleType.Pan | ViewHandleType.TargetCenter, oneShot, isDraggingRequired); } - public onReinitialize(): void { super.onReinitialize(); IModelApp.notifications.outputPromptByKey("CoreTools:tools.View.Rotate.Prompts.FirstPoint"); } + public onReinitialize(): void { super.onReinitialize(); ViewTool.showPrompt("Rotate.Prompts.FirstPoint"); } } /** A tool that performs the look operation */ @@ -1650,7 +1642,7 @@ export class LookViewTool extends ViewManip { constructor(vp: ScreenViewport, oneShot = false, isDraggingRequired = false) { super(vp, ViewHandleType.Look, oneShot, isDraggingRequired); } - public onReinitialize(): void { super.onReinitialize(); IModelApp.notifications.outputPromptByKey("CoreTools:tools.View.Look.Prompts.FirstPoint"); } + public onReinitialize(): void { super.onReinitialize(); ViewTool.showPrompt("Look.Prompts.FirstPoint"); } } /** A tool that performs the scroll operation */ @@ -1659,7 +1651,7 @@ export class ScrollViewTool extends ViewManip { constructor(vp: ScreenViewport, oneShot = false, isDraggingRequired = false) { super(vp, ViewHandleType.Scroll, oneShot, isDraggingRequired); } - public onReinitialize(): void { super.onReinitialize(); IModelApp.notifications.outputPromptByKey("CoreTools:tools.View.Scroll.Prompts.FirstPoint"); } + public onReinitialize(): void { super.onReinitialize(); ViewTool.showPrompt("Scroll.Prompts.FirstPoint"); } } /** A tool that performs the zoom operation */ @@ -1668,7 +1660,7 @@ export class ZoomViewTool extends ViewManip { constructor(vp: ScreenViewport, oneShot = false, isDraggingRequired = false) { super(vp, ViewHandleType.Zoom, oneShot, isDraggingRequired); } - public onReinitialize(): void { super.onReinitialize(); IModelApp.notifications.outputPromptByKey("CoreTools:tools.View.Zoom.Prompts.FirstPoint"); } + public onReinitialize(): void { super.onReinitialize(); ViewTool.showPrompt("Zoom.Prompts.FirstPoint"); } } /** A tool that performs the walk operation */ @@ -1677,7 +1669,7 @@ export class WalkViewTool extends ViewManip { constructor(vp: ScreenViewport, oneShot = false, isDraggingRequired = false) { super(vp, ViewHandleType.Walk, oneShot, isDraggingRequired); } - public onReinitialize(): void { super.onReinitialize(); IModelApp.notifications.outputPromptByKey("CoreTools:tools.View.Walk.Prompts.FirstPoint"); } + public onReinitialize(): void { super.onReinitialize(); ViewTool.showPrompt("Walk.Prompts.FirstPoint"); } } /** A tool that performs the fly operation */ @@ -1686,17 +1678,16 @@ export class FlyViewTool extends ViewManip { constructor(vp: ScreenViewport, oneShot = false, isDraggingRequired = false) { super(vp, ViewHandleType.Fly, oneShot, isDraggingRequired); } - public onReinitialize(): void { super.onReinitialize(); IModelApp.notifications.outputPromptByKey("CoreTools:tools.View.Fly.Prompts.FirstPoint"); } + public onReinitialize(): void { super.onReinitialize(); ViewTool.showPrompt("Fly.Prompts.FirstPoint"); } } /** A tool that performs a fit view */ export class FitViewTool extends ViewTool { public static toolId = "View.Fit"; - public viewport: ScreenViewport; public oneShot: boolean; public doAnimate: boolean; constructor(viewport: ScreenViewport, oneShot: boolean, doAnimate = true) { - super(); + super(viewport); this.viewport = viewport; this.oneShot = oneShot; this.doAnimate = doAnimate; @@ -1712,7 +1703,7 @@ export class FitViewTool extends ViewTool { public onPostInstall() { super.onPostInstall(); if (undefined === this.viewport || !this.oneShot) - IModelApp.notifications.outputPromptByKey("CoreTools:tools.View.Fit.Prompts.FirstPoint"); + ViewTool.showPrompt("Fit.Prompts.FirstPoint"); if (this.viewport) this.doFit(this.viewport, this.oneShot, this.doAnimate); @@ -1726,6 +1717,33 @@ export class FitViewTool extends ViewTool { } } +/** A tool that rotates the view to one of the standard views. */ +export class StandardViewTool extends ViewTool { + public static toolId = "View.Standard"; + constructor(viewport: ScreenViewport, private _standardViewId: StandardViewId) { super(viewport); } + + public onPostInstall() { + super.onPostInstall(); + if (this.viewport) { + const id = this._standardViewId; + const vp = this.viewport; + const rMatrix = AccuDraw.getStandardRotation(id, vp, vp.isContextRotationRequired); + const inverse = rMatrix.inverse(); + if (undefined !== inverse) { + const targetMatrix = inverse.multiplyMatrixMatrix(vp.rotation); + const rotateTransform = Transform.createFixedPointAndMatrix(vp.view.getTargetPoint(), targetMatrix); + const startFrustum = vp.getFrustum(); + const newFrustum = startFrustum.clone(); + newFrustum.multiply(rotateTransform); + vp.animateFrustumChange(startFrustum, newFrustum); + vp.view.setupFromFrustum(newFrustum); + vp.synchWithView(true); + } + } + this.exitTool(); + } +} + /** A tool that performs a Window-area view operation */ export class WindowAreaTool extends ViewTool { public static toolId = "View.WindowArea"; @@ -1733,15 +1751,12 @@ export class WindowAreaTool extends ViewTool { private _firstPtWorld: Point3d = Point3d.create(); private _secondPtWorld: Point3d = Point3d.create(); private _lastPtView?: Point3d; - private _viewport: Viewport; private _corners = [new Point3d(), new Point3d()]; private _shapePts = [new Point3d(), new Point3d(), new Point3d(), new Point3d(), new Point3d()]; private _fillColor = ColorDef.from(0, 0, 255, 200); - constructor(viewport: Viewport) { super(); this._viewport = viewport; } - - public onPostInstall() { super.onPostInstall(); IModelApp.notifications.outputPromptByKey("CoreTools:tools.View.WindowArea.Prompts.FirstPoint"); } - public onReinitialize() { this._haveFirstPoint = false; this._firstPtWorld.setZero(); this._secondPtWorld.setZero(); IModelApp.notifications.outputPromptByKey("CoreTools:tools.View.WindowArea.Prompts.FirstPoint"); } + public onPostInstall() { super.onPostInstall(); ViewTool.showPrompt("WindowArea.Prompts.FirstPoint"); } + public onReinitialize() { this._haveFirstPoint = false; this._firstPtWorld.setZero(); this._secondPtWorld.setZero(); ViewTool.showPrompt("WindowArea.Prompts.FirstPoint"); } public async onResetButtonUp(ev: BeButtonEvent): Promise { if (this._haveFirstPoint) { this.onReinitialize(); return EventHandled.Yes; } return super.onResetButtonUp(ev); } public async onDataButtonDown(ev: BeButtonEvent): Promise { @@ -1749,13 +1764,13 @@ export class WindowAreaTool extends ViewTool { this._secondPtWorld.setFrom(ev.point); this.doManipulation(ev, false); this.onReinitialize(); - this._viewport.invalidateDecorations(); + this.viewport!.invalidateDecorations(); } else { this._firstPtWorld.setFrom(ev.point); this._secondPtWorld.setFrom(this._firstPtWorld); this._haveFirstPoint = true; this._lastPtView = ev.viewPoint; - IModelApp.notifications.outputPromptByKey("CoreTools:tools.View.WindowArea.Prompts.NextPoint"); + ViewTool.showPrompt("WindowArea.Prompts.NextPoint"); } return EventHandled.Yes; @@ -1769,7 +1784,7 @@ export class WindowAreaTool extends ViewTool { public async onTouchCancel(ev: BeTouchEvent): Promise { if (this._haveFirstPoint) return IModelApp.toolAdmin.convertTouchEndToButtonUp(ev, BeButton.Reset); } private computeWindowCorners(): Point3d[] | undefined { - const vp = this._viewport; + const vp = this.viewport!; const corners = this._corners; corners[0].setFrom(this._firstPtWorld); @@ -1780,7 +1795,7 @@ export class WindowAreaTool extends ViewTool { if (delta.magnitudeXY() < vp.pixelsFromInches(ToolSettings.startDragDistanceInches)) return undefined; - const currentDelta = this._viewport.viewDelta; + const currentDelta = vp.viewDelta; if (currentDelta.x === 0 || delta.x === 0) return undefined; @@ -1806,7 +1821,8 @@ export class WindowAreaTool extends ViewTool { } public decorate(context: DecorateContext): void { - const color = this._viewport.getContrastToBackgroundColor(); + const vp = this.viewport!; + const color = vp.getContrastToBackgroundColor(); if (this._haveFirstPoint) { const corners = this.computeWindowCorners(); if (undefined === corners) @@ -1818,7 +1834,7 @@ export class WindowAreaTool extends ViewTool { this._shapePts[2].y = this._shapePts[3].y = corners[1].y; this._shapePts[0].z = this._shapePts[1].z = this._shapePts[2].z = this._shapePts[3].z = corners[0].z; this._shapePts[4].setFrom(this._shapePts[0]); - this._viewport.viewToWorldArray(this._shapePts); + vp.viewToWorldArray(this._shapePts); const builder = context.createGraphicBuilder(GraphicType.WorldOverlay); @@ -1839,7 +1855,7 @@ export class WindowAreaTool extends ViewTool { return; const cursorPt = this._lastPtView.clone(); cursorPt.x = Math.floor(cursorPt.x) + 0.5; cursorPt.y = Math.floor(cursorPt.y) + 0.5; - const viewRect = this._viewport.viewRect; + const viewRect = vp.viewRect; const drawDecoration = (ctx: CanvasRenderingContext2D) => { ctx.beginPath(); @@ -1867,7 +1883,7 @@ export class WindowAreaTool extends ViewTool { return; let delta: Vector3d; - const vp = this._viewport; + const vp = this.viewport!; const startFrust = vp.getWorldFrustum(); vp.viewToWorldArray(corners); @@ -1935,10 +1951,9 @@ export class DefaultViewTouchTool extends ViewManip { private _startTouchCount = 0; private _frustum = new Frustum(); - constructor(startEv: BeTouchEvent, ev: BeTouchEvent) { + constructor(startEv: BeTouchEvent, _ev: BeTouchEvent) { super(startEv.viewport!, 0, true, false); this.onStart(startEv); - this.handleEvent(ev); } public onStart(ev: BeTouchEvent): void { @@ -2135,12 +2150,10 @@ export class DefaultViewTouchTool extends ViewManip { /** A tool that performs view undo operation. An application could also just call Viewport.doUndo directly, creating a ViewTool isn't required. */ export class ViewUndoTool extends ViewTool { public static toolId = "View.Undo"; - private _viewport: ScreenViewport; - - constructor(vp: ScreenViewport) { super(); this._viewport = vp; } public onPostInstall() { - this._viewport.doUndo(ToolSettings.animationTime); + if (this.viewport) + this.viewport.doUndo(ToolSettings.animationTime); this.exitTool(); } } @@ -2148,12 +2161,10 @@ export class ViewUndoTool extends ViewTool { /** A tool that performs view redo operation. An application could also just call Viewport.doRedo directly, creating a ViewTool isn't required. */ export class ViewRedoTool extends ViewTool { public static toolId = "View.Redo"; - private _viewport: ScreenViewport; - - constructor(vp: ScreenViewport) { super(); this._viewport = vp; } public onPostInstall() { - this._viewport.doRedo(ToolSettings.animationTime); + if (this.viewport) + this.viewport.doRedo(ToolSettings.animationTime); this.exitTool(); } } @@ -2161,19 +2172,21 @@ export class ViewRedoTool extends ViewTool { /** A tool that toggles the camera on/off in a spatial view */ export class ViewToggleCameraTool extends ViewTool { public static toolId = "View.ToggleCamera"; - private _viewport: Viewport; - constructor(viewport: Viewport) { super(); this._viewport = viewport; } - - public onInstall(): boolean { return (undefined !== this._viewport && this._viewport.view.allow3dManipulations()); } + public onInstall(): boolean { return (undefined !== this.viewport && this.viewport.view.allow3dManipulations()); } public onPostInstall(): void { - if (this._viewport.isCameraOn) - (this._viewport.view as ViewState3d).turnCameraOff(); - else - this._viewport.turnCameraOn(); + if (this.viewport) { + const vp = this.viewport; + if (vp.isCameraOn) + (vp.view as ViewState3d).turnCameraOff(); + else + vp.turnCameraOn(); - this._viewport.synchWithView(true); + const startFrustum = vp.getFrustum(); + vp.synchWithView(true); + vp.animateFrustumChange(startFrustum, vp.getFrustum()); + } this.exitTool(); } } diff --git a/core/frontend/src/webgl.ts b/core/frontend/src/webgl.ts index 1337005..55af928 100644 --- a/core/frontend/src/webgl.ts +++ b/core/frontend/src/webgl.ts @@ -11,6 +11,7 @@ export * from "./render/webgl/BranchState"; export * from "./render/webgl/CachedGeometry"; export * from "./render/webgl/ClipVolume"; export * from "./render/webgl/ColorInfo"; +export * from "./render/webgl/Diagnostics"; export * from "./render/webgl/DrawCommand"; export * from "./render/webgl/EdgeOverrides"; export * from "./render/webgl/FeatureDimensions"; diff --git a/core/frontend/src/webworker/WebWorker.ts b/core/frontend/src/webworker/WebWorker.ts new file mode 100644 index 0000000..8116943 --- /dev/null +++ b/core/frontend/src/webworker/WebWorker.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +/* ----------------------------------------- +This is an example of a web worker that can (after transpiling) be loaded by frontend/WebWorkerManager +and invoked to do specific compute-intensive task by calling WebWorkerManager.queueOperation. + +Currently, this source is not used and not built. +That is because we discovered that there wasn't as much to be gained from using web workers +to decode jpeg tile as we had hoped there would be, and it added some complexity, as well as +requiring additional work in some browsers (it worked in Firefox and Chrome, did not in Edge). +To restore it, uncomment the use of it in in GltfTileIO.ts, and add the lines below to the +"submMdules" key to core/frontend/package.json + +{ + "dest": "./lib/module", + "entry": "./lib/webworker/WebWorker.js", + "bundleName": "frontend-webworker", + "type": "webworker" +} + +------------------------------------------- */ +// reply sent through PostMessage +class WorkerReply { + constructor(public msgId: number, public result: any) { } +} + +// error thrown when we detect or catch an error. +class WorkerError extends Error { + constructor(public msgId: number, public originalError: Error) { + super("Worker Error"); + } +} + +class TestWebWorker { + public static add(a: number, b: number): Promise { + return Promise.resolve(a + b); + } + + // example of calculating factorial. Just for illustration, not actually used. + public static factorial(a: number): Promise { + if (a <= 1) + Promise.resolve(1); + let result: number = 1; + for (; a > 1; a--) { + result = result * a; + } + return Promise.resolve(result); + } + + // an example of loading a url and converting it to a ImageBitmap, not actually used. + public static async urlToImageBitmap(url: string): Promise { + // first we have to fetch the file. + const response: Response = await fetch(url, { mode: "cors" }); + const blob = await response.blob(); + const imageBitMap = await createImageBitmap(blob); + return { result: imageBitMap, transferable: true }; + } + + // the incoming message is a Uint8Array. We convert it to an HtmlImageElement and send that back. + // This can be used from GltfTileIO.ts, but is currently commented out. See GltfTileIO.ts for more information. + public static async imageBytesToImageBitmap(bytes: ArrayBuffer, mimeType: string) { + const blob: Blob = new Blob([bytes], { type: mimeType }); + const imageBitMap = await createImageBitmap(blob); + return { result: imageBitMap, transferable: true }; + } + + // message handling method (see the onmessage assignment at the end of the file). + // The operation is sent in a RequestOperation from one of the WebWorkerProxy objects, routed here + // based on the "operation" property on the event, and the result returned. + public static messageHandler(event: MessageEvent) { + if (!event.data.hasOwnProperty("msgId")) { + throw new WorkerError(0, new Error("Improperly formatted message: msgId property needed")); + } + if (!event.data.hasOwnProperty("operation")) { + throw new WorkerError(event.data.msgId, new Error("Improperly formatted message: operation property needed")); + } + if (!event.data.hasOwnProperty("operands")) { + throw new WorkerError(event.data.msgId, new Error("Improperly formatted message: operands array property needed")); + } + const operation = event.data.operation; + const operands = event.data.operands; + const response = (TestWebWorker as any)[operation].apply(TestWebWorker, operands); + response.then((result: any) => { + // We got an object back from the operation. + if (typeof result === "object") { + const reply: WorkerReply = new WorkerReply(event.data.msgId, result.result); + postMessage(reply, result.transferable ? [reply.result] : undefined); + if (result.callback) + result.callback(); + } else { + const reply: WorkerReply = new WorkerReply(event.data.msgId, result); + postMessage(reply); + } + }).catch((error: Error) => { + // the setTimeout here came from a search of trying to send to the onerror: https://stackoverflow.com/questions/39992417/how-to-bubble-a-web-worker-error-in-a-promise-via-worker-onerror + setTimeout(() => { throw new WorkerError(event.data.msgId, error); }); + }); + } +} + +// set up the handler called whenever a message is sent to the web worker. +onmessage = TestWebWorker.messageHandler; diff --git a/core/frontend/src/webworker/tsconfig.json b/core/frontend/src/webworker/tsconfig.json new file mode 100644 index 0000000..12110c2 --- /dev/null +++ b/core/frontend/src/webworker/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../node_modules/@bentley/build-tools/tsconfig-base.json", + "compilerOptions": { + "skipLibCheck": true, + "outDir": "../../lib/webworker", + "composite": true, + "lib": [ + "es2017", + "webworker" + ], + }, + "include": [ + "./**/*.ts", + ] +} \ No newline at end of file diff --git a/core/frontend/tsconfig.json b/core/frontend/tsconfig.json index 020cd2b..b119c83 100644 --- a/core/frontend/tsconfig.json +++ b/core/frontend/tsconfig.json @@ -10,6 +10,12 @@ ], "exclude": [ "lib", - "node_modules" + "node_modules", + "src/webworker/" + ], + "references": [ + { + "path": "./src/webworker" + } ] } \ No newline at end of file diff --git a/core/geometry/CHANGELOG.json b/core/geometry/CHANGELOG.json index b6073bb..1c03efb 100644 --- a/core/geometry/CHANGELOG.json +++ b/core/geometry/CHANGELOG.json @@ -1,6 +1,84 @@ { "name": "@bentley/geometry-core", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/geometry-core_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": " geometry-core camel case" + }, + { + "comment": "allow subclasses of Range to use static methods" + }, + { + "comment": "Changes package.json to include api-extractor and adds api-extractor.json" + }, + { + "comment": "Use new buildIModelJsBuild script" + }, + { + "comment": "AxisAlignedBox and ElementAlignedBox are now typed to Range3d rather than classes" + }, + { + "comment": "clone methods are no longer generic" + }, + { + "comment": "Remove uneeded typedoc plugin depedency" + }, + { + "comment": "PolyfaceBuilder solid primitive improvements" + }, + { + "comment": "PolyfaceBuilder improvements. Construct normals for sweeps. Mesh pairing closure test." + }, + { + "comment": "PolyfaceBuilder creates params and normals for all Solid types" + }, + { + "comment": "Mesh Normal bugs, some @internal markup" + }, + { + "comment": "Consistent naming of \"get\" methods in Growable arrays." + }, + { + "comment": "Distribute .test.ts files to subdirectories" + }, + { + "comment": "Improve polygon triangulations quality by early flipping behind the earcut front" + }, + { + "comment": "added freeze methods to Angle and Point2d" + }, + { + "comment": "bug fixes in PolyfaceBuilder" + }, + { + "comment": "update for geometry GrowableXYArray usage" + }, + { + "comment": "New class SmoothTransformBetweenFrusta for smooth frustum animation" + }, + { + "comment": "Save BUILD_SEMVER to globally accessible map" + }, + { + "comment": "add optional argument to SmoothTransformBetweenFrusta" + }, + { + "comment": "upgrade to TypeScript 3.2.2" + } + ] + } + }, + { + "version": "0.188.0", + "tag": "@bentley/geometry-core_v0.188.0", + "date": "Wed, 16 Jan 2019 16:36:09 GMT", + "comments": {} + }, { "version": "0.187.0", "tag": "@bentley/geometry-core_v0.187.0", diff --git a/core/geometry/CHANGELOG.md b/core/geometry/CHANGELOG.md index 3fdd0f5..3af566b 100644 --- a/core/geometry/CHANGELOG.md +++ b/core/geometry/CHANGELOG.md @@ -1,6 +1,38 @@ # Change Log - @bentley/geometry-core -This log was last generated on Tue, 15 Jan 2019 15:18:59 GMT and should not be manually modified. +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- geometry-core camel case +- allow subclasses of Range to use static methods +- Changes package.json to include api-extractor and adds api-extractor.json +- Use new buildIModelJsBuild script +- AxisAlignedBox and ElementAlignedBox are now typed to Range3d rather than classes +- clone methods are no longer generic +- Remove uneeded typedoc plugin depedency +- PolyfaceBuilder solid primitive improvements +- PolyfaceBuilder improvements. Construct normals for sweeps. Mesh pairing closure test. +- PolyfaceBuilder creates params and normals for all Solid types +- Mesh Normal bugs, some @internal markup +- Consistent naming of "get" methods in Growable arrays. +- Distribute .test.ts files to subdirectories +- Improve polygon triangulations quality by early flipping behind the earcut front +- added freeze methods to Angle and Point2d +- bug fixes in PolyfaceBuilder +- update for geometry GrowableXYArray usage +- New class SmoothTransformBetweenFrusta for smooth frustum animation +- Save BUILD_SEMVER to globally accessible map +- add optional argument to SmoothTransformBetweenFrusta +- upgrade to TypeScript 3.2.2 + +## 0.188.0 +Wed, 16 Jan 2019 16:36:09 GMT + +*Version update only* ## 0.187.0 Tue, 15 Jan 2019 15:18:59 GMT diff --git a/core/geometry/package.json b/core/geometry/package.json index 1c8a408..3d442cc 100644 --- a/core/geometry/package.json +++ b/core/geometry/package.json @@ -1,57 +1,60 @@ { "name": "@bentley/geometry-core", - "version": "0.187.0", + "version": "0.189.0", "license": "MIT", "description": "Bentley Core Geometry library", "main": "lib/geometry-core.js", "typings": "lib/geometry-core", + "scripts": { + "build": "node ./node_modules/@bentley/webpack-tools/bin/buildIModelJsModule.js", + "clean": "rimraf ./lib ./src/test/output package-deps.json", + "watch": "tsc --watch", + "test": "node ./node_modules/@bentley/build-tools/scripts/test.js", + "extract-api": "node ./node_modules/@bentley/build-tools/scripts/extract-api.js --entry=geometry-core", + "docs": "node ./node_modules/@bentley/build-tools/scripts/docs.js --source=./src --tsIndexFile=./geometry-core.ts --json=../../generated-docs/core/geometry-core/file.json --onlyJson %TYPEDOC_THEME%", + "cover": "nyc npm test", + "cover:docs": "node ./node_modules/@bentley/build-tools/scripts/docscoverage.js", + "lint": "tslint --project . 1>&2" + }, + "iModelJs": { + "buildModule": { + "type": "system", + "webpack": { + "dest": "./lib/module", + "entry": "./lib/geometry-core.js", + "bundleName": "geometry-core" + } + } + }, "repository": { "type": "git", "url": "https://github.com/imodeljs/imodeljs" }, + "keywords": [ + "Bentley", + "iModel" + ], + "author": { + "name": "Bentley Systems, Inc.", + "url": "http://www.bentley.com" + }, "devDependencies": { - "@bentley/build-tools": "0.187.0", - "@bentley/webpack-tools": "0.187.0", + "@bentley/build-tools": "0.189.0", + "@bentley/webpack-tools": "0.189.0", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", - "@types/node": "10.10.3", + "@types/node": "10.12.18", "chai": "^4.1.2", "cpx": "^1.5.0", "debug": "^2.6.9", - "make-dir-cli": "^1.0.0", "mocha": "^5.2.0", "nyc": "^13.0.1", "rimraf": "^2.6.2", "semver": "^5.5.0", - "source-map-support": "^0.5.6", - "source-map-loader": "^0.2.3", "ts-node": "^7.0.1", "tslint": "^5.11.0", "typedoc": "^0.11.1", - "typedoc-plugin-external-module-name": "^1.1.1", - "typescript": "~3.1.0", - "webpack": "^4.20.2", - "webpack-cli": "^3.1.0" - }, - "scripts": { - "build": "tsc 1>&2 && npm run webpackModule-dev", - "clean": "rimraf ./lib ./src/test/output package-deps.json", - "watch": "tsc --watch", - "test": "node ./node_modules/@bentley/build-tools/scripts/test.js", - "docs": "node ./node_modules/@bentley/build-tools/scripts/docs.js --source=./src --tsIndexFile=./geometry-core.ts --json=../../generated-docs/core/geometry-core/file.json --onlyJson %TYPEDOC_THEME%", - "cover": "nyc npm test", - "cover:docs": "node ./node_modules/@bentley/build-tools/scripts/docscoverage.js", - "lint": "tslint --project . 1>&2", - "webpackModule-dev": "make-dir ./lib/module/dev/ && webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/geometry-core.js --env.bundlename=geometry-core --json >./lib/module/dev/webpackStats.json", - "webpackModule-prod": "webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/geometry-core.js --env.bundlename=geometry-core --env.prod" - }, - "keywords": [ - "Bentley", - "iModel" - ], - "author": { - "name": "Bentley Systems, Inc.", - "url": "http://www.bentley.com" + "typescript": "~3.2.2" }, "dependencies": {}, "nyc": { diff --git a/core/geometry/src/Geometry.ts b/core/geometry/src/Geometry.ts index e16b9db..67c66a0 100644 --- a/core/geometry/src/Geometry.ts +++ b/core/geometry/src/Geometry.ts @@ -409,7 +409,12 @@ export class Geometry { public static dotProductXYZXYZ(ux: number, uy: number, uz: number, vx: number, vy: number, vz: number): number { return ux * vx + uy * vy + uz * vz; } - + /** + * Clamp to (min(a,b), max(a,b)) + * @param x + * @param a + * @param b + */ public static clampToStartEnd(x: number, a: number, b: number): number { if (a > b) return Geometry.clampToStartEnd(x, b, a); @@ -419,9 +424,17 @@ export class Geometry { return b; return x; } - + /** + * Clamp value to (min,max) with no test for order of (min,max) + * @param value C + * @param min + * @param max + */ public static clamp(value: number, min: number, max: number): number { return Math.max(min, Math.min(max, value)); } + public static resolveNumber(value: number | undefined, defaultValue: number = 0): number { + return value !== undefined ? value : defaultValue; + } /** simple interpolation between values, but choosing (based on fraction) a or b as starting point for maximum accuracy. */ public static interpolate(a: number, f: number, b: number): number { return f <= 0.5 ? a + f * (b - a) : b - (1.0 - f) * (b - a); diff --git a/core/geometry/src/bspline/BSplineCurve.ts b/core/geometry/src/bspline/BSplineCurve.ts index f58c60f..f500183 100644 --- a/core/geometry/src/bspline/BSplineCurve.ts +++ b/core/geometry/src/bspline/BSplineCurve.ts @@ -14,6 +14,7 @@ import { Ray3d } from "../geometry3d/Ray3d"; import { Plane3dByOriginAndVectors } from "../geometry3d/Plane3dByOriginAndVectors"; import { CurvePrimitive } from "../curve/CurvePrimitive"; +import { StrokeCountMap } from "../curve/Query/StrokeCountMap"; import { CurveLocationDetail } from "../curve/CurveLocationDetail"; import { StrokeOptions } from "../curve/StrokeOptions"; @@ -468,7 +469,7 @@ export class BSplineCurve3d extends BSplineCurve3dBase { for (let spanIndex = 0; spanIndex < numSpan; spanIndex++) { const bezier = this.getSaturatedBezierSpan3dOr3dH(spanIndex, false, workBezier); if (bezier) { - numStrokes = bezier.strokeCount(options); + numStrokes = bezier.computeStrokeCountForOptions(options); if (needBeziers) { handler.announceBezierCurve!(bezier, numStrokes, this, spanIndex, @@ -484,6 +485,42 @@ export class BSplineCurve3d extends BSplineCurve3dBase { } } + /** + * Assess legnth and turn to determine a stroke count. + * @param options stroke options structure. + */ + public computeStrokeCountForOptions(options?: StrokeOptions): number { + const workBezier = this.initializeWorkBezier(); + const numSpan = this.numSpan; + let numStroke = 0; + for (let spanIndex = 0; spanIndex < numSpan; spanIndex++) { + const bezier = this.getSaturatedBezierSpan3dH(spanIndex, workBezier); + if (bezier) + numStroke += bezier.computeStrokeCountForOptions(options); + } + return numStroke; + } + /** + * Compute individual segment stroke counts. Attach in a StrokeCountMap. + * @param options StrokeOptions that determine count + * @param parentStrokeMap evolving parent map. + */ + public computeAndAttachRecursiveStrokeCounts(options?: StrokeOptions, parentStrokeMap?: StrokeCountMap) { + const workBezier = this.initializeWorkBezier(); + const numSpan = this.numSpan; + const myData = StrokeCountMap.createWithCurvePrimitiveAndOptionalParent(this, parentStrokeMap, []); + + for (let spanIndex = 0; spanIndex < numSpan; spanIndex++) { + const bezier = this.getSaturatedBezierSpan3dH(spanIndex, workBezier); + if (bezier) { + const segmentLength = workBezier.curveLength(); + const numStrokeOnSegment = workBezier.computeStrokeCountForOptions(options); + myData.addToCountAndLength(numStrokeOnSegment, segmentLength); + } + } + CurvePrimitive.installStrokeCountMap(this, myData, parentStrokeMap); + } + public emitStrokes(dest: LineString3d, options?: StrokeOptions): void { const workBezier = this.initializeWorkBezier(); const numSpan = this.numSpan; @@ -493,7 +530,6 @@ export class BSplineCurve3d extends BSplineCurve3dBase { bezier.emitStrokes(dest, options); } } - /** * Test knots, control points, and wrappable flag to see if all agree for a possible wrapping. * @returns the manner of closing. Se BSplineWrapMode for particulars of each mode. diff --git a/core/geometry/src/bspline/BSplineCurve3dH.ts b/core/geometry/src/bspline/BSplineCurve3dH.ts index 388d1ba..c1729a3 100644 --- a/core/geometry/src/bspline/BSplineCurve3dH.ts +++ b/core/geometry/src/bspline/BSplineCurve3dH.ts @@ -24,6 +24,8 @@ import { Point3dArray, Point4dArray } from "../geometry3d/PointHelpers"; import { BezierCurveBase } from "./BezierCurveBase"; import { BezierCurve3dH } from "./BezierCurve3dH"; import { BSplineCurve3dBase } from "./BSplineCurve"; +import { CurvePrimitive } from "../curve/CurvePrimitive"; +import { StrokeCountMap } from "../curve/Query/StrokeCountMap"; /** * Weighted (Homogeneous) BSplineCurve in 3d @@ -208,7 +210,7 @@ export class BSplineCurve3dH extends BSplineCurve3dBase { for (let spanIndex = 0; spanIndex < numSpan; spanIndex++) { const bezier = this.getSaturatedBezierSpan3dOr3dH(spanIndex, false, workBezier); if (bezier) { - numStrokes = bezier.strokeCount(options); + numStrokes = bezier.computeStrokeCountForOptions(options); if (needBeziers) { (handler as any).announceBezierCurve(bezier, numStrokes, this, spanIndex, @@ -233,7 +235,41 @@ export class BSplineCurve3dH extends BSplineCurve3dBase { bezier.emitStrokes(dest, options); } } + /** + * Assess legnth and turn to determine a stroke count. + * @param options stroke options structure. + */ + public computeStrokeCountForOptions(options?: StrokeOptions): number { + const workBezier = this.initializeWorkBezier(); + const numSpan = this.numSpan; + let numStroke = 0; + for (let spanIndex = 0; spanIndex < numSpan; spanIndex++) { + const bezier = this.getSaturatedBezierSpan3dH(spanIndex, workBezier); + if (bezier) + numStroke += bezier.computeStrokeCountForOptions(options); + } + return numStroke; + } + /** + * Compute individual segment stroke counts. Attach in a StrokeCountMap. + * @param options StrokeOptions that determine count + * @param parentStrokeMap evolving parent map. + */ + public computeAndAttachRecursiveStrokeCounts(options?: StrokeOptions, parentStrokeMap?: StrokeCountMap) { + const workBezier = this.initializeWorkBezier(); + const numSpan = this.numSpan; + const myData = StrokeCountMap.createWithCurvePrimitiveAndOptionalParent(this, parentStrokeMap, []); + for (let spanIndex = 0; spanIndex < numSpan; spanIndex++) { + const bezier = this.getSaturatedBezierSpan3dH(spanIndex, workBezier); + if (bezier) { + const segmentLength = workBezier.curveLength(); + const numStrokeOnSegment = workBezier.computeStrokeCountForOptions(options); + myData.addToCountAndLength(numStrokeOnSegment, segmentLength); + } + } + CurvePrimitive.installStrokeCountMap(this, myData, parentStrokeMap); + } /** * return true if the spline is (a) unclamped with (degree-1) matching knot intervals, * (b) (degree-1) wrapped points, diff --git a/core/geometry/src/bspline/BezierCurve3d.ts b/core/geometry/src/bspline/BezierCurve3d.ts index 4618a89..a50f0d2 100644 --- a/core/geometry/src/bspline/BezierCurve3d.ts +++ b/core/geometry/src/bspline/BezierCurve3d.ts @@ -162,7 +162,7 @@ export class BezierCurve3d extends BezierCurveBase { * Assess legnth and turn to determine a stroke count. * @param options stroke options structure. */ - public strokeCount(options?: StrokeOptions): number { + public computeStrokeCountForOptions(options?: StrokeOptions): number { const data = this._polygon.packedData; let dx0 = data[3] - data[0]; let dy0 = data[4] - data[1]; diff --git a/core/geometry/src/bspline/BezierCurve3dH.ts b/core/geometry/src/bspline/BezierCurve3dH.ts index 091f5e7..f470f55 100644 --- a/core/geometry/src/bspline/BezierCurve3dH.ts +++ b/core/geometry/src/bspline/BezierCurve3dH.ts @@ -200,7 +200,7 @@ export class BezierCurve3dH extends BezierCurveBase { * Assess legnth and turn to determine a stroke count. * @param options stroke options structure. */ - public strokeCount(options?: StrokeOptions): number { + public computeStrokeCountForOptions(options?: StrokeOptions): number { // ugh. for pure 3d case, local dx,dy,dz vars worked efficiently. // managing the weights is tricky, so just do the easy code with temporary point vars. this.getPolePoint3d(0, this._workPoint0); diff --git a/core/geometry/src/bspline/BezierCurveBase.ts b/core/geometry/src/bspline/BezierCurveBase.ts index 2f16384..6bb0c77 100644 --- a/core/geometry/src/bspline/BezierCurveBase.ts +++ b/core/geometry/src/bspline/BezierCurveBase.ts @@ -82,11 +82,11 @@ export abstract class BezierCurveBase extends CurvePrimitive { public setInterval(a: number, b: number) { this._polygon.setInterval(a, b); } public fractionToParentFraction(fraction: number): number { return this._polygon.fractionToParentFraction(fraction); } /** Return a stroke count appropriate for given stroke options. */ - public abstract strokeCount(options?: StrokeOptions): number; + public abstract computeStrokeCountForOptions(options?: StrokeOptions): number; /** append stroke points to a linestring, based on `strokeCount` and `fractionToPoint` from derived class*/ public emitStrokes(dest: LineString3d, options?: StrokeOptions): void { - const numPerSpan = this.strokeCount(options); + const numPerSpan = this.computeStrokeCountForOptions(options); const fractionStep = 1.0 / numPerSpan; for (let i = 0; i <= numPerSpan; i++) { const fraction = i * fractionStep; @@ -96,7 +96,7 @@ export abstract class BezierCurveBase extends CurvePrimitive { } /** announce intervals with stroke counts */ public emitStrokableParts(handler: IStrokeHandler, _options?: StrokeOptions): void { - const numPerSpan = this.strokeCount(_options); + const numPerSpan = this.computeStrokeCountForOptions(_options); handler.announceIntervalForUniformStepStrokes(this, numPerSpan, 0.0, 1.0); } /** Return a simple array of arrays with the control points as `[[x,y,z],[x,y,z],..]` */ diff --git a/core/geometry/src/clipping/ClipUtils.ts b/core/geometry/src/clipping/ClipUtils.ts index 5bd099a..e8d74a6 100644 --- a/core/geometry/src/clipping/ClipUtils.ts +++ b/core/geometry/src/clipping/ClipUtils.ts @@ -51,13 +51,13 @@ export class ClipUtilities { unsortedFractions.push(0); unsortedFractions.push(1); unsortedFractions.sort(); - let f0 = unsortedFractions.at(0); + let f0 = unsortedFractions.atUncheckedIndex(0); let f1; let fMid; const testPoint = ClipUtilities._selectIntervals01TestPoint; const n = unsortedFractions.length; for (let i = 1; i < n; i++ , f0 = f1) { - f1 = unsortedFractions.at(i); + f1 = unsortedFractions.atUncheckedIndex(i); fMid = 0.5 * (f0 + f1); if (f1 > f0 && (fMid >= 0.0 && fMid <= 1.0)) { curve.fractionToPoint(fMid, testPoint); @@ -109,7 +109,11 @@ export class ClipUtilities { return output; } - /** Given an array of points, return whether or not processing is required to clip to a ClipPlaneSet region. */ + /** Given an array of points, test for trivial containment conditions. + * * ClipStatus.TrivialAccept if all points are in any one of the convexSet's. + * * ClipStatus.ClipRequired if (in any single convexSet) there were points on both sides of any single plane. + * * ClipStatus.TrivialReject if neither of those occurred. + */ public static pointSetSingleClipStatus(points: GrowableXYZArray, planeSet: UnionOfConvexClipPlaneSets, tolerance: number): ClipStatus { if (planeSet.convexSets.length === 0) return ClipStatus.TrivialAccept; @@ -124,7 +128,7 @@ export class ClipUtilities { const currPt = Point3d.create(); const currVec = Vector3d.create(); for (let i = 0; i < points.length; i++) { - points.getPoint3dAt(i, currPt); + points.getPoint3dAtUncheckedPointIndex(i, currPt); currVec.setFrom(currPt); currVec.dotProduct(plane.inwardNormalRef) > planeDistance ? numInside++ : numOutside++; } diff --git a/core/geometry/src/curve/Arc3d.ts b/core/geometry/src/curve/Arc3d.ts index 1fea4a4..c4db54a 100644 --- a/core/geometry/src/curve/Arc3d.ts +++ b/core/geometry/src/curve/Arc3d.ts @@ -325,7 +325,7 @@ export class Arc3d extends CurvePrimitive implements BeJSONFunctions { const uu = this.matrix.columnXMagnitudeSquared(); const uv = this._matrix.columnXDotColumnY(); const vv = this._matrix.columnYMagnitudeSquared(); - TrigPolynomial.SolveUnitCircleImplicitQuadricIntersection( + TrigPolynomial.solveUnitCircleImplicitQuadricIntersection( uv, vv - uu, -uv, @@ -546,29 +546,31 @@ export class Arc3d extends CurvePrimitive implements BeJSONFunctions { /** Emit strokes to caller-supplied linestring */ public emitStrokes(dest: LineString3d, options?: StrokeOptions): void { - let numStrokes = 1; - if (options) { - const rMax = this.maxVectorLength(); - numStrokes = options.applyTolerancesToArc(rMax, this._sweep.sweepRadians); - } else { - numStrokes = StrokeOptions.applyAngleTol(undefined, 1, this._sweep.sweepRadians); - } + const numStrokes = this.computeStrokeCountForOptions(options); dest.appendFractionalStrokePoints(this, numStrokes, 0.0, 1.0, true); } /** Emit strokes to caller-supplied handler */ public emitStrokableParts(handler: IStrokeHandler, options?: StrokeOptions): void { - let numStrokes = 1; + const numStrokes = this.computeStrokeCountForOptions(options); + handler.startCurvePrimitive(this); + handler.announceIntervalForUniformStepStrokes(this, numStrokes, 0.0, 1.0); + handler.endCurvePrimitive(this); + } + + /** + * return the stroke count required for given options. + * @param options StrokeOptions that determine count + */ + public computeStrokeCountForOptions(options?: StrokeOptions): number { + let numStroke = 1; if (options) { const rMax = this.maxVectorLength(); - numStrokes = options.applyTolerancesToArc(rMax, this._sweep.sweepRadians); + numStroke = options.applyTolerancesToArc(rMax, this._sweep.sweepRadians); } else { - numStrokes = StrokeOptions.applyAngleTol(undefined, 1, this._sweep.sweepRadians); + numStroke = StrokeOptions.applyAngleTol(undefined, 1, this._sweep.sweepRadians); } - - handler.startCurvePrimitive(this); - handler.announceIntervalForUniformStepStrokes(this, numStrokes, 0.0, 1.0); - handler.endCurvePrimitive(this); + return numStroke; } public dispatchToGeometryHandler(handler: GeometryHandler): any { diff --git a/core/geometry/src/curve/ConstructCurveBetweenCurves.ts b/core/geometry/src/curve/ConstructCurveBetweenCurves.ts index 0d58784..ae8cff4 100644 --- a/core/geometry/src/curve/ConstructCurveBetweenCurves.ts +++ b/core/geometry/src/curve/ConstructCurveBetweenCurves.ts @@ -10,8 +10,9 @@ import { NullGeometryHandler } from "../geometry3d/GeometryHandler"; import { LineSegment3d } from "./LineSegment3d"; import { Arc3d } from "./Arc3d"; -import { Point3d } from "../geometry3d/Point3dVector3d"; +import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; import { LineString3d } from "./LineString3d"; +import { Geometry } from "../Geometry"; /** * Context for constructing a curve that is interpolated between two other curves. @@ -52,16 +53,43 @@ export class ConstructCurveBetweenCurves extends NullGeometryHandler { if (this._geometry1 instanceof LineString3d) { const ls1 = this._geometry1 as LineString3d; if (ls0.numPoints() === ls1.numPoints()) { + const numPoints = ls0.numPoints(); const ls = LineString3d.create(); const workPoint = Point3d.create(); const workPoint0 = Point3d.create(); const workPoint1 = Point3d.create(); - for (let i = 0; i < ls0.numPoints(); i++) { + let workVector0; + let workVector1; + const fraction = this._fraction; + for (let i = 0; i < numPoints; i++) { ls0.pointAt(i, workPoint0); ls1.pointAt(i, workPoint1); - workPoint0.interpolate(this._fraction, workPoint1, workPoint); + workPoint0.interpolate(fraction, workPoint1, workPoint); ls.addPoint(workPoint); } + if (ls0.fractions && ls1.fractions) { + for (let i = 0; i < numPoints; i++) { + ls.addFraction(Geometry.interpolate(ls0.fractions.atUncheckedIndex(i), fraction, ls1.fractions.atUncheckedIndex(i))); + } + } + if (ls0.strokeData && ls1.strokeData) { + // Policy: simple clone of stroke count map from ls0. + // The curveLength will not match. + // But we expect to be called at a time compatible count and a0,a1 are the important thing. + ls.strokeData = ls0.strokeData.clone(); + } + if (ls0.packedDerivatives && ls1.packedDerivatives) { + if (!workVector0) + workVector0 = Vector3d.create(); + if (!workVector1) + workVector1 = Vector3d.create(); + for (let i = 0; i < numPoints; i++) { + ls0.packedDerivatives.getVector3dAtCheckedVectorIndex(i, workVector0); + ls1.packedDerivatives.getVector3dAtCheckedVectorIndex(i, workVector1); + ls.addDerivative(workVector0.interpolate(fraction, workVector1)); + } + + } return ls; } } @@ -94,7 +122,7 @@ export class ConstructCurveBetweenCurves extends NullGeometryHandler { * @param fraction fractional positon * @param geometry1 geometry "at fraction 1" */ - public static InterpolateBetween(geometry0: GeometryQuery, fraction: number, geometry1: GeometryQuery): GeometryQuery | undefined { + public static interpolateBetween(geometry0: GeometryQuery, fraction: number, geometry1: GeometryQuery): GeometryQuery | undefined { const handler = new ConstructCurveBetweenCurves(geometry0, fraction, geometry1); return geometry0.dispatchToGeometryHandler(handler); } diff --git a/core/geometry/src/curve/CurveChainWithDistanceIndex.ts b/core/geometry/src/curve/CurveChainWithDistanceIndex.ts index b3ea281..3437650 100644 --- a/core/geometry/src/curve/CurveChainWithDistanceIndex.ts +++ b/core/geometry/src/curve/CurveChainWithDistanceIndex.ts @@ -10,7 +10,8 @@ import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; import { Transform } from "../geometry3d/Transform"; import { IStrokeHandler, GeometryHandler } from "../geometry3d/GeometryHandler"; import { StrokeOptions } from "./StrokeOptions"; -import { CurvePrimitive } from "./CurvePrimitive"; +import { CurvePrimitive } from "../curve/CurvePrimitive"; +import { StrokeCountMap } from "../curve/Query/StrokeCountMap"; import { GeometryQuery } from "./GeometryQuery"; import { CurveChain } from "./CurveCollection"; import { Plane3dByOriginAndUnitNormal } from "../geometry3d/Plane3dByOriginAndUnitNormal"; @@ -228,9 +229,32 @@ export class CurveChainWithDistanceIndex extends CurvePrimitive { c.emitStrokableParts(dest, options); } } + /** + * return the stroke count required for given options. + * @param options StrokeOptions that determine count + */ + public computeStrokeCountForOptions(options?: StrokeOptions): number { + let numStroke = 0; + for (const c of this._path.children) { + numStroke += c.computeStrokeCountForOptions(options); + } + return numStroke; + } + /** + * construct StrokeCountMap for each child, accumulating data to stroke count map for this primitive. + * @param options StrokeOptions that determine count + * @param parentStrokeMap evolving parent map. + */ + public computeAndAttachRecursiveStrokeCounts(options?: StrokeOptions, parentStrokeMap?: StrokeCountMap) { + const myMap = StrokeCountMap.createWithCurvePrimitiveAndOptionalParent(this, parentStrokeMap); + for (const c of this._path.children) { + c.computeAndAttachRecursiveStrokeCounts(options, myMap); + } + CurvePrimitive.installStrokeCountMap(this, myMap, parentStrokeMap); + } /** dispatch the path to the handler */ public dispatchToGeometryHandler(handler: GeometryHandler): any { - this._path.dispatchToGeometryHandler(handler); + return this._path.dispatchToGeometryHandler(handler); } /** Extend (increase) `rangeToExtend` as needed to include these curves (optionally transformed) */ diff --git a/core/geometry/src/curve/CurveCollection.ts b/core/geometry/src/curve/CurveCollection.ts index deda482..efb01f5 100644 --- a/core/geometry/src/curve/CurveCollection.ts +++ b/core/geometry/src/curve/CurveCollection.ts @@ -15,6 +15,7 @@ import { LineSegment3d } from "./LineSegment3d"; import { LineString3d } from "./LineString3d"; import { GrowableXYZArray } from "../geometry3d/GrowableXYZArray"; import { GeometryHandler } from "../geometry3d/GeometryHandler"; + // import { SumLengthsContext, GapSearchContext, CountLinearPartsSearchContext, CloneCurvesContext, TransformInPlaceContext } from "./CurveSearches"; /** Algorithmic class: Accumulate maximum gap between adjacent primitives of CurveChain. diff --git a/core/geometry/src/curve/CurveCurveIntersectXY.ts b/core/geometry/src/curve/CurveCurveIntersectXY.ts index 46b5157..b03b211 100644 --- a/core/geometry/src/curve/CurveCurveIntersectXY.ts +++ b/core/geometry/src/curve/CurveCurveIntersectXY.ts @@ -320,8 +320,8 @@ class CurveCurveIntersectXY extends NullGeometryHandler { const radians = new GrowableFloat64Array(2); const numRoots = AnalyticRoots.appendImplicitLineUnitCircleIntersections(alpha, beta, gamma, cosines, sines, radians); for (let i = 0; i < numRoots; i++) { - const arcPoint = data.center.plus2Scaled(data.vector0, cosines.at(i), data.vector90, sines.at(i)); - const arcFraction = data.sweep.radiansToSignedPeriodicFraction(radians.at(i)); + const arcPoint = data.center.plus2Scaled(data.vector0, cosines.atUncheckedIndex(i), data.vector90, sines.atUncheckedIndex(i)); + const arcFraction = data.sweep.radiansToSignedPeriodicFraction(radians.atUncheckedIndex(i)); const lineFraction = SmallSystem.lineSegment3dHXYClosestPointUnbounded(pointA0H, pointA1H, arcPoint); if (lineFraction !== undefined && this.acceptFraction(extendA0, lineFraction, extendA1) && this.acceptFraction(extendB0, arcFraction, extendB1)) { this.recordPointWithLocalFractions(lineFraction, cpA, fractionA0, fractionA1, @@ -344,8 +344,8 @@ class CurveCurveIntersectXY extends NullGeometryHandler { const radians = new GrowableFloat64Array(2); const numRoots = AnalyticRoots.appendImplicitLineUnitCircleIntersections(alpha, beta, gamma, cosines, sines, radians); for (let i = 0; i < numRoots; i++) { - const arcPoint = data.center.plus2Scaled(data.vector0, cosines.at(i), data.vector90, sines.at(i)); - const arcFraction = data.sweep.radiansToSignedPeriodicFraction(radians.at(i)); + const arcPoint = data.center.plus2Scaled(data.vector0, cosines.atUncheckedIndex(i), data.vector90, sines.atUncheckedIndex(i)); + const arcFraction = data.sweep.radiansToSignedPeriodicFraction(radians.atUncheckedIndex(i)); const lineFraction = SmallSystem.lineSegment3dXYClosestPointUnbounded(pointA0Local, pointA1Local, arcPoint); if (lineFraction !== undefined && this.acceptFraction(extendA0, lineFraction, extendA1) && this.acceptFraction(extendB0, arcFraction, extendB1)) { this.recordPointWithLocalFractions(lineFraction, cpA, fractionA0, fractionA1, @@ -360,7 +360,7 @@ class CurveCurveIntersectXY extends NullGeometryHandler { // invert the projection matrix matrixA. // apply the inverse to matrixB. Then arcb is an ellipse in the circular space of A - private dispatchArcArc_thisOrder( + private dispatchArcArcThisOrder( cpA: Arc3d, matrixA: Matrix3d, // homogeneous xyw projection !!! extendA: boolean, @@ -374,7 +374,7 @@ class CurveCurveIntersectXY extends NullGeometryHandler { const localB = inverseA.multiplyMatrixMatrix(matrixB); const ellipseRadians: number[] = []; const circleRadians: number[] = []; - TrigPolynomial.SolveUnitCircleHomogeneousEllipseIntersection( + TrigPolynomial.solveUnitCircleHomogeneousEllipseIntersection( localB.coffs[2], localB.coffs[5], localB.coffs[8], // center xyw localB.coffs[0], localB.coffs[3], localB.coffs[6], // center xyw localB.coffs[1], localB.coffs[4], localB.coffs[7], // center xyw @@ -424,9 +424,9 @@ class CurveCurveIntersectXY extends NullGeometryHandler { const conditionA = matrixA.conditionNumber(); const conditionB = matrixB.conditionNumber(); if (conditionA > conditionB) - this.dispatchArcArc_thisOrder(cpA, matrixA, extendA, cpB, matrixB, extendB, reversed); + this.dispatchArcArcThisOrder(cpA, matrixA, extendA, cpB, matrixB, extendB, reversed); else - this.dispatchArcArc_thisOrder(cpB, matrixB, extendB, cpA, matrixA, extendA, !reversed); + this.dispatchArcArcThisOrder(cpB, matrixB, extendB, cpA, matrixA, extendA, !reversed); } // Caller accesses data from two arcs. // Selects the best conditioned arc (in xy parts) as "circle after inversion" @@ -623,8 +623,8 @@ class CurveCurveIntersectXY extends NullGeometryHandler { for (let a = 0; a < numA; a++) { for (let b = 0; b < numB; b++) { if (rangeA[a].intersectsRangeXY(rangeB[b])) { - const strokeCountA = bezierSpanA[a].strokeCount(); - const strokeCountB = bezierSpanB[b].strokeCount(); + const strokeCountA = bezierSpanA[a].computeStrokeCountForOptions(); + const strokeCountB = bezierSpanB[b].computeStrokeCountForOptions(); if (strokeCountA < strokeCountB) this.dispatchBezierBezierStrokeFirst(bezierSpanA[a], bcurveA, strokeCountA, bezierSpanB[b], bcurveB, strokeCountB, univairateCoffsB, !_reversed); else @@ -897,7 +897,7 @@ export class CurveCurve { * @param geometryB second geometry * @param extendB true to allow geometryB to extend */ - public static IntersectionXY(geometryA: GeometryQuery, extendA: boolean, geometryB: GeometryQuery, extendB: boolean): CurveLocationDetailArrayPair { + public static intersectionXY(geometryA: GeometryQuery, extendA: boolean, geometryB: GeometryQuery, extendB: boolean): CurveLocationDetailArrayPair { const handler = new CurveCurveIntersectXY(undefined, geometryA, extendA, geometryB, extendB); geometryA.dispatchToGeometryHandler(handler); return handler.grabResults(); @@ -910,7 +910,7 @@ export class CurveCurve { * @param geometryB second geometry * @param extendB true to allow geometryB to extend */ - public static IntersectionProjectedXY(worldToLocal: Matrix4d, + public static intersectionProjectedXY(worldToLocal: Matrix4d, geometryA: GeometryQuery, extendA: boolean, geometryB: GeometryQuery, extendB: boolean): CurveLocationDetailArrayPair { const handler = new CurveCurveIntersectXY(worldToLocal, geometryA, extendA, geometryB, extendB); geometryA.dispatchToGeometryHandler(handler); diff --git a/core/geometry/src/curve/CurvePrimitive.ts b/core/geometry/src/curve/CurvePrimitive.ts index 767e312..5024e43 100644 --- a/core/geometry/src/curve/CurvePrimitive.ts +++ b/core/geometry/src/curve/CurvePrimitive.ts @@ -19,6 +19,8 @@ import { LineString3d } from "./LineString3d"; import { Clipper } from "../clipping/ClipUtils"; import { CurveLocationDetail, CurveSearchStatus } from "./CurveLocationDetail"; import { GeometryQuery } from "./GeometryQuery"; +import { StrokeCountMap } from "../curve/Query/StrokeCountMap"; + /** Type for callback function which announces a pair of numbers, such as a fractional interval, along with a containing CurvePrimitive. */ export type AnnounceNumberNumberCurvePrimitive = (a0: number, a1: number, cp: CurvePrimitive) => void; export type AnnounceNumberNumber = (a0: number, a1: number) => void; @@ -39,6 +41,11 @@ export type AnnounceCurvePrimitive = (cp: CurvePrimitive) => void; */ export abstract class CurvePrimitive extends GeometryQuery { protected constructor() { super(); } + /** + * data attached during stroking for facets. + */ + public strokeData?: StrokeCountMap; + /** Return the point (x,y,z) on the curve at fractional position. * @param fraction fractional position along the geometry. * @returns Returns a point on the curve. @@ -91,6 +98,17 @@ export abstract class CurvePrimitive extends GeometryQuery { return Transform.createRefs(plane.origin, axes, result); return undefined; } + + /** + * Construct a point extrapolated along tangent at fraction. + * @param fraction fractional position on the primitive + * @param distance (signed) distance to move on the tangent. + */ + public fractionAndDistanceToPointOnTangent(fraction: number, distance: number): Point3d { + const ray = this.fractionToPointAndUnitTangent(fraction); + return ray.fractionToPoint(distance); + } + /** * * * Curve length is always positive. @@ -361,6 +379,68 @@ export abstract class CurvePrimitive extends GeometryQuery { * See IStrokeHandler for description of the sequence of the method calls. */ public abstract emitStrokableParts(dest: IStrokeHandler, options?: StrokeOptions): void; + /** + * return the stroke count required for given options. + * * This returns a single number + * * See computeComponentStrokeCountForOptions to get structured per-component counts and fraction mappings. + * @param options StrokeOptions that determine count + */ + public abstract computeStrokeCountForOptions(options?: StrokeOptions): number; + + /** + * attach StrokeCountMap structure to this primitive (and recursively to any children) + * * Base class implementation (here) gets the simple count from computeStrokeCountForOptions and attaches it. + * * LineString3d, arc3d, BezierCurve3d, BezierCurve3dH accept that default. + * * Subdivided primitives (linestring, bspline curve) implment themselves and attach a StrokeCountMap containing the + * total count, and also containing an array of StrokeCountMap per component. + * * For CurvePrimitiveWithDistanceIndex, the top level gets (only) a total count, and each child gets + * its own StrokeCountMap with appropriate structure. + * @param options StrokeOptions that determine count + * @param parentStrokeMap optional map from parent. Its count, curveLength, and a1 values are incresed with count and distance from this primitive. + * @return sum of `a0+this.curveLength()`, for use as `a0` of successor in chain. + */ + public computeAndAttachRecursiveStrokeCounts(options?: StrokeOptions, parentMap?: StrokeCountMap) { + const n = this.computeStrokeCountForOptions(options); + const a = this.curveLength(); + CurvePrimitive.installStrokeCountMap( + this, + StrokeCountMap.createWithCurvePrimitive(this, n, a, 0, a), + parentMap); + } + + /** + * * evaluate strokes at fractions indicated in a StrokeCountMap. + * * Base class implementation (here) gets the simple count from computeStrokeCountForOptions and strokes at uniform fractions. + * * LineString3d, arc3d, BezierCurve3d, BezierCurve3dH accept that default. + * * Subdivided primitives (linestring, bspline curve) implment themselves and evaluate within components. + * * CurvePrimitiveWithDistanceIndex recurses to its children. + * * if packedFraction and packedDerivative arrays are present in the LineString3d, fill them. + * @param map = stroke count data. + * @param linestring = receiver linestring. + * @return number of strokes added. 0 if any errors matching the map to the curve primitive. + */ + public addMappedStrokesToLineString3D(map: StrokeCountMap, linestring: LineString3d): number { + const numPoint0 = linestring.numPoints(); + if (map.primitive && map.primitive === this && map.numStroke > 0) { + for (let i = 0; i <= map.numStroke; i++) { + const fraction = i / map.numStroke; + linestring.appendFractionToPoint(this, fraction); + } + } + return linestring.numPoints() - numPoint0; + } + + /** + * final install step to save curveMap in curve. If parentMap is given, update its length, count, and a1 fields + * @param curve curve to receive the annotation + * @param map + * @param parentMap + */ + public static installStrokeCountMap(curve: CurvePrimitive, curveMap: StrokeCountMap, parentMap?: StrokeCountMap) { + if (parentMap) + parentMap.addToCountAndLength(curveMap.numStroke, curveMap.curveLength); + curve.strokeData = curveMap; + } } /** Intermediate class for managing the parentCurve announcements from an IStrokeHandler */ diff --git a/core/geometry/src/curve/LineSegment3d.ts b/core/geometry/src/curve/LineSegment3d.ts index 13d521a..c9742e5 100644 --- a/core/geometry/src/curve/LineSegment3d.ts +++ b/core/geometry/src/curve/LineSegment3d.ts @@ -252,29 +252,29 @@ export class LineSegment3d extends CurvePrimitive implements BeJSONFunctions { /** Emit strokes to caller-supplied linestring */ public emitStrokes(dest: LineString3d, options?: StrokeOptions): void { - - if (options) { - let numStroke = 1; - if (options.maxEdgeLength) - numStroke = options.applyMaxEdgeLength(numStroke, this.curveLength()); - numStroke = options.applyMinStrokesPerPrimitive(numStroke); - dest.appendFractionalStrokePoints(this, numStroke, 0.0, 1.0); - } else { - dest.appendFractionalStrokePoints(this, 1, 0.0, 1.0); - } + const numStroke = this.computeStrokeCountForOptions(options); + dest.appendFractionalStrokePoints(this, numStroke, 0.0, 1.0); } /** Emit strokes to caller-supplied handler */ public emitStrokableParts(handler: IStrokeHandler, options?: StrokeOptions): void { handler.startCurvePrimitive(this); - const tangent = this._point0.vectorTo(this._point1); + const numStroke = this.computeStrokeCountForOptions(options); + handler.announceSegmentInterval(this, this._point0, this._point1, numStroke, 0.0, 1.0); + handler.endCurvePrimitive(this); + } + + /** + * return the stroke count required for given options. + * @param options StrokeOptions that determine count + */ + public computeStrokeCountForOptions(options?: StrokeOptions): number { let numStroke = 1; if (options) { if (options.maxEdgeLength) - numStroke = options.applyMaxEdgeLength(numStroke, tangent.magnitude()); + numStroke = options.applyMaxEdgeLength(numStroke, this.curveLength()); numStroke = options.applyMinStrokesPerPrimitive(numStroke); } - handler.announceSegmentInterval(this, this._point0, this._point1, numStroke, 0.0, 1.0); - handler.endCurvePrimitive(this); + return numStroke; } public dispatchToGeometryHandler(handler: GeometryHandler): any { diff --git a/core/geometry/src/curve/LineString3d.ts b/core/geometry/src/curve/LineString3d.ts index a26bbcd..d63c386 100644 --- a/core/geometry/src/curve/LineString3d.ts +++ b/core/geometry/src/curve/LineString3d.ts @@ -2,14 +2,11 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ - /** @module Curve */ - import { Geometry, AxisOrder, BeJSONFunctions, PlaneAltitudeEvaluator } from "../Geometry"; import { Angle } from "../geometry3d/Angle"; import { XAndY } from "../geometry3d/XYZProps"; import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Point2d } from "../geometry3d/Point2dVector2d"; import { Range3d } from "../geometry3d/Range"; import { Transform } from "../geometry3d/Transform"; import { Matrix3d } from "../geometry3d/Matrix3d"; @@ -22,89 +19,13 @@ import { GrowableXYArray } from "../geometry3d/GrowableXYArray"; import { GeometryHandler, IStrokeHandler } from "../geometry3d/GeometryHandler"; import { StrokeOptions } from "./StrokeOptions"; import { CurvePrimitive, AnnounceNumberNumberCurvePrimitive } from "./CurvePrimitive"; +import { StrokeCountMap } from "./Query/StrokeCountMap"; import { GeometryQuery } from "./GeometryQuery"; import { CurveLocationDetail, CurveSearchStatus, CurveIntervalRole } from "./CurveLocationDetail"; import { Clipper } from "../clipping/ClipUtils"; import { LineSegment3d } from "./LineSegment3d"; + /* tslint:disable:variable-name no-empty*/ -/** - * context to be called to incrementally accumulate distance along line segments. - */ -class MoveByDistanceContext { - public distance0: number; // accumulated distance through point0 - public point0: Point3d; // most recent point - public fraction0: number; // most recent fraction position - public targetDistance: number; // this is always positive. - /** CAPTURE point0, fraction0, targetDistance */ - public constructor(point0: Point3d, fraction0: number, targetDistance: number) { - this.point0 = point0; - this.distance0 = 0.0; - this.targetDistance = Math.abs(targetDistance); - this.fraction0 = fraction0; - } - // Return CurveSearchStatus indicating whether the accumulated distance has reached the target. - public distanceStatus(): CurveSearchStatus { - return Geometry.isSameCoordinate(this.distance0, this.targetDistance) ? - CurveSearchStatus.success : CurveSearchStatus.stoppedAtBoundary; - } - /** - * Announce next point on the polyline. - * * if the additional segment does NOT reach the target: - * * accumulate the segment length - * * update point0 and fraction0 - * * return false - * * if the additional segment DOES reach the target: - * * update point0 and fraction0 to the (possibly interpolated) final point and fraction - * * return true - * @param point1 new point - * @param fraction1 fraction at point1 - * @return true if targetDistance reached. - */ - public announcePoint(point1: Point3d, fraction1: number): boolean { - const a = this.point0.distance(point1); - const distance1 = this.distance0 + a; - if (distance1 < this.targetDistance && !Geometry.isSameCoordinate(distance1, this.targetDistance)) { - this.point0.setFromPoint3d(point1); - this.distance0 = distance1; - this.fraction0 = fraction1; - return false; - } - const b = this.targetDistance - this.distance0; - const intervalFraction = Geometry.safeDivideFraction(b, a, 0.0); - this.point0.interpolate(intervalFraction, point1, this.point0); - this.fraction0 = Geometry.interpolate(this.fraction0, intervalFraction, fraction1); - this.distance0 = this.targetDistance; - return true; - } - /** - * Update point0, fraction0, and distance0 based on extrapolation of a segment between indices of a point array. - * @returns true if extraploation succeeded. (False if indexed points are coincident) - * @param points - * @param index0 - * @param index1 - * @param fraction0 - * @param fraction1 - * @param result - * @param CurveLocationDetail - */ - public announceExtrapolation(points: GrowableXYZArray, - index0: number, index1: number, - fraction0: number, fraction1: number): boolean { - const residual = this.targetDistance - this.distance0; - const d01 = points.distance(index0, index1); - if (!d01) - return false; - const extensionFraction = Geometry.conditionalDivideFraction(residual, d01); - if (extensionFraction === undefined) - return false; - // (Remark: indices are swapped and extensionFraction negated to prevent incidental precision - // loss with the alternative call with (index0, 1 + extensionFraction, index1); - points.interpolate(index1, -extensionFraction, index0, this.point0); - this.distance0 = this.targetDistance; - this.fraction0 = Geometry.interpolate(fraction1, -extensionFraction, fraction0); - return true; - } -} /* Starting wtih baseIndex and moving index by stepDirection: If the vector from baseIndex to baseIndex +1 crossed with vectorA can be normalized, accumulate it (scaled) to normal. @@ -162,6 +83,12 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { private _fractions?: GrowableFloat64Array; private _uvParams?: GrowableXYArray; private _derivatives?: GrowableXYZArray; + private _surfaceNormals?: GrowableXYZArray; + + private _pointIndices?: GrowableFloat64Array; + private _uvIndices?: GrowableFloat64Array; + private _normalIndices?: GrowableFloat64Array; + /** return the points array (cloned). */ public get points(): Point3d[] { return this._points.getPoint3dArray(); } /** Return (reference to) point data in packed GrowableXYZArray. */ @@ -170,6 +97,46 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { public get fractions(): GrowableFloat64Array | undefined { return this._fractions; } public get packedDerivatives(): GrowableXYZArray | undefined { return this._derivatives; } public get packedUVParams(): GrowableXYArray | undefined { return this._uvParams; } + public get packedSurfaceNormals(): GrowableXYZArray | undefined { return this._surfaceNormals; } + public get normalIndices(): GrowableFloat64Array | undefined { return this._normalIndices; } + public get paramIndices(): GrowableFloat64Array | undefined { return this._uvIndices; } + public get pointIndices(): GrowableFloat64Array | undefined { return this._pointIndices; } + /** + * create and attach an array to record fractional data. + * @param retainArrayContentsIfAlreadyPresent if true and there is a prexisting array, leave the prior array contents in place. + * If false and there is a preexisting array, leave the array there but clear its contents. + */ + public initializeFractionArray(retainArrayContentsIfAlreadyPresent: boolean = false) { + if (!this._fractions) + this._fractions = new GrowableFloat64Array(); + if (!retainArrayContentsIfAlreadyPresent) + this._fractions.clear(); + } + + /** + * create and attach an array to record derivative data. + * @param retainArrayContentsIfAlreadyPresent if true and there is a prexisting array, leave the prior array contents in place. + * If false and there is a preexisting array, leave the array there but clear its contents. + */ + public initializeDerivativeArray(retainArrayContentsIfAlreadyPresent: boolean = false) { + if (!this._derivatives) + this._derivatives = new GrowableXYZArray(); + if (retainArrayContentsIfAlreadyPresent) + this._derivatives.clear(); + } + + /** + * create and attach an array to record derivative data. + * @param retainArrayContentsIfAlreadyPresent if true and there is a prexisting array, leave the prior array contents in place. + * If false and there is a preexisting array, leave the array there but clear its contents. + */ + public initializeUVParamsArray(retainArrayContentsIfAlreadyPresent: boolean = false) { + if (!this._uvParams) + this._uvParams = new GrowableXYArray(); + if (retainArrayContentsIfAlreadyPresent) + this._uvParams.clear(); + } + private constructor() { super(); this._points = new GrowableXYZArray(); @@ -246,19 +213,88 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { this._fractions.push(fraction); } + /** Ensure that the parameter array exists with no points but at least the capacity of the point array. */ + public ensureEmptyUVParams(): GrowableXYArray { + const n = this.numPoints(); + if (!this._uvParams) { + this._uvParams = new GrowableXYArray(n); + return this._uvParams; + } + this._uvParams.clear(); + this._uvParams.ensureCapacity(n); + return this._uvParams; + } + /** Ensure that the surfaceNormals array exists with no points but at least the capacity of the point array. */ + public ensureEmptySurfaceNormals(): GrowableXYZArray { + const n = this.numPoints(); + if (!this._surfaceNormals) { + this._surfaceNormals = new GrowableXYZArray(n); + return this._surfaceNormals; + } + this._surfaceNormals.clear(); + this._surfaceNormals.ensureCapacity(n); + return this._surfaceNormals; + } + + /** Ensure that the surfaceNormals array exists with no points but at least the capacity of the point array. */ + public ensureEmptyNormalIndices(): GrowableFloat64Array { + const n = this.numPoints(); + if (!this._normalIndices) { + this._normalIndices = new GrowableFloat64Array(n); + return this._normalIndices; + } + this._normalIndices.clear(); + this._normalIndices.ensureCapacity(n); + return this._normalIndices; + } + + /** Ensure that the surfaceNormals array exists with no points but at least the capacity of the point array. */ + public ensureEmptyUVIndices(): GrowableFloat64Array { + const n = this.numPoints(); + if (!this._uvIndices) { + this._uvIndices = new GrowableFloat64Array(n); + return this._uvIndices; + } + this._uvIndices.clear(); + this._uvIndices.ensureCapacity(n); + return this._uvIndices; + } + + /** Ensure that the surfaceNormals array exists with no points but at least the capacity of the point array. */ + public ensureEmptyPointIndices(): GrowableFloat64Array { + const n = this.numPoints(); + if (!this._pointIndices) { + this._pointIndices = new GrowableFloat64Array(n); + return this._pointIndices; + } + this._pointIndices.clear(); + this._pointIndices.ensureCapacity(n); + return this._pointIndices; + } + /** - * Append a fraction to the fractions array. + * Append a uv coordinate to the uvParams array * @param uv */ - public addUVParam(uvParam: Point2d) { + public addUVParam(uvParam: XAndY) { if (!this._uvParams) this._uvParams = new GrowableXYArray(); this._uvParams.pushXY(uvParam.x, uvParam.y); } /** - * Append a fraction to the fractions array. - * @param fraction + * Append a uv coordinate to the uvParams array + * @param uv + */ + public addUVParamAsUV(u: number, v: number) { + if (!this._uvParams) + this._uvParams = new GrowableXYArray(); + this._uvParams.pushXY(u, v); + } + + /** + * Append a derivative to the derivative array + * @param vector */ public addDerivative(vector: Vector3d) { if (!this._derivatives) @@ -266,6 +302,16 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { this._derivatives.push(vector); } + /** + * Append a surface normal to the surface normal array. + * @param vector + */ + public addSurfaceNormal(vector: Vector3d) { + if (!this._derivatives) + this._derivatives = new GrowableXYZArray(); + this._derivatives.push(vector); + } + /** * If the linestring is not already closed, add a closure point. */ @@ -278,7 +324,10 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { public popPoint() { this._points.pop(); } - + /** Compute `uvParams` array as (xy parts of) a linear transform of the xyz coordinates */ + public computeUVFromXYZTransform(transform: Transform) { + this._uvParams = GrowableXYArray.createFromGrowableXYZArray(this._points, transform); + } public static createRectangleXY(point0: Point3d, ax: number, ay: number, closed: boolean = true): LineString3d { const ls = LineString3d.create(); const x0 = point0.x; @@ -323,12 +372,24 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { } public setFrom(other: LineString3d) { - this._points.clear(); - let i = 0; - while (other._points.isIndexValid(i)) { - this._points.push(other._points.getPoint3dAt(i)); - i++; - } + // ugly -- "clone" methods are inconsistent about 'reuse' and 'result' parameter . . . + this._points = other._points.clone(this._points); + if (other._derivatives) + this._derivatives = other._derivatives.clone(this._derivatives); + else + this._derivatives = undefined; + if (other._fractions) + this._fractions = other._fractions.clone(false); + else this._fractions = undefined; + if (other._surfaceNormals) + this._surfaceNormals = other._surfaceNormals.clone(this._surfaceNormals); + else + this._surfaceNormals = undefined; + if (other._uvParams) + this._uvParams = other._uvParams.clone(); + else + this._uvParams = undefined; + } public static createPoints(points: Point3d[]): LineString3d { @@ -368,7 +429,7 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { const value = []; let i = 0; while (this._points.isIndexValid(i)) { - value.push(this._points.getPoint3dAt(i).toJSON()); + value.push(this._points.getPoint3dAtUncheckedPointIndex(i).toJSON()); i++; } return value; @@ -381,7 +442,7 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { if (n === 0) return Point3d.createZero(); if (n === 1) - return Point3d.createFrom(this._points.getPoint3dAt(0), result); + return Point3d.createFrom(this._points.getPoint3dAtUncheckedPointIndex(0), result); const df = 1.0 / (n - 1); if (fraction <= df) return this._points.interpolate(0, fraction / df, 1, result)!; @@ -397,7 +458,7 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { if (n <= 1) { result.direction.setZero(); if (n === 1) - result.origin.setFrom(this._points.getPoint3dAt(0)); + result.origin.setFrom(this._points.getPoint3dAtUncheckedPointIndex(0)); else result.origin.setZero(); return result; } @@ -451,7 +512,7 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { const n = this._points.length; if (n <= 1) { if (n === 1) - return Transform.createTranslation(this._points.getPoint3dAt(0), result); + return Transform.createTranslation(this._points.getPoint3dAtUncheckedPointIndex(0), result); return Transform.createIdentity(result); } @@ -498,12 +559,12 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { public startPoint() { if (this._points.length === 0) return Point3d.createZero(); - return this._points.getPoint3dAt(0); + return this._points.getPoint3dAtUncheckedPointIndex(0); } /** If i is a valid index, return that point. */ public pointAt(i: number, result?: Point3d): Point3d | undefined { if (this._points.isIndexValid(i)) - return this._points.getPoint3dAt(i, result); + return this._points.getPoint3dAtUncheckedPointIndex(i, result); return undefined; } /** If i and j are both valid indices, return the vector from point i to point j @@ -514,7 +575,14 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { /** If i is a valid index, return that stored derivative vector. */ public derivativeAt(i: number, result?: Vector3d): Vector3d | undefined { if (this._derivatives && this._derivatives.isIndexValid(i)) - return this._points.atVector3dIndex(i, result); + return this._derivatives.getVector3dAtCheckedVectorIndex(i, result); + return undefined; + } + + /** If i is a valid index, return that stored surfaceNormal vector. */ + public surfaceNormalAt(i: number, result?: Vector3d): Vector3d | undefined { + if (this._surfaceNormals && this._surfaceNormals.isIndexValid(i)) + return this._surfaceNormals.getVector3dAtCheckedVectorIndex(i, result); return undefined; } @@ -523,18 +591,18 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { public endPoint() { if (this._points.length === 0) return Point3d.createZero(); - return this._points.getPoint3dAt(this._points.length - 1); + return this._points.getPoint3dAtUncheckedPointIndex(this._points.length - 1); } public reverseInPlace(): void { if (this._points.length >= 2) { let i0 = 0; let i1 = this._points.length - 1; - let a: Point3d = this._points.getPoint3dAt(0); + let a: Point3d = this._points.getPoint3dAtUncheckedPointIndex(0); while (i0 < i1) { - a = this._points.getPoint3dAt(i0); - this._points.setAt(i0, this._points.getPoint3dAt(i1)); - this._points.setAt(i1, a); + a = this._points.getPoint3dAtUncheckedPointIndex(i0); + this._points.setAtCheckedPointIndex(i0, this._points.getPoint3dAtUncheckedPointIndex(i1)); + this._points.setAtCheckedPointIndex(i1, a); i0++; i1--; } @@ -542,6 +610,10 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { } public tryTransformInPlace(transform: Transform): boolean { this._points.multiplyTransformInPlace(transform); + if (this._derivatives) + this._derivatives.multiplyMatrix3dInPlace(transform.matrix); + if (this._surfaceNormals) + this._surfaceNormals.multiplyAndRenormalizeMatrix3dInverseTransposeInPlace(transform.matrix); return true; } @@ -593,7 +665,7 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { if (signedDistance > 0.0) { for (; leftPointIndex <= numSegments;) { leftPointIndex++; - this._points.atPoint3dIndex(leftPointIndex, point1); + this._points.getPoint3dAtCheckedPointIndex(leftPointIndex, point1); if (context.announcePoint(point1, leftPointIndex / numSegments)) return CurveLocationDetail.createCurveFractionPointDistanceCurveSearchStatus(this, context.fraction0, context.point0, signedDistance, CurveSearchStatus.success, result); @@ -608,7 +680,7 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { if (localFraction <= 0.0) leftPointIndex--; for (; leftPointIndex >= 0; leftPointIndex--) { - this._points.atPoint3dIndex(leftPointIndex, point1); + this._points.getPoint3dAtCheckedPointIndex(leftPointIndex, point1); if (context.announcePoint(point1, leftPointIndex / numSegments)) return CurveLocationDetail.createCurveFractionPointDistanceCurveSearchStatus(this, context.fraction0, context.point0, signedDistance, CurveSearchStatus.success, result); @@ -646,14 +718,14 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { const numPoints = this._points.length; if (numPoints > 0) { const lastIndex = numPoints - 1; - result.setFP(1.0, this._points.getPoint3dAt(lastIndex), undefined); + result.setFP(1.0, this._points.getPoint3dAtUncheckedPointIndex(lastIndex), undefined); result.setDistanceTo(spacePoint); if (numPoints > 1) { let segmentFraction = 0; let d = 0; const df = 1.0 / lastIndex; for (let i = 1; i < numPoints; i++) { - segmentFraction = spacePoint.fractionOfProjectionToLine(this._points.getPoint3dAt(i - 1), this._points.getPoint3dAt(i)); + segmentFraction = spacePoint.fractionOfProjectionToLine(this._points.getPoint3dAtUncheckedPointIndex(i - 1), this._points.getPoint3dAtUncheckedPointIndex(i)); if (segmentFraction < 0) { if (!extend || i > 1) segmentFraction = 0.0; @@ -661,7 +733,7 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { if (!extend || i < lastIndex) segmentFraction = 1.0; } - this._points.getPoint3dAt(i - 1).interpolate(segmentFraction, this._points.getPoint3dAt(i), result.pointQ); + this._points.getPoint3dAtUncheckedPointIndex(i - 1).interpolate(segmentFraction, this._points.getPoint3dAtUncheckedPointIndex(i), result.pointQ); d = result.pointQ.distance(spacePoint); if (d < result.a) { result.setFP((i - 1 + segmentFraction) * df, result.pointQ, undefined, d); @@ -704,13 +776,13 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { const pointA = LineString3d._workPointA; const pointB = LineString3d._workPointB; const pointC = LineString3d._workPointC; - this._points.getPoint3dAt(0, pointA); + this._points.getPoint3dAtUncheckedPointIndex(0, pointA); let hB = 0; let numConsecutiveZero = 0; let hA = 0; let segmentFraction = 0; for (let i = 0; i < this._points.length; i++ , pointA.setFrom(pointB), hA = hB) { - this._points.getPoint3dAt(i, pointB); + this._points.getPoint3dAtUncheckedPointIndex(i, pointB); hB = Geometry.correctSmallMetricDistance(plane.altitude(pointB)); if (hB === 0.0) LineString3d.pushVertexHit(result, numConsecutiveZero++, this, i / divisor, pointB); @@ -739,10 +811,12 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { /** Append (clone of) one point. * BUT ... skip if duplicates the tail of prior points. */ - public appendStrokePoint(point: Point3d) { + public appendStrokePoint(point: Point3d, fraction?: number) { const n = this._points.length; - if (n === 0 || !point.isAlmostEqual(this._points.getPoint3dAt(n - 1))) + if (n === 0 || !point.isAlmostEqual(this._points.getPoint3dAtUncheckedPointIndex(n - 1))) this._points.push(point); + if (fraction !== undefined && this._fractions !== undefined) + this.addFraction(fraction); } /** Append a suitable evaluation of a curve .. @@ -752,24 +826,19 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { * BUT ... skip if duplicates the tail of prior points. */ public appendFractionToPoint(curve: CurvePrimitive, fraction: number) { - const n = this._points.length; if (this._derivatives) { const ray = curve.fractionToPointAndDerivative(fraction, LineString3d._workRay); - if (n === 0 || !ray.origin.isAlmostEqual(this._points.getPoint3dAt(n - 1))) { - if (this._fractions) - this._fractions.push(fraction); - this._points.push(ray.origin); - if (this._derivatives) - this._derivatives.push(ray.direction); + if (this._fractions) + this._fractions.push(fraction); + this._points.push(ray.origin); + if (this._derivatives) + this._derivatives.push(ray.direction); - } } else { const point = curve.fractionToPoint(fraction, LineString3d._workPointA); - if (n === 0 || !point.isAlmostEqual(this._points.getPoint3dAt(n - 1))) { - if (this._fractions) - this._fractions.push(fraction); - this._points.push(point); - } + if (this._fractions) + this._fractions.push(fraction); + this._points.push(point); } } /** @@ -800,8 +869,10 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { ls._fractions = new GrowableFloat64Array(capacity); ls._uvParams = new GrowableXYArray(capacity); } - if (options.needNormals) + if (options.needNormals) { ls._derivatives = new GrowableXYZArray(capacity); + ls._surfaceNormals = new GrowableXYZArray(capacity); + } } return ls; } @@ -855,10 +926,10 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { // There is no need for chordTol and angleTol within a segment. // Do NOT apply minstrokes per primitive. if (options && options.hasMaxEdgeLength) { - dest.appendStrokePoint(this._points.getPoint3dAt(0)); + dest.appendStrokePoint(this._points.getPoint3dAtUncheckedPointIndex(0)); for (let i = 1; i < n; i++) { - this._points.getPoint3dAt(i - 1, pointA); - this._points.getPoint3dAt(i, pointB); + this._points.getPoint3dAtUncheckedPointIndex(i - 1, pointA); + this._points.getPoint3dAtUncheckedPointIndex(i, pointB); const numStroke = options.applyMaxEdgeLength(1, pointA.distance(pointB)); if (numStroke > 1) dest.appendInterpolatedStrokePoints(numStroke, pointA, pointB, false); @@ -866,7 +937,7 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { } } else { for (let i = 0; i < n; i++) { - dest.appendStrokePoint(this._points.getPoint3dAt(i)); + dest.appendStrokePoint(this._points.getPoint3dAtUncheckedPointIndex(i)); } } } @@ -886,18 +957,50 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { // Do NOT apply minstrokes per primitive. if (options && options.hasMaxEdgeLength) { for (let i = 1; i < n; i++) { - const numStroke = options.applyMaxEdgeLength(1, this._points.getPoint3dAt(i - 1).distance(this._points.getPoint3dAt(i))); - handler.announceSegmentInterval(this, this._points.getPoint3dAt(i - 1), this._points.getPoint3dAt(i), numStroke, (i - 1) * df, i * df); + const numStroke = options.applyMaxEdgeLength(1, this._points.getPoint3dAtUncheckedPointIndex(i - 1).distance(this._points.getPoint3dAtUncheckedPointIndex(i))); + handler.announceSegmentInterval(this, this._points.getPoint3dAtUncheckedPointIndex(i - 1), this._points.getPoint3dAtUncheckedPointIndex(i), numStroke, (i - 1) * df, i * df); } } else { for (let i = 1; i < n; i++) { - handler.announceSegmentInterval(this, this._points.getPoint3dAt(i - 1), this._points.getPoint3dAt(i), 1, (i - 1) * df, i * df); + handler.announceSegmentInterval(this, this._points.getPoint3dAtUncheckedPointIndex(i - 1), this._points.getPoint3dAtUncheckedPointIndex(i), 1, (i - 1) * df, i * df); } } } handler.endCurvePrimitive(this); } + /** + * return the stroke count required for given options. + * @param options StrokeOptions that determine count + */ + public computeStrokeCountForOptions(options?: StrokeOptions): number { + const numPoints = this._points.length; + let numStroke = numPoints - 1; + + if (options && options.hasMaxEdgeLength) { + numStroke = 0; + for (let i = 1; i < numPoints; i++) { + numStroke += options.applyMaxEdgeLength(1, this._points.distance(i - 1, i)!); + } + } + return numStroke; + } + /** + * Compute individual segment stroke counts. Attach in a StrokeCountMap. + * @param options StrokeOptions that determine count + * @param parentStrokeMap evolving parent map. + */ + public computeAndAttachRecursiveStrokeCounts(options?: StrokeOptions, parentStrokeMap?: StrokeCountMap) { + const numPoints = this._points.length; + const applyOptions = options !== undefined && options.hasMaxEdgeLength; + const myData = StrokeCountMap.createWithCurvePrimitiveAndOptionalParent(this, parentStrokeMap, []); + for (let i = 1; i < numPoints; i++) { + const segmentLength = this._points.distance(i - 1, i)!; + const numStrokeOnSegment = applyOptions ? options!.applyMaxEdgeLength(1, segmentLength)! : 1; + myData.addToCountAndLength(numStrokeOnSegment, segmentLength); + } + CurvePrimitive.installStrokeCountMap(this, myData, parentStrokeMap); + } public dispatchToGeometryHandler(handler: GeometryHandler): any { return handler.handleLineString3d(this); } @@ -923,10 +1026,10 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { }; const pointA = LineString3d._workPointA; const pointB = LineString3d._workPointB; - this._points.getPoint3dAt(0, pointA); + this._points.getPoint3dAtUncheckedPointIndex(0, pointA); let status = false; for (let i = 1; i < n; i++ , pointA.setFrom(pointB), globalFractionA = globalFractionB) { - this._points.getPoint3dAt(i, pointB); + this._points.getPoint3dAtUncheckedPointIndex(i, pointB); globalFractionB = i / (n - 1); if (clipper.announceClippedSegmentIntervals(0.0, 1.0, pointA, pointB, capture)) status = true; @@ -938,7 +1041,7 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { const n = this._points.length; if (n === 0) return; if (n === 1) { - this._points.getPoint3dAt(0, LineString3d._indexPoint); + this._points.getPoint3dAtUncheckedPointIndex(0, LineString3d._indexPoint); dest.push(LineString3d._indexPoint); return; } @@ -980,7 +1083,7 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { const result = LineString3d.create(); this.addResolvedPoint(indexA, localFractionA, result._points); for (let index = indexA + 1; index <= indexB; index++) { - this._points.getPoint3dAt(index, LineString3d._workPointA); + this._points.getPoint3dAtUncheckedPointIndex(index, LineString3d._workPointA); result._points.push(LineString3d._workPointA); } if (!Geometry.isSmallRelative(localFractionB)) { @@ -991,11 +1094,56 @@ export class LineString3d extends CurvePrimitive implements BeJSONFunctions { /** Return (if possible) a specific segment of the linestring */ public getIndexedSegment(index: number): LineSegment3d | undefined { if (index >= 0 && index + 1 < this._points.length) - return LineSegment3d.create(this._points.atPoint3dIndex(index)!, this._points.atPoint3dIndex(index + 1)!); + return LineSegment3d.create(this._points.getPoint3dAtCheckedPointIndex(index)!, this._points.getPoint3dAtCheckedPointIndex(index + 1)!); return undefined; } -} + /** + * @returns true if first and last points are within metric tolerance. + */ + public get isPhysicallyClosed(): boolean { + return this._points.length > 0 && Geometry.isSmallMetricDistance(this._points.distance(0, this._points.length - 1)!); + } + + /** + * evaluate strokes at fractions indicated in a StrokeCountMap. + * * The map must have an array of component counts corresponding to the segemnet of this linestring. + * * "fractions" in the output are mapped within a0,a1 of the map.componetData + * @param map = stroke count data. + * @param destLinestring = receiver linestring. + * @return number of strokes added. 0 if `map.componentData` does not match the linestring + */ + public addMappedStrokesToLineString3D(map: StrokeCountMap, destLinestring: LineString3d): number { + const numPoint0 = destLinestring.numPoints(); + const needFractions = destLinestring._fractions !== undefined; + const needDerivatives = destLinestring._derivatives !== undefined; + const points = this._points; + const pointA = LineString3d._workPointA; + const pointB = LineString3d._workPointB; + const pointC = LineString3d._workPointC; + const numParentPoint = points.length; + if (map.primitive && map.primitive === this && map.componentData && map.componentData.length + 1 === numParentPoint) { + points.getPoint3dAtUncheckedPointIndex(0, pointA); + for (let k = 0; k + 1 < numParentPoint; k++ , pointA.setFromPoint3d(pointB)) { + points.getPoint3dAtUncheckedPointIndex(k + 1, pointB); + const segmentMap = map.componentData![k]; + const m = segmentMap.numStroke; + const vectorAB = pointA.vectorTo(pointB); + vectorAB.scale(m); + for (let i = 0; i <= m; i++) { + const fraction = i / m; + const outputFraction = segmentMap.fractionToA(fraction); + destLinestring.addPoint(pointA.interpolate(fraction, pointB, pointC)); + if (needFractions) + destLinestring._fractions!.push((outputFraction)); + if (needDerivatives) + destLinestring._derivatives!.push(vectorAB); + } + } + } + return destLinestring.numPoints() - numPoint0; + } +} /** An AnnotatedLineString3d is a linestring with additional data attached to each point * * This is useful in facet construction. */ @@ -1008,3 +1156,81 @@ export class AnnotatedLineString3d { public vecturU?: GrowableXYZArray; public vectorV?: GrowableXYZArray; } +/** + * context to be called to incrementally accumulate distance along line segments. + */ +class MoveByDistanceContext { + public distance0: number; // accumulated distance through point0 + public point0: Point3d; // most recent point + public fraction0: number; // most recent fraction position + public targetDistance: number; // this is always positive. + /** CAPTURE point0, fraction0, targetDistance */ + public constructor(point0: Point3d, fraction0: number, targetDistance: number) { + this.point0 = point0; + this.distance0 = 0.0; + this.targetDistance = Math.abs(targetDistance); + this.fraction0 = fraction0; + } + // Return CurveSearchStatus indicating whether the accumulated distance has reached the target. + public distanceStatus(): CurveSearchStatus { + return Geometry.isSameCoordinate(this.distance0, this.targetDistance) ? + CurveSearchStatus.success : CurveSearchStatus.stoppedAtBoundary; + } + /** + * Announce next point on the polyline. + * * if the additional segment does NOT reach the target: + * * accumulate the segment length + * * update point0 and fraction0 + * * return false + * * if the additional segment DOES reach the target: + * * update point0 and fraction0 to the (possibly interpolated) final point and fraction + * * return true + * @param point1 new point + * @param fraction1 fraction at point1 + * @return true if targetDistance reached. + */ + public announcePoint(point1: Point3d, fraction1: number): boolean { + const a = this.point0.distance(point1); + const distance1 = this.distance0 + a; + if (distance1 < this.targetDistance && !Geometry.isSameCoordinate(distance1, this.targetDistance)) { + this.point0.setFromPoint3d(point1); + this.distance0 = distance1; + this.fraction0 = fraction1; + return false; + } + const b = this.targetDistance - this.distance0; + const intervalFraction = Geometry.safeDivideFraction(b, a, 0.0); + this.point0.interpolate(intervalFraction, point1, this.point0); + this.fraction0 = Geometry.interpolate(this.fraction0, intervalFraction, fraction1); + this.distance0 = this.targetDistance; + return true; + } + /** + * Update point0, fraction0, and distance0 based on extrapolation of a segment between indices of a point array. + * @returns true if extraploation succeeded. (False if indexed points are coincident) + * @param points + * @param index0 + * @param index1 + * @param fraction0 + * @param fraction1 + * @param result + * @param CurveLocationDetail + */ + public announceExtrapolation(points: GrowableXYZArray, + index0: number, index1: number, + fraction0: number, fraction1: number): boolean { + const residual = this.targetDistance - this.distance0; + const d01 = points.distance(index0, index1); + if (!d01) + return false; + const extensionFraction = Geometry.conditionalDivideFraction(residual, d01); + if (extensionFraction === undefined) + return false; + // (Remark: indices are swapped and extensionFraction negated to prevent incidental precision + // loss with the alternative call with (index0, 1 + extensionFraction, index1); + points.interpolate(index1, -extensionFraction, index0, this.point0); + this.distance0 = this.targetDistance; + this.fraction0 = Geometry.interpolate(fraction1, -extensionFraction, fraction0); + return true; + } +} diff --git a/core/geometry/src/curve/Query/CylindricalRange.ts b/core/geometry/src/curve/Query/CylindricalRange.ts new file mode 100644 index 0000000..053ad0c --- /dev/null +++ b/core/geometry/src/curve/Query/CylindricalRange.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +/** @module Curve */ + +import { GeometryQuery } from "../GeometryQuery"; +import { RecurseToCurvesGeometryHandler } from "../../geometry3d/GeometryHandler"; + +import { LineSegment3d } from "../LineSegment3d"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { LineString3d } from "../LineString3d"; +import { Transform } from "../../geometry3d/Transform"; +import { StrokeOptions } from "../StrokeOptions"; +import { Arc3d } from "../Arc3d"; +import { Ray3d } from "../../geometry3d/Ray3d"; +import { AnyCurve } from "../CurveChain"; + +/** + * Context for computing geometry range around an axis. + * * The publicly called method is `computeZRRange (ray, geometry) + */ +export class CylindricalQuery extends RecurseToCurvesGeometryHandler { + // private geometry0: GeometryQuery; <-- Never used + private _perpVector: Vector3d; + private _maxDistance: number; + private _localToWorld: Transform; + /** capture ray and initialize evolving ranges. */ + private constructor(ray: Ray3d) { + super(); + this._perpVector = Vector3d.createZero(); + this._maxDistance = 0.0; + this._localToWorld = ray.toRigidZFrame()!; + } + private _localPoint = Point3d.create(); + private _worldPoint = Point3d.create(); + private announcePoint(xyz: Point3d) { + this._localToWorld.multiplyInversePoint3d(xyz, this._localPoint); + const distance = this._localPoint.magnitudeXY(); + if (distance >= this._maxDistance) { + this._maxDistance = distance; + this._perpVector.setFromPoint3d(this._localPoint); + this._perpVector.z = 0.0; + this._localToWorld.matrix.multiplyXY(this._localPoint.x, this._localPoint.y, this._perpVector); + } + } + + public handleLineSegment3d(segment0: LineSegment3d) { + this.announcePoint(segment0.startPoint(this._worldPoint)); + this.announcePoint(segment0.endPoint(this._worldPoint)); + } + public handleLineString3d(ls0: LineString3d) { + for (let i = 0; i < ls0.numPoints(); i++) { + ls0.pointAt(i, this._worldPoint); + this.announcePoint(this._worldPoint); + } + } + + public handleArc3d(arc0: Arc3d): any { + // humbug .. just stroke it .. + // exact solution is: + // project the arc to the z=0 plane of the local system. + // find max distance to origin. + const numStroke = StrokeOptions.applyAngleTol(undefined, 3, arc0.sweep.sweepRadians, 0.1); + const df = 1.0 / numStroke; + for (let i = 0; i <= numStroke; i++) { + arc0.fractionToPoint(i * df, this._worldPoint); + this.announcePoint(this._worldPoint); + } + return undefined; + } + + /** + * Compute the largest vector perpendicular to a ray and ending on the geometry. + * @param geometry0 geometry to search + * @returns vector from ray to geometry. + */ + public static computeMaxVectorFromRay(ray: Ray3d, geometry: GeometryQuery): Vector3d { + const accumulator = new CylindricalQuery(ray); + geometry.dispatchToGeometryHandler(accumulator); + return accumulator._perpVector.clone(); + } + + /** + * Recurse through geometry.children to find linestrings. + * In each linestring, compute the surface normal annotation from + * * the curve tangent stored in the linestring + * * the axis of rotation + * * a default V vector to be used when the linestring point is close to the axis. + * @param geometry + * @param axis + * @param defaultVectorV + */ + public static buildRotationalNormalsInLineStrings(geometry: AnyCurve, axis: Ray3d, defaultVectorFromAxis: Vector3d) { + if (geometry instanceof LineString3d) { + const points = geometry.packedPoints; + const derivatives = geometry.packedDerivatives; + const normals = geometry.ensureEmptySurfaceNormals(); + if (derivatives && normals) { + const vectorU = Vector3d.create(); + const vectorV = Vector3d.create(); // v direction (forwward along sweep) for surface of rotation. + const xyz = Point3d.create(); + const n = points.length; + for (let i = 0; i < n; i++) { + points.getPoint3dAtUncheckedPointIndex(i, xyz); + axis.perpendicularPartOfVectorToTarget(xyz, vectorU); + if (vectorU.isAlmostZero) + axis.direction.crossProduct(defaultVectorFromAxis, vectorV); + else + axis.direction.crossProduct(vectorU, vectorV); + geometry.packedDerivatives!.getVector3dAtCheckedVectorIndex(i, vectorU); // reuse vector U as curve derivative + vectorU.crossProduct(vectorV, vectorV); // reuse vector V as normal! + vectorV.normalizeInPlace(); + normals.push(vectorV); + } + } + } else if (geometry.children) { + const children = geometry.children; + for (const child of children) { + this.buildRotationalNormalsInLineStrings(child as AnyCurve, axis, defaultVectorFromAxis); + } + } + } +} diff --git a/core/geometry/src/curve/Query/StrokeCountChain.ts b/core/geometry/src/curve/Query/StrokeCountChain.ts new file mode 100644 index 0000000..d3da9b7 --- /dev/null +++ b/core/geometry/src/curve/Query/StrokeCountChain.ts @@ -0,0 +1,463 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { StrokeCountMap } from "./StrokeCountMap"; +import { CurveCollection, CurveChain } from "../CurveCollection"; +import { ParityRegion } from "../ParityRegion"; +import { StrokeOptions } from "../StrokeOptions"; +import { LineString3d } from "../LineString3d"; +import { AnyCurve } from "../CurveChain"; + +import { Loop } from "../Loop"; +import { Range1d } from "../../geometry3d/Range"; +import { Point3d } from "../../geometry3d/Point3dVector3d"; +import { Geometry } from "../../Geometry"; + +/** + * abstract methods for callbacks during sweeps of collections of StrokeCount Structures. + * * A set of StrokeCountMaps are to be visited multiple times. + * * The logic that controls the sweep is as below. + * * The callback object controls the number of sweeps and can adapt its action to the respective sweeps. + * * Note that a "false" from cb.startPass() terminates passes for this chainIndex and primitiveIndex, but all others exit the whole sequence. + * * This logic occurs 2 or three levels deep + * * outer level is "chains". Simple swept path or loops have only one outer; parity regions have one outer per loop of the parity region + * * second level is primitive within chain. + * * If the primitives in a set are "single component", second level is lowest. + * * startSweep() and endSweep() calls are two parameters, with undefined componentIndex + * * If the primitives in a set are multicomponet, there is a third level looping through corresponding components. + * ` + * if (!cb.startSweeps (chainIndex, primitiveIndex, componentIndex)) + * return false; + * for (let pass = 0;cb.startPass (pass); pass++){ + * for (each map in working set) + * if (!cb.visit (pass, map)) return false; + * if (!cb.endPass ()) return false; + * } + * } + * if (!cb.endSweeps (chainIndex, primitiveIndex, componentIndex)) return false; + * return true; + * ` + * @internal + */ +export abstract class StrokeCountMapMultipassVisitor { + /** + * called to announce the begnining of one or more sweeps through related StrokeCountMap's + * @param chainIndex index of loop or path within the various contours. + * @param primitiveIndexx index of primitive within the loop or path. + * @param componentIndex optional component index. + * @returns the number of sweeps to perform. + */ + public startSweeps(_chainIndex: number, _primitiveIndex: number, _componentIndex?: number): boolean { return true; } + /** + * announce the begninning of a sweep pass. + * @param pass the index (0,1...) for this sweep pass. + * @return true to execute this pass. false to break from the pass loop (next method called is endSweeps) + */ + public abstract startPass(pass: number): boolean; + public abstract visit(pass: number, map: StrokeCountMap): boolean; + /** + * announce the end of a pass + * @param pass the index (0,1...) for this sweep pass. + * @return true to continue the sweeps. + */ + + public abstract endPass(pass: number): boolean; + /** + * announce the end of handling for particular chainIndex and primitiveIndex; + * @return true to continue outer loops. + */ + public endSweeps(_chainIndex: number, _primitiveIndex: number, _componentIndex?: number): boolean { return true; } + +} +/** + * * pass 1: determine max numStroke + * * pass 2: impose max numStroke + * @internal + */ +export class StrokeCountMapVisitorApplyMaxCount extends StrokeCountMapMultipassVisitor { + public myMap: StrokeCountMap; + public constructor() { + super(); + this.myMap = StrokeCountMap.createWithComponentIndex(); + } + /** set up for a pass through corresponding maps. */ + public startPass(pass: number): boolean { + if (pass === 0) { + this.myMap.numStroke = 0; + return true; + } else if (pass === 1) { + // nothing to change == numStroke will be applied to each primitive. + return true; + } + // all other pass numbers are rejected ... + return false; + } + /** visit one of the set of corresponding maps. */ + public visit(pass: number, map: StrokeCountMap): boolean { + if (pass === 0) { + if (map.numStroke > this.myMap.numStroke) + this.myMap.numStroke = map.numStroke; + return true; + } else if (pass === 1) { + // apply the max from prior pass + map.numStroke = this.myMap.numStroke; + return true; + } + // no other pass values should happen -- canceled by startPass. + return false; + } + + public endPass(_pass: number): boolean { return true; } +} +/** + * * pass 1: determine max curveLength among maps presented. + * * pass 2: set the a0 and a1 values to 0 and that max distance + * @internal + */ +export class StrokeCountMapVisitorApplyMaxCurveLength extends StrokeCountMapMultipassVisitor { + public maxCurveLength: number; + public constructor() { + super(); + this.maxCurveLength = 0.0; + } + /** set up for a pass through corresponding maps. */ + public startPass(pass: number): boolean { + if (pass === 0) { + this.maxCurveLength = 0; + return true; + } else if (pass === 1) { + // nothing to change == numStroke will be applied to each primitive. + return true; + } + // all other pass numbers are rejected ... + return false; + } + /** visit one of the set of corresponding maps. */ + public visit(pass: number, map: StrokeCountMap): boolean { + if (pass === 0) { + this.maxCurveLength = Geometry.maxXY(map.curveLength, this.maxCurveLength); + return true; + } else if (pass === 1) { + // apply the max from prior pass + map.a0 = 0.0; + map.a1 = this.maxCurveLength; + return true; + } + // no other pass values should happen -- canceled by startPass. + return false; + } + + public endPass(_pass: number): boolean { return true; } +} +/** + * class `StrokeCountChain` contains: + * * `maps` = an array of `StrokeCountMap` + * * `parent` = parent CurveCollection. + * + * An instance is normally created with either a `Path` or `Loop` as the parent. + */ +export class StrokeCountChain { + public maps: StrokeCountMap[]; + public parent?: CurveCollection; + /** + * options are used (with different purposes) at two times: + * * When the StrokeCountChain is created, the options affect the stroke counts. This is just creating markup, not actual strokes. + * * When actual stroking happens, the options control creation of parameters and tangents. + */ + public options?: StrokeOptions; + + private constructor(parent?: CurveCollection, options?: StrokeOptions) { + this.parent = parent; + this.maps = []; + this.options = options; + } + public static createForCurveChain(chain: CurveChain, options?: StrokeOptions): StrokeCountChain { + const result = new StrokeCountChain(chain, options); + result.parent = chain; + // A chain can only contain primitives !!!! + for (const p of chain.children) { + p.computeAndAttachRecursiveStrokeCounts(options); + if (p.strokeData) + result.maps.push(p.strokeData); + } + return result; + } + public getStrokes(): LineString3d { + const ls = LineString3d.create(); + if (this.options) { + if (this.options.needNormals || this.options.needParams) { + ls.initializeFractionArray(); + ls.initializeDerivativeArray(); + ls.initializeUVParamsArray(); + } + } + for (const m of this.maps) { + if (m.primitive) + m.primitive.addMappedStrokesToLineString3D(m, ls); + } + return ls; + + } + /** internal form of */ + private static applySummed01LimitsWithinArray(maps: StrokeCountMap[], incomingSum: number): number { + let movingSum = incomingSum; + for (const m of maps) { + m.a0 += movingSum; + if (m.componentData) { + m.a1 = this.applySummed01LimitsWithinArray(m.componentData, m.a0); + } else { + m.a1 += movingSum; + } + movingSum = m.a1; + } + return movingSum; + } + /** + * walk the maps in the array. + * * in maps with no component data + * * increment map.a0 and map.a1 by the incoming distance a0 + * * in maps with component data: + * * recurse through the component array. + * * increment map.a0 by the incoming a0. + * * returned a1 from the componentData array becomes a1 + * @returns upper value of a1 in final map. + * @param maps + * @param incomingSum lower value to add to a0 for first map. + */ + public applySummed01Limits(incomingSum: number): number { + return StrokeCountChain.applySummed01LimitsWithinArray(this.maps, incomingSum); + } +} + +/** + * class `StrokeCountSection`\ + * * contains an array of `StrokeCountChain`. + * * Hence it is the internal node level of a (1-level-deep) tree of `StrokeCountChain` + */ +export class StrokeCountSection { + public chains: StrokeCountChain[]; + public parent?: CurveCollection; + private constructor(parent?: CurveCollection) { this.parent = parent; this.chains = []; } + /** + * construct array of arrays of `StrokeCountMap`s + * @param parent + */ + public static createForParityRegionOrChain(parent: CurveCollection, options?: StrokeOptions): StrokeCountSection { + const result = new StrokeCountSection(parent); + if (parent instanceof ParityRegion) { + for (const child of parent.children) { + const p = StrokeCountChain.createForCurveChain(child, options); + result.chains.push(p); + } + } else if (parent instanceof CurveChain) { + result.chains.push(StrokeCountChain.createForCurveChain(parent, options)); + } + return result; + } + /** test if all sections have the same structure. */ + public static areSectionsCompatible(sections: StrokeCountSection[], enforceCounts: boolean): boolean { + if (sections.length < 2) + return true; // hm.. don't know if that is useful, but nothing to check here. + const numChains = sections[0].chains.length; + for (let i = 1; i < sections.length; i++) { + // first level: must match number of paths or loops + if (sections[i].chains.length !== numChains) + return false; + // second level: must have same number of primitives in each path or loop + for (let j = 0; j < sections[0].chains.length; j++) { + const numPrimitive = sections[0].chains[j].maps.length; + if (sections[i].chains[j].maps.length !== numPrimitive) + return false; + for (let k = 0; k < numPrimitive; k++) { + if (!sections[0].chains[j].maps[k].isCompatibleComponentStructure(sections[i].chains[j].maps[k], enforceCounts)) + return false; + } + } + + } + return true; + } + + /** Within each section, sweep accumulate curveLength field, recording entry and exit sum in each map. + * * In expected use, (a0,a1) are (0,a) where a is the (previously computed) max length among corresponding maps up and down the section arrays. + */ + public static remapa0a1WithinEachChain(sections: StrokeCountSection[]) { + for (const section of sections) { + for (const chain of section.chains) { + chain.applySummed01Limits(0.0); + } + } + } + + private static applyMultipassVisitorCallbackNoComponents(sections: StrokeCountSection[], chainIndex: number, primitiveIndex: number, + componentIndex: number | undefined, callback: StrokeCountMapMultipassVisitor) { + const numSection = sections.length; + if (!callback.startSweeps(chainIndex, primitiveIndex, componentIndex)) return false; + if (componentIndex === undefined) { + // there are corresponding primitves directly at the section, chain, primitive index: + for (let pass = 0; ; pass++) { + if (!callback.startPass(pass)) + break; + for (let sectionIndex = 0; sectionIndex < numSection; sectionIndex++) + if (!callback.visit(pass, sections[sectionIndex].chains[chainIndex].maps[primitiveIndex])) + return false; + if (!callback.endPass(pass)) + return false; + } + } else { + // there are corresponding primitves at the section, chain, primitive,componentIndex + // there are corresponding primitves directly at the section, chain, primitive index: + for (let pass = 0; ; pass++) { + if (!callback.startPass(pass)) + break; + for (let sectionIndex = 0; sectionIndex < numSection; sectionIndex++) + if (!callback.visit(pass, sections[sectionIndex].chains[chainIndex].maps[primitiveIndex].componentData![componentIndex])) + return false; + if (!callback.endPass(pass)) + return false; + } + + } + if (!callback.endSweeps(chainIndex, primitiveIndex, componentIndex)) return false; + return true; + } + + /** + * Walk through the sections, emitting callbacks delimiting groups of corresponding primitives. + * @param sections array of sections (possibly a single path or loop at each section, or possibly a set of parity loops.) + * @param callback object to be notified during the traversal + */ + public static runMultiPassVisitorAtCorrespondingPrimitives(sections: StrokeCountSection[], callback: StrokeCountMapMultipassVisitor): boolean { + const numChainPerSection = sections[0].chains.length; + for (let chainIndex = 0; chainIndex < numChainPerSection; chainIndex++) { + const numPrimitive = sections[0].chains[chainIndex].maps.length; + for (let primitiveIndex = 0; primitiveIndex < numPrimitive; primitiveIndex++) { + if (sections[0].chains[chainIndex].maps[primitiveIndex].componentData) { + const numComponent = sections[0].chains[chainIndex].maps[primitiveIndex]!.componentData!.length; + for (let i = 0; i < numComponent; i++) + if (!this.applyMultipassVisitorCallbackNoComponents(sections, chainIndex, primitiveIndex, i, callback)) + return false; + + } else { + if (!this.applyMultipassVisitorCallbackNoComponents(sections, chainIndex, primitiveIndex, undefined, callback)) + return false; + } + } + } + return true; + } + + /** + * * Confirm that all sections in the array have the same structure. + * * Within each corresponding set of entries, apply the max count to all. + * @param sections array of per-section stroke count entries + */ + public static enforceStrokeCountCompatibility(sections: StrokeCountSection[]): boolean { + + if (sections.length < 2) + return true; + if (!StrokeCountSection.areSectionsCompatible(sections, false)) + return false; + const visitor = new StrokeCountMapVisitorApplyMaxCount(); + this.runMultiPassVisitorAtCorrespondingPrimitives(sections, visitor); + return true; + + } + /** + * * Confirm that all sections in the array have the same structure. + * * Within each corresponding set of entries up and down the sections, set curveLength as the maximum of the respective curve lengths. + * * Along each section, sum curveLengths (which were just reset) to get consistent along-chain parameters + * @param sections array of per-section stroke count entries + */ + public static enforceCompatibleDistanceSums(sections: StrokeCountSection[]): boolean { + + if (sections.length < 2) + return true; + if (!StrokeCountSection.areSectionsCompatible(sections, false)) + return false; + const visitor = new StrokeCountMapVisitorApplyMaxCurveLength(); + this.runMultiPassVisitorAtCorrespondingPrimitives(sections, visitor); + this.remapa0a1WithinEachChain(sections); + return true; + + } + + /** + * Return stroked form of the section. + */ + public getStrokes(): AnyCurve { + if (this.chains.length === 1) { + return this.chains[0].getStrokes(); + } else { + const region = ParityRegion.create(); + for (const c of this.chains) { + const strokes = c.getStrokes(); + if (strokes instanceof LineString3d) + region.tryAddChild(Loop.create(strokes)); + } + return region; + } + } + /** + * Given two compatibile stroke sets (as returned by getStrokes) extend a range + * with the distances between corresponding points. + * * Each set of strokes may be: + * * linestring + * * ParityRegion + * * CurveChain (Loop or Path) + * @param strokeA first set of strokes + * @param strokeB second set of strokes + * @param rangeToExtend caller-allocated range to be extended. + * @returns true if structures are compatible. + */ + public static extendDistanceRangeBetweenStrokes(strokeA: AnyCurve, strokeB: AnyCurve, rangeToExtend: Range1d): boolean { + if (!strokeA.isSameGeometryClass(strokeB)) + return false; + if (strokeA instanceof LineString3d) { + if (!(strokeB instanceof LineString3d)) + return false; + if (strokeA.numPoints() === strokeB.numPoints()) { + const n = strokeA.numPoints(); + const pointA = Point3d.create(); + const pointB = Point3d.create(); + const allPointA = strokeA.packedPoints; + const allPointB = strokeB.packedPoints; + + for (let i = 0; i < n; i++) { + allPointA.getPoint3dAtCheckedPointIndex(i, pointA); + allPointB.getPoint3dAtCheckedPointIndex(i, pointB); + rangeToExtend.extendX(pointA.distance(pointB)); + } + return true; + } + } else if (strokeA instanceof ParityRegion) { + if (!(strokeB instanceof ParityRegion)) + return false; + const childrenA = strokeA.children; + const childrenB = strokeB.children; + const n = childrenA.length; + if (n === childrenB.length) { + for (let i = 0; i < n; i++) { + if (!this.extendDistanceRangeBetweenStrokes(childrenA[i], childrenB[i], rangeToExtend)) + return false; + } + return true; + } + } else if (strokeA instanceof CurveChain) { + if (!(strokeB instanceof CurveChain)) + return false; + const childrenA = strokeA.children; + const childrenB = strokeB.children; + const n = childrenA.length; + if (n === childrenB.length) { + for (let i = 0; i < n; i++) { + if (!this.extendDistanceRangeBetweenStrokes(childrenA[i], childrenB[i], rangeToExtend)) + return false; + } + return true; + } + } + return false; + } +} diff --git a/core/geometry/src/curve/Query/StrokeCountMap.ts b/core/geometry/src/curve/Query/StrokeCountMap.ts new file mode 100644 index 0000000..4a97a2c --- /dev/null +++ b/core/geometry/src/curve/Query/StrokeCountMap.ts @@ -0,0 +1,147 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { CurvePrimitive } from "../CurvePrimitive"; +import { Geometry } from "../../Geometry"; + +/** @module Curve */ +/** + * data carrier interface for per-primitive stroke counts and distances used by PolyfaceBuilder. + * * For a simple primitive (Line segment or arc) that is stroked with uniform fraction, the members are: + * * `numStroke` = total number of strokes + * * `curveLength` = length of this curve + * * `a0` = external mapped coordinate for fraction 0 on this primitive or component + * * `a1` = external mapped coordinate for fraction 1 on this primitive or component + * + * * For linestring and bspline curve, those numbers are totals for the overall curve, and breakdown within + * the components (line segments or bezier spans) is recoreded on the optional array `componentData[]` + * * Members of the array are annotated with componentIndex within the linestring or bspline curve + */ +export class StrokeCountMap { + + public numStroke: number; + public curveLength: number; + public a0: number; + public a1: number; + public componentData?: StrokeCountMap[]; + public primitive?: CurvePrimitive; + public componentIndex?: number; + /** + * Constructor. Initialize all fields from arguments. + * * Callers that expect to announce numStroke and curveLength for multiple components send an empty componentData array. + * * Callers that do not have multiple components send undefined component data. + * @param numStroke + * @param curveLength + * @param a0 + * @param a1 + * @param componentData + */ + private constructor(numStroke: number, curveLength: number, a0: number, a1: number, componentData?: StrokeCountMap[]) { + this.numStroke = numStroke; + this.curveLength = curveLength; + this.a0 = a0; + this.a1 = a1; + this.componentData = componentData; + } + /** + * Create a `StrokeCountMap` with curve primitive and optional compontnData array. + * @param primitive + * @param numStroke + * @param curveLength + * @param a0 + * @param a1 + * @param componentData + */ + public static createWithCurvePrimitive(primitive: CurvePrimitive, numStroke: number, curveLength: number, a0: number, a1: number, componentData?: StrokeCountMap[]) { + const result = new StrokeCountMap(numStroke, curveLength, a0, a1, componentData); + result.primitive = primitive; + return result; + } + /** + * Create a `StrokeCountMap` with `componentIndex` (but no primitive or componentData array) + * @param index + * @param numStroke + * @param curveLength + * @param a0 + * @param a1 + */ + public static createWithComponentIndex(componentIndex: number = 0, numStroke: number = 0, curveLength: number = 0, a0: number = 0, a1: number = 0) { + const result = new StrokeCountMap(numStroke, curveLength, a0, a1); + result.componentIndex = componentIndex; + return result; + } + + /** + * create a StrokeCountMap, optionally + * * (a) use parent a1 as new a0 + * * (b) attach a (usually empty) array for component counts. + * @param parentMap optional map whose a1 becomes a0 in the new map. + * @param componentData optional array of component StrokeCountMaps. + */ + public static createWithCurvePrimitiveAndOptionalParent(curvePrimitive: CurvePrimitive, parentMap?: StrokeCountMap, componentData?: StrokeCountMap[]): StrokeCountMap { + const a0 = parentMap ? parentMap.a1 : 0.0; + const result = new StrokeCountMap(0, 0, a0, a0, componentData); + result.primitive = curvePrimitive; + return result; + + } + /** + * Apply stroke count and curve length from a component to a parent map. + * If componentData is present, install the new count and length with distance limits + * @param parentMap map to be updated. + * @param numStroke number of strokes on new child curve + * @param curveLength curve length for new child curve. + */ + public addToCountAndLength(numStroke: number, curveLength: number) { + const a2 = this.a1 + curveLength; + if (this.componentData) { + this.componentData.push( + new StrokeCountMap(numStroke, curveLength, this.a1, a2)); + } + this.numStroke += numStroke; + this.curveLength += curveLength; + this.a1 = a2; + } + /** return true if `other` has the same component structure as `this` + * * testing recursives through corresponding members of cmomponentData arrays. + */ + public isCompatibleComponentStructure(other: StrokeCountMap, enforceCounts: boolean): boolean { + if (enforceCounts && this.numStroke !== other.numStroke) + return false; + if (this.componentData === undefined && other.componentData === undefined) + return true; + if (this.componentData && other.componentData) { + // both have components. Recurse . . .. + if (this.componentData.length !== other.componentData.length) + return false; + const n = this.componentData.length; + for (let i = 0; i < n; i++) + if (!this.componentData[i].isCompatibleComponentStructure(other.componentData[i], enforceCounts)) + return false; + return true; + } + // one has componentData, the other not. + return false; + } + /** + * * clone all data from root. + * * clone componentData arrays recursively. + */ + public clone(): StrokeCountMap { + const a = new StrokeCountMap(this.numStroke, this.curveLength, this.a0, this.a1); + if (this.componentData) { + a.componentData = []; + for (const child of this.componentData) + a.componentData.push(child.clone()); + } + return a; + } + /** + * interpolate in the a0,a1 mapping. + * @param fraction fractional position between a0 and a1 + */ + public fractionToA(fraction: number) { + return Geometry.interpolate(this.a0, fraction, this.a1); + } +} diff --git a/core/geometry/src/curve/TransitionSpiral.ts b/core/geometry/src/curve/TransitionSpiral.ts index 3386bf5..e5e3e18 100644 --- a/core/geometry/src/curve/TransitionSpiral.ts +++ b/core/geometry/src/curve/TransitionSpiral.ts @@ -376,6 +376,24 @@ export class TransitionSpiral3d extends CurvePrimitive { this._strokes.emitStrokableParts(dest, options); dest.endParentCurvePrimitive(this); } + + /** + * return the stroke count required for given options. + * @param options StrokeOptions that determine count + */ + public computeStrokeCountForOptions(options?: StrokeOptions): number { + let numStroke = 1; + if (options) { + const rMin = Math.min(Math.abs(this.radius01.x0), Math.abs(this.radius01.x1)); + numStroke = options.applyTolerancesToArc(rMin, this.bearing01.sweepRadians); + numStroke = options.applyMaxEdgeLength(numStroke, this.curveLength()); + numStroke = options.applyMinStrokesPerPrimitive(numStroke); + } else { + numStroke = StrokeOptions.applyAngleTol(undefined, 4, this.bearing01.sweepRadians); + } + return numStroke; + } + // hm.. nothing to do but reverse the interval . . . maybe that's cheesy . . . public reverseInPlace(): void { this.activeFractionInterval.reverseInPlace(); diff --git a/core/geometry/src/geometry-core.ts b/core/geometry/src/geometry-core.ts index d29f278..d35c17a 100644 --- a/core/geometry/src/geometry-core.ts +++ b/core/geometry/src/geometry-core.ts @@ -143,6 +143,7 @@ export * from "./geometry3d/Segment1d"; export * from "./geometry3d/Transform"; export * from "./geometry3d/XYZProps"; export * from "./geometry3d/YawPitchRollAngles"; +export * from "./geometry3d/FrustumAnimation"; export * from "./Geometry"; export * from "./Constant"; @@ -219,3 +220,11 @@ export * from "./topology/Graph"; export * from "./topology/Triangulation"; export * from "./serialization/IModelJsonSchema"; export * from "./serialization/DeepCompare"; + +// Set the version number so it can be found at runtime. BUILD_SEMVER is replaced at build time by the webpack DefinePlugin. +declare var BUILD_SEMVER: string; +if ((typeof (BUILD_SEMVER) !== "undefined") && (typeof window !== "undefined") && window) { + if (!(window as any).iModelJsVersions) + (window as any).iModelJsVersions = new Map(); + (window as any).iModelJsVersions.set("geometry-core", BUILD_SEMVER); +} diff --git a/core/geometry/src/geometry3d/Angle.ts b/core/geometry/src/geometry3d/Angle.ts index c0cae96..db13573 100644 --- a/core/geometry/src/geometry3d/Angle.ts +++ b/core/geometry/src/geometry3d/Angle.ts @@ -20,6 +20,8 @@ export class Angle implements BeJSONFunctions { private _degrees?: number; private constructor(radians = 0, degrees?: number) { this._radians = radians; this._degrees = degrees; } public clone(): Angle { return new Angle(this._radians, this._degrees); } + public freeze() { Object.freeze(this); } + /** * Return a new Angle object for angle given in degrees. * @param degrees angle in degrees @@ -30,6 +32,12 @@ export class Angle implements BeJSONFunctions { * @param radians angle in radians */ public static createRadians(radians: number) { return new Angle(radians); } + /** + * Return a (new) Angle object, with angle scaled from existing angle. + * @param scale scale factor to apply to angle. + */ + public cloneScaled(scale: number) { return new Angle(this.radians * scale); } + /** * Set this angle to a value given in radians. * @param radians angle given in radians @@ -135,7 +143,12 @@ export class Angle implements BeJSONFunctions { */ public tan(): number { return Math.tan(this._radians); } public static isFullCircleRadians(radians: number) { return Math.abs(radians) >= Geometry.fullCircleRadiansMinusSmallAngle; } + /** Test if the radians value is a cmoplete circle */ + public static isHalfCircleRadians(radians: number) { return (Math.abs(Math.abs(radians)) - Math.PI) <= Geometry.smallAngleRadians; } + /** test if the angle is aa full circle */ public get isFullCircle(): boolean { return Angle.isFullCircleRadians(this._radians); } + /** test if the angle is a half circle (in either direction) */ + public get isHalfCircle(): boolean { return Angle.isHalfCircleRadians(this._radians); } /** Adjust a radians value so it is positive in 0..360 */ public static adjustDegrees0To360(degrees: number): number { if (degrees >= 0) { diff --git a/core/geometry/src/geometry3d/AngleSweep.ts b/core/geometry/src/geometry3d/AngleSweep.ts index 3330f99..93f272e 100644 --- a/core/geometry/src/geometry3d/AngleSweep.ts +++ b/core/geometry/src/geometry3d/AngleSweep.ts @@ -168,7 +168,7 @@ export class AngleSweep implements BeJSONFunctions { public radiansArraytoPositivePeriodicFractions(data: GrowableFloat64Array) { const n = data.length; for (let i = 0; i < n; i++) { - data.reassign(i, this.radiansToPositivePeriodicFraction(data.at(i))); + data.reassign(i, this.radiansToPositivePeriodicFraction(data.atUncheckedIndex(i))); } } public radiansToPositivePeriodicFraction(radians: number): number { diff --git a/core/geometry/src/geometry3d/BilinearPatch.ts b/core/geometry/src/geometry3d/BilinearPatch.ts new file mode 100644 index 0000000..e9b5498 --- /dev/null +++ b/core/geometry/src/geometry3d/BilinearPatch.ts @@ -0,0 +1,181 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +/** @module Solid */ + +import { Point3d } from "./Point3dVector3d"; +import { Range3d } from "./Range"; +import { Transform } from "./Transform"; + +import { UVSurface } from "./GeometryHandler"; +import { Plane3dByOriginAndVectors } from "./Plane3dByOriginAndVectors"; +import { Geometry } from "../Geometry"; +/** + * * A Bilinear patch is defined by its 4 corner points. + * * the corner points do not have to be coplanar + * + * * v direction (up) + * | + * | + * | + * point01---A1-----------point11 + * | | | + * B0----X------------B1 + * | | | + * point00--A0-----------point10 -----------> u direction + * + * * To evaluate aa point at (u,v), the following are equivalent: + * * interpolate with u to get both A0 and A1, viz + * * A0 = interpolate between point00 and point10 at fraction u + * * A1 = interpolate between point01 and point11 at fraction u + * * X = interpolate between A0 and A1 at fraction v + * * interpolate first with v to get B0 and B1, viz + * * B0 = interpolate between point00 and point01 at fraction v + * * B1 = interpolate between point10 and point11 at fraction v + * * X = interpolate between B0 and B1 at fraction u + * * sum all at once as + * * X = (1-u)* (1-v) *point00 + (1-u)*v * point01 + u * (1-v) *point10 + u* v * point11 + * + */ +export class BilinearPatch implements UVSurface { + public point00: Point3d; + public point10: Point3d; + public point01: Point3d; + public point11: Point3d; + /** + * Capture (not clone) corner points, in u direction at v=0, then in same direction at v=1 + * @param point00 Point at uv=0,0 + * @param point10 Point at uv=1,0 + * @param point10 Point at uv=0,1 + * @param point11 Point at uv=11 + */ + public constructor(point00: Point3d, point10: Point3d, point01: Point3d, point11: Point3d) { + this.point00 = point00; + this.point10 = point10; + this.point01 = point01; + this.point11 = point11; + } + /** clone (not capture) corners to create a new BilinearPatch + * @param point00 Point at uv=0,0 + * @param point10 Point at uv=1,0 + * @param point10 Point at uv=0,1 + * @param point11 Point at uv=11 + */ + public static create(point00: Point3d, point10: Point3d, point01: Point3d, point11: Point3d) { + return new BilinearPatch(point00.clone(), point10.clone(), point01.clone(), point11.clone()); + } + + /** create a patch with from xyz values of the 4 corners + */ + public static createXYZ(x00: number, y00: number, z00: number, + x10: number, y10: number, z10: number, + x01: number, y01: number, z01: number, + x11: number, y11: number, z11: number) { + return new BilinearPatch(Point3d.create(x00, y00, z00), + Point3d.create(x10, y10, z10), + Point3d.create(x01, y01, z01), + Point3d.create(x11, y11, z11)); + } + + /** return a clone with same coordinates */ + public clone(): BilinearPatch { + return new BilinearPatch( + this.point00.clone(), + this.point10.clone(), + this.point01.clone(), + this.point11.clone()); + } + /** test equality of the 4 points */ + public isAlmostEqual(other: BilinearPatch): boolean { + return this.point00.isAlmostEqual(other.point00) + && this.point10.isAlmostEqual(other.point10) + && this.point01.isAlmostEqual(other.point01) + && this.point11.isAlmostEqual(other.point11); + } + /** Apply the transform to each point */ + public tryTransformInPlace(transform: Transform): boolean { + transform.multiplyPoint3d(this.point00, this.point00); + transform.multiplyPoint3d(this.point10, this.point10); + transform.multiplyPoint3d(this.point01, this.point01); + transform.multiplyPoint3d(this.point11, this.point11); + return true; + } + /** + * return a cloned and transformed patch. + * @param transform + */ + public cloneTransformed(transform: Transform): BilinearPatch | undefined { + const result = this.clone(); + result.tryTransformInPlace(transform); + return result; + } + /** Extend a range by the range of the(optionally transformed) patch + */ + public extendRange(range: Range3d, transform?: Transform) { + if (transform) { + range.extendTransformedPoint(transform, this.point00); + range.extendTransformedPoint(transform, this.point10); + range.extendTransformedPoint(transform, this.point01); + range.extendTransformedPoint(transform, this.point11); + } else { + range.extendPoint(this.point00); + range.extendPoint(this.point10); + range.extendPoint(this.point01); + range.extendPoint(this.point11); + } + } + /** Evaluate as a uv surface + * @param u fractional position in minor (phi) + * @param v fractional position on major (theta) arc + */ + public uvFractionToPoint(u: number, v: number, result?: Point3d): Point3d { + const f00 = (1.0 - u) * (1.0 - v); + const f10 = u * (1.0 - v); + const f01 = (1.0 - u) * v; + const f11 = u * v; + return Point3d.create( + f00 * this.point00.x + f10 * this.point10.x + f01 * this.point01.x + f11 * this.point11.x, + f00 * this.point00.y + f10 * this.point10.y + f01 * this.point01.y + f11 * this.point11.y, + f00 * this.point00.z + f10 * this.point10.z + f01 * this.point01.z + f11 * this.point11.z, + result); + } + /** Evaluate as a uv surface, returning point and two derivative vectors. + * @param u fractional position + * @param v fractional position + */ + public uvFractionToPointAndTangents(u: number, v: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors { + const u0 = 1.0 - u; + const v0 = 1.0 - v; + const f00 = u0 * v0; + const f10 = u * v0; + const f01 = u0 * v; + const f11 = u * v; + return Plane3dByOriginAndVectors.createOriginAndVectorsXYZ( + f00 * this.point00.x + f10 * this.point10.x + f01 * this.point01.x + f11 * this.point11.x, + f00 * this.point00.y + f10 * this.point10.y + f01 * this.point01.y + f11 * this.point11.y, + f00 * this.point00.z + f10 * this.point10.z + f01 * this.point01.z + f11 * this.point11.z, + // u derivative .. + v0 * (this.point10.x - this.point00.x) + v * (this.point11.x - this.point01.x), + v0 * (this.point10.y - this.point00.y) + v * (this.point11.y - this.point01.y), + v0 * (this.point10.z - this.point00.z) + v * (this.point11.z - this.point01.z), + // v derivative .. + u0 * (this.point01.x - this.point00.x) + u * (this.point11.x - this.point10.x), + u0 * (this.point01.y - this.point00.y) + u * (this.point11.y - this.point10.y), + u0 * (this.point01.z - this.point00.z) + u * (this.point11.z - this.point10.z), + result); + } + /** + * @returns the larger of the u-direction edge lengths at v=0 and v=1 + */ + public maxUEdgeLength(): number { + return Geometry.maxXY(this.point00.distance(this.point10), this.point01.distance(this.point11)); + } + /** + * @returns the larger of the v-direction edge lengths at u=0 and u=1 + */ + public maxVEdgeLength(): number { + return Geometry.maxXY(this.point00.distance(this.point01), this.point10.distance(this.point11)); + } +} diff --git a/core/geometry/src/geometry3d/FrustumAnimation.ts b/core/geometry/src/geometry3d/FrustumAnimation.ts new file mode 100644 index 0000000..c19e05d --- /dev/null +++ b/core/geometry/src/geometry3d/FrustumAnimation.ts @@ -0,0 +1,99 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +/** @module Solid */ + +import { Point3d, Vector3d } from "./Point3dVector3d"; +import { Transform } from "./Transform"; +import { Point3dArray } from "./PointHelpers"; +import { Matrix3d } from "./Matrix3d"; +import { AxisOrder } from "../Geometry"; +import { Angle } from "./Angle"; +/* +* context for constructing smooth motion a startFrsutum and endFrutum. +* The externally interesting calls are +* 1) Declare and initialize a context to shift corner0 to corner1, with the (NPC coordinate) point (fractionU, fractionV, fractionW) +* moving along its connecting segment, all other points rotating smoothly from the start orientation to end orientation: +* +* SmoothTransformBetweenFrusta context; +* if (context.InitFractionalFrustumTransform (corner0, corner1, fractionU, fractionV, fractionW)) .... +* (this only fails for flattened frustum -- should not happen) +* 2) Get any intermediate 8 corners (at fraction) with +*/ +export class SmoothTransformBetweenFrusta { + // raw frusta: + private _localCornerA: Point3d[]; + private _localCornerB: Point3d[]; + + private _localToWorldA: Transform; + private _localToWorldB: Transform; + + private _rotationAxis: Vector3d; + private _rotationAngle: Angle; + + /** + * CAPTURE local corners, pickup and putdown frames, and rotation-around-vector data + * @param localCornerA + * @param localCornerB + * @param localToWordA + * @param localToWordB + * @param rotationAxis + * @param rotationAngle + */ + private constructor(localToWorldA: Transform, localCornerA: Point3d[], localToWorldB: Transform, localCornerB: Point3d[], rotationAxis: Vector3d, rotationAngle: Angle) { + this._localCornerA = localCornerA; + this._localCornerB = localCornerB; + this._localToWorldA = localToWorldA; + this._localToWorldB = localToWorldB; + this._rotationAxis = rotationAxis; + this._rotationAngle = rotationAngle; + } + /** + * Set up rotation data for smooth transition from 8 point frusta cornerA and cornerB + * @param cornerA + * @param cornerB + */ + public static create(cornerA: Point3d[], cornerB: Point3d[]): SmoothTransformBetweenFrusta | undefined { + const localToWorldA = Point3dArray.evaluateTrilinearDerivativeTransform(cornerA, 0.5, 0.5, 0.5); + const localToWorldB = Point3dArray.evaluateTrilinearDerivativeTransform(cornerB, 0.5, 0.5, 0.5); + const rigidA = Transform.createOriginAndMatrix(localToWorldA.origin, Matrix3d.createRigidFromMatrix3d(localToWorldA.matrix, AxisOrder.ZXY)); + const rigidB = Transform.createOriginAndMatrix(localToWorldB.origin, Matrix3d.createRigidFromMatrix3d(localToWorldB.matrix, AxisOrder.ZXY)); + if (rigidA.matrix.computeCachedInverse(true) && rigidB.matrix.computeCachedInverse(true)) { + const spinMatrix = rigidB.matrix.multiplyMatrixMatrixInverse(rigidA.matrix)!; + const spinAxis = spinMatrix.getAxisAndAngleOfRotation(); + const localCornerA = rigidA.multiplyInversePoint3dArray(cornerA)!; + const localCornerB = rigidB.multiplyInversePoint3dArray(cornerB)!; + return new SmoothTransformBetweenFrusta(rigidA, localCornerA, rigidB, localCornerB, + spinAxis.axis, spinAxis.angle); + } + return undefined; + } + + // interpolate local corner coordinates at fractional move from m_localFrustum0 to m_localFrustum1 + public interpolateLocalCorners(fraction: number, result?: Point3d[]): Point3d[] { + result = result || []; + result.length = 0; + const n = this._localCornerA.length; + for (let i = 0; i < n; i++) { + result.push(this._localCornerA[i].interpolate(fraction, this._localCornerB[i])); + } + return result; + } + /* + * After initialization, call this for various intermediate fractions. + * The returned corner points are in world coordinates "between" start and end positions. + */ + public fractionToWorldCorners(fraction: number, result?: Point3d[]): Point3d[] { + const corners = this.interpolateLocalCorners(fraction, result); + const fractionalRotation = Matrix3d.createRotationAroundVector(this._rotationAxis, + this._rotationAngle.cloneScaled(fraction))!; + const axes0 = this._localToWorldA.matrix; + const fractionalAxes = fractionalRotation.multiplyMatrixMatrix(axes0); + const fractionalOrigin = this._localToWorldA.getOrigin().interpolate(fraction, this._localToWorldB.origin); + const putdownFrame = Transform.createOriginAndMatrix(fractionalOrigin, fractionalAxes); + putdownFrame.multiplyPoint3dArray(corners, corners); + return corners; + } +} diff --git a/core/geometry/src/geometry3d/GeometryHandler.ts b/core/geometry/src/geometry3d/GeometryHandler.ts index 977ea02..0d332a7 100644 --- a/core/geometry/src/geometry3d/GeometryHandler.ts +++ b/core/geometry/src/geometry3d/GeometryHandler.ts @@ -34,6 +34,8 @@ import { LineString3d } from "../curve/LineString3d"; import { PointString3d } from "../curve/PointString3d"; import { Plane3dByOriginAndVectors } from "./Plane3dByOriginAndVectors"; import { BezierCurveBase } from "../bspline/BezierCurveBase"; +import { GeometryQuery } from "../curve/GeometryQuery"; +import { Vector2d } from "./Point2dVector2d"; export abstract class GeometryHandler { // Currently will include functionality on "how to handle" (note: Subclasses of CurveCollection are linked to one method) @@ -110,6 +112,49 @@ export class NullGeometryHandler extends GeometryHandler { public handleBezierCurve3d(_g: BezierCurve3d): any { return undefined; } public handleBezierCurve3dH(_g: BezierCurve3dH): any { return undefined; } } +/** + * Implement GeometryHandler methods, with all curve collection methods recursing to children. + */ +export class RecurseToCurvesGeometryHandler extends GeometryHandler { + public handleLineSegment3d(_g: LineSegment3d): any { return undefined; } + public handleLineString3d(_g: LineString3d): any { return undefined; } + public handleArc3d(_g: Arc3d): any { return undefined; } + public handleCurveCollection(_g: CurveCollection): any { return undefined; } + public handleBSplineCurve3d(_g: BSplineCurve3d): any { return undefined; } + public handleBSplineCurve3dH(_g: BSplineCurve3dH): any { return undefined; } + public handleBSplineSurface3d(_g: BSplineSurface3d): any { return undefined; } + + public handleCoordinateXYZ(_g: CoordinateXYZ): any { return undefined; } + public handleBSplineSurface3dH(_g: BSplineSurface3dH): any { return undefined; } + public handleIndexedPolyface(_g: IndexedPolyface): any { return undefined; } + public handleTransitionSpiral(_g: TransitionSpiral3d): any { return undefined; } + + public handleChildren(g: GeometryQuery): any { + const children = g.children; + if (children) + for (const child of children) { + child.dispatchToGeometryHandler(this); + } + } + + public handlePath(g: Path): any { return this.handleChildren(g); } + public handleLoop(g: Loop): any { return this.handleChildren(g); } + public handleParityRegion(g: ParityRegion): any { return this.handleChildren(g); } + public handleUnionRegion(g: UnionRegion): any { return this.handleChildren(g); } + public handleBagOfCurves(g: BagOfCurves): any { return this.handleChildren(g); } + + public handleSphere(_g: Sphere): any { return undefined; } + public handleCone(_g: Cone): any { return undefined; } + public handleBox(_g: Box): any { return undefined; } + public handleTorusPipe(_g: TorusPipe): any { return undefined; } + public handleLinearSweep(_g: LinearSweep): any { return undefined; } + public handleRotationalSweep(_g: RotationalSweep): any { return undefined; } + public handleRuledSweep(_g: RuledSweep): any { return undefined; } + public handlePointString3d(_g: PointString3d): any { return undefined; } + public handleBezierCurve3d(_g: BezierCurve3d): any { return undefined; } + public handleBezierCurve3dH(_g: BezierCurve3dH): any { return undefined; } +} + /** IStrokeHandler is an interface with methods to receive data about curves being stroked. * CurvePrimitives emitStrokes () methods emit calls to a handler object with these methods. * The various CurvePrimitive types are free to announce either single points (announcePoint), linear fragments, @@ -184,12 +229,28 @@ export interface UVSurface { * @param vFraction fractional coordinate in the v direction * @param result optional pre-allocated point */ - UVFractionToPoint(uFraction: number, vFraction: number, result?: Point3d): Point3d; + uvFractionToPoint(uFraction: number, vFraction: number, result?: Point3d): Point3d; /** - * Convert fractional u and v coordinates to surface point and partial derivatives + * Convert fractional u and v coordinates to surface point and in-surface tangent directions. + * * Remark: the vectors are expected to be non-zero tangents which can be crossed to get a normal. + * * Hence the are NOT precisely either (a) partial derivatives or (b) frenet vectors * @param uFraction fractional coordinate in u direction * @param vFraction fractional coordinate in the v direction * @param result optional pre-allocated carrier for point and vectors */ - UVFractionToPointAndTangents(uFraction: number, vFraction: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; + uvFractionToPointAndTangents(uFraction: number, vFraction: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors; +} +/** + * Interface for queries of distance-along in u and v directions + */ +export interface UVSurfaceIsoParametricDistance { + /** + * * Return a vector whose x and y parts are "size" of the surface in the u and v directions. + * * Sizes are use for applying scaling to mesh parameters + * * These sizes are (reasonable approximations of) the max curve length along u and v isoparameter lines. + * * e.g. for a sphere, these are: + * * u direction = distance around the equator + * * v direction = distance from south pole to north pole. + */ + maxIsoParametricDistance(): Vector2d; } diff --git a/core/geometry/src/geometry3d/GrowableFloat64Array.ts b/core/geometry/src/geometry3d/GrowableFloat64Array.ts index e8d8548..438bf67 100644 --- a/core/geometry/src/geometry3d/GrowableFloat64Array.ts +++ b/core/geometry/src/geometry3d/GrowableFloat64Array.ts @@ -60,7 +60,7 @@ export class GrowableFloat64Array { * @param index index of entry to set * @param value value to set */ - public setAt(index: number, value: number) { + public setAtUncheckedIndex(index: number, value: number) { this._data[index] = value; } @@ -166,7 +166,7 @@ export class GrowableFloat64Array { } } - public at(index: number): number { + public atUncheckedIndex(index: number): number { return this._data[index]; } diff --git a/core/geometry/src/geometry3d/GrowableXYArray.ts b/core/geometry/src/geometry3d/GrowableXYArray.ts index 8aebbd8..dde7edc 100644 --- a/core/geometry/src/geometry3d/GrowableXYArray.ts +++ b/core/geometry/src/geometry3d/GrowableXYArray.ts @@ -168,11 +168,27 @@ export class GrowableXYArray extends IndexedXYCollection { * @param pointIndex index to access * @param result optional result */ - public getPoint2dAt(pointIndex: number, result?: Point2d): Point2d { + public getPoint2dAtUncheckedPointIndex(pointIndex: number, result?: Point2d): Point2d { const index = 2 * pointIndex; return Point2d.create(this._data[index], this._data[index + 1], result); } + /** + * Get x coordinate by point index, with no index checking + * @param pointIndex index to access + */ + public getXAtUncheckedPointIndex(pointIndex: number): number { + return this._data[2 * pointIndex]; + } + + /** + * Get y coordinate by index, with no index checking + * @param pointIndex index to access + */ + public getYAtUncheckedPointIndex(pointIndex: number): number { + return this._data[2 * pointIndex + 1]; + } + /** * Gather all points as a Point2d[] */ @@ -186,7 +202,7 @@ export class GrowableXYArray extends IndexedXYCollection { } /** copy xyz into strongly typed Point2d */ - public atPoint2dIndex(pointIndex: number, result?: Point2d): Point2d | undefined { + public getPoint2dAtCheckedPointIndex(pointIndex: number, result?: Point2d): Point2d | undefined { const index = 2 * pointIndex; if (this.isIndexValid(pointIndex)) { return Point2d.create(this._data[index], this._data[index + 1], result); @@ -195,7 +211,7 @@ export class GrowableXYArray extends IndexedXYCollection { } /** copy xyz into strongly typed Vector2d */ - public atVector2dIndex(vectorIndex: number, result?: Vector2d): Vector2d | undefined { + public getVector2dAtCheckedVectorIndex(vectorIndex: number, result?: Vector2d): Vector2d | undefined { const index = 2 * vectorIndex; if (this.isIndexValid(vectorIndex)) { return Vector2d.create(this._data[index], this._data[index + 1], result); @@ -225,38 +241,81 @@ export class GrowableXYArray extends IndexedXYCollection { /** * push coordinates from the source array to the end of this array. * @param source source array - * @param sourceIndex xyz index within the source + * @param sourceIndex xyz index within the source. If undefined, push entire contents of source * @returns true if sourceIndex is valid. */ - public pushFromGrowableXYArray(source: GrowableXYArray, sourceIndex: number) { + public pushFromGrowableXYArray(source: GrowableXYArray, sourceIndex?: number): number { + if (sourceIndex === undefined) { + const numPresent = this.length; + const numPush = source.length; + this.ensureCapacity(numPresent + numPush); + const numFloatPresent = 2 * numPresent; + const numFloatAdd = 2 * numPush; + for (let i = 0; i < numFloatAdd; i++) + this._data[numFloatPresent + i] = source._data[i]; + this._xyInUse += numPush; + return numPush; + } if (source.isIndexValid(sourceIndex)) { const j = sourceIndex * 2; this.pushXY(source._data[j], source._data[j + 1]); - return true; + return 1; } - return false; + return 0; } + /** + * push coordinates from the source array to the end of this array. + * @param source source array + * @param transform optional transform to apply to points. + * @param dest optional result. + */ + public static createFromGrowableXYZArray(source: GrowableXYZArray, transform?: Transform, dest?: GrowableXYArray) { + const packedXYZ = source.float64Data(); + const numXYZ = source.length; // this is in xyz points + const nDouble = 3 * numXYZ; + if (!dest) + dest = new GrowableXYArray(numXYZ); + dest.clear(); + let x; + let y; + let z; + if (transform) { + for (let i = 0; i < nDouble; i += 3) { + x = packedXYZ[i]; + y = packedXYZ[i + 1]; + z = packedXYZ[i + 2]; + dest.pushXY(transform.multiplyComponentXYZ(0, x, y, z), transform.multiplyComponentXYZ(1, x, y, z)); + } + } else { + for (let i = 0; i < nDouble; i += 3) { + x = packedXYZ[i]; + y = packedXYZ[i + 1]; + dest.pushXY(x, y); + } + } + return dest; + } /** * @returns Return the first point, or undefined if the array is empty. */ public front(result?: Point2d): Point2d | undefined { if (this._xyInUse === 0) return undefined; - return this.getPoint2dAt(0, result); + return this.getPoint2dAtUncheckedPointIndex(0, result); } /** * @returns Return the last point, or undefined if the array is empty. */ public back(result?: Point2d): Point2d | undefined { if (this._xyInUse < 1) return undefined; - return this.getPoint2dAt(this._xyInUse - 1, result); + return this.getPoint2dAtUncheckedPointIndex(this._xyInUse - 1, result); } /** * Set the coordinates of a single point. * @param pointIndex index of point to set * @param value coordinates to set */ - public setAt(pointIndex: number, value: XAndY): boolean { + public setAtCheckedPointIndex(pointIndex: number, value: XAndY): boolean { if (!this.isIndexValid(pointIndex)) return false; const index = pointIndex * 2; @@ -271,7 +330,7 @@ export class GrowableXYArray extends IndexedXYCollection { * @param y y coordinate * @param z z coordinate */ - public setCoordinates(pointIndex: number, x: number, y: number): boolean { + public setXYZAtCheckedPointIndex(pointIndex: number, x: number, y: number): boolean { if (!this.isIndexValid(pointIndex)) return false; const index = pointIndex * 2; @@ -488,7 +547,7 @@ export class GrowableXYArray extends IndexedXYCollection { if (dataA.length !== dataB.length) return false; for (let i = 0; i < dataA.length; i++) - if (!dataA.getPoint2dAt(i).isAlmostEqual(dataB.getPoint2dAt(i))) + if (!dataA.getPoint2dAtUncheckedPointIndex(i).isAlmostEqual(dataB.getPoint2dAtUncheckedPointIndex(i))) return false; return true; } diff --git a/core/geometry/src/geometry3d/GrowableXYZArray.ts b/core/geometry/src/geometry3d/GrowableXYZArray.ts index c92b9cd..94ce636 100644 --- a/core/geometry/src/geometry3d/GrowableXYZArray.ts +++ b/core/geometry/src/geometry3d/GrowableXYZArray.ts @@ -8,7 +8,7 @@ import { Geometry } from "../Geometry"; import { XYAndZ } from "./XYZProps"; import { Point3d, Vector3d } from "./Point3dVector3d"; -import { Range3d } from "./Range"; +import { Range3d, Range1d } from "./Range"; import { Transform } from "./Transform"; import { Matrix3d } from "./Matrix3d"; import { IndexedXYZCollection } from "./IndexedXYZCollection"; @@ -80,14 +80,19 @@ export class GrowableXYZArray extends IndexedXYZCollection { * Make a copy of the (active) points in this array. * (The clone does NOT get excess capacity) */ - public clone(): GrowableXYZArray { - const newPoints = new GrowableXYZArray(this.length); + public clone(result?: GrowableXYZArray): GrowableXYZArray { const numValue = this.length * 3; - const newData = newPoints._data; + if (!result) + result = new GrowableXYZArray(this.length); + else { + result.clear(); + result.ensureCapacity(this.length); + } + const newData = result._data; const data = this._data; for (let i = 0; i < numValue; i++) newData[i] = data[i]; - newPoints._xyzInUse = this.length; - return newPoints; + result._xyzInUse = this.length; + return result; } public static create(data: XYAndZ[]): GrowableXYZArray { @@ -150,27 +155,27 @@ export class GrowableXYZArray extends IndexedXYZCollection { this._xyzInUse = 0; } /** - * Get a point by index, strongly typed as a Point3d. This is unchecked. Use atPoint3dIndex to have validity test. + * Get a point by index, strongly typed as a Point3d. This is unchecked. Use getPoint3dAtCheckedPointIndex to have validity test. * @param pointIndex index to access * @param result optional result */ - public getPoint3dAt(pointIndex: number, result?: Point3d): Point3d { + public getPoint3dAtUncheckedPointIndex(pointIndex: number, result?: Point3d): Point3d { const index = 3 * pointIndex; return Point3d.create(this._data[index], this._data[index + 1], this._data[index + 2], result); } /** - * Get a point by index, strongly typed as a Point2d. This is unchecked. Use atPoint2dIndex to have validity test. + * Get a point by index, strongly typed as a Point2d. This is unchecked. Use getPoint2dAtCheckedPointIndex to have validity test. * @param pointIndex index to access * @param result optional result */ - public getPoint2dAt(pointIndex: number, result?: Point2d): Point2d { + public getPoint2dAtUncheckedPointIndex(pointIndex: number, result?: Point2d): Point2d { const index = 3 * pointIndex; return Point2d.create(this._data[index], this._data[index + 1], result); } /** copy xyz into strongly typed Point3d */ - public atPoint3dIndex(pointIndex: number, result?: Point3d): Point3d | undefined { + public getPoint3dAtCheckedPointIndex(pointIndex: number, result?: Point3d): Point3d | undefined { const index = 3 * pointIndex; if (this.isIndexValid(pointIndex)) { if (!result) result = Point3d.create(); @@ -183,7 +188,7 @@ export class GrowableXYZArray extends IndexedXYZCollection { } /** copy xy into strongly typed Point2d */ - public atPoint2dIndex(pointIndex: number, result?: Point2d): Point2d | undefined { + public getPoint2dAtCheckedPointIndex(pointIndex: number, result?: Point2d): Point2d | undefined { const index = 3 * pointIndex; if (this.isIndexValid(pointIndex)) { if (!result) result = Point2d.create(); @@ -194,7 +199,7 @@ export class GrowableXYZArray extends IndexedXYZCollection { return undefined; } /** copy xyz into strongly typed Vector3d */ - public atVector3dIndex(vectorIndex: number, result?: Vector3d): Vector3d | undefined { + public getVector3dAtCheckedVectorIndex(vectorIndex: number, result?: Vector3d): Vector3d | undefined { const index = 3 * vectorIndex; if (vectorIndex >= 0 && vectorIndex < this._xyzInUse) { if (!result) result = Vector3d.create(); @@ -245,21 +250,21 @@ export class GrowableXYZArray extends IndexedXYZCollection { */ public front(result?: Point3d): Point3d | undefined { if (this._xyzInUse === 0) return undefined; - return this.getPoint3dAt(0, result); + return this.getPoint3dAtUncheckedPointIndex(0, result); } /** * @returns Return the last point, or undefined if the array is empty. */ public back(result?: Point3d): Point3d | undefined { if (this._xyzInUse < 1) return undefined; - return this.getPoint3dAt(this._xyzInUse - 1, result); + return this.getPoint3dAtUncheckedPointIndex(this._xyzInUse - 1, result); } /** * Set the coordinates of a single point. * @param pointIndex index of point to set * @param value coordinates to set */ - public setAt(pointIndex: number, value: XYAndZ): boolean { + public setAtCheckedPointIndex(pointIndex: number, value: XYAndZ): boolean { if (!this.isIndexValid(pointIndex)) return false; let index = pointIndex * 3; @@ -275,7 +280,7 @@ export class GrowableXYZArray extends IndexedXYZCollection { * @param y y coordinate * @param z z coordinate */ - public setCoordinates(pointIndex: number, x: number, y: number, z: number): boolean { + public setXYZAtCheckedPointIndex(pointIndex: number, x: number, y: number, z: number): boolean { if (!this.isIndexValid(pointIndex)) return false; let index = pointIndex * 3; @@ -309,7 +314,7 @@ export class GrowableXYZArray extends IndexedXYZCollection { let x = 0; let y = 0; let z = 0; - for (let i = 0; i + 3 <= nDouble; i += 3) { + for (let i = 0; i + 2 <= nDouble; i += 3) { x = data[i]; y = data[i + 1]; z = data[i + 2]; @@ -327,7 +332,7 @@ export class GrowableXYZArray extends IndexedXYZCollection { let x = 0; let y = 0; let z = 0; - for (let i = 0; i + 3 <= nDouble; i += 3) { + for (let i = 0; i + 2 <= nDouble; i += 3) { x = data[i]; y = data[i + 1]; z = data[i + 2]; @@ -337,6 +342,47 @@ export class GrowableXYZArray extends IndexedXYZCollection { } } + /** multiply each xyz (as a vector) by matrix inverse transpse, renormalize the vector, replace values. + * * This is the way to apply a matrix (possibly with skew and scale) to a surface normal, and + * have it end up perpendicular to the transformed in-surface vectors. + * + */ + public multiplyAndRenormalizeMatrix3dInverseTransposeInPlace(matrix: Matrix3d) { + const data = this._data; + const nDouble = this.float64Length; + const coffs = matrix.coffs; + const tol = 1.0e-15; + let x = 0; + let y = 0; + let z = 0; + let x1; + let y1; + let z1; + let q; + let a; + for (let i = 0; i + 2 <= nDouble; i += 3) { + x = data[i]; + y = data[i + 1]; + z = data[i + 2]; + x1 = coffs[0] * x + coffs[1] * y + coffs[2] * z; + y1 = coffs[3] * x + coffs[4] * y + coffs[5] * z; + z1 = coffs[6] * x + coffs[7] * y + coffs[8] * z; + a = x1 * x1 + y1 * y1 + z1 * z1; + if (a < tol) { + // put the originals back .. + x1 = x; y1 = y; z1 = z; + } else if (Math.abs(a - 1.0) > tol) { + q = 1.0 / Math.sqrt(a); + x1 *= q; + y1 *= q; + z1 *= q; + } // else -- q is near 1, no need to do the division !! + data[i] = x1; + data[i + 1] = y1; + data[i + 2] = z1; + } + } + /** multiply each point by the transform, replace values. */ public tryTransformInverseInPlace(transform: Transform): boolean { const data = this._data; @@ -523,7 +569,7 @@ export class GrowableXYZArray extends IndexedXYZCollection { return undefined; } /** Return the distance between an array point and the input point. */ - public distanceIndexToPoint(i: number, spacePoint: Point3d): number | undefined { + public distanceIndexToPoint(i: number, spacePoint: XYAndZ): number | undefined { if (i >= 0 && i < this._xyzInUse) { const i0 = 3 * i; return Geometry.hypotenuseXYZ( @@ -534,12 +580,26 @@ export class GrowableXYZArray extends IndexedXYZCollection { return undefined; } + /** Return the distance between points in distinct arrays. */ + public static distanceBetweenPointsIn2Arrays(arrayA: GrowableXYZArray, i: number, arrayB: GrowableXYZArray, j: number): number | undefined { + + if (i >= 0 && i < arrayA._xyzInUse && j >= 0 && j <= arrayB._xyzInUse) { + const i0 = 3 * i; + const j0 = 3 * j; + return Geometry.hypotenuseXYZ( + arrayB._data[j0] - arrayA._data[i0], + arrayB._data[j0 + 1] - arrayA._data[i0 + 1], + arrayB._data[j0 + 2] - arrayA._data[i0 + 2]); + } + return undefined; + } + public static isAlmostEqual(dataA: GrowableXYZArray | undefined, dataB: GrowableXYZArray | undefined): boolean { if (dataA && dataB) { if (dataA.length !== dataB.length) return false; for (let i = 0; i < dataA.length; i++) - if (!dataA.getPoint3dAt(i).isAlmostEqual(dataB.getPoint3dAt(i))) + if (!dataA.getPoint3dAtUncheckedPointIndex(i).isAlmostEqual(dataB.getPoint3dAtUncheckedPointIndex(i))) return false; return true; } @@ -597,4 +657,25 @@ export class GrowableXYZArray extends IndexedXYZCollection { numAdded++; } } + + /** + * find the min and max distance between corresponding indexed points. Excess points are ignored. + * @param arrayA first array + * @param arrayB second array + */ + public static distanceRangeBetweenCorrespondingPoints(arrayA: GrowableXYZArray, arrayB: GrowableXYZArray): Range1d { + const dataA = arrayA._data; + const dataB = arrayB._data; + const n = Math.min(arrayA.length, arrayB.length); + let i = 0; + let k0; + const range = Range1d.createNull(); + while (i < n) { + k0 = 3 * i; + range.extendX(Geometry.hypotenuseXYZ(dataA[k0] - dataB[k0], dataA[k0 + 1] - dataB[k0 + 1], dataA[k0 + 2] - dataB[k0 + 2])); + i++; + } + return range; + } + } diff --git a/core/geometry/src/geometry3d/IndexedXYCollection.ts b/core/geometry/src/geometry3d/IndexedXYCollection.ts index 98c5010..ef3ed8d 100644 --- a/core/geometry/src/geometry3d/IndexedXYCollection.ts +++ b/core/geometry/src/geometry3d/IndexedXYCollection.ts @@ -24,13 +24,13 @@ export abstract class IndexedXYCollection { * @param result caller-allocated destination * @returns undefined if the index is out of bounds */ - public abstract atPoint2dIndex(index: number, result?: Point2d): Point2d | undefined; + public abstract getPoint2dAtCheckedPointIndex(index: number, result?: Point2d): Point2d | undefined; /** * @param index index of point within the array * @param result caller-allocated destination * @returns undefined if the index is out of bounds */ - public abstract atVector2dIndex(index: number, result?: Vector2d): Vector2d | undefined; + public abstract getVector2dAtCheckedVectorIndex(index: number, result?: Vector2d): Vector2d | undefined; /** * @param indexA index of point within the array * @param indexB index of point within the array diff --git a/core/geometry/src/geometry3d/IndexedXYZCollection.ts b/core/geometry/src/geometry3d/IndexedXYZCollection.ts index e1f05e5..1993d8f 100644 --- a/core/geometry/src/geometry3d/IndexedXYZCollection.ts +++ b/core/geometry/src/geometry3d/IndexedXYZCollection.ts @@ -24,13 +24,13 @@ export abstract class IndexedXYZCollection { * @param result caller-allocated destination * @returns undefined if the index is out of bounds */ - public abstract atPoint3dIndex(index: number, result?: Point3d): Point3d | undefined; + public abstract getPoint3dAtCheckedPointIndex(index: number, result?: Point3d): Point3d | undefined; /** * @param index index of point within the array * @param result caller-allocated destination * @returns undefined if the index is out of bounds */ - public abstract atVector3dIndex(index: number, result?: Vector3d): Vector3d | undefined; + public abstract getVector3dAtCheckedVectorIndex(index: number, result?: Vector3d): Vector3d | undefined; /** * @param indexA index of point within the array * @param indexB index of point within the array diff --git a/core/geometry/src/geometry3d/Matrix3d.ts b/core/geometry/src/geometry3d/Matrix3d.ts index 1100a65..4488d9d 100644 --- a/core/geometry/src/geometry3d/Matrix3d.ts +++ b/core/geometry/src/geometry3d/Matrix3d.ts @@ -647,7 +647,7 @@ export class Matrix3d implements BeJSONFunctions { result.angle.setRadians(Math.atan2(sine, cosine)); result.axis.setFrom(cross); result.error = !result.axis.tryNormalizeInPlace(); - return result; + return result } */ /** @@ -1083,9 +1083,9 @@ export class Matrix3d implements BeJSONFunctions { public static createColumns(vectorU: Vector3d, vectorV: Vector3d, vectorW: Vector3d, result?: Matrix3d): Matrix3d { return Matrix3d.createRowValues ( - vectorU.x, vectorV.x, vectorW.x, - vectorU.y, vectorV.y, vectorW.y, - vectorU.z, vectorV.z, vectorW.z, result); + vectorU.x, vectorV.x, vectorW.x, + vectorU.y, vectorV.y, vectorW.y, + vectorU.z, vectorV.z, vectorW.z, result); } /** Create a matrix from column vectors. @@ -1094,9 +1094,9 @@ export class Matrix3d implements BeJSONFunctions { public static createColumnsXYW(vectorU: XAndY, uz: number, vectorV: XAndY, vz: number, vectorW: XAndY, wz: number, result?: Matrix3d): Matrix3d { return Matrix3d.createRowValues ( - vectorU.x, vectorV.x, vectorW.x, - vectorU.y, vectorV.y, vectorW.y, - uz, vz, wz, result); + vectorU.x, vectorV.x, vectorW.x, + vectorU.y, vectorV.y, vectorW.y, + uz, vz, wz, result); } /** Install data from xyz parts of Point4d (w part of Point4d ignored) */ @@ -1171,9 +1171,9 @@ export class Matrix3d implements BeJSONFunctions { public static createRows(vectorU: Vector3d, vectorV: Vector3d, vectorW: Vector3d, result?: Matrix3d): Matrix3d { return Matrix3d.createRowValues ( - vectorU.x, vectorU.y, vectorU.z, - vectorV.x, vectorV.y, vectorV.z, - vectorW.x, vectorW.y, vectorW.z, result); + vectorU.x, vectorU.y, vectorU.z, + vectorV.x, vectorV.y, vectorV.z, + vectorW.x, vectorW.y, vectorW.z, result); } /** Create a matrix that scales along a specified direction. The scale factor can be negative. for instance scale of -1.0 (negative one) is a mirror. */ @@ -1186,9 +1186,9 @@ export class Matrix3d implements BeJSONFunctions { const a = (scale - 1); return Matrix3d.createRowValues ( - 1 + a * x * x, a * x * y, a * x * z, - a * y * x, 1 + a * y * y, a * y * z, - a * z * x, a * z * y, 1 + a * z * z, result); + 1 + a * x * x, a * x * y, a * x * z, + a * y * x, 1 + a * y * y, a * y * z, + a * z * x, a * z * y, 1 + a * z * z, result); } return Matrix3d.createUniformScale(scale); } @@ -1225,7 +1225,7 @@ export class Matrix3d implements BeJSONFunctions { (this.coffs[6] * v.x + this.coffs[7] * v.y + this.coffs[8] * v.z)); } - public static XYZMinusMatrixTimesXYZ(origin: XYZ, matrix: Matrix3d, vector: XYZ, result?: Point3d): Point3d { + public static xyzMinusMatrixTimesXYZ(origin: XYZ, matrix: Matrix3d, vector: XYZ, result?: Point3d): Point3d { const x = vector.x; const y = vector.y; const z = vector.z; @@ -1236,7 +1236,7 @@ export class Matrix3d implements BeJSONFunctions { result); } - public static XYPlusMatrixTimesXY(origin: XAndY, matrix: Matrix3d, vector: XAndY, result?: Point2d): Point2d { + public static xyPlusMatrixTimesXY(origin: XAndY, matrix: Matrix3d, vector: XAndY, result?: Point2d): Point2d { const x = vector.x; const y = vector.y; return Point2d.create( @@ -1245,7 +1245,7 @@ export class Matrix3d implements BeJSONFunctions { result); } - public static XYZPlusMatrixTimesXYZ(origin: XYZ, matrix: Matrix3d, vector: XYAndZ, result?: Point3d): Point3d { + public static xyzPlusMatrixTimesXYZ(origin: XYZ, matrix: Matrix3d, vector: XYAndZ, result?: Point3d): Point3d { const x = vector.x; const y = vector.y; const z = vector.z; @@ -1256,7 +1256,7 @@ export class Matrix3d implements BeJSONFunctions { result); } - public static XYZPlusMatrixTimesCoordinates(origin: XYZ, matrix: Matrix3d, x: number, y: number, z: number, result?: Point3d): Point3d { + public static xyzPlusMatrixTimesCoordinates(origin: XYZ, matrix: Matrix3d, x: number, y: number, z: number, result?: Point3d): Point3d { return Point3d.create( origin.x + matrix.coffs[0] * x + matrix.coffs[1] * y + matrix.coffs[2] * z, origin.y + matrix.coffs[3] * x + matrix.coffs[4] * y + matrix.coffs[5] * z, @@ -1275,7 +1275,7 @@ export class Matrix3d implements BeJSONFunctions { * @param w w part of multiplied point * @param result optional result. */ - public static XYZPlusMatrixTimesWeightedCoordinates(origin: XYZ, matrix: Matrix3d, x: number, y: number, z: number, w: number, result?: Point4d): Point4d { + public static xyzPlusMatrixTimesWeightedCoordinates(origin: XYZ, matrix: Matrix3d, x: number, y: number, z: number, w: number, result?: Point4d): Point4d { return Point4d.create( w * origin.x + matrix.coffs[0] * x + matrix.coffs[1] * y + matrix.coffs[2] * z, w * origin.y + matrix.coffs[3] * x + matrix.coffs[4] * y + matrix.coffs[5] * z, @@ -1294,7 +1294,7 @@ export class Matrix3d implements BeJSONFunctions { * @param w w part of multiplied point * @param result optional result. */ - public static XYZPlusMatrixTimesWeightedCoordinatesToFloat64Array(origin: XYZ, matrix: Matrix3d, x: number, y: number, z: number, w: number, result?: Float64Array): Float64Array { + public static xyzPlusMatrixTimesWeightedCoordinatesToFloat64Array(origin: XYZ, matrix: Matrix3d, x: number, y: number, z: number, w: number, result?: Float64Array): Float64Array { if (!result) result = new Float64Array(4); result[0] = w * origin.x + matrix.coffs[0] * x + matrix.coffs[1] * y + matrix.coffs[2] * z; @@ -1315,7 +1315,7 @@ export class Matrix3d implements BeJSONFunctions { * @param w w part of multiplied point * @param result optional result. */ - public static XYZPlusMatrixTimesCoordinatesToFloat64Array(origin: XYZ, matrix: Matrix3d, x: number, y: number, z: number, result?: Float64Array): Float64Array { + public static xyzPlusMatrixTimesCoordinatesToFloat64Array(origin: XYZ, matrix: Matrix3d, x: number, y: number, z: number, result?: Float64Array): Float64Array { if (!result) result = new Float64Array(3); result[0] = origin.x + matrix.coffs[0] * x + matrix.coffs[1] * y + matrix.coffs[2] * z; @@ -1489,6 +1489,16 @@ export class Matrix3d implements BeJSONFunctions { return result; } + /** Multiply this matrix times inverse of other + * @return the matrix result + */ + public multiplyMatrixMatrixInverse(other: Matrix3d, result?: Matrix3d): Matrix3d | undefined { + if (!other.computeCachedInverse(true)) + return undefined; + result = result ? result : new Matrix3d(); + PackedMatrix3dOps.multiplyMatrixMatrix(this.coffs, other.inverseCoffs!, result.coffs); + return result; + } /** Matrix multiplication `this * otherTranspose` * @return the matrix result */ @@ -1767,10 +1777,10 @@ export class Matrix3d implements BeJSONFunctions { public scaleColumns(scaleX: number, scaleY: number, scaleZ: number, result?: Matrix3d): Matrix3d { return Matrix3d.createRowValues ( - this.coffs[0] * scaleX, this.coffs[1] * scaleY, this.coffs[2] * scaleZ, - this.coffs[3] * scaleX, this.coffs[4] * scaleY, this.coffs[5] * scaleZ, - this.coffs[6] * scaleX, this.coffs[7] * scaleY, this.coffs[8] * scaleZ, - result); + this.coffs[0] * scaleX, this.coffs[1] * scaleY, this.coffs[2] * scaleZ, + this.coffs[3] * scaleX, this.coffs[4] * scaleY, this.coffs[5] * scaleZ, + this.coffs[6] * scaleX, this.coffs[7] * scaleY, this.coffs[8] * scaleZ, + result); } /** create a Matrix3d whose columns are scaled copies of this Matrix3d. @@ -1807,10 +1817,10 @@ export class Matrix3d implements BeJSONFunctions { public scaleRows(scaleX: number, scaleY: number, scaleZ: number, result?: Matrix3d): Matrix3d { return Matrix3d.createRowValues ( - this.coffs[0] * scaleX, this.coffs[1] * scaleX, this.coffs[2] * scaleX, - this.coffs[3] * scaleY, this.coffs[4] * scaleY, this.coffs[5] * scaleY, - this.coffs[6] * scaleZ, this.coffs[7] * scaleZ, this.coffs[8] * scaleZ, - result); + this.coffs[0] * scaleX, this.coffs[1] * scaleX, this.coffs[2] * scaleX, + this.coffs[3] * scaleY, this.coffs[4] * scaleY, this.coffs[5] * scaleY, + this.coffs[6] * scaleZ, this.coffs[7] * scaleZ, this.coffs[8] * scaleZ, + result); } /** * add scaled values from other Matrix3d to this Matrix3d @@ -1830,10 +1840,10 @@ export class Matrix3d implements BeJSONFunctions { public scale(scale: number, result?: Matrix3d): Matrix3d { return Matrix3d.createRowValues ( - this.coffs[0] * scale, this.coffs[1] * scale, this.coffs[2] * scale, - this.coffs[3] * scale, this.coffs[4] * scale, this.coffs[5] * scale, - this.coffs[6] * scale, this.coffs[7] * scale, this.coffs[8] * scale, - result); + this.coffs[0] * scale, this.coffs[1] * scale, this.coffs[2] * scale, + this.coffs[3] * scale, this.coffs[4] * scale, this.coffs[5] * scale, + this.coffs[6] * scale, this.coffs[7] * scale, this.coffs[8] * scale, + result); } @@ -2051,22 +2061,12 @@ export class Matrix3d implements BeJSONFunctions { if (mag2 === 0.0) { return Matrix3d.createIdentity(); } else { - const props: number[][] = [[], [], []]; const a = 1.0 / mag2; - props[0][0] = a * (qqw + qqx - qqy - qqz); - props[1][0] = 2.0 * a * (quat.w * quat.z + quat.x * quat.y); - props[2][0] = 2.0 * a * (quat.x * quat.z - quat.w * quat.y); - - props[0][1] = 2.0 * a * (quat.x * quat.y - quat.w * quat.z); - props[1][1] = a * (qqw - qqx + qqy - qqz); - props[2][1] = 2.0 * a * (quat.w * quat.x + quat.y * quat.z); - - props[0][2] = 2.0 * a * (quat.x * quat.z + quat.w * quat.y); - props[1][2] = 2.0 * a * (quat.y * quat.z - quat.w * quat.x); - props[2][2] = a * (qqw - qqx - qqy + qqz); - const matrix = Matrix3d.fromJSON(props); - matrix.transposeInPlace(); + const matrix = Matrix3d.createRowValues( + a * (qqw + qqx - qqy - qqz), 2.0 * a * (quat.w * quat.z + quat.x * quat.y), 2.0 * a * (quat.x * quat.z - quat.w * quat.y), + 2.0 * a * (quat.x * quat.y - quat.w * quat.z), a * (qqw - qqx + qqy - qqz), 2.0 * a * (quat.w * quat.x + quat.y * quat.z), + 2.0 * a * (quat.x * quat.z + quat.w * quat.y), 2.0 * a * (quat.y * quat.z - quat.w * quat.x), a * (qqw - qqx - qqy + qqz)); return matrix; } } diff --git a/core/geometry/src/geometry3d/Point2dArrayCarrier.ts b/core/geometry/src/geometry3d/Point2dArrayCarrier.ts index 172b9c4..f34a247 100644 --- a/core/geometry/src/geometry3d/Point2dArrayCarrier.ts +++ b/core/geometry/src/geometry3d/Point2dArrayCarrier.ts @@ -30,7 +30,7 @@ export class Point2dArrayCarrier extends IndexedXYCollection { * @param result caller-allocated destination * @returns undefined if the index is out of bounds */ - public atPoint2dIndex(index: number, result?: Point2d): Point2d | undefined { + public getPoint2dAtCheckedPointIndex(index: number, result?: Point2d): Point2d | undefined { if (this.isValidIndex(index)) { const source = this.data[index]; return Point2d.create(source.x, source.y, result); @@ -42,7 +42,7 @@ export class Point2dArrayCarrier extends IndexedXYCollection { * @param result caller-allocated destination * @returns undefined if the index is out of bounds */ - public atVector2dIndex(index: number, result?: Vector2d): Vector2d | undefined { + public getVector2dAtCheckedVectorIndex(index: number, result?: Vector2d): Vector2d | undefined { if (this.isValidIndex(index)) { const source = this.data[index]; return Vector2d.create(source.x, source.y, result); diff --git a/core/geometry/src/geometry3d/Point2dVector2d.ts b/core/geometry/src/geometry3d/Point2dVector2d.ts index 656e1d4..5bfd0f0 100644 --- a/core/geometry/src/geometry3d/Point2dVector2d.ts +++ b/core/geometry/src/geometry3d/Point2dVector2d.ts @@ -28,10 +28,14 @@ export class XY implements XAndY { this.x = 0; this.y = 0; } } + public freeze() { Object.freeze(this); } /** Returns true if this and other have equal x,y parts within Geometry.smallMetricDistance. */ public isAlmostEqual(other: XAndY, tol?: number): boolean { return Geometry.isSameCoordinate(this.x, other.x, tol) && Geometry.isSameCoordinate(this.y, other.y, tol); } + /** Returns true if this and other have equal x,y parts within Geometry.smallMetricDistance. */ + public isAlmostEqualXY(x: number, y: number, tol?: number): boolean { return Geometry.isSameCoordinate(this.x, x, tol) && Geometry.isSameCoordinate(this.y, y, tol); } + /** return a json array or object with the [x,y] data. */ public toJSON(): XYProps { return [this.x, this.y]; } public toJSONXY(): XYProps { return { x: this.x, y: this.y }; } diff --git a/core/geometry/src/geometry3d/Point3dVector3d.ts b/core/geometry/src/geometry3d/Point3dVector3d.ts index cc02b19..efe62cb 100644 --- a/core/geometry/src/geometry3d/Point3dVector3d.ts +++ b/core/geometry/src/geometry3d/Point3dVector3d.ts @@ -303,7 +303,7 @@ export class Point3d extends XYZ { return Geometry.crossProductXYXY(pointA.x - this.x, pointA.y - this.y, pointB.x - this.x, pointB.y - this.y); } /** Return a point interpolated between this point and the right param. */ - public interpolate(fraction: number, other: Point3d, result?: Point3d): Point3d { + public interpolate(fraction: number, other: XYAndZ, result?: Point3d): Point3d { if (fraction <= 0.5) return Point3d.create(this.x + fraction * (other.x - this.x), this.y + fraction * (other.y - this.y), this.z + fraction * (other.z - this.z), result); const t: number = fraction - 1.0; @@ -834,7 +834,7 @@ export class Vector3d extends XYZ { * @param pointA start point of second vector of dot product * @param pointB end point of second vector of dot product */ - public dotProductStartEnd(pointA: Point3d, pointB: Point3d): number { + public dotProductStartEnd(pointA: XYAndZ, pointB: XYAndZ): number { return this.x * (pointB.x - pointA.x) + this.y * (pointB.y - pointA.y) + this.z * (pointB.z - pointA.z); diff --git a/core/geometry/src/geometry3d/PointHelpers.ts b/core/geometry/src/geometry3d/PointHelpers.ts index 3820cce..d9f0d26 100644 --- a/core/geometry/src/geometry3d/PointHelpers.ts +++ b/core/geometry/src/geometry3d/PointHelpers.ts @@ -21,7 +21,7 @@ export class NumberArray { /** return the sum of values in an array, The summation is done with correction terms which * improves last-bit numeric accuracy. */ - public static PreciseSum(data: number[]) { + public static preciseSum(data: number[]) { const n = data.length; if (n === 0) return 0.0; @@ -77,7 +77,7 @@ export class NumberArray { } return false; } - public static MaxAbsArray(values: number[]) { + public static maxAbsArray(values: number[]) { const arrLen = values.length; if (arrLen === 0) { return 0.0; @@ -91,7 +91,7 @@ export class NumberArray { } return a; } - public static MaxAbsTwo(a1: number, a2: number) { + public static maxAbsTwo(a1: number, a2: number) { a1 = Math.abs(a1); a2 = Math.abs(a2); return (a1 > a2) ? a1 : a2; @@ -283,6 +283,112 @@ export class Point3dArray { } return result; } + /** + * Compute the 8 weights of trilinear mapping + * By appropriate choice of weights, this can be usef for both point and derivative mappints. + * @param weights preallocated array to recevie weights. + * @param u0 low u weight + * @param u1 high u weight + * @param v0 low v weight + * @param v1 high v weight + * @param w0 low w weight + * @param w1 high w weight + */ + public static evaluateTrilinearWeights(weights: Float64Array, u0: number, u1: number, v0: number, v1: number, w0: number, w1: number) { + + weights[0] = u0 * v0 * w0; + weights[1] = u1 * v0 * w0; + weights[2] = u0 * v1 * w0; + weights[3] = u1 * v1 * w0; + weights[4] = u0 * v0 * w1; + weights[5] = u1 * v0 * w1; + weights[6] = u0 * v1 * w1; + weights[7] = u1 * v1 * w1; + } + /** + * sum the weighted x components from a point array. + * * weights.length is the number of summed terms + * * points must have at least that length + * @param weights + * @param points + */ + public static sumWeightedX(weights: Float64Array, points: Point3d[]): number { + let sum = 0.0; + const n = weights.length; + for (let i = 0; i < n; i++) + sum += weights[i] * points[i].x; + return sum; + } + + /** + * sum the weighted x components from a point array. + * * weights.length is the number of summed terms + * * points must have at least that length + * @param weights + * @param points + */ + public static sumWeightedY(weights: Float64Array, points: Point3d[]): number { + let sum = 0.0; + const n = weights.length; + for (let i = 0; i < n; i++) + sum += weights[i] * points[i].y; + return sum; + } + + /** + * sum the weighted x components from a point array. + * * weights.length is the number of summed terms + * * points must have at least that length + * @param weights + * @param points + */ + public static sumWeightedZ(weights: Float64Array, points: Point3d[]): number { + let sum = 0.0; + const n = weights.length; + for (let i = 0; i < n; i++) + sum += weights[i] * points[i].z; + return sum; + } + + private static _weightUVW = new Float64Array(8); + private static _weightDU = new Float64Array(8); + private static _weightDV = new Float64Array(8); + private static _weightDW = new Float64Array(8); + /** + * Compute a point by trilinear mapping. + * @param points array of 8 points at corners, with x index varying fastest. + * @param result optional result point + */ + public static evaluateTrilinearPoint(points: Point3d[], u: number, v: number, w: number, result?: Point3d): Point3d { + if (!result) result = Point3d.create(0, 0, 0); + this.evaluateTrilinearWeights(this._weightUVW, 1 - u, u, 1 - v, v, 1 - w, w); + let a; + for (let i = 0; i < 8; i++) { + a = this._weightUVW[i]; + result.x += a * points[i].x; + result.y += a * points[i].y; + result.z += a * points[i].z; + } + return result; + } + /** + * Compute a point and derivatives wrt uvw by trilinear mapping. + * * evaluated point is the point part of the transform + * * u,v,w derivatives are the respective columns of the matrix part of the transform. + * @param points array of 8 points at corners, with x index varying fastest. + * @param result optional result transform + */ + public static evaluateTrilinearDerivativeTransform(points: Point3d[], u: number, v: number, w: number, result?: Transform): Transform { + this.evaluateTrilinearWeights(this._weightUVW, 1 - u, u, 1 - v, v, 1 - w, w); + this.evaluateTrilinearWeights(this._weightDU, -1, 1, 1 - v, v, 1 - w, w); + this.evaluateTrilinearWeights(this._weightDV, 1 - u, u, -1, 1, 1 - w, w); + this.evaluateTrilinearWeights(this._weightDW, 1 - u, u, 1 - v, v, -1, 1); + return Transform.createRowValues( + this.sumWeightedX(this._weightDU, points), this.sumWeightedX(this._weightDV, points), this.sumWeightedX(this._weightDW, points), this.sumWeightedX(this._weightUVW, points), + this.sumWeightedY(this._weightDU, points), this.sumWeightedY(this._weightDV, points), this.sumWeightedY(this._weightDW, points), this.sumWeightedY(this._weightUVW, points), + this.sumWeightedZ(this._weightDU, points), this.sumWeightedZ(this._weightDV, points), this.sumWeightedZ(this._weightDW, points), this.sumWeightedZ(this._weightUVW, points), + result); + } public static unpackNumbersToPoint3dArray(data: Float64Array | number[]): Point3d[] { const result = []; @@ -380,7 +486,7 @@ export class Point3dArray { const p = Point3d.create(); if (points.length > 0) { for (let i = 0; i < points.length; i++) { - points.atPoint3dIndex(i, p); + points.getPoint3dAtCheckedPointIndex(i, p); result.x += p.x; result.y += p.y; result.z += p.z; } result.scaleInPlace(1.0 / points.length); @@ -945,7 +1051,7 @@ export class Point3dArrayCarrier extends IndexedXYZCollection { * @param result caller-allocated destination * @returns undefined if the index is out of bounds */ - public atPoint3dIndex(index: number, result?: Point3d): Point3d | undefined { + public getPoint3dAtCheckedPointIndex(index: number, result?: Point3d): Point3d | undefined { if (this.isValidIndex(index)) { const source = this.data[index]; return Point3d.create(source.x, source.y, source.z, result); @@ -957,7 +1063,7 @@ export class Point3dArrayCarrier extends IndexedXYZCollection { * @param result caller-allocated destination * @returns undefined if the index is out of bounds */ - public atVector3dIndex(index: number, result?: Vector3d): Vector3d | undefined { + public getVector3dAtCheckedVectorIndex(index: number, result?: Vector3d): Vector3d | undefined { if (this.isValidIndex(index)) { const source = this.data[index]; return Vector3d.create(source.x, source.y, source.z, result); diff --git a/core/geometry/src/geometry3d/Range.ts b/core/geometry/src/geometry3d/Range.ts index e8dde36..7b394b3 100644 --- a/core/geometry/src/geometry3d/Range.ts +++ b/core/geometry/src/geometry3d/Range.ts @@ -5,13 +5,13 @@ /** @module CartesianGeometry */ -import { Geometry, BeJSONFunctions } from "../Geometry"; +import { BeJSONFunctions, Geometry } from "../Geometry"; +import { GrowableXYZArray } from "./GrowableXYZArray"; +import { Matrix3d } from "./Matrix3d"; import { Point2d, Vector2d } from "./Point2dVector2d"; import { Point3d, Vector3d } from "./Point3dVector3d"; import { Transform } from "./Transform"; -import { Matrix3d } from "./Matrix3d"; -import { Range1dProps, Range2dProps, Range3dProps, LowAndHighXYZ, LowAndHighXY, XAndY, XYAndZ } from "./XYZProps"; -import { GrowableXYZArray } from "./GrowableXYZArray"; +import { LowAndHighXY, LowAndHighXYZ, Range1dProps, Range2dProps, Range3dProps, XAndY, XYAndZ } from "./XYZProps"; export abstract class RangeBase { protected static readonly _EXTREME_POSITIVE: number = 1.0e200; @@ -53,21 +53,6 @@ export abstract class RangeBase { return x - high; return 0.0; } - - /** - * move low and high points by scaleFactor around the center point. - * @param scaleFactor scale factor applied to low, high distance from center. - */ - public abstract scaleAboutCenterInPlace(scaleFactor: number): void; - - /** - * move all limits by a fixed amount. - * * positive delta expands the range size - * * negative delta reduces the range size - * * if any dimension reduces below zero size, the whole range becomes null - * @param delta shift to apply. - */ - public abstract expandInPlace(delta: number): void; } export class Range3d extends RangeBase implements LowAndHighXYZ, BeJSONFunctions { @@ -94,17 +79,17 @@ export class Range3d extends RangeBase implements LowAndHighXYZ, BeJSONFunctions * @param f64 the array, which should contain exactly 6 values in this order: lowx, lowy, lowz, highx, highy, highz * @return a new Range3d object */ - public static fromFloat64Array(f64: Float64Array) { + public static fromFloat64Array(f64: Float64Array): T { if (f64.length !== 6) throw new Error("invalid array"); - return new Range3d(f64[0], f64[1], f64[2], f64[3], f64[4], f64[5]); + return new this(f64[0], f64[1], f64[2], f64[3], f64[4], f64[5]) as T; } /** * Construct a Range3d from an un-typed array. This mostly useful when interpreting ECSQL query results of the 'blob' type, where you know that that result is a Range3d. * @param buffer untyped array * @return a new Range3d object */ - public static fromArrayBuffer(buffer: ArrayBuffer) { return this.fromFloat64Array(new Float64Array(buffer)); } + public static fromArrayBuffer(buffer: ArrayBuffer): T { return this.fromFloat64Array(new Float64Array(buffer)); } // explicit ctor - no enforcement of value relationships public constructor(lowx: number = RangeBase._EXTREME_POSITIVE, lowy: number = RangeBase._EXTREME_POSITIVE, lowz: number = RangeBase._EXTREME_POSITIVE, @@ -122,9 +107,9 @@ export class Range3d extends RangeBase implements LowAndHighXYZ, BeJSONFunctions /** copy low and high values from other. */ public setFrom(other: Range3d) { this.low.setFrom(other.low); this.high.setFrom(other.high); } - public static createFrom(other: Range3d, result?: Range3d): Range3d { + public static createFrom(other: Range3d, result?: T): T { if (result) { result.setFrom(other); return result; } - return Range3d.createXYZXYZOrCorrectToNull(other.low.x, other.low.y, other.low.z, + return this.createXYZXYZOrCorrectToNull(other.low.x, other.low.y, other.low.z, other.high.x, other.high.y, other.high.z, result); } @@ -150,8 +135,8 @@ export class Range3d extends RangeBase implements LowAndHighXYZ, BeJSONFunctions /** Return a JSON object */ public toJSON(): Range3dProps { return { low: this.low.toJSON(), high: this.high.toJSON() }; } - public static fromJSON(json?: Range3dProps): Range3d { - const result = new Range3d(); + public static fromJSON(json?: Range3dProps): T { + const result = new this() as T; result.setFromJSON(json); return result; } @@ -172,14 +157,14 @@ export class Range3d extends RangeBase implements LowAndHighXYZ, BeJSONFunctions } } - public clone(result?: Range3d) { - result = result ? result : new Range3d(); + public clone(result?: this): this { + result = result ? result : new (this.constructor as any)() as this; result.setDirect(this.low.x, this.low.y, this.low.z, this.high.x, this.high.y, this.high.z, false); return result; } /** Return a range initialized to have no content. */ - public static createNull(result?: Range3d): Range3d { - result = result ? result : new Range3d(); + public static createNull(result?: T): T { + result = result ? result : new this() as T; result.setNull(); return result; } @@ -199,23 +184,23 @@ export class Range3d extends RangeBase implements LowAndHighXYZ, BeJSONFunctions return result; } /** create a Range3d enclosing the transformed points. */ - public static createTransformed(transform: Transform, ...point: Point3d[]): Range3d { - const result = Range3d.createNull(); + public static createTransformed(transform: Transform, ...point: Point3d[]): T { + const result = this.createNull(); let p; for (p of point) result.extendTransformedXYZ(transform, p.x, p.y, p.z); return result; } /** create a Range3d enclosing the transformed points. */ - public static createTransformedArray(transform: Transform, points: Point3d[]): Range3d { - const result = Range3d.createNull(); + public static createTransformedArray(transform: Transform, points: Point3d[]): T { + const result = this.createNull(); result.extendArray(points, transform); return result; } /** create a Range3d enclosing the points after inverse transform. */ - public static createInverseTransformedArray(transform: Transform, points: Point3d[]): Range3d { - const result = Range3d.createNull(); + public static createInverseTransformedArray(transform: Transform, points: Point3d[]): T { + const result = this.createNull(); result.extendInverseTransformedArray(points, transform); return result; } @@ -227,15 +212,15 @@ export class Range3d extends RangeBase implements LowAndHighXYZ, BeJSONFunctions } /** Create a single point range */ - public static createXYZ(x: number, y: number, z: number, result?: Range3d): Range3d { - result = result ? result : new Range3d(); + public static createXYZ(x: number, y: number, z: number, result?: T): T { + result = result ? result : new this() as T; result.setDirect(x, y, z, x, y, z, false); return result; } /** Create a box with 2 pairs of xyz candidates. Theses are compared and shuffled as needed for the box. */ - public static createXYZXYZ(xA: number, yA: number, zA: number, xB: number, yB: number, zB: number, result?: Range3d): Range3d { - result = result ? result : new Range3d(); + public static createXYZXYZ(xA: number, yA: number, zA: number, xB: number, yB: number, zB: number, result?: T): T { + result = result ? result : new this() as T; result.setDirect( Math.min(xA, xB), Math.min(yA, yB), Math.min(zA, zB), Math.max(xA, xB), Math.max(yA, yB), Math.max(zA, zB), false); @@ -243,8 +228,8 @@ export class Range3d extends RangeBase implements LowAndHighXYZ, BeJSONFunctions } /** Create a box with 2 pairs of xyz candidates. If any direction has order flip, create null. */ - public static createXYZXYZOrCorrectToNull(xA: number, yA: number, zA: number, xB: number, yB: number, zB: number, result?: Range3d): Range3d { - result = result ? result : new Range3d(); + public static createXYZXYZOrCorrectToNull(xA: number, yA: number, zA: number, xB: number, yB: number, zB: number, result?: T): T { + result = result ? result : new this() as T; result.setDirect( Math.min(xA, xB), Math.min(yA, yB), Math.min(zA, zB), Math.max(xA, xB), Math.max(yA, yB), Math.max(zA, zB), true); @@ -252,8 +237,8 @@ export class Range3d extends RangeBase implements LowAndHighXYZ, BeJSONFunctions } /** Creates a 3d range from a 2d range's low and high members, setting the corresponding z values to the value given. */ - public static createRange2d(range: Range2d, z: number = 0, result?: Range3d): Range3d { - const retVal = result ? result : new Range3d(); + public static createRange2d(range: Range2d, z: number = 0, result?: T): T { + const retVal = result ? result : new this() as T; retVal.setNull(); retVal.extendXYZ(range.low.x, range.low.y, z); @@ -262,8 +247,8 @@ export class Range3d extends RangeBase implements LowAndHighXYZ, BeJSONFunctions } /** Create a range around an array of points. */ - public static createArray(points: Point3d[], result?: Range3d): Range3d { - result = result ? result : new Range3d(); + public static createArray(points: Point3d[], result?: T): T { + result = result ? result : new this() as T; result.setNull(); let point; for (point of points) @@ -283,10 +268,10 @@ export class Range3d extends RangeBase implements LowAndHighXYZ, BeJSONFunctions else // growable array -- this should be implemented without point extraction !!! if (transform) for (let i = 0; i < points.length; i++) - this.extendTransformedXYZ(transform, points.getPoint3dAt(i).x, points.getPoint3dAt(i).y, points.getPoint3dAt(i).z); + this.extendTransformedXYZ(transform, points.getPoint3dAtUncheckedPointIndex(i).x, points.getPoint3dAtUncheckedPointIndex(i).y, points.getPoint3dAtUncheckedPointIndex(i).z); else for (let i = 0; i < points.length; i++) - this.extendXYZ(points.getPoint3dAt(i).x, points.getPoint3dAt(i).y, points.getPoint3dAt(i).z); + this.extendXYZ(points.getPoint3dAtUncheckedPointIndex(i).x, points.getPoint3dAtUncheckedPointIndex(i).y, points.getPoint3dAtUncheckedPointIndex(i).z); } /** extend a range around an array of points (optionally transformed) */ @@ -296,7 +281,7 @@ export class Range3d extends RangeBase implements LowAndHighXYZ, BeJSONFunctions this.extendInverseTransformedXYZ(transform, point.x, point.y, point.z); else // growable array -- this should be implemented without point extraction !!! for (let i = 0; i < points.length; i++) - this.extendInverseTransformedXYZ(transform, points.getPoint3dAt(i).x, points.getPoint3dAt(i).y, points.getPoint3dAt(i).z); + this.extendInverseTransformedXYZ(transform, points.getPoint3dAtUncheckedPointIndex(i).x, points.getPoint3dAtUncheckedPointIndex(i).y, points.getPoint3dAtUncheckedPointIndex(i).z); } /** multiply the point x,y,z by transform and use the coordinate to extend this range. @@ -370,6 +355,18 @@ export class Range3d extends RangeBase implements LowAndHighXYZ, BeJSONFunctions && this.high.z === this.low.z; } + /** Get the center point of this Range3d */ + public get center(): Point3d { return this.low.interpolate(.5, this.high); } + public get left(): number { return this.low.x; } + public get bottom(): number { return this.low.y; } + public get front(): number { return this.low.z; } + public get right(): number { return this.high.x; } + public get top(): number { return this.high.y; } + public get back(): number { return this.high.z; } + public get width(): number { return this.xLength(); } + public get depth(): number { return this.yLength(); } + public get height(): number { return this.zLength(); } + /** Return the length of the box in the x direction */ public xLength(): number { const a = this.high.x - this.low.x; return a > 0.0 ? a : 0.0; } @@ -578,9 +575,9 @@ export class Range3d extends RangeBase implements LowAndHighXYZ, BeJSONFunctions return Range3d.createNull(result); return Range3d.createXYZXYZOrCorrectToNull ( - Math.max(this.low.x, other.low.x), Math.max(this.low.y, other.low.y), Math.max(this.low.z, other.low.z), - Math.min(this.high.x, other.high.x), Math.min(this.high.y, other.high.y), Math.min(this.high.z, other.high.z), - result); + Math.max(this.low.x, other.low.x), Math.max(this.low.y, other.low.y), Math.max(this.low.z, other.low.z), + Math.min(this.high.x, other.high.x), Math.min(this.high.y, other.high.y), Math.min(this.high.z, other.high.z), + result); } @@ -589,13 +586,13 @@ export class Range3d extends RangeBase implements LowAndHighXYZ, BeJSONFunctions if (this.isNull) return other.clone(result); if (other.isNull) - return this.clone(result); + return this.clone(result as this); // we trust null ranges have EXTREME values, so a null in either input leads to expected results. return Range3d.createXYZXYZOrCorrectToNull ( - Math.min(this.low.x, other.low.x), Math.min(this.low.y, other.low.y), Math.min(this.low.z, other.low.z), - Math.max(this.high.x, other.high.x), Math.max(this.high.y, other.high.y), Math.max(this.high.z, other.high.z), - result); + Math.min(this.low.x, other.low.x), Math.min(this.low.y, other.low.y), Math.min(this.low.z, other.low.z), + Math.max(this.high.x, other.high.x), Math.max(this.high.y, other.high.y), Math.max(this.high.z, other.high.z), + result); } /** * move low and high points by scaleFactor around the center point. @@ -654,7 +651,29 @@ export class Range3d extends RangeBase implements LowAndHighXYZ, BeJSONFunctions matrix.coffs[8] = 1; return transform; } + + /** Ensure that the length of each dimension of this AxisAlignedBox3d is at least a minimum size. If not, expand to minimum about the center. + * @param min The minimum length for each dimension. + */ + public ensureMinLengths(min: number = .001) { + let size = (min - this.xLength()) / 2.0; + if (size > 0) { + this.low.x -= size; + this.high.x += size; + } + size = (min - this.yLength()) / 2.0; + if (size > 0) { + this.low.y -= size; + this.high.y += size; + } + size = (min - this.zLength()) / 2.0; + if (size > 0) { + this.low.z -= size; + this.high.z += size; + } + } } + export class Range1d extends RangeBase { // low and high are always non-null objects @@ -667,7 +686,7 @@ export class Range1d extends RangeBase { this.high = RangeBase._EXTREME_NEGATIVE; } // internal use only -- directly set all coordinates, test only if directed. - private setDirect(low: number, high: number, correctToNull: boolean) { + private setDirect(low: number, high: number, correctToNull: boolean = false) { this.low = low; this.high = high; if (correctToNull && low > high) @@ -679,7 +698,7 @@ export class Range1d extends RangeBase { high: number = RangeBase._EXTREME_NEGATIVE) { super(); this.low = low; this.high = high; // duplicates set_direct, but compiler is not convinced they are set. - this.set_direct(low, high); + this.setDirect(low, high); } /** Returns true if this and other have equal low and high parts */ public isAlmostEqual(other: Range1d): boolean { @@ -707,8 +726,8 @@ export class Range1d extends RangeBase { this.extendX(json.high); } } - public static fromJSON(json?: Range1dProps): Range1d { - const result = new Range1d(); + public static fromJSON(json?: Range1dProps): T { + const result = new this() as T; if (json) result.setFromJSON(json); return result; @@ -720,33 +739,29 @@ export class Range1d extends RangeBase { */ public toJSON(): Range1dProps { if (this.isNull) return new Array(); else return [this.low, this.high]; } - // internal use only -- directly set both lwo and high coordinates, without tests. - private set_direct(low: number, high: number) { - this.low = low; this.high = high; - } /** return a new Range1d with contents of this. * @param result optional result. */ - public clone(result?: Range1d) { - result = result ? result : new Range1d(); - result.set_direct(this.low, this.high); + public clone(result?: this): this { + result = result ? result : new (this.constructor as any)() as this; + result.setDirect(this.low, this.high); return result; } /** return a new Range1d with contents of this. * @param result optional result. */ - public static createFrom(other: Range1d, result?: Range1d) { - result = result ? result : new Range1d(); - result.set_direct(other.low, other.high); + public static createFrom(other: T, result?: T) { + result = result ? result : new this() as T; + result.setDirect(other.low, other.high); return result; } /** Create a range with no content. * @param result optional result. */ - public static createNull(result?: Range1d): Range1d { - result = result ? result : new Range1d(); + public static createNull(result?: T): T { + result = result ? result : new this() as T; result.setNull(); return result; } @@ -758,9 +773,9 @@ export class Range1d extends RangeBase { public setX(x: number) { this.low = this.high = x; } /** Create a single point box */ - public static createX(x: number, result?: Range1d): Range1d { - result = result ? result : new Range1d(); - result.set_direct(x, x); + public static createX(x: number, result?: T): T { + result = result ? result : new this() as T; + result.setDirect(x, x); return result; } @@ -768,9 +783,9 @@ export class Range1d extends RangeBase { * @param xA first value * @param xB second value */ - public static createXX(xA: number, xB: number, result?: Range1d): Range1d { - result = result ? result : new Range1d(); - result.set_direct( + public static createXX(xA: number, xB: number, result?: T): T { + result = result ? result : new this() as T; + result.setDirect( Math.min(xA, xB), Math.max(xA, xB)); return result; @@ -780,12 +795,12 @@ export class Range1d extends RangeBase { * @param xA first value * @param xB second value */ - public static createXXOrCorrectToNull(xA: number, xB: number, result?: Range1d): Range1d { + public static createXXOrCorrectToNull(xA: number, xB: number, result?: T): T { if (xB < xA) return Range1d.createNull(result); - result = result ? result : new Range1d(); - result.set_direct( + result = result ? result : new this() as T; + result.setDirect( Math.min(xA, xB), Math.max(xA, xB)); return result; @@ -795,8 +810,8 @@ export class Range1d extends RangeBase { * @param values array of points to be contained in the range. * @param result optional result. */ - public static createArray(values: Float64Array | number[], result?: Range1d): Range1d { - result = result ? result : new Range1d(); + public static createArray(values: Float64Array | number[], result?: T): T { + result = result ? result : new this() as T; let x; for (x of values) result.extendX(x); @@ -898,9 +913,9 @@ export class Range1d extends RangeBase { return Range1d.createXXOrCorrectToNull ( - Math.max(this.low, other.low), - Math.min(this.high, other.high), - result); + Math.max(this.low, other.low), + Math.min(this.high, other.high), + result); } @@ -910,9 +925,9 @@ export class Range1d extends RangeBase { // we trust null ranges have EXTREME values, so a null in either input leads to expected results. return Range1d.createXX ( - Math.min(this.low, other.low), - Math.max(this.high, other.high), - result); + Math.min(this.low, other.low), + Math.max(this.high, other.high), + result); } /** * move low and high points by scaleFactor around the center point. @@ -960,17 +975,17 @@ export class Range2d extends RangeBase implements LowAndHighXY { * @param f64 the array, which should contain exactly 4 values in this order: lowx, lowy, highx, highy * @return a new Range2d object */ - public static fromFloat64Array(f64: Float64Array) { - if (f64.length !== 6) + public static fromFloat64Array(f64: Float64Array): T { + if (f64.length !== 4) throw new Error("invalid array"); - return new Range3d(f64[0], f64[1], f64[2], f64[3], f64[4], f64[5]); + return new this(f64[0], f64[1], f64[2], f64[3]) as T; } /** * Construct a Range2d from an un-typed array. This mostly useful when interpreting ECSQL query results of the 'blob' type, where you know that that result is a Range3d. * @param buffer untyped array * @return a new Range2d object */ - public static fromArrayBuffer(buffer: ArrayBuffer) { return this.fromFloat64Array(new Float64Array(buffer)); } + public static fromArrayBuffer(buffer: ArrayBuffer): T { return this.fromFloat64Array(new Float64Array(buffer)); } // explicit ctor - no enforcement of value relationships public constructor(lowx = Range2d._EXTREME_POSITIVE, lowy = Range2d._EXTREME_POSITIVE, highx = Range2d._EXTREME_NEGATIVE, highy = Range2d._EXTREME_NEGATIVE) { @@ -987,9 +1002,9 @@ export class Range2d extends RangeBase implements LowAndHighXY { this.low.set(other.low.x, other.low.y); this.high.set(other.high.x, other.high.y); } - public static createFrom(other: LowAndHighXY, result?: Range2d): Range2d { + public static createFrom(other: LowAndHighXY, result?: T): T { if (result) { result.setFrom(other); return result; } - return Range2d.createXYXYOrCorrectToNull(other.low.x, other.low.y, other.high.x, other.high.y, result); + return this.createXYXYOrCorrectToNull(other.low.x, other.low.y, other.high.x, other.high.y, result) as T; } /** treat any array of numbers as numbers to be inserted !!! */ public setFromJSON(json: Range2dProps): void { @@ -1013,8 +1028,8 @@ export class Range2d extends RangeBase implements LowAndHighXY { public freeze() { Object.freeze(this.low); Object.freeze(this.high); } public toJSON(): Range2dProps { return this.isNull ? [] : [this.low.toJSON(), this.high.toJSON()]; } - public static fromJSON(json?: Range2dProps): Range2d { - const result = new Range2d(); + public static fromJSON(json?: Range2dProps): T { + const result = new this() as T; if (json) result.setFromJSON(json); return result; @@ -1032,14 +1047,14 @@ export class Range2d extends RangeBase implements LowAndHighXY { } } /** return a clone of this range (or copy to optional result) */ - public clone(result?: Range2d) { - result = result ? result : new Range2d(); + public clone(result?: this): this { + result = result ? result : new (this.constructor as any)() as this; result.setDirect(this.low.x, this.low.y, this.high.x, this.high.y, false); return result; } /** create a range with no content. */ - public static createNull(result?: Range2d): Range2d { - result = result ? result : new Range2d(); + public static createNull(result?: T): T { + result = result ? result : new this() as T; result.setNull(); return result; } @@ -1050,25 +1065,25 @@ export class Range2d extends RangeBase implements LowAndHighXY { } /** Create a single point box */ - public static createXY(x: number, y: number, result?: Range2d): Range2d { - result = result ? result : new Range2d(); + public static createXY(x: number, y: number, result?: T): T { + result = result ? result : new this() as T; result.setDirect(x, y, x, y, false); return result; } /** Create a box with 2 pairs of xy candidates. Theses are compared and shuffled as needed for the box. */ - public static createXYXY(xA: number, yA: number, xB: number, yB: number, result?: Range2d): Range2d { - result = result ? result : new Range2d(); + public static createXYXY(xA: number, yA: number, xB: number, yB: number, result?: T): T { + result = result ? result : new this() as T; result.setDirect( Math.min(xA, xB), Math.min(yA, yB), Math.max(xA, xB), Math.max(yA, yB), false); return result; } /** Create a box with 2 pairs of xy candidates. If any direction has order flip, create null. */ - public static createXYXYOrCorrectToNull(xA: number, yA: number, xB: number, yB: number, result?: Range2d): Range2d { + public static createXYXYOrCorrectToNull(xA: number, yA: number, xB: number, yB: number, result?: T): T { if (xA > xB || yA > yB) - return Range2d.createNull(result); - result = result ? result : new Range2d(); + return this.createNull(result); + result = result ? result : new this() as T; result.setDirect( Math.min(xA, xB), Math.min(yA, yB), Math.max(xA, xB), Math.max(yA, yB), true); @@ -1076,8 +1091,8 @@ export class Range2d extends RangeBase implements LowAndHighXY { } /** Create a range around an array of points. */ - public static createArray(points: Point2d[], result?: Range2d): Range2d { - result = result ? result : new Range2d(); + public static createArray(points: Point2d[], result?: T): T { + result = result ? result : new this() as T; let point; for (point of points) result.extendPoint(point); @@ -1102,6 +1117,14 @@ export class Range2d extends RangeBase implements LowAndHighXY { && this.high.y === this.low.y; } + public get center(): Point2d { return this.low.interpolate(.5, this.high); } + public get left(): number { return this.low.x; } + public get bottom(): number { return this.low.y; } + public get right(): number { return this.high.x; } + public get top(): number { return this.high.y; } + public get width(): number { return this.xLength(); } + public get height(): number { return this.yLength(); } + /** Length of the box in the x direction */ public xLength(): number { const a = this.high.x - this.low.x; return a > 0.0 ? a : 0.0; } @@ -1209,9 +1232,9 @@ export class Range2d extends RangeBase implements LowAndHighXY { return Range2d.createNull(result); return Range2d.createXYXY ( - Math.max(this.low.x, other.low.x), Math.max(this.low.y, other.low.y), - Math.min(this.high.x, other.high.x), Math.min(this.high.y, other.high.y), - result); + Math.max(this.low.x, other.low.x), Math.max(this.low.y, other.low.y), + Math.min(this.high.x, other.high.x), Math.min(this.high.y, other.high.y), + result); } @@ -1220,13 +1243,13 @@ export class Range2d extends RangeBase implements LowAndHighXY { if (this.isNull) return Range2d.createFrom(other, result); if (Range2d.isNull(other)) - return this.clone(result); + return this.clone(result as this); // we trust null ranges have EXTREME values, so a null in either input leads to expected results. return Range2d.createXYXY ( - Math.min(this.low.x, other.low.x), Math.min(this.low.y, other.low.y), - Math.max(this.high.x, other.high.x), Math.max(this.high.y, other.high.y), - result); + Math.min(this.low.x, other.low.x), Math.min(this.low.y, other.low.y), + Math.max(this.high.x, other.high.x), Math.max(this.high.y, other.high.y), + result); } /** diff --git a/core/geometry/src/geometry3d/Ray3d.ts b/core/geometry/src/geometry3d/Ray3d.ts index 07d1318..dfe37ce 100644 --- a/core/geometry/src/geometry3d/Ray3d.ts +++ b/core/geometry/src/geometry3d/Ray3d.ts @@ -9,6 +9,7 @@ import { Transform } from "./Transform"; import { Matrix3d } from "./Matrix3d"; import { AxisOrder, BeJSONFunctions, Geometry } from "../Geometry"; import { Plane3dByOriginAndUnitNormal } from "./Plane3dByOriginAndUnitNormal"; +import { XYAndZ } from "./XYZProps"; /** A Ray3d contains * * an origin point. * * a direction vector. The vector is NOT required to be normalized. @@ -233,4 +234,17 @@ export class Ray3d implements BeJSONFunctions { } return division; } + + /** Construct a vector from `ray.origin` to target point. + * * return the part of the vector that is perpendicular to `ray.direction`. + * * i.e. return the shortest vector from the ray to the point. + */ + public perpendicularPartOfVectorToTarget(targetPoint: XYAndZ, result?: Vector3d): Vector3d { + const vectorV = Vector3d.createStartEnd(this.origin, targetPoint); + const uu = this.direction.magnitudeSquared(); + const uv = this.direction.dotProductStartEnd(this.origin, targetPoint); + const fraction = Geometry.safeDivideFraction(uv, uu, 0.0); + return vectorV.plusScaled(this.direction, -fraction, result); + + } } diff --git a/core/geometry/src/geometry3d/Segment1d.ts b/core/geometry/src/geometry3d/Segment1d.ts index 4ea71e9..ce65a1f 100644 --- a/core/geometry/src/geometry3d/Segment1d.ts +++ b/core/geometry/src/geometry3d/Segment1d.ts @@ -27,7 +27,17 @@ export class Segment1d { this.x0 = x0; this.x1 = x1; } + /** + * replace both end values. + * @param x0 new x0 value + * @param x1 new y0 value + */ public set(x0: number, x1: number) { this.x0 = x0, this.x1 = x1; } + /** + * shift (translate) the segment along its axis by adding `dx` to both `x0` and `x1`. + * @param dx value to add to both x0 and x1 + */ + public shift(dx: number) { this.x0 += dx, this.x1 += dx; } /** * create segment1d with given end values * @param x0 start value diff --git a/core/geometry/src/geometry3d/Transform.ts b/core/geometry/src/geometry3d/Transform.ts index 8a6b418..4b3f876 100644 --- a/core/geometry/src/geometry3d/Transform.ts +++ b/core/geometry/src/geometry3d/Transform.ts @@ -210,31 +210,40 @@ export class Transform implements BeJSONFunctions { * so that the fixedPoint maps back to itself. */ public static createFixedPointAndMatrix(fixedPoint: Point3d, matrix: Matrix3d, result?: Transform): Transform { - const origin = Matrix3d.XYZMinusMatrixTimesXYZ(fixedPoint, matrix, fixedPoint); + const origin = Matrix3d.xyzMinusMatrixTimesXYZ(fixedPoint, matrix, fixedPoint); return Transform.createRefs(origin, matrix.clone(), result); } + /** Create a transform with the specified matrix, acting on any `pointX `via + * `pointY = matrix * (pointX - pointA) + pointB` + * so that the fixedPoint maps back to itself. + */ + public static createMatrixPickupPutdown(matrix: Matrix3d, pointA: Point3d, pointB: Point3d, result?: Transform): Transform { + const origin = Matrix3d.xyzMinusMatrixTimesXYZ(pointB, matrix, pointA); + return Transform.createRefs(origin, matrix.clone(), result); + } + /** Create a Transform which leaves the fixedPoint unchanged and * scales everything else around it by a single scale factor. */ public static createScaleAboutPoint(fixedPoint: Point3d, scale: number, result?: Transform): Transform { const matrix = Matrix3d.createScale(scale, scale, scale); - const origin = Matrix3d.XYZMinusMatrixTimesXYZ(fixedPoint, matrix, fixedPoint); + const origin = Matrix3d.xyzMinusMatrixTimesXYZ(fixedPoint, matrix, fixedPoint); return Transform.createRefs(origin, matrix, result); } /** Transform the input 2d point. Return as a new point or in the pre-allocated result (if result is given) */ public multiplyPoint2d(source: XAndY, result?: Point2d): Point2d { - return Matrix3d.XYPlusMatrixTimesXY(this._origin, this._matrix, source, result); + return Matrix3d.xyPlusMatrixTimesXY(this._origin, this._matrix, source, result); } /** Transform the input 3d point. Return as a new point or in the pre-allocated result (if result is given) */ public multiplyPoint3d(point: XYAndZ, result?: Point3d): Point3d { - return Matrix3d.XYZPlusMatrixTimesXYZ(this._origin, this._matrix, point, result); + return Matrix3d.xyzPlusMatrixTimesXYZ(this._origin, this._matrix, point, result); } /** Transform the input point. Return as a new point or in the pre-allocated result (if result is given) */ public multiplyXYZ(x: number, y: number, z: number, result?: Point3d): Point3d { - return Matrix3d.XYZPlusMatrixTimesCoordinates(this._origin, this._matrix, x, y, z, result); + return Matrix3d.xyzPlusMatrixTimesCoordinates(this._origin, this._matrix, x, y, z, result); } /** Multiply a specific row of the transform times xyz. Return the (number). */ public multiplyComponentXYZ(componentIndex: number, x: number, y: number, z: number): number { @@ -252,16 +261,16 @@ export class Transform implements BeJSONFunctions { /** Transform the input homogeneous point. Return as a new point or in the pre-allocated result (if result is given) */ public multiplyXYZW(x: number, y: number, z: number, w: number, result?: Point4d): Point4d { - return Matrix3d.XYZPlusMatrixTimesWeightedCoordinates(this._origin, this._matrix, x, y, z, w, result); + return Matrix3d.xyzPlusMatrixTimesWeightedCoordinates(this._origin, this._matrix, x, y, z, w, result); } /** Transform the input homogeneous point. Return as a new point or in the pre-allocated result (if result is given) */ public multiplyXYZWToFloat64Array(x: number, y: number, z: number, w: number, result?: Float64Array): Float64Array { - return Matrix3d.XYZPlusMatrixTimesWeightedCoordinatesToFloat64Array(this._origin, this._matrix, x, y, z, w, result); + return Matrix3d.xyzPlusMatrixTimesWeightedCoordinatesToFloat64Array(this._origin, this._matrix, x, y, z, w, result); } /** Transform the input homogeneous point. Return as a new point or in the pre-allocated result (if result is given) */ public multiplyXYZToFloat64Array(x: number, y: number, z: number, result?: Float64Array): Float64Array { - return Matrix3d.XYZPlusMatrixTimesCoordinatesToFloat64Array(this._origin, this._matrix, x, y, z, result); + return Matrix3d.xyzPlusMatrixTimesCoordinatesToFloat64Array(this._origin, this._matrix, x, y, z, result); } /** Multiply the tranposed transform (as 4x4 with 0001 row) by Point4d given as xyzw.. Return as a new point or in the pre-allocated result (if result is given) */ public multiplyTransposeXYZW(x: number, y: number, z: number, w: number, result?: Point4d): Point4d { @@ -279,7 +288,7 @@ export class Transform implements BeJSONFunctions { public multiplyPoint3dArrayInPlace(points: Point3d[]) { let point; for (point of points) - Matrix3d.XYZPlusMatrixTimesXYZ(this._origin, this._matrix, point, point); + Matrix3d.xyzPlusMatrixTimesXYZ(this._origin, this._matrix, point, point); } /** @returns Return product of the transform's inverse times a point. */ @@ -362,12 +371,12 @@ export class Transform implements BeJSONFunctions { if (result) { const n = Transform.matchArrayLengths(source, result, Point2d.createZero); for (let i = 0; i < n; i++) - Matrix3d.XYPlusMatrixTimesXY(this._origin, this._matrix, source[i], result[i]); + Matrix3d.xyPlusMatrixTimesXY(this._origin, this._matrix, source[i], result[i]); return result; } result = []; for (const p of source) - result.push(Matrix3d.XYPlusMatrixTimesXY(this._origin, this._matrix, p)); + result.push(Matrix3d.xyPlusMatrixTimesXY(this._origin, this._matrix, p)); return result; } @@ -380,12 +389,12 @@ export class Transform implements BeJSONFunctions { if (result) { const n = Transform.matchArrayLengths(source, result, Point3d.createZero); for (let i = 0; i < n; i++) - Matrix3d.XYZPlusMatrixTimesXYZ(this._origin, this._matrix, source[i], result[i]); + Matrix3d.xyzPlusMatrixTimesXYZ(this._origin, this._matrix, source[i], result[i]); return result; } result = []; for (const p of source) - result.push(Matrix3d.XYZPlusMatrixTimesXYZ(this._origin, this._matrix, p)); + result.push(Matrix3d.xyzPlusMatrixTimesXYZ(this._origin, this._matrix, p)); return result; } @@ -413,7 +422,7 @@ export class Transform implements BeJSONFunctions { public multiplyTransformTransform(other: Transform, result?: Transform) { if (!result) return Transform.createRefs( - Matrix3d.XYZPlusMatrixTimesXYZ(this._origin, this._matrix, other._origin), + Matrix3d.xyzPlusMatrixTimesXYZ(this._origin, this._matrix, other._origin), this._matrix.multiplyMatrixMatrix(other._matrix)); result.setMultiplyTransformTransform(this, other); return result; @@ -426,7 +435,7 @@ export class Transform implements BeJSONFunctions { public setMultiplyTransformTransform(transformA: Transform, transformB: Transform): void { if (Transform._scratchPoint === undefined) Transform._scratchPoint = Point3d.create(); - Matrix3d.XYZPlusMatrixTimesXYZ(transformA._origin, transformA._matrix, transformB._origin, Transform._scratchPoint); + Matrix3d.xyzPlusMatrixTimesXYZ(transformA._origin, transformA._matrix, transformB._origin, Transform._scratchPoint); this._origin.setFrom(Transform._scratchPoint); transformA._matrix.multiplyMatrixMatrix(transformB._matrix, this._matrix); } diff --git a/core/geometry/src/geometry4d/Point4d.ts b/core/geometry/src/geometry4d/Point4d.ts index 4464c02..87c40ca 100644 --- a/core/geometry/src/geometry4d/Point4d.ts +++ b/core/geometry/src/geometry4d/Point4d.ts @@ -150,7 +150,7 @@ export class Point4d implements BeJSONFunctions { public magnitudeXYZW(): number { return Geometry.hypotenuseXYZW(this.xyzw[0], this.xyzw[1], this.xyzw[2], this.xyzw[3]); } - /** @returns Returns the magnitude of the leading xyz components */ + /** @returns Returns the magnitude of the leading xyz components. w is ignored. (i.e. the leading xyz are NOT divided by w.) */ public magnitudeSquaredXYZ(): number { return Geometry.hypotenuseSquaredXYZ(this.xyzw[0], this.xyzw[1], this.xyzw[2]); } @@ -237,7 +237,7 @@ export class Point4d implements BeJSONFunctions { public altitude(point: Point3d): number { return this.xyzw[0] * point.x + this.xyzw[1] * point.y + this.xyzw[2] * point.z + this.xyzw[3]; } - /** dotProduct with (point.x, point.y, point.z, 1) Used in PlaneAltitudeEvaluator interface */ + /** dotProduct with (point.x, point.y, point.z, point.w) Used in PlaneAltitudeEvaluator interface */ public weightedAltitude(point: Point4d): number { return this.xyzw[0] * point.x + this.xyzw[1] * point.y + this.xyzw[2] * point.z + this.xyzw[3] * point.w; } @@ -489,4 +489,14 @@ export class Point4d implements BeJSONFunctions { result = Point4d.createAdd2Scaled(q0, Math.cos(angleOfInterpolant), q2, Math.sin(angleOfInterpolant)); return result; } + // Return the (radians of the) angle from `this` to `other`, considering xyzw (ALL) parts. + public radiansToPoint4dXYZW(other: Point4d): number | undefined { + const magA = this.magnitudeXYZW(); + const magB = other.magnitudeXYZW(); + const dot = this.dotProduct(other); // == cos (theta) * magA * magB + const cos = Geometry.conditionalDivideFraction(dot, magA * magB); + if (cos === undefined) + return undefined; + return Math.acos(cos); + } } diff --git a/core/geometry/src/numerics/ClusterableArray.ts b/core/geometry/src/numerics/ClusterableArray.ts index a74a690..a3b5298 100644 --- a/core/geometry/src/numerics/ClusterableArray.ts +++ b/core/geometry/src/numerics/ClusterableArray.ts @@ -179,7 +179,7 @@ export class ClusterableArray extends GrowableBlockedArray { data[k] = dot; } } - public ToJSON(): any[] { + public toJSON(): any[] { const result: any[] = []; for (let b = 0; b < this.numBlocks; b++) { let i = this.blockIndexToDoubleIndex(b); @@ -197,7 +197,7 @@ export class ClusterableArray extends GrowableBlockedArray { * @returns Return an array of indices from block index to cluster index. * @param clusteredBlocks clusters of block indices followed by separators. */ - public createIndex_blockToClusterIndex(clusteredBlocks: Uint32Array): Uint32Array { + public createIndexBlockToClusterIndex(clusteredBlocks: Uint32Array): Uint32Array { const numBlocks = this.numBlocks; const blockToCluster = new Uint32Array(numBlocks); blockToCluster.fill(ClusterableArray.clusterTerminator); @@ -215,7 +215,7 @@ export class ClusterableArray extends GrowableBlockedArray { * @returns Return an array of indices from block index to index of its cluster's start in the cluster index array. * @param clusteredBlocks clusters of block indices followed by separators. */ - public createIndex_blockToClusterStart(clusteredBlocks: Uint32Array): Uint32Array { + public createIndexBlockToClusterStart(clusteredBlocks: Uint32Array): Uint32Array { const n = clusteredBlocks.length; const numBlocks = this.numBlocks; const blockToClusterStart = new Uint32Array(numBlocks); @@ -245,7 +245,7 @@ export class ClusterableArray extends GrowableBlockedArray { /** create a reverse index: given a cluster index k, clusterToClusterStart[k] is the place * the cluster's block indices appear in clusterBlocks */ - public createIndex_clusterToClusterStart(clusteredBlocks: Uint32Array): Uint32Array { + public createIndexClusterToClusterStart(clusteredBlocks: Uint32Array): Uint32Array { let numCluster = this.countClusters(clusteredBlocks); const clusterToClusterStart = new Uint32Array(numCluster); const terminator = ClusterableArray.clusterTerminator; @@ -332,7 +332,7 @@ export class ClusterableArray extends GrowableBlockedArray { const p = Point3d.create(); const numSourcePoint = source.length; for (let i = 0; i < numSourcePoint; i++) { - source.getPoint3dAt(i, p); + source.getPoint3dAtUncheckedPointIndex(i, p); clusterArray.addDirect(p.x, p.y, p.z); } const order = clusterArray.clusterIndicesLexical(tolerance); diff --git a/core/geometry/src/numerics/ConvexPolygon2d.ts b/core/geometry/src/numerics/ConvexPolygon2d.ts index 889296b..4e778fa 100644 --- a/core/geometry/src/numerics/ConvexPolygon2d.ts +++ b/core/geometry/src/numerics/ConvexPolygon2d.ts @@ -40,11 +40,11 @@ export class Ray2d { return new Ray2d(this._origin.addForwardLeft(0.0, leftFraction, this._direction), this._direction); } - public CCWPerpendicularRay(): Ray2d { + public ccwPerpendicularRay(): Ray2d { return new Ray2d(this._origin, this._direction.rotate90CCWXY()); } - public CWPerpendicularRay(): Ray2d { + public cwPerpendicularRay(): Ray2d { return new Ray2d(this._origin, this._direction.rotate90CWXY()); } diff --git a/core/geometry/src/numerics/Polynomials.ts b/core/geometry/src/numerics/Polynomials.ts index 6bf1a7f..f40a877 100644 --- a/core/geometry/src/numerics/Polynomials.ts +++ b/core/geometry/src/numerics/Polynomials.ts @@ -271,7 +271,7 @@ export class TorusImplicit { * @param xyz space point in local coordinates. * @return object with properties theta, phi, distance, rho */ - public XYZToThetaPhiDistance(xyz: Point3d): { theta: number, phi: number, distance: number, rho: number, safePhi: boolean } { + public xyzToThetaPhiDistance(xyz: Point3d): { theta: number, phi: number, distance: number, rho: number, safePhi: boolean } { const rho = xyz.magnitudeXY(); const majorRadiusFactor = Geometry.conditionalDivideFraction(this.majorRadius, rho); let safeMajor; @@ -343,7 +343,7 @@ export class SphereImplicit { return (wx * wx + wy * wy + wz * wz) - this.radius * this.radius * w * w; } - public XYZToThetaPhiR(xyz: Point3d): { theta: number, phi: number, r: number, valid: boolean } { + public xyzToThetaPhiR(xyz: Point3d): { theta: number, phi: number, r: number, valid: boolean } { const rhoSquared = xyz.x * xyz.x + xyz.y * xyz.y; const rho = Math.sqrt(rhoSquared); const r = Math.sqrt(rhoSquared + xyz.z * xyz.z); @@ -443,7 +443,7 @@ export class AnalyticRoots { public static readonly s_quadricRelTol = 1.0e-14; public static readonly sTestWindow = 1.0e-6; /** Absolute zero test with a tolerance that has worked well for the analytic root use case . . . */ - public static IsZero(x: number): boolean { + public static isZero(x: number): boolean { return Math.abs(x) < this.EQN_EPS; } /** Without actually doing a division, test if (x/y) is small. @@ -471,7 +471,7 @@ export class AnalyticRoots { * @param defaultValue value to save if denominator is too small to divide. * @param offset index of value to replace. */ - public static SafeDivide(values: Float64Array, numerator: number, denominator: number, defaultValue: number = 0.0, offset: number): boolean { + public static safeDivide(values: Float64Array, numerator: number, denominator: number, defaultValue: number = 0.0, offset: number): boolean { if (Math.abs(denominator) > (this.s_safeDivideFactor * Math.abs(numerator))) { values[offset] = numerator / denominator; return true; @@ -482,14 +482,14 @@ export class AnalyticRoots { // Used in NewtonMethod for testing if a root has been adjusted past its bounding region private static checkRootProximity(roots: GrowableFloat64Array, i: number): boolean { if (i === 0) { // Case 1: Beginning Root (check root following it) - return roots.at(i) < roots.at(i + 1); + return roots.atUncheckedIndex(i) < roots.atUncheckedIndex(i + 1); } else if (i > 0 && i + 1 < roots.length) { // Case 2: Middle Root (check roots before and after) - return (roots.at(i) > roots.at(i - 1)) && (roots.at(i) < roots.at(i + 1)); + return (roots.atUncheckedIndex(i) > roots.atUncheckedIndex(i - 1)) && (roots.atUncheckedIndex(i) < roots.atUncheckedIndex(i + 1)); } else { // Case 3: End root (check preceding root) - return (roots.at(i) > roots.at(i - 1)); + return (roots.atUncheckedIndex(i) > roots.atUncheckedIndex(i - 1)); } } - private static NewtonMethodAdjustment(coffs: Float64Array | number[], root: number, order: number) { + private static newtonMethodAdjustment(coffs: Float64Array | number[], root: number, order: number) { if (order === 3) { const f = coffs[0] + root * (coffs[1] + root * (coffs[2] + root * coffs[3])); const df = coffs[1] + root * (2.0 * coffs[2] + root * 3.0 * coffs[3]); @@ -507,23 +507,23 @@ export class AnalyticRoots { // Loop through each root for (let i = 0; i < roots.length; i++) { - let dx = this.NewtonMethodAdjustment(coffs, roots.at(i), degree); + let dx = this.newtonMethodAdjustment(coffs, roots.atUncheckedIndex(i), degree); if (!dx) continue; // skip if newton step had divide by zero. - const originalValue = roots.at(i); + const originalValue = roots.atUncheckedIndex(i); let counter = 0; let convergenceCounter = 0; // Loop through applying changes to found root until dx is diminished or counter is hit while (dx !== 0 && (counter < 10)) { // consider it converged if two successive iterations satisfy the (not too demanding) tolerance. - if (Math.abs(dx) < relTol * (1.0 + Math.abs(roots.at(i)))) { + if (Math.abs(dx) < relTol * (1.0 + Math.abs(roots.atUncheckedIndex(i)))) { if (++convergenceCounter > 1) break; } else { convergenceCounter = 0; } - const rootDX = roots.at(i) - dx; + const rootDX = roots.atUncheckedIndex(i) - dx; roots.reassign(i, rootDX); // If root is thrown past one of its neighboring roots, unstable condition is assumed.. revert @@ -533,7 +533,7 @@ export class AnalyticRoots { break; } - dx = this.NewtonMethodAdjustment(coffs, roots.at(i), degree); + dx = this.newtonMethodAdjustment(coffs, roots.atUncheckedIndex(i), degree); counter++; } } @@ -572,15 +572,15 @@ export class AnalyticRoots { public static mostDistantFromMean(data: GrowableFloat64Array | undefined): number { if (!data || data.length === 0) return 0; let a = 0.0; // to become the sum and finally the average. - for (let i = 0; i < data.length; i++) a += data.at(i); + for (let i = 0; i < data.length; i++) a += data.atUncheckedIndex(i); a /= data.length; let dMax = 0.0; - let result = data.at(0); + let result = data.atUncheckedIndex(0); for (let i = 0; i < data.length; i++) { - const d = Math.abs(data.at(i) - a); + const d = Math.abs(data.atUncheckedIndex(i) - a); if (d > dMax) { dMax = d; - result = data.at(i); + result = data.atUncheckedIndex(i); } } return result; @@ -604,7 +604,7 @@ export class AnalyticRoots { const D = p * p - q; - if (this.IsZero(D)) { + if (this.isZero(D)) { this.appendSolution(-p, values); return; } else if (D < 0) { @@ -617,7 +617,7 @@ export class AnalyticRoots { return; } private static addConstant(value: number, data: GrowableFloat64Array) { - for (let i = 0; i < data.length; i++) data.reassign(i, data.at(i) + value); + for (let i = 0; i < data.length; i++) data.reassign(i, data.atUncheckedIndex(i) + value); } /** return roots of a cubic c0 + c1 *x + c2 * x^2 + c2 * x3. * In the usual case where c0 is non-zero, there are either 1 or 3 roots. @@ -660,8 +660,8 @@ export class AnalyticRoots { cb_p = p * p * p; D = q * q + cb_p; const origin = A / (-3.0); - if (D >= 0.0 && this.IsZero(D)) { - if (this.IsZero(q)) { + if (D >= 0.0 && this.isZero(D)) { + if (this.isZero(q)) { // One triple solution results.push(origin); results.push(origin); @@ -721,7 +721,7 @@ export class AnalyticRoots { // normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 const coffScale = new Float64Array(1); - if (!this.SafeDivide(coffScale, 1.0, c[4], 0.0, 0)) { + if (!this.safeDivide(coffScale, 1.0, c[4], 0.0, 0)) { this.appendCubicRoots(c, results); return; } @@ -739,7 +739,7 @@ export class AnalyticRoots { const tempStack = new GrowableFloat64Array(); - if (this.IsZero(r)) { + if (this.isZero(r)) { // no absolute term: y(y^3 + py + q) = 0 coeffs[0] = q; @@ -779,7 +779,7 @@ export class AnalyticRoots { v = Math.sqrt(v); } else { for (let i = 0; i < tempStack.length; i++) { - results.push(tempStack.at(i)); + results.push(tempStack.atUncheckedIndex(i)); } return; } @@ -931,7 +931,7 @@ export class PowerPolynomial { return p; } // Evaluate a standard basis polynomial - public static Evaluate(coff: Float64Array, x: number): number { + public static evaluate(coff: Float64Array, x: number): number { const degree = coff.length - 1; return this.degreeKnownEvaluate(coff, degree, x); } @@ -939,7 +939,7 @@ export class PowerPolynomial { // Accumulate Q*scale into P. Both are treated as full degree. // (Expect Address exceptions if P is smaller than Q) // Returns degree of result as determined by comparing leading coefficients to zero - public static Accumulate(coffP: Float64Array, coffQ: Float64Array, scaleQ: number): number { + public static accumulate(coffP: Float64Array, coffQ: Float64Array, scaleQ: number): number { let degreeP = coffP.length - 1; const degreeQ = coffQ.length - 1; @@ -953,7 +953,7 @@ export class PowerPolynomial { return degreeP; } // Zero all coefficients in a polynomial - public static Zero(coff: Float64Array) { + public static zero(coff: Float64Array) { for (let i = 0; i < coff.length; i++) { coff[i] = 0.0; } @@ -1001,7 +1001,7 @@ export class TrigPolynomial { // ------------------------------------------------------------------------------------------------ // Solve a standard basis polynomial. Immediately use the roots as ordinates // in rational polynomials for sine and cosine, and convert to angle via arctan - public static SolveAngles(coff: Float64Array, nominalDegree: number, referenceCoefficient: number, + public static solveAngles(coff: Float64Array, nominalDegree: number, referenceCoefficient: number, radians: number[]): boolean { let maxCoff = Math.abs(referenceCoefficient); let a; @@ -1048,8 +1048,8 @@ export class TrigPolynomial { // Math.Cos(theta)=C(t)/W(t), ,sin(theta)=S(t)/W(t) // Division by W has no effect on Atan2 calculations, so we just compute S(t),C(t) for (let i = 0; i < roots.length; i++) { - const ss = PowerPolynomial.Evaluate(this.S, roots.at(i)); - const cc = PowerPolynomial.Evaluate(this.C, roots.at(i)); + const ss = PowerPolynomial.evaluate(this.S, roots.atUncheckedIndex(i)); + const cc = PowerPolynomial.evaluate(this.C, roots.atUncheckedIndex(i)); radians.push(Math.atan2(ss, cc)); } @@ -1075,23 +1075,23 @@ export class TrigPolynomial { /// Constant coefficient /// solution angles /// number of solution angles (Passed as array to make changes to reference) - public static SolveUnitCircleImplicitQuadricIntersection(axx: number, axy: number, ayy: number, + public static solveUnitCircleImplicitQuadricIntersection(axx: number, axy: number, ayy: number, ax: number, ay: number, a1: number, radians: number[]): boolean { const Coffs = new Float64Array(5); - PowerPolynomial.Zero(Coffs); + PowerPolynomial.zero(Coffs); let degree = 2; if (Math.hypot(axx, axy, ayy) > TrigPolynomial.coeffientRelTol * Math.hypot(ax, ay, a1)) { - PowerPolynomial.Accumulate(Coffs, this.CW, ax); - PowerPolynomial.Accumulate(Coffs, this.SW, ay); - PowerPolynomial.Accumulate(Coffs, this.WW, a1); - PowerPolynomial.Accumulate(Coffs, this.SS, ayy); - PowerPolynomial.Accumulate(Coffs, this.CC, axx); - PowerPolynomial.Accumulate(Coffs, this.SC, axy); + PowerPolynomial.accumulate(Coffs, this.CW, ax); + PowerPolynomial.accumulate(Coffs, this.SW, ay); + PowerPolynomial.accumulate(Coffs, this.WW, a1); + PowerPolynomial.accumulate(Coffs, this.SS, ayy); + PowerPolynomial.accumulate(Coffs, this.CC, axx); + PowerPolynomial.accumulate(Coffs, this.SC, axy); degree = 4; } else { - PowerPolynomial.Accumulate(Coffs, this.C, ax); - PowerPolynomial.Accumulate(Coffs, this.S, ay); - PowerPolynomial.Accumulate(Coffs, this.W, a1); + PowerPolynomial.accumulate(Coffs, this.C, ax); + PowerPolynomial.accumulate(Coffs, this.S, ay); + PowerPolynomial.accumulate(Coffs, this.W, a1); degree = 2; } @@ -1104,7 +1104,7 @@ export class TrigPolynomial { Math.abs(ay), Math.abs(a1)); - const b = this.SolveAngles(Coffs, degree, maxCoff, radians); + const b = this.solveAngles(Coffs, degree, maxCoff, radians); /* for (const theta of angles) { const c = theta.cos(); @@ -1128,7 +1128,7 @@ export class TrigPolynomial { /// solution angles in ellipse parameter space /// solution angles in circle parameter space /// number of solution angles (passed as an array to change reference) - public static SolveUnitCircleEllipseIntersection(cx: number, cy: number, ux: number, uy: number, + public static solveUnitCircleEllipseIntersection(cx: number, cy: number, ux: number, uy: number, vx: number, vy: number, ellipseRadians: number[], circleRadians: number[]): boolean { circleRadians.length = 0; const acc = ux * ux + uy * uy; @@ -1137,7 +1137,7 @@ export class TrigPolynomial { const ac = 2.0 * (ux * cx + uy * cy); const asi = 2.0 * (vx * cx + vy * cy); const a = cx * cx + cy * cy - 1.0; - const boolstat = this.SolveUnitCircleImplicitQuadricIntersection(acc, acs, ass, ac, asi, a, ellipseRadians); + const boolstat = this.solveUnitCircleImplicitQuadricIntersection(acc, acs, ass, ac, asi, a, ellipseRadians); for (const radians of ellipseRadians) { const cc = Math.cos(radians); const ss = Math.sin(radians); @@ -1163,7 +1163,7 @@ export class TrigPolynomial { /// solution angles in ellipse parameter space /// solution angles in circle parameter space /// number of solution angles (passed as an array to change reference) - public static SolveUnitCircleHomogeneousEllipseIntersection(cx: number, cy: number, cw: number, + public static solveUnitCircleHomogeneousEllipseIntersection(cx: number, cy: number, cw: number, ux: number, uy: number, uw: number, vx: number, vy: number, vw: number, ellipseRadians: number[], circleRadians: number[]): boolean { @@ -1174,7 +1174,7 @@ export class TrigPolynomial { const ac = 2.0 * (ux * cx + uy * cy - uw * cw); const asi = 2.0 * (vx * cx + vy * cy - vw * cw); const a = cx * cx + cy * cy - cw * cw; - const boolstat = this.SolveUnitCircleImplicitQuadricIntersection(acc, acs, ass, ac, asi, a, ellipseRadians); + const boolstat = this.solveUnitCircleImplicitQuadricIntersection(acc, acs, ass, ac, asi, a, ellipseRadians); for (const radians of ellipseRadians) { const cc = Math.cos(radians); const ss = Math.sin(radians); diff --git a/core/geometry/src/numerics/TriDiagonalSystem.ts b/core/geometry/src/numerics/TriDiagonalSystem.ts index 786424b..9811ed0 100644 --- a/core/geometry/src/numerics/TriDiagonalSystem.ts +++ b/core/geometry/src/numerics/TriDiagonalSystem.ts @@ -36,11 +36,11 @@ export class TriDiagonalSystem { this._aRight = new Float64Array(n); this._b = new Float64Array(n); this._x = new Float64Array(n); - this.Reset(); + this.reset(); } // Reset to RawMatrix state with all coefficients zero - public Reset() { + public reset() { this._dataState = DataState.RawMatrix; const n = this._aDiag.length; for (let i = 0; i < n; i++) { @@ -48,43 +48,43 @@ export class TriDiagonalSystem { } } // Install data in a row of the matrix - public SetRow(row: number, left: number, diag: number, right: number) { + public setRow(row: number, left: number, diag: number, right: number) { this._aLeft[row] = left; this._aDiag[row] = diag; this._aRight[row] = right; } // Add to row of matrix - public AddToRow(row: number, left: number, diag: number, right: number) { + public addToRow(row: number, left: number, diag: number, right: number) { this._aLeft[row] += left; this._aDiag[row] += diag; this._aRight[row] += right; } // Install data in the right side (B) vector - public SetB(row: number, bb: number) { + public setB(row: number, bb: number) { this._b[row] = bb; } // Add to an entry in the right side (B) vector - public AddToB(row: number, bb: number) { + public addToB(row: number, bb: number) { this._b[row] += bb; } // Access data from the right side (B) vector - public GetB(row: number): number { + public getB(row: number): number { return this._b[row]; } // Install data in the solution (X) vector - public SetX(row: number, xx: number) { + public setX(row: number, xx: number) { this._x[row] = xx; } // Access data frin the solution (X) vector - public GetX(row: number): number { + public getX(row: number): number { return this._x[row]; } // Get method for matrix and vector order - public Order(): number { + public order(): number { return this._aDiag.length; } // Compute product of AX and save as B - public MultiplyAX(): boolean { + public multiplyAX(): boolean { if (this._dataState === DataState.FactorFailed) { return false; } else if (this._dataState === DataState.FactorOK) { @@ -114,7 +114,7 @@ export class TriDiagonalSystem { } // Compute product of AX and save as B - public MultiplyAXPoints(pointX: Point3d[], pointB: Point3d[]): boolean { + public multiplyAXPoints(pointX: Point3d[], pointB: Point3d[]): boolean { pointB.length = 0; while (pointB.length < pointX.length) pointB.push(Point3d.create()); @@ -153,7 +153,7 @@ export class TriDiagonalSystem { } // Multiply the stored factors together to return to plain matrix form - public Defactor(): boolean { + public defactor(): boolean { if (this._dataState === DataState.RawMatrix) { return true; } @@ -171,7 +171,7 @@ export class TriDiagonalSystem { return true; } // Factor the tridiagonal matrix to LU parts. b, x, not altered - public Factor(): boolean { + public factor(): boolean { if (this._dataState === DataState.FactorOK) { return true; } @@ -193,10 +193,10 @@ export class TriDiagonalSystem { return true; } // Solve AX=B. A is left in factored state. B unchanged. - public FactorAndBackSubstitute(): boolean { + public factorAndBackSubstitute(): boolean { const n = this._aDiag.length; const n1 = n - 1; - if (!this.Factor()) + if (!this.factor()) return false; // Apply Linv to B, same sequence as was done to A: @@ -219,7 +219,7 @@ export class TriDiagonalSystem { return true; } // Solve AX=B. A is left in factored state. B unchanged. vectorB and vectorX may be the same array - public FactorAndBackSubstitutePointArrays(vectorB: Point3d[], vectorX: Point3d[]): boolean { + public factorAndBackSubstitutePointArrays(vectorB: Point3d[], vectorX: Point3d[]): boolean { const n = this._aDiag.length; if (vectorB.length < n) return false; @@ -228,7 +228,7 @@ export class TriDiagonalSystem { vectorX.push(Point3d.create(0, 0, 0)); vectorX.length = n; const n1 = n - 1; - if (!this.Factor()) + if (!this.factor()) return false; // Apply Linv to B, same sequence as was done to A: @@ -266,7 +266,7 @@ export class TriDiagonalSystem { return true; } // Allocate a complete copy - public Copy(): TriDiagonalSystem { + public copy(): TriDiagonalSystem { const n = this._aDiag.length; const B = new TriDiagonalSystem(n); for (let i = 0; i < n; i++) { diff --git a/core/geometry/src/polyface/FacetFaceData.ts b/core/geometry/src/polyface/FacetFaceData.ts index ef17a39..22620e9 100644 --- a/core/geometry/src/polyface/FacetFaceData.ts +++ b/core/geometry/src/polyface/FacetFaceData.ts @@ -2,7 +2,7 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Point2d } from "../geometry3d/Point2dVector2d"; +import { Point2d, Vector2d } from "../geometry3d/Point2dVector2d"; import { Point3d } from "../geometry3d/Point3dVector3d"; import { Range2d } from "../geometry3d/Range"; import { IndexedPolyface, IndexedPolyfaceVisitor } from "./Polyface"; @@ -39,23 +39,31 @@ export class FacetFaceData { this._paramRange.setNull(); } /** Return distance-based parameter from stored parameter value. */ - public convertParamToDistance(param: Point2d, result?: Point2d): Point2d { + public convertParamXYToDistance(x: number, y: number, result?: Point2d): Point2d { result = result ? result : Point2d.create(); const paramDelta = this._paramRange.high.minus(this._paramRange.low); - result.x = (0 === paramDelta.x) ? param.x : (this._paramDistanceRange.low.x + (param.x - this._paramRange.low.x) + result.x = (0 === paramDelta.x) ? x : (this._paramDistanceRange.low.x + (x - this._paramRange.low.x) * (this._paramDistanceRange.high.x - this._paramDistanceRange.low.x) / paramDelta.x); - result.y = (0.0 === paramDelta.y) ? param.y : (this.paramDistanceRange.low.y + (param.y - this._paramRange.low.y) + result.y = (0.0 === paramDelta.y) ? y : (this.paramDistanceRange.low.y + (y - this._paramRange.low.y) * (this._paramDistanceRange.high.y - this._paramDistanceRange.low.y) / paramDelta.y); return result; } /** Return normalized (0-1) parameter from stored parameter value. */ - public convertParamToNormalized(param: Point2d, result?: Point2d): Point2d { + public convertParamXYToNormalized(x: number, y: number, result?: Point2d): Point2d { result = result ? result : Point2d.create(); const paramDelta = this._paramRange.high.minus(this._paramRange.low); - result.x = (0.0 === paramDelta.x) ? param.x : ((param.x - this._paramRange.low.x) / paramDelta.x); - result.y = (0.0 === paramDelta.y) ? param.y : ((param.y - this._paramRange.low.y) / paramDelta.y); + result.x = (0.0 === paramDelta.x) ? x : ((x - this._paramRange.low.x) / paramDelta.x); + result.y = (0.0 === paramDelta.y) ? y : ((y - this._paramRange.low.y) / paramDelta.y); return result; } + /** Return distance-based parameter from stored parameter value. */ + public convertParamToDistance(param: Point2d, result?: Point2d): Point2d { + return this.convertParamXYToDistance(param.x, param.y, result); + } + /** Return normalized (0-1) parameter from stored parameter value. */ + public convertParamToNormalized(param: Point2d, result?: Point2d): Point2d { + return this.convertParamXYToNormalized(param.x, param.y, result); + } /** Scale distance paramaters. */ public scaleDistances(distanceScale: number) { this._paramDistanceRange.low.x *= distanceScale; @@ -84,15 +92,17 @@ export class FacetFaceData { const triangleParamIndexes: number[] = []; if (!visitorParams) return false; + visitorParams.extendRange(this._paramRange); + const dUV0 = Vector2d.create(); + const dUV1 = Vector2d.create(); for (let k = 0; k < numPointsInFacet; k++) { - this._paramRange.extendPoint(visitorParams[k]); trianglePointIndexes[2] = k; triangleParamIndexes[2] = k; if (k > 1) { - const dUV0 = visitorParams[triangleParamIndexes[0]].minus(visitorParams[triangleParamIndexes[1]]); - const dUV1 = visitorParams[triangleParamIndexes[1]].minus(visitorParams[triangleParamIndexes[2]]); - const delta0 = visitorPoints.getPoint3dAt(trianglePointIndexes[0]).minus(visitorPoints.getPoint3dAt(trianglePointIndexes[1])); - const delta1 = visitorPoints.getPoint3dAt(trianglePointIndexes[1]).minus(visitorPoints.getPoint3dAt(trianglePointIndexes[2])); + visitorParams.vectorIndexIndex(triangleParamIndexes[1], triangleParamIndexes[0], dUV0); + visitorParams.vectorIndexIndex(triangleParamIndexes[1], triangleParamIndexes[2], dUV1); + const delta0 = visitorPoints.getPoint3dAtUncheckedPointIndex(trianglePointIndexes[0]).minus(visitorPoints.getPoint3dAtUncheckedPointIndex(trianglePointIndexes[1])); + const delta1 = visitorPoints.getPoint3dAtUncheckedPointIndex(trianglePointIndexes[1]).minus(visitorPoints.getPoint3dAtUncheckedPointIndex(trianglePointIndexes[2])); const uvCross = Math.abs(dUV0.x * dUV1.y - dUV1.x * dUV0.y); if (uvCross) { const dwDu = Point3d.createFrom(delta0); diff --git a/core/geometry/src/polyface/IndexedEdgeMatcher.ts b/core/geometry/src/polyface/IndexedEdgeMatcher.ts new file mode 100644 index 0000000..a37c295 --- /dev/null +++ b/core/geometry/src/polyface/IndexedEdgeMatcher.ts @@ -0,0 +1,175 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +/** @module Polyface */ + +// For boundary sorting, an edge exists as a (packed!) Float64Array. +// Fixed entries are: +// 0: +/** + * * For boundary sorting, an edge is a (packed!) Float64Array. + * * Fixed entry positions are: + * * [0] is start vertex index (in CCW order around its facet) + * * [1] is end vertex index (in CCW order around its facet) + * * [2] is facet index. + */ +export class SortableEdge extends Float64Array { + /** Return the vertex index that appears first in the order stored. */ + public get vertexIndexA(): number { return this[0]; } + /** Return the vertex index that appears seccond in the order stored. */ + public get vertexIndexB(): number { return this[1]; } + /** Return the facet index. */ + public get facetIndex(): number { return this[2]; } + /** return true if vertexIndexA is less than vertexIndexB */ + public get islowHigh(): boolean { return this[0] < this[1]; } + /** Return the vertex index with lower numeric value */ + public get lowVertexIndex(): number { return this[0] < this[1] ? this[0] : this[1]; } + /** Return the vertex index with higher numeric value */ + public get highVertexIndex(): number { return this[0] > this[1] ? this[0] : this[1]; } + /** Return true if the vertices edgeA and edgeB are the same vertex indices in opposite order */ + public static areDirectedPartners(edgeA: SortableEdge, edgeB: SortableEdge): boolean { return edgeA[0] === edgeB[1] && edgeA[1] === edgeB[0]; } + /** Return true if the vertices edgeA and edgeB are the same vertex indices with no consideration of order */ + public static areUndirectedPartners(edgeA: SortableEdge, edgeB: SortableEdge): boolean { + return (edgeA[0] === edgeB[0] && edgeA[1] === edgeB[1]) || ((edgeA[0] === edgeB[1] && edgeA[1] === edgeB[0])); + } + public get isNullEdge(): boolean { return this[0] === this[1]; } + /** + * lexical comparison of two edges. + * * If the edges have the same vertex pair (in same or opposite order) they will end up adjacent in a sort + * * If the edges have 0 or 1 shared vertex indices, the one with lowest low comes first. + * @param edgeA first edge + * @param edgeB second edge + */ + public static lessThan(edgeA: SortableEdge, edgeB: SortableEdge): number { + // primary compare is based on undirect indices + const lowA = edgeA.lowVertexIndex; + const lowB = edgeB.lowVertexIndex; + if (lowA < lowB) + return -1; + if (lowB < lowA) + return 1; + const highA = edgeA.highVertexIndex; + const highB = edgeB.highVertexIndex; + if (highA < highB) + return -1; + if (highB < highA) + return 1; + // undirected indices match ... use directed vertexIndexA + return edgeA.vertexIndexA - edgeB.vertexIndexA; + } + public constructor(vertexA: number, vertexB: number, facetIndex: number) { + super(3); + this[0] = vertexA; + this[1] = vertexB; + this[2] = facetIndex; + } + public toJSON(): any { return [this[0], this[1], this[2]]; } + public static clusterToJSON(data: SortableEdgeCluster): any { + if (data instanceof SortableEdge) + return data.toJSON(); + + const result = []; + for (const edge of data) result.push(edge.toJSON()); + } + public static clusterArrayToJSON(data: SortableEdgeCluster[]) { + const result = []; + for (const cluster of data) + result.push(SortableEdge.clusterToJSON(cluster)); + return result; + } +} + +export type SortableEdgeCluster = SortableEdge | SortableEdge[]; +/** + * An IndexedEdgeMatcher carres an array (`edges`) of edges start & end indices for sorting and subsequent analyses (such as testing for closed mesh) + */ +export class IndexedEdgeMatcher { + public edges: SortableEdge[]; + + constructor() { + this.edges = []; + } + /** + * push a new edge. + * @returns the edge (as emplaced at the back of the sortableEdge array) + * @param vertexA start vertex + * @param vertexB end vertex + * @param facetIndex facet index + */ + public addEdge(vertexA: number, vertexB: number, facetIndex: number): SortableEdge { + const edge = new SortableEdge(vertexA, vertexB, facetIndex); + this.edges.push(edge); + return edge; + } + /** + * Push edges all around a facet, returning to vertexArray[0] + * @param vertexArray array of vertex indices around facet + * @param facetIndex + */ + public addPath(vertexArray: number[], facetIndex: number, closeLoop: boolean = true) { + if (vertexArray.length === 0) return; + const m = vertexArray.length - 1; + for (let i = 0; i < m; i++) { + this.addEdge(vertexArray[i], vertexArray[i + 1], facetIndex); + } + if (closeLoop) + this.addEdge(vertexArray[m], vertexArray[0], facetIndex); + } + /** Sort the edge index array. */ + public sort() { + this.edges.sort(SortableEdge.lessThan); + } + /** Create a single or compound SortableEdgeCluster in dest. */ + private collectSortableEdgeCluster(index0: number, index1: number, dest: SortableEdgeCluster[] | undefined) { + if (dest !== undefined && index1 > index0) { + if (index1 === index0 + 1) { + dest.push(this.edges[index0]); + } else { + const cluster = []; + for (let i = index0; i < index1; i++) + cluster.push(this.edges[i]); + dest.push(cluster); + } + + } + } + /** + * sort the edges, and look for three categories of paired edges: + * * caller must allocate all result arrays of interest. + * * Any combination of the result arrays may be `undefined`, indicating that category is to be ignored. + * * Any combination of the result arrays may be aliased as the same target, in which case those to categories are merged into the target. + * * For instance, to ignore manifold pairs and collect all others (singleton and other) as a single array `allOther`, create `const allOther = []` as an empty array and call + * `sortAndCollectClusters (undefined, allOther, allOther);` + * @param manifoldPairs optional array to receive pairs of properly mated SortableEdgePairs, i.e. simple interior edges adjacent to two facets in opposing directions. + * @param singletons optional array to receive eges that are simple bounary edges. + * @param nullEdges clusters with null edges (same start and end vertex) + * @param allOtherClusters optional array to receive arrays in which all the edges are partners in an undirected sense but not a simple directed pair. + */ + public sortAndcollectClusters(manifoldPairs: SortableEdgeCluster[] | undefined, singletons: SortableEdgeCluster[] | undefined, nullEdges: SortableEdgeCluster[] | undefined, allOtherClusters: SortableEdgeCluster[] | undefined) { + this.sort(); + if (manifoldPairs) manifoldPairs.length = 0; + if (singletons) singletons.length = 0; + if (nullEdges) nullEdges.length = 0; + if (allOtherClusters) allOtherClusters.length = 0; + const n = this.edges.length; + let clusterLength; + for (let index0 = 0; index0 < n; index0 += clusterLength) { + const baseEdge = this.edges[index0]; + clusterLength = 1; + for (let index1 = index0 + 1; index1 < n && SortableEdge.areUndirectedPartners(baseEdge, this.edges[index1]); index1++) { + clusterLength++; + } + if (this.edges[index0].isNullEdge) { + this.collectSortableEdgeCluster(index0, index0 + clusterLength, nullEdges); + } else if (clusterLength === 2 && SortableEdge.areDirectedPartners(baseEdge, this.edges[index0 + 1])) { + this.collectSortableEdgeCluster(index0, index0 + 1, manifoldPairs); + } else if (clusterLength === 1) { + this.collectSortableEdgeCluster(index0, index0 + 1, singletons); + } else { + this.collectSortableEdgeCluster(index0, index0 + clusterLength, allOtherClusters); + } + } + } +} diff --git a/core/geometry/src/polyface/Polyface.ts b/core/geometry/src/polyface/Polyface.ts index a1954f4..f1c5caf 100644 --- a/core/geometry/src/polyface/Polyface.ts +++ b/core/geometry/src/polyface/Polyface.ts @@ -19,6 +19,8 @@ import { GeometryQuery } from "../curve/GeometryQuery"; import { GeometryHandler } from "../geometry3d/GeometryHandler"; import { PolyfaceData } from "./PolyfaceData"; import { FacetFaceData } from "./FacetFaceData"; +import { Geometry } from "../Geometry"; +import { GrowableXYArray } from "../geometry3d/GrowableXYArray"; function allDefined(valueA: any, valueB: any, valueC: any): boolean { return valueA !== undefined && valueB !== undefined && valueC !== undefined; @@ -65,7 +67,10 @@ export abstract class Polyface extends GeometryQuery { return false; return true; } - + /** + * @returns true if this polyface has no contents. + */ + public abstract get isEmpty(): boolean; } export class IndexedPolyface extends Polyface { public isSameGeometryClass(other: any): boolean { return other instanceof IndexedPolyface; } @@ -77,6 +82,10 @@ export class IndexedPolyface extends Polyface { } return false; } + /** + * @returns true if either the point array or the point index array is empty. + */ + public get isEmpty(): boolean { return this.data.pointCount === 0 || this.data.pointIndex.length === 0; } /** * * apply the transform to points * * apply the (inverse transpose of) the matrix part to normals @@ -164,7 +173,7 @@ export class IndexedPolyface extends Polyface { const sourcePoints = source.data.point; const xyz = Point3d.create(); for (let i = 0, n = source.data.point.length; i < n; i++) { - sourcePoints.getPoint3dAt(i, xyz); + sourcePoints.getPoint3dAtUncheckedPointIndex(i, xyz); if (transform) { transform.multiplyPoint3d(xyz, xyz); sourceToDestPointIndex.push(this.addPoint(xyz)); @@ -179,11 +188,11 @@ export class IndexedPolyface extends Polyface { const i1 = source._facetStart[i + 1]; if (reversed) { for (let j = i1; j-- > i0;) { - this.addPointIndex(sourceToDestPointIndex.at(source.data.pointIndex[j]), source.data.edgeVisible[j]); + this.addPointIndex(sourceToDestPointIndex.atUncheckedIndex(source.data.pointIndex[j]), source.data.edgeVisible[j]); } } else { for (let j = i0; j < i1; j++) { - this.addPointIndex(sourceToDestPointIndex.at(source.data.pointIndex[j]), source.data.edgeVisible[j]); + this.addPointIndex(sourceToDestPointIndex.atUncheckedIndex(source.data.pointIndex[j]), source.data.edgeVisible[j]); } } this.terminateFacet(false); @@ -191,17 +200,16 @@ export class IndexedPolyface extends Polyface { // Add param and param index data if (copyParams) { - const startOfNewParams = this.data.param!.length; - for (const param of source.data.param!) { - const sourceParam = param.clone(); - this.addParam(sourceParam); - } + const myParams = this.data.param!; + + const startOfNewParams = myParams.length; + myParams.pushFromGrowableXYArray(source.data.param!); for (let i = 0; i < source._facetStart.length; i++) { // Expect facet start and ends for points to match normals const i0 = source._facetStart[i]; const i1 = source._facetStart[i + 1]; if (reversed) { for (let j = i1; j-- > i0;) - this.addParamIndex(startOfNewParams + source.data.paramIndex![j - 1]); + this.addParamIndex(startOfNewParams + source.data.paramIndex![j]); } else { for (let j = i0; j < i1; j++) this.addParamIndex(startOfNewParams + source.data.paramIndex![j]); @@ -214,7 +222,7 @@ export class IndexedPolyface extends Polyface { const startOfNewNormals = this.data.normal!.length; const numNewNOrmals = source.data.normal.length; for (let i = 0; i < numNewNOrmals; i++) { - const sourceNormal = source.data.normal.atVector3dIndex(i)!; + const sourceNormal = source.data.normal.getVector3dAtCheckedVectorIndex(i)!; if (transform) { transform.multiplyVector(sourceNormal, sourceNormal); this.addNormal(sourceNormal); @@ -227,7 +235,7 @@ export class IndexedPolyface extends Polyface { const i1 = source._facetStart[i + 1]; if (reversed) { for (let j = i1; j-- > i0;) - this.addNormalIndex(startOfNewNormals + source.data.normalIndex![j - 1]); + this.addNormalIndex(startOfNewNormals + source.data.normalIndex![j]); } else { for (let j = i0; j < i1; j++) this.addNormalIndex(startOfNewNormals + source.data.normalIndex![j]); @@ -279,32 +287,65 @@ export class IndexedPolyface extends Polyface { return new IndexedPolyface(new PolyfaceData(needNormals, needParams, needColors)); } /** add (a clone of ) a point. return its 0 based index. + * @param point point coordinates + * @param priorIndex optional index of prior point to check for repeated coordinates * @returns Returns the zero-based index of the added point. */ - public addPoint(point: Point3d): number { this.data.point.pushXYZ(point.x, point.y, point.z); return this.data.point.length - 1; } + public addPoint(point: Point3d, priorIndex?: number): number { + if (priorIndex !== undefined) { + const distance = this.data.point.distanceIndexToPoint(priorIndex, point); + if (distance !== undefined && Geometry.isSmallMetricDistance(distance)) + return priorIndex; + } + this.data.point.pushXYZ(point.x, point.y, point.z); + return this.data.point.length - 1; + } /** add a point. * @returns Returns the zero-based index of the added point. */ public addPointXYZ(x: number, y: number, z: number): number { this.data.point.push(Point3d.create(x, y, z)); return this.data.point.length - 1; } public addParam(param: Point2d): number { - if (!this.data.param) this.data.param = []; - this.data.param.push(param.clone()); + if (!this.data.param) this.data.param = new GrowableXYArray(); + this.data.param.push(param); return this.data.param.length - 1; } - public addParamUV(u: number, v: number): number { - if (!this.data.param) this.data.param = []; + public addParamUV(u: number, v: number, priorIndexA?: number, priorIndexB?: number): number { + if (!this.data.param) this.data.param = new GrowableXYArray(); + if (priorIndexA !== undefined && this.data.isAlmostEqualParamIndexUV(priorIndexA, u, v)) + return priorIndexA; + if (priorIndexB !== undefined && this.data.isAlmostEqualParamIndexUV(priorIndexB, u, v)) + return priorIndexB; this.data.param.push(Point2d.create(u, v)); return this.data.param.length - 1; } public addParamXY(x: number, y: number): number { - if (!this.data.param) this.data.param = []; + if (!this.data.param) this.data.param = new GrowableXYArray(); this.data.param.push(Point2d.create(x, y)); return this.data.param.length - 1; } - public addNormal(normal: Vector3d): number { + public addNormal(normal: Vector3d, priorIndexA?: number, priorIndexB?: number): number { + if (this.data.normal !== undefined) { + let distance; + + if (priorIndexA !== undefined) { + distance = this.data.normal.distanceIndexToPoint(priorIndexA, normal); + if (distance !== undefined && Geometry.isSmallMetricDistance(distance)) + return priorIndexA; + } + if (priorIndexB !== undefined) { + distance = this.data.normal.distanceIndexToPoint(priorIndexB, normal); + if (distance !== undefined && Geometry.isSmallMetricDistance(distance)) + return priorIndexB; + } + const tailIndex = this.data.normal.length - 1; + distance = this.data.normal.distanceIndexToPoint(tailIndex, normal); + if (distance !== undefined && Geometry.isSmallMetricDistance(distance)) + return tailIndex; + } + return this.addNormalXYZ(normal.x, normal.y, normal.z); } @@ -441,10 +482,8 @@ export class IndexedPolyface extends Polyface { const setParamRange: boolean = faceData.paramRange.isNull && paramDefined; do { - for (let i = 0; i < visitor.numEdgesThisFacet; i++) { - if (setParamRange && visitor.param !== undefined) - faceData.paramRange.extendPoint(visitor.param[i]); - } + if (setParamRange && visitor.param !== undefined) + visitor.param.extendRange(faceData.paramRange); } while (visitor.moveToNextFacet() && visitor.currentReadIndex() < endFacetIndex); if (paramDefined && !(this.data.param!.length === 0) && faceData.paramDistanceRange.isNull) @@ -458,11 +497,6 @@ export class IndexedPolyface extends Polyface { return true; } - /** TODO: IMPLEMENT */ - public checkIfClosedByEdgePairing(): boolean { - return false; - } - public dispatchToGeometryHandler(handler: GeometryHandler): any { return handler.handleIndexedPolyface(this); } @@ -547,7 +581,7 @@ export class IndexedPolyfaceVisitor extends PolyfaceData implements PolyfaceVisi const faceData = this._polyface.tryGetFaceData(this._currentFacetIndex); if (!faceData) return undefined; - return faceData.convertParamToDistance(this.param[index], result); + return faceData.convertParamXYToDistance(this.param.getXAtUncheckedPointIndex(index), this.param.getYAtUncheckedPointIndex(index), result); } /** @@ -564,7 +598,7 @@ export class IndexedPolyfaceVisitor extends PolyfaceData implements PolyfaceVisi const faceData = this._polyface.tryGetFaceData(this._currentFacetIndex); if (!faceData) return undefined; - return faceData.convertParamToNormalized(this.param[index], result); + return faceData.convertParamXYToNormalized(this.param.getXAtUncheckedPointIndex(index), this.param.getYAtUncheckedPointIndex(index), result); } public currentReadIndex(): number { return this._currentFacetIndex; } diff --git a/core/geometry/src/polyface/PolyfaceBuilder.ts b/core/geometry/src/polyface/PolyfaceBuilder.ts index edcb3e6..1cf51e3 100644 --- a/core/geometry/src/polyface/PolyfaceBuilder.ts +++ b/core/geometry/src/polyface/PolyfaceBuilder.ts @@ -9,14 +9,14 @@ import { IndexedPolyface } from "./Polyface"; import { GrowableFloat64Array } from "../geometry3d/GrowableFloat64Array"; import { Point2d } from "../geometry3d/Point2dVector2d"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; +import { Point3d, Vector3d, XYZ } from "../geometry3d/Point3dVector3d"; import { Transform } from "../geometry3d/Transform"; import { Matrix3d } from "../geometry3d/Matrix3d"; import { BoxTopology } from "./BoxTopology"; import { StrokeOptions } from "../curve/StrokeOptions"; import { GeometryQuery } from "../curve/GeometryQuery"; import { Cone } from "../solid/Cone"; -import { CurveChain } from "../curve/CurveCollection"; +import { CurveChain, CurveCollection } from "../curve/CurveCollection"; import { Sphere } from "../solid/Sphere"; import { TorusPipe } from "../solid/TorusPipe"; @@ -30,44 +30,158 @@ import { LineString3d } from "../curve/LineString3d"; import { HalfEdgeGraph, HalfEdge, HalfEdgeToBooleanFunction } from "../topology/Graph"; import { NullGeometryHandler, UVSurface } from "../geometry3d/GeometryHandler"; import { GrowableXYArray } from "../geometry3d/GrowableXYArray"; +import { Plane3dByOriginAndVectors } from "../geometry3d/Plane3dByOriginAndVectors"; +import { CurvePrimitive } from "../curve/CurvePrimitive"; +import { StrokeCountSection } from "../curve/Query/StrokeCountChain"; +import { ParityRegion } from "../curve/ParityRegion"; +import { Range1d } from "../geometry3d/Range"; +import { ConstructCurveBetweenCurves } from "../curve/ConstructCurveBetweenCurves"; +import { CylindricalQuery } from "../curve/Query/CylindricalRange"; +import { GrowableXYZArray } from "../geometry3d/GrowableXYZArray"; +import { Segment1d } from "../geometry3d/Segment1d"; +import { BilinearPatch } from "../geometry3d/BilinearPatch"; -class Edgelet { - public indexAlong: number; - public pointIndex0?: number; - public pointIndex1?: number; - public paramIndex0?: number; - public paramIndex1?: number; - public normalIndex0?: number; - public normalIndex1?: number; - public needNormals: boolean; - public needParams: boolean; - public linestringA: LineString3d; - public linestringB: LineString3d; - public builder: PolyfaceBuilder; - - public constructor(builder: PolyfaceBuilder, linestringA: LineString3d, linestringB: LineString3d, needNormals: boolean, needParams: boolean) { - this.linestringA = linestringA; - this.linestringB = linestringB; - this.builder = builder; - this.indexAlong = -1; - this.needNormals = needNormals; - this.needParams = needParams; - } +/* tslint:disable:variable-name prefer-for-of*/ - public loadAtIndex(index: number, vParamA: number, vParamB: number) { - this.indexAlong = index; - this.pointIndex0 = this.builder.findOrAddPointInLineString(this.linestringA, index); - this.pointIndex1 = this.builder.findOrAddPointInLineString(this.linestringB, index); - if (this.needParams) { - this.paramIndex0 = this.builder.findOrAddParamInLineString(this.linestringA, index, vParamA); - this.paramIndex1 = this.builder.findOrAddParamInLineString(this.linestringB, index, vParamB); +/** + * A FacetSector + * * initially holds coordinate data for a place where xyz and sectionDerivative are known + * * normal is computed as a deferred step using an edge to adjacent place on ruled surface + * * indices are set up even later. + */ +class FacetSector { + public xyz: Point3d; + public xyzIndex: number; + public normal?: Vector3d; + public normalIndex: number; + public uv?: Point2d; + public uvIndex: number; + public sectionDerivative?: Vector3d; + public constructor(needNormal: boolean = false, needUV: boolean = false, needSectionDerivative: boolean = false) { + this.xyz = Point3d.create(); + this.normalIndex = -1; + this.uvIndex = -1; + + this.xyzIndex = -1; + if (needNormal) { + this.normal = Vector3d.create(); + } + if (needUV) { + this.uv = Point2d.create(); + this.uvIndex = -1; } - if (this.needNormals) { - this.normalIndex0 = this.builder.findOrAddNormalInLineStringPair(this.linestringA, this.linestringB, index, true); - this.normalIndex1 = this.builder.findOrAddNormalInLineStringPair(this.linestringB, this.linestringB, index, false); + if (needSectionDerivative) { + this.sectionDerivative = Vector3d.create(); + } + } + /** copy contents (not pointers) from source + * * ASSUME all fields defined in this are defined int the source (undefined check only needed on this) + */ + public copyContentsFrom(other: FacetSector) { + this.xyz.setFromPoint3d(other.xyz); + this.xyzIndex = other.xyzIndex; + if (this.normal) + this.normal.setFromVector3d(other.normal!); + this.normalIndex = other.normalIndex; + if (this.uv) + this.uv.setFrom(other.uv); + this.uvIndex = other.uvIndex; + if (this.sectionDerivative) + this.sectionDerivative.setFrom(other.sectionDerivative!); + } + /** access xyz, derivative from given arrays. + * * ASSUME corresponding defined conditions + * * xyz and derivative are set. + * * index fields for updated data are cleared to -1. + */ + public loadIndexedPointAndDerivativeCoordinatesFromPackedArrays(i: number, packedXYZ: GrowableXYZArray, packedDerivatives?: GrowableXYZArray, fractions?: GrowableFloat64Array, v?: number) { + packedXYZ.getPoint3dAtCheckedPointIndex(i, this.xyz); + if (fractions && v !== undefined) + this.uv = Point2d.create(fractions.atUncheckedIndex(i), v); + this.xyzIndex = -1; + this.normalIndex = -1; + this.uvIndex = -1; + if (this.sectionDerivative !== undefined && packedDerivatives !== undefined) + packedDerivatives!.getVector3dAtCheckedVectorIndex(i, this.sectionDerivative); + } + private static suppressSmallUnitVectorComponents(uvw: XYZ) { + const tol = 1.0e-15; + if (Math.abs(uvw.x) < tol) uvw.x = 0.0; + if (Math.abs(uvw.y) < tol) uvw.y = 0.0; + if (Math.abs(uvw.z) < tol) uvw.z = 0.0; + } + private static _edgeVector: Vector3d = Vector3d.create(); + /** + * given two sectors with xyz and sectionDerivative (u derivative) + * use the edge from A to B as v direction in-surface derivative. + * compute cross products (and normalize) + * @param sectorA "lower" sector + * @param sectorB "upper" sector + * + */ + public static computeNormalsAlongRuleLine(sectorA: FacetSector, sectorB: FacetSector) { + // We expect that if sectionDerivative is defined so is normal. + // (If not, the cross product calls will generate normals that are never used .. not good, garbage collector will clean up.) + if (sectorA.sectionDerivative && sectorB.sectionDerivative) { + const vectorAB = FacetSector._edgeVector; + Vector3d.createStartEnd(sectorA.xyz, sectorB.xyz, vectorAB); + sectorA.sectionDerivative!.crossProduct(vectorAB, sectorA.normal); + sectorB.sectionDerivative!.crossProduct(vectorAB, sectorB.normal); + sectorA.normal!.normalizeInPlace(); + sectorB.normal!.normalizeInPlace(); + FacetSector.suppressSmallUnitVectorComponents(sectorA.normal!); + FacetSector.suppressSmallUnitVectorComponents(sectorB.normal!); } } +} +/** + * UVSurfaceOps is a class containing static methods operating on UVSurface objects. + */ +export class UVSurfaceOps { + private constructor() { } // private constructor -- no instances. + /** + * * evaluate `numEdge+1` points at surface uv parameters interpolated between (u0,v0) and (u1,v1) + * * accumulate the xyz in a linestring. + * * If xyzToUV is given, also accumulate transformed values as surfaceUV + * * use xyzToUserUV transform to convert xyz to uv stored in the linestring (this uv is typically different from surface uv -- e.g. torus cap plane coordintes) + * @param surface + * @param u0 u coordinate at start of parameter space line + * @param v0 v coordinate at end of parameter space line + * @param u1 u coordinate at start of parameter space line + * @param v1 v coordinate at end of parameter space line + * @param numEdge number of edges. (`numEdge+1` points are evaluated) + * @param saveUV if true, save each surface uv fractions with `linestring.addUVParamsAsUV (u,v)` + * @param saveFraction if true, save each fractional coordinate (along the u,v line) with `linestring.addFraction (fraction)` + * + * @param xyzToUV + */ + public static createLinestringOnUVLine( + surface: UVSurface, + u0: number, + v0: number, + u1: number, + v1: number, + numEdge: number, + saveUV: boolean = false, + saveFraction: boolean = false): LineString3d { + const ls = LineString3d.create(); + const xyz = Point3d.create(); + let fraction, u, v; + const numEvaluate = numEdge + 1; + for (let i = 0; i < numEvaluate; i++) { + fraction = i / numEdge; + u = Geometry.interpolate(u0, fraction, u1); + v = Geometry.interpolate(v0, fraction, v1); + surface.uvFractionToPoint(u, v, xyz); + ls.addPoint(xyz); + if (saveUV) + ls.addUVParamAsUV(u, v); + if (saveFraction) + ls.addFraction(fraction); + } + return ls; + } } /** * @@ -114,7 +228,6 @@ class Edgelet { * *** `builder.findOrAddPointInLineString (linestring, index)` * *** `builder.findorAddTransformedPointInLineString(linestring, index, transform)` * *** `builder.findOrAddPointXYZ(x,y,z)` - * *** `builder.addTriangleFanFromIndex0(indexArray, toggle)` * *** `builder.addTriangle (point0, point1, point2)` * *** `builder.addQuad (point0, point1, point2, point3)` * *** `builder.addOneBasedPointIndex (index)` @@ -200,7 +313,9 @@ export class PolyfaceBuilder extends NullGeometryHandler { normal.scaleInPlace(-1.0); normalIndex = this._polyface.addNormal(normal); } - const packedUV = ls.packedUVParams; + const needParams = this._options.needParams; + + const packedUV = needParams ? ls.packedUVParams : undefined; let paramIndex0 = -1; let paramIndex1 = -1; let paramIndex2 = -1; @@ -211,13 +326,16 @@ export class PolyfaceBuilder extends NullGeometryHandler { const pointIndex0 = this.findOrAddPointInLineString(ls, 0)!; let pointIndex1 = this.findOrAddPointInLineString(ls, 1)!; let pointIndex2 = 0; - for (let i = 2; i < n; i++ , pointIndex1 = pointIndex2, paramIndex1 = paramIndex2) { + let numEdge = n; + if (ls.isPhysicallyClosed) + numEdge--; + for (let i = 2; i < numEdge; i++ , pointIndex1 = pointIndex2, paramIndex1 = paramIndex2) { pointIndex2 = this.findOrAddPointInLineString(ls, i)!; this.addIndexedTrianglePointIndexes(pointIndex0, pointIndex1, pointIndex2, false); if (normalIndex !== undefined) this.addIndexedTriangleNormalIndexes(normalIndex, normalIndex, normalIndex); if (packedUV) { - paramIndex2 = this.findOrAddParamInGrowableXYArray(packedUV, 1)!; + paramIndex2 = this.findOrAddParamInGrowableXYArray(packedUV, i)!; this.addIndexedTriangleParamIndexes(paramIndex0, paramIndex1, paramIndex2); } this._polyface.terminateFacet(); @@ -226,31 +344,6 @@ export class PolyfaceBuilder extends NullGeometryHandler { this.toggleReversedFacetFlag(); } } - /** Add triangles from points[0] to each far edge. - * @param ls linestring with point coordinates - * @param reverse if true, wrap the triangle creation in toggleReversedFacetFlag. - */ - public addTriangleFanFromIndex0(index: GrowableFloat64Array, toggle: boolean, needNormals: boolean = false, needParams: boolean = false): void { - const n = index.length; - if (n > 2) { - if (toggle) - this.toggleReversedFacetFlag(); - const index0 = index.at(0); - let index1 = index.at(1); - let index2 = 0; - for (let i = 2; i < n; i++) { - index2 = index.at(i); - this.addIndexedTrianglePointIndexes(index0, index1, index2); - if (needNormals) - this.addIndexedTriangleNormalIndexes(index0, index1, index2); - if (needParams) - this.addIndexedTriangleParamIndexes(index0, index1, index2); - index1 = index2; - } - if (toggle) - this.toggleReversedFacetFlag(); - } - } /** * Announce point coordinates. The implemetation is free to either create a new point or (if known) return indxex of a prior point with the same coordinates. @@ -266,7 +359,6 @@ export class PolyfaceBuilder extends NullGeometryHandler { return this._polyface.addParamXY(x, y); } private static _workPointFindOrAddA = Point3d.create(); - private static _workPointFindOrAddB = Point3d.create(); private static _workVectorFindOrAdd = Vector3d.create(); private static _workUVFindOrAdd = Point2d.create(); /** @@ -274,12 +366,12 @@ export class PolyfaceBuilder extends NullGeometryHandler { * @returns Returns the point index in the Polyface. * @param index Index of the point in the linestring. */ - public findOrAddPointInLineString(ls: LineString3d, index: number, transform?: Transform): number | undefined { + public findOrAddPointInLineString(ls: LineString3d, index: number, transform?: Transform, priorIndex?: number): number | undefined { const q = ls.pointAt(index, PolyfaceBuilder._workPointFindOrAddA); if (q) { if (transform) transform.multiplyPoint3d(q, q); - return this._polyface.addPoint(q); + return this._polyface.addPoint(q, priorIndex); } return undefined; } @@ -292,39 +384,40 @@ export class PolyfaceBuilder extends NullGeometryHandler { public findOrAddParamInGrowableXYArray(data: GrowableXYArray, index: number): number | undefined { if (!data) return undefined; - const q = data.getPoint2dAt(index, PolyfaceBuilder._workUVFindOrAdd); + const q = data.getPoint2dAtUncheckedPointIndex(index, PolyfaceBuilder._workUVFindOrAdd); if (q) { return this._polyface.addParam(q); } return undefined; } /** - * Announce param coordinates, taking u from linestring and v from parameter. The implemetation is free to either create a new param or (if knonw) return indxex of a prior point with the same coordinates. + * Announce param coordinates, taking u from ls.fractions and v from parameter. The implemetation is free to either create a new param or (if knonw) return indxex of a prior point with the same coordinates. * @returns Returns the point index in the Polyface. * @param index Index of the point in the linestring. */ - public findOrAddParamInLineString(ls: LineString3d, index: number, v: number): number | undefined { - const u = (ls.fractions && index < ls.fractions.length) ? ls.fractions.at(index) : index / ls.points.length; - return this._polyface.addParamUV(u, v); + public findOrAddParamInLineString(ls: LineString3d, index: number, v: number, priorIndexA?: number, priorIndexB?: number): number | undefined { + const u = (ls.fractions && index < ls.fractions.length) ? ls.fractions.atUncheckedIndex(index) : index / ls.points.length; + return this._polyface.addParamUV(u, v, priorIndexA, priorIndexB); } + /** - * Return a normal index, with the normal computed as crossproduct of (a) vector between linestrings and (b) vector along linestring + * Announce normal coordinates found at index in the linestring's surfaceNormal array * @returns Returns the point index in the Polyface. * @param index Index of the point in the linestring. - * @param atLsA true if the normal is being formed along lsA, false if along lsB + * @param priorIndex possible prior normal index to reuse */ - public findOrAddNormalInLineStringPair(lsA: LineString3d, lsB: LineString3d, index: number, atLsA: boolean): number | undefined { - if (!lsA.packedDerivatives || !lsB.packedDerivatives) - return undefined; - const pointA = lsA.pointAt(index, PolyfaceBuilder._workPointFindOrAddA); - const pointB = lsB.pointAt(index, PolyfaceBuilder._workPointFindOrAddB); - const vectorAlong = (atLsA ? lsA : lsB).derivativeAt(index); - if (vectorAlong && pointA && pointB) { - const normalVector = vectorAlong.crossProductStartEnd(pointA, pointB).normalize(PolyfaceBuilder._workVectorFindOrAdd); - if (normalVector) - return this._polyface.addNormal(normalVector); + public findOrAddNormalnLineString(ls: LineString3d, index: number, transform?: Transform, priorIndexA?: number, priorIndexB?: number): number | undefined { + const linestringNormals = ls.packedSurfaceNormals; + if (linestringNormals) { + const q = linestringNormals.getVector3dAtCheckedVectorIndex(index, PolyfaceBuilder._workVectorFindOrAdd); + if (q) { + if (transform) + transform.multiplyVector(q, q); + return this._polyface.addNormal(q, priorIndexA, priorIndexB); + } } return undefined; + } /** @@ -584,70 +677,106 @@ export class PolyfaceBuilder extends NullGeometryHandler { this._polyface.addNormalIndex(indexB); } } + /** Find or add xyzIndex and normalIndex for coordinates in the sector. */ + private setSectorIndices(sector: FacetSector) { + sector.xyzIndex = this.findOrAddPoint(sector.xyz); + if (sector.normal) + sector.normalIndex = this._polyface.addNormal(sector.normal); + if (sector.uv) + sector.uvIndex = this._polyface.addParam(sector.uv); + } + private addSectorQuadA01B01(sectorA0: FacetSector, sectorA1: FacetSector, sectorB0: FacetSector, sectorB1: FacetSector) { + if (sectorA0.xyz.isAlmostEqual(sectorA1.xyz) && sectorB0.xyz.isAlmostEqual(sectorB1.xyz)) { + // ignore null quad !! + } else { + if (this._options.needNormals) + this.addIndexedQuadNormalIndexes(sectorA0.normalIndex, sectorA1.normalIndex, sectorB0.normalIndex, sectorB1.normalIndex); + if (this._options.needParams) + this.addIndexedQuadParamIndexes(sectorA0.uvIndex, sectorA1.uvIndex, sectorB0.uvIndex, sectorB1.uvIndex); + this.addIndexedQuadPointIndexes(sectorA0.xyzIndex, sectorA1.xyzIndex, sectorB0.xyzIndex, sectorB1.xyzIndex); + this._polyface.terminateFacet(); + } + + } /** Add facets betwee lineStrings with matched point counts. - * + * * surface normals are computed from (a) curve tangents in the linestrings and (b)rule line between linestrings. * * Facets are announced to addIndexedQuad. * * addIndexedQuad is free to apply reversal or triangulation options. */ - public addBetweenLineStrings(lineStringA: LineString3d, lineStringB: LineString3d, addClosure: boolean = false) { - const pointA = lineStringA.points; - const pointB = lineStringB.points; + public addBetweenLineStringsWithRuleEdgeNormals(lineStringA: LineString3d, vA: number, lineStringB: LineString3d, vB: number, addClosure: boolean = false) { + const pointA = lineStringA.packedPoints; + const pointB = lineStringB.packedPoints; + const derivativeA = lineStringA.packedDerivatives; + const derivativeB = lineStringB.packedDerivatives; + const fractionA = lineStringA.fractions; + const fractionB = lineStringB.fractions; + const needNormals = this._options.needNormals; + const needParams = this._options.needParams; + const sectorA0 = new FacetSector(needNormals, needParams, needNormals); + const sectorA1 = new FacetSector(needNormals, needParams, needNormals); + const sectorB0 = new FacetSector(needNormals, needParams, needNormals); + const sectorB1 = new FacetSector(needNormals, needParams, needNormals); + const sectorA00 = new FacetSector(needNormals, needParams, needNormals); + const sectorB00 = new FacetSector(needNormals, needParams, needNormals); + const numPoints = pointA.length; if (numPoints < 2 || numPoints !== pointB.length) return; - let pointIndexA0 = this.findOrAddPoint(pointA[0]); - let pointIndexB0 = this.findOrAddPoint(pointB[0]); - const pointIndexA00 = pointIndexA0; - const pointIndexB00 = pointIndexB0; - let pointIndexA1 = 0; - let pointIndexB1 = 0; + sectorA0.loadIndexedPointAndDerivativeCoordinatesFromPackedArrays(0, pointA, derivativeA, fractionA, vA); + sectorB0.loadIndexedPointAndDerivativeCoordinatesFromPackedArrays(0, pointB, derivativeB, fractionB, vB); + if (needNormals) + FacetSector.computeNormalsAlongRuleLine(sectorA0, sectorB0); + this.setSectorIndices(sectorA0); + this.setSectorIndices(sectorB0); + + sectorA00.copyContentsFrom(sectorA0); + sectorB00.copyContentsFrom(sectorB0); for (let i = 1; i < numPoints; i++) { - pointIndexA1 = this.findOrAddPoint(pointA[i]); - pointIndexB1 = this.findOrAddPoint(pointB[i]); - this.addIndexedQuadPointIndexes(pointIndexA0, pointIndexA1, pointIndexB0, pointIndexB1); - pointIndexA0 = pointIndexA1; - pointIndexB0 = pointIndexB1; + sectorA1.loadIndexedPointAndDerivativeCoordinatesFromPackedArrays(i, pointA, derivativeA, fractionA, vA); + sectorB1.loadIndexedPointAndDerivativeCoordinatesFromPackedArrays(i, pointB, derivativeA, fractionB, vB); + FacetSector.computeNormalsAlongRuleLine(sectorA1, sectorB1); + this.setSectorIndices(sectorA1); + this.setSectorIndices(sectorB1); + // create the facet ... + this.addSectorQuadA01B01(sectorA0, sectorA1, sectorB0, sectorB1); + sectorA0.copyContentsFrom(sectorA1); + sectorB0.copyContentsFrom(sectorB1); } if (addClosure) - this.addIndexedQuadPointIndexes(pointIndexA0, pointIndexA00, pointIndexB0, pointIndexB00); - } - - public addQuadBetweenEdgelets(edgeA: Edgelet, edgeB: Edgelet) { - if (this._options.needNormals) - this.addIndexedQuadNormalIndexes(edgeA.normalIndex0!, edgeB.normalIndex0!, edgeB.normalIndex1!, edgeA.normalIndex1!); - if (this._options.needParams) - this.addIndexedQuadParamIndexes(edgeA.paramIndex0!, edgeB.paramIndex0!, edgeB.paramIndex1!, edgeA.paramIndex1!); - this.addIndexedQuadPointIndexes(edgeA.pointIndex0!, edgeB.pointIndex0!, edgeB.pointIndex1!, edgeA.pointIndex1!, false); - this._polyface.terminateFacet(); + this.addSectorQuadA01B01(sectorA0, sectorA00, sectorB0, sectorB00); } /** Add facets betwee lineStrings with matched point counts. - * - * * Facets are announced to addIndexedQuad. - * * addIndexedQuad is free to apply reversal or triangulation options. + * * point indices prestored + * * normal indices prestored + * * uv indices prestored */ - public addBetweenLineStringsExt(lineStringA: LineString3d, lineStringB: LineString3d, - vA: number, - vB: number, - addClosure: boolean = false) { - const numPoints = lineStringA.numPoints(); - if (lineStringA.numPoints() < 2 || lineStringB.numPoints() !== lineStringA.numPoints()) - return; - let edgeA = new Edgelet(this, lineStringA, lineStringB, this._options.needNormals, this._options.needParams); - let edgeB = new Edgelet(this, lineStringA, lineStringB, this._options.needNormals, this._options.needParams); - edgeA.loadAtIndex(0, vA, vB); - let edgeQ; - for (let i = 1; i < numPoints; i++) { - edgeB.loadAtIndex(i, vA, vB); - // SWAP references to struts ... - edgeQ = edgeA; - edgeA = edgeB; - edgeB = edgeQ; - this.addQuadBetweenEdgelets(edgeA, edgeB); + public addBetweenLineStringsWithStoredIndices(lineStringA: LineString3d, lineStringB: LineString3d) { + const pointA = lineStringA.pointIndices!; + const pointB = lineStringB.pointIndices!; + let normalA: GrowableFloat64Array | undefined = lineStringA.normalIndices; + let normalB: GrowableFloat64Array | undefined = lineStringB.normalIndices; + if (!this._options.needNormals) { + normalA = undefined; + normalB = undefined; + } + let paramA: GrowableFloat64Array | undefined = lineStringA.paramIndices; + let paramB: GrowableFloat64Array | undefined = lineStringB.paramIndices; + if (!this._options.needParams) { + paramA = undefined; + paramB = undefined; } - if (addClosure) { - edgeB.loadAtIndex(0, vA, vB); - this.addQuadBetweenEdgelets(edgeA, edgeB); + + const numPoints = pointA.length; + for (let i = 1; i < numPoints; i++) { + if (pointA.atUncheckedIndex(i - 1) !== pointA.atUncheckedIndex(i) || pointB.atUncheckedIndex(i - 1) !== pointB.atUncheckedIndex(i)) { + this.addIndexedQuadPointIndexes(pointA.atUncheckedIndex(i - 1), pointA.atUncheckedIndex(i), pointB.atUncheckedIndex(i - 1), pointB.atUncheckedIndex(i)); + if (normalA && normalB) + this.addIndexedQuadNormalIndexes(normalA.atUncheckedIndex(i - 1), normalA.atUncheckedIndex(i), normalB.atUncheckedIndex(i - 1), normalB.atUncheckedIndex(i)); + if (paramA && paramB) + this.addIndexedQuadParamIndexes(paramA.atUncheckedIndex(i - 1), paramA.atUncheckedIndex(i), paramB.atUncheckedIndex(i - 1), paramB.atUncheckedIndex(i)); + this._polyface.terminateFacet(); + } } } @@ -685,9 +814,15 @@ export class PolyfaceBuilder extends NullGeometryHandler { } } - public addBetweenStroked(dataA: AnyCurve, dataB: AnyCurve) { + private addBetweenStrokeSetPair(dataA: AnyCurve, vA: number, dataB: AnyCurve, vB: number) { if (dataA instanceof LineString3d && dataB instanceof LineString3d) { - this.addBetweenLineStrings(dataA, dataB, false); + this.addBetweenLineStringsWithRuleEdgeNormals(dataA, vA, dataB, vB, false); + } else if (dataA instanceof ParityRegion && dataB instanceof ParityRegion) { + if (dataA.children.length === dataB.children.length) { + for (let i = 0; i < dataA.children.length; i++) { + this.addBetweenStrokeSetPair(dataA.children[i], vA, dataB.children[i], vB); + } + } } else if (dataA instanceof CurveChain && dataB instanceof CurveChain) { const chainA = dataA.children; const chainB = dataB.children; @@ -696,7 +831,7 @@ export class PolyfaceBuilder extends NullGeometryHandler { const cpA = chainA[i]; const cpB = chainB[i]; if (cpA instanceof LineString3d && cpB instanceof LineString3d) { - this.addBetweenLineStrings(cpA, cpB); + this.addBetweenLineStringsWithRuleEdgeNormals(cpA, vA, cpB, vB); } } } @@ -710,18 +845,29 @@ export class PolyfaceBuilder extends NullGeometryHandler { public addCone(cone: Cone) { // ensure identical stroke counts at each end . . . let strokeCount = 16; - if (this._options) { + if (this._options) strokeCount = this._options.applyTolerancesToArc(cone.getMaxRadius()); - } + let axisStrokeCount = 1; const lineStringA = cone.strokeConstantVSection(0.0, strokeCount, this._options); const lineStringB = cone.strokeConstantVSection(1.0, strokeCount, this._options); - this.addBetweenLineStringsExt(lineStringA, lineStringB, 0.0, 1.0, false); + + if (this._options) { + const vDistanceRange = GrowableXYZArray.distanceRangeBetweenCorrespondingPoints(lineStringA.packedPoints, lineStringB.packedPoints); + axisStrokeCount = this._options.applyMaxEdgeLength(1, vDistanceRange.low); + } + const sizes = cone.maxIsoParametricDistance(); + this.addUVGridBody(cone, strokeCount, axisStrokeCount, Segment1d.create(0, sizes.x), Segment1d.create(0, sizes.y)); this.endFace(); + if (cone.capped) { - this.addTrianglesInUncheckedConvexPolygon(lineStringA, true); // lower triangles flip - this.endFace(); - this.addTrianglesInUncheckedConvexPolygon(lineStringB, false); // upper triangles to not flip. - this.endFace(); + if (!Geometry.isSmallMetricDistance(cone.getRadiusA())) { + this.addTrianglesInUncheckedConvexPolygon(lineStringA, true); // lower triangles flip + this.endFace(); + } + if (!Geometry.isSmallMetricDistance(cone.getRadiusB())) { + this.addTrianglesInUncheckedConvexPolygon(lineStringB, false); // upper triangles to not flip. + this.endFace(); + } } } @@ -731,12 +877,54 @@ export class PolyfaceBuilder extends NullGeometryHandler { * @param strokeCount number of strokes around the cone. If omitted, use the strokeOptions previously supplied to the builder. */ public addTorusPipe(surface: TorusPipe, phiStrokeCount?: number, thetaStrokeCount?: number) { + const thetaFraction = surface.getThetaFraction(); + const numU = Geometry.clamp(Geometry.resolveNumber(phiStrokeCount, 8), 4, 64); + const numV = Geometry.clamp( + Geometry.resolveNumber(thetaStrokeCount, Math.ceil(16 * thetaFraction)), + 2, 64); + this.toggleReversedFacetFlag(); - this.addUVGrid(surface, - phiStrokeCount ? phiStrokeCount : 8, - thetaStrokeCount ? thetaStrokeCount : Math.ceil(16 * surface.getThetaFraction()), - surface.capped); + const sizes = surface.maxIsoParametricDistance(); + this.addUVGridBody(surface, numU, numV, Segment1d.create(0, sizes.x), Segment1d.create(0, sizes.y)); this.toggleReversedFacetFlag(); + + if (surface.capped && thetaFraction < 1.0) { + const centerFrame = surface.getConstructiveFrame()!; + const minorRadius = surface.getMinorRadius(); + const majorRadius = surface.getMajorRadius(); + const a = 2 * minorRadius; + const r0 = majorRadius - minorRadius; + const r1 = majorRadius + minorRadius; + const z0 = -minorRadius; + const cap0ToLocal = Transform.createRowValues( + a, 0, 0, r0, + 0, 0, -1, 0, + 0, a, 0, z0); + const cap0ToWorld = centerFrame.multiplyTransformTransform(cap0ToLocal); + const worldToCap0 = cap0ToWorld.inverse(); + if (worldToCap0) { + const ls0 = UVSurfaceOps.createLinestringOnUVLine(surface, 0, 0, 1, 0, numU, false, true); + ls0.computeUVFromXYZTransform(worldToCap0); + this.addTrianglesInUncheckedConvexPolygon(ls0, false); + } + const thetaRadians = surface.getSweepAngle().radians; + const cc = Math.cos(thetaRadians); + const ss = Math.sin(thetaRadians); + + const cap1ToLocal = Transform.createRowValues( + -cc * a, 0, -ss, r1 * cc, + -ss * a, 0, cc, r1 * ss, + 0, a, 0, z0); + + const cap1ToWorld = centerFrame.multiplyTransformTransform(cap1ToLocal); + const worldToCap1 = cap1ToWorld.inverse(); + if (worldToCap1) { + const ls1 = UVSurfaceOps.createLinestringOnUVLine(surface, 1, 1, 0, 1, numU, false, true); + ls1.computeUVFromXYZTransform(worldToCap1); + this.addTrianglesInUncheckedConvexPolygon(ls1, false); + } + + } } /** @@ -744,7 +932,7 @@ export class PolyfaceBuilder extends NullGeometryHandler { * @param vector sweep vector * @param contour contour which contains only linestrings */ - public addLinearSweepLineStrings(contour: AnyCurve, vector: Vector3d) { + public addLinearSweepLineStringsXYZOnly(contour: AnyCurve, vector: Vector3d) { if (contour instanceof LineString3d) { const ls = contour as LineString3d; let pointA = Point3d.create(); @@ -767,85 +955,269 @@ export class PolyfaceBuilder extends NullGeometryHandler { } } else if (contour instanceof CurveChain) { for (const ls of contour.children) { - this.addLinearSweepLineStrings(ls, vector); + this.addLinearSweepLineStringsXYZOnly(ls, vector); } } } public addRotationalSweep(surface: RotationalSweep) { - const strokes = surface.getCurves().cloneStroked(); - const numStep = StrokeOptions.applyAngleTol(this._options, 1, surface.getSweep().radians, undefined); - const transformA = Transform.createIdentity(); - const transformB = Transform.createIdentity(); + const contour = surface.getCurves(); + const section0 = StrokeCountSection.createForParityRegionOrChain(contour, this._options); + const baseStrokes = section0.getStrokes(); + + const axis = surface.cloneAxisRay(); + const perpendicularVector = CylindricalQuery.computeMaxVectorFromRay(axis, baseStrokes); + const swingVector = axis.direction.crossProduct(perpendicularVector); + if (this._options.needNormals) + CylindricalQuery.buildRotationalNormalsInLineStrings(baseStrokes, axis, swingVector); + const maxDistance = perpendicularVector.magnitude(); + const maxPath = Math.abs(maxDistance * surface.getSweep().radians); + let numStep = StrokeOptions.applyAngleTol(this._options, 1, surface.getSweep().radians, undefined); + numStep = StrokeOptions.applyMaxEdgeLength(this._options, numStep, maxPath); for (let i = 1; i <= numStep; i++) { - surface.getFractionalRotationTransform(i / numStep, transformB); - this.addBetweenTransformedLineStrings(strokes, transformA, transformB); - transformA.setFrom(transformB); + const transformA = surface.getFractionalRotationTransform((i - 1) / numStep); + const transformB = surface.getFractionalRotationTransform(i / numStep); + this.addBetweenRotatedStrokeSets(baseStrokes, transformA, i - 1, transformB, i); } if (surface.capped) { - const contour = surface.getSweepContourRef(); - contour.emitFacets(this, true, undefined); - contour.emitFacets(this, false, transformB); + const capContour = surface.getSweepContourRef(); + capContour.purgeFacets(); + capContour.emitFacets(this, true, undefined); + // final loop pass left transformA at end .. + capContour.emitFacets(this, false, surface.getFractionalRotationTransform(1.0)); + } + } + /** + * * Recursively visit all children of data. + * * At each primitive, invoke the computeStrokeCountForOptions method, with options from the builder. + * @param data + */ + public applyStrokeCountsToCurvePrimitives(data: AnyCurve | GeometryQuery) { + const options = this._options; + if (data instanceof CurvePrimitive) { + data.computeStrokeCountForOptions(options); + } else if (data instanceof CurveCollection) { + const children = data.children; + if (children) + for (const child of children) { + this.applyStrokeCountsToCurvePrimitives(child); + } + } + } + + private addBetweenStrokeSetsWithRuledNormals(stroke0: AnyCurve, stroke1: AnyCurve, numVEdge: number) { + const strokeSets = [stroke0]; + const fractions = [0.0]; + for (let vIndex = 1; vIndex < numVEdge; vIndex++) { + const vFraction = vIndex / numVEdge; + const strokeA = ConstructCurveBetweenCurves.interpolateBetween(stroke0, vIndex / numVEdge, stroke1) as AnyCurve; + strokeSets.push(strokeA); + fractions.push(vFraction); + } + strokeSets.push(stroke1); + fractions.push(1.0); + for (let vIndex = 0; vIndex < numVEdge; vIndex++) { + this.addBetweenStrokeSetPair(strokeSets[vIndex], fractions[vIndex], strokeSets[vIndex + 1], fractions[vIndex + 1]); + } + } + private createIndicesInLineString(ls: LineString3d, vParam: number, transform?: Transform) { + + const n = ls.numPoints(); + { + const pointIndices = ls.ensureEmptyPointIndices(); + const index0 = this.findOrAddPointInLineString(ls, 0, transform); + pointIndices.push(index0!); + if (n > 1) { + let indexA = index0; + let indexB; + for (let i = 1; i + 1 < n; i++) { + indexB = this.findOrAddPointInLineString(ls, i, transform, indexA); + pointIndices.push(indexB!); + indexA = indexB; + } + // assume last point can only repeat back to zero ... + indexB = this.findOrAddPointInLineString(ls, n - 1, transform, index0); + pointIndices.push(indexB!); + } + } + if (this._options.needNormals && ls.packedSurfaceNormals !== undefined) { + const normalIndices = ls.ensureEmptyNormalIndices(); + const normalIndex0 = this.findOrAddNormalnLineString(ls, 0, transform); + normalIndices.push(normalIndex0!); + let normalIndexA = normalIndex0; + let normalIndexB; + if (n > 1) { + for (let i = 1; i + 1 < n; i++) { + normalIndexB = this.findOrAddNormalnLineString(ls, i, transform, normalIndexA); + normalIndices.push(normalIndexB!); + normalIndexA = normalIndexB; + } + // assume last point can only repeat back to zero ... + normalIndexB = this.findOrAddNormalnLineString(ls, n - 1, transform, normalIndex0, normalIndexA); + normalIndices.push(normalIndexB!); + } + } + if (this._options.needParams && ls.packedUVParams !== undefined) { + const uvIndices = ls.ensureEmptyUVIndices(); + const uvIndex0 = this.findOrAddParamInLineString(ls, 0, vParam); + uvIndices.push(uvIndex0!); + let uvIndexA = uvIndex0; + let uvIndexB; + if (n > 1) { + for (let i = 1; i + 1 < n; i++) { + uvIndexB = this.findOrAddParamInLineString(ls, i, vParam, uvIndexA); + uvIndices.push(uvIndexB!); + uvIndexA = uvIndexB; + } + // assume last point can only repeat back to zero ... + uvIndexB = this.findOrAddParamInLineString(ls, n - 1, vParam, uvIndexA, uvIndex0); + uvIndices.push(uvIndexB!); + } } } + private addBetweenRotatedStrokeSets(stroke0: AnyCurve, transformA: Transform, vA: number, transformB: Transform, vB: number) { + if (stroke0 instanceof LineString3d) { + const strokeA = stroke0.cloneTransformed(transformA) as LineString3d; + this.createIndicesInLineString(strokeA, vA); + const strokeB = stroke0.cloneTransformed(transformB) as LineString3d; + this.createIndicesInLineString(strokeB, vB); + this.addBetweenLineStringsWithStoredIndices(strokeA, strokeB); + } else if (stroke0 instanceof ParityRegion) { + for (let i = 0; i < stroke0.children.length; i++) { + this.addBetweenRotatedStrokeSets(stroke0.children[i], transformA, vA, transformB, vB); + } + } else if (stroke0 instanceof CurveChain) { + const chainA = stroke0.children; + for (let i = 0; i < chainA.length; i++) { + const cpA = chainA[i]; + if (cpA instanceof LineString3d) { + this.addBetweenRotatedStrokeSets(cpA, transformA, vA, transformB, vB); + } + } + } + } /** * * @param cone cone to facet */ public addLinearSweep(surface: LinearSweep) { - const baseStrokes = surface.getCurvesRef().cloneStroked(); - this.addLinearSweepLineStrings(baseStrokes, surface.cloneSweepVector()); - if (surface.capped) { - const contour = surface.getSweepContourRef(); - contour.emitFacets(this, true, undefined); - contour.emitFacets(this, false, Transform.createTranslation(surface.cloneSweepVector())); + const contour = surface.getCurvesRef(); + const section0 = StrokeCountSection.createForParityRegionOrChain(contour, this._options); + const stroke0 = section0.getStrokes(); + const sweepVector = surface.cloneSweepVector(); + const sweepTransform = Transform.createTranslation(sweepVector); + const stroke1 = stroke0.cloneTransformed(sweepTransform) as AnyCurve; + const numVEdge = this._options.applyMaxEdgeLength(1, sweepVector.magnitude()); + this.addBetweenStrokeSetsWithRuledNormals(stroke0, stroke1, numVEdge); + + if (surface.capped && contour.isAnyRegionType) { + const contourA = surface.getSweepContourRef(); + contourA.purgeFacets(); + + contourA.emitFacets(this, true, undefined); + contourA.emitFacets(this, false, sweepTransform); } } /** * - * @param cone cone to facet + * @param surface RuledSurface to facet. */ - public addRuledSweep(surface: RuledSweep) { + public addRuledSweep(surface: RuledSweep): boolean { const contours = surface.sweepContoursRef(); - let stroke0; - let stroke1; + let stroke0: AnyCurve | undefined; + let stroke1: AnyCurve; + const sectionMaps = []; for (let i = 0; i < contours.length; i++) { - stroke1 = contours[i].curves.cloneStroked(); - if (i > 0 && stroke0 && stroke1) - this.addBetweenStroked(stroke0, stroke1); - stroke0 = stroke1; + sectionMaps.push(StrokeCountSection.createForParityRegionOrChain(contours[i].curves, this._options)); + } + if (StrokeCountSection.enforceStrokeCountCompatibility(sectionMaps)) { + StrokeCountSection.enforceCompatibleDistanceSums(sectionMaps); + for (let i = 0; i < contours.length; i++) { + stroke1 = sectionMaps[i].getStrokes(); + if (!stroke1) + stroke1 = contours[i].curves.cloneStroked(); + if (i > 0 && stroke0 && stroke1) { + const distanceRange = Range1d.createNull(); + if (StrokeCountSection.extendDistanceRangeBetweenStrokes(stroke0, stroke1, distanceRange) + && !distanceRange.isNull) { + const numVEdge = this._options.applyMaxEdgeLength(1, distanceRange.high); + this.addBetweenStrokeSetsWithRuledNormals(stroke0, stroke1, numVEdge); + } + } + stroke0 = stroke1; + } + } + + if (surface.capped && contours[0].curves.isAnyRegionType) { + contours[0].purgeFacets(); + + contours[0].emitFacets(this, true, undefined); + contours[contours.length - 1].purgeFacets(); + contours[contours.length - 1].emitFacets(this, false, undefined); } - contours[0].emitFacets(this, true, undefined); - contours[contours.length - 1].emitFacets(this, false, undefined); + return true; } public addSphere(sphere: Sphere, strokeCount?: number) { - const numLongitudeStroke = strokeCount ? strokeCount : this._options.defaultCircleStrokes; - const numLatitudeStroke = Geometry.clampToStartEnd(numLongitudeStroke * 0.5, 4, 32); - let lineStringA = sphere.strokeConstantVSection(0.0, numLongitudeStroke); - if (sphere.capped && !Geometry.isSmallMetricDistance(lineStringA.quickLength())) + const numStrokeTheta = strokeCount ? strokeCount : this._options.defaultCircleStrokes; + const numStrokePhi = Geometry.clampToStartEnd(numStrokeTheta * sphere.latitudeSweepFraction, 1, Math.ceil(numStrokeTheta * 0.5)); + + const lineStringA = sphere.strokeConstantVSection(0.0, numStrokeTheta, this._options); + if (sphere.capped && !Geometry.isSmallMetricDistance(lineStringA.quickLength())) { this.addTrianglesInUncheckedConvexPolygon(lineStringA, true); // lower triangles flip - for (let i = 1; i <= numLatitudeStroke; i++) { - const lineStringB = sphere.strokeConstantVSection(i / numLatitudeStroke, numLongitudeStroke); - this.addBetweenLineStrings(lineStringA, lineStringB); - lineStringA = lineStringB; + this.endFace(); } - if (sphere.capped && !Geometry.isSmallMetricDistance(lineStringA.quickLength())) - this.addTrianglesInUncheckedConvexPolygon(lineStringA, true); // upper triangles do not flip + const sizes = sphere.maxIsoParametricDistance(); + this.addUVGridBody(sphere, numStrokeTheta, numStrokePhi, Segment1d.create(0, sizes.x), Segment1d.create(0, sizes.y)); + this.endFace(); + + const lineStringB = sphere.strokeConstantVSection(1.0, numStrokeTheta, this._options); + if (sphere.capped && !Geometry.isSmallMetricDistance(lineStringB.quickLength())) { + this.addTrianglesInUncheckedConvexPolygon(lineStringB, false); // upper triangles do not flip + this.endFace(); + } } public addBox(box: Box) { - const lineStringA = box.strokeConstantVSection(0.0); - const lineStringB = box.strokeConstantVSection(1.0); - this.addBetweenLineStrings(lineStringA, lineStringB); + const corners = box.getCorners(); + const xLength = Geometry.maxXY(box.getBaseX(), box.getBaseX()); + const yLength = Geometry.maxXY(box.getBaseY(), box.getTopY()); + let zLength = 0.0; + for (let i = 0; i < 4; i++) { + zLength = Geometry.maxXY(zLength, corners[i].distance(corners[i + 4])); + + } + + const numX = this._options.applyMaxEdgeLength(1, xLength); + const numY = this._options.applyMaxEdgeLength(1, yLength); + const numZ = this._options.applyMaxEdgeLength(1, zLength); + // Wrap the 4 out-of-plane faces as a single parameters space with "distance" advancing in x then y then negative x then negative y ... + const uParamRange = Segment1d.create(0, xLength); + const vParamRange = Segment1d.create(0, zLength); + this.addUVGridBody(BilinearPatch.create(corners[0], corners[1], corners[4], corners[5]), numX, numZ, uParamRange, vParamRange); + uParamRange.shift(xLength); + this.addUVGridBody(BilinearPatch.create(corners[1], corners[3], corners[5], corners[7]), numY, numZ, uParamRange, vParamRange); + uParamRange.shift(yLength); + this.addUVGridBody(BilinearPatch.create(corners[3], corners[2], corners[7], corners[6]), numX, numZ, uParamRange, vParamRange); + uParamRange.shift(xLength); + this.addUVGridBody(BilinearPatch.create(corners[2], corners[0], corners[6], corners[4]), numY, numZ, uParamRange, vParamRange); + // finally end that wraparound face !! + this.endFace(); if (box.capped) { - this.addTrianglesInUncheckedConvexPolygon(lineStringA, true); // lower triangles flip - this.addTrianglesInUncheckedConvexPolygon(lineStringB, false); // upper triangles to not flip. + uParamRange.set(0.0, xLength); + vParamRange.set(0.0, yLength); + this.addUVGridBody(BilinearPatch.create(corners[4], corners[5], corners[6], corners[7]), numX, numY, uParamRange, vParamRange); + this.endFace(); + + uParamRange.set(0.0, xLength); + vParamRange.set(0.0, yLength); + this.addUVGridBody(BilinearPatch.create(corners[2], corners[3], corners[0], corners[1]), numX, numY, uParamRange, vParamRange); + this.endFace(); } } @@ -910,6 +1282,11 @@ export class PolyfaceBuilder extends NullGeometryHandler { */ public addGraph(graph: HalfEdgeGraph, needParams: boolean, acceptFaceFunction: HalfEdgeToBooleanFunction = HalfEdge.testNodeMaskNotExterior) { let index = 0; + const needNormals = this._options.needNormals; + let normalIndex = 0; + if (needNormals) + normalIndex = this._polyface.addNormalXYZ(0, 0, 1); // big assumption !!!! someday check if that's where the facets actually are!! + graph.announceFaceLoops( (_graph: HalfEdgeGraph, seed: HalfEdge) => { if (acceptFaceFunction(seed)) { @@ -921,6 +1298,9 @@ export class PolyfaceBuilder extends NullGeometryHandler { index = this.findOrAddParamXY(node.x, node.y); this._polyface.addParamIndex(index); } + if (needNormals) { + this._polyface.addNormalIndex(normalIndex); + } node = node.faceSuccessor; } while (node !== seed); this._polyface.terminateFacet(); @@ -934,8 +1314,6 @@ export class PolyfaceBuilder extends NullGeometryHandler { builder.endFace(); return builder.claimPolyface(); } - private static _index0 = new GrowableFloat64Array(); - private static _index1 = new GrowableFloat64Array(); /** * Given arrays of coordinates for multiple facets. @@ -961,66 +1339,95 @@ export class PolyfaceBuilder extends NullGeometryHandler { if (endFace) this.endFace(); } - - public addUVGrid(surface: UVSurface, numU: number, numV: number, createFanInCaps: boolean) { - let index0 = PolyfaceBuilder._index0; - let index1 = PolyfaceBuilder._index1; + /** + * * Evaluate `(numU + 1) * (numV + 1)` grid points (in 0..1 in both u and v) on a surface. + * * Add the facets for `numU * numV` quads. + * * uv params are the 0..1 fractions. + * * normals are cross products of u and v direction partial derivatives. + * @param surface + * @param numU + * @param numV + */ + public addUVGridBody(surface: UVSurface, numU: number, numV: number, uMap?: Segment1d, vMap?: Segment1d) { + let xyzIndex0 = new GrowableFloat64Array(numU); + let xyzIndex1 = new GrowableFloat64Array(numU); + let paramIndex0: GrowableFloat64Array | undefined; + let paramIndex1: GrowableFloat64Array | undefined; + let normalIndex0: GrowableFloat64Array | undefined; + let normalIndex1: GrowableFloat64Array | undefined; const reverse = this._reversed; const needNormals = this.options.needNormals; + if (needNormals) { + normalIndex0 = new GrowableFloat64Array(numU); + normalIndex1 = new GrowableFloat64Array(numU); + } const needParams = this.options.needParams; + if (needParams) { + paramIndex0 = new GrowableFloat64Array(numU); + paramIndex1 = new GrowableFloat64Array(numU); + } + let indexSwap; - index0.ensureCapacity(numU); - index1.ensureCapacity(numU); + xyzIndex0.ensureCapacity(numU); + xyzIndex1.ensureCapacity(numU); const uv = Point2d.create(); const normal = Vector3d.create(); const du = 1.0 / numU; const dv = 1.0 / numV; - // BIG ASSUMPTION: addPoint, addParam, addNormal all add points in simple order (to be compressed later) and can share indices. + const plane = Plane3dByOriginAndVectors.createXYPlane(); for (let v = 0; v <= numV; v++) { // evaluate new points .... - index1.clear(); + xyzIndex1.clear(); + if (needNormals) + normalIndex1!.clear(); + if (needParams) + paramIndex1!.clear(); for (let u = 0; u <= numU; u++) { const uFrac = u * du; const vFrac = v * dv; - const plane = surface.UVFractionToPointAndTangents(uFrac, vFrac); + surface.uvFractionToPointAndTangents(uFrac, vFrac, plane); + xyzIndex1.push(this._polyface.addPoint(plane.origin)); if (needNormals) { plane.vectorU.crossProduct(plane.vectorV, normal); normal.normalizeInPlace(); if (reverse) normal.scaleInPlace(-1.0); - this._polyface.addNormal(normal); + normalIndex1!.push(this._polyface.addNormal(normal)); } if (needParams) - this._polyface.addParam(Point2d.create(u, v, uv)); - index1.push(this._polyface.addPoint(plane.origin)); + paramIndex1!.push(this._polyface.addParam(Point2d.create( + uMap ? uMap.fractionToPoint(uFrac) : uFrac, + vMap ? vMap.fractionToPoint(vFrac) : vFrac, + uv))); } - if (createFanInCaps && (v === 0 || v === numV)) { - this.addTriangleFanFromIndex0(index1, v === 0, true, true); - } if (v > 0) { for (let u = 0; u < numU; u++) { this.addIndexedQuadPointIndexes( - index0.at(u), index0.at(u + 1), - index1.at(u), index1.at(u + 1), false); - if (this._options.needParams) + xyzIndex0.atUncheckedIndex(u), xyzIndex0.atUncheckedIndex(u + 1), + xyzIndex1.atUncheckedIndex(u), xyzIndex1.atUncheckedIndex(u + 1), false); + if (needNormals) this.addIndexedQuadNormalIndexes( - index0.at(u), index0.at(u + 1), - index1.at(u), index1.at(u + 1)); - if (this._options.needParams) + normalIndex0!.atUncheckedIndex(u), normalIndex0!.atUncheckedIndex(u + 1), + normalIndex1!.atUncheckedIndex(u), normalIndex1!.atUncheckedIndex(u + 1)); + if (needParams) this.addIndexedQuadParamIndexes( - index0.at(u), index0.at(u + 1), - index1.at(u), index1.at(u + 1)); + paramIndex0!.atUncheckedIndex(u), paramIndex0!.atUncheckedIndex(u + 1), + paramIndex1!.atUncheckedIndex(u), paramIndex1!.atUncheckedIndex(u + 1)); this._polyface.terminateFacet(); } - } - indexSwap = index1; - index1 = index0; - index0 = indexSwap; + indexSwap = xyzIndex1; xyzIndex1 = xyzIndex0; xyzIndex0 = indexSwap; + if (needParams) { + indexSwap = paramIndex1; paramIndex1 = paramIndex0; paramIndex0 = indexSwap; + } + if (needNormals) { + indexSwap = normalIndex1; normalIndex1 = normalIndex0; normalIndex0 = indexSwap; + } + } - index0.clear(); - index1.clear(); + xyzIndex0.clear(); + xyzIndex1.clear(); } } diff --git a/core/geometry/src/polyface/PolyfaceData.ts b/core/geometry/src/polyface/PolyfaceData.ts index 69ba2f4..2fffd92 100644 --- a/core/geometry/src/polyface/PolyfaceData.ts +++ b/core/geometry/src/polyface/PolyfaceData.ts @@ -9,11 +9,13 @@ import { Point2d } from "../geometry3d/Point2dVector2d"; import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; import { Range3d } from "../geometry3d/Range"; import { Transform } from "../geometry3d/Transform"; -import { NumberArray, Point2dArray } from "../geometry3d/PointHelpers"; +import { NumberArray } from "../geometry3d/PointHelpers"; import { GrowableXYZArray } from "../geometry3d/GrowableXYZArray"; import { ClusterableArray } from "../numerics/ClusterableArray"; import { PolyfaceAuxData } from "./AuxData"; import { FacetFaceData } from "./FacetFaceData"; +import { GrowableXYArray } from "../geometry3d/GrowableXYArray"; +import { Geometry } from "../Geometry"; /** * PolyfaceData carries data arrays for point, normal, param, color and their indices. @@ -41,7 +43,7 @@ export class PolyfaceData { public normal: GrowableXYZArray | undefined; public normalIndex: number[] | undefined; - public param: Point2d[] | undefined; + public param?: GrowableXYArray; public paramIndex: number[] | undefined; public color: number[] | undefined; public colorIndex: number[] | undefined; @@ -54,7 +56,7 @@ export class PolyfaceData { this.pointIndex = []; this.edgeVisible = []; this.face = []; if (needNormals) { this.normal = new GrowableXYZArray(); this.normalIndex = []; } - if (needParams) { this.param = []; this.paramIndex = []; } + if (needParams) { this.param = new GrowableXYArray(); this.paramIndex = []; } if (needColors) { this.color = []; this.colorIndex = []; } } @@ -68,7 +70,7 @@ export class PolyfaceData { if (this.normal) result.normal = this.normal.clone(); if (this.param) - result.param = Point2dArray.clonePoint2dArray(this.param); + result.param = this.param.clone(); if (this.color) result.color = this.color.slice(); @@ -92,7 +94,7 @@ export class PolyfaceData { if (!GrowableXYZArray.isAlmostEqual(this.normal, other.normal)) return false; if (!NumberArray.isExactEqual(this.normalIndex, other.normalIndex)) return false; - if (!Point2dArray.isAlmostEqual(this.param, other.param)) return false; + if (!GrowableXYArray.isAlmostEqual(this.param, other.param)) return false; if (!NumberArray.isExactEqual(this.paramIndex, other.paramIndex)) return false; if (!NumberArray.isExactEqual(this.color, other.color)) return false; @@ -112,21 +114,28 @@ export class PolyfaceData { public get faceCount() { return this.face.length; } /** return indexed point. This is a copy of the coordinates, not a reference. */ - public getPoint(i: number): Point3d | undefined { return this.point.atPoint3dIndex(i); } - /** return indexed normal. This is the REFERENCE to the normal, not a copy. */ - public getNormal(i: number): Vector3d | undefined { return this.normal ? this.normal.atVector3dIndex(i) : undefined; } - /** return indexed param. This is the REFERENCE to the param, not a copy. */ - public getParam(i: number): Point2d { return this.param ? this.param[i] : Point2d.create(); } + public getPoint(i: number): Point3d | undefined { return this.point.getPoint3dAtCheckedPointIndex(i); } + /** return indexed normal. This is the COPY to the normal, not a reference. */ + public getNormal(i: number): Vector3d | undefined { return this.normal ? this.normal.getVector3dAtCheckedVectorIndex(i) : undefined; } + /** return indexed param. This is the COPY of the coordinates, not a reference. */ + public getParam(i: number): Point2d | undefined { return this.param ? this.param.getPoint2dAtCheckedPointIndex(i) : undefined; } /** return indexed color */ public getColor(i: number): number { return this.color ? this.color[i] : 0; } /** return indexed visibility */ public getEdgeVisible(i: number): boolean { return this.edgeVisible[i]; } /** Copy the contents (not pointer) of point[i] into dest. */ - public copyPointTo(i: number, dest: Point3d): void { this.point.getPoint3dAt(i, dest); } + public copyPointTo(i: number, dest: Point3d): void { this.point.getPoint3dAtUncheckedPointIndex(i, dest); } /** Copy the contents (not pointer) of normal[i] into dest. */ - public copyNormalTo(i: number, dest: Vector3d): void { if (this.normal) this.normal.atVector3dIndex(i, dest); } + public copyNormalTo(i: number, dest: Vector3d): void { if (this.normal) this.normal.getVector3dAtCheckedVectorIndex(i, dest); } /** Copy the contents (not pointer) of param[i] into dest. */ - public copyParamTo(i: number, dest: Point2d): void { if (this.param) dest.setFrom(this.param[i]); } + public copyParamTo(i: number, dest: Point2d): void { if (this.param) this.param.getPoint2dAtCheckedPointIndex(i, dest); } + /** test if normal at a specified index matches uv */ + public isAlmostEqualParamIndexUV(index: number, u: number, v: number): boolean { + if (this.param !== undefined && index >= 0 && index < this.param.length) + return Geometry.isSameCoordinate(u, this.param.getXAtUncheckedPointIndex(index)) + && Geometry.isSameCoordinate(v, this.param.getYAtUncheckedPointIndex(index)); + return false; + } /** * * Copy data from other to this. * * This is the essense of transfering coordinates spread throughout a large polyface into a visitor's single facet. @@ -173,9 +182,9 @@ export class PolyfaceData { if (this.param && this.paramIndex && other.param && other.paramIndex) { for (let i = 0; i < numEdge; i++) - this.param[i].setFrom(other.param[other.paramIndex[index0 + i]]); + this.param.transferFromGrowableXYArray(i, other.param, other.paramIndex[index0 + i]); for (let i = 0; i < numWrap; i++) - this.param[numEdge + i].setFrom(this.param[i]); + this.param.transferFromGrowableXYArray(numEdge + i, this.param, i); for (let i = 0; i < numEdge; i++) this.paramIndex[i] = other.paramIndex[index0 + i]; @@ -256,7 +265,7 @@ export class PolyfaceData { this.edgeVisible.length = length; this.pointIndex.length = length; if (this.normal) this.normal.resize(length); - if (this.param) this.param.length = length; + if (this.param) this.param.resize(length); if (this.color) this.color.length = length; if (this.auxData) { for (const channel of this.auxData.channels) { diff --git a/core/geometry/src/polyface/PolyfaceQuery.ts b/core/geometry/src/polyface/PolyfaceQuery.ts index 14541d4..abe2337 100644 --- a/core/geometry/src/polyface/PolyfaceQuery.ts +++ b/core/geometry/src/polyface/PolyfaceQuery.ts @@ -16,20 +16,21 @@ import { Loop } from "../curve/Loop"; import { LineString3d } from "../curve/LineString3d"; import { PolygonOps } from "../geometry3d/PointHelpers"; import { MomentData } from "../geometry4d/MomentData"; +import { IndexedEdgeMatcher, SortableEdgeCluster } from "./IndexedEdgeMatcher"; /** PolyfaceQuery is a static class whose methods implement queries on a polyface or polyface visitor provided as a parameter to each mtehod. */ export class PolyfaceQuery { /** copy the points from a visitor into a Linestring3d in a Loop object */ - public static VisitorToLoop(visitor: PolyfaceVisitor) { + public static visitorToLoop(visitor: PolyfaceVisitor) { const ls = LineString3d.createPoints(visitor.point.getPoint3dArray()); return Loop.create(ls); } /** Create a linestring loop for each facet of the polyface. */ - public static IndexedPolyfaceToLoops(polyface: Polyface): BagOfCurves { + public static indexedPolyfaceToLoops(polyface: Polyface): BagOfCurves { const result = BagOfCurves.create(); const visitor = polyface.createVisitor(1); while (visitor.moveToNextFacet()) { - const loop = PolyfaceQuery.VisitorToLoop(visitor); + const loop = PolyfaceQuery.visitorToLoop(visitor); result.tryAddChild(loop); } return result; @@ -65,20 +66,20 @@ export class PolyfaceQuery { visitor.reset(); while (visitor.moveToNextFacet()) { if (myOrigin === undefined) - myOrigin = visitor.point.getPoint3dAt(0); - visitor.point.getPoint3dAt(0, facetOrigin); + myOrigin = visitor.point.getPoint3dAtUncheckedPointIndex(0); + visitor.point.getPoint3dAtUncheckedPointIndex(0, facetOrigin); for (let i = 1; i + 1 < visitor.point.length; i++) { - visitor.point.getPoint3dAt(i, targetA); - visitor.point.getPoint3dAt(i + 1, targetB); + visitor.point.getPoint3dAtUncheckedPointIndex(i, targetA); + visitor.point.getPoint3dAtUncheckedPointIndex(i + 1, targetB); s += myOrigin.tripleProductToPoints(facetOrigin, targetA, targetB); } } return s / 6.0; } /** Return the inertia products [xx,xy,xz,xw, yw, etc] integrated over all facets. */ - public static SumFacetSecondAreaMomentProducts(source: Polyface | PolyfaceVisitor, origin: Point3d): Matrix4d { + public static sumFacetSecondAreaMomentProducts(source: Polyface | PolyfaceVisitor, origin: Point3d): Matrix4d { if (source instanceof Polyface) - return PolyfaceQuery.SumFacetSecondAreaMomentProducts(source.createVisitor(0), origin); + return PolyfaceQuery.sumFacetSecondAreaMomentProducts(source.createVisitor(0), origin); const products = Matrix4d.createZero(); const visitor = source as PolyfaceVisitor; visitor.reset(); @@ -95,8 +96,25 @@ export class PolyfaceQuery { public static computePrincipalAreaMoments(source: Polyface): MomentData | undefined { const origin = source.data.getPoint(0); if (!origin) return undefined; - const inertiaProducts = PolyfaceQuery.SumFacetSecondAreaMomentProducts(source, origin); + const inertiaProducts = PolyfaceQuery.sumFacetSecondAreaMomentProducts(source, origin); return MomentData.inertiaProductsToPrincipalAxes(origin, inertiaProducts); } - + /** + * Test if the facets in `source` occur in perfectly mated pairs, as is required for a closed manifold volume. + * @param source + */ + public static isPolyfaceClosedByEdgePairing(source: Polyface): boolean { + const edges = new IndexedEdgeMatcher(); + const visitor = source.createVisitor(1) as PolyfaceVisitor; + visitor.reset(); + while (visitor.moveToNextFacet()) { + const numEdges = visitor.pointCount - 1; + for (let i = 0; i < numEdges; i++) { + edges.addEdge(visitor.clientPointIndex(i), visitor.clientPointIndex(i + 1), visitor.currentReadIndex()); + } + } + const badClusters: SortableEdgeCluster[] = []; + edges.sortAndcollectClusters(undefined, badClusters, undefined, badClusters); + return badClusters.length === 0; + } } diff --git a/core/geometry/src/serialization/GeometrySamples.ts b/core/geometry/src/serialization/GeometrySamples.ts index cbe6125..1ea9b39 100644 --- a/core/geometry/src/serialization/GeometrySamples.ts +++ b/core/geometry/src/serialization/GeometrySamples.ts @@ -5,7 +5,7 @@ /** @module Serialization */ -import { Geometry } from "../Geometry"; +import { Geometry, AxisOrder } from "../Geometry"; import { AngleSweep } from "../geometry3d/AngleSweep"; import { Angle } from "../geometry3d/Angle"; import { Plane3dByOriginAndUnitNormal } from "../geometry3d/Plane3dByOriginAndUnitNormal"; @@ -29,7 +29,7 @@ import { Loop } from "../curve/Loop"; import { Path } from "../curve/Path"; import { IndexedPolyface } from "../polyface/Polyface"; import { BSplineCurve3d, BSplineCurve3dBase } from "../bspline/BSplineCurve"; -import { BSplineSurface3d, BSplineSurface3dH } from "../bspline/BSplineSurface"; +import { BSplineSurface3d, BSplineSurface3dH, WeightStyle } from "../bspline/BSplineSurface"; import { Sphere } from "../solid/Sphere"; import { Cone } from "../solid/Cone"; import { Box } from "../solid/Box"; @@ -53,6 +53,7 @@ import { BezierCurve3d } from "../bspline/BezierCurve3d"; import { BezierCurve3dH } from "../bspline/BezierCurve3dH"; import { CurveChainWithDistanceIndex } from "../curve/CurveChainWithDistanceIndex"; import { KnotVector, BSplineWrapMode } from "../bspline/KnotVector"; +import { SolidPrimitive } from "../solid/SolidPrimitive"; /* tslint:disable:no-console */ @@ -286,6 +287,44 @@ export class Sample { return result; } + /** Return array [x,y,z,w] bspline control points for an arc in 90 degree bspline spans. + * @param points array of [x,y,z,w] + * @param center center of arc + * @param axes matrix with 0 and 90 degree axes + * @param radius0 radius mulitplier for x direction. + * @param radius90 radius multiplier for y direction. + * @param applyWeightsToXYZ + */ + public static createBsplineArc90SectionToXYZWArrays( + center: Point3d, + axes: Matrix3d, + radius0: number, + radius90: number, + applyWeightsToXYZ: boolean): number[][] { + const a = Math.sqrt(0.5); + const xyz = Point3d.create(); + Matrix3d.xyzPlusMatrixTimesCoordinates(center, axes, radius0, 0.0, 0, xyz); + const controlPoints = []; + controlPoints.push([xyz.x, xyz.y, xyz.z, 1.0]); + const cornerTrig = [1, 1, -1, -1, 1]; + const axisTrig = [1, 0, -1, 0, 1]; + for (let i = 0; i < 4; i++) { + Matrix3d.xyzPlusMatrixTimesCoordinates(center, axes, radius0 * cornerTrig[i + 1], radius90 * cornerTrig[i], 0, xyz); + controlPoints.push([xyz.x, xyz.y, xyz.z, a]); + Matrix3d.xyzPlusMatrixTimesCoordinates(center, axes, radius0 * axisTrig[i + 1], radius90 * axisTrig[i], 0, xyz); + controlPoints.push([xyz.x, xyz.y, xyz.z, 1.0]); + } + if (applyWeightsToXYZ) { + for (const xyzw of controlPoints) { + const b = xyzw[3]; + xyzw[0] *= b; + xyzw[1] *= b; + xyzw[2] *= b; + } + } + return controlPoints; + } + /** * Create both unweigthed and weighted bspline curves. * (This is the combined results from createBsplineCurves and createBspline3dHCurves) @@ -915,6 +954,53 @@ export class Sample { return result; } + /** + * @param centerA center at section A + * @param centerB center at section B + * @param radiusA radius at point A + * @param radiusB radius at point B + */ + public static createConeBsplineSurface( + centerA: Point3d, + centerB: Point3d, + radiusA: number, + radiusB: number, + numSection: number): BSplineSurface3dH | undefined { + if (numSection < 2) + numSection = 2; + const controlPoints: number[][][] = []; + const numVPole = numSection; + const q1 = 0.25; + const q2 = 0.5; + const q3 = 0.75; + + const uKnots = [0, 0, q1, q1, q2, q2, q3, q3, 1, 1]; + const vKnots = []; + const dv = 1.0 / (numSection - 1); + for (let i = 0; i < numSection; i++) { + vKnots.push(i * dv); + } + const center = Point3d.create(); + const vectorAB = Vector3d.createStartEnd(centerA, centerB); + const axes = Matrix3d.createRigidHeadsUp(vectorAB, AxisOrder.ZXY); + let r0, r90, v; + for (let iV = 0; iV < numVPole; iV++) { + v = iV * dv; + centerA.interpolate(v, centerB, center); + r0 = r90 = Geometry.interpolate(radiusA, v, radiusB); + controlPoints.push(Sample.createBsplineArc90SectionToXYZWArrays(center, axes, r0, r90, false)); + } + + const result = BSplineSurface3dH.createGrid(controlPoints, + WeightStyle.WeightsSeparateFromCoordinates, + 3, uKnots, 2, vKnots); + // if (result) { + // result.setWrappable(0, BSplineWrapMode.OpenByAddingControlPoints); + // result.setWrappable(1, BSplineWrapMode.OpenByAddingControlPoints); + // } + return result; + } + public static createWeightedXYGridBsplineSurface( numU: number, numV: number, orderU: number, orderV: number, weight00: number = 1.0, @@ -984,7 +1070,7 @@ export class Sample { Vector3d.unitX(radius), Vector3d.unitY(radius), AngleSweep.createStartEndDegrees(startDegrees, endDegrees)); - return [arc, LineSegment3d.create(arc.fractionToPoint(0.0), arc.fractionToPoint(1.0))]; + return [arc, LineSegment3d.create(arc.fractionToPoint(1.0), arc.fractionToPoint(0.0))]; } /** Return a Path structure for a segment of arc, with closure segment */ public static createCappedArcPath(radius: number, startDegrees: number, endDegrees: number): Path { @@ -1004,7 +1090,7 @@ export class Sample { Ray3d.createXYZUVW(0, 0, 0, 0, 1, 0), Ray3d.createXYZUVW(5, 0, 0, 0, 1, 0), Ray3d.createXYZUVW(-1, 0, 0, -1, 1, 0)]) { - result.push(RotationalSweep.create(base, axis, Angle.createDegrees(120.0), false) as RotationalSweep); + result.push(RotationalSweep.create(base, axis, Angle.createDegrees(45.0), false) as RotationalSweep); result.push(RotationalSweep.create(base, axis, Angle.createDegrees(150.0), true) as RotationalSweep); } @@ -1056,6 +1142,8 @@ export class Sample { const topZ = Point3d.create(0, 0, 5); const centerA = Point3d.create(1, 2, 1); const centerB = Point3d.create(2, 3, 8); + result.push(Cone.createAxisPoints(Point3d.create(0, 0, 0), Point3d.create(0, 0, 1), 0.5, 0.5, false) as Cone); + result.push(Cone.createAxisPoints(centerA, centerB, 0.5, 0.5, false) as Cone); result.push(Cone.createAxisPoints(origin, topZ, 1.0, 0.2, true) as Cone); result.push(Cone.createAxisPoints(centerA, centerB, 0.2, 0.5, false) as Cone); @@ -1066,7 +1154,7 @@ export class Sample { public static createTorusPipes(): TorusPipe[] { const result: TorusPipe[] = []; - const center = Point3d.create(1, 50, 3); + const center = Point3d.create(1, 2, 3); const frame = Matrix3d.createRotationAroundVector( Vector3d.create(1, 2, 3), Angle.createRadians(10)) as Matrix3d; @@ -1074,6 +1162,7 @@ export class Sample { const vectorY = frame.columnY(); const vectorZ = frame.columnZ(); result.push(TorusPipe.createInFrame(Transform.createIdentity(), 5.0, 0.8, Angle.create360(), false)!); + result.push(TorusPipe.createInFrame(Transform.createIdentity(), 5.0, 1.0, Angle.createDegrees(90), true)!); result.push(TorusPipe.createDgnTorusPipe(center, vectorX, vectorY, 10, 1, Angle.createDegrees(180), true)!); result.push(TorusPipe.createDgnTorusPipe(center, vectorY, vectorZ, 10, 1, Angle.createDegrees(45), true) as TorusPipe); @@ -1081,7 +1170,7 @@ export class Sample { return result; } - public static createBoxes(): Box[] { + public static createBoxes(capped: boolean = true): Box[] { const result: Box[] = []; const cornerA = Point3d.create(1, 2, 3); const aX = 3.0; @@ -1093,18 +1182,18 @@ export class Sample { Vector3d.create(0, 0, 1), Angle.createDegrees(10)) as Matrix3d; const vectorX = frame.columnX(); const vectorY = frame.columnY(); - const cornerB = Matrix3d.XYZPlusMatrixTimesCoordinates(cornerA, frame, 0, 0, h); + const cornerB = Matrix3d.xyzPlusMatrixTimesCoordinates(cornerA, frame, 0, 0, h); result.push(Box.createDgnBox(cornerA, Vector3d.unitX(), Vector3d.unitY(), - cornerB, aX, aY, aX, aY, true) as Box); + cornerB, aX, aY, aX, aY, capped) as Box); result.push(Box.createDgnBox(cornerA, Vector3d.unitX(), Vector3d.unitY(), - cornerB, aX, aY, bX, bY, true) as Box); - result.push(Box.createDgnBox(cornerA, vectorX, vectorY, cornerB, aX, aY, bX, bY, true) as Box); + cornerB, aX, aY, bX, bY, capped) as Box); + result.push(Box.createDgnBox(cornerA, vectorX, vectorY, cornerB, aX, aY, bX, bY, capped) as Box); const frameY = Matrix3d.createRotationAroundVector( Vector3d.create(0, 1, 0), Angle.createDegrees(10)) as Matrix3d; result.push(Box.createDgnBox(cornerA, frameY.columnX(), frameY.columnY(), - cornerA.plusScaled(frameY.columnZ(), h), aX, aY, bX, bY, true) as Box); + cornerA.plusScaled(frameY.columnZ(), h), aX, aY, bX, bY, capped) as Box); return result; } /** create an array of points for a rectangle with corners (x0,y0,z) and (x1,y1,z) @@ -1251,9 +1340,9 @@ export class Sample { public static createFractalDiamonConvexPattern(numRecursion: number, perpendicularFactor: number): Point3d[] { const pattern: Point2d[] = [ Point2d.create(), - Point2d.create(0.3, 0.1), - Point2d.create(0.5, 0.15), - Point2d.create(0.7, 0.1), + Point2d.create(0.3, 0.05), + Point2d.create(0.5, 0.10), + Point2d.create(0.7, 0.04), Point2d.create(1.0, 0.0), ]; const poles: Point3d[] = [ @@ -1284,6 +1373,27 @@ export class Sample { return Sample.createRecursvieFractalPolygon(poles, pattern, numRecursion, perpendicularFactor); } + public static createFractalHatReversingPattern(numRecursion: number, perpendicularFactor: number): Point3d[] { + const pattern: Point2d[] = [ + Point2d.create(), + Point2d.create(0.25, 0), + Point2d.create(0.25, 0.1), + Point2d.create(0.50, 0.1), + Point2d.create(0.50, -0.1), + Point2d.create(0.75, -0.1), + Point2d.create(0.75, 0), + Point2d.create(1.0, 0.0), + ]; + const poles: Point3d[] = [ + Point3d.create(), + Point3d.create(1, 0, 0), + Point3d.create(1, 1, 0), + Point3d.create(0, 1, 0), + Point3d.create(0, 0, 0), + ]; + return Sample.createRecursvieFractalPolygon(poles, pattern, numRecursion, perpendicularFactor); + } + public static createFractalLReversingPatterh(numRecursion: number, perpendicularFactor: number): Point3d[] { const pattern: Point2d[] = [ Point2d.create(), @@ -1308,9 +1418,9 @@ export class Sample { public static createFractalLMildConcavePatter(numRecursion: number, perpendicularFactor: number): Point3d[] { const pattern: Point2d[] = [ Point2d.create(), - Point2d.create(0.25, 0.1), + Point2d.create(0.25, 0.05), Point2d.create(0.5, 0.15), - Point2d.create(0.75, 0.1), + Point2d.create(0.75, 0.05), Point2d.create(1.0, 0.0), ]; const poles: Point3d[] = [ @@ -1318,7 +1428,7 @@ export class Sample { Point3d.create(1, 0, 0), Point3d.create(1, 1, 0), Point3d.create(2, 2, 0), - Point3d.create(2, 3, 0), + Point3d.create(1.5, 3, 0), Point3d.create(0, 3, 0), Point3d.create(), ]; @@ -1515,9 +1625,9 @@ export class Sample { if (structure === 1) { const pointA = Point3d.create(); const pointB = Point3d.create(); - allPoints.getPoint3dAt(0, pointA); + allPoints.getPoint3dAtUncheckedPointIndex(0, pointA); for (let i1 = 0; i1 + 1 < numPoints; i1++) { - allPoints.getPoint3dAt(i1, pointB); + allPoints.getPoint3dAtUncheckedPointIndex(i1, pointB); path.tryAddChild(LineSegment3d.create(pointA, pointB)); pointA.setFromPoint3d(pointB); } @@ -1599,4 +1709,101 @@ export class Sample { LineSegment3d.create(corners[2], corners[6]), LineSegment3d.create(corners[3], corners[7])); } + /** Create swept "solids" that can be capped. + * * At least one of each solid type. + * * each is within 10 of the origin all directions. + */ + public static createClosedSolidSampler(capped: boolean): SolidPrimitive[] { + const result = []; + result.push(Box.createRange(Range3d.createXYZXYZ(0, 0, 0, 3, 2, 5), capped)!); + + result.push(Cone.createAxisPoints(Point3d.create(0, 0, 0), Point3d.create(0, 0, 5), 1.0, 1.0, capped)!); + + result.push(Sphere.createCenterRadius(Point3d.create(0, 0, 0), 1.0)!); + + result.push(TorusPipe.createInFrame(Transform.createIdentity(), 3.0, 1.0, Angle.create360(), capped)!); + const arcA = Arc3d.createXY(Point3d.create(6, 1, 0), 1.0, AngleSweep.createStartEndDegrees(-90, 0)); + const point0 = arcA.fractionAndDistanceToPointOnTangent(0.0, -4); + const pointQ1 = arcA.fractionAndDistanceToPointOnTangent(1.0, 2); + const pointQ2 = arcA.fractionAndDistanceToPointOnTangent(1.0, 0.5); + const pointR1 = Point3d.create(point0.x, pointQ1.y); + const pointR2 = Point3d.create(point0.x, pointQ1.y); + const linestringQ1 = LineString3d.create(arcA.fractionToPoint(1.0), pointQ1, pointR1, point0); + const linestringQ2 = LineString3d.create(arcA.fractionToPoint(1.0), pointQ2, pointR2, point0); + const contourZ = Path.create(linestringQ1.clone()); + + const contourA = Loop.create( + LineSegment3d.create(point0, arcA.fractionToPoint(0)), + arcA.clone(), + linestringQ1.clone()); + const contourB = Loop.create( + LineSegment3d.create(point0, arcA.fractionToPoint(0)), + arcA.clone(), + linestringQ2.clone()); + contourB.tryTransformInPlace(Transform.createTranslationXYZ(1, 1, 3)); + + // const contourC = contourB.cloneTransformed(Transform.createTranslationXYZ(2, 1, 4))!; + result.push(LinearSweep.create(contourA, Vector3d.create(0, 0, 5), capped)!); + const axis = Ray3d.createXYZUVW(0, 8, 0, 1, 0, 0); + result.push(RotationalSweep.create(contourA.clone()!, axis.clone(), Angle.createDegrees(90), capped)!); + + if (!capped) + result.push(RotationalSweep.create(contourZ.clone()!, axis.clone(), Angle.createDegrees(90), false)!); + + result.push(RuledSweep.create([contourA.clone()!, contourB.clone()!], capped)!); + + const transformC = Transform.createScaleAboutPoint(Point3d.create(0, 0, 8), 0.5); + const contourC = contourB.cloneTransformed(transformC)!; + result.push(RuledSweep.create([contourA.clone()!, contourB.clone()!, contourC.clone()!], capped)!); + return result; + } + /** Create a rotational sweep with segment, arc, and linestring in its contour. + */ + public static createRotationalSweepLineSegment3dArc3dLineString3d(capped: boolean): SolidPrimitive[] { + const result = []; + const arcA = Arc3d.createXY(Point3d.create(6, 1, 0), 1.0, AngleSweep.createStartEndDegrees(-90, 0)); + const point0 = arcA.fractionAndDistanceToPointOnTangent(0.0, -4); + const pointQ1 = arcA.fractionAndDistanceToPointOnTangent(1.0, 2); + const pointR1 = Point3d.create(point0.x, pointQ1.y); + const linestringQ1 = LineString3d.create(arcA.fractionToPoint(1.0), pointQ1, pointR1, point0); + const contourZ = Path.create(linestringQ1.clone()); + const axis = Ray3d.createXYZUVW(0, 8, 0, 1, 0, 0); + result.push(RotationalSweep.create(contourZ.clone()!, axis.clone(), Angle.createDegrees(90), capped)!); + return result; + } + /** + * Create points: + * * `numRadialEdges` radially from origin to polar point (r,sweep.start) + * * `numArcEdges` along arc from (r,sweep.start) to (r,sweep.end) + * * `numRadialEdges` returning to origin. + * * optionally include closure point at origin. + * @param x0 center x + * @param y0 center y + * @param radius radius of circle. + * @param sweep start and end angles of sweep. + * @param numRadialEdges number of edges from center to arc + * @param numArcEdges number of edges along arc + * @param addCLosure true to repeat center as closure point + */ + public static createCutPie(x0: number, y0: number, radius: number, sweep: AngleSweep, numRadialEdges: number, numArcEdges: number, addClosure = false): Point3d[] { + + const points = []; + const center = Point3d.create(x0, y0); + points.push(center); + const pointA = Point3d.create(x0 + radius * Math.cos(sweep.startRadians), y0 + radius * Math.sin(sweep.startRadians)); + const pointB = Point3d.create(x0 + radius * Math.cos(sweep.endRadians), y0 + radius * Math.sin(sweep.endRadians)); + for (let i = 1; i < numRadialEdges; i++) + points.push(center.interpolate(i / numRadialEdges, pointA)); + points.push(pointA); + for (let i = 1; i < numArcEdges; i++) { + const radians = sweep.fractionToRadians(i / numArcEdges); + points.push(Point3d.create(x0 + radius * Math.cos(radians), y0 + radius * Math.sin(radians))); + } + points.push(pointB); + for (let i = 1; i < numRadialEdges; i++) + points.push(pointB.interpolate(i / numRadialEdges, center)); + if (addClosure) + points.push(center.clone()); + return points; + } } diff --git a/core/geometry/src/serialization/IModelJsonSchema.ts b/core/geometry/src/serialization/IModelJsonSchema.ts index 846f053..a9e0409 100644 --- a/core/geometry/src/serialization/IModelJsonSchema.ts +++ b/core/geometry/src/serialization/IModelJsonSchema.ts @@ -966,7 +966,7 @@ export namespace IModelJson { const axes = Reader.parseOrientation(json, true)!; if (baseOrigin && !topOrigin) - topOrigin = Matrix3d.XYZMinusMatrixTimesXYZ(baseOrigin, axes, Vector3d.create(0, 0, height)); + topOrigin = Matrix3d.xyzMinusMatrixTimesXYZ(baseOrigin, axes, Vector3d.create(0, 0, height)); if (capped !== undefined && baseX !== undefined @@ -1468,22 +1468,25 @@ export namespace IModelJson { const normals = []; const params = []; const colors = []; - const p = Point3d.create(); - for (let i = 0; pf.data.point.atPoint3dIndex(i, p); i++) - points.push(p.toJSON()); - + { + const p = Point3d.create(); + for (let i = 0; pf.data.point.getPoint3dAtCheckedPointIndex(i, p); i++) + points.push(p.toJSON()); + } if (pf.data.normal) { const numNormal = pf.data.normal.length; const normal = Vector3d.create(); for (let i = 0; i < numNormal; i++) { - pf.data.normal.atVector3dIndex(i, normal); + pf.data.normal.getVector3dAtCheckedVectorIndex(i, normal); normals.push(normal.toJSON()); } } if (pf.data.param) { - for (const value of pf.data.param) params.push(value.toJSON()); + const uv = Point2d.create(); + for (let i = 0; pf.data.param.getPoint2dAtCheckedPointIndex(i, uv); i++) + params.push(uv.toJSON()); } if (pf.data.color) { diff --git a/core/geometry/src/solid/Box.ts b/core/geometry/src/solid/Box.ts index 973f9e8..8e6e78e 100644 --- a/core/geometry/src/solid/Box.ts +++ b/core/geometry/src/solid/Box.ts @@ -161,6 +161,24 @@ export class Box extends SolidPrimitive { result.addPoint(workPoint); return result; } + /** + * @returns the 8 corners in x fastest, then y, finally z lexical order. + */ + public getCorners(): Point3d[] { + const transform = this._localToWorld; + const ax = this._baseX; + const ay = this._baseY; + const bx = this._topX; + const by = this._topY; + return [transform.multiplyXYZ(0, 0, 0), + transform.multiplyXYZ(ax, 0, 0), + transform.multiplyXYZ(0, ay, 0), + transform.multiplyXYZ(ax, ay, 0), + transform.multiplyXYZ(0, 0, 1), + transform.multiplyXYZ(bx, 0, 1), + transform.multiplyXYZ(0, by, 1), + transform.multiplyXYZ(bx, by, 1)]; + } public constantVSection(zFraction: number): CurveCollection { const ls = this.strokeConstantVSection(zFraction); @@ -192,4 +210,10 @@ export class Box extends SolidPrimitive { range.extendTransformedXYZ(boxTransform, bx, by, 1); } } + /** + * @return true if this is a closed volume. + */ + public get isClosedVolume(): boolean { + return this.capped; + } } diff --git a/core/geometry/src/solid/Cone.ts b/core/geometry/src/solid/Cone.ts index 46d3138..9c23723 100644 --- a/core/geometry/src/solid/Cone.ts +++ b/core/geometry/src/solid/Cone.ts @@ -11,7 +11,7 @@ import { Transform } from "../geometry3d/Transform"; import { Matrix3d } from "../geometry3d/Matrix3d"; import { GeometryQuery } from "../curve/GeometryQuery"; import { Geometry } from "../Geometry"; -import { GeometryHandler, UVSurface } from "../geometry3d/GeometryHandler"; +import { GeometryHandler, UVSurface, UVSurfaceIsoParametricDistance } from "../geometry3d/GeometryHandler"; import { SolidPrimitive } from "./SolidPrimitive"; import { StrokeOptions } from "../curve/StrokeOptions"; import { Loop } from "../curve/Loop"; @@ -20,6 +20,7 @@ import { Plane3dByOriginAndVectors } from "../geometry3d/Plane3dByOriginAndVecto import { Arc3d } from "../curve/Arc3d"; import { LineString3d } from "../curve/LineString3d"; +import { Vector2d } from "../geometry3d/Point2dVector2d"; /** * A cone with axis along the z axis of a (possibly skewed) local coordinate system. * @@ -28,7 +29,7 @@ import { LineString3d } from "../curve/LineString3d"; * * The stored matrix has unit vectors in the xy columns, and full-length z column. * * */ -export class Cone extends SolidPrimitive implements UVSurface { +export class Cone extends SolidPrimitive implements UVSurface, UVSurfaceIsoParametricDistance { private _localToWorld: Transform; // Transform from local to global. private _radiusA: number; // nominal radius at z=0. skewed axes may make it an ellipse private _radiusB: number; // radius at z=1. skewed axes may make it an ellipse @@ -146,27 +147,40 @@ export class Cone extends SolidPrimitive implements UVSurface { const fractions = result.fractions; // possibly undefined !!! const derivatives = result.packedDerivatives; // possibly undefined !!! const uvParams = result.packedUVParams; // possibly undefined !! + const surfaceNormals = result.packedSurfaceNormals; const xyz = Point3d.create(); - const uvw = Vector3d.create(); + const dXdu = Vector3d.create(); + const dXdv = Vector3d.create(); + const normal = Vector3d.create(); const transform = this._localToWorld; - let rc, rs; + let rc, rs, cc, ss; for (let i = 0; i <= strokeCount; i++) { if (i * 2 <= strokeCount) radians = i * deltaRadians; else radians = (i - strokeCount) * deltaRadians; - rc = r * Math.cos(radians); - rs = r * Math.sin(radians); + cc = Math.cos(radians); + ss = Math.sin(radians); + rc = r * cc; + rs = r * ss; + transform.multiplyXYZ(rc, rs, v, xyz); result.addPoint(xyz); if (fractions) fractions.push(i / strokeCount); if (derivatives) { - transform.matrix.multiplyXYZ(-rs * twoPi, rc * twoPi, 0.0, uvw); - derivatives.push(uvw); + transform.matrix.multiplyXYZ(-rs * twoPi, rc * twoPi, 0.0, dXdu); + derivatives.push(dXdu); + } + if (surfaceNormals) { + // the along-hoop vector does not need to be scaled by radius -- just need the direction for a cross product. + transform.matrix.multiplyXYZ(-ss, cc, 0.0, dXdu); + transform.matrix.multiplyXYZ(0, 0, 1, dXdv); + dXdu.unitCrossProduct(dXdv, normal); + surfaceNormals.push(normal); } if (uvParams) { - uvParams.pushXY(rc, rs); + uvParams.pushXY(i / strokeCount, v); } } return result; @@ -190,14 +204,14 @@ export class Cone extends SolidPrimitive implements UVSurface { arc1.extendRange(range, transform); } - public UVFractionToPoint(uFraction: number, vFraction: number, result?: Point3d): Point3d { + public uvFractionToPoint(uFraction: number, vFraction: number, result?: Point3d): Point3d { const theta = uFraction * Math.PI * 2.0; const r = Geometry.interpolate(this._radiusA, vFraction, this._radiusB); const cosTheta = Math.cos(theta); const sinTheta = Math.sin(theta); return this._localToWorld.multiplyXYZ(r * cosTheta, r * sinTheta, vFraction, result); } - public UVFractionToPointAndTangents(uFraction: number, vFraction: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors { + public uvFractionToPointAndTangents(uFraction: number, vFraction: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors { const theta = uFraction * Math.PI * 2.0; const r = Geometry.interpolate(this._radiusA, vFraction, this._radiusB); const drdv = this._radiusB - this._radiusA; @@ -210,4 +224,27 @@ export class Cone extends SolidPrimitive implements UVSurface { this._localToWorld.multiplyVectorXYZ(drdv * cosTheta, drdv * sinTheta, 1.0), result); } + /** + * @return true if this is a closed volume. + */ + public get isClosedVolume(): boolean { + return this.capped; + } + /** + * Directional distance query + * * u direction is around longitude circle at maximum distance from axis. + * * v direction is on a line of longitude between the latitude limits. + */ + public maxIsoParametricDistance(): Vector2d { + const vectorX = this._localToWorld.matrix.columnX(); + const vectorY = this._localToWorld.matrix.columnY(); + const columnZ = this._localToWorld.matrix.columnZ(); + + const xyNormal = vectorX.unitCrossProduct(vectorY)!; + const hZ = xyNormal.dotProduct(columnZ); + const zSkewVector = columnZ.plusScaled(xyNormal, hZ); + const zSkewDistance = zSkewVector.magnitudeXY(); + return Vector2d.create(Math.PI * 2 * Math.max(this._radiusA, this._radiusB), + Geometry.hypotenuseXY(Math.abs(this._radiusB - this._radiusA) + zSkewDistance, hZ)); + } } diff --git a/core/geometry/src/solid/LinearSweep.ts b/core/geometry/src/solid/LinearSweep.ts index 7c44cf2..05fb785 100644 --- a/core/geometry/src/solid/LinearSweep.ts +++ b/core/geometry/src/solid/LinearSweep.ts @@ -54,6 +54,7 @@ export class LinearSweep extends SolidPrimitive { public static createZSweep(xyPoints: XAndY[], z: number, zSweep: number, capped: boolean): LinearSweep | undefined { const xyz = LineString3d.createXY(xyPoints, z, capped); if (capped) { + xyz.addClosurePoint(); const area = PolygonOps.areaXY(xyz.points); if (area * zSweep < 0.0) xyz.points.reverse(); @@ -128,4 +129,10 @@ export class LinearSweep extends SolidPrimitive { } range.extendRange(contourRange); } + /** + * @return true if this is a closed volume. + */ + public get isClosedVolume(): boolean { + return this.capped && this._contour.curves.isAnyRegionType; + } } diff --git a/core/geometry/src/solid/RotationalSweep.ts b/core/geometry/src/solid/RotationalSweep.ts index 11ba724..8bf18bf 100644 --- a/core/geometry/src/solid/RotationalSweep.ts +++ b/core/geometry/src/solid/RotationalSweep.ts @@ -124,4 +124,10 @@ export class RotationalSweep extends SolidPrimitive { strokes.extendRange(range, this.getFractionalRotationTransform(i / numStep, stepTransform)); } } + /** + * @return true if this is a closed volume. + */ + public get isClosedVolume(): boolean { + return this.capped || this._sweepAngle.isFullCircle; + } } diff --git a/core/geometry/src/solid/RuledSweep.ts b/core/geometry/src/solid/RuledSweep.ts index a098298..a3c6145 100644 --- a/core/geometry/src/solid/RuledSweep.ts +++ b/core/geometry/src/solid/RuledSweep.ts @@ -118,7 +118,7 @@ export class RuledSweep extends SolidPrimitive { const localFraction = Geometry.clampToStartEnd(q - section0, 0, 1); return RuledSweep.mutatePartners(this._contours[section0].curves, this._contours[section1].curves, (primitive0: CurvePrimitive, primitive1: CurvePrimitive): CurvePrimitive | undefined => { - const newPrimitive = ConstructCurveBetweenCurves.InterpolateBetween(primitive0, localFraction, primitive1); + const newPrimitive = ConstructCurveBetweenCurves.interpolateBetween(primitive0, localFraction, primitive1); if (newPrimitive instanceof CurvePrimitive) return newPrimitive; return undefined; }); @@ -177,5 +177,12 @@ export class RuledSweep extends SolidPrimitive { } return undefined; } + /** + * @return true if this is a closed volume. + */ + public get isClosedVolume(): boolean { + const n = this._contours.length; + return n > 1 && (this.capped || this._contours[0].isAlmostEqual(this._contours[n - 1])); + } } diff --git a/core/geometry/src/solid/SolidPrimitive.ts b/core/geometry/src/solid/SolidPrimitive.ts index f152fc6..dca635c 100644 --- a/core/geometry/src/solid/SolidPrimitive.ts +++ b/core/geometry/src/solid/SolidPrimitive.ts @@ -26,4 +26,12 @@ export abstract class SolidPrimitive extends GeometryQuery { * * The particulars of origin and orientation are specific to each SolidPrimitive type. */ public abstract getConstructiveFrame(): Transform | undefined; + /** + * @return true if this is a closed volume. + * * LinearSweep, Box, Cone only depend on capped. + * * Sphere affected by capped and latitude sweep + * * TorusPipe and RotationalSweep affected by capped and sweep + * * RuledSweep is affected by capped and match of first, last contour + */ + public abstract get isClosedVolume(): boolean; } diff --git a/core/geometry/src/solid/Sphere.ts b/core/geometry/src/solid/Sphere.ts index 684d5b2..4d3212a 100644 --- a/core/geometry/src/solid/Sphere.ts +++ b/core/geometry/src/solid/Sphere.ts @@ -22,6 +22,7 @@ import { CurveCollection } from "../curve/CurveCollection"; import { Arc3d } from "../curve/Arc3d"; import { LineString3d } from "../curve/LineString3d"; import { Plane3dByOriginAndVectors } from "../geometry3d/Plane3dByOriginAndVectors"; +import { Vector2d } from "../geometry3d/Point2dVector2d"; /** * A Sphere is * @@ -68,7 +69,8 @@ export class Sphere extends SolidPrimitive implements UVSurface { public getConstructiveFrame(): Transform | undefined { return this._localToWorld.cloneRigid(); } - + /** Return the latitude sweep as fraction of south pole to north pole. */ + public get latitudeSweepFraction(): number { return this._latitudeSweep.sweepRadians / Math.PI; } public static createCenterRadius(center: Point3d, radius: number, latitudeSweep?: AngleSweep): Sphere { const localToWorld = Transform.createOriginAndMatrix(center, Matrix3d.createUniformScale(radius)); return new Sphere(localToWorld, @@ -139,30 +141,56 @@ export class Sphere extends SolidPrimitive implements UVSurface { * @param v fractional position along the cone axis * @param strokes stroke count or options. */ - public strokeConstantVSection(v: number, strokes: number | StrokeOptions | undefined): LineString3d { + public strokeConstantVSection(v: number, fixedStrokeCount: number | undefined, + options?: StrokeOptions): LineString3d { let strokeCount = 16; - if (strokes === undefined) { - // accept the default above. - } else if (Number.isFinite(strokes as number)) { - strokeCount = strokes as number; - } else if (strokes instanceof StrokeOptions) { - strokeCount = strokes.applyTolerancesToArc(Geometry.maxXY(this._localToWorld.matrix.columnXMagnitude(), this._localToWorld.matrix.columnYMagnitude())); + if (fixedStrokeCount !== undefined && Number.isFinite(fixedStrokeCount)) { + strokeCount = fixedStrokeCount as number; + } else if (options instanceof StrokeOptions) { + strokeCount = options.applyTolerancesToArc(Geometry.maxXY(this._localToWorld.matrix.columnXMagnitude(), this._localToWorld.matrix.columnYMagnitude())); } strokeCount = Geometry.clampToStartEnd(strokeCount, 4, 64); const transform = this._localToWorld; const phi = this.vFractionToRadians(v); const c1 = Math.cos(phi); const s1 = Math.sin(phi); - const result = LineString3d.create(); + let c0, s0; + const result = LineString3d.createForStrokes(fixedStrokeCount, options); const deltaRadians = Math.PI * 2.0 / strokeCount; + const fractions = result.fractions; // possibly undefined !!! + const derivatives = result.packedDerivatives; // possibly undefined !!! + const uvParams = result.packedUVParams; // possibly undefined !! + const surfaceNormals = result.packedSurfaceNormals; + const dXdu = Vector3d.create(); + const dXdv = Vector3d.create(); + const normal = Vector3d.create(); let radians = 0; for (let i = 0; i <= strokeCount; i++) { if (i * 2 <= strokeCount) radians = i * deltaRadians; else radians = (i - strokeCount) * deltaRadians; - const xyz = transform.multiplyXYZ(c1 * Math.cos(radians), c1 * Math.sin(radians), s1); + c0 = Math.cos(radians); + s0 = Math.sin(radians); + const xyz = transform.multiplyXYZ(c1 * c0, c1 * s0, s1); result.addPoint(xyz); + + if (fractions) + fractions.push(i / strokeCount); + + if (derivatives) { + transform.matrix.multiplyXYZ(-c1 * s0, c1 * c0, 0.0, dXdu); + derivatives.push(dXdu); + } + if (uvParams) { + uvParams.pushXY(i / strokeCount, v); + } + if (surfaceNormals) { + transform.matrix.multiplyXYZ(-s0, c0, 0, dXdu); + transform.matrix.multiplyXYZ(-s1 * c0, -s1 * s0, c1, dXdv); + dXdu.unitCrossProduct(dXdv, normal); + surfaceNormals.push(normal); + } } return result; } @@ -206,7 +234,7 @@ export class Sphere extends SolidPrimitive implements UVSurface { * @param uFraction fractional position in minor (phi) * @param vFraction fractional position on major (theta) arc */ - public UVFractionToPoint(uFraction: number, vFraction: number, result?: Point3d): Point3d { + public uvFractionToPoint(uFraction: number, vFraction: number, result?: Point3d): Point3d { // sphere with radius 1 . . . const thetaRadians = this.uFractionToRadians(uFraction); const phiRadians = this.vFractionToRadians(vFraction); @@ -220,7 +248,7 @@ export class Sphere extends SolidPrimitive implements UVSurface { * @param u fractional position in minor (phi) * @param v fractional position on major (theta) arc */ - public UVFractionToPointAndTangents(uFraction: number, vFraction: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors { + public uvFractionToPointAndTangents(uFraction: number, vFraction: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors { const thetaRadians = this.uFractionToRadians(uFraction); const phiRadians = this.vFractionToRadians(vFraction); const fTheta = Math.PI * 2.0; @@ -231,8 +259,35 @@ export class Sphere extends SolidPrimitive implements UVSurface { const cosPhi = Math.cos(phiRadians); return Plane3dByOriginAndVectors.createOriginAndVectors( this._localToWorld.multiplyXYZ(cosTheta * cosPhi, sinTheta * cosPhi, sinPhi), - this._localToWorld.multiplyVectorXYZ(-fTheta * sinTheta * cosPhi, fTheta * cosTheta * cosPhi, 0), - this._localToWorld.multiplyVectorXYZ(-fPhi * cosTheta * sinPhi, -fPhi * sinTheta * sinPhi, fPhi * cosPhi), + this._localToWorld.matrix.multiplyXYZ(-fTheta * sinTheta, fTheta * cosTheta, 0), // !!! note cosTheta term is omitted -- scale is wrong, but remains non-zero at poles. + this._localToWorld.matrix.multiplyXYZ(-fPhi * cosTheta * sinPhi, -fPhi * sinTheta * sinPhi, fPhi * cosPhi), result); } + /** + * * A sphere is can be closed two ways: + * * full sphere (no caps needed for closure) + * * incomplete but with caps + * @return true if this is a closed volume. + */ + public get isClosedVolume(): boolean { + return this.capped || this._latitudeSweep.isFullLatitudeSweep; + } + /** + * Directional distance query + * * u direction is around longitude circle at maximum distance from axis. + * * v direction is on a line of longitude between the latitude limits. + */ + public maxIsoParametricDistance(): Vector2d { + // approximate radius at equator .. if elliptic, this is not exact . . . + const rX = this._localToWorld.matrix.columnXMagnitude(); + const rY = this._localToWorld.matrix.columnYMagnitude(); + const rZ = this._localToWorld.matrix.columnZMagnitude(); + const rMaxU = Math.max(rX, rY); + let dMaxU = Math.PI * 2.0 * rMaxU; + if (!this._latitudeSweep.isRadiansInSweep(0.0)) + dMaxU *= Math.max(Math.cos(Math.abs(this._latitudeSweep.startRadians)), Math.cos(Math.abs(this._latitudeSweep.endRadians))); + const dMaxV = Math.max(rMaxU, rZ) * Math.abs(this._latitudeSweep.sweepRadians); + + return Vector2d.create(dMaxU, dMaxV); + } } diff --git a/core/geometry/src/solid/SweepContour.ts b/core/geometry/src/solid/SweepContour.ts index faeaed7..90234fa 100644 --- a/core/geometry/src/solid/SweepContour.ts +++ b/core/geometry/src/solid/SweepContour.ts @@ -108,7 +108,8 @@ export class SweepContour { } else if (this.curves instanceof ParityRegion) { this._xyStrokes = this.curves.cloneStroked(options); if (this._xyStrokes instanceof (ParityRegion)) { - this._xyStrokes.tryTransformInPlace(this.localToWorld); + const worldToLocal = this.localToWorld.inverse()!; + this._xyStrokes.tryTransformInPlace(worldToLocal); const strokes = []; for (const childLoop of this._xyStrokes.children) { const loopCurves = childLoop.children; @@ -128,6 +129,13 @@ export class SweepContour { } } } + /** delete existing facets. + * * This protects against PolyfaceBuilder reusing facets constructed with different options settings. + */ + public purgeFacets() { + this._facets = undefined; + } + /** Emit facets to a builder. * This method may cache and reuse facets over multiple calls. */ diff --git a/core/geometry/src/solid/TorusPipe.ts b/core/geometry/src/solid/TorusPipe.ts index bedba8d..f09f348 100644 --- a/core/geometry/src/solid/TorusPipe.ts +++ b/core/geometry/src/solid/TorusPipe.ts @@ -13,13 +13,14 @@ import { GeometryQuery } from "../curve/GeometryQuery"; import { Geometry } from "../Geometry"; import { AngleSweep } from "../geometry3d/AngleSweep"; import { Angle } from "../geometry3d/Angle"; -import { GeometryHandler, UVSurface } from "../geometry3d/GeometryHandler"; +import { GeometryHandler, UVSurface, UVSurfaceIsoParametricDistance } from "../geometry3d/GeometryHandler"; import { SolidPrimitive } from "./SolidPrimitive"; import { Loop } from "../curve/Loop"; import { Path } from "../curve/Path"; import { CurveCollection } from "../curve/CurveCollection"; import { Arc3d } from "../curve/Arc3d"; import { Plane3dByOriginAndVectors } from "../geometry3d/Plane3dByOriginAndVectors"; +import { Vector2d } from "../geometry3d/Point2dVector2d"; /** * the stored form of the torus pipe is oriented for positive volume: * @@ -27,7 +28,7 @@ import { Plane3dByOriginAndVectors } from "../geometry3d/Plane3dByOriginAndVecto * * The sweep is positive * * The coordinate system has positive determinant. */ -export class TorusPipe extends SolidPrimitive implements UVSurface { +export class TorusPipe extends SolidPrimitive implements UVSurface, UVSurfaceIsoParametricDistance { private _localToWorld: Transform; private _radiusA: number; // radius of (large) circle in xy plane private _radiusB: number; // radius of (small) circle in xz plane. @@ -125,6 +126,9 @@ export class TorusPipe extends SolidPrimitive implements UVSurface { } return false; } + /** Return the angle (in radians) for given fractional position around the major hoop. + */ + public vFractionToRadians(v: number): number { return this._sweep.radians * v; } public dispatchToGeometryHandler(handler: GeometryHandler): any { return handler.handleTorusPipe(this); } @@ -133,8 +137,8 @@ export class TorusPipe extends SolidPrimitive implements UVSurface { * @returns Return the Arc3d section at vFraction. For the TorusPipe, this is a minor circle. * @param vFraction fractional position along the sweep direction */ - public constantVSection(vFraction: number): CurveCollection | undefined { - const thetaRadians = this._sweep.radians * vFraction; + public constantVSection(v: number): CurveCollection | undefined { + const thetaRadians = this.vFractionToRadians(v); const c0 = Math.cos(thetaRadians); const s0 = Math.sin(thetaRadians); const majorRadius = this.getMajorRadius(); @@ -214,7 +218,7 @@ export class TorusPipe extends SolidPrimitive implements UVSurface { * @param u fractional position in minor (phi) * @param v fractional position on major (theta) arc */ - public UVFractionToPoint(u: number, v: number, result?: Point3d): Point3d { + public uvFractionToPoint(u: number, v: number, result?: Point3d): Point3d { const thetaRadians = v * this._sweep.radians; const phiRadians = u * Math.PI * 2.0; const cosTheta = Math.cos(thetaRadians); @@ -227,7 +231,7 @@ export class TorusPipe extends SolidPrimitive implements UVSurface { * @param u fractional position in minor (phi) * @param v fractional position on major (theta) arc */ - public UVFractionToPointAndTangents(u: number, v: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors { + public uvFractionToPointAndTangents(u: number, v: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors { const thetaRadians = v * this._sweep.radians; const phiRadians = u * Math.PI * 2.0; const fTheta = this._sweep.radians; @@ -246,5 +250,21 @@ export class TorusPipe extends SolidPrimitive implements UVSurface { this._localToWorld.multiplyVectorXYZ(-rxy * sinTheta * fTheta, rxy * cosTheta * fTheta, 0), result); } + /** + * Directional distance query + * * u direction is around the (full) minor hoop + * * v direction is around the outer radius, sum of (absolute values of) major and minor radii. + */ + public maxIsoParametricDistance(): Vector2d { + const a = Math.abs(this.getMajorRadius()); + const b = Math.abs(this.getMinorRadius()); + return Vector2d.create(b * Math.PI * 2.0, (a + b) * this._sweep.radians); + } + /** + * @return true if this is a closed volume. + */ + public get isClosedVolume(): boolean { + return this.capped || this._sweep.isFullCircle; + } } diff --git a/core/geometry/src/test/Checker.ts b/core/geometry/src/test/Checker.ts index 1e3072f..a5ce2b8 100644 --- a/core/geometry/src/test/Checker.ts +++ b/core/geometry/src/test/Checker.ts @@ -140,7 +140,7 @@ export class Checker { if (dataA && dataB) { let numError = 0; for (let i = 0; i < dataA.length; i++) { - if (!Geometry.isSameCoordinate(dataA[i], dataB.at(i))) + if (!Geometry.isSameCoordinate(dataA[i], dataB.atUncheckedIndex(i))) numError++; } if (numError !== 0) @@ -159,7 +159,7 @@ export class Checker { if (dataA && dataB) { let numError = 0; for (let i = 0; i < dataA.length; i++) { - if (!Geometry.isSameCoordinate(dataA.at(i), dataB.at(i))) + if (!Geometry.isSameCoordinate(dataA.atUncheckedIndex(i), dataB.atUncheckedIndex(i))) numError++; } if (numError !== 0) @@ -362,7 +362,7 @@ export class Checker { // return true if dataA is strictly before dataB as a signed toleranced coordinate value. public testContainsCoordinate(dataA: GrowableFloat64Array, dataB: number, ...params: any[]): boolean { for (let i = 0; i < dataA.length; i++) - if (Geometry.isSameCoordinate(dataA.at(i), dataB)) { + if (Geometry.isSameCoordinate(dataA.atUncheckedIndex(i), dataB)) { return this.announceOK(); } return this.announceError("Expect containsCoordinate", dataA, dataB, params); diff --git a/core/geometry/src/test/GeometryCoreTestIO.ts b/core/geometry/src/test/GeometryCoreTestIO.ts index b2a33a6..e11a52a 100644 --- a/core/geometry/src/test/GeometryCoreTestIO.ts +++ b/core/geometry/src/test/GeometryCoreTestIO.ts @@ -9,6 +9,8 @@ import * as fs from "fs"; import { IModelJson } from "../serialization/IModelJsonSchema"; import { Arc3d } from "../curve/Arc3d"; import { Point3d } from "../geometry3d/Point3dVector3d"; +/* tslint:disable:no-console */ + // Methods (called from other files in the test suite) for doing I/O of tests files. export class GeometryCoreTestIO { public static outputRootDirectory = "./src/test/output"; @@ -19,9 +21,11 @@ export class GeometryCoreTestIO { if (!fs.existsSync(path)) fs.mkdirSync(path); } - const filename = path + "/" + fileName + ".imjs"; + const fullPath = path + "/" + fileName + ".imjs"; + // console.log("saveGeometry:: " + fullPath); + const imjs = IModelJson.Writer.toIModelJson(geometry); - fs.writeFileSync(filename, prettyPrint(imjs)); + fs.writeFileSync(fullPath, prettyPrint(imjs)); } public static captureGeometry(collection: GeometryQuery[], newGeometry: GeometryQuery, dx: number = 0, dy: number = 0, dz: number = 0) { if (newGeometry) { diff --git a/core/geometry/src/test/BSplineSurface.test.ts b/core/geometry/src/test/bspline/BSplineSurface.test.ts similarity index 83% rename from core/geometry/src/test/BSplineSurface.test.ts rename to core/geometry/src/test/bspline/BSplineSurface.test.ts index 66dc1f1..1ce24a1 100644 --- a/core/geometry/src/test/BSplineSurface.test.ts +++ b/core/geometry/src/test/bspline/BSplineSurface.test.ts @@ -5,17 +5,17 @@ // import { Point3d } from "../PointVector"; // import { BSplineSurface3d } from "../BSplineSurface"; -import { Sample } from "../serialization/GeometrySamples"; -import { Checker } from "./Checker"; -import { Geometry } from "../Geometry"; -import { Point3d } from "../geometry3d/Point3dVector3d"; -import { Range3d } from "../geometry3d/Range"; -import { Transform } from "../geometry3d/Transform"; -import { BSplineSurface3dQuery, BSplineSurface3dH } from "../bspline/BSplineSurface"; +import { Sample } from "../../serialization/GeometrySamples"; +import { Checker } from "../Checker"; +import { Geometry } from "../../Geometry"; +import { Point3d } from "../../geometry3d/Point3dVector3d"; +import { Range3d } from "../../geometry3d/Range"; +import { Transform } from "../../geometry3d/Transform"; +import { BSplineSurface3dQuery, BSplineSurface3dH } from "../../bspline/BSplineSurface"; import { expect } from "chai"; -import { Plane3dByOriginAndUnitNormal } from "../geometry3d/Plane3dByOriginAndUnitNormal"; -import { GeometryCoreTestIO } from "./GeometryCoreTestIO"; -import { BSplineWrapMode } from "../bspline/KnotVector"; +import { Plane3dByOriginAndUnitNormal } from "../../geometry3d/Plane3dByOriginAndUnitNormal"; +import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; +import { BSplineWrapMode } from "../../bspline/KnotVector"; /* tslint:disable:no-console */ function testBasisValues(ck: Checker, data: Float64Array, expectedValue: number = 1) { let s = 0.0; for (const a of data) s += a; @@ -188,4 +188,29 @@ describe("BSplineSurface", () => { expect(ck.getNumErrors()).equals(0); }); + it("Cones", () => { + const ck = new Checker(); + const allGeometry = []; + let dx = 0.0; + const dy = 0.0; + for (const bsurf of [Sample.createConeBsplineSurface( + Point3d.create(0, 0, 0), + Point3d.create(0, 0, 1), + 4.0, 1.0, 2), + Sample.createConeBsplineSurface( + Point3d.create(0, 0, 0), + Point3d.create(1, 3, 1), + 4.0, 1.0, + 3)]) { + if (ck.testPointer(bsurf) && bsurf) { + bsurf.tryTranslateInPlace(dx, dy); + allGeometry.push(bsurf); + dx += 10.0; + } + } + GeometryCoreTestIO.saveGeometry(allGeometry, "BSplineSurface", "createCone"); + ck.checkpoint("BSplineSurface.Wrapped"); + expect(ck.getNumErrors()).equals(0); + }); + }); diff --git a/core/geometry/src/test/BezierCurve.test.ts b/core/geometry/src/test/bspline/BezierCurve.test.ts similarity index 90% rename from core/geometry/src/test/BezierCurve.test.ts rename to core/geometry/src/test/bspline/BezierCurve.test.ts index 5a808f5..861ea6b 100644 --- a/core/geometry/src/test/BezierCurve.test.ts +++ b/core/geometry/src/test/bspline/BezierCurve.test.ts @@ -2,21 +2,21 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Point3d } from "../geometry3d/Point3dVector3d"; -import { Checker } from "./Checker"; +import { Point3d } from "../../geometry3d/Point3dVector3d"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { BezierCurve3d } from "../bspline/BezierCurve3d"; -import { Point4d } from "../geometry4d/Point4d"; -import { BezierCurve3dH } from "../bspline/BezierCurve3dH"; -import { Bezier1dNd } from "../bspline/Bezier1dNd"; -import { Point2d } from "../geometry3d/Point2dVector2d"; -import { KnotVector } from "../bspline/KnotVector"; -import { GeometryCoreTestIO } from "./GeometryCoreTestIO"; -import { GeometryQuery } from "../curve/GeometryQuery"; -import { BSplineCurve3d } from "../bspline/BSplineCurve"; -import { LineString3d } from "../curve/LineString3d"; -import { LineSegment3d } from "../curve/LineSegment3d"; -import { Plane3dByOriginAndUnitNormal } from "../geometry3d/Plane3dByOriginAndUnitNormal"; +import { BezierCurve3d } from "../../bspline/BezierCurve3d"; +import { Point4d } from "../../geometry4d/Point4d"; +import { BezierCurve3dH } from "../../bspline/BezierCurve3dH"; +import { Bezier1dNd } from "../../bspline/Bezier1dNd"; +import { Point2d } from "../../geometry3d/Point2dVector2d"; +import { KnotVector } from "../../bspline/KnotVector"; +import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; +import { GeometryQuery } from "../../curve/GeometryQuery"; +import { BSplineCurve3d } from "../../bspline/BSplineCurve"; +import { LineString3d } from "../../curve/LineString3d"; +import { LineSegment3d } from "../../curve/LineSegment3d"; +import { Plane3dByOriginAndUnitNormal } from "../../geometry3d/Plane3dByOriginAndUnitNormal"; function exercise1dNdBase(ck: Checker, curve: Bezier1dNd) { ck.testLE(1, curve.order, "Bezier1dNd has nontrivial order"); diff --git a/core/geometry/src/test/BsplineCurve.test.ts b/core/geometry/src/test/bspline/BsplineCurve.test.ts similarity index 91% rename from core/geometry/src/test/BsplineCurve.test.ts rename to core/geometry/src/test/bspline/BsplineCurve.test.ts index 02670c5..efc9f4c 100644 --- a/core/geometry/src/test/BsplineCurve.test.ts +++ b/core/geometry/src/test/bspline/BsplineCurve.test.ts @@ -2,33 +2,33 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Point3d } from "../geometry3d/Point3dVector3d"; -import { Geometry } from "../Geometry"; -import { AngleSweep } from "../geometry3d/AngleSweep"; -import { Angle } from "../geometry3d/Angle"; -import { Checker } from "./Checker"; +import { Point3d } from "../../geometry3d/Point3dVector3d"; +import { Geometry } from "../../Geometry"; +import { AngleSweep } from "../../geometry3d/AngleSweep"; +import { Angle } from "../../geometry3d/Angle"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { KnotVector, BSplineWrapMode } from "../bspline/KnotVector"; -import { BSplineCurve3d } from "../bspline/BSplineCurve"; -import { BezierCurveBase } from "../bspline/BezierCurveBase"; -import { BezierCurve3d } from "../bspline/BezierCurve3d"; -import { GeometryQuery } from "../curve/GeometryQuery"; -import { GeometryCoreTestIO } from "./GeometryCoreTestIO"; -import { LineString3d } from "../curve/LineString3d"; -import { Transform } from "../geometry3d/Transform"; -import { StrokeOptions } from "../curve/StrokeOptions"; -import { BSplineCurve3dH } from "../bspline/BSplineCurve3dH"; -import { Sample } from "../serialization/GeometrySamples"; -import { CurvePrimitive } from "../curve/CurvePrimitive"; -import { Path } from "../curve/Path"; +import { KnotVector, BSplineWrapMode } from "../../bspline/KnotVector"; +import { BSplineCurve3d } from "../../bspline/BSplineCurve"; +import { BezierCurveBase } from "../../bspline/BezierCurveBase"; +import { BezierCurve3d } from "../../bspline/BezierCurve3d"; +import { GeometryQuery } from "../../curve/GeometryQuery"; +import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; +import { LineString3d } from "../../curve/LineString3d"; +import { Transform } from "../../geometry3d/Transform"; +import { StrokeOptions } from "../../curve/StrokeOptions"; +import { BSplineCurve3dH } from "../../bspline/BSplineCurve3dH"; +import { Sample } from "../../serialization/GeometrySamples"; +import { CurvePrimitive } from "../../curve/CurvePrimitive"; +import { Path } from "../../curve/Path"; // import { prettyPrint } from "./testFunctions"; -import { CurveLocationDetail } from "../curve/CurveLocationDetail"; -import { Plane3dByOriginAndUnitNormal } from "../geometry3d/Plane3dByOriginAndUnitNormal"; -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { LineSegment3d } from "../curve/LineSegment3d"; -import { Range3d } from "../geometry3d/Range"; -import { Point4d } from "../geometry4d/Point4d"; -import { IModelJson } from "../serialization/IModelJsonSchema"; +import { CurveLocationDetail } from "../../curve/CurveLocationDetail"; +import { Plane3dByOriginAndUnitNormal } from "../../geometry3d/Plane3dByOriginAndUnitNormal"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { LineSegment3d } from "../../curve/LineSegment3d"; +import { Range3d } from "../../geometry3d/Range"; +import { Point4d } from "../../geometry4d/Point4d"; +import { IModelJson } from "../../serialization/IModelJsonSchema"; /** return knots [0,0,0, step, 2*step, ... N,N,N] * where there are: * * (order-1) leading and trailing clamp values. diff --git a/core/geometry/src/test/ConvexClipPlaneSet.test.ts b/core/geometry/src/test/bspline/ConvexClipPlaneSet.test.ts similarity index 82% rename from core/geometry/src/test/ConvexClipPlaneSet.test.ts rename to core/geometry/src/test/bspline/ConvexClipPlaneSet.test.ts index 0622c23..78dc118 100644 --- a/core/geometry/src/test/ConvexClipPlaneSet.test.ts +++ b/core/geometry/src/test/bspline/ConvexClipPlaneSet.test.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; -import { Checker } from "./Checker"; -import { Geometry } from "../Geometry"; -import { Angle } from "../geometry3d/Angle"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { Transform } from "../geometry3d/Transform"; -import { LineSegment3d } from "../curve/LineSegment3d"; -import { ConvexClipPlaneSet } from "../clipping/ConvexClipPlaneSet"; +import { Checker } from "../Checker"; +import { Geometry } from "../../Geometry"; +import { Angle } from "../../geometry3d/Angle"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { Transform } from "../../geometry3d/Transform"; +import { LineSegment3d } from "../../curve/LineSegment3d"; +import { ConvexClipPlaneSet } from "../../clipping/ConvexClipPlaneSet"; /* tslint:disable:no-console */ describe("ConvexClipPlaneSet", () => { diff --git a/core/geometry/src/test/ConvexPolygon2d.test.ts b/core/geometry/src/test/bspline/ConvexPolygon2d.test.ts similarity index 93% rename from core/geometry/src/test/ConvexPolygon2d.test.ts rename to core/geometry/src/test/bspline/ConvexPolygon2d.test.ts index e892259..4ed80eb 100644 --- a/core/geometry/src/test/ConvexPolygon2d.test.ts +++ b/core/geometry/src/test/bspline/ConvexPolygon2d.test.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; -import { Checker } from "./Checker"; -import { Geometry } from "../Geometry"; -import { Point2d, Vector2d } from "../geometry3d/Point2dVector2d"; -import { Ray2d, ConvexPolygon2d } from "../numerics/ConvexPolygon2d"; -import { PolygonOps } from "../geometry3d/PointHelpers"; +import { Checker } from "../Checker"; +import { Geometry } from "../../Geometry"; +import { Point2d, Vector2d } from "../../geometry3d/Point2dVector2d"; +import { Ray2d, ConvexPolygon2d } from "../../numerics/ConvexPolygon2d"; +import { PolygonOps } from "../../geometry3d/PointHelpers"; /* tslint:disable:no-console no-trailing-whitespace */ @@ -229,8 +229,8 @@ describe("ConvexPolygon2d", () => { it("Ray2d", () => { const ck = new Checker(); const ray0 = Ray2d.createOriginAndDirection(Point2d.create(2, 3), Vector2d.create(1, 4)); - const perp0 = ray0.CCWPerpendicularRay(); - const perp1 = ray0.CWPerpendicularRay(); + const perp0 = ray0.ccwPerpendicularRay(); + const perp1 = ray0.cwPerpendicularRay(); ck.testCoordinate(ray0.direction.magnitude(), perp0.direction.magnitude()); ck.testCoordinate(ray0.direction.magnitude(), perp1.direction.magnitude()); ck.testPerpendicular2d(perp0.direction, ray0.direction, "CCW rotate"); diff --git a/core/geometry/src/test/AlternatingConvexClipTree.test.ts b/core/geometry/src/test/clipping/AlternatingConvexClipTree.test.ts similarity index 91% rename from core/geometry/src/test/AlternatingConvexClipTree.test.ts rename to core/geometry/src/test/clipping/AlternatingConvexClipTree.test.ts index 4a06bf4..6703611 100644 --- a/core/geometry/src/test/AlternatingConvexClipTree.test.ts +++ b/core/geometry/src/test/clipping/AlternatingConvexClipTree.test.ts @@ -4,19 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; -import { Checker, UsageSums, SaveAndRestoreCheckTransform } from "./Checker"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Range3d } from "../geometry3d/Range"; -import { Geometry } from "../Geometry"; -import { AngleSweep } from "../geometry3d/AngleSweep"; -import { AlternatingCCTreeNode } from "../clipping/AlternatingConvexClipTree"; -import { CurvePrimitive } from "../curve/CurvePrimitive"; -import { CurveLocationDetailPair } from "../curve/CurveLocationDetail"; -import { LineSegment3d } from "../curve/LineSegment3d"; -import { LineString3d } from "../curve/LineString3d"; -import { Arc3d } from "../curve/Arc3d"; -import { BSplineCurve3d } from "../bspline/BSplineCurve"; -import { Sample } from "../serialization/GeometrySamples"; +import { Checker, UsageSums, SaveAndRestoreCheckTransform } from "../Checker"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Range3d } from "../../geometry3d/Range"; +import { Geometry } from "../../Geometry"; +import { AngleSweep } from "../../geometry3d/AngleSweep"; +import { AlternatingCCTreeNode } from "../../clipping/AlternatingConvexClipTree"; +import { CurvePrimitive } from "../../curve/CurvePrimitive"; +import { CurveLocationDetailPair } from "../../curve/CurveLocationDetail"; +import { LineSegment3d } from "../../curve/LineSegment3d"; +import { LineString3d } from "../../curve/LineString3d"; +import { Arc3d } from "../../curve/Arc3d"; +import { BSplineCurve3d } from "../../bspline/BSplineCurve"; +import { Sample } from "../../serialization/GeometrySamples"; /* tslint:disable: no-console */ diff --git a/core/geometry/src/test/ClipPlanes.test.ts b/core/geometry/src/test/clipping/ClipPlanes.test.ts similarity index 85% rename from core/geometry/src/test/ClipPlanes.test.ts rename to core/geometry/src/test/clipping/ClipPlanes.test.ts index c061a06..a9d3441 100644 --- a/core/geometry/src/test/ClipPlanes.test.ts +++ b/core/geometry/src/test/clipping/ClipPlanes.test.ts @@ -4,31 +4,32 @@ *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; -import { Checker } from "./Checker"; -import { Plane3dByOriginAndUnitNormal } from "../geometry3d/Plane3dByOriginAndUnitNormal"; -import { Geometry } from "../Geometry"; -import { AngleSweep } from "../geometry3d/AngleSweep"; -import { Angle } from "../geometry3d/Angle"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Segment1d } from "../geometry3d/Segment1d"; -import { Range1d, Range3d } from "../geometry3d/Range"; -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { Transform } from "../geometry3d/Transform"; -import { PolygonOps } from "../geometry3d/PointHelpers"; -import { LineSegment3d } from "../curve/LineSegment3d"; -import { Arc3d } from "../curve/Arc3d"; -import { LineString3d } from "../curve/LineString3d"; - -import { CurvePrimitive, AnnounceNumberNumberCurvePrimitive } from "../curve/CurvePrimitive"; -import { GeometryQuery } from "../curve/GeometryQuery"; - -import { ClipPlaneContainment, Clipper, ClipUtilities } from "../clipping/ClipUtils"; -import { ClipPlane } from "../clipping/ClipPlane"; -import { ConvexClipPlaneSet } from "../clipping/ConvexClipPlaneSet"; -import { UnionOfConvexClipPlaneSets } from "../clipping/UnionOfConvexClipPlaneSets"; -import { Sample } from "../serialization/GeometrySamples"; - -import { GeometryCoreTestIO } from "./GeometryCoreTestIO"; +import { Checker } from "../Checker"; +import { Plane3dByOriginAndUnitNormal } from "../../geometry3d/Plane3dByOriginAndUnitNormal"; +import { Geometry } from "../../Geometry"; +import { AngleSweep } from "../../geometry3d/AngleSweep"; +import { Angle } from "../../geometry3d/Angle"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Segment1d } from "../../geometry3d/Segment1d"; +import { Range1d, Range3d } from "../../geometry3d/Range"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { Transform } from "../../geometry3d/Transform"; +import { PolygonOps } from "../../geometry3d/PointHelpers"; +import { LineSegment3d } from "../../curve/LineSegment3d"; +import { Arc3d } from "../../curve/Arc3d"; +import { LineString3d } from "../../curve/LineString3d"; + +import { CurvePrimitive, AnnounceNumberNumberCurvePrimitive } from "../../curve/CurvePrimitive"; +import { GeometryQuery } from "../../curve/GeometryQuery"; + +import { ClipPlaneContainment, Clipper, ClipUtilities, ClipStatus } from "../../clipping/ClipUtils"; +import { ClipPlane } from "../../clipping/ClipPlane"; +import { ConvexClipPlaneSet } from "../../clipping/ConvexClipPlaneSet"; +import { UnionOfConvexClipPlaneSets } from "../../clipping/UnionOfConvexClipPlaneSets"; +import { Sample } from "../../serialization/GeometrySamples"; + +import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; +import { GrowableXYZArray } from "../../geometry3d/GrowableXYZArray"; /* tslint:disable:no-console no-trailing-whitespace */ Checker.noisy.clipPlane = false; @@ -786,4 +787,74 @@ describe("CurveClips", () => { ck.checkpoint("SweptPolygon"); expect(ck.getNumErrors()).equals(0); }); + + it("QuickClipStatus", () => { + const ck = new Checker(); + const convexSetA = ConvexClipPlaneSet.createXYBox(0, 0, 4, 4); + const convexSetB = ConvexClipPlaneSet.createXYBox(6, 6, 11, 10); + const clipAB = UnionOfConvexClipPlaneSets.createConvexSets([convexSetA, convexSetB]); + + // These are all contained in convexSetA. + const pointsInA = GrowableXYZArray.create([Point3d.create(1, 1), Point3d.create(2, 2), Point3d.create(2, 3)]); + const pointsInB = GrowableXYZArray.create([Point3d.create(7, 8), Point3d.create(8, 8), Point3d.create(8, 9)]); + + ck.testExactNumber(ClipStatus.TrivialAccept, ClipUtilities.pointSetSingleClipStatus(pointsInA, clipAB, 0.0)); + ck.testExactNumber(ClipStatus.TrivialAccept, ClipUtilities.pointSetSingleClipStatus(pointsInB, clipAB, 0.0)); + + const boundaryA = GrowableXYZArray.create([Point3d.create(1, 1), Point3d.create(1, -1)]); + const boundaryB = GrowableXYZArray.create([Point3d.create(7, 7), Point3d.create(7, 5)]); + ck.testExactNumber(ClipStatus.ClipRequired, ClipUtilities.pointSetSingleClipStatus(boundaryA, clipAB, 0.0)); + ck.testExactNumber(ClipStatus.ClipRequired, ClipUtilities.pointSetSingleClipStatus(boundaryB, clipAB, 0.0)); + + const spreadQ = GrowableXYZArray.create( + [Point3d.create(1, 8), Point3d.create(2, 8)]); + ck.testExactNumber(ClipStatus.TrivialReject, ClipUtilities.pointSetSingleClipStatus(spreadQ, clipAB, 0.0)); + ck.testExactNumber(ClipStatus.TrivialAccept, ClipUtilities.pointSetSingleClipStatus(spreadQ, + UnionOfConvexClipPlaneSets.createConvexSets([]), 0.0)); + ck.checkpoint("QuickClipStatus"); + expect(ck.getNumErrors()).equals(0); + }); + + it("createPlaneVariants", () => { + const ck = new Checker(); + const clipPlaneA = ClipPlane.createNormalAndPointXYZXYZ(1, 0, 0, 0, 0, 0); + const unitB = Vector3d.create(1, 5, 2); + unitB.normalizeInPlace(); + const planeByNormalB = Plane3dByOriginAndUnitNormal.create(Point3d.create(1, 2, 3), unitB)!; + const clipPlaneB0 = ClipPlane.createPlane(planeByNormalB); + const clipPlaneB1 = ClipPlane.createPlane(planeByNormalB, false, false, clipPlaneA); + ck.testTrue(clipPlaneB0.isAlmostEqual(clipPlaneB1)); + ck.checkpoint("QuickClipStatus"); + expect(ck.getNumErrors()).equals(0); + }); + + it("createNormalAndDistanceVariants", () => { + const ck = new Checker(); + const clipPlaneA = ClipPlane.createNormalAndPointXYZXYZ(1, 0, 0, 0, 0, 0); + const unitB = Vector3d.create(1, 5, 2); + unitB.normalizeInPlace(); + const distance = -12.0; + const clipPlaneB0 = ClipPlane.createNormalAndDistance(unitB, distance, false, true)!; + const clipPlaneB1 = ClipPlane.createNormalAndDistance(unitB, distance, false, true, clipPlaneA)!; + ck.testTrue(clipPlaneB0.isAlmostEqual(clipPlaneB1)); + + ck.testUndefined(ClipPlane.createNormalAndDistance(Vector3d.createZero(), distance)); + ck.checkpoint("QuickClipStatus"); + expect(ck.getNumErrors()).equals(0); + }); + + it("createNormalAndPointVariants", () => { + const ck = new Checker(); + const clipPlaneA = ClipPlane.createNormalAndPointXYZXYZ(1, 0, 0, 0, 0, 0); + const unitB = Vector3d.create(1, 5, 2); + unitB.normalizeInPlace(); + const pointOnPlane = Point3d.create(3, 2, 9); + const clipPlaneB0 = ClipPlane.createNormalAndPoint(unitB, pointOnPlane, false, true)!; + const clipPlaneB1 = ClipPlane.createNormalAndPoint(unitB, pointOnPlane, false, true, clipPlaneA)!; + ck.testTrue(clipPlaneB0.isAlmostEqual(clipPlaneB1)); + + ck.testUndefined(ClipPlane.createNormalAndPoint(Vector3d.createZero(), pointOnPlane)); + ck.checkpoint("QuickClipStatus"); + expect(ck.getNumErrors()).equals(0); + }); }); diff --git a/core/geometry/src/test/ClipPrimitives.test.ts b/core/geometry/src/test/clipping/ClipPrimitives.test.ts similarity index 95% rename from core/geometry/src/test/ClipPrimitives.test.ts rename to core/geometry/src/test/clipping/ClipPrimitives.test.ts index 6a2d01b..79caaba 100644 --- a/core/geometry/src/test/ClipPrimitives.test.ts +++ b/core/geometry/src/test/clipping/ClipPrimitives.test.ts @@ -3,20 +3,20 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Checker } from "./Checker"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { ClipPrimitive, ClipShape, PlaneSetParamsCache, ClipMask } from "../clipping/ClipPrimitive"; -import { ClipPlane } from "../clipping/ClipPlane"; -import { ConvexClipPlaneSet } from "../clipping/ConvexClipPlaneSet"; -import { UnionOfConvexClipPlaneSets } from "../clipping/UnionOfConvexClipPlaneSets"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Range3d } from "../geometry3d/Range"; -import { Transform } from "../geometry3d/Transform"; -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { Geometry } from "../Geometry"; -import { ClipUtilities } from "../clipping/ClipUtils"; -import { Triangulator } from "../topology/Triangulation"; -import { HalfEdgeGraph, HalfEdge, HalfEdgeMask } from "../topology/Graph"; +import { ClipPrimitive, ClipShape, PlaneSetParamsCache, ClipMask } from "../../clipping/ClipPrimitive"; +import { ClipPlane } from "../../clipping/ClipPlane"; +import { ConvexClipPlaneSet } from "../../clipping/ConvexClipPlaneSet"; +import { UnionOfConvexClipPlaneSets } from "../../clipping/UnionOfConvexClipPlaneSets"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Range3d } from "../../geometry3d/Range"; +import { Transform } from "../../geometry3d/Transform"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { Geometry } from "../../Geometry"; +import { ClipUtilities } from "../../clipping/ClipUtils"; +import { Triangulator } from "../../topology/Triangulation"; +import { HalfEdgeGraph, HalfEdge, HalfEdgeMask } from "../../topology/Graph"; // tslint:disable:no-console diff --git a/core/geometry/src/test/ClipVector.test.ts b/core/geometry/src/test/clipping/ClipVector.test.ts similarity index 95% rename from core/geometry/src/test/ClipVector.test.ts rename to core/geometry/src/test/clipping/ClipVector.test.ts index 96c901f..517d0ab 100644 --- a/core/geometry/src/test/ClipVector.test.ts +++ b/core/geometry/src/test/clipping/ClipVector.test.ts @@ -4,16 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; -import { Checker } from "./Checker"; -import { Point2d, Vector2d } from "../geometry3d/Point2dVector2d"; -import { Point3d } from "../geometry3d/Point3dVector3d"; -import { Range3d } from "../geometry3d/Range"; -import { Transform } from "../geometry3d/Transform"; -import { ClipShape, ClipMask } from "../clipping/ClipPrimitive"; -import { ClipVector } from "../clipping/ClipVector"; -import { SmallSystem } from "../numerics/Polynomials"; -import { LineSegment3d } from "../curve/LineSegment3d"; -import { Matrix4d } from "../geometry4d/Matrix4d"; +import { Checker } from "../Checker"; +import { Point2d, Vector2d } from "../../geometry3d/Point2dVector2d"; +import { Point3d } from "../../geometry3d/Point3dVector3d"; +import { Range3d } from "../../geometry3d/Range"; +import { Transform } from "../../geometry3d/Transform"; +import { ClipShape, ClipMask } from "../../clipping/ClipPrimitive"; +import { ClipVector } from "../../clipping/ClipVector"; +import { SmallSystem } from "../../numerics/Polynomials"; +import { LineSegment3d } from "../../curve/LineSegment3d"; +import { Matrix4d } from "../../geometry4d/Matrix4d"; // External test functions import { clipShapesAreEqual } from "./ClipPrimitives.test"; diff --git a/core/geometry/src/test/Arc3d.test.ts b/core/geometry/src/test/curve/Arc3d.test.ts similarity index 92% rename from core/geometry/src/test/Arc3d.test.ts rename to core/geometry/src/test/curve/Arc3d.test.ts index 1b0242b..86dbfee 100644 --- a/core/geometry/src/test/Arc3d.test.ts +++ b/core/geometry/src/test/curve/Arc3d.test.ts @@ -3,21 +3,21 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Range1d } from "../geometry3d/Range"; -import { Transform } from "../geometry3d/Transform"; -import { Matrix3d } from "../geometry3d/Matrix3d"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Range1d } from "../../geometry3d/Range"; +import { Transform } from "../../geometry3d/Transform"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; -import { Arc3d } from "../curve/Arc3d"; -import { AngleSweep } from "../geometry3d/AngleSweep"; -import { Angle } from "../geometry3d/Angle"; -import { prettyPrint } from "./testFunctions"; -import { Checker } from "./Checker"; +import { Arc3d } from "../../curve/Arc3d"; +import { AngleSweep } from "../../geometry3d/AngleSweep"; +import { Angle } from "../../geometry3d/Angle"; +import { prettyPrint } from "../testFunctions"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { Sample } from "../serialization/GeometrySamples"; -import { LineString3d } from "../curve/LineString3d"; -import { CoordinateXYZ } from "../curve/CoordinateXYZ"; -import { Geometry } from "../Geometry"; +import { Sample } from "../../serialization/GeometrySamples"; +import { LineString3d } from "../../curve/LineString3d"; +import { CoordinateXYZ } from "../../curve/CoordinateXYZ"; +import { Geometry } from "../../Geometry"; /* tslint:disable:no-console */ function exerciseArcSet(ck: Checker, arcA: Arc3d) { diff --git a/core/geometry/src/test/Curve.test.ts b/core/geometry/src/test/curve/Curve.test.ts similarity index 86% rename from core/geometry/src/test/Curve.test.ts rename to core/geometry/src/test/curve/Curve.test.ts index a3ff982..3e2b2ee 100644 --- a/core/geometry/src/test/Curve.test.ts +++ b/core/geometry/src/test/curve/Curve.test.ts @@ -2,44 +2,111 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { AngleSweep } from "../geometry3d/AngleSweep"; -import { Angle } from "../geometry3d/Angle"; -import { Point2d } from "../geometry3d/Point2dVector2d"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Segment1d } from "../geometry3d/Segment1d"; -import { Range3d } from "../geometry3d/Range"; -import { Transform } from "../geometry3d/Transform"; -import { StrokeOptions } from "../curve/StrokeOptions"; -import { CurvePrimitive } from "../curve/CurvePrimitive"; -import { NewtonEvaluatorRtoR, Newton1dUnboundedApproximateDerivative } from "../numerics/Newton"; -import { BSplineCurve3d } from "../bspline/BSplineCurve"; -import { BSplineCurve3dH } from "../bspline/BSplineCurve3dH"; -import { Sample } from "../serialization/GeometrySamples"; -import { Geometry } from "../Geometry"; -import { Ray3d } from "../geometry3d/Ray3d"; -import { TransitionSpiral3d } from "../curve/TransitionSpiral"; -import { LineString3d } from "../curve/LineString3d"; -import { Arc3d } from "../curve/Arc3d"; -import { LineSegment3d } from "../curve/LineSegment3d"; -import { Plane3dByOriginAndUnitNormal } from "../geometry3d/Plane3dByOriginAndUnitNormal"; -import { Checker } from "./Checker"; +import { AngleSweep } from "../../geometry3d/AngleSweep"; +import { Angle } from "../../geometry3d/Angle"; +import { Point2d } from "../../geometry3d/Point2dVector2d"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Segment1d } from "../../geometry3d/Segment1d"; +import { Range3d } from "../../geometry3d/Range"; +import { Transform } from "../../geometry3d/Transform"; +import { StrokeOptions } from "../../curve/StrokeOptions"; +import { CurvePrimitive } from "../../curve/CurvePrimitive"; +import { StrokeCountMap } from "../../curve/Query/StrokeCountMap"; +import { NewtonEvaluatorRtoR, Newton1dUnboundedApproximateDerivative } from "../../numerics/Newton"; +import { BSplineCurve3d } from "../../bspline/BSplineCurve"; +import { BSplineCurve3dH } from "../../bspline/BSplineCurve3dH"; +import { Sample } from "../../serialization/GeometrySamples"; +import { Geometry } from "../../Geometry"; +import { Ray3d } from "../../geometry3d/Ray3d"; +import { TransitionSpiral3d } from "../../curve/TransitionSpiral"; +import { LineString3d } from "../../curve/LineString3d"; +import { Arc3d } from "../../curve/Arc3d"; +import { LineSegment3d } from "../../curve/LineSegment3d"; +import { Plane3dByOriginAndUnitNormal } from "../../geometry3d/Plane3dByOriginAndUnitNormal"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { prettyPrint } from "./testFunctions"; -import { IModelJson } from "../serialization/IModelJsonSchema"; -import { GeometryCoreTestIO } from "./GeometryCoreTestIO"; -import { BezierCurve3dH } from "../bspline/BezierCurve3dH"; -import { BezierCurve3d } from "../bspline/BezierCurve3d"; -import { Point4d } from "../geometry4d/Point4d"; -import { CurveLocationDetail, CurveIntervalRole, CurveSearchStatus } from "../curve/CurveLocationDetail"; -import { CoordinateXYZ } from "../curve/CoordinateXYZ"; -import { Path } from "../curve/Path"; -import { CurveChainWithDistanceIndex } from "../curve/CurveChainWithDistanceIndex"; -import { RuledSweep } from "../solid/RuledSweep"; -import { GeometryQuery } from "../curve/GeometryQuery"; -import { BagOfCurves, CurveCollection } from "../curve/CurveCollection"; +import { prettyPrint } from "../testFunctions"; +import { IModelJson } from "../../serialization/IModelJsonSchema"; +import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; +import { BezierCurve3dH } from "../../bspline/BezierCurve3dH"; +import { BezierCurve3d } from "../../bspline/BezierCurve3d"; +import { Point4d } from "../../geometry4d/Point4d"; +import { CurveLocationDetail, CurveIntervalRole, CurveSearchStatus } from "../../curve/CurveLocationDetail"; +import { CoordinateXYZ } from "../../curve/CoordinateXYZ"; +import { Path } from "../../curve/Path"; +import { CurveChainWithDistanceIndex } from "../../curve/CurveChainWithDistanceIndex"; +import { RuledSweep } from "../../solid/RuledSweep"; +import { GeometryQuery } from "../../curve/GeometryQuery"; +import { BagOfCurves, CurveCollection } from "../../curve/CurveCollection"; +import { NullGeometryHandler } from "../../geometry3d/GeometryHandler"; /* tslint:disable:no-console */ +class StrokeCountSearch extends NullGeometryHandler { + public emitPackedStrokeCountMap(m: StrokeCountMap): any { + const baseData = [m.numStroke, m.curveLength, m.a0, m.a1]; + if (!m.componentData) return baseData; + const components = []; + for (const cd of m.componentData) { + const json = this.emitPackedStrokeCountMap(cd); + components.push(json); + } + return { base: baseData, array: components }; + } + public emitCountData(g: any) { + if (g.strokeData instanceof StrokeCountMap) + return this.emitPackedStrokeCountMap(g.strokeData); + return undefined; + } + public handleLineString3d(g: LineString3d) { return { LineString3d: this.emitCountData(g) }; } + public handleArc3d(g: Arc3d) { return { Arc3d: this.emitCountData(g) }; } + public handleLineSegment3d(g: LineSegment3d) { return { LineSegment3d: this.emitCountData(g) }; } + public handleBSplineCurve3d(g: BSplineCurve3d) { return { BSplineCurve3d: this.emitCountData(g) }; } + public handleBSplineCurve3dH(g: BSplineCurve3dH) { return { BSplineCurve3dH: this.emitCountData(g) }; } + public handleLBezierCurve3d(g: BezierCurve3d) { return { BezierCurve3d: this.emitCountData(g) }; } + public handleLBezierCurve3dH(g: BezierCurve3dH) { return { BezierCurve3dH: this.emitCountData(g) }; } + public handleCurveChainWithDistanceIndex(g: CurveChainWithDistanceIndex) { + const data = { CurveVectorWithDistanceIndex: this.emitCountData(g) }; + const children = g.children; + if (children) { + (data as any).childCounts = []; + for (const c of children) { + (data as any).childCounts.push(this.emitCountData(c)); + } + } + } + public handlePath(g: Path) { + const childData = []; + const children = g.children; + if (children) { + for (const c of children) { + childData.push(this.emitCountData(c)); + } + } + return { path: childData }; + } + + public static getJSON(g: GeometryQuery): any { + const handler = new StrokeCountSearch(); + return g.dispatchToGeometryHandler(handler); + } +} + class ExerciseCurve { + public static exerciseStrokeData(ck: Checker, curve: CurvePrimitive) { + + const curveA = curve.clone() as CurvePrimitive; + const count0 = curveA.computeStrokeCountForOptions(); + curveA.computeAndAttachRecursiveStrokeCounts(); + // console.log("strokes by count", count0); + // console.log("attached to curve", prettyPrint(StrokeCountSearch.getJSON(curveA))); + + if (ck.testPointer(curveA.strokeData, "StrokeData attached", curveA) && curveA.strokeData) { + if (!ck.testExactNumber(curveA.strokeData.numStroke, count0, curveA)) { + console.log("strokes by count", count0); + console.log("attached to curve", prettyPrint(StrokeCountSearch.getJSON(curveA))); + } + } + } public static exerciseCloneAndTransform(ck: Checker, curveA: CurvePrimitive) { const u0 = 0.25; @@ -226,6 +293,7 @@ class ExerciseCurve { ExerciseCurve.exerciseReverseInPlace(ck, curve); ExerciseCurve.exerciseCloneAndTransform(ck, curve); ExerciseCurve.exerciseCloneAndTransform(ck, curve); + ExerciseCurve.exerciseStrokeData(ck, curve); // evaluate near endpoints to trigger end conditions const point0A = curve.startPoint(); const point1A = curve.endPoint(); @@ -293,7 +361,7 @@ class ExerciseCurve { console.log("STROKES", strokes); } } - public static RunTest(ck: Checker) { + public static testManyCurves(ck: Checker) { { const segment = LineSegment3d.create(Point3d.create(1, 2, 3), Point3d.create(4, 5, 10)); @@ -430,10 +498,10 @@ class ExerciseCurve { } } -describe("CurveChainWithDistanceIndex", () => { +describe("Curves", () => { it("Exercise", () => { const ck = new Checker(); - ExerciseCurve.RunTest(ck); + ExerciseCurve.testManyCurves(ck); ck.checkpoint("End CurvePrimitive.Evaluations"); expect(ck.getNumErrors()).equals(0); }); diff --git a/core/geometry/src/test/CurveCollection.test.ts b/core/geometry/src/test/curve/CurveCollection.test.ts similarity index 91% rename from core/geometry/src/test/CurveCollection.test.ts rename to core/geometry/src/test/curve/CurveCollection.test.ts index edd4aac..88be7ee 100644 --- a/core/geometry/src/test/CurveCollection.test.ts +++ b/core/geometry/src/test/curve/CurveCollection.test.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Sample } from "../serialization/GeometrySamples"; -import { CurveCollection } from "../curve/CurveCollection"; -import { Point3d } from "../geometry3d/Point3dVector3d"; -import { Transform } from "../geometry3d/Transform"; -import { Checker } from "./Checker"; +import { Sample } from "../../serialization/GeometrySamples"; +import { CurveCollection } from "../../curve/CurveCollection"; +import { Point3d } from "../../geometry3d/Point3dVector3d"; +import { Transform } from "../../geometry3d/Transform"; +import { Checker } from "../Checker"; import { expect } from "chai"; // import { prettyPrint } from "./testFunctions"; // import { CurveLocationDetail } from "../curve/CurvePrimitive"; diff --git a/core/geometry/src/test/CurveCurveIntersectionXY.test.ts b/core/geometry/src/test/curve/CurveCurveIntersectionXY.test.ts similarity index 85% rename from core/geometry/src/test/CurveCurveIntersectionXY.test.ts rename to core/geometry/src/test/curve/CurveCurveIntersectionXY.test.ts index fc2c49b..c7605a1 100644 --- a/core/geometry/src/test/CurveCurveIntersectionXY.test.ts +++ b/core/geometry/src/test/curve/CurveCurveIntersectionXY.test.ts @@ -2,20 +2,20 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Point2d } from "../geometry3d/Point2dVector2d"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { CurveCurve, CurveLocationDetailArrayPair } from "../curve/CurveCurveIntersectXY"; -import { LineString3d } from "../curve/LineString3d"; -import { LineSegment3d } from "../curve/LineSegment3d"; -import { Checker } from "./Checker"; +import { Point2d } from "../../geometry3d/Point2dVector2d"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { CurveCurve, CurveLocationDetailArrayPair } from "../../curve/CurveCurveIntersectXY"; +import { LineString3d } from "../../curve/LineString3d"; +import { LineSegment3d } from "../../curve/LineSegment3d"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { Map4d } from "../geometry4d/Map4d"; -import { Matrix4d } from "../geometry4d/Matrix4d"; -import { Transform } from "../geometry3d/Transform"; -import { Arc3d } from "../curve/Arc3d"; -import { BSplineCurve3d } from "../bspline/BSplineCurve"; -import { GeometryCoreTestIO } from "./GeometryCoreTestIO"; -import { GeometryQuery } from "../curve/GeometryQuery"; +import { Map4d } from "../../geometry4d/Map4d"; +import { Matrix4d } from "../../geometry4d/Matrix4d"; +import { Transform } from "../../geometry3d/Transform"; +import { Arc3d } from "../../curve/Arc3d"; +import { BSplineCurve3d } from "../../bspline/BSplineCurve"; +import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; +import { GeometryQuery } from "../../curve/GeometryQuery"; function createSamplePerspectiveMaps(): Map4d[] { const origin = Point3d.create(-20, -20, -1); @@ -85,10 +85,10 @@ describe("CurveCurve", () => { const worldToLocal = map.transform0; // that's world to local. The perspective frustum forced that. Seems backwards. const segment0 = LineSegment3d.createXYXY(1, 2, 4, 2); const segment1 = LineSegment3d.createXYXY(4, 1, 2, 3); - const intersectionsAB = CurveCurve.IntersectionProjectedXY(worldToLocal, segment0, false, segment1, false); + const intersectionsAB = CurveCurve.intersectionProjectedXY(worldToLocal, segment0, false, segment1, false); testIntersectionsXY(ck, worldToLocal, intersectionsAB, 1, 1); - const intersectionsBA = CurveCurve.IntersectionProjectedXY(worldToLocal, segment1, false, segment0, false); + const intersectionsBA = CurveCurve.intersectionProjectedXY(worldToLocal, segment1, false, segment0, false); testIntersectionsXY(ck, worldToLocal, intersectionsBA, 1, 1); } @@ -109,10 +109,10 @@ describe("CurveCurve", () => { pointA.z += dz; pointB.z += 0.1 * dz; const segment0 = LineSegment3d.create(pointA, pointB); - const intersectionsAB = CurveCurve.IntersectionProjectedXY(worldToLocal, segment0, true, arc1, true); + const intersectionsAB = CurveCurve.intersectionProjectedXY(worldToLocal, segment0, true, arc1, true); testIntersectionsXY(ck, worldToLocal, intersectionsAB, 2, 2); - const intersectionsBA = CurveCurve.IntersectionProjectedXY(worldToLocal, arc1, true, segment0, true); + const intersectionsBA = CurveCurve.intersectionProjectedXY(worldToLocal, arc1, true, segment0, true); testIntersectionsXY(ck, worldToLocal, intersectionsBA, 2, 2); } @@ -130,18 +130,18 @@ describe("CurveCurve", () => { const linestring0 = LineString3d.create(Point3d.create(1, 1), Point3d.create(3, 0), Point3d.create(3, 5)); const linestring1 = LineString3d.create(Point3d.create(2, 4, 2), Point3d.create(4, 1, 0), Point3d.create(2, 5, 0)); - const intersections = CurveCurve.IntersectionProjectedXY(worldToLocal, segment0, false, linestring0, false); + const intersections = CurveCurve.intersectionProjectedXY(worldToLocal, segment0, false, linestring0, false); testIntersectionsXY(ck, worldToLocal, intersections, 1, 1); - const intersections1 = CurveCurve.IntersectionProjectedXY(worldToLocal, linestring0, false, segment0, false); + const intersections1 = CurveCurve.intersectionProjectedXY(worldToLocal, linestring0, false, segment0, false); testIntersectionsXY(ck, worldToLocal, intersections1, 1, 1); - const intersections2 = CurveCurve.IntersectionProjectedXY(worldToLocal, linestring0, false, linestring1, false); + const intersections2 = CurveCurve.intersectionProjectedXY(worldToLocal, linestring0, false, linestring1, false); testIntersectionsXY(ck, worldToLocal, intersections2, 2, 2); - const intersections2r = CurveCurve.IntersectionProjectedXY(worldToLocal, linestring1, false, linestring0, false); + const intersections2r = CurveCurve.intersectionProjectedXY(worldToLocal, linestring1, false, linestring0, false); testIntersectionsXY(ck, worldToLocal, intersections2r, 2, 2); - const intersectionsX = CurveCurve.IntersectionProjectedXY(worldToLocal, segment0, true, linestring0, true); + const intersectionsX = CurveCurve.intersectionProjectedXY(worldToLocal, segment0, true, linestring0, true); testIntersectionsXY(ck, worldToLocal, intersectionsX, 2, 2); } expect(ck.getNumErrors()).equals(0); @@ -153,10 +153,10 @@ describe("CurveCurve", () => { const worldToLocal = map.transform0; // that's world to local. The perspective frustum forced that. Seems backwards. const arcA = Arc3d.create(Point3d.create(1, 2, 0), Vector3d.create(4, 0, 0), Vector3d.create(0, 1, 0)); const arcB = Arc3d.create(Point3d.create(0, 1, 1), Vector3d.create(2, 0, 0), Vector3d.create(0, 4, 0)); - const intersectionsAB = CurveCurve.IntersectionProjectedXY(worldToLocal, arcA, true, arcB, true); + const intersectionsAB = CurveCurve.intersectionProjectedXY(worldToLocal, arcA, true, arcB, true); testIntersectionsXY(ck, worldToLocal, intersectionsAB, 4, 4); - const intersectionsBA = CurveCurve.IntersectionProjectedXY(worldToLocal, arcB, true, arcA, true); + const intersectionsBA = CurveCurve.intersectionProjectedXY(worldToLocal, arcB, true, arcA, true); testIntersectionsXY(ck, worldToLocal, intersectionsBA, 4, 4); } @@ -172,10 +172,10 @@ describe("CurveCurve", () => { const bspline1 = BSplineCurve3d.createUniformKnots( [Point3d.create(1, 2, 0), Point3d.create(1, 1, 0), Point3d.create(1, 0, 0), Point3d.create(0, -1, 0), Point3d.create(0, -2, 0)], 3)!; - const intersectionsAB = CurveCurve.IntersectionProjectedXY(worldToLocal, segment0, false, bspline1, false); + const intersectionsAB = CurveCurve.intersectionProjectedXY(worldToLocal, segment0, false, bspline1, false); testIntersectionsXY(ck, worldToLocal, intersectionsAB, 1, 1); - const intersectionsBA = CurveCurve.IntersectionProjectedXY(worldToLocal, bspline1, false, segment0, false); + const intersectionsBA = CurveCurve.intersectionProjectedXY(worldToLocal, bspline1, false, segment0, false); testIntersectionsXY(ck, worldToLocal, intersectionsBA, 1, 1); } @@ -193,10 +193,10 @@ describe("CurveCurve", () => { const bspline1 = BSplineCurve3d.createUniformKnots( [Point3d.create(1, 2, 0), Point3d.create(1, 1, 0), Point3d.create(1, 0, 0), Point3d.create(0, -1, 0), Point3d.create(0, -2, 0)], 3)!; - const intersectionsAB = CurveCurve.IntersectionProjectedXY(worldToLocal, g0, false, bspline1, false); + const intersectionsAB = CurveCurve.intersectionProjectedXY(worldToLocal, g0, false, bspline1, false); testIntersectionsXY(ck, worldToLocal, intersectionsAB, 2, 2); - const intersectionsBA = CurveCurve.IntersectionProjectedXY(worldToLocal, bspline1, false, g0, false); + const intersectionsBA = CurveCurve.intersectionProjectedXY(worldToLocal, bspline1, false, g0, false); testIntersectionsXY(ck, worldToLocal, intersectionsBA, 2, 2); } @@ -215,10 +215,10 @@ describe("CurveCurve", () => { const bspline1 = BSplineCurve3d.createUniformKnots( [Point3d.create(-1, 1, 0), Point3d.create(0, 1, 0), Point3d.create(2, 2, 0), Point3d.create(3, 3, 0), Point3d.create(4, 3, 0)], order)!; - const intersectionsAB = CurveCurve.IntersectionProjectedXY(worldToLocal, g0, false, bspline1, false); + const intersectionsAB = CurveCurve.intersectionProjectedXY(worldToLocal, g0, false, bspline1, false); testIntersectionsXY(ck, worldToLocal, intersectionsAB, 1, 1); - const intersectionsBA = CurveCurve.IntersectionProjectedXY(worldToLocal, bspline1, false, g0, false); + const intersectionsBA = CurveCurve.intersectionProjectedXY(worldToLocal, bspline1, false, g0, false); testIntersectionsXY(ck, worldToLocal, intersectionsBA, 1, 1); } @@ -273,9 +273,9 @@ describe("CurveCurve", () => { const point1 = bspline1.fractionToPoint(fraction1); bspline1.tryTranslateInPlace(point0.x - point1.x, point0.y - point1.y); GeometryCoreTestIO.captureGeometry(allGeometry, bspline1.clone(), dx, dy); - let intersectionsAB = CurveCurve.IntersectionProjectedXY(worldToLocal, bspline0, false, bspline1, false); + let intersectionsAB = CurveCurve.intersectionProjectedXY(worldToLocal, bspline0, false, bspline1, false); if (!testIntersectionsXY(ck, worldToLocal, intersectionsAB, 1, 1)) - intersectionsAB = CurveCurve.IntersectionProjectedXY(worldToLocal, bspline0, false, bspline1, false); + intersectionsAB = CurveCurve.intersectionProjectedXY(worldToLocal, bspline0, false, bspline1, false); for (let i = 0; i < intersectionsAB.dataA.length; i++) { GeometryCoreTestIO.captureGeometry(allGeometry, Arc3d.createXY(intersectionsAB.dataA[i].point, rA), dx, dy); GeometryCoreTestIO.captureGeometry(allGeometry, Arc3d.createXY(intersectionsAB.dataB[i].point, rB), dx, dy); diff --git a/core/geometry/src/test/CurveLocationDetail.test.ts b/core/geometry/src/test/curve/CurveLocationDetail.test.ts similarity index 90% rename from core/geometry/src/test/CurveLocationDetail.test.ts rename to core/geometry/src/test/curve/CurveLocationDetail.test.ts index 0abbb8a..77e556b 100644 --- a/core/geometry/src/test/CurveLocationDetail.test.ts +++ b/core/geometry/src/test/curve/CurveLocationDetail.test.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ // import { Sample } from "../serialization/GeometrySamples"; -import { CurveLocationDetail, CurveLocationDetailPair } from "../curve/CurveLocationDetail"; -import { LineSegment3d } from "../curve/LineSegment3d"; +import { CurveLocationDetail, CurveLocationDetailPair } from "../../curve/CurveLocationDetail"; +import { LineSegment3d } from "../../curve/LineSegment3d"; // import { Point3d, Transform } from "../PointVector"; -import { Checker } from "./Checker"; +import { Checker } from "../Checker"; import { expect } from "chai"; /* tslint:disable:no-console */ describe("CurveLocationDetail", () => { diff --git a/core/geometry/src/test/EmitStrokableParts.test.ts b/core/geometry/src/test/curve/EmitStrokableParts.test.ts similarity index 86% rename from core/geometry/src/test/EmitStrokableParts.test.ts rename to core/geometry/src/test/curve/EmitStrokableParts.test.ts index 5204c63..32a3ed2 100644 --- a/core/geometry/src/test/EmitStrokableParts.test.ts +++ b/core/geometry/src/test/curve/EmitStrokableParts.test.ts @@ -4,16 +4,16 @@ *--------------------------------------------------------------------------------------------*/ // import { Point3d, Vector3d, Transform, Matrix3d, Range1d } from "../PointVector"; -import { Sample } from "../serialization/GeometrySamples"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { LineString3d } from "../curve/LineString3d"; -import { StrokeOptions } from "../curve/StrokeOptions"; -import { CurvePrimitive } from "../curve/CurvePrimitive"; -import { IStrokeHandler } from "../geometry3d/GeometryHandler"; -import { FrameBuilder } from "../geometry3d/FrameBuilder"; +import { Sample } from "../../serialization/GeometrySamples"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { LineString3d } from "../../curve/LineString3d"; +import { StrokeOptions } from "../../curve/StrokeOptions"; +import { CurvePrimitive } from "../../curve/CurvePrimitive"; +import { IStrokeHandler } from "../../geometry3d/GeometryHandler"; +import { FrameBuilder } from "../../geometry3d/FrameBuilder"; // import { Geometry } from "../Geometry"; -import { Checker } from "./Checker"; +import { Checker } from "../Checker"; import { expect } from "chai"; function maxSegmentLength(linestring: LineString3d): number { let aMax = 0; diff --git a/core/geometry/src/test/LineSegment3d.test.ts b/core/geometry/src/test/curve/LineSegment3d.test.ts similarity index 80% rename from core/geometry/src/test/LineSegment3d.test.ts rename to core/geometry/src/test/curve/LineSegment3d.test.ts index bb38e44..436511f 100644 --- a/core/geometry/src/test/LineSegment3d.test.ts +++ b/core/geometry/src/test/curve/LineSegment3d.test.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Point3d } from "../geometry3d/Point3dVector3d"; -import { Transform } from "../geometry3d/Transform"; -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { LineSegment3d } from "../curve/LineSegment3d"; -import { Checker } from "./Checker"; +import { Point3d } from "../../geometry3d/Point3dVector3d"; +import { Transform } from "../../geometry3d/Transform"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { LineSegment3d } from "../../curve/LineSegment3d"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { CoordinateXYZ } from "../curve/CoordinateXYZ"; -import { Geometry } from "../Geometry"; +import { CoordinateXYZ } from "../../curve/CoordinateXYZ"; +import { Geometry } from "../../Geometry"; function exerciseLineSegment3d(ck: Checker, segmentA: LineSegment3d) { const a = 4.2; @@ -75,13 +75,13 @@ describe("LineSegment3d", () => { const segmentD = LineSegment3d.createXYZXYZ(1, 2, 3, 4, 5, 6); const segmentE = LineSegment3d.createXYZXYZ(1, 2, 3, 4, 5, 6, segmentC); // overwrite the default segmentC. - ck.testPointer (segmentE, segmentC, "reuse of optional arg"); - ck.testTrue (segmentD.isAlmostEqual (segmentE)); + ck.testPointer(segmentE, segmentC, "reuse of optional arg"); + ck.testTrue(segmentD.isAlmostEqual(segmentE)); - const segmentF = LineSegment3d.create (segmentA.endPoint(), segmentA.startPoint (), segmentD); // another optional - ck.testFalse (segmentF.isAlmostEqual (segmentA)); - segmentF.reverseInPlace (); - ck.testTrue (segmentF.isAlmostEqual (segmentA)); + const segmentF = LineSegment3d.create(segmentA.endPoint(), segmentA.startPoint(), segmentD); // another optional + ck.testFalse(segmentF.isAlmostEqual(segmentA)); + segmentF.reverseInPlace(); + ck.testTrue(segmentF.isAlmostEqual(segmentA)); ck.checkpoint("LineSegment3d.HelloWorld"); expect(ck.getNumErrors()).equals(0); diff --git a/core/geometry/src/test/LineString3d.test.ts b/core/geometry/src/test/curve/LineString3d.test.ts similarity index 93% rename from core/geometry/src/test/LineString3d.test.ts rename to core/geometry/src/test/curve/LineString3d.test.ts index 295a769..4aa22b7 100644 --- a/core/geometry/src/test/LineString3d.test.ts +++ b/core/geometry/src/test/curve/LineString3d.test.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Transform } from "../geometry3d/Transform"; -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { LineString3d } from "../curve/LineString3d"; -import { Checker } from "./Checker"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Transform } from "../../geometry3d/Transform"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { LineString3d } from "../../curve/LineString3d"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { ClipPlane } from "../clipping/ClipPlane"; -import { CurvePrimitive } from "../curve/CurvePrimitive"; -import { Point2d } from "../geometry3d/Point2dVector2d"; -import { GrowableXYZArray } from "../geometry3d/GrowableXYZArray"; +import { ClipPlane } from "../../clipping/ClipPlane"; +import { CurvePrimitive } from "../../curve/CurvePrimitive"; +import { Point2d } from "../../geometry3d/Point2dVector2d"; +import { GrowableXYZArray } from "../../geometry3d/GrowableXYZArray"; /* tslint:disable:no-console */ function exerciseLineString3d(ck: Checker, lsA: LineString3d) { diff --git a/core/geometry/src/test/PointString3d.test.ts b/core/geometry/src/test/curve/PointString3d.test.ts similarity index 85% rename from core/geometry/src/test/PointString3d.test.ts rename to core/geometry/src/test/curve/PointString3d.test.ts index 7166cc5..700cf5b 100644 --- a/core/geometry/src/test/PointString3d.test.ts +++ b/core/geometry/src/test/curve/PointString3d.test.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Point3d } from "../geometry3d/Point3dVector3d"; -import { Range3d } from "../geometry3d/Range"; -import { PointString3d } from "../curve/PointString3d"; -import { Point3dArray } from "../geometry3d/PointHelpers"; -import { Checker } from "./Checker"; +import { Point3d } from "../../geometry3d/Point3dVector3d"; +import { Range3d } from "../../geometry3d/Range"; +import { PointString3d } from "../../curve/PointString3d"; +import { Point3dArray } from "../../geometry3d/PointHelpers"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { Plane3dByOriginAndUnitNormal } from "../geometry3d/Plane3dByOriginAndUnitNormal"; -import { Sample } from "../serialization/GeometrySamples"; +import { Plane3dByOriginAndUnitNormal } from "../../geometry3d/Plane3dByOriginAndUnitNormal"; +import { Sample } from "../../serialization/GeometrySamples"; function exercisePointString3d(ck: Checker, lsA: PointString3d) { const numPoints = lsA.numPoints(); diff --git a/core/geometry/src/test/Region.test.ts b/core/geometry/src/test/curve/Region.test.ts similarity index 76% rename from core/geometry/src/test/Region.test.ts rename to core/geometry/src/test/curve/Region.test.ts index ec181da..68de41e 100644 --- a/core/geometry/src/test/Region.test.ts +++ b/core/geometry/src/test/curve/Region.test.ts @@ -1,16 +1,16 @@ /*--------------------------------------------------------------------------------------------- -* Copyright (c) 2018 Bentley Systems, Incorporated. All rights reserved. +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Checker } from "./Checker"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { UnionRegion } from "../curve/UnionRegion"; -import { LineSegment3d } from "../curve/LineSegment3d"; -import { Sample } from "../serialization/GeometrySamples"; -import { LineString3d } from "../curve/LineString3d"; -import { Loop } from "../curve/Loop"; -import { ParityRegion } from "../curve/ParityRegion"; +import { UnionRegion } from "../../curve/UnionRegion"; +import { LineSegment3d } from "../../curve/LineSegment3d"; +import { Sample } from "../../serialization/GeometrySamples"; +import { LineString3d } from "../../curve/LineString3d"; +import { Loop } from "../../curve/Loop"; +import { ParityRegion } from "../../curve/ParityRegion"; describe("Regions", () => { it("UnionRegion", () => { diff --git a/core/geometry/src/test/StrokeOptions.test.ts b/core/geometry/src/test/curve/StrokeOptions.test.ts similarity index 85% rename from core/geometry/src/test/StrokeOptions.test.ts rename to core/geometry/src/test/curve/StrokeOptions.test.ts index 841f43d..4e43f35 100644 --- a/core/geometry/src/test/StrokeOptions.test.ts +++ b/core/geometry/src/test/curve/StrokeOptions.test.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { GeometryQuery } from "../curve/GeometryQuery"; -import { Checker } from "./Checker"; +import { GeometryQuery } from "../../curve/GeometryQuery"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { GeometryCoreTestIO } from "./GeometryCoreTestIO"; -import { StrokeOptions } from "../curve/StrokeOptions"; -import { Arc3d } from "../curve/Arc3d"; -import { Point3d } from "../geometry3d/Point3dVector3d"; -import { AngleSweep } from "../geometry3d/AngleSweep"; +import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; +import { StrokeOptions } from "../../curve/StrokeOptions"; +import { Arc3d } from "../../curve/Arc3d"; +import { Point3d } from "../../geometry3d/Point3dVector3d"; +import { AngleSweep } from "../../geometry3d/AngleSweep"; /* tslint:disable:no-console */ diff --git a/core/geometry/src/test/TransitionSpiral3d.test.ts b/core/geometry/src/test/curve/TransitionSpiral3d.test.ts similarity index 84% rename from core/geometry/src/test/TransitionSpiral3d.test.ts rename to core/geometry/src/test/curve/TransitionSpiral3d.test.ts index 2342ba7..51ff0f1 100644 --- a/core/geometry/src/test/TransitionSpiral3d.test.ts +++ b/core/geometry/src/test/curve/TransitionSpiral3d.test.ts @@ -7,15 +7,15 @@ // import { Range1d } from "../Range"; // import { Matrix3d, Transform } from "../geometry3d/Transform"; -import { TransitionConditionalProperties, TransitionSpiral3d } from "../curve/TransitionSpiral"; -import { Angle } from "../geometry3d/Angle"; -import { Checker } from "./Checker"; +import { TransitionConditionalProperties, TransitionSpiral3d } from "../../curve/TransitionSpiral"; +import { Angle } from "../../geometry3d/Angle"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { AngleSweep } from "../geometry3d/AngleSweep"; -import { Transform } from "../geometry3d/Transform"; -import { Segment1d } from "../geometry3d/Segment1d"; - -describe("TransitionSprialProperties", () => { +import { AngleSweep } from "../../geometry3d/AngleSweep"; +import { Transform } from "../../geometry3d/Transform"; +import { Segment1d } from "../../geometry3d/Segment1d"; +/* tslint:disable:no-console */ +describe("TransitionSpiralProperties", () => { it("HelloWorld", () => { const ck = new Checker(); const b0 = Angle.createDegrees(10); @@ -54,6 +54,7 @@ describe("TransitionSprialProperties", () => { ck.testFalse(spiralB.isAlmostEqual(spiralA)); spiralB.setFrom(spiralA); ck.testTrue(spiralA.isAlmostEqual(spiralB)); + console.log(TransitionSpiral3d.radiusRadiusLengthToSweepRadians(0, 10, 50)); expect(ck.getNumErrors()).equals(0); }); }); diff --git a/core/geometry/src/test/snippet.test.ts b/core/geometry/src/test/docSnippets/snippet.test.ts similarity index 94% rename from core/geometry/src/test/snippet.test.ts rename to core/geometry/src/test/docSnippets/snippet.test.ts index d4a5138..e16a74b 100644 --- a/core/geometry/src/test/snippet.test.ts +++ b/core/geometry/src/test/docSnippets/snippet.test.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ // tslint:disable:no-duplicate-imports -import * as geometry from "../geometry-core"; -import { YawPitchRollAngles, Matrix3d } from "../geometry-core"; -import { Vector3d, Point3d } from "../geometry3d/Point3dVector3d"; -import { Plane3dByOriginAndUnitNormal } from "../geometry3d/Plane3dByOriginAndUnitNormal"; -import { Ray3d } from "../geometry3d/Ray3d"; +import * as geometry from "../../geometry-core"; +import { YawPitchRollAngles, Matrix3d } from "../../geometry-core"; +import { Vector3d, Point3d } from "../../geometry3d/Point3dVector3d"; +import { Plane3dByOriginAndUnitNormal } from "../../geometry3d/Plane3dByOriginAndUnitNormal"; +import { Ray3d } from "../../geometry3d/Ray3d"; // import { LineSegment3d } from "../curve/LineSegment3d"; // import { GeometryQuery } from "../curve/CurvePrimitive"; // import { LineString3d } from "../curve/LineString3d"; diff --git a/core/geometry/src/test/Angle.test.ts b/core/geometry/src/test/geometry3d/Angle.test.ts similarity index 94% rename from core/geometry/src/test/Angle.test.ts rename to core/geometry/src/test/geometry3d/Angle.test.ts index e3b251c..2b1fb4a 100644 --- a/core/geometry/src/test/Angle.test.ts +++ b/core/geometry/src/test/geometry3d/Angle.test.ts @@ -2,24 +2,24 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { Complex } from "../numerics/Complex"; -import { Range1d } from "../geometry3d/Range"; -import { Angle } from "../geometry3d/Angle"; -import { AngleSweep } from "../geometry3d/AngleSweep"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { YawPitchRollAngles } from "../geometry3d/YawPitchRollAngles"; -import { Geometry, AxisOrder } from "../Geometry"; - -import { Sample } from "../serialization/GeometrySamples"; -import { Checker } from "./Checker"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { Complex } from "../../numerics/Complex"; +import { Range1d } from "../../geometry3d/Range"; +import { Angle } from "../../geometry3d/Angle"; +import { AngleSweep } from "../../geometry3d/AngleSweep"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { YawPitchRollAngles } from "../../geometry3d/YawPitchRollAngles"; +import { Geometry, AxisOrder } from "../../Geometry"; + +import { Sample } from "../../serialization/GeometrySamples"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { OrderedRotationAngles } from "../geometry3d/OrderedRotationAngles"; +import { OrderedRotationAngles } from "../../geometry3d/OrderedRotationAngles"; /* tslint:disable:no-console */ class AngleTests { constructor(public noisy: boolean = false) { } - public TestAlmostEqual(ck: Checker) { + public testAlmostEqual(ck: Checker) { const a = 0.5 * Geometry.smallAngleRadians; const b = 8.0 * Geometry.smallAngleRadians; const c = 1.0; // radians, a nonzero angle @@ -51,7 +51,7 @@ class AngleTests { } } } - public TestAdjust(ck: Checker) { + public testAdjust(ck: Checker) { const degreeCandidates = [0, 2, 56, 179, 180, 181]; const periodCandidates = [0, 1, -1, 4, -6]; let shiftPeriod = 0; @@ -73,7 +73,7 @@ class AngleTests { } } - public TestFractions(sweep: AngleSweep, ck: Checker) { + public testFractions(sweep: AngleSweep, ck: Checker) { const fractionCandidates = [0, 0.25, 0.5, 0.75, 1.0, -0.3, 1.3, -10, 10]; let f0 = 0; const reverseSweep = sweep.clone(); @@ -119,7 +119,7 @@ describe("Angle.AlmostEqual", () => { it("Verify angle tolerance tests", () => { const ck = new Checker(); const source = new AngleTests(false); - source.TestAlmostEqual(ck); + source.testAlmostEqual(ck); ck.testTrue(Angle.createDegrees(0).isExactZero); ck.testTrue(Angle.createRadians(0).isExactZero); ck.testFalse(Angle.createRadians(1.0e-20).isExactZero); @@ -132,7 +132,7 @@ describe("Angle.Adjust", () => { it("Verify angle period adjustments tests", () => { const ck = new Checker(); const source = new AngleTests(false); - source.TestAdjust(ck); + source.testAdjust(ck); ck.checkpoint("End Angle.Adjust"); expect(ck.getNumErrors()).equals(0); }); @@ -166,10 +166,10 @@ describe("AngleSweep", () => { it("Fractions", () => { const ck = new Checker(); const source = new AngleTests(false); - source.TestFractions(AngleSweep.createStartSweepDegrees(0, 90), ck); - source.TestFractions(AngleSweep.createStartSweepRadians(-1, 1), ck); - source.TestFractions(AngleSweep.createStartSweepRadians(1, -2), ck); - source.TestFractions(AngleSweep.createStartSweep(Angle.createRadians(0), Angle.createDegrees(360)), ck); + source.testFractions(AngleSweep.createStartSweepDegrees(0, 90), ck); + source.testFractions(AngleSweep.createStartSweepRadians(-1, 1), ck); + source.testFractions(AngleSweep.createStartSweepRadians(1, -2), ck); + source.testFractions(AngleSweep.createStartSweep(Angle.createRadians(0), Angle.createDegrees(360)), ck); ck.checkpoint("AngleSweeps.TestFractions"); expect(ck.getNumErrors()).equals(0); }); diff --git a/core/geometry/src/test/geometry3d/BilinearPatch.test.ts b/core/geometry/src/test/geometry3d/BilinearPatch.test.ts new file mode 100644 index 0000000..693b572 --- /dev/null +++ b/core/geometry/src/test/geometry3d/BilinearPatch.test.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { Checker } from "../Checker"; +import { expect } from "chai"; +import { BilinearPatch } from "../../geometry3d/BilinearPatch"; +import { Point2d } from "../../geometry3d/Point2dVector2d"; +import { Transform } from "../../geometry3d/Transform"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { Angle } from "../../geometry3d/Angle"; +import { Vector3d, Point3d } from "../../geometry3d/Point3dVector3d"; +import { Range3d } from "../../geometry3d/Range"; +import { Geometry } from "../../Geometry"; + +function verifyPatch(ck: Checker, patch: BilinearPatch) { + const transform = Transform.createOriginAndMatrix(Point3d.create(10, 20, 10), Matrix3d.createRotationAroundVector(Vector3d.create(1, 4, 2), Angle.createDegrees(20))); + const patch1 = patch.cloneTransformed(transform)!; + const range = Range3d.createNull(); + patch.extendRange(range); + const range1 = Range3d.createNull(); + patch1.extendRange(range1); + const range1A = Range3d.create(); + patch.extendRange(range1A, transform); + ck.testRange3d(range1, range1A); + ck.testTrue(patch.isAlmostEqual(patch), "compare to self"); + ck.testFalse(patch.isAlmostEqual(patch1), "compare to transformed"); + ck.testTrue(patch1.isAlmostEqual(patch1), "compare to self"); + + for (const uv of [Point2d.create(0.0, 0.0), Point2d.create(1, 0), Point2d.create(0, 1), Point2d.create(1, 1), Point2d.create(0.3, 0.5)]) { + const xyz = patch.uvFractionToPoint(uv.x, uv.y); + const plane = patch.uvFractionToPointAndTangents(uv.x, uv.y); + ck.testPoint3d(xyz, plane.origin); + const xyz1 = patch1.uvFractionToPoint(uv.x, uv.y); + const plane1 = patch1.uvFractionToPointAndTangents(uv.x, uv.y); + ck.testPoint3d(xyz1, plane1.origin); + ck.testPoint3d(xyz1, transform.multiplyPoint3d(xyz), "rigid transform commutes with evaluation"); + ck.testVector3d(plane1.vectorU, transform.multiplyVector(plane.vectorU), "rigid transform commutes with evaluation"); + ck.testVector3d(plane1.vectorV, transform.multiplyVector(plane.vectorV), "rigid transform commutes with evaluation"); + } + + const plane00 = patch1.uvFractionToPointAndTangents(0, 0); + const plane11 = patch1.uvFractionToPointAndTangents(1, 1); + ck.testCoordinate(patch1.maxUEdgeLength(), Geometry.maxXY(plane00.vectorU.magnitude(), plane11.vectorU.magnitude())); + ck.testCoordinate(patch1.maxVEdgeLength(), Geometry.maxXY(plane00.vectorV.magnitude(), plane11.vectorV.magnitude())); +} + +describe("BilinearPatch", () => { + it("Create", () => { + const ck = new Checker(); + verifyPatch(ck, BilinearPatch.createXYZ(0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0)); + verifyPatch(ck, BilinearPatch.createXYZ(0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1)); + verifyPatch(ck, BilinearPatch.createXYZ(1, 2, 3, 5, 2, -1, 6, 7, 10, -4, 2, 1)); + ck.checkpoint("BilinearPatch.Create"); + expect(ck.getNumErrors()).equals(0); + }); + +}); diff --git a/core/geometry/src/test/geometry3d/FrustumAnimation.test.ts b/core/geometry/src/test/geometry3d/FrustumAnimation.test.ts new file mode 100644 index 0000000..7ab3d04 --- /dev/null +++ b/core/geometry/src/test/geometry3d/FrustumAnimation.test.ts @@ -0,0 +1,117 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { Point3d } from "../../geometry3d/Point3dVector3d"; +import { YawPitchRollAngles } from "../../geometry3d/YawPitchRollAngles"; +import { Transform } from "../../geometry3d/Transform"; +import { Checker } from "../Checker"; +import { expect } from "chai"; +import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; +import { LineString3d } from "../../curve/LineString3d"; +import { SmoothTransformBetweenFrusta } from "../../geometry3d/FrustumAnimation"; + +import { Angle } from "../../geometry3d/Angle"; +import { GeometryQuery } from "../../curve/GeometryQuery"; +/* tslint:disable:no-console */ +/** + * create a linestring that walks around all the edges (and some decoration) for a frustum defined by corners. + * @param corners + */ +function cornersToLineString(corners: Point3d[]): LineString3d { + return LineString3d.create( + [corners[0], corners[1], corners[3], corners[2], corners[0], // back retangle + corners[4], // move to front + corners[5], corners[1], corners[5], // front edge plus move to same back point and double back to front + corners[7], corners[3], corners[7], + corners[6], corners[2], corners[6], + corners[4], + corners[0], + corners[4].interpolate(0.5, corners[5]), + corners[0].interpolate(0.5, corners[1]), // some asymetric decoration on lower face + corners[0].interpolate(0.5, corners[2])]); +} +/** + * Within the given coordinate frame (usually rigid) make the 8 corners of a frustum + * @param frame frame with center on back plane, xy plane is back plane, z is dowards eye + * @param ax x axis half width + * @param ay y axis half width + * @param az z axis distance to front plane + * @param fz frustum contraction frmo back to front. + */ +function createFrustumPoints(frame: Transform, ax: number, ay: number, az: number, fz: number): Point3d[] { + return [ + frame.multiplyXYZ(-ax, -ay, 0.0), + frame.multiplyXYZ(ax, -ay, 0.0), + frame.multiplyXYZ(-ax, ay, 0.0), + frame.multiplyXYZ(ax, ay, 0.0), + frame.multiplyXYZ(-fz * ax, -fz * ay, az), + frame.multiplyXYZ(fz * ax, -fz * ay, az), + frame.multiplyXYZ(-fz * ax, fz * ay, az), + frame.multiplyXYZ(fz * ax, fz * ay, az)]; +} + +describe("FrustumAnimationTest", () => { + it("A", () => { + const ck = new Checker(); + const allGeometry: GeometryQuery[] = []; + const c20 = Math.cos(Angle.degreesToRadians(20)); + const s20 = Math.sin(Angle.degreesToRadians(20)); + let dy = 0.0; + for (const frame0 of [ + Transform.createIdentity(), + Transform.createOriginAndMatrix(Point3d.create(-2, 2, -1), + YawPitchRollAngles.createDegrees(10, 5, 30).toMatrix3d())] + ) { + let dx = 0.0; + for (const frame1 of [ + Transform.createTranslationXYZ(1, 2, 15), + // rotate 90 degrees while shifting along the x axis. + Transform.createRowValues( + c20, -s20, 0, 40, + s20, c20, 0, 0, + 0, 0, 1, 0), + // rotate 90 degrees while shifting along the x axis. + Transform.createRowValues( + 0, -1, 0, 40, + 1, 0, 0, 0, + 0, 0, 1, 0), + // rotate 180 degrees z and around center + Transform.createRowValues( + -1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 1, 0), + // rotate 180 degrees around z while shifting y + Transform.createRowValues( + -1, 0, 0, 0, + 0, -1, 0, 80, + 0, 0, 1, 0), + // translate the back plane, but repoint the eye vector (SKEW) + Transform.createRowValues( + 1, 0, 1, 0, + 0, 1, 2, 30, + 0, 0, 3, 0)]) { + const cornerA = createFrustumPoints(frame0, 4, 3, 2, 1); + const cornerB = createFrustumPoints(frame1, 2, 4, 3, 0.5); + GeometryCoreTestIO.captureGeometry(allGeometry, cornersToLineString(cornerA).clone(), dx, dy, 0); + GeometryCoreTestIO.captureGeometry(allGeometry, cornersToLineString(cornerB).clone(), dx, dy, 0); + const context = SmoothTransformBetweenFrusta.create(cornerA, cornerB); + if (ck.testPointer(context) && context) { + const g = 0.05; + const dy1 = dy + 100; + for (const fraction of [0.0, g, 2.0 * g, 0.25, 0.5, 0.75, 1.0 - 2.0 * g, 1.0 - g, 1.0]) { + const cornerF = context.fractionToWorldCorners(fraction); + GeometryCoreTestIO.captureGeometry(allGeometry, cornersToLineString(cornerF), dx, dy1, 0); + + } + } + dx += 100.0; + } + dy += 400.0; + } + + GeometryCoreTestIO.saveGeometry(allGeometry, "Geoemtry3d", "FrustumAnimationA"); + expect(ck.getNumErrors()).equals(0); + }); + +}); diff --git a/core/geometry/src/test/GrowableArray.test.ts b/core/geometry/src/test/geometry3d/GrowableArray.test.ts similarity index 83% rename from core/geometry/src/test/GrowableArray.test.ts rename to core/geometry/src/test/geometry3d/GrowableArray.test.ts index 2f83864..50bacf5 100644 --- a/core/geometry/src/test/GrowableArray.test.ts +++ b/core/geometry/src/test/geometry3d/GrowableArray.test.ts @@ -2,22 +2,22 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Checker } from "./Checker"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { GrowableFloat64Array } from "../geometry3d/GrowableFloat64Array"; -import { GrowableXYZArray } from "../geometry3d/GrowableXYZArray"; -import { ClusterableArray } from "../numerics/ClusterableArray"; -import { prettyPrint } from "./testFunctions"; -import { PolyfaceQuery } from "../polyface/PolyfaceQuery"; -import { Plane3dByOriginAndUnitNormal } from "../geometry3d/Plane3dByOriginAndUnitNormal"; -import { Point2d, Vector2d } from "../geometry3d/Point2dVector2d"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Point3dArrayCarrier, Point3dArray } from "../geometry3d/PointHelpers"; -import { Transform } from "../geometry3d/Transform"; -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { Sample } from "../serialization/GeometrySamples"; -import { Angle } from "../geometry3d/Angle"; -import { GrowableBlockedArray } from "../geometry3d/GrowableBlockedArray"; +import { GrowableFloat64Array } from "../../geometry3d/GrowableFloat64Array"; +import { GrowableXYZArray } from "../../geometry3d/GrowableXYZArray"; +import { ClusterableArray } from "../../numerics/ClusterableArray"; +import { prettyPrint } from "../testFunctions"; +import { PolyfaceQuery } from "../../polyface/PolyfaceQuery"; +import { Plane3dByOriginAndUnitNormal } from "../../geometry3d/Plane3dByOriginAndUnitNormal"; +import { Point2d, Vector2d } from "../../geometry3d/Point2dVector2d"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Point3dArrayCarrier, Point3dArray } from "../../geometry3d/PointHelpers"; +import { Transform } from "../../geometry3d/Transform"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { Sample } from "../../serialization/GeometrySamples"; +import { Angle } from "../../geometry3d/Angle"; +import { GrowableBlockedArray } from "../../geometry3d/GrowableBlockedArray"; /* tslint:disable: no-console */ @@ -32,16 +32,16 @@ describe("GrowableFloat64Array.HelloWorld", () => { arr.push(1); arr.push(c); const l = arr.length; - ck.testExactNumber(c, arr.at(l - 1)); - arr.setAt(l - 1, b); - ck.testExactNumber(arr.at(l - 1), b); + ck.testExactNumber(c, arr.atUncheckedIndex(l - 1)); + arr.setAtUncheckedIndex(l - 1, b); + ck.testExactNumber(arr.atUncheckedIndex(l - 1), b); arr.resize(1); ck.testExactNumber(arr.length, 1); arr.resize(5); ck.testExactNumber(arr.length, 5); ck.testExactNumber(arr.front(), 1); - ck.testExactNumber(arr.at(1), 0); + ck.testExactNumber(arr.atUncheckedIndex(1), 0); ck.testExactNumber(arr.back(), 0); const capacityB = 100; const lB = arr.length; @@ -82,21 +82,21 @@ describe("GrowableFloat64Array.HelloWorld", () => { for (let i = 0, j = n - 1; i < j; i++ , j--) { // swap odd i by logic at this level. others swap with single method call .swap if ((i % 2) === 1) { - const a = data.at(i); + const a = data.atUncheckedIndex(i); data.move(j, i); - data.setAt(j, a); + data.setAtUncheckedIndex(j, a); } else { data.swap(i, j); } } for (let i = 0; i < n; i++) - ck.testExactNumber(data0.at(i), data.at(n - 1 - i)); + ck.testExactNumber(data0.atUncheckedIndex(i), data.atUncheckedIndex(n - 1 - i)); // block copy a subset to the end .... const numCopy = n - 4; const c0 = 2; data.pushBlockCopy(2, numCopy); for (let i = 0; i < numCopy; i++) - ck.testExactNumber(data.at(c0 + i), data.at(n + i)); + ck.testExactNumber(data.atUncheckedIndex(c0 + i), data.atUncheckedIndex(n + i)); ck.checkpoint("GrowableArray.move"); expect(ck.getNumErrors()).equals(0); @@ -153,9 +153,9 @@ describe("BlockedArray", () => { // get clusters !!!! // note that the order the clusters appear has no relationship to 012345 above. - if (Checker.noisy.cluster) console.log(blocks.ToJSON()); + if (Checker.noisy.cluster) console.log(blocks.toJSON()); const clusterIndices = blocks.clusterIndicesLexical(tolerance); - if (Checker.noisy.cluster) console.log(blocks.ToJSON()); + if (Checker.noisy.cluster) console.log(blocks.toJSON()); if (Checker.noisy.cluster) console.log(JSON.stringify(clusterIndices)); for (let i = 0; i < clusterIndices.length; i++) { const k0 = clusterIndices[i]; @@ -180,9 +180,9 @@ describe("BlockedArray", () => { } } // verify the various forms of index . .. - const clusterToClusterStart = blocks.createIndex_clusterToClusterStart(clusterIndices); - const blockToClusterStart = blocks.createIndex_blockToClusterStart(clusterIndices); - const blockToClusterIndex = blocks.createIndex_blockToClusterIndex(clusterIndices); + const clusterToClusterStart = blocks.createIndexClusterToClusterStart(clusterIndices); + const blockToClusterStart = blocks.createIndexBlockToClusterStart(clusterIndices); + const blockToClusterIndex = blocks.createIndexBlockToClusterIndex(clusterIndices); const n = clusterIndices.length; ck.testExactNumber(blockToClusterStart.length, blocks.numBlocks); ck.testExactNumber(blockToClusterIndex.length, blocks.numBlocks); @@ -207,7 +207,7 @@ describe("BlockedArray", () => { blocks2.addPoint2d(Point2d.create(1, 2), 5, 6, 7); blocks2.addPoint3d(Point3d.create(1, 2, 3), 5, 6, 7); ck.testPoint3d(blocks2.getPoint3d(1), Point3d.create(1, 2, 3)); - const obj = blocks2.ToJSON(); + const obj = blocks2.toJSON(); ck.testTrue(obj[0][2][0] === 1 && obj[0][2][1] === 2); ck.testTrue(obj[1][2][0] === 1 && obj[1][2][1] === 2); blocks2.popBlock(); @@ -274,12 +274,12 @@ describe("GrowablePoint3dArray", () => { ck.testPoint3d(pointB[0], pointA.front() as Point3d); } for (let i = 0; i < n; i++) - ck.testPoint3d(pointB[i], pointA.getPoint3dAt(i) as Point3d); + ck.testPoint3d(pointB[i], pointA.getPoint3dAtUncheckedPointIndex(i) as Point3d); ck.testExactNumber(pointA.length, pointB.length, "array lengths"); let lengthA = 0; for (let i = 0; i + 1 < n; i++) { - lengthA += pointA.getPoint3dAt(i).distance(pointA.getPoint3dAt(i + 1)); + lengthA += pointA.getPoint3dAtUncheckedPointIndex(i).distance(pointA.getPoint3dAtUncheckedPointIndex(i + 1)); } const lengthA1 = pointA.sumLengths(); ck.testCoordinate(lengthA, lengthA1, "polyline length"); @@ -306,14 +306,14 @@ describe("GrowablePoint3dArray", () => { pointA.pushWrap(numWrap); ck.testExactNumber(n + numWrap, pointA.length, "pushWrap increases length"); for (let i = 0; i < numWrap; i++) { - ck.testPoint3d(pointA.getPoint3dAt(i), pointA.getPoint3dAt(n + i), "wrapped point"); + ck.testPoint3d(pointA.getPoint3dAtUncheckedPointIndex(i), pointA.getPoint3dAtUncheckedPointIndex(n + i), "wrapped point"); } let numDup = 0; const sortOrder = pointA.sortIndicesLexical(); for (let i = 0; i + 1 < pointA.length; i++) { const k0 = sortOrder[i]; const k1 = sortOrder[i + 1]; - if (pointA.getPoint3dAt(k0).isAlmostEqual(pointA.getPoint3dAt(k1))) { + if (pointA.getPoint3dAtUncheckedPointIndex(k0).isAlmostEqual(pointA.getPoint3dAtUncheckedPointIndex(k1))) { ck.testLT(k0, k1, "lexical sort preserves order for duplicates"); numDup++; } else { @@ -366,8 +366,8 @@ describe("GrowablePoint3dArray", () => { ck.testExactNumber(arr.length, 2); ck.testTrue(arr.compareLexicalBlock(0, 1) < 0 && arr.compareLexicalBlock(1, 0) > 0); const point = Point3d.create(); - arr.atPoint3dIndex(1, point); - const vector = arr.atVector3dIndex(1)!; + arr.getPoint3dAtCheckedPointIndex(1, point); + const vector = arr.getVector3dAtCheckedVectorIndex(1)!; ck.testTrue(point.isAlmostEqual(vector)); ck.testPoint3d(point, Point3d.create(4, 5, 6)); @@ -384,7 +384,7 @@ describe("GrowablePoint3dArray", () => { ck.testTrue(arr.tryTransformInverseInPlace(transform)); ck.testFalse(arr.tryTransformInverseInPlace(noInverseTransform)); - ck.testPoint3d(arr.getPoint3dAt(0), Point3d.create(1, -1, 1)); + ck.testPoint3d(arr.getPoint3dAtUncheckedPointIndex(0), Point3d.create(1, -1, 1)); arr.resize(1); @@ -406,15 +406,15 @@ describe("GrowablePoint3dArray", () => { const gPoints = new GrowableXYZArray(); gPoints.pushAll(points); const iPoints = new Point3dArrayCarrier(points); - const iOrigin = iPoints.atPoint3dIndex(0)!; - const gOrigin = gPoints.atPoint3dIndex(0)!; + const iOrigin = iPoints.getPoint3dAtCheckedPointIndex(0)!; + const gOrigin = gPoints.getPoint3dAtCheckedPointIndex(0)!; ck.testPoint3d(iOrigin, gOrigin, "point 0 access"); for (let i = 1; i + 1 < points.length; i++) { const j = i + 1; - const pointIA = iPoints.atPoint3dIndex(i)!; - const pointGA = gPoints.atPoint3dIndex(i)!; - const pointIB = iPoints.atPoint3dIndex(j)!; - const pointGB = gPoints.atPoint3dIndex(j)!; + const pointIA = iPoints.getPoint3dAtCheckedPointIndex(i)!; + const pointGA = gPoints.getPoint3dAtCheckedPointIndex(i)!; + const pointIB = iPoints.getPoint3dAtCheckedPointIndex(j)!; + const pointGB = gPoints.getPoint3dAtCheckedPointIndex(j)!; const vectorIA = iPoints.vectorIndexIndex(i, j)!; const vectorGA = gPoints.vectorIndexIndex(i, j)!; @@ -433,8 +433,8 @@ describe("GrowablePoint3dArray", () => { gPoints.crossProductXYAndZIndexIndex(gOrigin, i, j)!); ck.testVector3d( - iPoints.atVector3dIndex(i)!, - gPoints.atVector3dIndex(i)!, + iPoints.getVector3dAtCheckedVectorIndex(i)!, + gPoints.getVector3dAtCheckedVectorIndex(i)!, "atVector3dIndex"); ck.testPoint3d(Point3dArray.centroid(iPoints), Point3dArray.centroid(gPoints), "centroid"); @@ -466,8 +466,8 @@ describe("GrowablePoint3dArray", () => { const n2 = n0 - deltaN; xyzPoints.resize(n2); // blow away some points. - ck.testUndefined(xyzPoints.atVector3dIndex(-4)); - ck.testUndefined(xyzPoints.atVector3dIndex(n2)); + ck.testUndefined(xyzPoints.getVector3dAtCheckedVectorIndex(-4)); + ck.testUndefined(xyzPoints.getVector3dAtCheckedVectorIndex(n2)); // verify duplicate methods .... for (let i0 = 3; i0 < n2; i0 += 5) { @@ -484,7 +484,7 @@ describe("GrowablePoint3dArray", () => { const spacePoint = Point3d.create(1, 4, 3); for (let i0 = 2; i0 < n2; i0 += 6) { const distance0 = xyzPoints.distanceIndexToPoint(i0, spacePoint); - const distance1 = xyzPoints.atPoint3dIndex(i0)!.distance(spacePoint); + const distance1 = xyzPoints.getPoint3dAtCheckedPointIndex(i0)!.distance(spacePoint); const vectorI0 = xyzPoints.vectorXYAndZIndex(spacePoint, i0); if (ck.testPointer(vectorI0) && vectorI0 && distance0 !== undefined) { ck.testCoordinate(vectorI0.magnitude(), distance0)!; @@ -496,11 +496,11 @@ describe("GrowablePoint3dArray", () => { ck.testUndefined(xyzPoints.distance(0, -1), "distance to invalid indexB"); ck.testUndefined(xyzPoints.distanceIndexToPoint(-1, spacePoint), "distance to invalid indexA"); - ck.testFalse(xyzPoints.setCoordinates(-5, 1, 2, 3), "negative index for setCoordinates"); - ck.testFalse(xyzPoints.setCoordinates(100, 1, 2, 3), "huge index for setCoordinates"); + ck.testFalse(xyzPoints.setXYZAtCheckedPointIndex(-5, 1, 2, 3), "negative index for setCoordinates"); + ck.testFalse(xyzPoints.setXYZAtCheckedPointIndex(100, 1, 2, 3), "huge index for setCoordinates"); - ck.testFalse(xyzPoints.setAt(-5, spacePoint), "negative index for setAt"); - ck.testFalse(xyzPoints.setAt(100, spacePoint), "huge index for setAt"); + ck.testFalse(xyzPoints.setAtCheckedPointIndex(-5, spacePoint), "negative index for setAt"); + ck.testFalse(xyzPoints.setAtCheckedPointIndex(100, spacePoint), "huge index for setAt"); ck.testUndefined(xyzPoints.vectorXYAndZIndex(spacePoint, -5), "negative index for vectorXYAndZIndex"); expect(ck.getNumErrors()).equals(0); @@ -548,8 +548,8 @@ describe("GrowablePoint3dArray", () => { && ck.testPointer(array0.interpolate(k, interpolationFraction, k1, resultA)))) { const k2 = (2 * k + 1) % n0; - const point0 = array0.getPoint3dAt(k); - const point1 = array0.getPoint3dAt(k1); + const point0 = array0.getPoint3dAtUncheckedPointIndex(k); + const point1 = array0.getPoint3dAtUncheckedPointIndex(k1); const resultB = point0.interpolate(interpolationFraction, point1); ck.testPoint3d(resultA, resultB, "compare interpolation paths"); const crossA = array0.crossProductIndexIndexIndex(k, k1, k2); diff --git a/core/geometry/src/test/GrowableXYArray.test.ts b/core/geometry/src/test/geometry3d/GrowableXYArray.test.ts similarity index 80% rename from core/geometry/src/test/GrowableXYArray.test.ts rename to core/geometry/src/test/geometry3d/GrowableXYArray.test.ts index 3e9ff12..d20578e 100644 --- a/core/geometry/src/test/GrowableXYArray.test.ts +++ b/core/geometry/src/test/geometry3d/GrowableXYArray.test.ts @@ -2,21 +2,21 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Checker } from "./Checker"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { GrowableFloat64Array } from "../geometry3d/GrowableFloat64Array"; -import { GrowableXYArray } from "../geometry3d/GrowableXYArray"; +import { GrowableFloat64Array } from "../../geometry3d/GrowableFloat64Array"; +import { GrowableXYArray } from "../../geometry3d/GrowableXYArray"; // import { ClusterableArray } from "../numerics/ClusterableArray"; // import { prettyPrint } from "./testFunctions"; -import { Point2d, Vector2d } from "../geometry3d/Point2dVector2d"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Transform } from "../geometry3d/Transform"; -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { Sample } from "../serialization/GeometrySamples"; -import { Angle } from "../geometry3d/Angle"; -import { Point2dArrayCarrier } from "../geometry3d/Point2dArrayCarrier"; -import { Geometry } from "../Geometry"; -import { Range2d } from "../geometry3d/Range"; +import { Point2d, Vector2d } from "../../geometry3d/Point2dVector2d"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Transform } from "../../geometry3d/Transform"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { Sample } from "../../serialization/GeometrySamples"; +import { Angle } from "../../geometry3d/Angle"; +import { Point2dArrayCarrier } from "../../geometry3d/Point2dArrayCarrier"; +import { Geometry } from "../../Geometry"; +import { Range2d } from "../../geometry3d/Range"; /* tslint:disable: no-console */ @@ -46,12 +46,12 @@ describe("GrowableXYArray", () => { ck.testPoint2d(pointB[0], pointA.front() as Point2d); } for (let i = 0; i < n; i++) - ck.testPoint2d(pointB[i], pointA.getPoint2dAt(i) as Point2d); + ck.testPoint2d(pointB[i], pointA.getPoint2dAtUncheckedPointIndex(i) as Point2d); ck.testExactNumber(pointA.length, pointB.length, "array lengths"); let lengthA = 0; for (let i = 0; i + 1 < n; i++) { - lengthA += pointA.getPoint2dAt(i).distance(pointA.getPoint2dAt(i + 1)); + lengthA += pointA.getPoint2dAtUncheckedPointIndex(i).distance(pointA.getPoint2dAtUncheckedPointIndex(i + 1)); } const lengthA1 = pointA.sumLengths(); ck.testCoordinate(lengthA, lengthA1, "polyline length"); @@ -85,7 +85,7 @@ describe("GrowableXYArray", () => { const xyBuffer = pointAXY.float64Data(); for (let i = 0; i < n; i++) { ck.testTrue(Geometry.isSamePoint2d( - pointAXY.getPoint2dAt(i), Point2d.create(xyBuffer[2 * i], xyBuffer[2 * i + 1]))); + pointAXY.getPoint2dAtUncheckedPointIndex(i), Point2d.create(xyBuffer[2 * i], xyBuffer[2 * i + 1]))); } pointD.clear(); @@ -104,11 +104,11 @@ describe("GrowableXYArray", () => { const pointB = new GrowableXYArray(10); ck.testFalse(pointA.isAlmostEqual(pointB), "isAlmostEqual detects length"); ck.testFalse(GrowableXYArray.isAlmostEqual(pointA, pointB), "static isAlmostEqual"); - ck.testUndefined(pointA.atPoint2dIndex(-5)); + ck.testUndefined(pointA.getPoint2dAtCheckedPointIndex(-5)); for (let i = 0; i < pointA.length; i++) { pointB.pushXY(100, 100); - const xy = pointA.atPoint2dIndex(i)!; + const xy = pointA.getPoint2dAtCheckedPointIndex(i)!; pointB.transferFromGrowableXYArray(i, pointA, i); ck.testExactNumber(0, pointB.distanceIndexToPoint(i, xy)!); } @@ -117,7 +117,7 @@ describe("GrowableXYArray", () => { const pointA3d = pointA.getPoint3dArray(z0); for (let i = 0; i < pointA3d.length; i++) { const xyz = pointA3d[i]; - const xy = pointA.getPoint2dAt(i); + const xy = pointA.getPoint2dAtUncheckedPointIndex(i); ck.testExactNumber(z0, xyz.z); ck.testExactNumber(xyz.x, xy.x); ck.testExactNumber(xyz.y, xy.y); @@ -129,9 +129,9 @@ describe("GrowableXYArray", () => { const eps = 1.0e-16; const pointF = pointA.clone(); for (let i = 0; i < pointE.length; i++) { - const xy = pointE.atPoint2dIndex(i)!.plus(vector); - pointE.setAt(i, xy); - pointF.setCoordinates(i, xy.x, xy.y); + const xy = pointE.getPoint2dAtCheckedPointIndex(i)!.plus(vector); + pointE.setAtCheckedPointIndex(i, xy); + pointF.setXYZAtCheckedPointIndex(i, xy.x, xy.y); } const pointG = pointA.clone(); pointG.multiplyTransformInPlace(transform); @@ -200,14 +200,14 @@ describe("GrowableXYArray", () => { pointA.pushWrap(numWrap); ck.testExactNumber(n + numWrap, pointA.length, "pushWrap increases length"); for (let i = 0; i < numWrap; i++) { - ck.testPoint2d(pointA.getPoint2dAt(i), pointA.getPoint2dAt(n + i), "wrapped point"); + ck.testPoint2d(pointA.getPoint2dAtUncheckedPointIndex(i), pointA.getPoint2dAtUncheckedPointIndex(n + i), "wrapped point"); } let numDup = 0; const sortOrder = pointA.sortIndicesLexical(); for (let i = 0; i + 1 < pointA.length; i++) { const k0 = sortOrder[i]; const k1 = sortOrder[i + 1]; - if (pointA.getPoint2dAt(k0).isAlmostEqual(pointA.getPoint2dAt(k1))) { + if (pointA.getPoint2dAtUncheckedPointIndex(k0).isAlmostEqual(pointA.getPoint2dAtUncheckedPointIndex(k1))) { ck.testLT(k0, k1, "lexical sort preserves order for duplicates"); numDup++; } else { @@ -235,8 +235,8 @@ describe("GrowableXYArray", () => { ck.testExactNumber(arr.length, 2); ck.testTrue(arr.compareLexicalBlock(0, 1) < 0 && arr.compareLexicalBlock(1, 0) > 0); const point = Point2d.create(); - arr.atPoint2dIndex(1, point); - const vector = arr.atVector2dIndex(1)!; + arr.getPoint2dAtCheckedPointIndex(1, point); + const vector = arr.getVector2dAtCheckedVectorIndex(1)!; ck.testTrue(point.isAlmostEqual(vector)); ck.testPoint2d(point, Point2d.create(4, 5)); @@ -253,7 +253,7 @@ describe("GrowableXYArray", () => { ck.testTrue(arr.tryTransformInverseInPlace(transform)); ck.testFalse(arr.tryTransformInverseInPlace(noInverseTransform)); - ck.testPoint2d(arr.getPoint2dAt(0), Point2d.create(1, -1)); + ck.testPoint2d(arr.getPoint2dAtUncheckedPointIndex(0), Point2d.create(1, -1)); arr.resize(1); @@ -274,15 +274,15 @@ describe("GrowableXYArray", () => { const gPoints = new GrowableXYArray(); gPoints.pushAllXYAndZ(points); const iPoints = new Point2dArrayCarrier(points2d); - const iOrigin = iPoints.atPoint2dIndex(0)!; - const gOrigin = gPoints.atPoint2dIndex(0)!; + const iOrigin = iPoints.getPoint2dAtCheckedPointIndex(0)!; + const gOrigin = gPoints.getPoint2dAtCheckedPointIndex(0)!; ck.testPoint2d(iOrigin, gOrigin, "point 0 access"); for (let i = 1; i + 1 < points.length; i++) { const j = i + 1; - const pointIA = iPoints.atPoint2dIndex(i)!; - const pointGA = gPoints.atPoint2dIndex(i)!; - const pointIB = iPoints.atPoint2dIndex(j)!; - const pointGB = gPoints.atPoint2dIndex(j)!; + const pointIA = iPoints.getPoint2dAtCheckedPointIndex(i)!; + const pointGA = gPoints.getPoint2dAtCheckedPointIndex(i)!; + const pointIB = iPoints.getPoint2dAtCheckedPointIndex(j)!; + const pointGB = gPoints.getPoint2dAtCheckedPointIndex(j)!; const vectorIA = iPoints.vectorIndexIndex(i, j)!; const vectorGA = gPoints.vectorIndexIndex(i, j)!; @@ -301,8 +301,8 @@ describe("GrowableXYArray", () => { gPoints.crossProductXAndYIndexIndex(gOrigin, i, j)!); ck.testVector2d( - iPoints.atVector2dIndex(i)!, - gPoints.atVector2dIndex(i)!, + iPoints.getVector2dAtCheckedVectorIndex(i)!, + gPoints.getVector2dAtCheckedVectorIndex(i)!, "atVector2dIndex"); } @@ -333,8 +333,8 @@ describe("GrowableXYArray", () => { const n2 = n0 - deltaN; xyPoints.resize(n2); // blow away some points. - ck.testUndefined(xyPoints.atVector2dIndex(-4)); - ck.testUndefined(xyPoints.atVector2dIndex(n2)); + ck.testUndefined(xyPoints.getVector2dAtCheckedVectorIndex(-4)); + ck.testUndefined(xyPoints.getVector2dAtCheckedVectorIndex(n2)); // verify duplicate methods .... for (let i0 = 3; i0 < n2; i0 += 5) { @@ -351,7 +351,7 @@ describe("GrowableXYArray", () => { const spacePoint = Point2d.create(1, 4); for (let i0 = 2; i0 < n2; i0 += 6) { const distance0 = xyPoints.distanceIndexToPoint(i0, spacePoint); - const distance1 = xyPoints.atPoint2dIndex(i0)!.distance(spacePoint); + const distance1 = xyPoints.getPoint2dAtCheckedPointIndex(i0)!.distance(spacePoint); const vectorI0 = xyPoints.vectorXAndYIndex(spacePoint, i0); if (ck.testPointer(vectorI0) && vectorI0 && distance0 !== undefined) { ck.testCoordinate(vectorI0.magnitude(), distance0)!; @@ -361,17 +361,17 @@ describe("GrowableXYArray", () => { ck.testUndefined(xyPoints.distance(-1, 0), "distance to invalid indexA"); ck.testUndefined(xyPoints.distance(0, -1), "distance to invalid indexB"); - const point0 = xyPoints.atPoint2dIndex(0)!; + const point0 = xyPoints.getPoint2dAtCheckedPointIndex(0)!; for (let i = 1; i < xyPoints.length; i++) { ck.testExactNumber(xyPoints.distance(0, i)!, xyPoints.distanceIndexToPoint(i, point0)!); } ck.testUndefined(xyPoints.distanceIndexToPoint(-1, spacePoint), "distance to invalid indexA"); - ck.testFalse(xyPoints.setCoordinates(-5, 1, 2), "negative index for setCoordinates"); - ck.testFalse(xyPoints.setCoordinates(100, 1, 2), "huge index for setCoordinates"); + ck.testFalse(xyPoints.setXYZAtCheckedPointIndex(-5, 1, 2), "negative index for setCoordinates"); + ck.testFalse(xyPoints.setXYZAtCheckedPointIndex(100, 1, 2), "huge index for setCoordinates"); - ck.testFalse(xyPoints.setAt(-5, spacePoint), "negative index for setAt"); - ck.testFalse(xyPoints.setAt(100, spacePoint), "huge index for setAt"); + ck.testFalse(xyPoints.setAtCheckedPointIndex(-5, spacePoint), "negative index for setAt"); + ck.testFalse(xyPoints.setAtCheckedPointIndex(100, spacePoint), "huge index for setAt"); ck.testUndefined(xyPoints.vectorXAndYIndex(spacePoint, -5), "negative index for vectorXYAndZIndex"); expect(ck.getNumErrors()).equals(0); @@ -388,8 +388,8 @@ describe("GrowableXYArray", () => { const array1 = new GrowableXYArray(); // transfers with bad source index - ck.testFalse(array1.pushFromGrowableXYArray(array0, -1), "invalide source index for pushFromGrowable"); - ck.testFalse(array1.pushFromGrowableXYArray(array0, n0 + 1), "invalide source index for pushFromGrowable"); + ck.testExactNumber(0, array1.pushFromGrowableXYArray(array0, -1), "invalid source index for pushFromGrowable"); + ck.testExactNumber(0, array1.pushFromGrowableXYArray(array0, n0 + 1), "invalid source index for pushFromGrowable"); // Any trasnfer into empty array is bad . .. ck.testFalse(array1.transferFromGrowableXYArray(-1, array0, 1), "invalid source index transferFromGrowable"); ck.testFalse(array1.transferFromGrowableXYArray(0, array0, 1), "invalid source index transferFromGrowable"); @@ -405,7 +405,7 @@ describe("GrowableXYArray", () => { const resultA = Point2d.create(); const interpolationFraction = 0.321; for (let k = 1; k + 2 < n0; k++) { - ck.testTrue(array1.pushFromGrowableXYArray(array0, k), "transformFromGrowable"); + ck.testExactNumber(1, array1.pushFromGrowableXYArray(array0, k), "transformFromGrowable"); ck.testUndefined(array1.interpolate(-1, 0.3, k), "interpolate with bad index"); ck.testUndefined(array1.interpolate(100, 0.3, k), "interpolate with bad index"); @@ -419,8 +419,8 @@ describe("GrowableXYArray", () => { && ck.testPointer(array0.interpolate(k, interpolationFraction, k1, resultA)))) { const k2 = (2 * k + 1) % n0; - const point0 = array0.getPoint2dAt(k); - const point1 = array0.getPoint2dAt(k1); + const point0 = array0.getPoint2dAtUncheckedPointIndex(k); + const point1 = array0.getPoint2dAtUncheckedPointIndex(k1); const resultB = point0.interpolate(interpolationFraction, point1); ck.testPoint2d(resultA, resultB, "compare interpolation paths"); const crossA = array0.crossProductIndexIndexIndex(k, k1, k2); diff --git a/core/geometry/src/test/Plane3dByOriginAndUnitNormal.test.ts b/core/geometry/src/test/geometry3d/Plane3dByOriginAndUnitNormal.test.ts similarity index 83% rename from core/geometry/src/test/Plane3dByOriginAndUnitNormal.test.ts rename to core/geometry/src/test/geometry3d/Plane3dByOriginAndUnitNormal.test.ts index d8caa4d..dd67430 100644 --- a/core/geometry/src/test/Plane3dByOriginAndUnitNormal.test.ts +++ b/core/geometry/src/test/geometry3d/Plane3dByOriginAndUnitNormal.test.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { Plane3dByOriginAndUnitNormal } from "../geometry3d/Plane3dByOriginAndUnitNormal"; -import { Checker } from "./Checker"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { Plane3dByOriginAndUnitNormal } from "../../geometry3d/Plane3dByOriginAndUnitNormal"; +import { Checker } from "../Checker"; import { expect } from "chai"; /** Exercise two planes expected to be parallel. */ @@ -76,10 +76,10 @@ describe("Plane3dByOriginAndUnitNormal", () => { Plane3dByOriginAndUnitNormal.create(planeABU.getOriginRef(), planeABU.getNormalRef(), planeF); ck.testTrue(planeF.isAlmostEqual(planeABU), "plane set(origin, normal)"); ck.checkpoint("Plane3dByOriginAndUnitNormal.HelloWorld"); -// exercise error branches and supplied result .. - planeF.setFromJSON ({}); - planeF.setFromJSON (); - planeD.clone (planeF); + // exercise error branches and supplied result .. + planeF.setFromJSON({}); + planeF.setFromJSON(); + planeD.clone(planeF); expect(ck.getNumErrors()).equals(0); }); @@ -87,18 +87,18 @@ describe("Plane3dByOriginAndUnitNormal", () => { const ck = new Checker(); const pointA = Point3d.create(3, 2, 9); const vectorU = Vector3d.create(-3, 4, 1); - const planeAU = Plane3dByOriginAndUnitNormal.create (pointA, vectorU)!; + const planeAU = Plane3dByOriginAndUnitNormal.create(pointA, vectorU)!; const a = 1.4; - const pointB = planeAU.altitudeToPoint (a); - ck.testCoordinate (a, planeAU.altitude (pointB), "altitude match"); + const pointB = planeAU.altitudeToPoint(a); + ck.testCoordinate(a, planeAU.altitude(pointB), "altitude match"); expect(ck.getNumErrors()).equals(0); }); it("nulls", () => { const ck = new Checker(); const pointA = Point3d.create(3, 2, 9); - const failPlane = Plane3dByOriginAndUnitNormal.create (pointA, Vector3d.createZero ()); - ck.testUndefined (failPlane, "plane with null normal"); + const failPlane = Plane3dByOriginAndUnitNormal.create(pointA, Vector3d.createZero()); + ck.testUndefined(failPlane, "plane with null normal"); expect(ck.getNumErrors()).equals(0); }); }); diff --git a/core/geometry/src/test/Plane3dByOriginAndVectors.test.ts b/core/geometry/src/test/geometry3d/Plane3dByOriginAndVectors.test.ts similarity index 92% rename from core/geometry/src/test/Plane3dByOriginAndVectors.test.ts rename to core/geometry/src/test/geometry3d/Plane3dByOriginAndVectors.test.ts index d98fd3a..47a5398 100644 --- a/core/geometry/src/test/Plane3dByOriginAndVectors.test.ts +++ b/core/geometry/src/test/geometry3d/Plane3dByOriginAndVectors.test.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Vector2d } from "../geometry3d/Point2dVector2d"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Plane3dByOriginAndVectors } from "../geometry3d/Plane3dByOriginAndVectors"; -import { Checker } from "./Checker"; +import { Vector2d } from "../../geometry3d/Point2dVector2d"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Plane3dByOriginAndVectors } from "../../geometry3d/Plane3dByOriginAndVectors"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { Transform } from "../geometry3d/Transform"; +import { Transform } from "../../geometry3d/Transform"; describe("Plane3dByOriginAndVectors", () => { it("HelloWorld", () => { diff --git a/core/geometry/src/test/Point2d.test.ts b/core/geometry/src/test/geometry3d/Point2d.test.ts similarity index 95% rename from core/geometry/src/test/Point2d.test.ts rename to core/geometry/src/test/geometry3d/Point2d.test.ts index d01dfa1..57e270c 100644 --- a/core/geometry/src/test/Point2d.test.ts +++ b/core/geometry/src/test/geometry3d/Point2d.test.ts @@ -2,11 +2,11 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Point2d, Vector2d } from "../geometry3d/Point2dVector2d"; -import { Point3d } from "../geometry3d/Point3dVector3d"; -import { Sample } from "../serialization/GeometrySamples"; -import { Angle } from "../geometry3d/Angle"; -import * as bsiChecker from "./Checker"; +import { Point2d, Vector2d } from "../../geometry3d/Point2dVector2d"; +import { Point3d } from "../../geometry3d/Point3dVector3d"; +import { Sample } from "../../serialization/GeometrySamples"; +import { Angle } from "../../geometry3d/Angle"; +import * as bsiChecker from "../Checker"; // import { Sample } from "../serialization/GeometrySamples"; import { expect } from "chai"; /* tslint:disable:no-console */ diff --git a/core/geometry/src/test/Point3d.test.ts b/core/geometry/src/test/geometry3d/Point3d.test.ts similarity index 94% rename from core/geometry/src/test/Point3d.test.ts rename to core/geometry/src/test/geometry3d/Point3d.test.ts index 280eafc..322c227 100644 --- a/core/geometry/src/test/Point3d.test.ts +++ b/core/geometry/src/test/geometry3d/Point3d.test.ts @@ -2,11 +2,11 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Point3d, XYZ } from "../geometry3d/Point3dVector3d"; -import { Geometry } from "../Geometry"; -import { Angle } from "../geometry3d/Angle"; -import * as bsiChecker from "./Checker"; -import { Sample } from "../serialization/GeometrySamples"; +import { Point3d, XYZ } from "../../geometry3d/Point3dVector3d"; +import { Geometry } from "../../Geometry"; +import { Angle } from "../../geometry3d/Angle"; +import * as bsiChecker from "../Checker"; +import { Sample } from "../../serialization/GeometrySamples"; import { expect } from "chai"; /* tslint:disable:no-console */ describe("Point3d", () => { diff --git a/core/geometry/src/test/Point3dVector3d.test.ts b/core/geometry/src/test/geometry3d/Point3dVector3d.test.ts similarity index 90% rename from core/geometry/src/test/Point3dVector3d.test.ts rename to core/geometry/src/test/geometry3d/Point3dVector3d.test.ts index e010ca5..fdee968 100644 --- a/core/geometry/src/test/Point3dVector3d.test.ts +++ b/core/geometry/src/test/geometry3d/Point3dVector3d.test.ts @@ -2,20 +2,20 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Transform } from "../geometry3d/Transform"; -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { Range3d } from "../geometry3d/Range"; -import { Angle } from "../geometry3d/Angle"; +import { Transform } from "../../geometry3d/Transform"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { Range3d } from "../../geometry3d/Range"; +import { Angle } from "../../geometry3d/Angle"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import * as bsiChecker from "./Checker"; -import { Sample } from "../serialization/GeometrySamples"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import * as bsiChecker from "../Checker"; +import { Sample } from "../../serialization/GeometrySamples"; import { expect } from "chai"; -import { AxisOrder } from "../Geometry"; +import { AxisOrder } from "../../Geometry"; /* tslint:disable:no-console */ export class MatrixTests { - public static TestCreateProperties(ck: bsiChecker.Checker) { + public static testCreateProperties(ck: bsiChecker.Checker) { const matrix = Matrix3d.createRotationAroundVector(Vector3d.create(0, 0, 1), Angle.createDegrees(45.0)); if (matrix) { const vectorA = Vector3d.create(10, 1, 1); @@ -51,13 +51,13 @@ export class MatrixTests { ck.testPerpendicular(vectorB, frame.columnZ(), "input Y perp frame.Z"); ck.testParallel(vectorA, frame.columnX(), "input X parallel frame.X"); ck.testCoordinateOrder(0, vectorB.dotProduct(frame.columnY()), "vectorB in positive XY half plane"); - MatrixTests.CheckProperties(ck, frame, false, true, true, true, undefined); + MatrixTests.checkProperties(ck, frame, false, true, true, true, undefined); const frame1 = frame.multiplyMatrixMatrix(Matrix3d.createScale(1, 1, 2)); - MatrixTests.CheckProperties(ck, frame1, false, false, false, true, false); + MatrixTests.checkProperties(ck, frame1, false, false, false, true, false); const frame2 = frame.multiplyMatrixMatrix(Matrix3d.createScale(1, 1, -1)); - MatrixTests.CheckProperties(ck, frame2, false, true, false, true, false); + MatrixTests.checkProperties(ck, frame2, false, true, false, true, false); let vector; const e = 1.0 / 64.0; for (vector of [ @@ -71,7 +71,7 @@ export class MatrixTests { ]) { const triad = Matrix3d.createRigidHeadsUp(vector); if (ck.testPointer(triad) && triad) { - MatrixTests.CheckProperties(ck, triad, undefined, true, true, true, + MatrixTests.checkProperties(ck, triad, undefined, true, true, true, vector.isAlmostEqual(Vector3d.unitZ())); ck.testParallel(vector, triad.columnZ()); } @@ -79,7 +79,7 @@ export class MatrixTests { } } } - public static CheckInverse(ck: bsiChecker.Checker, matrixA: Matrix3d) { + public static checkInverse(ck: bsiChecker.Checker, matrixA: Matrix3d) { const matrixB = matrixA.inverse(); ck.testPointer(matrixB, "inverse"); // console.log("matrixA", matrixA); @@ -89,7 +89,7 @@ export class MatrixTests { ck.testBoolean(true, AB.isIdentity, "A * Ainverse = I"); } } - public static CheckProperties( + public static checkProperties( ck: bsiChecker.Checker, matrix: Matrix3d, isIdentity: boolean | undefined, @@ -116,7 +116,7 @@ export class MatrixTests { if (isDiagonal !== undefined) ck.testBoolean(isDiagonal, matrix.isDiagonal, "isDiagonal"); } - public static CheckPointArrays( + public static checkPointArrays( ck: bsiChecker.Checker, pointA: Point3d[]) { const transform = Transform.createScaleAboutPoint(Point3d.create(3, 3, 3), 2); const pointB = transform.multiplyPoint3dArray(pointA); @@ -143,7 +143,7 @@ export class MatrixTests { describe("Matrix3d.Construction", () => { it("Verify properties of Matrix3d.create", () => { const ck = new bsiChecker.Checker(); - MatrixTests.TestCreateProperties(ck); + MatrixTests.testCreateProperties(ck); ck.checkpoint("End Matrix3d.Construction"); expect(ck.getNumErrors()).equals(0); }); @@ -155,7 +155,7 @@ describe("Matrix3d.Inverse", () => { const matrixA = Matrix3d.createRowValues(4, 2, 1, -1, 5, 3, 0.5, 0.75, 9); - MatrixTests.CheckInverse(ck, matrixA); + MatrixTests.checkInverse(ck, matrixA); ck.checkpoint("End Matrix3d.Inverse"); expect(ck.getNumErrors()).equals(0); }); @@ -165,7 +165,7 @@ describe("Point3dArray.HelloWorld", () => { it("Point3dArray.HelloWorld", () => { const ck = new bsiChecker.Checker(); const pointA = [Point3d.create(1, 2, 3), Point3d.create(4, 5, 2)]; - MatrixTests.CheckPointArrays(ck, pointA); + MatrixTests.checkPointArrays(ck, pointA); ck.checkpoint("Point3dArray.HelloWorld"); expect(ck.getNumErrors()).equals(0); }); diff --git a/core/geometry/src/test/PointHelper.test.ts b/core/geometry/src/test/geometry3d/PointHelper.test.ts similarity index 91% rename from core/geometry/src/test/PointHelper.test.ts rename to core/geometry/src/test/geometry3d/PointHelper.test.ts index cb5bb77..0d1b6da 100644 --- a/core/geometry/src/test/PointHelper.test.ts +++ b/core/geometry/src/test/geometry3d/PointHelper.test.ts @@ -2,27 +2,27 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Geometry, AxisScaleSelect } from "../Geometry"; -import { Angle } from "../geometry3d/Angle"; -import { Plane3dByOriginAndUnitNormal } from "../geometry3d/Plane3dByOriginAndUnitNormal"; -import { Matrix4d } from "../geometry4d/Matrix4d"; -import { Point2d, Vector2d } from "../geometry3d/Point2dVector2d"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Range3d } from "../geometry3d/Range"; -import { Transform } from "../geometry3d/Transform"; -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { LineString3d } from "../curve/LineString3d"; -import { Arc3d } from "../curve/Arc3d"; -import { StrokeOptions } from "../curve/StrokeOptions"; -import { PolygonOps, Point3dArray, Point2dArray, Vector3dArray, Point4dArray, NumberArray, Point3dArrayCarrier } from "../geometry3d/PointHelpers"; -import { FrameBuilder } from "../geometry3d/FrameBuilder"; +import { Geometry, AxisScaleSelect } from "../../Geometry"; +import { Angle } from "../../geometry3d/Angle"; +import { Plane3dByOriginAndUnitNormal } from "../../geometry3d/Plane3dByOriginAndUnitNormal"; +import { Matrix4d } from "../../geometry4d/Matrix4d"; +import { Point2d, Vector2d } from "../../geometry3d/Point2dVector2d"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Range3d } from "../../geometry3d/Range"; +import { Transform } from "../../geometry3d/Transform"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { LineString3d } from "../../curve/LineString3d"; +import { Arc3d } from "../../curve/Arc3d"; +import { StrokeOptions } from "../../curve/StrokeOptions"; +import { PolygonOps, Point3dArray, Point2dArray, Vector3dArray, Point4dArray, NumberArray, Point3dArrayCarrier } from "../../geometry3d/PointHelpers"; +import { FrameBuilder } from "../../geometry3d/FrameBuilder"; import { MatrixTests } from "./Point3dVector3d.test"; -import { Checker } from "./Checker"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { Sample } from "../serialization/GeometrySamples"; -import { MomentData } from "../geometry4d/MomentData"; -import { GrowableXYZArray } from "../geometry3d/GrowableXYZArray"; -import { Point4d } from "../geometry4d/Point4d"; +import { Sample } from "../../serialization/GeometrySamples"; +import { MomentData } from "../../geometry4d/MomentData"; +import { GrowableXYZArray } from "../../geometry3d/GrowableXYZArray"; +import { Point4d } from "../../geometry4d/Point4d"; /* tslint:disable:no-console */ describe("FrameBuilder.HelloWorld", () => { @@ -139,7 +139,7 @@ describe("FrameBuilder.HelloWorldB", () => { AxisScaleSelect.NonUniformRangeContainment]) { const localToWorld = FrameBuilder.createLocalToWorldTransformInRange(range, select, 0, 0, 0, 2.0); if (ck.testPointer(localToWorld) && localToWorld) { - MatrixTests.CheckProperties(ck, + MatrixTests.checkProperties(ck, localToWorld.matrix, select === AxisScaleSelect.Unit, // unit axes in range are identity select === AxisScaleSelect.Unit, // and of course unitPerpendicular @@ -263,7 +263,18 @@ describe("MomentData.HelloWorld", () => { } } } + + const axes122 = Matrix3d.createScale(2, 1, 2); + const momentsXYZ = Vector3d.create(2, 1, 2); + MomentData.sortColumnsForIncreasingMoments(axes122, momentsXYZ); ck.checkpoint("MomentData.HelloWorld"); + + ck.testExactNumber(1, momentsXYZ.at(0)); + ck.testExactNumber(2, momentsXYZ.at(1)); + ck.testExactNumber(2, momentsXYZ.at(2)); + ck.testCoordinate(1, axes122.columnXMagnitude()); + ck.testCoordinate(2, axes122.columnYMagnitude()); + ck.testCoordinate(2, axes122.columnZMagnitude()); expect(ck.getNumErrors()).equals(0); }); }); @@ -530,10 +541,10 @@ describe("Point3dArray", () => { const a = carrier.length; // These methdos should return undefined if any index is bad. // (we know the index tests happen in a single validation function -- "some" calls need to test both extremes of out-of-bounds, but any pariticular arg only has to be tested in one direction) - ck.testUndefined(carrier.atPoint3dIndex(-1)); - ck.testUndefined(carrier.atPoint3dIndex(a)); - ck.testUndefined(carrier.atVector3dIndex(-1)); - ck.testUndefined(carrier.atVector3dIndex(a)); + ck.testUndefined(carrier.getPoint3dAtCheckedPointIndex(-1)); + ck.testUndefined(carrier.getPoint3dAtCheckedPointIndex(a)); + ck.testUndefined(carrier.getVector3dAtCheckedVectorIndex(-1)); + ck.testUndefined(carrier.getVector3dAtCheckedVectorIndex(a)); const cross = Vector3d.create(); ck.testUndefined(carrier.accumulateCrossProductIndexIndexIndex(-1, 1, 3, cross)); @@ -600,7 +611,7 @@ describe("Point3dArray", () => { ck.testFalse(NumberArray.isAlmostEqual([], dataA0, 0.01)); ck.testFalse(NumberArray.isAlmostEqual(dataA0, undefined, 0.01)); ck.testFalse(NumberArray.isAlmostEqual(undefined, dataA0, 0.01)); - ck.testExactNumber(0, NumberArray.PreciseSum([])); + ck.testExactNumber(0, NumberArray.preciseSum([])); const e = 0.01; dataA1[3] += e; ck.testTrue(NumberArray.isAlmostEqual(dataA0, dataA1, 2 * e)); @@ -610,7 +621,7 @@ describe("Point3dArray", () => { ck.testTrue(NumberArray.isCoordinateInArray(x, dataA0)); ck.testFalse(NumberArray.isCoordinateInArray(x + 0.1231897897, dataA0)); } - ck.testExactNumber(0, NumberArray.MaxAbsArray([])); + ck.testExactNumber(0, NumberArray.maxAbsArray([])); expect(ck.getNumErrors()).equals(0); }); @@ -662,15 +673,15 @@ describe("Point3dArray", () => { expect(ck.getNumErrors()).equals(0); }); }); -/* + function compareAreaData(ck: Checker, polygonA: Point3d[], polygonB: Point3d[] | GrowableXYZArray) { const areaA = PolygonOps.area(polygonA); - const rayA = PolygonOps.centroidAreaNormal (polygonA); + // const rayA = PolygonOps.centroidAreaNormal(polygonA); let areaB = -10203213; - let rayB = Ray3d.create (); + // let rayB = Ray3d.create(); if (polygonB instanceof GrowableXYZArray) { const normalB = Vector3d.create(); - PolygonOps.areaNormalGrowablePoint3dArrayGo(polygonB, normalB); + PolygonOps.areaNormalGo(polygonB, normalB); areaB = normalB.magnitude(); } else { areaB = PolygonOps.area(polygonB); @@ -698,4 +709,3 @@ describe("PolygonAreas", () => { expect(ck.getNumErrors()).equals(0); }); }); -*/ diff --git a/core/geometry/src/test/Quaternion.test.ts b/core/geometry/src/test/geometry3d/Quaternion.test.ts similarity index 86% rename from core/geometry/src/test/Quaternion.test.ts rename to core/geometry/src/test/geometry3d/Quaternion.test.ts index 0b6761a..cb9632f 100644 --- a/core/geometry/src/test/Quaternion.test.ts +++ b/core/geometry/src/test/geometry3d/Quaternion.test.ts @@ -2,12 +2,12 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Matrix3d } from "../geometry3d/Matrix3d"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; import { expect } from "chai"; -import { Vector3d } from "../geometry3d/Point3dVector3d"; -import { YawPitchRollAngles } from "../geometry3d/YawPitchRollAngles"; -import * as bsiChecker from "./Checker"; -import { Point4d } from "../geometry4d/Point4d"; +import { Vector3d } from "../../geometry3d/Point3dVector3d"; +import { YawPitchRollAngles } from "../../geometry3d/YawPitchRollAngles"; +import * as bsiChecker from "../Checker"; +import { Point4d } from "../../geometry4d/Point4d"; /* tslint:disable:no-console */ function rotatexyzw(xyzw: Point4d): Point4d { diff --git a/core/geometry/src/test/Range1dArray.test.ts b/core/geometry/src/test/geometry3d/Range1dArray.test.ts similarity index 93% rename from core/geometry/src/test/Range1dArray.test.ts rename to core/geometry/src/test/geometry3d/Range1dArray.test.ts index 1d42490..fb40703 100644 --- a/core/geometry/src/test/Range1dArray.test.ts +++ b/core/geometry/src/test/geometry3d/Range1dArray.test.ts @@ -2,12 +2,12 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Geometry } from "../Geometry"; -import { Range1d } from "../geometry3d/Range"; -import { Range1dArray, compareRange1dLexicalLowHigh } from "../numerics/Range1dArray"; -import { Checker } from "./Checker"; +import { Geometry } from "../../Geometry"; +import { Range1d } from "../../geometry3d/Range"; +import { Range1dArray, compareRange1dLexicalLowHigh } from "../../numerics/Range1dArray"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { GrowableFloat64Array } from "../geometry3d/GrowableFloat64Array"; +import { GrowableFloat64Array } from "../../geometry3d/GrowableFloat64Array"; // import { prettyPrint } from "./testFunctions"; /* tslint:disable:no-console */ @@ -24,13 +24,13 @@ import { GrowableFloat64Array } from "../geometry3d/GrowableFloat64Array"; function constructGapPoints(data: GrowableFloat64Array, leftDelta: undefined | number, interiorFraction: undefined | number, rightDelta: undefined | number): GrowableFloat64Array { const result = new GrowableFloat64Array(); if (data.length > 0) { - let a = data.at(0); + let a = data.atUncheckedIndex(0); let b; if (leftDelta !== undefined) result.push(a + leftDelta); if (interiorFraction !== undefined) { for (let i = 1; i < data.length; i++ , a = b) { - b = data.at(i); + b = data.atUncheckedIndex(i); result.push(Geometry.interpolate(a, interiorFraction, b)); } if (rightDelta !== undefined) @@ -58,7 +58,7 @@ function testUnionSimplify(ck: Checker, dataA: Range1d[]): void { for (const testPoints of [testPointsA, testPointsB]) for (let i = 0; i < testPoints.length; i++) { { - const value = testPoints.at(i); + const value = testPoints.atUncheckedIndex(i); const inA = Range1dArray.testUnion(dataA, value); const inB = Range1dArray.testUnion(dataB, value); ck.testBoolean(inA, inB, "Union simplification agrees"); @@ -78,7 +78,7 @@ function testParitySimplify(ck: Checker, dataA: Range1d[]): void { for (const testPoints of [testPointsA, testPointsB]) for (let i = 0; i < testPoints.length; i++) { { - const value = testPoints.at(i); + const value = testPoints.atUncheckedIndex(i); const inA = Range1dArray.testParity(dataA, value); const inB = Range1dArray.testParity(dataB, value); ck.testBoolean(inA, inB, "Union simplification agrees"); diff --git a/core/geometry/src/test/Range3d.test.ts b/core/geometry/src/test/geometry3d/Range3d.test.ts similarity index 96% rename from core/geometry/src/test/Range3d.test.ts rename to core/geometry/src/test/geometry3d/Range3d.test.ts index 4642e38..e71968d 100644 --- a/core/geometry/src/test/Range3d.test.ts +++ b/core/geometry/src/test/geometry3d/Range3d.test.ts @@ -2,12 +2,12 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Point3d } from "../geometry3d/Point3dVector3d"; -import { Transform } from "../geometry3d/Transform"; -import { Geometry } from "../Geometry"; -import { RangeBase, Range1d, Range2d, Range3d } from "../geometry3d/Range"; -import { Checker } from "./Checker"; -import { Sample } from "../serialization/GeometrySamples"; +import { Point3d } from "../../geometry3d/Point3dVector3d"; +import { Transform } from "../../geometry3d/Transform"; +import { Geometry } from "../../Geometry"; +import { RangeBase, Range1d, Range2d, Range3d } from "../../geometry3d/Range"; +import { Checker } from "../Checker"; +import { Sample } from "../../serialization/GeometrySamples"; import { expect, assert } from "chai"; /* tslint:disable:no-console */ diff --git a/core/geometry/src/test/Ray3d.test.ts b/core/geometry/src/test/geometry3d/Ray3d.test.ts similarity index 92% rename from core/geometry/src/test/Ray3d.test.ts rename to core/geometry/src/test/geometry3d/Ray3d.test.ts index 925b9ba..a70610f 100644 --- a/core/geometry/src/test/Ray3d.test.ts +++ b/core/geometry/src/test/geometry3d/Ray3d.test.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Transform } from "../geometry3d/Transform"; -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { Ray3d } from "../geometry3d/Ray3d"; -import { Checker } from "./Checker"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Transform } from "../../geometry3d/Transform"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { Ray3d } from "../../geometry3d/Ray3d"; +import { Checker } from "../Checker"; import { expect } from "chai"; /** create rays, using optional result (which may be undefined) diff --git a/core/geometry/src/test/RotMatrix.test.ts b/core/geometry/src/test/geometry3d/RotMatrix.test.ts similarity index 95% rename from core/geometry/src/test/RotMatrix.test.ts rename to core/geometry/src/test/geometry3d/RotMatrix.test.ts index d77b184..c446418 100644 --- a/core/geometry/src/test/RotMatrix.test.ts +++ b/core/geometry/src/test/geometry3d/RotMatrix.test.ts @@ -2,14 +2,14 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Point2d } from "../geometry3d/Point2dVector2d"; -import { Vector3d, Point3d } from "../geometry3d/Point3dVector3d"; -import { Matrix3d, InverseMatrixState } from "../geometry3d/Matrix3d"; -import { Transform } from "../geometry3d/Transform"; -import { AxisOrder, Geometry, AxisIndex } from "../Geometry"; -import { Angle } from "../geometry3d/Angle"; -import { Sample } from "../serialization/GeometrySamples"; -import { Checker } from "./Checker"; +import { Point2d } from "../../geometry3d/Point2dVector2d"; +import { Vector3d, Point3d } from "../../geometry3d/Point3dVector3d"; +import { Matrix3d, InverseMatrixState } from "../../geometry3d/Matrix3d"; +import { Transform } from "../../geometry3d/Transform"; +import { AxisOrder, Geometry, AxisIndex } from "../../Geometry"; +import { Angle } from "../../geometry3d/Angle"; +import { Sample } from "../../serialization/GeometrySamples"; +import { Checker } from "../Checker"; // import { prettyPrint } from "./testFunctions"; import { expect } from "chai"; /* tslint:disable:no-console */ @@ -584,7 +584,7 @@ describe("Matrix3d.ViewConstructions", () => { const origin = Point2d.create(4, 3); columnX.z = columnY.z = 0.0; for (const v of vectors) { - const xy1 = Matrix3d.XYPlusMatrixTimesXY(origin, projector, v); + const xy1 = Matrix3d.xyPlusMatrixTimesXY(origin, projector, v); const xy2 = origin.plus2Scaled(columnX, v.x, columnY, v.y); ck.testPoint2d(xy1, xy2); @@ -623,9 +623,9 @@ describe("Matrix3d.ViewConstructions", () => { const origin = Point3d.create(4, 3, 0.1231); const w = 0.9213123678687689769; for (const v of vectors) { - const resultBW = Matrix3d.XYZPlusMatrixTimesWeightedCoordinatesToFloat64Array(origin, matrix, v.x, v.y, v.z, w); + const resultBW = Matrix3d.xyzPlusMatrixTimesWeightedCoordinatesToFloat64Array(origin, matrix, v.x, v.y, v.z, w); const resultAW = Point3d.createScale(origin, w).plus3Scaled(columnX, v.x, columnY, v.y, columnZ, v.z); - const resultB = Matrix3d.XYZPlusMatrixTimesCoordinatesToFloat64Array(origin, matrix, v.x, v.y, v.z); + const resultB = Matrix3d.xyzPlusMatrixTimesCoordinatesToFloat64Array(origin, matrix, v.x, v.y, v.z); const resultA = origin.plus3Scaled(columnX, v.x, columnY, v.y, columnZ, v.z); ck.testXYZ(resultA, Vector3d.createFrom(resultB), "XYZPlusMatrixTimesWeightedCoordinatesToFloat64Array"); ck.testXYZ(resultAW, Vector3d.createFrom(resultBW), "XYZPlusMatrixTimesCoordinatesToFloat64Array"); diff --git a/core/geometry/src/test/Segment1d.test.ts b/core/geometry/src/test/geometry3d/Segment1d.test.ts similarity index 75% rename from core/geometry/src/test/Segment1d.test.ts rename to core/geometry/src/test/geometry3d/Segment1d.test.ts index f65e1bb..4e80426 100644 --- a/core/geometry/src/test/Segment1d.test.ts +++ b/core/geometry/src/test/geometry3d/Segment1d.test.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Segment1d } from "../geometry3d/Segment1d"; -import { Geometry } from "../Geometry"; -import { Checker } from "./Checker"; +import { Segment1d } from "../../geometry3d/Segment1d"; +import { Geometry } from "../../Geometry"; +import { Checker } from "../Checker"; import { expect } from "chai"; function verifySegment(ck: Checker, a: number, b: number) { @@ -13,7 +13,7 @@ function verifySegment(ck: Checker, a: number, b: number) { const s1 = s0.clone(); const s2 = Segment1d.create(); const s3 = Segment1d.create(); - const s4 = Segment1d.create (a, b, Segment1d.create()); + const s4 = Segment1d.create(a, b, Segment1d.create()); s3.setFrom(s0); s2.set(a, b); @@ -21,10 +21,10 @@ function verifySegment(ck: Checker, a: number, b: number) { ck.testSegment1d(s0, s2); ck.testSegment1d(s0, s3); ck.testSegment1d(s0, s4); - s3.reverseInPlace (); + s3.reverseInPlace(); for (const f of [-1, 0.2, 0.5, 0.9, 2]) { - ck.testCoordinate(s0.fractionToPoint(f), Geometry.interpolate (a, f, b)); - ck.testCoordinate(s0.fractionToPoint(f), s3.fractionToPoint (1.0 - f)); + ck.testCoordinate(s0.fractionToPoint(f), Geometry.interpolate(a, f, b)); + ck.testCoordinate(s0.fractionToPoint(f), s3.fractionToPoint(1.0 - f)); } } describe("Segment1d", () => { diff --git a/core/geometry/src/test/Transform.test.ts b/core/geometry/src/test/geometry3d/Transform.test.ts similarity index 94% rename from core/geometry/src/test/Transform.test.ts rename to core/geometry/src/test/geometry3d/Transform.test.ts index 05d87af..21b4466 100644 --- a/core/geometry/src/test/Transform.test.ts +++ b/core/geometry/src/test/geometry3d/Transform.test.ts @@ -2,15 +2,15 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Point2d } from "../geometry3d/Point2dVector2d"; -import { Point3d } from "../geometry3d/Point3dVector3d"; -import { Transform } from "../geometry3d/Transform"; -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { Checker } from "./Checker"; +import { Point2d } from "../../geometry3d/Point2dVector2d"; +import { Point3d } from "../../geometry3d/Point3dVector3d"; +import { Transform } from "../../geometry3d/Transform"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { Checker } from "../Checker"; // import { prettyPrint } from "./testFunctions"; -import { Sample } from "../serialization/GeometrySamples"; +import { Sample } from "../../serialization/GeometrySamples"; import { expect } from "chai"; -import { AxisOrder } from "../Geometry"; +import { AxisOrder } from "../../Geometry"; /* tslint:disable:no-console */ describe("Transform", () => { diff --git a/core/geometry/src/test/Vector3d.test.ts b/core/geometry/src/test/geometry3d/Vector3d.test.ts similarity index 91% rename from core/geometry/src/test/Vector3d.test.ts rename to core/geometry/src/test/geometry3d/Vector3d.test.ts index b33143c..1d52295 100644 --- a/core/geometry/src/test/Vector3d.test.ts +++ b/core/geometry/src/test/geometry3d/Vector3d.test.ts @@ -2,9 +2,9 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Angle } from "../geometry3d/Angle"; -import * as bsiChecker from "./Checker"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Angle } from "../../geometry3d/Angle"; +import * as bsiChecker from "../Checker"; // import { Sample } from "../serialization/GeometrySamples"; import { expect } from "chai"; /* tslint:disable:no-console */ @@ -63,7 +63,7 @@ describe("Vector3d", () => { ck.testCoordinate(1.0, vectorB.magnitude(), "unit vector magnitude"); const vectorC = Vector3d.createZero(); const vectorD = vectorC.normalizeWithDefault(0, 0, 1); - ck.testVector3d (vectorD, Vector3d.unitZ ()); + ck.testVector3d(vectorD, Vector3d.unitZ()); ck.checkpoint("Point3dArray.HelloWorld"); expect(ck.getNumErrors()).equals(0); }); diff --git a/core/geometry/src/test/ViewBox.test.ts b/core/geometry/src/test/geometry3d/ViewBox.test.ts similarity index 91% rename from core/geometry/src/test/ViewBox.test.ts rename to core/geometry/src/test/geometry3d/ViewBox.test.ts index c5ffcd0..e20ae60 100644 --- a/core/geometry/src/test/ViewBox.test.ts +++ b/core/geometry/src/test/geometry3d/ViewBox.test.ts @@ -2,23 +2,23 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { IndexedPolyface } from "../polyface/Polyface"; -import { PolyfaceBuilder } from "../polyface/PolyfaceBuilder"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { YawPitchRollAngles } from "../geometry3d/YawPitchRollAngles"; -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { Transform } from "../geometry3d/Transform"; -import { Loop } from "../curve/Loop"; -import { Checker } from "./Checker"; +import { IndexedPolyface } from "../../polyface/Polyface"; +import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { YawPitchRollAngles } from "../../geometry3d/YawPitchRollAngles"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { Transform } from "../../geometry3d/Transform"; +import { Loop } from "../../curve/Loop"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { GeometryCoreTestIO } from "./GeometryCoreTestIO"; -import { GeometryQuery } from "../curve/GeometryQuery"; -import { LineSegment3d } from "../curve/LineSegment3d"; -import { LineString3d } from "../curve/LineString3d"; -import { Arc3d } from "../curve/Arc3d"; -import { StandardViewIndex } from "../Geometry"; -import { AngleSweep } from "../geometry3d/AngleSweep"; -import { Sample } from "../serialization/GeometrySamples"; +import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; +import { GeometryQuery } from "../../curve/GeometryQuery"; +import { LineSegment3d } from "../../curve/LineSegment3d"; +import { LineString3d } from "../../curve/LineString3d"; +import { Arc3d } from "../../curve/Arc3d"; +import { StandardViewIndex } from "../../Geometry"; +import { AngleSweep } from "../../geometry3d/AngleSweep"; +import { Sample } from "../../serialization/GeometrySamples"; /* tslint:disable:no-console */ /* Create an XYZ triad with arcs to clarify XY and XYZ planes */ @@ -315,9 +315,9 @@ describe("RaggedMatrix", () => { const matrixB = yprA.toMatrix3d(); const matrixC = yprC!.toMatrix3d(); const diffBC = matrixB.maxDiff(matrixC); - const diffAB = matrixB.maxDiff (raggedMatrix); - ck.testLT (diffBC, 5.0e-7, "ragged matrix YPR round trip versus cleanup rigid"); - ck.testLT (diffAB, 5.0e-7, "ragged matrix YPR round trip versus raggedMatrix"); + const diffAB = matrixB.maxDiff(raggedMatrix); + ck.testLT(diffBC, 5.0e-7, "ragged matrix YPR round trip versus cleanup rigid"); + ck.testLT(diffAB, 5.0e-7, "ragged matrix YPR round trip versus raggedMatrix"); if (Checker.noisy.RaggedViewMatrix) { console.log(" clean matrix ", cleanMatrix.toJSON()); console.log(" maxDiff " + maxDiff); diff --git a/core/geometry/src/test/YPR.test.ts b/core/geometry/src/test/geometry3d/YPR.test.ts similarity index 74% rename from core/geometry/src/test/YPR.test.ts rename to core/geometry/src/test/geometry3d/YPR.test.ts index 1835b36..11e57ec 100644 --- a/core/geometry/src/test/YPR.test.ts +++ b/core/geometry/src/test/geometry3d/YPR.test.ts @@ -2,15 +2,16 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Vector3d, Point3d } from "../geometry3d/Point3dVector3d"; -import { YawPitchRollAngles } from "../geometry3d/YawPitchRollAngles"; -import { Transform } from "../geometry3d/Transform"; +import { Vector3d, Point3d } from "../../geometry3d/Point3dVector3d"; +import { YawPitchRollAngles } from "../../geometry3d/YawPitchRollAngles"; +import { Transform } from "../../geometry3d/Transform"; -import { Geometry } from "../Geometry"; -import { Angle } from "../geometry3d/Angle"; -import * as bsiChecker from "./Checker"; +import { Geometry } from "../../Geometry"; +import { Angle } from "../../geometry3d/Angle"; +import * as bsiChecker from "../Checker"; // import { Sample } from "../serialization/GeometrySamples"; import { expect, assert } from "chai"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; /* tslint:disable:no-console */ describe("YPR", () => { it("hello", () => { @@ -78,4 +79,17 @@ describe("YPR", () => { ck.testTrue(ypr0.isAlmostEqual(ypr2)); expect(ck.getNumErrors()).equals(0); }); + it("mirror2d", () => { + const ck = new bsiChecker.Checker(); + const mirror45 = Matrix3d.createRowValues(0, 1, 0, 1, 0, 0, 0, 0, -1); + ck.testExactNumber(1.0, mirror45.determinant(), "confirm correct z direction for rotate around 45 degrees"); + const ypr = YawPitchRollAngles.createFromMatrix3d(mirror45); + if (ck.testPointer(ypr, "confirm ypr created from mirror xy") && ypr) { + ck.testAngleNoShift(Angle.createDegrees(180), ypr.roll); + ck.testAngleNoShift(Angle.createDegrees(90), ypr.yaw); + ck.testAngleNoShift(Angle.createDegrees(0), ypr.pitch); + } + expect(ck.getNumErrors()).equals(0); + }); + }); diff --git a/core/geometry/src/test/Geometry4d.test.ts b/core/geometry/src/test/geometry4d/Geometry4d.test.ts similarity index 88% rename from core/geometry/src/test/Geometry4d.test.ts rename to core/geometry/src/test/geometry4d/Geometry4d.test.ts index c778df8..5a56339 100644 --- a/core/geometry/src/test/Geometry4d.test.ts +++ b/core/geometry/src/test/geometry4d/Geometry4d.test.ts @@ -2,24 +2,24 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { PlaneByOriginAndVectors4d } from "../geometry4d/PlaneByOriginAndVectors4d"; -import { Map4d } from "../geometry4d/Map4d"; -import { Matrix4d } from "../geometry4d/Matrix4d"; -import { Point4d } from "../geometry4d/Point4d"; -import { Plane3dByOriginAndVectors } from "../geometry3d/Plane3dByOriginAndVectors"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Range3d } from "../geometry3d/Range"; -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { Transform } from "../geometry3d/Transform"; -import { LineString3d } from "../curve/LineString3d"; -import { LineSegment3d } from "../curve/LineSegment3d"; -import { Angle } from "../geometry3d/Angle"; -import * as bsiChecker from "./Checker"; +import { PlaneByOriginAndVectors4d } from "../../geometry4d/PlaneByOriginAndVectors4d"; +import { Map4d } from "../../geometry4d/Map4d"; +import { Matrix4d } from "../../geometry4d/Matrix4d"; +import { Point4d } from "../../geometry4d/Point4d"; +import { Plane3dByOriginAndVectors } from "../../geometry3d/Plane3dByOriginAndVectors"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Range3d } from "../../geometry3d/Range"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { Transform } from "../../geometry3d/Transform"; +import { LineString3d } from "../../curve/LineString3d"; +import { LineSegment3d } from "../../curve/LineSegment3d"; +import { Angle } from "../../geometry3d/Angle"; +import * as bsiChecker from "../Checker"; import { expect } from "chai"; -import { prettyPrint } from "./testFunctions"; -import { GeometryQuery } from "../curve/GeometryQuery"; -import { GeometryCoreTestIO } from "./GeometryCoreTestIO"; -import { SmallSystem } from "../numerics/Polynomials"; +import { prettyPrint } from "../testFunctions"; +import { GeometryQuery } from "../../curve/GeometryQuery"; +import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; +import { SmallSystem } from "../../numerics/Polynomials"; /* tslint:disable:no-console variable-name */ /** @@ -229,6 +229,7 @@ describe("Geometry4d.BoxMap", () => { const highA = Point3d.create(2, 3, 5); const lowB = Point3d.create(100, 100, 100); const highB = Point3d.create(101, 101, 101); + if (bsiChecker.Checker.noisy.boxMap) { console.log("lowA", lowA); console.log("highA", highA); @@ -236,6 +237,18 @@ describe("Geometry4d.BoxMap", () => { console.log("highB", highB); } const map = Map4d.createBoxMap(lowA, highA, lowB, highB); + const map2 = Map4d.createIdentity(); + const map3 = Map4d.createBoxMap(lowA, highA, lowB, highB, map2); + ck.testPointer(map); + ck.testPointer(map3); + ck.testTrue(map2 === map3, "reuse createBoxMap result"); + ck.testTrue(map!.isAlmostEqual(map3!), "verify reused map"); + ck.testUndefined(Map4d.createBoxMap(lowA, highA, lowB, lowB)); + ck.testUndefined(Map4d.createBoxMap(highA, highA, lowB, highB)); + ck.testUndefined(Map4d.createVectorFrustum(Point3d.create(0, 0, 1), + Vector3d.unitX(), Vector3d.unitY(), Vector3d.unitY(), 0.8), "expect no map from singular axes"); + const map4 = Map4d.fromJSON({}); + ck.testTrue(map4.isAlmostEqual(Map4d.createIdentity())); if (ck.testPointer(map, "Expect box map") && map) { if (bsiChecker.Checker.noisy.boxMap) { console.log("A==>B", prettyPrint(map.transform0)); @@ -421,6 +434,26 @@ describe("Matrix4d", () => { ck.testCoordinate(0, e, "A*Ainv error"); // console.log(" max error in A*Ainv - I: " + e); Matrix4d.createZero(p1); + + for (const singularMatrix of [ + Matrix4d.createRowValues( // row 1,2 match + 10, 2, 3, 4, + 5, 20, 2, 1, + 5, 20, 2, 1, + 3, 2, 1, 30), + Matrix4d.createRowValues( // column 2,3 match + 10, 2, 4, 4, + 5, 20, 2, 2, + 4, 6, 2, 2, + 3, 2, 30, 30), + Matrix4d.createRowValues( // row 3 is sum of rows 0,1,2 + 10, 2, 3, 4, + 5, 20, 2, 1, + 5, 20, 2, 1, + 20, 42, 7, 6), + ]) { + ck.testUndefined(singularMatrix.createInverse()); + } ck.testExactNumber(p1.maxAbs(), 0); } @@ -725,10 +758,10 @@ describe("Map4d", () => { ck.testPerpendicular(lineVector, spaceVector); } else { // recompute for debug ... - console.log ("Error case"); - console.log ("A0", hA0); - console.log ("A1", hA1); - console.log ("spacePoint", spacePoint); + console.log("Error case"); + console.log("A0", hA0); + console.log("A1", hA1); + console.log("spacePoint", spacePoint); badFraction = SmallSystem.lineSegment3dHXYClosestPointUnbounded(hA0, hA1, spacePoint); } } @@ -738,4 +771,20 @@ describe("Map4d", () => { expect(ck.getNumErrors()).equals(0); }); + it("CreateMap4dWithUndefinedTransform1", () => { + const ck = new bsiChecker.Checker(); + const transform1 = Transform.createRowValues( + 10, 1, 1, 0, + 1, 11, 2, 3, + 2, 4, 20, -2); // good transform, but skewed for fun + const singularTransform = Transform.createRowValues( + 10, 1, 1, 0, + 10, 1, 1, 0, + 2, 4, 20, -2); // repeated row makes it singular + const map1 = Map4d.createTransform(transform1); + const singularMap = Map4d.createTransform(singularTransform); + ck.testPointer(map1, "Map4d computed its own inverse"); + ck.testUndefined(singularMap, "Map4d detected singular input"); + expect(ck.getNumErrors()).equals(0); + }); }); diff --git a/core/geometry/src/test/geometry4d/Point4d.test.ts b/core/geometry/src/test/geometry4d/Point4d.test.ts new file mode 100644 index 0000000..5dca93f --- /dev/null +++ b/core/geometry/src/test/geometry4d/Point4d.test.ts @@ -0,0 +1,224 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { Point4d } from "../../geometry4d/Point4d"; +import { Checker } from "../Checker"; +import { expect } from "chai"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +/* tslint:disable:no-console variable-name */ + +function testExactPoint4dXYZW(ck: Checker, point: Point4d, x: number, y: number, z: number, w: number) { + ck.testExactNumber(x, point.x); + ck.testExactNumber(y, point.y); + ck.testExactNumber(z, point.z); + ck.testExactNumber(w, point.w); +} + +function testExactPoint4dPoint4d(ck: Checker, pointA: Point4d, pointB: Point4d) { + ck.testExactNumber(pointA.x, pointB.x); + ck.testExactNumber(pointA.y, pointB.y); + ck.testExactNumber(pointA.z, pointB.z); + ck.testExactNumber(pointA.w, pointB.w); +} + +describe("Point4d", () => { + it("HelloWorld", () => { + const ck = new Checker(); + const pointA = Point4d.create(3, 6, 9, 12); + testExactPoint4dXYZW(ck, pointA, 3, 6, 9, 12); + pointA.set(2, 4, 6, 8); + testExactPoint4dXYZW(ck, pointA, 2, 4, 6, 8); + pointA.x = 10; testExactPoint4dXYZW(ck, pointA, 10, 4, 6, 8); + pointA.y = 20; testExactPoint4dXYZW(ck, pointA, 10, 20, 6, 8); + pointA.z = 30; testExactPoint4dXYZW(ck, pointA, 10, 20, 30, 8); + pointA.w = 40; testExactPoint4dXYZW(ck, pointA, 10, 20, 30, 40); + + const pointB = Point4d.create(-1, -2, -3, -4); + pointB.setFrom(pointA); testExactPoint4dPoint4d(ck, pointA, pointB); + const pointC = pointA.clone(); testExactPoint4dPoint4d(ck, pointB, pointC); + const dataD = [12, 13, 14, 15]; + pointC.setFromJSON(dataD); + const pointC2 = Point4d.fromJSON(dataD); + testExactPoint4dPoint4d(ck, pointC, pointC2); + const e = 1.0e-14; // well below coordinate tolerance + ck.testTrue(pointC.isAlmostEqualXYZW(dataD[0], dataD[1], dataD[2], dataD[3])); + const q = 1.0 + e; + ck.testTrue(pointC.isAlmostEqualXYZW(dataD[0] * q, dataD[1] * q + e, dataD[2] / q, dataD[3] / q + e)); + const f = 1.0e-3; // much bigger than coordinte tolerance . . . + ck.testFalse(pointC.isAlmostEqualXYZW(dataD[0], dataD[1], dataD[2], dataD[3] - f)); + ck.testFalse(pointC.isAlmostEqualXYZW(dataD[0], dataD[1], dataD[2] + f, dataD[3])); + ck.testFalse(pointC.isAlmostEqualXYZW(dataD[0], dataD[1] - 2 * f, dataD[2], dataD[3])); + ck.testFalse(pointC.isAlmostEqualXYZW(dataD[0] + 3 * f, dataD[1], dataD[2], dataD[3])); + + const pointA1 = pointA.clone(); + ck.testTrue(pointA1.isAlmostEqual(pointA)); + pointA1.x += f; ck.testFalse(pointA1.isAlmostEqual(pointA)); pointA1.setFrom(pointA); + pointA1.y += f; ck.testFalse(pointA1.isAlmostEqual(pointA)); pointA1.setFrom(pointA); + pointA1.z += f; ck.testFalse(pointA1.isAlmostEqual(pointA)); pointA1.setFrom(pointA); + pointA1.w += f; ck.testFalse(pointA1.isAlmostEqual(pointA)); pointA1.setFrom(pointA); + + pointC.setFromJSON([1]); + testExactPoint4dXYZW(ck, pointC, 0, 0, 0, 0); + + const pointA3 = Point4d.fromJSON(pointA.toJSON()); + testExactPoint4dPoint4d(ck, pointA, pointA3); + + expect(ck.getNumErrors()).equals(0); + }); + + it("Shifts", () => { + const ck = new Checker(); + const pointA = Point4d.create(3, 6, 9, 12); + const e = 22.8231237123719; + for (let i = 0; i < 4; i++) { + const pointB = pointA.clone(); + const q = pointB.xyzw[i] + e; // we know that this is the maxabs !!! + pointB.xyzw[i] = q; + ck.testCoordinate(pointA.distanceXYZW(pointB), e, "single component distance", pointA, pointB); + ck.testCoordinate(pointA.distanceSquaredXYZW(pointB), e * e, "single component squared distance"); + ck.testCoordinate(pointA.maxDiff(pointB), e, "single component max diff"); + const vectorAB = pointB.minus(pointA); + ck.testCoordinate(vectorAB.magnitudeXYZW(), e); + if (i !== 3) + ck.testCoordinate(vectorAB.magnitudeSquaredXYZ(), e * e); + ck.testCoordinate(pointB.maxAbs(), q, "maxAbs"); + const pointB1 = pointA.plus(vectorAB); + ck.testPoint4d(pointB, pointB1, ".plus"); + } + for (let i = 0; i < 4; i++) { + const pointB = Point4d.createZero(); + ck.testTrue(pointB.isAlmostZero); + pointB.xyzw[i] = e; + ck.testFalse(pointB.isAlmostZero, "Point4d.isAlmostZero"); + } + expect(ck.getNumErrors()).equals(0); + }); + + it("Vectors", () => { + const ck = new Checker(); + const pointA = Point4d.create(3, 6, 9, 12); + const pointB = Point4d.create(-1.1, 0.2, 5.3, 1.4); + + const dABXY = pointA.realDistanceXY(pointB)!; + const pointA3 = pointA.normalizeWeight()!; + const pointB3 = pointB.normalizeWeight()!; + ck.testCoordinate(dABXY, pointA3.realDistanceXY(pointB3)!); + const diffAB3 = pointB.crossWeightedMinus(pointA); + const diffAB4 = pointB.scale(pointA.w).minus(pointA.scale(pointB.w)); + const diffAB3W = Point4d.create(diffAB3.x, diffAB3.y, diffAB3.z, 0.0); + ck.testPoint4d(diffAB4, diffAB3W, "crossWeightedDifference)"); + expect(ck.getNumErrors()).equals(0); + }); + + it("Planes", () => { + const ck = new Checker(); + const pointA = Point4d.create(3, 6, 9, 12); + const pointB = Point4d.create(-1.1, 0.2, 5.3, 1.4); + const planeABZ = Point4d.createPlanePointPointZ(pointA, pointB)!; + ck.testCoordinate(0.0, pointA.dotProduct(planeABZ)); + ck.testCoordinate(0.0, pointB.dotProduct(planeABZ)); + ck.testCoordinate(0.0, planeABZ.dotProductXYZW(0, 0, 1, 0)); + const pointU0 = Point4d.create(3, 7, 11, 0); + ck.testCoordinate(pointB.velocityXYZ(pointU0.x, pointU0.y, pointU0.z), + pointB.dotProductXYZW(pointU0.x, pointU0.y, pointU0.z, 0.0)); + + const pointC = Point3d.create(1, 2, 9); + const pointCH = Point4d.createFromPointAndWeight(pointC, 1.0); + ck.testCoordinate(planeABZ.altitude(pointC), planeABZ.dotProduct(pointCH)); + + const vectorE = Vector3d.create(7, 9, -1); + const vectorEH = Point4d.createFromPointAndWeight(vectorE, 0.0); + ck.testCoordinate(planeABZ.velocity(vectorE), planeABZ.dotProduct(vectorEH)); + + ck.testUndefined(Point4d.create(4, 2, 1, 0).toPlane3dByOriginAndUnitNormal()); + expect(ck.getNumErrors()).equals(0); + }); + + it("Vectors", () => { + const ck = new Checker(); + const pointA = Point4d.create(3, 6, 9, 12); + const pointB = Point4d.create(-1.1, 0.2, 5.3, 1.4); + + const dABXY = pointA.realDistanceXY(pointB)!; + const pointA3 = pointA.normalizeWeight()!; + const pointB3 = pointB.normalizeWeight()!; + ck.testCoordinate(dABXY, pointA3.realDistanceXY(pointB3)!); + const diffAB3 = pointB.crossWeightedMinus(pointA); + const diffAB4 = pointB.scale(pointA.w).minus(pointA.scale(pointB.w)); + const diffAB3W = Point4d.create(diffAB3.x, diffAB3.y, diffAB3.z, 0.0); + + ck.testPoint4d(diffAB4, diffAB3W, "crossWeightedDifference)"); + expect(ck.getNumErrors()).equals(0); + }); + + it("Packing", () => { + const ck = new Checker(); + const packedData = new Float64Array([1, 2, 3, 4, 11, 12, 13, 14, 21, 22, 23, 24]); + const unitVectors = [Point4d.unitX(), Point4d.unitY(), Point4d.unitZ(), Point4d.unitW()]; + for (let i = 0; i < 3; i++) { + const i0 = 4 * i; + const pointI = Point4d.createFromPackedXYZW(packedData, i0); + const q = 10 * i; + ck.testPoint4d(pointI, Point4d.create(q + 1, q + 2, q + 3, q + 4)); + for (let k = 0; k < 3; k++) { + ck.testCoordinate(packedData[i0 + i], pointI.dotProduct(unitVectors[i])); + } + } + expect(ck.getNumErrors()).equals(0); + }); + + it("Distances", () => { + const ck = new Checker(); + const pointA = Point4d.create(3, 6, 9, 12); + const pointB = Point4d.create(-1.1, 0.2, 5.3, 1.4); + const xyzA = pointA.normalizeWeight()!; + const xyzB = pointB.normalizeWeight()!; + const vectorA = xyzA.clone(); vectorA.w = 0.0; + const vectorB = xyzB.clone(); vectorB.w = 0.0; + + ck.testCoordinate(0.0, pointA.realDistanceXY(pointA)!); + ck.testCoordinate(pointA.realDistanceXY(pointB)!, xyzA.realDistanceXY(xyzB)!); + ck.testUndefined(pointA.realDistanceXY(vectorB)); + ck.testUndefined(vectorA.realDistanceXY(pointB)); + expect(ck.getNumErrors()).equals(0); + }); + + it("Quaternions", () => { + const ck = new Checker(); + const quatA = Point4d.create(3, 6, 9, 12); quatA.normalizeQuaternion(); + const quatB = Point4d.create(-1.1, 0.2, 5.3, 1.4); quatB.normalizeQuaternion(); + const quat0 = Point4d.interpolateQuaternions(quatA, 0, quatA); + const quat1 = Point4d.interpolateQuaternions(quatB, 1.0, quatB); + ck.testCoordinate(1.0, quatA.magnitudeXYZW()); + ck.testCoordinate(1.0, quatB.magnitudeXYZW()); + ck.testPoint4d(quatA, quat0); + ck.testPoint4d(quatB, quat1); + const quat20 = Point4d.interpolateQuaternions(quat0, 0.20, quat1); + const quat80 = Point4d.interpolateQuaternions(quat0, 0.80, quat1); + const quat50 = Point4d.interpolateQuaternions(quat0, 0.50, quat1); + const sum01 = quat0.plus(quat1); + ck.testCoordinate(0, quat50.radiansToPoint4dXYZW(sum01)!); + // const quatC = Point4d.interpolateQuaternions(quat0, 25, quat80); + // ck.testPoint4d(quat20, quatC, "variant path to interpolant"); + + const perp20 = Point4d.perpendicularPoint4dPlane(quat0, quat20, quat1); // The three inputs are in a plane. Quat should be zero? + const perp80 = Point4d.perpendicularPoint4dPlane(quat0, quat80, quat1); // The three inputs are in a plane. Quat should be zero? + ck.testCoordinate(0.0, perp20.magnitudeXYZW(), "coplanar quats have 0 cross product."); + ck.testCoordinate(0.0, perp80.magnitudeXYZW(), "coplanar quats have 0 cross product."); + const epsilon = 0.000001; + const nearly1 = 1.0 - epsilon; + const quatQ0 = Point4d.interpolateQuaternions(quat0, epsilon, quat1); + const quatQ1 = Point4d.interpolateQuaternions(quat0, nearly1, quat1); + const quat0Q0 = Point4d.interpolateQuaternions(quat0, 0.8, quatQ0); // nearly parallel !!! + ck.testLE(quat1.distanceXYZW(quatQ1), epsilon); + ck.testLE(quat0.distanceXYZW(quat0Q0), epsilon); + + const quatANeg = quatA.negate(); + + Point4d.interpolateQuaternions(quatA, 0.75, quatANeg); // not sure what this means physically. + + ck.testUndefined(quatA.radiansToPoint4dXYZW(Point4d.createZero())); + expect(ck.getNumErrors()).equals(0); + }); +}); diff --git a/core/geometry/src/test/DeepCompare.test.ts b/core/geometry/src/test/misc/DeepCompare.test.ts similarity index 94% rename from core/geometry/src/test/DeepCompare.test.ts rename to core/geometry/src/test/misc/DeepCompare.test.ts index 8eed8d5..2bf01ed 100644 --- a/core/geometry/src/test/DeepCompare.test.ts +++ b/core/geometry/src/test/misc/DeepCompare.test.ts @@ -2,9 +2,9 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Checker } from "./Checker"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { DeepCompare } from "../serialization/DeepCompare"; +import { DeepCompare } from "../../serialization/DeepCompare"; /* tslint:disable:no-console trailing-comma object-literal-key-quotes*/ diff --git a/core/geometry/src/test/Geometry.test.ts b/core/geometry/src/test/misc/Geometry.test.ts similarity index 93% rename from core/geometry/src/test/Geometry.test.ts rename to core/geometry/src/test/misc/Geometry.test.ts index e3af837..dd16537 100644 --- a/core/geometry/src/test/Geometry.test.ts +++ b/core/geometry/src/test/misc/Geometry.test.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { expect, assert } from "chai"; -import { Degree2PowerPolynomial } from "../numerics/Polynomials"; -import { Geometry } from "../Geometry"; -import { Angle } from "../geometry3d/Angle"; -import { Point2d, Vector2d } from "../geometry3d/Point2dVector2d"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { YawPitchRollAngles } from "../geometry3d/YawPitchRollAngles"; -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { Checker } from "./Checker"; +import { Degree2PowerPolynomial } from "../../numerics/Polynomials"; +import { Geometry } from "../../Geometry"; +import { Angle } from "../../geometry3d/Angle"; +import { Point2d, Vector2d } from "../../geometry3d/Point2dVector2d"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { YawPitchRollAngles } from "../../geometry3d/YawPitchRollAngles"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { Checker } from "../Checker"; /* tslint:disable:no-console */ describe("Geometry", () => { diff --git a/core/geometry/src/test/performance.test.ts b/core/geometry/src/test/misc/performance.test.ts similarity index 86% rename from core/geometry/src/test/performance.test.ts rename to core/geometry/src/test/misc/performance.test.ts index a233d74..7567057 100644 --- a/core/geometry/src/test/performance.test.ts +++ b/core/geometry/src/test/misc/performance.test.ts @@ -2,11 +2,11 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { Geometry } from "../Geometry"; -import { GrowableFloat64Array } from "../geometry3d/GrowableFloat64Array"; -import { GrowableXYZArray } from "../geometry3d/GrowableXYZArray"; -import { Point3d } from "../geometry3d/Point3dVector3d"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { Geometry } from "../../Geometry"; +import { GrowableFloat64Array } from "../../geometry3d/GrowableFloat64Array"; +import { GrowableXYZArray } from "../../geometry3d/GrowableXYZArray"; +import { Point3d } from "../../geometry3d/Point3dVector3d"; /* tslint:disable:no-console no-unused-variable */ function inverseCalculationLoop(numTest: number, usingCache: boolean, usingResult: boolean) { @@ -284,9 +284,9 @@ function arrayCheck(numTest: number, type: number) { console.time(name); for (let i = 0; i < numTest; i++) { // Fetch items - arr.atPoint3dIndex(0, result); - arr.atPoint3dIndex(1, result); - arr.atPoint3dIndex(2, result); + arr.getPoint3dAtUncheckedPointIndex(0, result); + arr.getPoint3dAtUncheckedPointIndex(1, result); + arr.getPoint3dAtUncheckedPointIndex(2, result); // Push items arr.push(toPush); arr.push(toPush); @@ -300,9 +300,9 @@ function arrayCheck(numTest: number, type: number) { arr.length; arr.length; // Reassignment - arr.setCoordinates(1, 1, 3, 5); - arr.setCoordinates(0, 0, 3, 5); - arr.setCoordinates(2, 1, 3, 5); + arr.setXYZAtCheckedPointIndex(1, 1, 3, 5); + arr.setXYZAtCheckedPointIndex(0, 0, 3, 5); + arr.setXYZAtCheckedPointIndex(2, 1, 3, 5); } console.timeEnd(name); break; @@ -311,7 +311,7 @@ function arrayCheck(numTest: number, type: number) { export class Matrix3dOps extends Matrix3d { /** Multiply two matrices.* */ - public static multiplyMatrixMatrix_directAssignment(matrixA: Matrix3d, matrixB: Matrix3d, result?: Matrix3d): Matrix3d { + public static multiplyMatrixMatrixdirectAssignment(matrixA: Matrix3d, matrixB: Matrix3d, result?: Matrix3d): Matrix3d { // WARNING -- matrixA does not allow result to be the same as one of the inputs . . . result = result ? result : new Matrix3d(); result.coffs[0] = (matrixA.coffs[0] * matrixB.coffs[0] + matrixA.coffs[1] * matrixB.coffs[3] + matrixA.coffs[2] * matrixB.coffs[6]); @@ -325,7 +325,7 @@ export class Matrix3dOps extends Matrix3d { result.coffs[8] = (matrixA.coffs[6] * matrixB.coffs[2] + matrixA.coffs[7] * matrixB.coffs[5] + matrixA.coffs[8] * matrixB.coffs[8]); return result; } - public static multiplyMatrixMatrix_directAssignmentN(numReps: number, matrixA: Matrix3d, matrixB: Matrix3d, result?: Matrix3d): Matrix3d { + public static multiplyMatrixMatrixdirectAssignmentN(numReps: number, matrixA: Matrix3d, matrixB: Matrix3d, result?: Matrix3d): Matrix3d { // WARNING -- matrixA does not allow result to be the same as one of the inputs . . . result = result ? result : new Matrix3d(); for (let i = 0; i < numReps; i++) { @@ -360,7 +360,7 @@ export class Matrix3dOps extends Matrix3d { } class TimingTests { - public static RunTestA(numTest: number) { + public static runTestA(numTest: number) { console.log("\n performance reps ", numTest); const matrixA = Matrix3d.createScale(1, 2, 3); const matrixB = Matrix3d.createScale(2, -1, 2); @@ -379,7 +379,7 @@ class TimingTests { console.timeEnd("multiplyMatrixMatrix(result)"); } - public static RunTestB(numTest: number) { + public static runTestB(numTest: number) { const matrixA = Matrix3dOps.createScale(1, 2, 3); const matrixB = Matrix3dOps.createScale(2, -1, 2); @@ -397,27 +397,27 @@ class TimingTests { matrixD = matrixA.multiplyMatrixMatrix(matrixB, matrixD); console.timeEnd("Test_pre-allocated_result_static_Outside"); - console.time("Matrix3dOps.multiplyMatrixMatrix_directAssignment"); + console.time("Matrix3dOps.multiplyMatrixMatrixdirectAssignment"); for (let k = 0; k < numTest; k++) - matrixD = Matrix3dOps.multiplyMatrixMatrix_directAssignment(matrixA, matrixB, matrixD); - console.timeEnd("Matrix3dOps.multiplyMatrixMatrix_directAssignment"); + matrixD = Matrix3dOps.multiplyMatrixMatrixdirectAssignment(matrixA, matrixB, matrixD); + console.timeEnd("Matrix3dOps.multiplyMatrixMatrixdirectAssignment"); for (let numReps = 0; numReps < 20; numReps = 3 * numReps + 1) { - const name = "Matrix3dOps.multiplyMatrixMatrix_directAssignment (numReps " + numReps + ")"; + const name = "Matrix3dOps.multiplyMatrixMatrixdirectAssignment (numReps " + numReps + ")"; console.time(name); for (let k = 0; k < numTest; k++) - matrixD = Matrix3dOps.multiplyMatrixMatrix_directAssignmentN(numReps, matrixA, matrixB, matrixD); + matrixD = Matrix3dOps.multiplyMatrixMatrixdirectAssignmentN(numReps, matrixA, matrixB, matrixD); console.timeEnd(name); } } - public static RunTestC(numTest: number) { + public static runTestC(numTest: number) { // console.log("=================="); inverseCalculationLoop(numTest, true, true); inverseCalculationLoop(numTest, false, true); inverseCalculationLoop(numTest, true, false); inverseCalculationLoop(numTest, false, false); } - public static RunTestD(numTest: number) { + public static runTestD(numTest: number) { // console.log("=================="); hypotenuseCalculationLoop(numTest, 1); hypotenuseCalculationLoop(numTest, 3); @@ -436,13 +436,13 @@ class TimingTests { hypotenuseCalculationLoop(numTest, 4); hypotenuseCalculationLoop(numTest, 9); } - public static RunTestE(numTest: number) { + public static runTestE(numTest: number) { // console.log("=================="); hypotenuseSquaredCalculationLoop(numTest, 0); hypotenuseSquaredCalculationLoop(numTest, 1); hypotenuseSquaredCalculationLoop(numTest, 2); } - public static RunTestG(numTest: number) { + public static runTestG(numTest: number) { // console.log("=================="); // arrayCheck(numTest, 1); // arrayCheck(numTest, 2); @@ -454,40 +454,40 @@ class TimingTests { const numTestGlobal = 200000; describe("Geometry.Allocations", () => { it("Expect caller-supplied result to be faster than constructor", () => { - TimingTests.RunTestA(numTestGlobal); + TimingTests.runTestA(numTestGlobal); }); }); describe("Geometry.ComputeBreakdown", () => { it("Expect increased time for shift to compute intensive", () => { - TimingTests.RunTestB(numTestGlobal / 10); // too many tests times out if there are background processes !!!! + TimingTests.runTestB(numTestGlobal / 10); // too many tests times out if there are background processes !!!! }); }); describe("Geometry.RotInverseCalculations", () => { it("RotmatrixInverse with (Cache*New) variants", () => { const myCount = numTestGlobal / 2; - TimingTests.RunTestC(myCount); - TimingTests.RunTestC(myCount); - TimingTests.RunTestC(myCount); + TimingTests.runTestC(myCount); + TimingTests.runTestC(myCount); + TimingTests.runTestC(myCount); }); }); describe("Geometry.Hypotenuse", () => { it("Math.hypotenuse against the Geometry's hypotenuse functions", () => { - TimingTests.RunTestD(numTestGlobal); + TimingTests.runTestD(numTestGlobal); }); }); describe("Geometry.HypotenuseSquared", () => { it("Various runtimes of Geometry's hypotenuse squared functions", () => { - TimingTests.RunTestE(numTestGlobal); + TimingTests.runTestE(numTestGlobal); }); }); describe("Array_Vs_Growable", () => { it("Use of indexing, pushing, popping, length, and reassignment of array types", () => { console.log({ numTest: numTestGlobal }); - TimingTests.RunTestG(numTestGlobal * 2); + TimingTests.runTestG(numTestGlobal * 2); }); }); diff --git a/core/geometry/src/test/AnalyticRoots.test.ts b/core/geometry/src/test/numerics/AnalyticRoots.test.ts similarity index 86% rename from core/geometry/src/test/AnalyticRoots.test.ts rename to core/geometry/src/test/numerics/AnalyticRoots.test.ts index f4446a6..1b415ee 100644 --- a/core/geometry/src/test/AnalyticRoots.test.ts +++ b/core/geometry/src/test/numerics/AnalyticRoots.test.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; -import { Checker } from "./Checker"; -import { NumberArray } from "../geometry3d/PointHelpers"; -import { GrowableFloat64Array } from "../geometry3d/GrowableFloat64Array"; -import { AnalyticRoots, Degree2PowerPolynomial, PowerPolynomial, TrigPolynomial, SmallSystem, Degree3PowerPolynomial } from "../numerics/Polynomials"; -import { Vector2d, Point2d } from "../geometry3d/Point2dVector2d"; -import { Point4d } from "../geometry4d/Point4d"; -import { Point3d } from "../geometry3d/Point3dVector3d"; +import { Checker } from "../Checker"; +import { NumberArray } from "../../geometry3d/PointHelpers"; +import { GrowableFloat64Array } from "../../geometry3d/GrowableFloat64Array"; +import { AnalyticRoots, Degree2PowerPolynomial, PowerPolynomial, TrigPolynomial, SmallSystem, Degree3PowerPolynomial } from "../../numerics/Polynomials"; +import { Vector2d, Point2d } from "../../geometry3d/Point2dVector2d"; +import { Point4d } from "../../geometry4d/Point4d"; +import { Point3d } from "../../geometry3d/Point3dVector3d"; /* tslint:disable:no-console no-trailing-whitespace */ @@ -30,7 +30,7 @@ describe("AnalyticRoots.SolveLinear", () => { if (slope !== 0) { if (ck.testPointer(s) && s) { ck.testExactNumber(s.length, 1, "SolveLinear (root a, slope)", a, slope); - ck.testCoordinate(s.at(0), a, "SolveLinear", powerCoffs); + ck.testCoordinate(s.atUncheckedIndex(0), a, "SolveLinear", powerCoffs); } } else { ck.testExactNumber(s.length, 0, " Expect no roots for linear equation with zero slope."); @@ -52,7 +52,7 @@ describe("AnalyticRoots.SolveQuadric", () => { if (ck.testPointer(roots) && roots) { roots.sort(compare); ck.testExactNumber(roots.length, 1, "SolveQuadric s = [" + i + ", " + i + "]"); - ck.testCoordinate(roots.at(0), i, "Quadratic double root"); + ck.testCoordinate(roots.atUncheckedIndex(0), i, "Quadratic double root"); } } ck.checkpoint("DoubleRoot"); @@ -79,8 +79,8 @@ describe("AnalyticRoots.SolveQuadric", () => { if (ck.testPointer(roots) && roots) { roots.sort(compare); ck.testExactNumber(roots.length, 2, "appendQuadraticSolutions ", roots, root0, root1); - ck.testCoordinate(roots.at(0), Math.min(root0, root1), "Quadratic two roots"); - ck.testCoordinate(roots.at(1), Math.max(root0, root1), "Quadratic two roots"); + ck.testCoordinate(roots.atUncheckedIndex(0), Math.min(root0, root1), "Quadratic two roots"); + ck.testCoordinate(roots.atUncheckedIndex(1), Math.max(root0, root1), "Quadratic two roots"); } } } @@ -105,8 +105,8 @@ describe("AnalyticRoots.SolveQuadric", () => { const coffs = new Float64Array([1, 2, 3, 4]); ck.testExactNumber(0, PowerPolynomial.degreeKnownEvaluate(coffs, -1, 4)); const radians: number[] = []; - ck.testTrue(TrigPolynomial.SolveAngles(new Float64Array([1, 1, 0, 0, 0]), 4, 100, radians)); - ck.testTrue(TrigPolynomial.SolveAngles(new Float64Array([0, 1, 1, 0, 0, 0]), 4, 100, radians)); + ck.testTrue(TrigPolynomial.solveAngles(new Float64Array([1, 1, 0, 0, 0]), 4, 100, radians)); + ck.testTrue(TrigPolynomial.solveAngles(new Float64Array([0, 1, 1, 0, 0, 0]), 4, 100, radians)); const z3 = new Degree3PowerPolynomial(); const z2 = new Degree2PowerPolynomial(); @@ -166,8 +166,8 @@ function matchRoots(target: number[], actual: GrowableFloat64Array) { eMax = 0.0; for (let i = 0; i < target.length; i++) { - const e = target[i] - actual.at(i); - eMax = NumberArray.MaxAbsTwo(e, eMax); + const e = target[i] - actual.atUncheckedIndex(i); + eMax = NumberArray.maxAbsTwo(e, eMax); } } return eMax; @@ -177,7 +177,7 @@ function maxDiffMatchedArrays(target: number[], actual: GrowableFloat64Array) { if (target.length !== actual.length) return Number.MAX_VALUE; const tempArray: number[] = []; - for (let i = 0; i < actual.length; i++) { tempArray.push(actual.at(i)); } + for (let i = 0; i < actual.length; i++) { tempArray.push(actual.atUncheckedIndex(i)); } return NumberArray.maxAbsDiff(target, tempArray); } @@ -213,7 +213,7 @@ describe("AnalyticRoots.SolveCubic", () => { ck.testCoordinate(actual.length, 1, "simple root count"); const eMax = matchRoots(target, actual); - ck.testTrue(eMax < (1.0e-14 * (1.0 + NumberArray.MaxAbsArray(target))), "root error"); + ck.testTrue(eMax < (1.0e-14 * (1.0 + NumberArray.maxAbsArray(target))), "root error"); if (Checker.noisy.cubicRoots) { console.log(" (target " + a + ") (b " + b + ")"); @@ -239,8 +239,8 @@ describe("AnalyticRoots.SolveCubic", () => { const coffs = new Float64Array(4); coffs[3] = 1.0; - coffs[2] = - NumberArray.PreciseSum([u0, u1, u2]); - coffs[1] = NumberArray.PreciseSum([u0 * u1, u1 * u2, u0 * u2]); + coffs[2] = - NumberArray.preciseSum([u0, u1, u2]); + coffs[1] = NumberArray.preciseSum([u0 * u1, u1 * u2, u0 * u2]); coffs[0] = - u0 * u1 * u2; const target = [u0, u1, u2]; @@ -249,7 +249,7 @@ describe("AnalyticRoots.SolveCubic", () => { AnalyticRoots.appendCubicRoots(coffs, actual); if (ck.testPointer(actual) && actual && ck.testExactNumber(3, actual.length, "3 root cubic")) { - const uMax = NumberArray.MaxAbsArray(target); + const uMax = NumberArray.maxAbsArray(target); const eMax = matchRoots(target, actual) / uMax; const eSafe = maxDiffMatchedArrays(target, actual) / uMax; const printTrigger = 1.0e-12; @@ -259,9 +259,9 @@ describe("AnalyticRoots.SolveCubic", () => { console.log("Cubic root variances. These may be expected behavior under extreme origin conditions"); console.log(" (known roots " + target[0] + " " + target[1] + " " + target[2] + ") (emax " + eMax + ") (eSafe " + eSafe + ")"); - console.log(" (computed roots " + actual.at(0) + " " + actual.at(1) + " " + actual.at(2) + ")"); - console.log(" (correction by newton from computed root " + NewtonStep(coffs, actual.at(0)) + - " " + NewtonStep(coffs, actual.at(1)) + " " + NewtonStep(coffs, actual.at(2)) + ")"); + console.log(" (computed roots " + actual.atUncheckedIndex(0) + " " + actual.atUncheckedIndex(1) + " " + actual.atUncheckedIndex(2) + ")"); + console.log(" (correction by newton from computed root " + NewtonStep(coffs, actual.atUncheckedIndex(0)) + + " " + NewtonStep(coffs, actual.atUncheckedIndex(1)) + " " + NewtonStep(coffs, actual.atUncheckedIndex(2)) + ")"); console.log(" (correction by newton from known root " + NewtonStep(coffs, target[0]) + " " + NewtonStep(coffs, target[1]) + " " + NewtonStep(coffs, target[2]) + ")"); } @@ -341,7 +341,7 @@ describe("AnalyticRoots.SolveCubic", () => { function CheckQuartic(u0: number, u1: number, u2: number, u3: number, tolerance: number, ck: Checker) { const coffs = new Float64Array(5); coffs[4] = 1.0; - coffs[3] = - NumberArray.PreciseSum([u0, u1, u2, u3]); + coffs[3] = - NumberArray.preciseSum([u0, u1, u2, u3]); const xx: number[] = []; xx[0] = u0 * u1; xx[1] = u0 * u2; @@ -349,8 +349,8 @@ function CheckQuartic(u0: number, u1: number, u2: number, u3: number, tolerance: xx[3] = u1 * u2; xx[4] = u1 * u3; xx[5] = u2 * u3; - coffs[2] = NumberArray.PreciseSum(xx); - coffs[1] = -NumberArray.PreciseSum([u0 * u1 * u2, u0 * u1 * u3, u0 * u2 * u3, u1 * u2 * u3]); + coffs[2] = NumberArray.preciseSum(xx); + coffs[1] = -NumberArray.preciseSum([u0 * u1 * u2, u0 * u1 * u3, u0 * u2 * u3, u1 * u2 * u3]); coffs[0] = u0 * u1 * u2 * u3; const target = [u0, u1, u2, u3]; @@ -362,12 +362,12 @@ function CheckQuartic(u0: number, u1: number, u2: number, u3: number, tolerance: ck.testExactNumber(4, actual.length, "quartic root count"); - const uMax = NumberArray.MaxAbsArray(target); + const uMax = NumberArray.maxAbsArray(target); let eMax = matchRoots(target, actual) / uMax; const ok: boolean = ck.testTrue(eMax < tolerance, "quartic root tolerance", eMax, tolerance); // Accurate when compared to multiple of 1.0e-8... any higher negative power likely to fail if (Checker.noisy.quarticRoots) { - console.log(" (actual " + actual.at(0) + " " + actual.at(1) + " " + actual.at(2) + " " + actual.at(3) + ")"); + console.log(" (actual " + actual.atUncheckedIndex(0) + " " + actual.atUncheckedIndex(1) + " " + actual.atUncheckedIndex(2) + " " + actual.atUncheckedIndex(3) + ")"); console.log(" (target " + target[0] + " " + target[1] + " " + target[2] + " " + target[3] + ")"); } @@ -375,14 +375,14 @@ function CheckQuartic(u0: number, u1: number, u2: number, u3: number, tolerance: // Additional testing based on NewtonStep for (let step = 0; (step < 10) && (eMax > 1.0e-14); step++) { if (!ok || printAll) { - console.log(" (actualDX " + NewtonStep4(coffs, actual.at(0)) + " " + NewtonStep4(coffs, actual.at(1)) + " " + - NewtonStep4(coffs, actual.at(2)) + " " + NewtonStep4(coffs, actual.at(3)) + ")"); + console.log(" (actualDX " + NewtonStep4(coffs, actual.atUncheckedIndex(0)) + " " + NewtonStep4(coffs, actual.atUncheckedIndex(1)) + " " + + NewtonStep4(coffs, actual.atUncheckedIndex(2)) + " " + NewtonStep4(coffs, actual.atUncheckedIndex(3)) + ")"); for (let k = 0; k < actual.length; k++) { - actual.reassign(k, actual.at(k) - NewtonStep4(coffs, actual.at(k))); + actual.reassign(k, actual.atUncheckedIndex(k) - NewtonStep4(coffs, actual.atUncheckedIndex(k))); } eMax = matchRoots(target, actual) / uMax; if (!ok || printAll) { - console.log(" (actual " + actual.at(0) + " " + actual.at(1) + " " + actual.at(2) + " " + actual.at(3) + " (emax " + + console.log(" (actual " + actual.atUncheckedIndex(0) + " " + actual.atUncheckedIndex(1) + " " + actual.atUncheckedIndex(2) + " " + actual.atUncheckedIndex(3) + " (emax " + eMax + ")"); } } @@ -434,7 +434,7 @@ describe("AnalyticRoots.CheckQuartic", () => { const ff: number[] = []; const numRoots = roots.length; for (let i = 0; i < numRoots; i++) { - const r = roots.at(i); + const r = roots.atUncheckedIndex(i); const f = coff[0] + r * (coff[1] + r * (coff[2] + r * (coff[3] + r * coff[4]))); maxF = Math.max(f, Math.abs(maxF)); ff.push(f); diff --git a/core/geometry/src/test/Bezier.test.ts b/core/geometry/src/test/numerics/Bezier.test.ts similarity index 93% rename from core/geometry/src/test/Bezier.test.ts rename to core/geometry/src/test/numerics/Bezier.test.ts index bbbb5ec..fd57b8f 100644 --- a/core/geometry/src/test/Bezier.test.ts +++ b/core/geometry/src/test/numerics/Bezier.test.ts @@ -4,21 +4,21 @@ *--------------------------------------------------------------------------------------------*/ // import { Sample } from "../serialization/GeometrySamples"; -import { UnivariateBezier, BezierCoffs, Order2Bezier, Order3Bezier, Order4Bezier, Order5Bezier } from "../numerics/BezierPolynomials"; -import { Geometry } from "../Geometry"; -import { PascalCoefficients } from "../numerics/PascalCoefficients"; -import { LineString3d } from "../curve/LineString3d"; -import { Point2d } from "../geometry3d/Point2dVector2d"; -import { Point3d } from "../geometry3d/Point3dVector3d"; -import { Checker } from "./Checker"; +import { UnivariateBezier, BezierCoffs, Order2Bezier, Order3Bezier, Order4Bezier, Order5Bezier } from "../../numerics/BezierPolynomials"; +import { Geometry } from "../../Geometry"; +import { PascalCoefficients } from "../../numerics/PascalCoefficients"; +import { LineString3d } from "../../curve/LineString3d"; +import { Point2d } from "../../geometry3d/Point2dVector2d"; +import { Point3d } from "../../geometry3d/Point3dVector3d"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { GeometryCoreTestIO } from "./GeometryCoreTestIO"; -import { Sample } from "../serialization/GeometrySamples"; -import { Range3d } from "../geometry3d/Range"; -import { Box } from "../solid/Box"; -import { Transform } from "../geometry3d/Transform"; -import { AngleSweep } from "../geometry3d/AngleSweep"; -import { Segment1d } from "../geometry3d/Segment1d"; +import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; +import { Sample } from "../../serialization/GeometrySamples"; +import { Range3d } from "../../geometry3d/Range"; +import { Box } from "../../solid/Box"; +import { Transform } from "../../geometry3d/Transform"; +import { AngleSweep } from "../../geometry3d/AngleSweep"; +import { Segment1d } from "../../geometry3d/Segment1d"; // import { prettyPrint } from "./testFunctions"; /* tslint:disable:no-console */ describe("Bezier", () => { diff --git a/core/geometry/src/test/Newton.test.ts b/core/geometry/src/test/numerics/Newton.test.ts similarity index 93% rename from core/geometry/src/test/Newton.test.ts rename to core/geometry/src/test/numerics/Newton.test.ts index 54298f9..28134e3 100644 --- a/core/geometry/src/test/Newton.test.ts +++ b/core/geometry/src/test/numerics/Newton.test.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Newton1dUnbounded, NewtonEvaluatorRtoRD } from "../numerics/Newton"; -import { Checker } from "./Checker"; +import { Newton1dUnbounded, NewtonEvaluatorRtoRD } from "../../numerics/Newton"; +import { Checker } from "../Checker"; import { expect } from "chai"; /* tslint:disable:no-console */ diff --git a/core/geometry/src/test/Polynomial.test.ts b/core/geometry/src/test/numerics/Polynomial.test.ts similarity index 92% rename from core/geometry/src/test/Polynomial.test.ts rename to core/geometry/src/test/numerics/Polynomial.test.ts index 2821b89..ccb1316 100644 --- a/core/geometry/src/test/Polynomial.test.ts +++ b/core/geometry/src/test/numerics/Polynomial.test.ts @@ -4,21 +4,21 @@ *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; -import { Checker } from "./Checker"; -import { NumberArray } from "../geometry3d/PointHelpers"; -import { Geometry } from "../Geometry"; -import { AngleSweep } from "../geometry3d/AngleSweep"; -import { Angle } from "../geometry3d/Angle"; -import { Quadrature } from "../numerics/Quadrature"; -import { Point2d, Vector2d } from "../geometry3d/Point2dVector2d"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Arc3d } from "../curve/Arc3d"; -import { Degree2PowerPolynomial, Degree3PowerPolynomial, Degree4PowerPolynomial, TrigPolynomial, SmallSystem, AnalyticRoots, SphereImplicit, TorusImplicit } from "../numerics/Polynomials"; +import { Checker } from "../Checker"; +import { NumberArray } from "../../geometry3d/PointHelpers"; +import { Geometry } from "../../Geometry"; +import { AngleSweep } from "../../geometry3d/AngleSweep"; +import { Angle } from "../../geometry3d/Angle"; +import { Quadrature } from "../../numerics/Quadrature"; +import { Point2d, Vector2d } from "../../geometry3d/Point2dVector2d"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Arc3d } from "../../curve/Arc3d"; +import { Degree2PowerPolynomial, Degree3PowerPolynomial, Degree4PowerPolynomial, TrigPolynomial, SmallSystem, AnalyticRoots, SphereImplicit, TorusImplicit } from "../../numerics/Polynomials"; /* tslint:disable:no-console */ -import { UnivariateBezier, Order2Bezier, Order3Bezier, Order4Bezier, Order5Bezier, BezierCoffs } from "../numerics/BezierPolynomials"; -import { GrowableFloat64Array } from "../geometry3d/GrowableFloat64Array"; -import { Point4d } from "../geometry4d/Point4d"; +import { UnivariateBezier, Order2Bezier, Order3Bezier, Order4Bezier, Order5Bezier, BezierCoffs } from "../../numerics/BezierPolynomials"; +import { GrowableFloat64Array } from "../../geometry3d/GrowableFloat64Array"; +import { Point4d } from "../../geometry4d/Point4d"; function testBezier(ck: Checker, bezier: BezierCoffs) { for (const f of [0, 0.25, 0.75]) { @@ -63,7 +63,7 @@ describe("Bezier.HelloWorld", () => { const ck = new Checker(); const nullTorus = new TorusImplicit(0, 0); // DEGENERATE ck.testExactNumber(1.0, nullTorus.implicitFunctionScale()); - const data = nullTorus.XYZToThetaPhiDistance(Point3d.create(0, 0, 0)); + const data = nullTorus.xyzToThetaPhiDistance(Point3d.create(0, 0, 0)); ck.testFalse(data.safePhi); ck.checkpoint("Bezier.degenerateCases"); expect(ck.getNumErrors()).equals(0); @@ -172,7 +172,7 @@ describe("TrigPolynomial.DegenerateLinear", () => { const ac = 0.6; const as = 0.9; const a0 = 0.1; - TrigPolynomial.SolveUnitCircleImplicitQuadricIntersection( + TrigPolynomial.solveUnitCircleImplicitQuadricIntersection( 0, 0, 0, ac, as, a0, angles); ck.testExactNumber(2, angles.length); @@ -198,7 +198,7 @@ describe("Ellipse.Intersection", () => { const center = Point3d.create(t, 2.0 * t, 0); const vectorU = Vector3d.create(majorRadius, 0, 0); const vectorV = Vector3d.create(0.1, minorRadius, 0); - TrigPolynomial.SolveUnitCircleEllipseIntersection(center.x, center.y, vectorU.x, vectorU.y, vectorV.x, vectorV.y, ellipseAngles, circleAngles); + TrigPolynomial.solveUnitCircleEllipseIntersection(center.x, center.y, vectorU.x, vectorU.y, vectorV.x, vectorV.y, ellipseAngles, circleAngles); if (ck.testExactNumber(ellipseAngles.length, circleAngles.length)) { // verify that all returned values are intersections. This prevents false positives but not false negatives. for (let i = 0; i < ellipseAngles.length; i++) { @@ -225,7 +225,7 @@ describe("Ellipse.Intersection", () => { const center = Point4d.create(t, 2.0 * t, 0, 0.98); const vectorU = Point4d.create(majorRadius, 0, 0, 0.2); const vectorV = Point4d.create(0.1, minorRadius, 0, 0.3); - TrigPolynomial.SolveUnitCircleHomogeneousEllipseIntersection( + TrigPolynomial.solveUnitCircleHomogeneousEllipseIntersection( center.x, center.y, center.w, vectorU.x, vectorU.y, vectorU.w, vectorV.x, vectorV.y, vectorV.w, @@ -279,14 +279,14 @@ describe("ImplicitSurface", () => { ck.testPerpendicular(vectorX, ddTheta, "implicit sphere perpendicular derivatives"); ck.testPerpendicular(vectorX, ddPhi, "implicit sphere perpendicular derivatives"); ck.testCoordinate(r, vectorX.magnitude(), "implicit sphere distance from origin"); - const thetaPhiA = sphere.XYZToThetaPhiR(xyz); + const thetaPhiA = sphere.xyzToThetaPhiR(xyz); ck.testCoordinate(thetaPhi[0], thetaPhiA.theta, "implicit sphere theta inverse"); ck.testCoordinate(thetaPhi[1], thetaPhiA.phi, "implicit sphere phi inverse"); } for (const sphereA of [ new SphereImplicit(0), new SphereImplicit(1)]) { - const thetaPhiB = sphereA.XYZToThetaPhiR(Point3d.create(0, 0, 0)); - const thetaPhiC = sphereA.XYZToThetaPhiR(Point3d.create(0, 0, 1)); + const thetaPhiB = sphereA.xyzToThetaPhiR(Point3d.create(0, 0, 0)); + const thetaPhiC = sphereA.xyzToThetaPhiR(Point3d.create(0, 0, 1)); ck.testExactNumber(0.0, thetaPhiB.phi); ck.testExactNumber(0.0, thetaPhiC.theta); } @@ -335,7 +335,7 @@ describe("ImplicitSurface", () => { const r = factorB * rB; const xyzR = torus.evaluateThetaPhiDistance(theta, phi, r); ck.testPerpendicular(ddTheta, ddPhi, "implicit torus perpendicular derivatives"); - const thetaPhiA = torus.XYZToThetaPhiDistance(xyzR); + const thetaPhiA = torus.xyzToThetaPhiDistance(xyzR); ck.testCoordinate(thetaPhi[0], thetaPhiA.theta, "implicit sphere theta inverse"); ck.testCoordinate(thetaPhi[1], thetaPhiA.phi, "implicit sphere phi inverse"); ck.testCoordinate(factorB * rB, thetaPhiA.distance, "implicit sphere inverse distance from major circle"); @@ -414,12 +414,12 @@ describe("PowerPolynomials", () => { bezier.realRoots(0.0, false, bezierRoots); // make sure that each reported root is a root ... this does not confirm completeness for (let i = 0; i < powerRoots.length; i++) { - const r = powerRoots.at(i); + const r = powerRoots.atUncheckedIndex(i); const f = power.evaluate(r); ck.testCoordinate(0, f); } for (let i = 0; i < bezierRoots.length; i++) { - const r = bezierRoots.at(i); + const r = bezierRoots.atUncheckedIndex(i); const f = bezier.evaluate(r); ck.testCoordinate(0, f); } diff --git a/core/geometry/src/test/TriDiagonalSystem.test.ts b/core/geometry/src/test/numerics/TriDiagonalSystem.test.ts similarity index 62% rename from core/geometry/src/test/TriDiagonalSystem.test.ts rename to core/geometry/src/test/numerics/TriDiagonalSystem.test.ts index 5d2fc4b..95abffd 100644 --- a/core/geometry/src/test/TriDiagonalSystem.test.ts +++ b/core/geometry/src/test/numerics/TriDiagonalSystem.test.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; -import { Checker } from "./Checker"; -import { TriDiagonalSystem } from "../numerics/TriDiagonalSystem"; -import { Point3d } from "../geometry3d/Point3dVector3d"; +import { Checker } from "../Checker"; +import { TriDiagonalSystem } from "../../numerics/TriDiagonalSystem"; +import { Point3d } from "../../geometry3d/Point3dVector3d"; /* tslint:disable:variable-name no-console*/ class TestFixture { @@ -14,17 +14,17 @@ class TestFixture { public constructor() { this.ck = new Checker(); } // Tester Methods ------------------------------------------------------------------- - public CheckX(A: TriDiagonalSystem, B: TriDiagonalSystem) { - const n = A.Order(); + public checkX(A: TriDiagonalSystem, B: TriDiagonalSystem) { + const n = A.order(); for (let i = 0; i < n; i++) { // TODO: Implement correct method of comparing two Float64Array's - this.ck.testCoordinate(A.GetX(i), B.GetX(i), "Solution vectors of A and B"); + this.ck.testCoordinate(A.getX(i), B.getX(i), "Solution vectors of A and B"); } } - public CheckB(A: TriDiagonalSystem, B: TriDiagonalSystem) { - const n = A.Order(); + public checkB(A: TriDiagonalSystem, B: TriDiagonalSystem) { + const n = A.order(); for (let i = 0; i < n; i++) { - this.ck.testCoordinate(A.GetB(i), B.GetB(i), "Right side vectors of A and B"); + this.ck.testCoordinate(A.getB(i), B.getB(i), "Right side vectors of A and B"); } } // Setup Methods -------------------------------------------------------------------- @@ -32,13 +32,13 @@ class TestFixture { const A = new TriDiagonalSystem(3); let B: TriDiagonalSystem; - A.SetRow(0, 1, 2, 1); - A.SetRow(1, 1, 2, 1); - A.SetRow(2, 1, 2, 1); - A.SetX(0, 2); - A.SetX(1, 3); - A.SetX(2, 4); - A.MultiplyAX(); + A.setRow(0, 1, 2, 1); + A.setRow(1, 1, 2, 1); + A.setRow(2, 1, 2, 1); + A.setX(0, 2); + A.setX(1, 3); + A.setX(2, 4); + A.multiplyAX(); // Checker.noisy.tridiagonalsolver = true; if (Checker.noisy.tridiagonalsolver) { console.log("(1) A X AX"); @@ -46,40 +46,40 @@ class TestFixture { // console.logxyzB("A,B dummy points", xyz); } - B = A.Copy(); - this.ck.testTrue(A.FactorAndBackSubstitute(), "FactorAndBackSubstitute"); - this.ck.testTrue(A.Factor(), "repeat factor"); + B = A.copy(); + this.ck.testTrue(A.factorAndBackSubstitute(), "FactorAndBackSubstitute"); + this.ck.testTrue(A.factor(), "repeat factor"); if (Checker.noisy.tridiagonalsolver) { console.log("(2) LU, X?, AX"); console.log(A); } - A.MultiplyAX(); + A.multiplyAX(); if (Checker.noisy.tridiagonalsolver) { console.log("(3) LU, X?, LU(X?)"); console.log(A); } - this.CheckX(A, B); - this.CheckB(A, B); + this.checkX(A, B); + this.checkB(A, B); const pointX0 = []; for (let i = 0; i < 3; i++) { - const x = A.GetX(i); + const x = A.getX(i); pointX0.push(Point3d.create(x, 2.0 * x, x + 1.0)); } const pointB: Point3d[] = []; const pointX: Point3d[] = []; const pointB1: Point3d[] = []; - B.MultiplyAXPoints(pointX0, pointB); + B.multiplyAXPoints(pointX0, pointB); const pointBX = []; - for (const b of pointB) pointBX.push(b.clone ()); + for (const b of pointB) pointBX.push(b.clone()); // console.log ("B*pointX0", pointB); - this.ck.testFalse(A.FactorAndBackSubstitutePointArrays([], []), "FactorAndBackSubstitute fails with empty inputs"); - this.ck.testTrue (B.FactorAndBackSubstitutePointArrays(pointB, pointX), "FactorAndBackSubstitutePointArrays"); - this.ck.testTrue (B.FactorAndBackSubstitutePointArrays (pointBX, pointBX), "factorAndBackSubstitutePointArrays with aliased B, X"); + this.ck.testFalse(A.factorAndBackSubstitutePointArrays([], []), "FactorAndBackSubstitute fails with empty inputs"); + this.ck.testTrue(B.factorAndBackSubstitutePointArrays(pointB, pointX), "FactorAndBackSubstitutePointArrays"); + this.ck.testTrue(B.factorAndBackSubstitutePointArrays(pointBX, pointBX), "factorAndBackSubstitutePointArrays with aliased B, X"); // console.log (B); // console.log ("solved points", pointX); this.ck.testPoint3dArray(pointX0, pointX, "tridiagonal point solution"); - this.ck.testPoint3dArray (pointX, pointBX, "aliased B,X"); - B.MultiplyAXPoints(pointX0, pointB1); + this.ck.testPoint3dArray(pointX, pointBX, "aliased B,X"); + B.multiplyAXPoints(pointX0, pointB1); this.ck.testPoint3dArray(pointB, pointB1, "tridiagonal point multiply"); const flatten = A.flatten(); @@ -93,64 +93,64 @@ class TestFixture { // Order 4 systems with only 1 nonzero in X ... for (let k = 0; k < 4; k++) { A = new TriDiagonalSystem(4); - A.SetRow(0, 0, 2, 0.1); - A.SetRow(1, 1.1, 3, 0.1); - A.SetRow(2, 1.2, 4, 0.1); - A.SetRow(3, 1.3, 5, 0); + A.setRow(0, 0, 2, 0.1); + A.setRow(1, 1.1, 3, 0.1); + A.setRow(2, 1.2, 4, 0.1); + A.setRow(3, 1.3, 5, 0); for (let i = 0; i < 4; i++) { - A.SetX(i, 0.0); + A.setX(i, 0.0); } - A.SetX(k, 2.0); - A.MultiplyAX(); - B = A.Copy(); + A.setX(k, 2.0); + A.multiplyAX(); + B = A.copy(); if (Checker.noisy.tridiagonalsolver) { console.log("A, X, AX"); console.log(A.flatten()); } - A.FactorAndBackSubstitute(); + A.factorAndBackSubstitute(); if (Checker.noisy.tridiagonalsolver) { console.log("LU, X?, AX"); console.log(A.flatten()); } - A.MultiplyAX(); + A.multiplyAX(); if (Checker.noisy.tridiagonalsolver) { console.log("LU, X?, LU(X?)"); console.log(A.flatten()); } - this.CheckX(A, B); - this.CheckB(A, B); + this.checkX(A, B); + this.checkB(A, B); } } public testOrder4() { const A = new TriDiagonalSystem(4); let B: TriDiagonalSystem; - A.SetRow(0, 0, -3.2, 0.3); - A.SetRow(1, 1.1, -3.4, 0.78); - A.SetRow(2, 0.3, -3.1, 0.98); - A.SetRow(3, 0.43, -3.04, 0); + A.setRow(0, 0, -3.2, 0.3); + A.setRow(1, 1.1, -3.4, 0.78); + A.setRow(2, 0.3, -3.1, 0.98); + A.setRow(3, 0.43, -3.04, 0); for (let i = 0; i < 4; i++) { - A.SetX(i, (1 + i * i)); + A.setX(i, (1 + i * i)); } - A.MultiplyAX(); - B = A.Copy(); + A.multiplyAX(); + B = A.copy(); if (Checker.noisy.tridiagonalsolver) { console.log("A, X, AX"); console.log(A.flatten()); } - A.FactorAndBackSubstitute(); + A.factorAndBackSubstitute(); if (Checker.noisy.tridiagonalsolver) { console.log("LU, X?, AX"); console.log(A.flatten()); } - A.MultiplyAX(); + A.multiplyAX(); if (Checker.noisy.tridiagonalsolver) { console.log("LU, X?, LU(X?)"); console.log(A.flatten()); } - this.CheckX(A, B); - this.CheckB(A, B); + this.checkX(A, B); + this.checkB(A, B); } public testLargeSystem() { // Larger system. Diagonals between 3 and 4, off diagonals between 0 and 2 @@ -158,40 +158,40 @@ class TestFixture { const A = new TriDiagonalSystem(n); let B: TriDiagonalSystem; const noisy = 0; - A.SetRow(0, 0, 4, 0.4); + A.setRow(0, 0, 4, 0.4); for (let i = 1; i < (n - 1); i++) { const u = i / n; const v = u * 0.5; - A.AddToRow(i, (1 - v * v), (3.0 + u), (1 + v * v)); + A.addToRow(i, (1 - v * v), (3.0 + u), (1 + v * v)); } - A.SetRow(n - 1, 1.235, 3.99, 0); + A.setRow(n - 1, 1.235, 3.99, 0); for (let i = 0; i < n; i++) { - A.SetX(i, i + 1); + A.setX(i, i + 1); } - A.MultiplyAX(); - B = A.Copy(); + A.multiplyAX(); + B = A.copy(); if (noisy) { console.log("A, X, AX"); console.log(A.flatten()); } - A.FactorAndBackSubstitute(); + A.factorAndBackSubstitute(); if (noisy) { console.log("LU, X?, AX"); console.log(A.flatten()); } - A.MultiplyAX(); + A.multiplyAX(); if (noisy) { console.log("LU, X?, LU(X?)"); console.log(A.flatten()); } - A.Defactor(); + A.defactor(); if (noisy) { console.log("Defatored"); console.log(A.flatten()); } - this.CheckX(A, B); - this.CheckB(A, B); + this.checkX(A, B); + this.checkB(A, B); } public testRareConditions() { const n = 4; @@ -200,23 +200,23 @@ class TestFixture { const f0 = (value: number) => (value + 0.5); const f1 = (value: number) => ((value + 1) * (value + 1)); for (let i = 0; i < n; i++) { - A.SetB(i, f0(i)); - A.AddToB(i, f1(i)); + A.setB(i, f0(i)); + A.addToB(i, f1(i)); } for (let i = 0; i < n; i++) { - this.ck.testCoordinate(f0(i) + f1(i), A.GetB(i)); + this.ck.testCoordinate(f0(i) + f1(i), A.getB(i)); } - this.ck.testTrue(A.Defactor(), "Defactor noop for raw matrix"); - this.ck.testFalse(A.Factor(), "Expect factor failure on unpopulated matrix"); - this.ck.testFalse(A.MultiplyAX(), "multiplyAX called for incomplete matrix."); + this.ck.testTrue(A.defactor(), "Defactor noop for raw matrix"); + this.ck.testFalse(A.factor(), "Expect factor failure on unpopulated matrix"); + this.ck.testFalse(A.multiplyAX(), "multiplyAX called for incomplete matrix."); const pointX: Point3d[] = []; - let pointB: Point3d[] = [Point3d.create (), Point3d.create (), Point3d.create (), Point3d.create ()]; - this.ck.testFalse(A.MultiplyAXPoints(pointX, pointB), "multiplyAXPoints after factor failure."); - this.ck.testFalse(A.Defactor(), "Defactor fails after factor fail"); - this.ck.testFalse(A.FactorAndBackSubstitute(), "FactorAndBackSubstitute fails after factor fail"); - pointB = [Point3d.create (), Point3d.create (), Point3d.create (), Point3d.create ()]; - this.ck.testFalse(A.FactorAndBackSubstitutePointArrays(pointB, pointX), "FactorAndBackSubstitutePointArrays after factor fail"); - this.ck.testFalse(A.FactorAndBackSubstitutePointArrays([], pointX), "FactorAndBackSubstitutePointArrays with incomplete input array"); + let pointB: Point3d[] = [Point3d.create(), Point3d.create(), Point3d.create(), Point3d.create()]; + this.ck.testFalse(A.multiplyAXPoints(pointX, pointB), "multiplyAXPoints after factor failure."); + this.ck.testFalse(A.defactor(), "Defactor fails after factor fail"); + this.ck.testFalse(A.factorAndBackSubstitute(), "FactorAndBackSubstitute fails after factor fail"); + pointB = [Point3d.create(), Point3d.create(), Point3d.create(), Point3d.create()]; + this.ck.testFalse(A.factorAndBackSubstitutePointArrays(pointB, pointX), "FactorAndBackSubstitutePointArrays after factor fail"); + this.ck.testFalse(A.factorAndBackSubstitutePointArrays([], pointX), "FactorAndBackSubstitutePointArrays with incomplete input array"); } } diff --git a/core/geometry/src/test/FacetFaceData.test.ts b/core/geometry/src/test/polyface/FacetFaceData.test.ts similarity index 84% rename from core/geometry/src/test/FacetFaceData.test.ts rename to core/geometry/src/test/polyface/FacetFaceData.test.ts index 90098d7..a10d4fa 100644 --- a/core/geometry/src/test/FacetFaceData.test.ts +++ b/core/geometry/src/test/polyface/FacetFaceData.test.ts @@ -2,14 +2,14 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { FacetFaceData } from "../polyface/FacetFaceData"; -import { Sample } from "../serialization/GeometrySamples"; -import { GeometryQuery } from "../curve/GeometryQuery"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Checker } from "./Checker"; +import { FacetFaceData } from "../../polyface/FacetFaceData"; +import { Sample } from "../../serialization/GeometrySamples"; +import { GeometryQuery } from "../../curve/GeometryQuery"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { GeometryCoreTestIO } from "./GeometryCoreTestIO"; -import { Point2d } from "../geometry3d/Point2dVector2d"; +import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; +import { Point2d } from "../../geometry3d/Point2dVector2d"; /* tslint:disable:no-console */ diff --git a/core/geometry/src/test/Polyface.test.ts b/core/geometry/src/test/polyface/Polyface.test.ts similarity index 73% rename from core/geometry/src/test/Polyface.test.ts rename to core/geometry/src/test/polyface/Polyface.test.ts index 666f69e..bbbe6bb 100644 --- a/core/geometry/src/test/Polyface.test.ts +++ b/core/geometry/src/test/polyface/Polyface.test.ts @@ -2,34 +2,35 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { IndexedPolyface, Polyface, IndexedPolyfaceVisitor } from "../polyface/Polyface"; -import { PolyfaceQuery } from "../polyface/PolyfaceQuery"; -import { Sample } from "../serialization/GeometrySamples"; -import { PolyfaceBuilder } from "../polyface/PolyfaceBuilder"; -import { GeometryQuery } from "../curve/GeometryQuery"; -import { Point2d } from "../geometry3d/Point2dVector2d"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { Transform } from "../geometry3d/Transform"; -import { Range3d, Range2d } from "../geometry3d/Range"; -import { SolidPrimitive } from "../solid/SolidPrimitive"; -import { LineString3d } from "../curve/LineString3d"; -import { ParityRegion } from "../curve/ParityRegion"; -import { Loop } from "../curve/Loop"; -import { SweepContour } from "../solid/SweepContour"; -import { Checker } from "./Checker"; +import { IndexedPolyface, Polyface, IndexedPolyfaceVisitor } from "../../polyface/Polyface"; +import { PolyfaceQuery } from "../../polyface/PolyfaceQuery"; +import { Sample } from "../../serialization/GeometrySamples"; +import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; +import { GeometryQuery } from "../../curve/GeometryQuery"; +import { Point2d } from "../../geometry3d/Point2dVector2d"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { Transform } from "../../geometry3d/Transform"; +import { Range3d, Range2d } from "../../geometry3d/Range"; +import { SolidPrimitive } from "../../solid/SolidPrimitive"; +import { LineString3d } from "../../curve/LineString3d"; +import { ParityRegion } from "../../curve/ParityRegion"; +import { Loop } from "../../curve/Loop"; +import { SweepContour } from "../../solid/SweepContour"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { IModelJson } from "../serialization/IModelJsonSchema"; +import { IModelJson } from "../../serialization/IModelJsonSchema"; import * as fs from "fs"; -import { GeometryCoreTestIO } from "./GeometryCoreTestIO"; -import { StrokeOptions } from "../curve/StrokeOptions"; -import { prettyPrint } from "./testFunctions"; -import { Arc3d } from "../curve/Arc3d"; -import { AngleSweep } from "../geometry3d/AngleSweep"; -import { UVSurface } from "../geometry3d/GeometryHandler"; -import { Plane3dByOriginAndVectors } from "../geometry3d/Plane3dByOriginAndVectors"; -import { Angle } from "../geometry3d/Angle"; -import { Cone } from "../solid/Cone"; +import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; +import { StrokeOptions } from "../../curve/StrokeOptions"; +import { prettyPrint } from "../testFunctions"; +import { Arc3d } from "../../curve/Arc3d"; +import { AngleSweep } from "../../geometry3d/AngleSweep"; +import { UVSurface } from "../../geometry3d/GeometryHandler"; +import { Plane3dByOriginAndVectors } from "../../geometry3d/Plane3dByOriginAndVectors"; +import { Angle } from "../../geometry3d/Angle"; +import { Cone } from "../../solid/Cone"; +import { Sphere } from "../../solid/Sphere"; /* tslint:disable:no-console */ // @param longEdgeIsHidden true if any edge longer than1/3 of face perimiter is expected to be hidden @@ -67,11 +68,11 @@ function exercisePolyface(ck: Checker, polyface: Polyface, for (let i = 0; i < numPoint1; i++) { const pointIndexA = visitor1.clientPointIndex(i); - const pointA = visitor1.point.getPoint3dAt(i); + const pointA = visitor1.point.getPoint3dAtUncheckedPointIndex(i); polyface.data.copyPointTo(pointIndexA, pointB); if (!ck.testPoint3d(pointA, pointB)) { const pointIndexQ = visitor1.clientPointIndex(i); - const pointQ = visitor1.point.getPoint3dAt(i); + const pointQ = visitor1.point.getPoint3dAtUncheckedPointIndex(i); polyface.data.copyPointTo(pointIndexQ, pointB); ck.testPoint3d(pointQ, pointB); } else { @@ -83,7 +84,8 @@ function exercisePolyface(ck: Checker, polyface: Polyface, const paramIndexA = visitor1.clientParamIndex(i); const paramY = polyface.data.getParam(paramIndexA); polyface.data.copyParamTo(paramIndexA, paramZ); - ck.testPoint2d(paramY, paramZ, "polyface getParam, copyParamTo"); + if (ck.testPointer(paramY) && paramY) + ck.testPoint2d(paramY, paramZ, "polyface getParam, copyParamTo"); const normalIndexA = visitor1.clientNormalIndex(i); const normalY = polyface.data.getNormal(normalIndexA); @@ -97,10 +99,10 @@ function exercisePolyface(ck: Checker, polyface: Polyface, if (longEdgeIsHidden) { let perimeter = 0; for (let i = 0; i < numEdge; i++) { - perimeter += visitor1.point.getPoint3dAt(i).distance(visitor1.point.getPoint3dAt(i + 1)); + perimeter += visitor1.point.getPoint3dAtUncheckedPointIndex(i).distance(visitor1.point.getPoint3dAtUncheckedPointIndex(i + 1)); } for (let i = 0; i < numEdge; i++) { - const a = visitor1.point.getPoint3dAt(i).distance(visitor1.point.getPoint3dAt(i + 1)); + const a = visitor1.point.getPoint3dAtUncheckedPointIndex(i).distance(visitor1.point.getPoint3dAtUncheckedPointIndex(i + 1)); const v = visitor1.getEdgeVisible(i); if (!ck.testBoolean(a < perimeter / 3.0, v, "diagonals hidden")) { console.log({ faceCounter: facetIndex, edgeIndex: i, edgeLength: a, visibilityFlag: v }); @@ -171,11 +173,13 @@ describe("Polyface.HelloWorld", () => { // we know .. normal is 001, param is integers . . ck.testVector3d(Vector3d.unitZ(), polyface0.data.getNormal(0)!, "access normal"); const point0 = polyface0.data.getPoint(0)!; - const param0 = polyface0.data.getParam(numX * numY - 1); + const param0 = polyface0.data.getParam(numX * numY - 1)!; const normal0 = polyface0.data.getNormal(0); ck.testPoint3d(origin, point0); - ck.testExactNumber(param0.x, numX - 1); - ck.testExactNumber(param0.y, numY - 1); + if (param0 && ck.testPointer(param0)) { + ck.testExactNumber(param0.x, numX - 1); + ck.testExactNumber(param0.y, numY - 1); + } ck.testVector3d(normal0!, Vector3d.unitZ()); ck.testExactNumber(0, polyface0.numEdgeInFacet(100000), "numEdgeInFacet for bad index"); ck.testExactNumber(3, polyface0.numEdgeInFacet(1), "numEdgeInFacet (triangulated)"); @@ -195,7 +199,7 @@ describe("Polyface.HelloWorld", () => { const numExpectedFacets = 2 * (numX - 1) * (numY - 1); // 2 triangles per quad . . const expectedEdgeLength = numExpectedFacets * (2.0 + Math.sqrt(2.0)); for (const pf of [polyface0, polyface1, polyface2]) { - const loops = PolyfaceQuery.IndexedPolyfaceToLoops(pf); + const loops = PolyfaceQuery.indexedPolyfaceToLoops(pf); ck.testExactNumber(pf.facetCount, loops.children.length, "facet count"); // console.log("polyface area", PolyfaceQuery.sumFacetAreas(polyface)); // console.log(loops); @@ -232,7 +236,7 @@ describe("Polyface.HelloWorld", () => { polyface.addPointIndex(5); polyface.terminateFacet(); polyface.data.compress(); - const loops = PolyfaceQuery.IndexedPolyfaceToLoops(polyface); + const loops = PolyfaceQuery.indexedPolyfaceToLoops(polyface); // console.log("polyface area", PolyfaceQuery.sumFacetAreas(polyface)); // console.log(loops); ck.testCoordinate(1.0, PolyfaceQuery.sumFacetAreas(polyface), "unit square facets area"); @@ -282,16 +286,31 @@ describe("Polyface.Box", () => { }); }); -function writeMeshes(geometry: GeometryQuery[], fileName: string) { +function writeMeshes(geometry: GeometryQuery[], fileName: string, options?: StrokeOptions, dx0: number = 0, dy0: number = 0) { + let fileName1 = fileName.slice() + ".X"; + if (options) { + if (options.hasMaxEdgeLength) fileName1 = fileName1 + "E"; + if (options.needNormals) fileName1 = fileName1 + "N"; + if (options.needParams) fileName1 = fileName1 + "P"; + } + const allMesh = []; - let dx = 0.0; - let dy = 0.0; + let dx = dx0; + let gCount = -1; for (const g of geometry) { - const builder = PolyfaceBuilder.create(); + gCount++; + if (options === undefined) { + options = new StrokeOptions(); + } + const builder = PolyfaceBuilder.create(options); + const gRange = g.range(); - dx += 2.0 * gRange.xLength(); - dy = 4.0 * gRange.yLength(); - const transformX = Transform.createTranslationXYZ(dx, 0, 0); + const dyLocal = Math.max(20.0, 2.0 * gRange.yLength()); + const dxLocal = Math.max(10.0, 1.25 * gRange.xLength()); + const dyUSection = dyLocal; + const dyVSection = 2.0 * dyLocal; + const transformForPolyface = Transform.createTranslationXYZ(dx, dy0, 0); + const transformForPolyfaceRangeSticks = Transform.createTranslationXYZ(dx, dy0 + dyVSection, 0); if (!gRange.isNull) { const corners = gRange.corners(); @@ -305,62 +324,145 @@ function writeMeshes(geometry: GeometryQuery[], fileName: string) { corners[2], corners[0], corners[4], corners[5], corners[7], corners[6], corners[4]); - ls.tryTransformInPlace(transformX); + ls.tryTransformInPlace(transformForPolyfaceRangeSticks); allMesh.push(ls); } builder.addGeometryQuery(g); const polyface = builder.claimPolyface(); if (polyface) { - polyface.tryTransformInPlace(transformX); + polyface.tryTransformInPlace(transformForPolyface); allMesh.push(polyface); } if (g instanceof SolidPrimitive) { + const isClosedMesh = PolyfaceQuery.isPolyfaceClosedByEdgePairing(polyface); + const isClosedSolid = g.isClosedVolume; + if (polyface.isEmpty) { + console.log(fileName1, gCount + " of " + geometry.length + "is empty polyface"); + } else if (isClosedMesh !== isClosedSolid) + console.log(fileName1, gCount + " of " + geometry.length, { isClosedBySolid: isClosedSolid, isClosedByEdgePairing: isClosedMesh }); for (const f of [0.0, 0.10, 0.20, 0.25, 0.50, 0.75, 1.0]) { const section = g.constantVSection(f); if (section) { - section.tryTransformInPlace(Transform.createTranslationXYZ(dx, dy, 0)); + section.tryTransformInPlace(Transform.createTranslationXYZ(dx, dy0 + dyVSection, 0)); allMesh.push(section); } if ((g as any).constantUSection) { const uSection = (g as any).constantUSection(f); if (uSection) { - uSection.tryTransformInPlace(Transform.createTranslationXYZ(dx, 2.0 * dy, 0)); + uSection.tryTransformInPlace(Transform.createTranslationXYZ(dx, dy0 + dyUSection, 0)); allMesh.push(uSection); } } } } - dx += 2.0 * gRange.xLength(); + dx += dxLocal; } if (allMesh.length > 0) { - GeometryCoreTestIO.saveGeometry(allMesh, "Polyface", fileName); + GeometryCoreTestIO.saveGeometry(allMesh, "Polyface", fileName1); } } +// call writeMeshes with multiple options and placements +function writeAllMeshes(geometry: GeometryQuery[], name: string, options: StrokeOptions[], y0: number, dy: number) { + for (let i = 0; i < options.length; i++) { + writeMeshes(geometry, name, options[i], 0, y0 + i * dy); + } +} +type GeometryData = GeometryQuery | GeometryQuery[]; +function flattenGeometry(...data: GeometryData[]): GeometryQuery[] { + const result = []; + for (const member of data) { + if (member instanceof GeometryQuery) + result.push(member); + else if (Array.isArray(member)) + for (const g of member) + result.push(g); + } + return result; +} describe("Polyface.Facets", () => { + const options0 = new StrokeOptions(); + const options0E = new StrokeOptions(); + options0E.maxEdgeLength = 0.5; + const optionsN = new StrokeOptions(); + const optionsP = new StrokeOptions(); + const optionsPN = new StrokeOptions(); + const optionsPNE = new StrokeOptions(); + optionsP.needParams = true; + optionsN.needNormals = true; + optionsPN.needNormals = true; + optionsPN.needParams = true; + + optionsPNE.needNormals = true; + optionsPNE.needParams = true; + optionsPNE.maxEdgeLength = 0.5; + + const bigYStep = 800.0; // step between starts for different solid types + const optionYStep = 100.0; // steps between starts for option vairants of same solid type + const y0OpenSweeps = 0.0; + const y0ClosedSampler = y0OpenSweeps + bigYStep; + const y0Box = y0ClosedSampler + bigYStep; + const y0Cone = y0Box + bigYStep; + const y0Sphere = y0Cone + bigYStep; + const y0TorusPipe = y0Sphere + bigYStep; + const y0LinearSweep = y0TorusPipe + bigYStep; + const y0RotationalSweep = y0LinearSweep + bigYStep; + const y0RuledSweep = y0RotationalSweep + bigYStep; + const allOptions = [options0, optionsN, optionsP, options0E, optionsPNE]; + // const allEOptions = [options0E, optionsPNE]; it("Cones", () => { - writeMeshes(Sample.createCones(), "FacetedCones"); + const all = Sample.createCones(); + // writeAllMeshes(all, "ConeE", [optionsP], -y0Cone, optionYStep); + writeAllMeshes(all, "Cone", allOptions, y0Cone, optionYStep); }); it("Spheres", () => { - writeMeshes(Sample.createSpheres(), "FacetedSpheres"); + const all = Sample.createSpheres(); + // writeAllMeshes(all, "SphereNN", [optionsN], 0.0, optionYStep); + writeAllMeshes(all, "Sphere", allOptions, y0Sphere, optionYStep); }); it("Boxes", () => { - writeMeshes(Sample.createBoxes(), "FacetedBoxes"); + const allBox = flattenGeometry(Sample.createBoxes(false), Sample.createBoxes(true)); + writeAllMeshes(allBox, "Box", allOptions, y0Box, optionYStep); }); it("TorusPipes", () => { - writeMeshes(Sample.createTorusPipes(), "FacetedTorusPipes"); + const allBox = Sample.createTorusPipes(); + writeAllMeshes(allBox, "TorusPipe", allOptions, y0TorusPipe, optionYStep); }); it("LinearSweeps", () => { - writeMeshes(Sample.createSimpleLinearSweeps(), "FacetedLinearSweeps"); + // writeAllMeshes(Sample.createSimpleLinearSweeps(), "LinearSweepSubset", allEOptions, -y0LinearSweep, optionYStep); + writeAllMeshes(Sample.createSimpleLinearSweeps(), "LinearSweep", allOptions, y0LinearSweep, optionYStep); }); it("RotationalSweeps", () => { - writeMeshes(Sample.createSimpleRotationalSweeps(), "FacetedRotationalSweeps"); + writeAllMeshes(Sample.createSimpleRotationalSweeps(), "RotationalSweep", allOptions, y0RotationalSweep, optionYStep); + // writeMeshes(Sample.createSimpleRotationalSweeps(), "RotationalSweep", optionsP, 0, y0LinearSweep + 2 * optionYStep); + // writeMeshes(Sample.createSimpleRotationalSweeps(), "RotationalSweep", options0E, 0, y0RotationalSweep); + // writeMeshes(Sample.createSimpleRotationalSweeps(), "RotationalSweep", optionsN, 0, y0RotationalSweep + optionYStep); }); it("RuledSweeps", () => { - writeMeshes(Sample.createRuledSweeps(true), "FacetedRuledSweeps"); + const sweepP = Sample.createRuledSweeps(true); + writeAllMeshes(sweepP, "RuledSweep", allOptions, y0RuledSweep, optionYStep); + + // writeMeshes(sweepP, "RuledSweep", optionsP, 0, y0RuledSweep + 2 * optionYStep); + // const sweepB = Sample.createRuledSweeps(true); + // writeMeshes(sweepB, "RuledSweep", options0E, 0, y0RuledSweep); + // const sweepA = Sample.createRuledSweeps(true); + // writeMeshes(sweepA, "RuledSweep", optionsN, 0, y0RuledSweep + optionYStep); + }); + it("Samplers", () => { + const openSweeps = Sample.createClosedSolidSampler(false); + writeAllMeshes([openSweeps[4]], "Work", [optionsPNE, optionsPNE], y0OpenSweeps, optionYStep); + writeAllMeshes(openSweeps, "OpenSweeps", allOptions, y0OpenSweeps, optionYStep); + const closedSolids = Sample.createClosedSolidSampler(true); + writeAllMeshes(closedSolids, "ClosedSweeps", allOptions, y0ClosedSampler, optionYStep); + }); + /* + it.only("SamplerA", () => { + const geometry = Sample.createRevolutionLsegArcLstr(false); + writeAllMeshes(geometry, "SamplerA", allOptions, 0, optionYStep); }); +*/ }); describe("Polyface.Faces", () => { @@ -443,45 +545,46 @@ describe("Polyface.Faces", () => { builder.addCoordinateFacets(grid, undefined, undefined, true); const polyface = builder.claimPolyface(false); - ck.testExactNumber(polyface.pointCount, polyface.normalCount, "Number of normals match point count"); + // ck.testExactNumber(polyface.pointCount, polyface.normalCount, "Number of normals match point count"); ck.testExactNumber(polyface.pointCount, polyface.paramCount, "Number of params matches point count"); // Check params for (let idx = 0; idx < polyface.data.paramIndex!.length; idx++) { - const currentPoint = polyface.data.point.getPoint3dAt(idx); - const currentParam = polyface.data.param![idx]; + const currentPoint = polyface.data.point.getPoint3dAtUncheckedPointIndex(idx); + const currentParam = polyface.data.param!.getPoint2dAtCheckedPointIndex(idx)!; if (idx % 4 === 0) { ck.testCoordinate(currentParam.x, 0); ck.testCoordinate(currentParam.y, 0); } else if (idx % 4 === 1) { - const oldPoint = polyface.data.point.getPoint3dAt(idx - 1); + const oldPoint = polyface.data.point.getPoint3dAtUncheckedPointIndex(idx - 1); ck.testCoordinate(currentParam.x, Math.hypot(currentPoint.x - oldPoint.x, currentPoint.y - oldPoint.y, currentPoint.z - oldPoint.z)); - ck.testCoordinate(polyface.data.param![idx].y, 0); + ck.testCoordinate(polyface.data.param!.getYAtUncheckedPointIndex(idx), 0); } // else if (idx % 4 === 2) // else } - - // Check normals - for (let idx = 0; idx < polyface.data.normalIndex!.length - 1; idx++) { - if (idx % 4 === 0) { - const pointA = polyface.data.point.getPoint3dAt(idx); - const pointB = polyface.data.point.getPoint3dAt(idx + 1); - const pointC = polyface.data.point.getPoint3dAt(idx + 2); - const vecAB = pointA.vectorTo(pointB); - const vecAC = pointA.vectorTo(pointC); - const normalArray = polyface.data.normal!; - ck.testCoordinate(normalArray.atVector3dIndex(idx)!.dotProduct(vecAB), 0, "Normal is perpendicular to grid surface"); - ck.testCoordinate(normalArray.atVector3dIndex(idx)!.dotProduct(vecAC), 0, "Normal is perpendicular to grid surface"); - ck.testCoordinate(normalArray.atVector3dIndex(idx + 1)!.dotProduct(vecAB), 0, "Normal is perpendicular to grid surface"); - ck.testCoordinate(normalArray.atVector3dIndex(idx + 1)!.dotProduct(vecAC), 0, "Normal is perpendicular to grid surface"); - ck.testCoordinate(normalArray.atVector3dIndex(idx + 2)!.dotProduct(vecAB), 0, "Normal is perpendicular to grid surface"); - ck.testCoordinate(normalArray.atVector3dIndex(idx + 2)!.dotProduct(vecAC), 0, "Normal is perpendicular to grid surface"); - ck.testCoordinate(normalArray.atVector3dIndex(idx + 3)!.dotProduct(vecAB), 0, "Normal is perpendicular to grid surface"); - ck.testCoordinate(normalArray.atVector3dIndex(idx + 3)!.dotProduct(vecAC), 0, "Normal is perpendicular to grid surface"); - } - } - + /* EDL -- this test makes assumptions about normal indices. + With recent (Feb 2019) optimizations of normal constructions, the normals cannot be accessed this way. + // Check normals + for (let idx = 0; idx < polyface.data.normalIndex!.length - 1; idx++) { + if (idx % 4 === 0) { + const pointA = polyface.data.point.getPoint3dAt(idx); + const pointB = polyface.data.point.getPoint3dAt(idx + 1); + const pointC = polyface.data.point.getPoint3dAt(idx + 2); + const vecAB = pointA.vectorTo(pointB); + const vecAC = pointA.vectorTo(pointC); + const normalArray = polyface.data.normal!; + ck.testCoordinate(normalArray.atVector3dIndex(idx)!.dotProduct(vecAB), 0, "Normal is perpendicular to grid surface"); + ck.testCoordinate(normalArray.atVector3dIndex(idx)!.dotProduct(vecAC), 0, "Normal is perpendicular to grid surface"); + ck.testCoordinate(normalArray.atVector3dIndex(idx + 1)!.dotProduct(vecAB), 0, "Normal is perpendicular to grid surface"); + ck.testCoordinate(normalArray.atVector3dIndex(idx + 1)!.dotProduct(vecAC), 0, "Normal is perpendicular to grid surface"); + ck.testCoordinate(normalArray.atVector3dIndex(idx + 2)!.dotProduct(vecAB), 0, "Normal is perpendicular to grid surface"); + ck.testCoordinate(normalArray.atVector3dIndex(idx + 2)!.dotProduct(vecAC), 0, "Normal is perpendicular to grid surface"); + ck.testCoordinate(normalArray.atVector3dIndex(idx + 3)!.dotProduct(vecAB), 0, "Normal is perpendicular to grid surface"); + ck.testCoordinate(normalArray.atVector3dIndex(idx + 3)!.dotProduct(vecAC), 0, "Normal is perpendicular to grid surface"); + } + } + */ expect(ck.getNumErrors()).equals(0); }); @@ -506,8 +609,8 @@ describe("Polyface.Faces", () => { const nativeParamIdx = nativePolyface.Group.Member[0].IndexedMesh.ParamIndex; ck.testExactNumber(jsParamsIdx!.length, nativeParamIdx!.length, "Number of params match"); for (let i = 0; i < jsParams!.length; i++) { - ck.testCoordinate(jsParams![polyface.data.paramIndex![i]].x, nativeParams![nativeParamIdx![i]][0]); - ck.testCoordinate(jsParams![polyface.data.paramIndex![i]].y, nativeParams![nativeParamIdx![i]][1]); + ck.testCoordinate(jsParams!.getXAtUncheckedPointIndex(polyface.data.paramIndex![i]), nativeParams![nativeParamIdx![i]][0]); + ck.testCoordinate(jsParams!.getYAtUncheckedPointIndex(polyface.data.paramIndex![i]), nativeParams![nativeParamIdx![i]][1]); } const jsNormals = polyface.data.normal!; @@ -516,7 +619,7 @@ describe("Polyface.Faces", () => { const nativeNormalIdx = nativePolyface.Group.Member[0].IndexedMesh.NormalIndex; ck.testExactNumber(jsNormalIdx!.length, nativeNormalIdx!.length, "Number of params match"); for (let i = 0; i < jsNormals!.length; i++) { - const normal = jsNormals.atVector3dIndex(i)!; + const normal = jsNormals.getVector3dAtCheckedVectorIndex(i)!; ck.testCoordinate(normal.x, nativeNormals![nativeNormalIdx![i]][0]); ck.testCoordinate(normal.y, nativeNormals![nativeNormalIdx![i]][1]); ck.testCoordinate(normal.z, nativeNormals![nativeNormalIdx![i]][2]); @@ -667,7 +770,7 @@ function createMeshByUVSurface(surface: UVSurface, numXEdge: number, numYEdge: n const builder = PolyfaceBuilder.create(options); if (reverseFacets) builder.toggleReversedFacetFlag(); - builder.addUVGrid(surface, numXEdge + 1, numYEdge + 1, false); + builder.addUVGridBody(surface, numXEdge + 1, numYEdge + 1); return builder.claimPolyface(); } @@ -688,12 +791,12 @@ class UVSinusoidalSurface implements UVSurface { this.thetaV = thetaV; this.transform = transform; } - public UVFractionToPoint(u: number, v: number): Point3d { + public uvFractionToPoint(u: number, v: number): Point3d { const thetaU = this.thetaU.fractionToRadians(u); const thetaV = this.thetaV.fractionToRadians(v); return this.transform.multiplyXYZ(u, v, Math.cos(thetaU) * Math.cos(thetaV)); } - public UVFractionToPointAndTangents(u: number, v: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors { + public uvFractionToPointAndTangents(u: number, v: number, result?: Plane3dByOriginAndVectors): Plane3dByOriginAndVectors { const thetaU = this.thetaU.fractionToRadians(u); const thetaV = this.thetaV.fractionToRadians(v); const cU = Math.cos(thetaU); @@ -894,28 +997,30 @@ it("EmptyPolyface", () => { }); it("VisitorParamQueries", () => { - const options = new StrokeOptions(); - options.needParams = true; - options.needNormals = true; - const builder = PolyfaceBuilder.create(options); - builder.toggleReversedFacetFlag(); - const cone = Cone.createAxisPoints(Point3d.create(0, 0, 0), Point3d.create(0, 0, 5), 1.0, 0.5, true)!; - - builder.addCone(cone); - const polyface = builder.claimPolyface(true); - const visitor = polyface.createVisitor(0) as IndexedPolyfaceVisitor; - let facetIndex = 0; - const distanceRange = Range2d.createNull(); - const fractionRange = Range2d.createNull(); - for (; visitor.moveToNextFacet(); facetIndex++) { - for (let i = 0; i < visitor.numEdgesThisFacet; i++) { - const distanceParam = visitor.tryGetDistanceParameter(i); - const fractionParam = visitor.tryGetNormalizedParameter(i); - distanceRange.extendPoint(distanceParam!); - fractionRange.extendPoint(fractionParam!); + for (const s of [ + Cone.createAxisPoints(Point3d.create(0, 0, 0), Point3d.create(0, 0, 5), 1.0, 0.5, true)!, + Sphere.createCenterRadius(Point3d.create(0, 0, 0), 2.0, AngleSweep.createStartEndDegrees(0, 90)), + ]) { + const options = new StrokeOptions(); + options.needParams = true; + options.needNormals = true; + const builder = PolyfaceBuilder.create(options); + // builder.toggleReversedFacetFlag(); + s.dispatchToGeometryHandler(builder); + const polyface = builder.claimPolyface(true); + const visitor = polyface.createVisitor(0) as IndexedPolyfaceVisitor; + let facetIndex = 0; + const distanceRange = Range2d.createNull(); + const fractionRange = Range2d.createNull(); + for (; visitor.moveToNextFacet(); facetIndex++) { + for (let i = 0; i < visitor.numEdgesThisFacet; i++) { + const distanceParam = visitor.tryGetDistanceParameter(i); + const fractionParam = visitor.tryGetNormalizedParameter(i); + distanceRange.extendPoint(distanceParam!); + fractionRange.extendPoint(fractionParam!); + } } } - }); it("VisitorQueryFailures", () => { diff --git a/core/geometry/src/test/PolyfaceAuxData.test.ts b/core/geometry/src/test/polyface/PolyfaceAuxData.test.ts similarity index 90% rename from core/geometry/src/test/PolyfaceAuxData.test.ts rename to core/geometry/src/test/polyface/PolyfaceAuxData.test.ts index 9bddfed..6d7d0fd 100644 --- a/core/geometry/src/test/PolyfaceAuxData.test.ts +++ b/core/geometry/src/test/polyface/PolyfaceAuxData.test.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Checker } from "./Checker"; -import { PolyfaceBuilder } from "../polyface/PolyfaceBuilder"; -import { PolyfaceAuxData, AuxChannel, AuxChannelData, AuxChannelDataType } from "../polyface/AuxData"; -import { StrokeOptions } from "../curve/StrokeOptions"; -import { Arc3d } from "../curve/Arc3d"; -import { LineString3d } from "../curve/LineString3d"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Transform } from "../geometry3d/Transform"; -import { IModelJson } from "../serialization/IModelJsonSchema"; +import { Checker } from "../Checker"; +import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; +import { PolyfaceAuxData, AuxChannel, AuxChannelData, AuxChannelDataType } from "../../polyface/AuxData"; +import { StrokeOptions } from "../../curve/StrokeOptions"; +import { Arc3d } from "../../curve/Arc3d"; +import { LineString3d } from "../../curve/LineString3d"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Transform } from "../../geometry3d/Transform"; +import { IModelJson } from "../../serialization/IModelJsonSchema"; import { expect } from "chai"; /** Create a polyface representing a cantilever beam with [[PolyfaceAuxData]] representing the stress and deflection. */ @@ -32,7 +32,7 @@ function createCantileverBeamPolyface(beamRadius: number = 10.0, beamLength: num const scratchPoint = Point3d.create(); for (let i = 0; i < polyface.data.point.length; i++) { - const point = polyface.data.point.atPoint3dIndex(i, scratchPoint) as Point3d; + const point = polyface.data.point.getPoint3dAtCheckedPointIndex(i, scratchPoint) as Point3d; heightData.push(point.z * zScale); } const heightChannel = new AuxChannel([new AuxChannelData(0.0, heightData)], AuxChannelDataType.Distance, "Height", ""); diff --git a/core/geometry/src/test/PolyfaceData.test.ts b/core/geometry/src/test/polyface/PolyfaceData.test.ts similarity index 90% rename from core/geometry/src/test/PolyfaceData.test.ts rename to core/geometry/src/test/polyface/PolyfaceData.test.ts index d3a44cd..4d45aa6 100644 --- a/core/geometry/src/test/PolyfaceData.test.ts +++ b/core/geometry/src/test/polyface/PolyfaceData.test.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Checker } from "./Checker"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { IndexedPolyface } from "../polyface/Polyface"; -import { PolyfaceData } from "../polyface/PolyfaceData"; +import { IndexedPolyface } from "../../polyface/Polyface"; +import { PolyfaceData } from "../../polyface/PolyfaceData"; /* tslint:disable:no-console */ describe("PolyfaceData", () => { diff --git a/core/geometry/src/test/polyface/SortableEdgeCluster.test.ts b/core/geometry/src/test/polyface/SortableEdgeCluster.test.ts new file mode 100644 index 0000000..c5cb84d --- /dev/null +++ b/core/geometry/src/test/polyface/SortableEdgeCluster.test.ts @@ -0,0 +1,94 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { Checker } from "../Checker"; +import { expect } from "chai"; +import { IndexedEdgeMatcher, SortableEdgeCluster, SortableEdge } from "../../polyface/IndexedEdgeMatcher"; + +describe("SortableEdgeCluster", () => { + it("hello", () => { + const ck = new Checker(); + const edgeArray = new IndexedEdgeMatcher(); + // + // 4 <<<<<<<< 3 + // v ^ + // v F0 ^ + // 1 >>>>>>>> 2 + // 0<<<<<<<<< 1 <<<<<<<< 2<<<<<<<<10 + // V ^V ^ V F5 ^ + // V F2 ^V F1 ^ V ^ + // 5 >>>>>>>> 6 >>>>>>>> 7>>>>>>>>10 degenerate 10 to 10 !! + // >>>>>>>> + // ^ V + // ^ F3 V + // 8 <<<<<<< 9 + // 8 <<<<<<< 9 + // 8 >>F4>>> 9 // nul face F4!!!! + // + edgeArray.addPath([1, 2, 3, 4], 0); + ck.testTrue(edgeArray.edges[0].islowHigh, "low to high"); + ck.testFalse(edgeArray.edges[3].islowHigh, "high to low"); + ck.testExactNumber(edgeArray.edges[0].vertexIndexA, edgeArray.edges[3].vertexIndexB, "confirm facet closure"); + edgeArray.addPath([1, 6, 7, 2], 1); + ck.testExactNumber(1, edgeArray.edges[4].facetIndex, "confirm facet index"); + const n1 = edgeArray.edges.length; + edgeArray.addPath([], 20); // force null return. + ck.testExactNumber(n1, edgeArray.edges.length, "confirm no edges added with empty input"); + edgeArray.addPath([5, 6, 1, 0], 2); + edgeArray.addPath([6, 9, 8, 5], 3); // Clockwise == creates error case 5>>>6 !!! + edgeArray.addPath([8, 9], 4); + edgeArray.addPath([2, 7, 10, 10, 2], 5, false); // null edge, with explicit closure + + const manifold: SortableEdgeCluster[] = []; + const boundary: SortableEdgeCluster[] = []; + const compound: SortableEdgeCluster[] = []; + const nullEdges: SortableEdgeCluster[] = []; + edgeArray.sortAndcollectClusters(manifold, boundary, nullEdges, compound); + ck.testExactNumber(3, manifold.length, "edge pairs", SortableEdge.clusterArrayToJSON(manifold)); + ck.testExactNumber(10, boundary.length, "boundary edges", SortableEdge.clusterArrayToJSON(boundary)); + ck.testExactNumber(2, compound.length, "clusters", JSON.stringify(SortableEdge.clusterArrayToJSON(compound))); + ck.testExactNumber(1, nullEdges.length, "null", JSON.stringify(SortableEdge.clusterArrayToJSON(nullEdges))); + + edgeArray.sortAndcollectClusters(undefined, undefined, undefined, undefined); + if (nullEdges.length === 1) { + const edge = nullEdges[0] as SortableEdge; + ck.testExactNumber(5, edge.facetIndex, "confirm facet of null edgge"); + } + expect(ck.getNumErrors()).equals(0); + }); + it("singleFaceCube", () => { + const ck = new Checker(); + // Cube vertex numbers ...... + // + // 0-------------------1 + // | \ / | + // | 2-----------3 | + // | | | | + // | | | | + // | 4-----------5 | + // | / \ | + // 6------------------7 + + const edgeArray = new IndexedEdgeMatcher(); + // wander around and catch both sides of all the edges in one path !! + edgeArray.addPath( + [5, 3, + 5, 7, 1, 3, + 1, 0, 2, 3, + 2, 4, 2, + 0, 6, 0, 1, 7, + 6, 4, 6, 7, 5, 4, 5], 0, false); + const manifold: SortableEdgeCluster[] = []; + const boundary: SortableEdgeCluster[] = []; + const compound: SortableEdgeCluster[] = []; + const nullEdges: SortableEdgeCluster[] = []; + edgeArray.sortAndcollectClusters(manifold, boundary, nullEdges, compound); + ck.testExactNumber(12, manifold.length, "edge pairs", SortableEdge.clusterArrayToJSON(manifold)); + ck.testExactNumber(0, boundary.length, "boundary edges", SortableEdge.clusterArrayToJSON(boundary)); + ck.testExactNumber(0, compound.length, "clusters", JSON.stringify(SortableEdge.clusterArrayToJSON(compound))); + ck.testExactNumber(0, nullEdges.length, "null", JSON.stringify(SortableEdge.clusterArrayToJSON(nullEdges))); + expect(ck.getNumErrors()).equals(0); + }); +}); diff --git a/core/geometry/src/test/BSIJSON.test.ts b/core/geometry/src/test/serialization/BSIJSON.test.ts similarity index 86% rename from core/geometry/src/test/BSIJSON.test.ts rename to core/geometry/src/test/serialization/BSIJSON.test.ts index 9d0cb41..396a62e 100644 --- a/core/geometry/src/test/BSIJSON.test.ts +++ b/core/geometry/src/test/serialization/BSIJSON.test.ts @@ -2,47 +2,47 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { BeJSONFunctions } from "../Geometry"; -import { AngleSweep } from "../geometry3d/AngleSweep"; -import { Angle } from "../geometry3d/Angle"; -import { Complex } from "../numerics/Complex"; -import { Plane3dByOriginAndUnitNormal } from "../geometry3d/Plane3dByOriginAndUnitNormal"; -import { Ray3d } from "../geometry3d/Ray3d"; -import { Plane3dByOriginAndVectors } from "../geometry3d/Plane3dByOriginAndVectors"; -import { Point2d, Vector2d } from "../geometry3d/Point2dVector2d"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Segment1d } from "../geometry3d/Segment1d"; -import { YawPitchRollAngles } from "../geometry3d/YawPitchRollAngles"; -import { Range1d, Range2d, Range3d } from "../geometry3d/Range"; -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { Transform } from "../geometry3d/Transform"; -import { UnionRegion } from "../curve/UnionRegion"; -import { BagOfCurves } from "../curve/CurveCollection"; -import { ParityRegion } from "../curve/ParityRegion"; -import { Loop } from "../curve/Loop"; -import { Path } from "../curve/Path"; - -import { IndexedPolyface } from "../polyface/Polyface"; -import { Checker } from "./Checker"; -import { Map4d } from "../geometry4d/Map4d"; -import { Matrix4d } from "../geometry4d/Matrix4d"; -import { Point4d } from "../geometry4d/Point4d"; -import { TransitionSpiral3d } from "../curve/TransitionSpiral"; +import { BeJSONFunctions } from "../../Geometry"; +import { AngleSweep } from "../../geometry3d/AngleSweep"; +import { Angle } from "../../geometry3d/Angle"; +import { Complex } from "../../numerics/Complex"; +import { Plane3dByOriginAndUnitNormal } from "../../geometry3d/Plane3dByOriginAndUnitNormal"; +import { Ray3d } from "../../geometry3d/Ray3d"; +import { Plane3dByOriginAndVectors } from "../../geometry3d/Plane3dByOriginAndVectors"; +import { Point2d, Vector2d } from "../../geometry3d/Point2dVector2d"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Segment1d } from "../../geometry3d/Segment1d"; +import { YawPitchRollAngles } from "../../geometry3d/YawPitchRollAngles"; +import { Range1d, Range2d, Range3d } from "../../geometry3d/Range"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { Transform } from "../../geometry3d/Transform"; +import { UnionRegion } from "../../curve/UnionRegion"; +import { BagOfCurves } from "../../curve/CurveCollection"; +import { ParityRegion } from "../../curve/ParityRegion"; +import { Loop } from "../../curve/Loop"; +import { Path } from "../../curve/Path"; + +import { IndexedPolyface } from "../../polyface/Polyface"; +import { Checker } from "../Checker"; +import { Map4d } from "../../geometry4d/Map4d"; +import { Matrix4d } from "../../geometry4d/Matrix4d"; +import { Point4d } from "../../geometry4d/Point4d"; +import { TransitionSpiral3d } from "../../curve/TransitionSpiral"; import { expect } from "chai"; -import { Sample } from "../serialization/GeometrySamples"; -import { NullGeometryHandler } from "../geometry3d/GeometryHandler"; - -import { LineString3d } from "../curve/LineString3d"; -import { PointString3d } from "../curve/PointString3d"; -import { Arc3d } from "../curve/Arc3d"; -import { LineSegment3d } from "../curve/LineSegment3d"; -import { IModelJson } from "../serialization/IModelJsonSchema"; +import { Sample } from "../../serialization/GeometrySamples"; +import { NullGeometryHandler } from "../../geometry3d/GeometryHandler"; + +import { LineString3d } from "../../curve/LineString3d"; +import { PointString3d } from "../../curve/PointString3d"; +import { Arc3d } from "../../curve/Arc3d"; +import { LineSegment3d } from "../../curve/LineSegment3d"; +import { IModelJson } from "../../serialization/IModelJsonSchema"; /* tslint:disable:no-console trailing-comma object-literal-key-quotes*/ // Requires for grabbing json object from external file import * as fs from "fs"; -import { GeometryQuery } from "../curve/GeometryQuery"; -import { CoordinateXYZ } from "../curve/CoordinateXYZ"; +import { GeometryQuery } from "../../curve/GeometryQuery"; +import { CoordinateXYZ } from "../../curve/CoordinateXYZ"; // Variables used for testing let outputFolderPath = "./src/test/output"; diff --git a/core/geometry/src/test/IModelJson.test.ts b/core/geometry/src/test/serialization/IModelJson.test.ts similarity index 92% rename from core/geometry/src/test/IModelJson.test.ts rename to core/geometry/src/test/serialization/IModelJson.test.ts index 8098d97..6c9a853 100644 --- a/core/geometry/src/test/IModelJson.test.ts +++ b/core/geometry/src/test/serialization/IModelJson.test.ts @@ -2,23 +2,23 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Point3d } from "../geometry3d/Point3dVector3d"; +import { Point3d } from "../../geometry3d/Point3dVector3d"; -import { GeometryQuery } from "../curve/GeometryQuery"; -import { Arc3d } from "../curve/Arc3d"; -import { Checker } from "./Checker"; +import { GeometryQuery } from "../../curve/GeometryQuery"; +import { Arc3d } from "../../curve/Arc3d"; +import { Checker } from "../Checker"; import { expect } from "chai"; -import { Sample } from "../serialization/GeometrySamples"; -import { DeepCompare } from "../serialization/DeepCompare"; -import { prettyPrint } from "./testFunctions"; +import { Sample } from "../../serialization/GeometrySamples"; +import { DeepCompare } from "../../serialization/DeepCompare"; +import { prettyPrint } from "../testFunctions"; -import { IModelJson } from "../serialization/IModelJsonSchema"; +import { IModelJson } from "../../serialization/IModelJsonSchema"; /* tslint:disable:no-console trailing-comma object-literal-key-quotes*/ // Requires for grabbing json object from external file import * as fs from "fs"; -import { CoordinateXYZ } from "../curve/CoordinateXYZ"; -import { GeometryCoreTestIO } from "./GeometryCoreTestIO"; +import { CoordinateXYZ } from "../../curve/CoordinateXYZ"; +import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; // directory containing imjs files produced by native geomlibs tests: const iModelJsonNativeSamplesDirectory = "./src/test/iModelJsonSamples/fromNative/"; diff --git a/core/geometry/src/test/constructorsAndIModelJson.test.ts b/core/geometry/src/test/serialization/constructorsAndIModelJson.test.ts similarity index 83% rename from core/geometry/src/test/constructorsAndIModelJson.test.ts rename to core/geometry/src/test/serialization/constructorsAndIModelJson.test.ts index 7557f13..4995f0d 100644 --- a/core/geometry/src/test/constructorsAndIModelJson.test.ts +++ b/core/geometry/src/test/serialization/constructorsAndIModelJson.test.ts @@ -3,28 +3,28 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { LineSegment3d } from "../curve/LineSegment3d"; -import { GeometryQuery } from "../curve/GeometryQuery"; -import { CoordinateXYZ } from "../curve/CoordinateXYZ"; -import { LineString3d } from "../curve/LineString3d"; -import { Arc3d } from "../curve/Arc3d"; -import { BSplineCurve3d } from "../bspline/BSplineCurve"; -import { AngleSweep } from "../geometry3d/AngleSweep"; -import { Angle } from "../geometry3d/Angle"; -import { ParityRegion } from "../curve/ParityRegion"; -import { Loop } from "../curve/Loop"; -import { Path } from "../curve/Path"; -import { Sphere } from "../solid/Sphere"; -import { Cone } from "../solid/Cone"; -import { Box } from "../solid/Box"; -import { Transform } from "../geometry3d/Transform"; -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { TorusPipe } from "../solid/TorusPipe"; -import { LinearSweep } from "../solid/LinearSweep"; -import { RotationalSweep } from "../solid/RotationalSweep"; -import { Ray3d } from "../geometry3d/Ray3d"; -import { IModelJson } from "../serialization/IModelJsonSchema"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { LineSegment3d } from "../../curve/LineSegment3d"; +import { GeometryQuery } from "../../curve/GeometryQuery"; +import { CoordinateXYZ } from "../../curve/CoordinateXYZ"; +import { LineString3d } from "../../curve/LineString3d"; +import { Arc3d } from "../../curve/Arc3d"; +import { BSplineCurve3d } from "../../bspline/BSplineCurve"; +import { AngleSweep } from "../../geometry3d/AngleSweep"; +import { Angle } from "../../geometry3d/Angle"; +import { ParityRegion } from "../../curve/ParityRegion"; +import { Loop } from "../../curve/Loop"; +import { Path } from "../../curve/Path"; +import { Sphere } from "../../solid/Sphere"; +import { Cone } from "../../solid/Cone"; +import { Box } from "../../solid/Box"; +import { Transform } from "../../geometry3d/Transform"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { TorusPipe } from "../../solid/TorusPipe"; +import { LinearSweep } from "../../solid/LinearSweep"; +import { RotationalSweep } from "../../solid/RotationalSweep"; +import { Ray3d } from "../../geometry3d/Ray3d"; +import { IModelJson } from "../../serialization/IModelJsonSchema"; /* tslint:disable:no-console */ // This file emits (to console.log) text suitable for use as markdown content for examples of construtor call and json of results diff --git a/core/geometry/src/test/fromJSON.test.ts b/core/geometry/src/test/serialization/fromJSON.test.ts similarity index 94% rename from core/geometry/src/test/fromJSON.test.ts rename to core/geometry/src/test/serialization/fromJSON.test.ts index 1616a62..1831690 100644 --- a/core/geometry/src/test/fromJSON.test.ts +++ b/core/geometry/src/test/serialization/fromJSON.test.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ /* tslint:disable: no-console */ -import { Checker } from "./Checker"; +import { Checker } from "../Checker"; // import { expect } from "chai"; -import * as g from "../geometry-core"; -import { SimpleFactory } from "./SimpleFactory"; +import * as g from "../../geometry-core"; +import { SimpleFactory } from "../SimpleFactory"; let noisy = 0; function report(a: any, b: any) { if (noisy > 0) diff --git a/core/geometry/src/test/Solid.test.ts b/core/geometry/src/test/solid/Solid.test.ts similarity index 85% rename from core/geometry/src/test/Solid.test.ts rename to core/geometry/src/test/solid/Solid.test.ts index 1d6ffdf..0b5fedd 100644 --- a/core/geometry/src/test/Solid.test.ts +++ b/core/geometry/src/test/solid/Solid.test.ts @@ -3,36 +3,36 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Sample } from "../serialization/GeometrySamples"; -import { GeometryQuery } from "../curve/GeometryQuery"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Transform } from "../geometry3d/Transform"; -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { SolidPrimitive } from "../solid/SolidPrimitive"; -import { Checker } from "./Checker"; +import { Sample } from "../../serialization/GeometrySamples"; +import { GeometryQuery } from "../../curve/GeometryQuery"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Transform } from "../../geometry3d/Transform"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { SolidPrimitive } from "../../solid/SolidPrimitive"; +import { Checker } from "../Checker"; import { expect } from "chai"; import * as fs from "fs"; -import { Cone } from "../solid/Cone"; -import { LineSegment3d } from "../curve/LineSegment3d"; -import { StrokeOptions } from "../curve/StrokeOptions"; -import { AngleSweep } from "../geometry3d/AngleSweep"; -import { Sphere } from "../solid/Sphere"; -import { Angle } from "../geometry3d/Angle"; -import { Range3d } from "../geometry3d/Range"; -import { Plane3dByOriginAndVectors } from "../geometry3d/Plane3dByOriginAndVectors"; -import { IModelJson } from "../serialization/IModelJsonSchema"; -import { prettyPrint } from "./testFunctions"; -import { GeometryCoreTestIO } from "./GeometryCoreTestIO"; -import { RotationalSweep } from "../solid/RotationalSweep"; -import { Path } from "../curve/Path"; -import { Ray3d } from "../geometry3d/Ray3d"; -import { TorusPipe } from "../solid/TorusPipe"; -import { SweepContour } from "../solid/SweepContour"; -import { LineString3d } from "../curve/LineString3d"; -import { ConstructCurveBetweenCurves } from "../curve/ConstructCurveBetweenCurves"; -import { Arc3d } from "../curve/Arc3d"; -import { BSplineCurve3d } from "../bspline/BSplineCurve"; -import { RuledSweep } from "../solid/RuledSweep"; +import { Cone } from "../../solid/Cone"; +import { LineSegment3d } from "../../curve/LineSegment3d"; +import { StrokeOptions } from "../../curve/StrokeOptions"; +import { AngleSweep } from "../../geometry3d/AngleSweep"; +import { Sphere } from "../../solid/Sphere"; +import { Angle } from "../../geometry3d/Angle"; +import { Range3d } from "../../geometry3d/Range"; +import { Plane3dByOriginAndVectors } from "../../geometry3d/Plane3dByOriginAndVectors"; +import { IModelJson } from "../../serialization/IModelJsonSchema"; +import { prettyPrint } from "../testFunctions"; +import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; +import { RotationalSweep } from "../../solid/RotationalSweep"; +import { Path } from "../../curve/Path"; +import { Ray3d } from "../../geometry3d/Ray3d"; +import { TorusPipe } from "../../solid/TorusPipe"; +import { SweepContour } from "../../solid/SweepContour"; +import { LineString3d } from "../../curve/LineString3d"; +import { ConstructCurveBetweenCurves } from "../../curve/ConstructCurveBetweenCurves"; +import { Arc3d } from "../../curve/Arc3d"; +import { BSplineCurve3d } from "../../bspline/BSplineCurve"; +import { RuledSweep } from "../../solid/RuledSweep"; /* tslint:disable:no-console */ let outputFolderPath = "./src/test/output"; @@ -141,13 +141,13 @@ describe("Solids", () => { Cone.createAxisPoints(Point3d.create(0, 0, 0), Point3d.create(0, 0, 1), 1, 0, false)!, Cone.createAxisPoints(Point3d.create(1, 3, 2), Point3d.create(3, 9, 2), 4, 2, false)!]) { for (const u of [0.0, 0.25, 1.0]) { - const plane0 = cone.UVFractionToPointAndTangents(u, 0.0); - const plane1 = cone.UVFractionToPointAndTangents(u, 1.0); + const plane0 = cone.uvFractionToPointAndTangents(u, 0.0); + const plane1 = cone.uvFractionToPointAndTangents(u, 1.0); const vector01 = Vector3d.createStartEnd(plane0.origin, plane1.origin); for (const v of [0.0, 0.40, 0.80]) { - const pointV = cone.UVFractionToPoint(u, v); + const pointV = cone.uvFractionToPoint(u, v); ck.testPoint3d(pointV, plane0.origin.interpolate(v, plane1.origin)); - const planeV = cone.UVFractionToPointAndTangents(u, v); + const planeV = cone.uvFractionToPointAndTangents(u, v); ck.testVector3d(vector01, planeV.vectorV, "V derivative is side stroke"); ck.testVector3d(planeV.vectorU, plane0.vectorU.interpolate(v, plane1.vectorU), "U derivative interpolates"); } @@ -187,15 +187,15 @@ describe("Solids", () => { for (const s of spheres) { const r = s.trueSphereRadius(); if (r !== undefined) { - ck.testCoordinate(r, s.cloneVectorX().magnitude()); - ck.testCoordinate(r, s.cloneVectorY().magnitude()); - ck.testCoordinate(r, s.cloneVectorZ().magnitude()); + ck.testCoordinate(r, s.cloneVectorX().magnitude(), s); + ck.testCoordinate(r, s.cloneVectorY().magnitude(), s); + ck.testCoordinate(r, s.cloneVectorZ().magnitude(), s); } const sectionA = s.strokeConstantVSection(0.25, undefined); const sectionB = s.strokeConstantVSection(0.25, 32); const options = StrokeOptions.createForCurves(); options.angleTol = Angle.createDegrees(360 / 12); - const sectionC = s.strokeConstantVSection(0.25, options); + const sectionC = s.strokeConstantVSection(0.25, undefined, options); ck.testExactNumber(sectionA.numPoints(), 17); ck.testExactNumber(sectionB.numPoints(), 33, "explicit stroke count"); ck.testExactNumber(sectionC.numPoints(), 13, "stroke count by angle"); @@ -389,13 +389,13 @@ describe("CurveCurve", () => { const points = [Point3d.create(0, 0, 0), Point3d.create(1, 1, 0), Point3d.create(3, 1, 0), Point3d.create(3, 0, 0)]; const bcurve = BSplineCurve3d.createUniformKnots(points, 3)!; const linestring = LineString3d.create(points); - ck.testUndefined(ConstructCurveBetweenCurves.InterpolateBetween(segment, 0.5, arc)); - ck.testUndefined(ConstructCurveBetweenCurves.InterpolateBetween(segment, 0.5, linestring)); - ck.testUndefined(ConstructCurveBetweenCurves.InterpolateBetween(segment, 0.5, bcurve)); - ck.testUndefined(ConstructCurveBetweenCurves.InterpolateBetween(arc, 0.5, linestring)); - ck.testUndefined(ConstructCurveBetweenCurves.InterpolateBetween(linestring, 0.5, arc)); - ck.testUndefined(ConstructCurveBetweenCurves.InterpolateBetween(arc, 0.5, bcurve)); - ck.testUndefined(ConstructCurveBetweenCurves.InterpolateBetween(bcurve, 0.5, segment)); + ck.testUndefined(ConstructCurveBetweenCurves.interpolateBetween(segment, 0.5, arc)); + ck.testUndefined(ConstructCurveBetweenCurves.interpolateBetween(segment, 0.5, linestring)); + ck.testUndefined(ConstructCurveBetweenCurves.interpolateBetween(segment, 0.5, bcurve)); + ck.testUndefined(ConstructCurveBetweenCurves.interpolateBetween(arc, 0.5, linestring)); + ck.testUndefined(ConstructCurveBetweenCurves.interpolateBetween(linestring, 0.5, arc)); + ck.testUndefined(ConstructCurveBetweenCurves.interpolateBetween(arc, 0.5, bcurve)); + ck.testUndefined(ConstructCurveBetweenCurves.interpolateBetween(bcurve, 0.5, segment)); expect(ck.getNumErrors()).equals(0); }); }); diff --git a/core/geometry/src/test/Graph.test.ts b/core/geometry/src/test/topology/Graph.test.ts similarity index 64% rename from core/geometry/src/test/Graph.test.ts rename to core/geometry/src/test/topology/Graph.test.ts index 0dcb180..750d9c6 100644 --- a/core/geometry/src/test/Graph.test.ts +++ b/core/geometry/src/test/topology/Graph.test.ts @@ -6,32 +6,39 @@ /* tslint:disable: no-console */ import { expect } from "chai"; -import { Checker } from "./Checker"; -import { LineString3d } from "../curve/LineString3d"; -import { LineSegment3d } from "../curve/LineSegment3d"; -import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d"; -import { Range3d } from "../geometry3d/Range"; -import { Transform } from "../geometry3d/Transform"; -import { Matrix3d } from "../geometry3d/Matrix3d"; -import { PolyfaceBuilder } from "../polyface/PolyfaceBuilder"; -import { HalfEdge, HalfEdgeGraph, HalfEdgeMask } from "../topology/Graph"; -import { HalfEdgeGraphSearch } from "../topology/HalfEdgeGraphSearch"; -import { HalfEdgeMaskValidation, HalfEdgePointerInspector } from "../topology/HalfEdgeGraphValidation"; -import { Merger, GraphMerge } from "../topology/Merging"; -import { Triangulator } from "../topology/Triangulation"; - -import { Angle } from "../geometry3d/Angle"; -import { Sample } from "../serialization/GeometrySamples"; -import { GeometryCoreTestIO } from "./GeometryCoreTestIO"; -import { Loop } from "../curve/Loop"; -import { GeometryQuery } from "../curve/GeometryQuery"; +import { Checker } from "../Checker"; +import { LineString3d } from "../../curve/LineString3d"; +import { LineSegment3d } from "../../curve/LineSegment3d"; +import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d"; +import { Range3d } from "../../geometry3d/Range"; +import { Transform } from "../../geometry3d/Transform"; +import { Matrix3d } from "../../geometry3d/Matrix3d"; +import { PolyfaceBuilder } from "../../polyface/PolyfaceBuilder"; +import { HalfEdge, HalfEdgeGraph, HalfEdgeMask } from "../../topology/Graph"; +import { HalfEdgeGraphSearch } from "../../topology/HalfEdgeGraphSearch"; +import { HalfEdgeMaskValidation, HalfEdgePointerInspector } from "../../topology/HalfEdgeGraphValidation"; +import { Merger, GraphMerge } from "../../topology/Merging"; +import { Triangulator } from "../../topology/Triangulation"; + +import { Angle } from "../../geometry3d/Angle"; +import { Sample } from "../../serialization/GeometrySamples"; +import { GeometryCoreTestIO } from "../GeometryCoreTestIO"; +import { Loop } from "../../curve/Loop"; +import { GeometryQuery } from "../../curve/GeometryQuery"; +import { Geometry } from "../../Geometry"; +import { AngleSweep } from "../../geometry3d/AngleSweep"; function exportGraph(graph: HalfEdgeGraph, filename: string) { const toExport = PolyfaceBuilder.graphToPolyface(graph); GeometryCoreTestIO.saveGeometry([toExport], "Graph", filename); } -function exportAnnotatedGraph(graph: HalfEdgeGraph, filename: string) { +function rotateArray(data: Point3d[], index0: number) { + const out = []; + for (let i = 0; i < data.length; i++) out.push(data[(index0 + i) % data.length].clone()); + return out; +} +function captureAnnotatedGraph(data: GeometryQuery[], graph: HalfEdgeGraph, dx: number = 0, dy: number = 0) { const maxTick = 0.01; const numTick = 20; const allNodes = graph.allHalfEdges; @@ -42,7 +49,8 @@ function exportAnnotatedGraph(graph: HalfEdgeGraph, filename: string) { const perpAB = Vector3d.create(); const perpAC = Vector3d.create(); const vectorAC = Vector3d.create(); - const data = []; + const count0 = data.length; + for (const nodeA of allNodes) { const nodeB = nodeA.faceSuccessor; const nodeC = nodeA.vertexSuccessor; @@ -90,7 +98,9 @@ function exportAnnotatedGraph(graph: HalfEdgeGraph, filename: string) { } } } - GeometryCoreTestIO.saveGeometry(data, "Graph", filename); + const transform = Transform.createTranslationXYZ(dx, dy, 0); + for (let i = count0; i < data.length; i++) + data[i].tryTransformInPlace(transform); } const printToConsole = false; function dumpGraph(graph: HalfEdgeGraph) { @@ -412,7 +422,8 @@ describe("VUGraph", () => { verifyGraphCounts(ck, graph, numEdge, 2 * numEdge, undefined); ck.testExactNumber(2 * numEdge, graph.countNodes(), "dangling nodes"); - exportAnnotatedGraph(graph, "VUGrid.1"); + const geometry: GeometryQuery[] = []; + captureAnnotatedGraph(geometry, graph, 0, 0); GraphMerge.clusterAndMergeXYTheta(graph); // after merge, there are interior faces and a single exterior face . . @@ -420,10 +431,10 @@ describe("VUGraph", () => { const numFaces = numInteriorFaces + 1; verifyGraphCounts(ck, graph, numFaces, (numX + 1) * (numY + 1) - 1, undefined); dumpGraph(graph); - exportAnnotatedGraph(graph, "VUGrid.2"); + captureAnnotatedGraph(geometry, graph, 0, 10); const segments = graph.collectSegments(); ck.testExactNumber(numEdge, segments.length, "segmentCount"); - + GeometryCoreTestIO.saveGeometry(geometry, "Graph", "GridFixup"); const componentsB = HalfEdgeGraphSearch.collectConnectedComponents(graph, HalfEdgeMask.EXTERIOR); ck.testTrue(HalfEdgeMaskValidation.isMaskConsistentAroundAllFaces(graph, HalfEdgeMask.EXTERIOR), "ParitySearch makes valid exterior Masks"); if (ck.testExactNumber(1, componentsB.length, "Expect single component")) { @@ -470,24 +481,119 @@ describe("VUGraph", () => { }); it("LargeCountTriangulation", () => { - const numRecursion = 2; const baseVectorA = Vector3d.create(0, 0, 0); const allGeometry = []; - for (const perpendicularFactor of [1.0, -1.0, -0.5]) { + // REMARK + // EDL Feb 20 2019 + // Triangulation introduces a search zheap (not understood by me at this time) for very large polygons. + // With original trigger of 80 edges, some invalid triangulations occur for numRecursion = 2 (the original limit) + // Raise the trigger to 200 and all is fine. + // But the statement coverage drops significantly -- 94% to 93.37 + // numRecursion = 3 generates larger polygons (around 400) and again there are some failures. + // so we conclude the zheap is large chunk of code with some bugs. + // This is an unlikely use case at this time. So + // 1) the heap trigger is left at 200 (see Triangulation.ts) + // 2) add a method `Triangulation.setAndReturnHeapTrigger (number): number` + // Someday debug that .... + for (const numRecursion of [1, 2, 3]) { + for (const perpendicularFactor of [0.85, -1.0, -0.5]) { + let yMax = 0.0; + const baseVectorB = baseVectorA.clone(); + for (const generatorFunction of [ + Sample.createFractalSquareReversingPattern, + Sample.createFractalDiamonConvexPattern, + Sample.createFractalLReversingPatterh, + Sample.createFractalHatReversingPattern, + Sample.createFractalLMildConcavePatter]) { + const points = generatorFunction(numRecursion, perpendicularFactor); + const range = Range3d.createArray(points); + const dy = range.yLength(); + yMax = Math.max(yMax, dy); + const transform = Transform.createTranslation(baseVectorB); + transform.multiplyPoint3dArray(points, points); + baseVectorB.addInPlace(Vector3d.create(2 * range.xLength(), 0, 0)); + allGeometry.push(Loop.create(LineString3d.create(points))); + const graph = Triangulator.earcutSingleLoop(points); + if (graph) { + const pfA = PolyfaceBuilder.graphToPolyface(graph); + pfA.tryTranslateInPlace(0, 2.0 * dy, 0); + allGeometry.push(pfA); + Triangulator.cleanupTriangulation(graph); + const pfB = PolyfaceBuilder.graphToPolyface(graph); + pfB.tryTranslateInPlace(0, 4.0 * dy, 0); + allGeometry.push(pfB); + } + } + baseVectorA.addInPlace(Vector3d.create(0, 8.0 * yMax, 0)); + } + baseVectorA.x += 100; + baseVectorA.y = 0.0; + } + + GeometryCoreTestIO.saveGeometry(allGeometry, "Graph", "LargeCountTriangulation"); + }); + /* These cases had problems -- but maybe only due to bad input? + it.only("ProblemTriangulation", () => { + Triangulator.setAndReturnHeapTrigger(80); + const baseVectorA = Vector3d.create(0, 0, 0); + const allGeometry = []; + for (let maxCut = 50; maxCut < 91; maxCut++) { + const numRecursion = 2; + const perpendicularFactor = 0.8; + let yMax = 0.0; + let xStep = 0.0; + const baseVectorB = baseVectorA.clone(); + for (const generatorFunction of [ + Sample.createFractalLMildConcavePatter, + ]) { + const points = generatorFunction(numRecursion, perpendicularFactor); + const range = Range3d.createArray(points); + const dy = range.yLength(); + yMax = Math.max(yMax, dy); + xStep += 2.0 * range.xLength(); + const transform = Transform.createTranslation(baseVectorB); + transform.multiplyPoint3dArray(points, points); + baseVectorB.addInPlace(Vector3d.create(2 * range.xLength(), 0, 0)); + allGeometry.push(Loop.create(LineString3d.create(points))); + const graph = Triangulator.earcutSingleLoop(points); + if (graph) { + const pfA = PolyfaceBuilder.graphToPolyface(graph); + pfA.tryTranslateInPlace(0, 2.0 * dy, 0); + allGeometry.push(pfA); + Triangulator.cleanupTriangulation(graph); + const pfB = PolyfaceBuilder.graphToPolyface(graph); + pfB.tryTranslateInPlace(0, 4.0 * dy, 0); + allGeometry.push(pfB); + } + } + baseVectorA.x += 2.0 * xStep; + } + Triangulator.setAndReturnHeapTrigger(undefined); + GeometryCoreTestIO.saveGeometry(allGeometry, "Graph", "ProblemTriangulation"); + }); + it("ProblemTriangulationB", () => { + Triangulator.setAndReturnHeapTrigger(5); + + const baseVectorA = Vector3d.create(0, 0, 0); + const allGeometry = []; let yMax = 0.0; - const baseVectorB = baseVectorA.clone(); - for (const generatorFunction of [ - Sample.createFractalDiamonConvexPattern, - Sample.createFractalSquareReversingPattern, - Sample.createFractalLReversingPatterh, - Sample.createFractalLMildConcavePatter]) { - const points = generatorFunction(numRecursion, perpendicularFactor); + let xStep = 0.0; + const basePoints = [ + Point3d.create(0.250, -0.100, 0.000), + Point3d.create(0.750, -0.100, 0.000), + Point3d.create(1.000, 1.000, 0.000), + Point3d.create(1.350, 1.150, 0.000), + Point3d.create(2.000, 2.000, 0.000), + Point3d.create(2.100, 2.250, 0.000), + Point3d.create(2.100, 2.750, 0.000)]; + for (const rotateIndex of [0, 1, 2, 3, 4, 5, 6]) { + const points = rotateArray(basePoints, rotateIndex); const range = Range3d.createArray(points); const dy = range.yLength(); yMax = Math.max(yMax, dy); - const transform = Transform.createTranslation(baseVectorB); + xStep = 2.0 * range.xLength(); + const transform = Transform.createTranslation(baseVectorA); transform.multiplyPoint3dArray(points, points); - baseVectorB.addInPlace(Vector3d.create(2 * range.xLength(), 0, 0)); allGeometry.push(Loop.create(LineString3d.create(points))); const graph = Triangulator.earcutSingleLoop(points); if (graph) { @@ -499,10 +605,94 @@ describe("VUGraph", () => { pfB.tryTranslateInPlace(0, 4.0 * dy, 0); allGeometry.push(pfB); } + baseVectorA.x += xStep; + } + // get back to base heap trigger ... + Triangulator.setAndReturnHeapTrigger(undefined); + + GeometryCoreTestIO.saveGeometry(allGeometry, "Graph", "ProblemTriangulationB"); + }); + */ + it("TriangulationWithColinearVertices", () => { + const numTheta = 5; + const numThetaSkip = 1; + const allGeometry = []; + const r = 1.0; + let x0 = 0.0; + const dy = 2.0; + for (const numColinear of [1, 3, 7]) { + const points = [Point3d.create(-r, 0, 0)]; + for (let i = 0; i <= numColinear; i++) + points.push(Point3d.create(Geometry.interpolate(-r, i / numColinear, r), 0, 0)); + for (let i = 1; i < numTheta; i++) { + const theta = Angle.createDegrees(i * 180 / numTheta); + points.push(Point3d.create(r * theta.cos(), r * theta.sin(), 0)); + } + // run the triangulator with the array rotated to each x-axis point, and one of every numThetaSkip points around the arc. + let y0 = 0.0; + for (let rotation = 0; rotation < points.length; rotation += (rotation < numColinear ? 1 : numThetaSkip)) { + const pointsB = rotateArray(points, rotation); + const graph = Triangulator.earcutSingleLoop(pointsB); + if (graph) { + const pfA = PolyfaceBuilder.graphToPolyface(graph); + pfA.tryTranslateInPlace(x0, y0 + 2.0 * dy, 0); + allGeometry.push(pfA); + Triangulator.cleanupTriangulation(graph); + const pfB = PolyfaceBuilder.graphToPolyface(graph); + pfB.tryTranslateInPlace(x0, y0 + 4.0 * dy, 0); + allGeometry.push(pfB); + y0 += 10.0; + } } - baseVectorA.addInPlace(Vector3d.create(0, 8.0 * yMax, 0)); + x0 += 4.0; } + GeometryCoreTestIO.saveGeometry(allGeometry, "Graph", "TriangulationWithColinearVertices"); + }); + // public static createCutPie(x0: number, y0: number, radius: number, sweep: AngleSweep, numRadialEdges: number, numArcEdges: number, addClosure = false) { + it("PieCuts", () => { - GeometryCoreTestIO.saveGeometry(allGeometry, "Graph", "LargeCountTriangulation"); + const numThetaSkip = 3; + const allGeometry = []; + const r = 1.0; + let x0 = 0.0; + // proimise: all x above x0 is free space. + for (const points of [ + Sample.createCutPie(0, 0, r, AngleSweep.createStartEndDegrees(0, 180), 1, 4, false), + Sample.createCutPie(0, 0, r, AngleSweep.createStartEndDegrees(0, 180), 5, 12, false), + Sample.createCutPie(0, 0, r, AngleSweep.createStartEndDegrees(0, 90), 2, 5, false), + Sample.createCutPie(0, 0, r, AngleSweep.createStartEndDegrees(0, 180), 3, 9, false), + Sample.createCutPie(0, 0, r, AngleSweep.createStartEndDegrees(0, 90), 3, 4, false), + Sample.createCutPie(0, 0, r, AngleSweep.createStartEndDegrees(0, 180), 5, 12, false), + Sample.createCutPie(0, 0, r, AngleSweep.createStartEndDegrees(0, 270), 2, 8, false), + Sample.createCutPie(0, 0, r, AngleSweep.createStartEndDegrees(0, 270), 5, 12, false), + Sample.createCutPie(0, 0, 100 * r, AngleSweep.createStartEndDegrees(0, 180), 5, 12, false), + ]) { + // run the triangulator with the array rotated to each x-axis point, and one of every numThetaSkip points around the arc. + let y0 = 0.0; + const range = Range3d.createArray(points); + const dx = range.xLength(); + const dy = range.yLength(); + const ex = x0 - range.low.x; + x0 += r; + for (let rotation = 0; rotation < points.length; rotation += (rotation < 4 ? 1 : numThetaSkip)) { + const pointsB = rotateArray(points, rotation); + const graph = Triangulator.earcutSingleLoop(pointsB); + const ls = LineString3d.create(points); + ls.tryTranslateInPlace(ex, 0); + allGeometry.push(ls); + if (graph) { + const pfA = PolyfaceBuilder.graphToPolyface(graph); + pfA.tryTranslateInPlace(ex, y0 + 1.5 * dy, 0); + allGeometry.push(pfA); + Triangulator.cleanupTriangulation(graph); + const pfB = PolyfaceBuilder.graphToPolyface(graph); + pfB.tryTranslateInPlace(ex, y0 + 3.0 * dy, 0); + allGeometry.push(pfB); + y0 += 8.0 * dy; + } + } + x0 += 2.0 * dx; + } + GeometryCoreTestIO.saveGeometry(allGeometry, "Graph", "PieCuts"); }); }); diff --git a/core/geometry/src/topology/Graph.ts b/core/geometry/src/topology/Graph.ts index 6c72718..6d33618 100644 --- a/core/geometry/src/topology/Graph.ts +++ b/core/geometry/src/topology/Graph.ts @@ -497,7 +497,7 @@ export class HalfEdge { node = node.vertexSuccessor; } while (node !== this); } - /** Returns the signed sum of a loop of nodes. + /** Returns the signed sum of xy areas of triangls from first node to edges. * * * A positive area is counterclockwise. * * A negative area is clockwise. diff --git a/core/geometry/src/topology/Merging.ts b/core/geometry/src/topology/Merging.ts index 5a56aa9..e22d816 100644 --- a/core/geometry/src/topology/Merging.ts +++ b/core/geometry/src/topology/Merging.ts @@ -34,7 +34,7 @@ export class Merger { // SORTING FUNCTIONS (compare methods used by sort() in larger algorithms) ----------------------------------------------------------- /** Compare function for sorting X, Y, and theta componenets stored in a Point3d, useful for forming a graph from an array of segments */ - private static XYThetaCompare(a: any, b: any) { + private static xyThetaCompare(a: any, b: any) { // Check x's if (!Geometry.isSameCoordinate(a.xyTheta.x, b.xyTheta.x)) if (a.xyTheta.x < b.xyTheta.x) @@ -317,7 +317,7 @@ export class Merger { const arr = Merger.segmentsToXYThetaNode(segments, returnGraph); // Sort lexically - arr.sort(Merger.XYThetaCompare); + arr.sort(Merger.xyThetaCompare); let lastNode = 0; // Connect nodes at vertices @@ -422,12 +422,12 @@ export class Merger { * * !! mark both new half edges visited!!! (This is strange) */ private static join(node0: HalfEdge, node1: HalfEdge, graph: HalfEdgeGraph) { - const alpha = graph.createEdgeXYZXYZ ( - node0.x, node0.y, node0.z, node0.i, - node1.x, node1.y, node1.z, node1.i); + const alpha = graph.createEdgeXYZXYZ( + node0.x, node0.y, node0.z, node0.i, + node1.x, node1.y, node1.z, node1.i); const beta = alpha.edgeMate; - HalfEdge.pinch (node0, alpha); - HalfEdge.pinch (node1, beta); + HalfEdge.pinch(node0, alpha); + HalfEdge.pinch(node1, beta); alpha.setMask(HalfEdgeMask.VISITED); beta.setMask(HalfEdgeMask.VISITED); } diff --git a/core/geometry/src/topology/Triangulation.ts b/core/geometry/src/topology/Triangulation.ts index e2b8ccd..f28eebc 100644 --- a/core/geometry/src/topology/Triangulation.ts +++ b/core/geometry/src/topology/Triangulation.ts @@ -17,7 +17,11 @@ export class Triangulator { // HalfEdgeGraph that is used by many of the private methods inside of the Triangulator class, until being returned at the end of triangulation private static _returnGraph: HalfEdgeGraph; - /** Given the six nodes that make up two bordering triangles, "pinch" and relocate the nodes to flip them */ + /** Given the six nodes that make up two bordering triangles, "pinch" and relocate the nodes to flip them + * The shared edge mates are a and d. + * (abc) are a triangle in CCW order + * (dfe) are a triangle in CCW order. (!! node dfe instead of def.) + */ private static flipTriangles(a: any, b: any, c: any, d: any, e: any, f: any) { // Reassign all of the pointers HalfEdge.pinch(a, e); @@ -48,9 +52,10 @@ export class Triangulator { * * (wx,wy): nodeA to nodeB2 * * this determinant is positive if nodeA is "in the circle" of nodeB2, nodeA1, nodeA2 * @param nodeA node on the diagonal edge of candidate for edge flip. + * @param if true, divide the determinant by the sum of absolute values of the cubic terms of the determinant. * @return the determinant (but undefined if the faces are not triangles as expected.) */ - private static computeInCircleDeterminant(nodeA: HalfEdge): number | undefined { + private static computeInCircleDeterminant(nodeA: HalfEdge, normalize: boolean): number | undefined { const nodeA1 = nodeA.faceSuccessor; const nodeA2 = nodeA1.faceSuccessor; if (nodeA2.faceSuccessor !== nodeA) @@ -64,13 +69,22 @@ export class Triangulator { const uy = nodeA1.y - nodeA.y; const vx = nodeA2.x - nodeA.x; const vy = nodeA2.y - nodeA.y; + if (Geometry.crossProductXYXY(ux, uy, vx, vy) < 0) + return undefined; // we assume identical coordinates in pairs (nodeA, nodeB1) and (nodeA1, nodeB) const wx = nodeB2.x - nodeA.x; const wy = nodeB2.y - nodeA.y; - return Geometry.tripleProduct( - wx, wy, wx * wx + wy * wy, - vx, vy, vx * vx + vy * vy, - ux, uy, ux * ux + uy * uy); + const tx = wx * wx + wy * wy; + const ty = vx * vx + vy * vy; + const tz = ux * ux + uy * uy; + const q = Geometry.tripleProduct( + wx, wy, tx, + vx, vy, ty, + ux, uy, tz); + if (!normalize) return q; + const denom = Math.abs(wx * vy * tz) + Math.abs(wx * ty * ux) + Math.abs(tx * vx * uy) + + Math.abs(wx * ty * uy) + Math.abs(wy * vx * tz) + Math.abs(tx * vy * ux); + return q / denom; // divide by zero? only if collapsed to a point. } /** * * Visit each node of the graph array @@ -95,7 +109,7 @@ export class Triangulator { continue; foundNonVisited = true; - const incircle = Triangulator.computeInCircleDeterminant(node); + const incircle = Triangulator.computeInCircleDeterminant(node, false); if (incircle !== undefined && incircle > 0.0) { // Mark all nodes involved in flip as needing to be buffer (other than alpha and beta node we started with) node.facePredecessor.clearMask(HalfEdgeMask.VISITED); @@ -150,6 +164,30 @@ export class Triangulator { Triangulator.earcutLinked(startingNode); return Triangulator._returnGraph; } + private static _heapTrigger = 200; + /** + * * Triangulation has a trigger value for using a sort heap for earcut searches. + * * Some polygons have had triangulation errors with the heap logic. + * * the default is set at 200 + * * if a user thinks (a) their polygons are safe and (b) they might get performance benefit, they can reset it here. + * * Sending undefined, 0 or negative resets to the default. + * * minimum trigger of 5 is enforced. + * @param trigger new value. + * @returns prior trigger value (for user to reset at end of their triangulation.) + */ + public static setAndReturnHeapTrigger(trigger: number | undefined): number { + const a = this._heapTrigger; + if (trigger === undefined || trigger <= 0) { + this._heapTrigger = 200; + return this._heapTrigger; + } else { + this._heapTrigger = trigger; + // this._heapTrigger = 200; + if (this._heapTrigger < 5) + this._heapTrigger = 5; + } + return a; + } /** * Triangulate the polygon made up of by a series of points. * * To triangulate a polygon with holes, use earcutFromOuterAndInnerLoops @@ -170,7 +208,9 @@ export class Triangulator { let size; // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox - if (data.length > 80) { + // EDL Feb 2019 When hashing was on at 80, a very long earcut created invalid triangulation. + // Leave it on for 200 . . .. it will be a problem someday. + if (data.length > Triangulator._heapTrigger) { minX = maxX = data[0].x; minY = maxY = data[0].y; const n = data.length; @@ -187,7 +227,7 @@ export class Triangulator { size = Math.max(maxX - minX, maxY - minY); } - Triangulator.earcutLinked(startingNode, minX, minY, size); + Triangulator.earcutLinked(startingNode, minX, minY, size, undefined); return Triangulator._returnGraph; } /** @@ -241,7 +281,7 @@ export class Triangulator { let baseNode: HalfEdge | undefined; const xyz = Point3d.create(); for (i = 0; i < data.length; i++) { - data.atPoint3dIndex(i, xyz); + data.getPoint3dAtCheckedPointIndex(i, xyz); baseNode = Triangulator.interiorEdgeSplit(graph, baseNode, xyz); } return baseNode; @@ -348,6 +388,62 @@ export class Triangulator { HalfEdge.pinch(ear.facePredecessor, alpha); ear.setMaskAroundFace(HalfEdgeMask.TRIANGULATED_NODE_MASK); } + private static isInteriorTriangle(a: HalfEdge) { + if (!a.isMaskSet(HalfEdgeMask.TRIANGULATED_NODE_MASK)) + return false; + const b = a.faceSuccessor; + if (!b.isMaskSet(HalfEdgeMask.TRIANGULATED_NODE_MASK)) + return false; + const c = b.faceSuccessor; + if (!c.isMaskSet(HalfEdgeMask.TRIANGULATED_NODE_MASK)) + return false; + return c.faceSuccessor === a; + } + + /** + * Perform 0, 1, or more edge flips to improve aspect ratio just behind an that was just cut. + * @param ear the triangle corner which just served as the ear node. + * @returns the node at the back corner after flipping."appropriately positioned" node for the usual advance to ear.faceSuccessor.edgeMate.faceSuccessor. + */ + private static doPostCutFlips(ear: HalfEdge) { + // B is the ear -- inside a (probably newly created) triangle ABC + // CA is the recently added cut edge. + // AB is the candidate to be flipped. + // triangle B1 A1 D is on the other side of AB + // The condition for flipping is: + // ! both triangles must be TRIANGULATED_NODE_MASK + // ! incircle condition flags D as in the circle of ABC + // after flip, node A moves to the vertex of D, and is the effective "ear", with the cap edge C A1 + // after flip, consider the A1 D (whose nodes are A1 and flipped A!!!) + // + // + // . C0| + // . | + // . | + // . ^| + // . A0 ----> B0| + // *=======================* + // \ A1 <---- B1/ + // \ / + // \ / + // \ D1 / + // * + let b0 = ear; + let a0 = b0.facePredecessor; + let b1 = a0.edgeMate; + while (Triangulator.isInteriorTriangle(a0) && Triangulator.isInteriorTriangle(b1)) { + const detA = Triangulator.computeInCircleDeterminant(a0, true); + if (detA === undefined || detA < 1.0e-10) + break; + // Flip the triangles + const a1 = b1.faceSuccessor; + Triangulator.flipTriangles(a1, a1.faceSuccessor, a1.facePredecessor, b0, b0.facePredecessor, b0.faceSuccessor); + b0 = a0; + a0 = b0.facePredecessor; + b1 = a0.edgeMate; + } + return b0; + } /** * main ear slicing loop which triangulates a polygon (given as a linked list) @@ -363,35 +459,38 @@ export class Triangulator { // interlink polygon nodes in z-order if (!pass && size) Triangulator.indexCurve(ear, minX, minY, size); - let stop = ear; let next; - + let numFail = 0; + let maxFail = (ear as HalfEdge).countEdgesAroundFace(); // iterate through ears, slicing them one by one while (!ear.isMaskSet(HalfEdgeMask.TRIANGULATED_NODE_MASK)) { next = ear.faceSuccessor; if (size ? Triangulator.isEarHashed(ear, minX, minY, size) : Triangulator.isEar(ear)) { // skipping the next vertice leads to less sliver triangles - stop = next.faceSuccessor; // If we already have a seperated triangle, do not join if (ear.faceSuccessor.faceSuccessor !== ear.facePredecessor) { Triangulator.joinNeighborsOfEar(ear); - ear = ear.faceSuccessor.edgeMate.faceSuccessor.faceSuccessor; + ear = Triangulator.doPostCutFlips(ear); + ear = ear.faceSuccessor.edgeMate.faceSuccessor; + // another step? Nate's 2017 code went one more. } else { ear.setMask(HalfEdgeMask.TRIANGULATED_NODE_MASK); ear.faceSuccessor.setMask(HalfEdgeMask.TRIANGULATED_NODE_MASK); ear.facePredecessor.setMask(HalfEdgeMask.TRIANGULATED_NODE_MASK); ear = next.faceSuccessor; } - + numFail = 0; + maxFail--; continue; } - + numFail++; ear = next; // if we looped through the whole remaining polygon and can't find any more ears - if (ear === stop) { + if (numFail >= maxFail) { + numFail = 0; // try filtering points and slicing again // if (!pass) { // Triangulator.earcutLinked(Triangulator.filterPoints(ear), minX, minY, size, 1); diff --git a/core/i18n/CHANGELOG.json b/core/i18n/CHANGELOG.json index 5617b3f..9150b8e 100644 --- a/core/i18n/CHANGELOG.json +++ b/core/i18n/CHANGELOG.json @@ -1,6 +1,48 @@ { "name": "@bentley/imodeljs-i18n", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/imodeljs-i18n_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "Changes package.json to include api-extractor and adds api-extractor.json" + }, + { + "comment": "Use new buildIModelJsBuild script" + }, + { + "comment": "Fixed type definitions" + }, + { + "comment": "Remove uneeded typedoc plugin depedency" + }, + { + "comment": "fix imports of i18next-xhr-backend" + }, + { + "comment": "Documentation improvements" + }, + { + "comment": "Save BUILD_SEMVER to globally accessible map" + }, + { + "comment": "Fix a broken build by upgrading 'i18next-xhr-backend' dependency to v2" + }, + { + "comment": "upgrade to TypeScript 3.2.2" + } + ] + } + }, + { + "version": "0.188.0", + "tag": "@bentley/imodeljs-i18n_v0.188.0", + "date": "Wed, 16 Jan 2019 16:36:09 GMT", + "comments": {} + }, { "version": "0.187.0", "tag": "@bentley/imodeljs-i18n_v0.187.0", diff --git a/core/i18n/CHANGELOG.md b/core/i18n/CHANGELOG.md index fe83492..2ef7377 100644 --- a/core/i18n/CHANGELOG.md +++ b/core/i18n/CHANGELOG.md @@ -1,6 +1,26 @@ # Change Log - @bentley/imodeljs-i18n -This log was last generated on Tue, 15 Jan 2019 15:18:59 GMT and should not be manually modified. +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- Changes package.json to include api-extractor and adds api-extractor.json +- Use new buildIModelJsBuild script +- Fixed type definitions +- Remove uneeded typedoc plugin depedency +- fix imports of i18next-xhr-backend +- Documentation improvements +- Save BUILD_SEMVER to globally accessible map +- Fix a broken build by upgrading 'i18next-xhr-backend' dependency to v2 +- upgrade to TypeScript 3.2.2 + +## 0.188.0 +Wed, 16 Jan 2019 16:36:09 GMT + +*Version update only* ## 0.187.0 Tue, 15 Jan 2019 15:18:59 GMT diff --git a/core/i18n/package.json b/core/i18n/package.json index e6027fe..d5b9050 100644 --- a/core/i18n/package.json +++ b/core/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/imodeljs-i18n", - "version": "0.187.0", + "version": "0.189.0", "description": "iModel.js localization code", "license": "MIT", "repository": { @@ -10,14 +10,23 @@ "main": "lib/imodeljs-i18n", "typings": "lib/imodeljs-i18n", "scripts": { - "build": "tsc 1>&2 && npm run webpackModule-dev", + "build": "node ./node_modules/@bentley/webpack-tools/bin/buildIModelJsModule.js", "clean": "rimraf lib package-deps.json", "docs": "node ./node_modules/@bentley/build-tools/scripts/docs.js --source=./src --includes=../../generated-docs/extract --json=../../generated-docs/core/imodeljs-i18n/file.json --tsIndexFile=./imodeljs-i18n.ts --onlyJson %TYPEDOC_THEME%", + "extract-api": "node ./node_modules/@bentley/build-tools/scripts/extract-api.js --entry=imodeljs-i18n", "lint": "tslint --project . 1>&2", "test": "", - "cover": "", - "webpackModule-dev": "make-dir ./lib/module/dev/ && webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/imodeljs-i18n.js --env.bundlename=imodeljs-i18n --json >./lib/module/dev/webpackStats.json", - "webpackModule-prod": "webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/imodeljs-i18n.js --env.bundlename=imodeljs-i18n --env.prod" + "cover": "" + }, + "iModelJs": { + "buildModule": { + "type": "system", + "webpack": { + "dest": "./lib/module", + "entry": "./lib/imodeljs-i18n.js", + "bundleName": "imodeljs-i18n" + } + } }, "keywords": [ "Bentley", @@ -29,29 +38,23 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "0.187.0" + "@bentley/bentleyjs-core": "0.189.0" }, "//devDependencies": [ "NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install", "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/build-tools": "0.187.0", - "@bentley/webpack-tools": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/build-tools": "0.189.0", + "@bentley/webpack-tools": "0.189.0", "@types/i18next": "^8.4.2", "@types/i18next-browser-languagedetector": "^2.0.1", - "@types/i18next-xhr-backend": "^1.4.1", - "@types/node": "10.10.3", - "make-dir-cli": "^1.0.0", + "@types/node": "10.12.18", "rimraf": "^2.6.2", - "source-map-loader": "^0.2.3", "tslint": "^5.11.0", "typedoc": "^0.11.1", - "typedoc-plugin-external-module-name": "^1.1.1", - "typescript": "~3.1.0", - "webpack": "^4.20.2", - "webpack-cli": "^3.1.0" + "typescript": "~3.2.2" }, "//dependencies": [ "NOTE: these dependencies are specific to imodeljs-i18n", @@ -61,6 +64,6 @@ "dependencies": { "i18next": "^10.2.2", "i18next-browser-languagedetector": "^2.1.0", - "i18next-xhr-backend": "^1.5.1" + "i18next-xhr-backend": "^2.0.1" } } diff --git a/core/i18n/src/Localization.ts b/core/i18n/src/Localization.ts index 3c6c59c..26d6fd7 100644 --- a/core/i18n/src/Localization.ts +++ b/core/i18n/src/Localization.ts @@ -5,30 +5,38 @@ /** @module AppAdministration */ import * as i18next from "i18next"; -import * as i18nextXHRBackend from "i18next-xhr-backend"; +import XHR, { I18NextXhrBackend } from "i18next-xhr-backend"; import * as i18nextBrowserLanguageDetector from "i18next-browser-languagedetector"; import { BentleyError, Logger } from "@bentley/bentleyjs-core"; export interface I18NOptions { urlTemplate?: string; } -/** Supplies Internationalization services. Internally, this class uses the [i18next](https://www.i18next.com/) package. */ +/** + * Supplies Internationalization services. + * @note + * Internally, this class uses the [i18next](https://www.i18next.com/) package. + * @public + */ export class I18N { private _i18n: i18next.i18n; private _namespaceRegistry: Map = new Map(); - public constructor(nameSpaces: string[], defaultNameSpace: string, options?: I18NOptions, renderFunction?: any) { + /** @internal */ + public constructor(nameSpaces: string[], defaultNameSpace: string, options?: I18NOptions, renderFunction?: i18next.Callback) { this._i18n = i18next.createInstance(); + const backendOptions: I18NextXhrBackend.BackendOptions = { + loadPath: options && options.urlTemplate ? options.urlTemplate : "locales/{{lng}}/{{ns}}.json", + crossDomain: true, + }; + const initOptions: i18next.InitOptions = { interpolation: { escapeValue: true }, fallbackLng: "en", ns: nameSpaces, defaultNS: defaultNameSpace, - backend: { - loadPath: options && options.urlTemplate ? options.urlTemplate : "locales/{{lng}}/{{ns}}.json", - crossDomain: true, - }, + backend: backendOptions, }; // if in a development environment, set to pseudo-localize, otherwise detect from browser. @@ -40,7 +48,7 @@ export class I18N { } // call the changeLanguage method right away, before any calls to I18NNamespace.register. Otherwise, the call doesn't happen until the deferred load of the default namespace - this._i18n.use(i18nextXHRBackend) + this._i18n.use(XHR) .use(BentleyLogger) .init(initOptions, renderFunction) .changeLanguage(isDevelopment ? "en-pseudo" : undefined as any, undefined); @@ -61,16 +69,37 @@ export class I18N { * ``` * @param line The input line, potentially containing %{keys}. * @returns The line with all %{keys} translated + * @public */ public translateKeys(line: string): string { return line.replace(/\%\{(.+?)\}/g, (_match, tag) => this.translate(tag)); } - /** Return the translated value of a key. */ + /** Return the translated value of a key. + * @param key - the key that matches a property in the JSON localization file. + * @note The key includes the namespace, which identifies the particular localization file that contains the property, + * followed by a colon, followed by the property in the JSON file. + * For example: + * ``` ts + * const dataString: string = IModelApp.i18n.translate("iModelJs:BackgroundMap.BingDataAttribution"); + * ``` + * assigns to dataString the string with property BackgroundMap.BingDataAttribution from the iModelJs.json localization file. + * @public + */ public translate(key: string | string[], options?: i18next.TranslationOptions): any { return this._i18n.t(key, options); } + /** @internal */ public loadNamespace(name: string, i18nCallback: any) { this._i18n.loadNamespaces(name, i18nCallback); } + + /** @internal */ public languageList(): string[] { return this._i18n.languages; } - // register a new Namespace. Must be unique in the system. + /** Register a new Namespace. The Namespace name must be unique in the system. + * @param name - the name of the namespace, which is the base name of the JSON file that contains the localization properties. + * @note - The registerNamespace method starts fetching the appropriate version of the JSON localization file from the server, + * based on the current locale. To make sure that fetch is complete before performing translations from this namespace, await + * fulfillment of the readPromise Promise property of the returned I18NNamespace. + * @see [Localization in iModel.js]($docs/learning/frontend/Localization.md) + * @public + */ public registerNamespace(name: string): I18NNamespace { if (this._namespaceRegistry.get(name)) throw new BentleyError(-1, "namespace '" + name + "' is not unique"); @@ -105,6 +134,10 @@ export class I18N { return thisNamespace; } + /** + * Waits for the Promises for all the registered namespaces to be fulfilled. + * @internal + */ public async waitForAllRead(): Promise { const namespacePromises = new Array>(); for (const thisNamespace of this._namespaceRegistry.values()) { @@ -113,13 +146,17 @@ export class I18N { return Promise.all(namespacePromises); } - /** @hidden */ + /** @internal */ public unregisterNamespace(name: string): void { this._namespaceRegistry.delete(name); } } +/** The class that represents a registered I18N Namespace + * @note + * The readFinished member is a Promise that is resolved when the JSON file for the namespace has been retrieved from the server, or rejected if an error occurs. + */ export class I18NNamespace { public constructor(public name: string, public readFinished: Promise) { } } diff --git a/core/i18n/src/imodeljs-i18n.ts b/core/i18n/src/imodeljs-i18n.ts index bef31e3..c3ab307 100644 --- a/core/i18n/src/imodeljs-i18n.ts +++ b/core/i18n/src/imodeljs-i18n.ts @@ -4,3 +4,11 @@ *--------------------------------------------------------------------------------------------*/ export * from "./Localization"; + +// Set the version number so it can be found at runtime. BUILD_SEMVER is replaced at build time by the webpack DefinePlugin. +declare var BUILD_SEMVER: string; +if ((typeof (BUILD_SEMVER) !== "undefined") && (typeof window !== "undefined") && window) { + if (!(window as any).iModelJsVersions) + (window as any).iModelJsVersions = new Map(); + (window as any).iModelJsVersions.set("imodeljs-i18n", BUILD_SEMVER); +} diff --git a/core/logger-config/.npmignore b/core/logger-config/.npmignore new file mode 100644 index 0000000..ab2596f --- /dev/null +++ b/core/logger-config/.npmignore @@ -0,0 +1,5 @@ +# start off ignoring everything, and then add back only the files we want +* +!lib/**/*.d.ts +!lib/**/*.js +!*.md diff --git a/core/logger-config/CHANGELOG.json b/core/logger-config/CHANGELOG.json new file mode 100644 index 0000000..0254860 --- /dev/null +++ b/core/logger-config/CHANGELOG.json @@ -0,0 +1,20 @@ +{ + "name": "@bentley/logger-config", + "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/logger-config_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "This package was created to house logger configuration classes that were moved out of @bentley/bentleyjs-core." + }, + { + "comment": "Add missing peerDependencies to package.json" + } + ] + } + } + ] +} diff --git a/core/logger-config/CHANGELOG.md b/core/logger-config/CHANGELOG.md new file mode 100644 index 0000000..bd91cdb --- /dev/null +++ b/core/logger-config/CHANGELOG.md @@ -0,0 +1,12 @@ +# Change Log - @bentley/logger-config + +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- This package was created to house logger configuration classes that were moved out of @bentley/bentleyjs-core. +- Add missing peerDependencies to package.json + diff --git a/core/logger-config/LICENSE.md b/core/logger-config/LICENSE.md new file mode 100644 index 0000000..a04aa6f --- /dev/null +++ b/core/logger-config/LICENSE.md @@ -0,0 +1,9 @@ +# MIT License + +Copyright © 2019 Bentley Systems, Incorporated. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/core/logger-config/README.md b/core/logger-config/README.md new file mode 100644 index 0000000..49e194e --- /dev/null +++ b/core/logger-config/README.md @@ -0,0 +1,11 @@ +# @bentley/logger-config + +Copyright © 2019 Bentley Systems, Incorporated. All rights reserved. + +## Description + +The __@bentley/logger-config__ package contains that help configure a backend logger. + +## Documentation + +See the [iModel.js](https://www.imodeljs.org) documentation for more information. diff --git a/core/logger-config/package.json b/core/logger-config/package.json new file mode 100644 index 0000000..e3f8eca --- /dev/null +++ b/core/logger-config/package.json @@ -0,0 +1,53 @@ +{ + "name": "@bentley/logger-config", + "version": "0.189.0", + "description": "Logger configuration", + "main": "lib/logger-config.js", + "typings": "lib/logger-config", + "license": "MIT", + "engines": { + "node": ">=10.6.0 <11.0" + }, + "scripts": { + "build": "tsc 1>&2 && node ./node_modules/@bentley/build-tools/scripts/ignoreargs.js 1>&2", + "clean": "rimraf lib package-deps.json", + "cover": "", + "docs": "node ./node_modules/@bentley/build-tools/scripts/docs.js --source=./src --includes=../../generated-docs/extract --json=../../generated-docs/core/logger-config/file.json --tsIndexFile=./logger-config.ts --onlyJson %TYPEDOC_THEME%", + "lint": "tslint --project . 1>&2", + "test": "" + }, + "author": { + "name": "Bentley Systems, Inc.", + "url": "http://www.bentley.com" + }, + "repository": { + "type": "git", + "url": "https://github.com/imodeljs/imodeljs" + }, + "keywords": [ + "Bentley", + "BIM", + "iModel" + ], + "peerDependencies": { + "@bentley/bentleyjs-core": "0.189.0", + "bunyan": "^1.8.12", + "bunyan-seq": "^0.2.0", + "request": "^2.88.0", + "request-promise": "^4.2.0" + }, + "devDependencies": { + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/build-tools": "0.189.0", + "@types/bunyan": "^1.8.4", + "@types/bunyan-seq": "^0.2.0", + "@types/node": "10.12.18", + "bunyan": "^1.8.12", + "bunyan-seq": "^0.2.0", + "rimraf": "^2.6.2", + "tslint": "^5.11.0", + "typedoc": "^0.11.1", + "typescript": "~3.2.2" + }, + "dependencies": {} +} diff --git a/core/bentley/src/BunyanLoggerConfig.ts b/core/logger-config/src/BunyanLoggerConfig.ts similarity index 94% rename from core/bentley/src/BunyanLoggerConfig.ts rename to core/logger-config/src/BunyanLoggerConfig.ts index c397540..c7b9285 100644 --- a/core/bentley/src/BunyanLoggerConfig.ts +++ b/core/logger-config/src/BunyanLoggerConfig.ts @@ -4,10 +4,8 @@ *--------------------------------------------------------------------------------------------*/ /** @module Logging */ -// tslint:disable-next-line:no-var-requires -const bunyan = require("bunyan"); -import { GetMetaDataFunction } from "./BentleyError"; -import { LogFunction, Logger } from "./Logger"; +import * as bunyan from "bunyan"; +import { GetMetaDataFunction, LogFunction, Logger } from "@bentley/bentleyjs-core"; /** Helps to configure the bentleyjs-core Logger to use bunyan. * To use bunyan for logging output, the app should depend on the bunyan package. @@ -18,6 +16,7 @@ import { LogFunction, Logger } from "./Logger"; * You can then pipe the output through the bunyan command-line program to format and filter it. * * See [[SeqLoggerConfig]] to log to a seq server using bunyan. + * @beta */ export class BunyanLoggerConfig { // Generate metadata for a bunyan record. If nothing else, it must contain the message category. @@ -51,5 +50,4 @@ export class BunyanLoggerConfig { const traceLogger: LogFunction = (category: string, message: string, getMetaData?: GetMetaDataFunction): void => blgr.trace(BunyanLoggerConfig.makeBunyanMetaData(category, getMetaData), message); Logger.initialize(errorLogger, warningLogger, infoLogger, traceLogger); } - } diff --git a/core/logger-config/src/FluentdBunyanLoggerConfig.ts b/core/logger-config/src/FluentdBunyanLoggerConfig.ts new file mode 100644 index 0000000..54694e2 --- /dev/null +++ b/core/logger-config/src/FluentdBunyanLoggerConfig.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module Logging */ + +import * as bunyan from "bunyan"; +import { BentleyError, IModelStatus } from "@bentley/bentleyjs-core"; +import { FluentdLoggerStream, IFluentdConfig } from "./FluentdLoggerStream"; + +/** Helps to configure the bentleyjs-core Logger to use fluentd and seq. + * Note: The app must depend on the bunyan, request and request-promise packages. + * @beta + */ +export class FluentdBunyanLoggerConfig { + /** Create a bunyan logger that streams to fluentd + * ``` + * BunyanLoggerConfig.logToBunyan(FluentdBunyanLoggerConfig.createBunyanFluentdLogger(fluentdConfig)); + * ``` + * See [[BunyanLoggerConfig.logToBunyan]] + */ + public static createBunyanFluentdLogger(fluentdConfig: IFluentdConfig, loggerName: string): any { + if (fluentdConfig === undefined) { + fluentdConfig = {}; + } + const params: IFluentdConfig = {}; + params.fluentdHost = (fluentdConfig.fluentdHost || "http://localhost"); + params.fluentdPort = (fluentdConfig.fluentdPort || 9880); + params.fluentdTimeout = (fluentdConfig.fluentdTimeout || 1500); + params.seqServerUrl = (fluentdConfig.seqServerUrl || "http://localhost"); + params.seqServerPort = (fluentdConfig.seqServerPort || 5341); + params.seqApiKey = (fluentdConfig.seqApiKey || "InvalidApiKey"); + + // nb: Define only one bunyan stream! Otherwise, we will get logging messages coming out multiple times, once for each stream. (https://github.com/trentm/node-bunyan/issues/334) + // this one stream must accept messages at all levels. That is why we set it to "trace". That is just its lower limit. + // const tracelevel: any = "trace"; + + return bunyan.createLogger({ + name: loggerName, + streams: [ + { stream: new FluentdLoggerStream(params), level: 10 }, + ], + }); + } + + /** Check that the specified object is a valid SeqConfig. This is useful when reading a config from a .json file. */ + public static validateProps(fluentdConfig: any): void { + const validProps: string[] = ["host", "port"]; + for (const prop of Object.keys(fluentdConfig)) { + if (!validProps.includes(prop)) { + throw new BentleyError(IModelStatus.BadArg, "unrecognized fluentdConfig property: " + prop); + } + } + } +} diff --git a/core/logger-config/src/FluentdLoggerStream.ts b/core/logger-config/src/FluentdLoggerStream.ts new file mode 100644 index 0000000..b70a4be --- /dev/null +++ b/core/logger-config/src/FluentdLoggerStream.ts @@ -0,0 +1,139 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module Logging */ + +import { Writable } from "stream"; +import * as domain from "domain"; +import * as bunyan from "bunyan"; +// tslint:disable-next-line:no-var-requires +const post = require("request-promise"); + +/** @beta */ +export interface GenericPost { + postasync(config: any, jsonbody: any): Promise; +} + +/** fluentd logging server configuration. + * @beta + */ +export interface IFluentdConfig { + /** The URL of the fluentd server to connect to. Defaults to localhost. */ + fluentdHost?: string; + /** The port of the fluentd server. Defaults to 9880. */ + fluentdPort?: number; + /** fluentd server request timeout. Defaults to 1500. */ + fluentdTimeout?: number; + /** The URL of the seq server to send logs. Defaults to localhost. */ + seqServerUrl?: string; + /** The port of the seq server. Defaults to 5341. */ + seqServerPort?: number; + /** The API Key to use when sending logs to Seq */ + seqApiKey?: string; +} + +/** @beta */ +export class PostFluentd implements GenericPost { + private generateOptions(config: IFluentdConfig, jsonbody: any): any { + const customHeaders: any = {}; + customHeaders["content-type"] = "application/json"; + customHeaders["seq-server"] = config.seqServerUrl; + customHeaders["seq-apikey"] = config.seqApiKey; + customHeaders["seq-port"] = config.seqServerPort; + // TODO: Handle SEQ_PORT (on fluentd side as well) and use kabab case instead of snake case. + return { + uri: config.fluentdHost + ":" + config.fluentdPort + "/seqlogging", + body: jsonbody, + headers: JSON.parse(JSON.stringify(customHeaders)), + resolveWithFullResponse: true, + timeout: config.fluentdTimeout, + }; + } + public async postasync(config: any, jsonbody: any): Promise { + const response = await post(this.generateOptions(config, jsonbody)); + return response.statusCode || -1; + } +} + +/** @beta */ +export class FluentdLoggerStream extends Writable { + private _fluentdParams: IFluentdConfig; + constructor(fluentdParams: IFluentdConfig) { + super(); + this._fluentdParams = fluentdParams; + } + + private mapLevelToString(level: any): any { + let response: string; + switch (level) { + case bunyan.TRACE: { + response = "Trace"; + break; + } + case bunyan.DEBUG: { + response = "Debug"; + break; + } + case bunyan.INFO: { + response = "Information"; + break; + } + case bunyan.WARN: { + response = "Warning"; + break; + } + case bunyan.ERROR: { + response = "Error"; + break; + } + case bunyan.FATAL: { + response = "Fatal"; + break; + } + default: { + response = "Information"; + } + } + return response; + } + + // tslint:disable-next-line:naming-convention + public _writev(chunks: Array<{ chunk: any, encoding: string }>, callback: (err?: Error) => void): void { + for (const entry of chunks) { + this._write(entry.chunk, entry.encoding, callback); + } + } + + // tslint:disable-next-line:naming-convention + public _write(chunk: any, encoding: string, callback: (err?: Error) => void): void { + // we create a domain to catch errors from the socket. Major errors like CONNECTION not made is sent to bunyan + const fluentdDomain: domain.Domain = domain.create(); + fluentdDomain.on("error", (errEvent: Error) => { + this.emit("error", new Error(`Fluentd domain error -- , ${errEvent.message}`)); + callback(errEvent); + }); + + fluentdDomain.run(() => { + // generate a valid json as body + let packet: any; + try { + packet = JSON.parse(chunk); + if (packet.hasOwnProperty("level")) { + packet.level = this.mapLevelToString(chunk.level); + } + packet = JSON.stringify(packet); + } catch (error) { + this.emit("error", new Error(`Error: ${error}, Encoding: ${encoding}`)); + packet = JSON.stringify(chunk); + } + + // Post to fluentd -- async + const poster = new PostFluentd(); + Promise.resolve(poster.postasync(this._fluentdParams, packet)) + .then((res) => { if (res === -1 || res !== 200) { throw new Error("invalid response from fluentd"); } }) + .catch((err) => { this.emit("error", new Error(`Fluentd post error -- ${err.message}`)); }); + callback(); + }); + } +} diff --git a/core/bentley/src/SeqLoggerConfig.ts b/core/logger-config/src/SeqLoggerConfig.ts similarity index 92% rename from core/bentley/src/SeqLoggerConfig.ts rename to core/logger-config/src/SeqLoggerConfig.ts index 322f225..29f9af9 100644 --- a/core/bentley/src/SeqLoggerConfig.ts +++ b/core/logger-config/src/SeqLoggerConfig.ts @@ -4,14 +4,13 @@ *--------------------------------------------------------------------------------------------*/ /** @module Logging */ -import { BentleyError, IModelStatus } from "./BentleyError"; +import * as bunyan from "bunyan"; +import * as seq from "bunyan-seq"; +import { BentleyError, IModelStatus } from "@bentley/bentleyjs-core"; -// tslint:disable-next-line:no-var-requires -const seq = require("bunyan-seq"); -// tslint:disable-next-line:no-var-requires -const bunyan = require("bunyan"); - -/** seq logging server configuration. */ +/** seq logging server configuration. + * @beta + */ export interface SeqConfig { /** The URL of the seq server to connect to. Defaults to localhost. */ hostURL?: string; @@ -29,6 +28,7 @@ export interface SeqConfig { /** Helps to configure the bentleyjs-core Logger to use bunyan and seq. * Note: The app must depend on the bunyan and bunyan-seq packages. + * @beta */ export class SeqLoggerConfig { diff --git a/core/logger-config/src/logger-config.ts b/core/logger-config/src/logger-config.ts new file mode 100644 index 0000000..bd21581 --- /dev/null +++ b/core/logger-config/src/logger-config.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +export * from "./BunyanLoggerConfig"; +export * from "./FluentdBunyanLoggerConfig"; +export * from "./FluentdLoggerStream"; +export * from "./SeqLoggerConfig"; diff --git a/core/logger-config/tsconfig.json b/core/logger-config/tsconfig.json new file mode 100644 index 0000000..020cd2b --- /dev/null +++ b/core/logger-config/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "./node_modules/@bentley/build-tools/tsconfig-base.json", + "compilerOptions": { + "target": "es2017", + "module": "commonjs", + "outDir": "./lib" + }, + "include": [ + "./src/**/*.ts" + ], + "exclude": [ + "lib", + "node_modules" + ] +} \ No newline at end of file diff --git a/core/logger-config/tslint.json b/core/logger-config/tslint.json new file mode 100644 index 0000000..66909b8 --- /dev/null +++ b/core/logger-config/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": "@bentley/build-tools/tslint.json" +} \ No newline at end of file diff --git a/core/quantity/CHANGELOG.json b/core/quantity/CHANGELOG.json index 842b1eb..7c17ee3 100644 --- a/core/quantity/CHANGELOG.json +++ b/core/quantity/CHANGELOG.json @@ -1,6 +1,36 @@ { "name": "@bentley/imodeljs-quantity", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/imodeljs-quantity_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "Add API tags to classes, interfaces, and enums as appropriate." + }, + { + "comment": "Changes package.json to include api-extractor and adds api-extractor.json" + }, + { + "comment": "Use new buildIModelJsBuild script" + }, + { + "comment": "Save BUILD_SEMVER to globally accessible map" + }, + { + "comment": "upgrade to TypeScript 3.2.2" + } + ] + } + }, + { + "version": "0.188.0", + "tag": "@bentley/imodeljs-quantity_v0.188.0", + "date": "Wed, 16 Jan 2019 16:36:09 GMT", + "comments": {} + }, { "version": "0.187.0", "tag": "@bentley/imodeljs-quantity_v0.187.0", diff --git a/core/quantity/CHANGELOG.md b/core/quantity/CHANGELOG.md index 6341b88..6f46c57 100644 --- a/core/quantity/CHANGELOG.md +++ b/core/quantity/CHANGELOG.md @@ -1,6 +1,22 @@ # Change Log - @bentley/imodeljs-quantity -This log was last generated on Tue, 15 Jan 2019 15:18:59 GMT and should not be manually modified. +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- Add API tags to classes, interfaces, and enums as appropriate. +- Changes package.json to include api-extractor and adds api-extractor.json +- Use new buildIModelJsBuild script +- Save BUILD_SEMVER to globally accessible map +- upgrade to TypeScript 3.2.2 + +## 0.188.0 +Wed, 16 Jan 2019 16:36:09 GMT + +*Version update only* ## 0.187.0 Tue, 15 Jan 2019 15:18:59 GMT diff --git a/core/quantity/package.json b/core/quantity/package.json index 2536c28..85d4c47 100644 --- a/core/quantity/package.json +++ b/core/quantity/package.json @@ -1,21 +1,30 @@ { "name": "@bentley/imodeljs-quantity", - "version": "0.187.0", + "version": "0.189.0", "description": "Quantity formatting and parsing concepts in typescript", "license": "MIT", "main": "lib/imodeljs-quantity.js", "typings": "lib/imodeljs-quantity", "repository": {}, "scripts": { - "build": "tsc 1>&2 && npm run webpackModule-dev", + "build": "node ./node_modules/@bentley/webpack-tools/bin/buildIModelJsModule.js", "clean": "rimraf lib package-deps.json .nyc_output", + "extract-api": "node ./node_modules/@bentley/build-tools/scripts/extract-api.js --entry=imodeljs-quantity", "lint": "tslint --project . 1>&2", "test": "node ./node_modules/@bentley/build-tools/scripts/test-tsnode.js --testDir=./test/", "docs": "node ./node_modules/@bentley/build-tools/scripts/docs.js --source=./src --includes=../../generated-docs/extract --json=../../generated-docs/core/imodeljs-quantity/file.json --tsIndexFile=./imodeljs-quantity.ts --onlyJson %TYPEDOC_THEME%", "cover": "nyc npm test", - "start": "npm run lint && npm run clean && npm run build && npm run test & npm run cover & npm run docs", - "webpackModule-dev": "make-dir ./lib/module/dev/ && webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/imodeljs-quantity.js --env.bundlename=imodeljs-quantity --json >./lib/module/dev/webpackStats.json", - "webpackModule-prod": "webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/imodeljs-quantity.js --env.bundlename=imodeljs-quantity --env.prod" + "start": "npm run lint && npm run clean && npm run build && npm run test & npm run cover & npm run docs" + }, + "iModelJs": { + "buildModule": { + "type": "system", + "webpack": { + "dest": "./lib/module", + "entry": "./lib/imodeljs-quantity.js", + "bundleName": "imodeljs-quantity" + } + } }, "keywords": [ "Bentley", @@ -27,33 +36,29 @@ "url": "http://www.bentley.com" }, "devDependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/build-tools": "0.187.0", - "@bentley/webpack-tools": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/build-tools": "0.189.0", + "@bentley/webpack-tools": "0.189.0", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/glob": "^5.0.35", "@types/mocha": "^5.2.5", - "@types/node": "10.10.3", + "@types/node": "10.12.18", "@types/sinon": "^5.0.5", "chai": "^4.1.2", "chai-as-promised": "^7", - "make-dir-cli": "^1.0.0", "mocha": "^5.2.0", "nyc": "^13.0.1", "rimraf": "^2.6.2", "sinon": "^7.1.1", - "source-map-loader": "^0.2.3", "ts-node": "^7.0.1", "tslint": "^5.11.0", "typedoc": "^0.11.1", - "typescript": "~3.1.0", - "webpack": "^4.20.2", - "webpack-cli": "^3.1.0" + "typescript": "~3.2.2" }, "dependencies": {}, "peerDependencies": { - "@bentley/bentleyjs-core": "0.187.0" + "@bentley/bentleyjs-core": "0.189.0" }, "nyc": { "nycrc-path": "./node_modules/@bentley/build-tools/.nycrc" diff --git a/core/quantity/src/Constants.ts b/core/quantity/src/Constants.ts index 83245b1..612b57f 100644 --- a/core/quantity/src/Constants.ts +++ b/core/quantity/src/Constants.ts @@ -3,6 +3,9 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ +/** Constants used internally for both formatting and parsing. + * @internal + */ export class QuantityConstants { public static readonly CHAR_COMMA = 44; public static readonly CHAR_SPACE = 32; diff --git a/core/quantity/src/Exception.ts b/core/quantity/src/Exception.ts index f5452d5..d2ae2b1 100644 --- a/core/quantity/src/Exception.ts +++ b/core/quantity/src/Exception.ts @@ -7,6 +7,7 @@ import { BentleyError } from "@bentley/bentleyjs-core"; /** * Status codes used during Quantity parsing and formatting processing. + * @alpha */ export const enum QuantityStatus { QUANTITY_ERROR_BASE = 0x88DF, @@ -15,7 +16,9 @@ export const enum QuantityStatus { InvalidCompositeFormat = QUANTITY_ERROR_BASE + 2, } -/** The error type thrown by this module. See [[QuantityStatus]] for `errorNumber` values. */ +/** The error type thrown by this module. See [[QuantityStatus]] for `errorNumber` values. + * @alpha + */ export class QuantityError extends BentleyError { public constructor(public readonly errorNumber: number, message?: string) { super(errorNumber, message); diff --git a/core/quantity/src/Formatter/Format.ts b/core/quantity/src/Formatter/Format.ts index 081b595..905356c 100644 --- a/core/quantity/src/Formatter/Format.ts +++ b/core/quantity/src/Formatter/Format.ts @@ -13,7 +13,9 @@ import { // cSpell:ignore ZERONORMALIZED, nosign, onlynegative, signalways, negativeparentheses // cSpell:ignore trailzeroes, keepsinglezero, zeroempty, keepdecimalpoint, applyrounding, fractiondash, showunitlabel, prependunitlabel, exponentonlynegative -/** A class used to both define the specifications for formatting a quantity values and the methods to do the formatting. */ +/** A class used to both define the specifications for formatting a quantity values and the methods to do the formatting. + * @alpha + */ export class Format implements FormatProps { private _name = ""; diff --git a/core/quantity/src/Formatter/FormatEnums.ts b/core/quantity/src/Formatter/FormatEnums.ts index e737f71..413ef2f 100644 --- a/core/quantity/src/Formatter/FormatEnums.ts +++ b/core/quantity/src/Formatter/FormatEnums.ts @@ -2,6 +2,7 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ +/** @alpha */ export const enum FormatTraits { TrailZeroes = 0x1, KeepSingleZero = 0x2, @@ -15,6 +16,7 @@ export const enum FormatTraits { ExponentOnlyNegative = 0x200, } +/** @alpha */ export const enum FractionalPrecision { One = 1, Two = 2, @@ -27,6 +29,7 @@ export const enum FractionalPrecision { TwoHundredFiftySix = 256, } +/** @alpha */ export const enum DecimalPrecision { Zero = 0, One = 1, @@ -43,6 +46,7 @@ export const enum DecimalPrecision { Twelve = 12, } +/** @alpha */ export const enum FormatType { Decimal, Fractional, @@ -50,12 +54,14 @@ export const enum FormatType { Station, } -export const enum ScientificType { // required if type is scientific; options: normalized, zeroNormalized +/** @alpha */ +export const enum ScientificType { Normalized, ZeroNormalized, } -export const enum ShowSignOption { // default is no sign +/** @alpha */ +export const enum ShowSignOption { NoSign, OnlyNegative, SignAlways, diff --git a/core/quantity/src/Formatter/Formatter.ts b/core/quantity/src/Formatter/Formatter.ts index 188fb1b..0137a27 100644 --- a/core/quantity/src/Formatter/Formatter.ts +++ b/core/quantity/src/Formatter/Formatter.ts @@ -7,10 +7,14 @@ import { QuantityStatus, QuantityError } from "../Exception"; import { FormatterSpec } from "./Format"; import { FormatType, ScientificType, ShowSignOption, DecimalPrecision, FractionalPrecision, FormatTraits } from "./FormatEnums"; -const FPV_MINTHRESHOLD = 1.0e-14; // format parameter default values -const FPV_ROUNDFACTOR = 0.50000000001; // rounding additive - -/** A private helper class used to format fraction part of value into a numerator and denominator. */ +/** rounding additive + * @internal + */ +const FPV_ROUNDFACTOR = 0.50000000001; + +/** A private helper class used to format fraction part of value into a numerator and denominator. + * @internal + */ class FractionalNumeric { private _integral: number = 0; private _numerator: number = 0; @@ -87,9 +91,14 @@ class FractionalNumeric { } } -/** A helper class that contains methods used to format quantity values based on a format that are defined via the Format class. */ +/** A helper class that contains methods used to format quantity values based on a format that are defined via the Format class. + * @alpha + */ export class Formatter { - private static isNegligible(value: number): boolean { return (Math.abs(value) < FPV_MINTHRESHOLD); } + // tslint:disable-next-line:naming-convention + private static FPV_MINTHRESHOLD = 1.0e-14; + + private static isNegligible(value: number): boolean { return (Math.abs(value) < Formatter.FPV_MINTHRESHOLD); } /** Return floating point value rounded by specific rounding factor. * @param value Value to be rounded. @@ -250,7 +259,7 @@ export class Formatter { let formattedValue = ""; if (isDecimal) { - const actualVal = isPrecisionZero ? posMagnitude + FPV_ROUNDFACTOR : posMagnitude + FPV_MINTHRESHOLD; + const actualVal = isPrecisionZero ? posMagnitude + FPV_ROUNDFACTOR : posMagnitude + Formatter.FPV_MINTHRESHOLD; let wholePart = Math.floor(actualVal); let fractionPart = actualVal - wholePart; if (!isPrecisionZero) { diff --git a/core/quantity/src/Formatter/Interfaces.ts b/core/quantity/src/Formatter/Interfaces.ts index 0f25f84..8979600 100644 --- a/core/quantity/src/Formatter/Interfaces.ts +++ b/core/quantity/src/Formatter/Interfaces.ts @@ -8,7 +8,9 @@ import { import { UnitProps } from "../Interfaces"; -/** This interface defines the properties required to format quantity values. */ +/** This interface defines the properties required to format quantity values. + * @alpha + */ export interface FormatProps { readonly name: string; readonly roundFactor: number; diff --git a/core/quantity/src/Interfaces.ts b/core/quantity/src/Interfaces.ts index b15f334..d0000d6 100644 --- a/core/quantity/src/Interfaces.ts +++ b/core/quantity/src/Interfaces.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ /** This interface provides basic information about a Unit that is return from a UnitProvider. This info * uniquely identifies a unit by its name. + * @alpha */ export interface UnitProps { /** Unique name for unit. */ @@ -17,6 +18,7 @@ export interface UnitProps { } /** This interface defines the required properties of a Quantity. + * @alpha */ export interface QuantityProps { readonly magnitude: number; @@ -26,6 +28,7 @@ export interface QuantityProps { /** This interface defines the properties required to convert a quantity value from one unit to another such as from meters to feet * or from Celsius to Fahrenheit. + * @alpha */ export interface UnitConversion { factor: number; @@ -34,6 +37,7 @@ export interface UnitConversion { /** This interface is implemented by the class that is responsible for locating units by name or label and providing conversion values between units. * The methods to be implemented are async allowing the UnitsProvider to query the backend when necessary to look up unit definition and conversion rules. + * @alpha */ export interface UnitsProvider { findUnit(unitLabel: string, unitFamily?: string): Promise; @@ -42,6 +46,7 @@ export interface UnitsProvider { } /** This class is a convenience class that can be returned when a valid Unit cannot be determined. + * @alpha */ export class BadUnit implements UnitProps { public name = ""; diff --git a/core/quantity/src/Parser.ts b/core/quantity/src/Parser.ts index 18504f0..def159f 100644 --- a/core/quantity/src/Parser.ts +++ b/core/quantity/src/Parser.ts @@ -8,7 +8,9 @@ import { Quantity } from "./Quantity"; import { Format } from "./Formatter/Format"; import { FormatType, FormatTraits } from "./Formatter/FormatEnums"; -/** A ParseToken holds either a numeric or string toke extracted from a string that represents a quantity value. */ +/** A ParseToken holds either a numeric or string token extracted from a string that represents a quantity value. + * @alpha + */ class ParseToken { public value: number | string; @@ -21,6 +23,9 @@ class ParseToken { public get isNumber(): boolean { return typeof this.value === "number"; } } +/** A ScientificToken holds an index and string representing the exponent. + * @alpha + */ class ScientificToken { public index: number; public exponent = ""; @@ -31,6 +36,9 @@ class ScientificToken { } } +/** A FractionToken holds an index and the fraction value of numerator / denominator. + * @alpha + */ class FractionToken { public index: number; public fraction = 0.0; @@ -42,7 +50,9 @@ class FractionToken { } } -/** A Parser class that is used to break a string that represents a quantity value into tokens. */ +/** A Parser class that is used to break a string that represents a quantity value into tokens. + * @alpha + */ export class Parser { private static checkForScientificNotation(index: number, stringToParse: string, uomSeparatorToIgnore: number): ScientificToken { let exponentString = ""; diff --git a/core/quantity/src/Quantity.ts b/core/quantity/src/Quantity.ts index 97272a1..b8f8442 100644 --- a/core/quantity/src/Quantity.ts +++ b/core/quantity/src/Quantity.ts @@ -7,6 +7,7 @@ import { QuantityProps, UnitProps, UnitConversion } from "./Interfaces"; /** The Quantity class is convenient container to specify both the magnitude and unit of a quantity. This class is commonly * returned as the result of parsing a string that represents a quantity. + * @alpha */ export class Quantity implements QuantityProps { protected _magnitude: number = 0.0; diff --git a/core/quantity/src/imodeljs-quantity.ts b/core/quantity/src/imodeljs-quantity.ts index 609a8b2..67d2fde 100644 --- a/core/quantity/src/imodeljs-quantity.ts +++ b/core/quantity/src/imodeljs-quantity.ts @@ -12,3 +12,11 @@ export * from "./Formatter/Format"; export * from "./Formatter/FormatEnums"; export * from "./Formatter/Formatter"; export * from "./Formatter/Interfaces"; + +// Set the version number so it can be found at runtime. BUILD_SEMVER is replaced at build time by the webpack DefinePlugin. +declare var BUILD_SEMVER: string; +if ((typeof (BUILD_SEMVER) !== "undefined") && (typeof window !== "undefined") && window) { + if (!(window as any).iModelJsVersions) + (window as any).iModelJsVersions = new Map(); + (window as any).iModelJsVersions.set("imodeljs-quantity", BUILD_SEMVER); +} diff --git a/core/webserver/CHANGELOG.json b/core/webserver/CHANGELOG.json index 34a0ade..da5a441 100644 --- a/core/webserver/CHANGELOG.json +++ b/core/webserver/CHANGELOG.json @@ -1,6 +1,33 @@ { "name": "@bentley/imodeljs-webserver", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/imodeljs-webserver_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "Changes package.json to include api-extractor and adds api-extractor.json" + }, + { + "comment": "Use new buildIModelJsBuild script" + }, + { + "comment": "Remove uneeded typedoc plugin depedency" + }, + { + "comment": "upgrade to TypeScript 3.2.2" + } + ] + } + }, + { + "version": "0.188.0", + "tag": "@bentley/imodeljs-webserver_v0.188.0", + "date": "Wed, 16 Jan 2019 16:36:09 GMT", + "comments": {} + }, { "version": "0.187.0", "tag": "@bentley/imodeljs-webserver_v0.187.0", diff --git a/core/webserver/CHANGELOG.md b/core/webserver/CHANGELOG.md index aa213c2..3098cbf 100644 --- a/core/webserver/CHANGELOG.md +++ b/core/webserver/CHANGELOG.md @@ -1,6 +1,21 @@ # Change Log - @bentley/imodeljs-webserver -This log was last generated on Tue, 15 Jan 2019 15:18:59 GMT and should not be manually modified. +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- Changes package.json to include api-extractor and adds api-extractor.json +- Use new buildIModelJsBuild script +- Remove uneeded typedoc plugin depedency +- upgrade to TypeScript 3.2.2 + +## 0.188.0 +Wed, 16 Jan 2019 16:36:09 GMT + +*Version update only* ## 0.187.0 Tue, 15 Jan 2019 15:18:59 GMT diff --git a/core/webserver/package.json b/core/webserver/package.json index 3a1dcc5..bce86f3 100644 --- a/core/webserver/package.json +++ b/core/webserver/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/imodeljs-webserver", - "version": "0.187.0", + "version": "0.189.0", "description": "Simple Express-based web server for iModel.js static files", "license": "MIT", "repository": { @@ -10,7 +10,7 @@ "main": "lib/WebServer", "typings": "lib/WebServer", "scripts": { - "build": "tsc 1>&2", + "build": "tsc 1>&2 && node ./node_modules/@bentley/build-tools/scripts/ignoreargs.js 1>&2", "clean": "rimraf lib package-deps.json", "docs": "node ./node_modules/@bentley/build-tools/scripts/docs.js --source=./src --includes=../../generated-docs/extract --json=../../generated-docs/core/webserver/file.json --tsIndexFile=./WebServer.ts --onlyJson %TYPEDOC_THEME%", "lint": "tslint --project . 1>&2", @@ -32,16 +32,15 @@ "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/build-tools": "0.187.0", + "@bentley/build-tools": "0.189.0", "@bentley/dev-cors-proxy-server": "0.0.9", - "@types/express": "^4.11.1", - "@types/node": "10.10.3", + "@types/express": "^4.16.1", + "@types/node": "10.12.18", "@types/yargs": "^12.0.5", "rimraf": "^2.6.2", "tslint": "^5.11.0", "typedoc": "^0.11.1", - "typedoc-plugin-external-module-name": "^1.1.1", - "typescript": "~3.1.0" + "typescript": "~3.2.2" }, "//dependencies": [ "NOTE: these dependencies are specific to imodeljs-i18n", diff --git a/core/webserver/src/WebServer.ts b/core/webserver/src/WebServer.ts index 19c6dde..1abb5d5 100644 --- a/core/webserver/src/WebServer.ts +++ b/core/webserver/src/WebServer.ts @@ -23,13 +23,13 @@ function getArgs(): yargs.Arguments { .usage("$0 ") .wrap(yargs.terminalWidth()) .option("port", { - alias: ["p", "port"], + alias: "p", description: "Web Server Port", default: 3000, type: "number", }) .option("resources", { - alias: ["r", "resources"], + alias: "r", description: "Path to resource root directory", demandOption: true, type: "string", diff --git a/docs/core/bis/intro/bis-organization.md b/docs/core/bis/intro/bis-organization.md index fe8b2b9..73b0a8b 100644 --- a/docs/core/bis/intro/bis-organization.md +++ b/docs/core/bis/intro/bis-organization.md @@ -40,7 +40,7 @@ The top layer is for the App schemas. These schemas are intended to be very smal ## BIS Compatibility Grades for Schemas -The conversion of products to use BIS Domain Schemas can occur incrementally, but an ecosystem of BIS-based infrastructure (including iModelHub and Navigator) is rapidly expanding. This creates a short-term need for BIS-based “compatibility” schemas that have not been as rigorously designed as true BIS schemas but allow usage and some level of interoperability with the BIS ecosystem. For this reason, a grading level for BIS schemas has been created: +The conversion of products to use BIS Domain Schemas can occur incrementally, but an ecosystem of BIS-based infrastructure (including iModelHub and Design Review) is rapidly expanding. This creates a short-term need for BIS-based “compatibility” schemas that have not been as rigorously designed as true BIS schemas but allow usage and some level of interoperability with the BIS ecosystem. For this reason, a grading level for BIS schemas has been created: - *Grade A*: True BIS schemas carefully designed for editing and interoperability - *Grade B*: Either: diff --git a/docs/core/changehistory/0.183.0.md b/docs/core/changehistory/0.183.0.md deleted file mode 100644 index 558fb14..0000000 --- a/docs/core/changehistory/0.183.0.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -deltaDoc: true -version: '0.183.0' ---- -# 0.183.0 Change Notes - -## New signature for RpcInterface.forward - -To support stricter type checking of apply, call, and bind usage in upcoming versions of the typescript compiler, the signature of RpcInterface.forward is now (parameters: IArguments). This is not a breaking change for most use cases within RPC interfaces that invoke forward via apply. However, it is now possible and preferable with the new signature to directly invoke this.foward(arguments) in RPC interfaces instead of using apply. - -## Node 10 - -The iModel.js backend now requires [Node version 10](https://nodejs.org) or later. If you run the backend, please install it before running this version. - -If you build the iModel.js packages from the monorepo, you should follow these steps: - -1) `rush clean` -1) `rush unlink` -1) uninstall current version of Node (on Windows, via "add or remove programs") -1) install latest version of Node 10 -1) `npm install -g @microsoft/rush` -1) `git pull` -1) `rush install` - - if you get an error about npm versions, do `npm uninstall -g npm` -1) `rush build` -1) `rush test` - diff --git a/docs/core/changehistory/0.186.0.md b/docs/core/changehistory/0.187.0.md similarity index 50% rename from docs/core/changehistory/0.186.0.md rename to docs/core/changehistory/0.187.0.md index 95752e4..71e1451 100644 --- a/docs/core/changehistory/0.186.0.md +++ b/docs/core/changehistory/0.187.0.md @@ -1,15 +1,38 @@ ---- -deltaDoc: true -version: '0.186.0' ---- -# 0.186.0 Change Notes +--- +deltaDoc: true +version: '0.187.0' +--- +# 0.187.0 Change Notes + +## New signature for RpcInterface.forward + +To support stricter type checking of apply, call, and bind usage in upcoming versions of the typescript compiler, the signature of RpcInterface.forward is now (parameters: IArguments). This is not a breaking change for most use cases within RPC interfaces that invoke forward via apply. However, it is now possible and preferable with the new signature to directly invoke this.foward(arguments) in RPC interfaces instead of using apply. + +## Node 10 + +The iModel.js backend now requires [Node version 10](https://nodejs.org) or later. If you run the backend, please install it before running this version. + +If you build the iModel.js packages from the monorepo, you should follow these steps: + +1. `rush clean` +1. `rush unlink` +1. uninstall current version of Node (on Windows, via "add or remove programs") +1. install latest version of Node 10 +1. `npm install -g @microsoft/rush` +1. `git pull` +1. `rush install` + - if you get an error about npm versions, do `npm uninstall -g npm` +1. `rush build` +1. `rush test` ## Allowed backend applications to specify HTTPS_PROXY + Applications can now configure backend HTTP requests to go through a firewall proxy server with the HTTPS_PROXY environment. To enable this, the backend application must call the following API as part of it's initialization: ```imodeljs-clients-backend: RequestHost.initialize();``` ## More diagnostics and trace logging of all HTTP requests + * Added API to detect and use fiddler proxy (if available) at the backend to route requests for troubleshooting. To enable this, the backend application must call the following API as part of it's initialization: ```imodeljs-clients-backend: RequestHost.initialize();``` @@ -20,5 +43,5 @@ To enable this, the backend application must call the following API as part of i ```Logger.setLevel("imodeljs-clients.Request", LogLevel.Trace);``` ## Removed IModelDb's cache of accessToken -For long running operations like AutoPush, the developer must explicitly supply an ```IAccessTokenManager``` to keep the token current. +For long running operations like AutoPush, the developer must explicitly supply an ```IAccessTokenManager``` to keep the token current. diff --git a/docs/core/changehistory/0.189.0.md b/docs/core/changehistory/0.189.0.md new file mode 100644 index 0000000..f72da9c --- /dev/null +++ b/docs/core/changehistory/0.189.0.md @@ -0,0 +1,80 @@ +--- +deltaDoc: true +version: '0.189.0' +--- +# 0.189.0 Change Notes + +## iModelHub file handlers have been moved to imodeljs-clients-backend + +Moved AzureFileHandler, IOSAzureFileHandler, UrlFileHandler and the iModelHub tests to the imodeljs-clients-backend package. This removes the dependency of imodeljs-clients on the "fs" module, and turns it into a browser only package. + +To fix related build errors, update imports for these utilities from +```import {AzureFileHandler, IOSAzureFileHandler, UrlFileHandler} from "@bentley/imodels-clients";``` +to +```import {AzureFileHandler, IOSAzureFileHandler, UrlFileHandler} from "@bentley/imodels-clients-backend";``` + +## Prevented partial downloads of ChangeSets and Briefcases + +Backend ChangeSet and Briefcase downloads are atomic - i.e., will not be partially downloaded, and can simultaneously happen in multiple machines. + +## Changes to IModelConnection + +* In the case of ReadWrite connections, IModelConnection.close() now always disposes the briefcase held at the backend. Applications must ensure that any changes are saved and pushed out to the iModelHub before making this call. +* IModelConnection.connectionTimeout is now public, allowing applications to customize this in the case of slow networks. +* Removed unique id per connection: IModelConnection.connectionId +* Removed `executeQuery` which could not page the result and therefore cannot stream results from backend. This is now replaced with `query`. Code can be updated as following. + +``` + const rows = await conn.executeQuery("SELECT ECInstanceId FROM bis.Element"); +``` +can be change to following async iterator call +``` + for await (const row of conn.query("SELECT ECInstanceId FROM bis.Element")) { + /* process row */ + } +``` +in case user want to control paging manually like in case of virtual grid +``` + const pageSize = 500; // Numer of rows per page + const pageNo = 20; // Page of interest + const pageOptions = {size: noOfRowsPerPage, start: pageNo}; + const rows = await conn.queryPage ("SELECT ECInstanceId FROM bis.Element", undefined, pageOptions); + // rows will contain 0 to pageSize rows. + if (rows.length > 0 ){ + // process rows + } else { + // empty page mean there is no rows in that page and next page would also return no rows. + } +``` +in addition to above following can get the maximum number of rows returned by the query +``` + const numberOfRows = await conn.queryRowCount("SELECT ECInstanceId FROM bis.Element"); + // initialize grid scrollbar according to expected number of rows that can be returned by query. +``` + +All above can also be used with `ECDb` and `IModelDb` on backend. +## Changes to IModelApp + +Added unique id per session: IModelApp.sessionId + +## Authentication and Authorization related changes (OpenID Connect, OAuth) + +Fixes to OidcDelegationClient-s. Backend applications can now exchange - +* OIDC Jason Web Tokens (JWT) for other JWT tokens with additional scope. +* JWT tokens for legacy SAML tokens for legacy applications. + +## Logger Configuration Changes + +The BunyanLoggerConfig, FluentdBunyanLoggerConfig, FluentdLoggerStream, and SeqLoggerConfig classes have been moved out of @bentley/bentleyjs-core and into the new @bentley/logger-config package. + +## Removed RbacClient + +The RBAC API is considered internal and has been removed from the iModel.js stack. More comments on the individual methods that have been removed below. + +``` +RbacClient.getProjects() // Use ConnectClient.getProjects() instead +RbacClient.getIModelHubPermissions() // The plan is for iModelHub to support this API. +RbacClient.getUsers() // This method is little used. Bentley internal clients can make the necessary REST API calls directly. + +``` + diff --git a/docs/core/changehistory/index.md b/docs/core/changehistory/index.md index 8e3309c..a9de20b 100644 --- a/docs/core/changehistory/index.md +++ b/docs/core/changehistory/index.md @@ -1,19 +1,75 @@ -# 0.186.0 Change Notes +# 0.189.0 Change Notes -## Allowed backend applications to specify HTTPS_PROXY -Applications can now configure backend HTTP requests to go through a firewall proxy server with the HTTPS_PROXY environment. -To enable this, the backend application must call the following API as part of it's initialization: - ```imodeljs-clients-backend: RequestHost.initialize();``` +## iModelHub file handlers have been moved to imodeljs-clients-backend -## More diagnostics and trace logging of all HTTP requests -* Added API to detect and use fiddler proxy (if available) at the backend to route requests for troubleshooting. -To enable this, the backend application must call the following API as part of it's initialization: - ```imodeljs-clients-backend: RequestHost.initialize();``` +Moved AzureFileHandler, IOSAzureFileHandler, UrlFileHandler and the iModelHub tests to the imodeljs-clients-backend package. This removes the dependency of imodeljs-clients on the "fs" module, and turns it into a browser only package. - The API is called automatically when opening iModel-s, but must be typically setup by backend applications at startup. +To fix related build errors, update imports for these utilities from +```import {AzureFileHandler, IOSAzureFileHandler, UrlFileHandler} from "@bentley/imodels-clients";``` +to +```import {AzureFileHandler, IOSAzureFileHandler, UrlFileHandler} from "@bentley/imodels-clients-backend";``` -* Setup trace logs of all requests made through the Request API. To enable this, do: - ```Logger.setLevel("imodeljs-clients.Request", LogLevel.Trace);``` +## Prevented partial downloads of ChangeSets and Briefcases -## Removed IModelDb's cache of accessToken -For long running operations like AutoPush, the developer must explicitly supply an ```IAccessTokenManager``` to keep the token current. +Backend ChangeSet and Briefcase downloads are atomic - i.e., will not be partially downloaded, and can simultaneously happen in multiple machines. + +## Changes to IModelConnection + +* In the case of ReadWrite connections, IModelConnection.close() now always disposes the briefcase held at the backend. Applications must ensure that any changes are saved and pushed out to the iModelHub before making this call. +* IModelConnection.connectionTimeout is now public, allowing applications to customize this in the case of slow networks. +* Removed unique id per connection: IModelConnection.connectionId +* Removed `executeQuery` which could not page the result and therefore cannot stream results from backend. This is now replaced with `query`. Code can be updated as following. + +``` + const rows = await conn.executeQuery("SELECT ECInstanceId FROM bis.Element"); +``` +can be change to following async iterator call +``` + for await (const row of conn.query("SELECT ECInstanceId FROM bis.Element")) { + /* process row */ + } +``` +in case user want to control paging manually like in case of virtual grid +``` + const pageSize = 500; // Numer of rows per page + const pageNo = 20; // Page of interest + const pageOptions = {size: noOfRowsPerPage, start: pageNo}; + const rows = await conn.queryPage ("SELECT ECInstanceId FROM bis.Element", undefined, pageOptions); + // rows will contain 0 to pageSize rows. + if (rows.length > 0 ){ + // process rows + } else { + // empty page mean there is no rows in that page and next page would also return no rows. + } +``` +in addition to above following can get the maximum number of rows returned by the query +``` + const numberOfRows = await conn.queryRowCount("SELECT ECInstanceId FROM bis.Element"); + // initialize grid scrollbar according to expected number of rows that can be returned by query. +``` + +All above can also be used with `ECDb` and `IModelDb` on backend. +## Changes to IModelApp + +Added unique id per session: IModelApp.sessionId + +## Authentication and Authorization related changes (OpenID Connect, OAuth) + +Fixes to OidcDelegationClient-s. Backend applications can now exchange - +* OIDC Jason Web Tokens (JWT) for other JWT tokens with additional scope. +* JWT tokens for legacy SAML tokens for legacy applications. + +## Logger Configuration Changes + +The BunyanLoggerConfig, FluentdBunyanLoggerConfig, FluentdLoggerStream, and SeqLoggerConfig classes have been moved out of @bentley/bentleyjs-core and into the new @bentley/logger-config package. + +## Removed RbacClient + +The RBAC API is considered internal and has been removed from the iModel.js stack. More comments on the individual methods that have been removed below. + +``` +RbacClient.getProjects() // Use ConnectClient.getProjects() instead +RbacClient.getIModelHubPermissions() // The plan is for iModelHub to support this API. +RbacClient.getUsers() // This method is little used. Bentley internal clients can make the necessary REST API calls directly. + +``` diff --git a/docs/core/changehistory/leftNav.md b/docs/core/changehistory/leftNav.md index 1c7ad67..4ddc42f 100644 --- a/docs/core/changehistory/leftNav.md +++ b/docs/core/changehistory/leftNav.md @@ -2,10 +2,9 @@ --- ### Versions -- [0.186.0](./0.186.0.md) - -- [0.183.0](./0.183.0.md) +- [0.189.0](./0.189.0.md) +- [0.187.0](./0.187.0.md) - [0.171.0](./0.171.0.md) diff --git a/docs/core/config/docSites.json b/docs/core/config/docSites.json index 4409f1e..ae819c7 100644 --- a/docs/core/config/docSites.json +++ b/docs/core/config/docSites.json @@ -42,7 +42,7 @@ "BIS": "bis", "Learning": "learning", "API Reference": "reference", - "v0.187.0": "changehistory" + "v0.189.0": "changehistory" }, "packageAliases": { "common": "imodeljs-common", diff --git a/docs/core/config/layouts/partials/landing_middle_content.html b/docs/core/config/layouts/partials/landing_middle_content.html index 039525d..f961941 100644 --- a/docs/core/config/layouts/partials/landing_middle_content.html +++ b/docs/core/config/layouts/partials/landing_middle_content.html @@ -3,35 +3,44 @@ {{#each this.middleContent}}
{{#if_even @index}} - {{this.imageAltText}} +
{{#if this.header}} -

{{{this.header}}}

+

{{{this.header}}}

{{/if}} {{#if this.content}} - {{{this.content}}} +

+ {{{this.content}}} +

{{/if}}
+
+ +
{{else}}
{{#if this.header}}

{{{this.header}}}

- {{/if}} + {{/if}} {{#if this.content}} - {{{this.content}}} +

+ {{{this.content}}} +

{{/if}}
- {{this.imageAltText}} + {{/if_even}}
{{/each}} -
-

Try iModel.js now

-
+
+

Try iModel.js now

+
\ No newline at end of file diff --git a/docs/core/config/layouts/partials/landing_page_header.html b/docs/core/config/layouts/partials/landing_page_header.html index 7ed1706..dbd0757 100644 --- a/docs/core/config/layouts/partials/landing_page_header.html +++ b/docs/core/config/layouts/partials/landing_page_header.html @@ -16,13 +16,14 @@ - - - @@ -74,7 +75,8 @@ View Documentation - View + View on GitHub
\ No newline at end of file diff --git a/docs/core/config/layouts/styles/landingPage.css b/docs/core/config/layouts/styles/landingPage.css index fa36d43..c0847b9 100644 --- a/docs/core/config/layouts/styles/landingPage.css +++ b/docs/core/config/layouts/styles/landingPage.css @@ -3,15 +3,54 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ +span.gradient { + background: linear-gradient(90deg, #038cd6 0%, #67b225 43%, #EFF8FF 100%) !important; + height: 3px; + display: block; + border: none; +} + #main-landing-wrapper { margin-bottom: 0em !important; background-color: #EFF8FF; + display: -ms-grid; + display: grid; + -ms-grid-columns: 1fr; + grid-template-columns: 1fr; + -ms-flex: auto; + flex: auto; + margin-bottom: 187px; +} + +#main-landing-wrapper .documentation-main { + -ms-grid-column: 1; + grid-column: 1; } #main-landing-wrapper #middle-content-wrapper { margin: 2em 5% !important; } +#main-landing-wrapper.full-width article#main { + padding-left: 15px; +} + +#main-landing-wrapper.full-width p { + padding-left: 10px; +} + +#middle-content-wrapper { + display: flex; + flex-wrap: wrap-reverse; + -ms-flex-wrap: wrap-reverse; + flex-flow: column; + -ms-flex-flow: row wrap; + justify-content: space-around; + margin: 2em 10%; + align-items: center; + align-content: center; +} + #navbar7 li:hover { height: 35px; border-bottom-color: white; @@ -24,7 +63,7 @@ text-align: center; line-height: 1em; margin: 3em auto; - background: #002A44C0; + background: rgba(0, 42, 68, 0.753); } .imodeljs-header-tagline p { @@ -55,6 +94,7 @@ p.digital-twin { position: relative; margin-bottom: 1em; flex-direction: column; + -ms-flex-direction: column; } .example-view-demonstrator { @@ -79,19 +119,35 @@ p.digital-twin { } .nav-landing-container .navbar-nav { - background: #002A44C0; + background: rgba(0, 42, 68, 0.753); border-radius: 10px; } .middle-content-group { + display: flex; + flex-direction: row; + -ms-flex-direction: row; margin-bottom: 2em; } -.middle-content-image { - width: 21%; +.middle-content-media { + width: 50%; margin-bottom: 3em; min-width: 0; flex-shrink: 0; + -ms-flex-shrink: 0; + max-height: 25vw; + max-width: 45vw; +} + +.middle-content-text { + min-width: 250px; + margin: 0px 4vw; +} + +.middle-content-youtube { + height: 101%; + width: 101%; } .feature-header { @@ -102,23 +158,39 @@ ul.list-unstyled.list-inline { margin-bottom: 0; } -.middle-content-text { - max-width: 900px; - min-width: 250px; - margin-bottom: 3em; +body.layout-full-width #main-landing-wrapper { + display: grid; + grid-template-columns: 1fr; + -ms-grid-columns: 1fr; } -span.gradient { - background: linear-gradient(90deg, #038cd6 0%, #67b225 43%, #EFF8FF 100%) !important; +#bottom-content-container { + -ms-flex-direction: row; + flex-direction: row; +} + +@media screen and (max-width: 1650px) { + #main-landing-wrapper #middle-content-wrapper { + margin: 2em 1% !important; + } + .middle-content-text { + margin: 0 1vw 0 4vw; + } } -@media screen and (max-width: 767px) { +@media screen and (max-width: 768px) { + #bottom-content-container { + -ms-flex-direction: column !important; + } .nav-landing-container .navbar-nav { padding-right: 15px; max-width: 38%; } - .middle-content-image { - width: 40%; + #navbar7 li:hover { + height: 35px; + border-bottom: none; + font-weight: 700; + transition: none; } .iframe-viewer { width: 500px; @@ -128,14 +200,13 @@ span.gradient { width: 500px; height: 313px; } - .nav-landing-container .navbar-toggler-icon { - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#002A44' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 8h24M4 16h24M4 24h24'/%3E%3C/svg%3E") !important; - } - #navbar7 li:hover { - height: 35px; - border-bottom: none; - font-weight: 700; - transition: none; + .middle-content-media { + width: 75vw; + height: 42vw; + max-width: 75vw; + max-height: 42vw; + margin: 0 10% 3em 10%; + order: 1 !important; } .landing-bottom-content-item { min-width: 250px; @@ -143,16 +214,11 @@ span.gradient { padding: 20px 5px 0px 5px; margin: 20px 5px 0px 5px; } -} - -@media screen and (max-width: 600px) { .middle-content-group { flex-flow: column; + -ms-flex-flow: row wrap; align-items: center; } - .middle-content-image { - order: 1 !important; - } .middle-content-text { order: 2 !important; } diff --git a/docs/core/getting-started/application-scopes.md b/docs/core/getting-started/application-scopes.md new file mode 100644 index 0000000..021614f --- /dev/null +++ b/docs/core/getting-started/application-scopes.md @@ -0,0 +1,16 @@ +--- +containsMetadata: 'AvailableScopes' +--- + + +# Available scopes + +| Name | Description | +|--------------------------|----------------------------------------------------------------------------------------------| +| openid | Required to get the id_token and the id of the user | +| email | Required to get the email of the user | +| profile | Required to get the user profile – the first and last names | +| organization | Required to get the user’s organization information | +| context-registry-service | Required to access the Context Registry, the service that allows access to a CONNECT project | +| imodelhub | Required to access the iModelHub, the service that allows access to the iModels | +| reality-data:read | Required for read access to reality data | \ No newline at end of file diff --git a/docs/core/getting-started/registration-dashboard.md b/docs/core/getting-started/registration-dashboard.md new file mode 100644 index 0000000..1bb6a40 --- /dev/null +++ b/docs/core/getting-started/registration-dashboard.md @@ -0,0 +1,4 @@ +--- +title: 'Registration Dashboard' +layout: 'registrationDashboard.html' +--- diff --git a/docs/core/landing-page/assets/iModeljs_white_016.png b/docs/core/landing-page/assets/iModeljs_white_016.png new file mode 100644 index 0000000..a644454 Binary files /dev/null and b/docs/core/landing-page/assets/iModeljs_white_016.png differ diff --git a/docs/core/landing-page/assets/iModeljs_white_024.png b/docs/core/landing-page/assets/iModeljs_white_024.png new file mode 100644 index 0000000..89b6de1 Binary files /dev/null and b/docs/core/landing-page/assets/iModeljs_white_024.png differ diff --git a/docs/core/landing-page/assets/iModeljs_white_032.png b/docs/core/landing-page/assets/iModeljs_white_032.png new file mode 100644 index 0000000..6709217 Binary files /dev/null and b/docs/core/landing-page/assets/iModeljs_white_032.png differ diff --git a/docs/core/landing-page/assets/iModeljs_white_036.png b/docs/core/landing-page/assets/iModeljs_white_036.png new file mode 100644 index 0000000..1886395 Binary files /dev/null and b/docs/core/landing-page/assets/iModeljs_white_036.png differ diff --git a/docs/core/landing-page/assets/iModeljs_white_040.png b/docs/core/landing-page/assets/iModeljs_white_040.png new file mode 100644 index 0000000..55e9852 Binary files /dev/null and b/docs/core/landing-page/assets/iModeljs_white_040.png differ diff --git a/docs/core/landing-page/assets/iModeljs_white_048.png b/docs/core/landing-page/assets/iModeljs_white_048.png new file mode 100644 index 0000000..8645ad9 Binary files /dev/null and b/docs/core/landing-page/assets/iModeljs_white_048.png differ diff --git a/docs/core/landing-page/assets/iModeljs_white_064.png b/docs/core/landing-page/assets/iModeljs_white_064.png new file mode 100644 index 0000000..34ac6e6 Binary files /dev/null and b/docs/core/landing-page/assets/iModeljs_white_064.png differ diff --git a/docs/core/landing-page/assets/iModeljs_white_072.png b/docs/core/landing-page/assets/iModeljs_white_072.png new file mode 100644 index 0000000..f47fbe4 Binary files /dev/null and b/docs/core/landing-page/assets/iModeljs_white_072.png differ diff --git a/docs/core/landing-page/assets/iModeljs_white_096.png b/docs/core/landing-page/assets/iModeljs_white_096.png new file mode 100644 index 0000000..c24fd69 Binary files /dev/null and b/docs/core/landing-page/assets/iModeljs_white_096.png differ diff --git a/docs/core/landing-page/assets/iModeljs_white_128.png b/docs/core/landing-page/assets/iModeljs_white_128.png new file mode 100644 index 0000000..a79b503 Binary files /dev/null and b/docs/core/landing-page/assets/iModeljs_white_128.png differ diff --git a/docs/core/landing-page/assets/iModeljs_white_256.png b/docs/core/landing-page/assets/iModeljs_white_256.png new file mode 100644 index 0000000..ebb7b02 Binary files /dev/null and b/docs/core/landing-page/assets/iModeljs_white_256.png differ diff --git a/docs/core/landing-page/assets/imodeljs_white.svg b/docs/core/landing-page/assets/imodeljs_white.svg new file mode 100644 index 0000000..dbdd24b --- /dev/null +++ b/docs/core/landing-page/assets/imodeljs_white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/core/landing-page/index.md b/docs/core/landing-page/index.md index e7b0ee6..8d88ce3 100644 --- a/docs/core/landing-page/index.md +++ b/docs/core/landing-page/index.md @@ -2,40 +2,21 @@ layout: landingPage.html middleContent: [ { - header: "Infrastructure Digital Twins", + header: "About iModel.js", content: -"An Infrastructure Digital Twin is a digital representation of an infrastructure asset or system and the context and controls of its surrounding environment. An iModel is a relational database that contains and organizes the digital components that comprise a digital twin. iModelHub manages the timeline of change to an iModel, and turns it into a distributed database. Use iModel.js to create immersive visualizations and connections that integrate your infrastructure digital twin into your digital workflows. It contains the magic for creating, visualizing, querying, mining, synchronizing, aligning, and securing your digital twin.", - image: "assets/landing_image_twin.svg", - imageAltText: "A digital simulation of an asset or project." - }, - { - header: "Open, for Business", - content: - "Infrastructure assets are complex, valuable, and long-lived, yet ever-changing. The businesses and governments that design, build, own and operate them wish to create digital twins that accurately reflect those traits. No single software system can possibly meet all of those requirements at once. So iModel.js was designed to be both flexible and open so that it can be used wherever needed and easily integrated with other systems wherever necessary. It is built to be as malleable as possible to satisfy your business requirements within your existing business systems. We strive to innovate while being both open and transparent, but also stable and secure. iModel.js is not a community project by part time volunteers with other jobs; it is the heart of Bentley's iTwin subscription service.", - image: "assets/landing_image_files.svg", - imageAltText: "Built to solve real world business problems." - }, - { - header: "Open, By Design", - content: -"We know that when you use a set of libraries like iModel.js, you're not expecting to 'become an iModel.js developer'. Rather, you wish to incorporate code and data from many sources, joining it into a unified system and user experience. For this reason, iModel.js layers atop the most popular and most standard cloud and web technologies. It starts with TypeScript - in our opinion the most productive and compelling programming ecosystem ever. It builds on SQLite, JavaScript, Node.js, NPM, Electron, Docker, Kubernetes and many other open source leaders, in addition to standards like HTML, CSS, and WebGL. The stack is intentionally chosen to be as mainstream and un-opinionated as possible to reduce input impedance with your codebase, and to preserve your flexibility over time. Whether you intend to do numerical calculations with TensorFlow or web interfaces with React, iModel.js should fit easily and naturally with your software architecture.", - image: "assets/landing_image_tools.svg", - imageAltText: "Purposely designed to fit in your open-source world." - }, - { - header: "Open, For Innovation", - content: -"Sometimes pushing the envelope in pursuit of new ideas means breaking new ground. A primary reason for open-sourcing the iModel.js library is to permit its use in scenarios outside of those for which it was originally conceived. We know firsthand that simply examining the inner workings of a codebase improves your understanding of its design and limitations. Source level debugging and even local modification can substantially improve programmer productivity. And of course the ability to fix/change the source when necessary means self-help is always available if needed.", - image: "assets/landing_image_computer.svg", - imageAltText: "An Open Ecosystem for innovation." - }, - { - header: "Open for suggestions", - content: -"iModel.js is built on a solid foundation of Bentley's experience creating infrastructure software over 35 years. We recognize that the opportunities for connecting infrastructure digital twins are so vast and diverse, that you may find problems, have suggestions for enhancements, or have new ideas that will benefit the iModel.js community. In those cases, please don't be shy about getting involved - either with Issue reports, discussion posts, or Pull Requests.", - image: "assets/landing_image_ideas.svg", - imageAltText: "Tell us what you think." - }, +"A Library of Tools for Infrastructure Digital Twins +
+iModel.js is a library for creating, accessing, leveraging and integrating infrastructure digital twins. An Infrastructure digital twin is a digital representation of an asset or system and the context and controls of its surrounding environment. Infrastructure owners and operators are embracing digital twins for better planning, delivery, operation and maintenance of their assets. +
+
+Infrastructure digital twins can be based upon a relational database—known as an iModel—that contains components that comprise a digital twin. Changes to an iModel can be managed by iModelHub and synchronized with distributed copies—creating a distributed database. The iModel.js library can be used to integrate your infrastructure digital twin into your digital workflows, and contains tools for creating, visualizing, querying, mining, synchronizing, aligning, and securing your digital twin. +
+
+Open for Business and Innovation +
+iModel.js was designed to be both flexible and open, so that it can be easily used and integrated with other systems. The library makes use of standard cloud and web technologies chosen to reduce input impedance with your codebase, and to preserve flexibility over time. A primary reason for open-sourcing the iModel.js library is to foster innovation and novel uses of the technology. The opportunities for connecting infrastructure digital twins are vast and diverse, and we strongly encourage your involvement and suggestions for enhancing iModel.js and the iModel.js community.", + video: "https://www.youtube.com/embed/5iglYhQmv6U" + } ] features: [ diff --git a/docs/core/learning/ECDbChange.ecschema.md b/docs/core/learning/ECDbChange.ecschema.md index 6de1d8c..35b54b8 100644 --- a/docs/core/learning/ECDbChange.ecschema.md +++ b/docs/core/learning/ECDbChange.ecschema.md @@ -1,88 +1,168 @@ --- +noEditThisPage: true Schema: ECDbChange This file was automatically generated via ecjson2md. Do not edit this file. Any edits made to this file will be overwritten the next time it is generated --- # ECDbChange ECSchema -## Classes +**alias:** change -### ChangeSummary +**version:** 1.0.1 -**Class Type:** EntityClass +**displayLabel:** ECDb Change -**Class Properties:** +## Entity Classes -| Name | Description | Type | Extended Type | -|:-----------|:------------------|:-----------|:-----------------------| -|ExtendedProperties||string|Json| -| | | | | +### ChangeSummary -### ChangeSummaryContainsInstanceChanges +**typeName:** EntityClass + +**displayLabel:** Summary -**Class Type:** RelationshipClass +**modifier:** Sealed -**Relationship Class:** +#### Properties -| | ConstraintClasses | Multiplicity | -|:---------|:------------------------|:-----------------------------------| -|**Source**|ChangeSummary|(1..1)| -|**Target**|InstanceChange|(0..*)| -| | | | +| Name | Description | Type | Extended Type | +|:-----------|:------------------|:-----------|:-----------------------| +|ExtendedProperties||string|Json| ### InstanceChange +**typeName:** EntityClass + Represents an instance change in a change summary -**Class Type:** EntityClass +**displayLabel:** Instance Change + +**modifier:** Sealed -**Class Properties:** +#### Properties | Name | Description | Type | Extended Type | |:-----------|:------------------|:-----------|:-----------------------| -|Summary|||| -|ChangedInstance|Key of the change instance||| +|Summary||[navigation](ecdbchange.ecschema.md#changesummarycontainsinstancechanges)|| +|ChangedInstance|Key of the change instance|struct|| |OpCode||OpCode|| |IsIndirect|Change happened due to a foreign key action or trigger|boolean|| -| | | | | - -### InstanceChangeOwnsPropertyValueChanges -**Class Type:** RelationshipClass +### PropertyValueChange -**Relationship Class:** +**typeName:** EntityClass -| | ConstraintClasses | Multiplicity | -|:---------|:------------------------|:-----------------------------------| -|**Source**|InstanceChange|(1..1)| -|**Target**|PropertyValueChange|(0..*)| -| | | | +Represents an property value change of an instance change in a change summary -### InstanceKey +**displayLabel:** Property Value Change -**Class Type:** StructClass +**modifier:** Sealed -**Class Properties:** +#### Properties | Name | Description | Type | Extended Type | |:-----------|:------------------|:-----------|:-----------------------| -|Id||long|Id| -|ClassId||long|Id| -| | | | | +|InstanceChange||[navigation](ecdbchange.ecschema.md#instancechangeownspropertyvaluechanges)|| +|AccessString||string|| +|RawOldValue|Untyped old value|binary|| +|RawNewValue|Untyped new value|binary|| -### PropertyValueChange +## Relationship Classes -Represents an property value change of an instance change in a change summary +### ChangeSummaryContainsInstanceChanges -**Class Type:** EntityClass +**typeName:** RelationshipClass -**Class Properties:** +**modifier:** Sealed -| Name | Description | Type | Extended Type | -|:-----------|:------------------|:-----------|:-----------------------| -|InstanceChange|||| -|AccessString||string|| -|RawOldValue|Untyped old value|binary|| -|RawNewValue|Untyped new value|binary|| -| | | | | +**Strength:** Embedding + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** has + +**multiplicity:** (1..1) + +##### Constraint Classes + +- [ChangeSummary](ecdbchange.ecschema.md#changesummary) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is contained in + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [InstanceChange](ecdbchange.ecschema.md#instancechange) + +### InstanceChangeOwnsPropertyValueChanges + +**typeName:** RelationshipClass + +**modifier:** Sealed + +**Strength:** Embedding + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** owns + +**multiplicity:** (1..1) + +##### Constraint Classes + +- [InstanceChange](ecdbchange.ecschema.md#instancechange) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is contained in + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [PropertyValueChange](ecdbchange.ecschema.md#propertyvaluechange) + +## Enumerations + +### OpCode + +**typeName:** Enumeration + +**Backing Type:** int + +**Strict:** true + +| Label | Value | +|:------------|:------------| +|Insert|1| +|Update|2| +|Delete|4| + +### InstanceKey + +**typeName:** StructClass + +**displayLabel:** InstanceKey + +**modifier:** Sealed + +#### Properties +| Name | Description | Label | Category | Read Only | Priority | +|:-----------|:--------------|:------------|:-----------|:-----------------|:---------------| +|Id||||false|0| +|ClassId||||false|0| diff --git a/docs/core/learning/ECDbMeta.ecschema.md b/docs/core/learning/ECDbMeta.ecschema.md index ce335ad..20460e9 100644 --- a/docs/core/learning/ECDbMeta.ecschema.md +++ b/docs/core/learning/ECDbMeta.ecschema.md @@ -1,64 +1,32 @@ --- +noEditThisPage: true Schema: ECDbMeta This file was automatically generated via ecjson2md. Do not edit this file. Any edits made to this file will be overwritten the next time it is generated --- # ECDbMeta ECSchema -## Classes +**alias:** meta -### ClassHasAllBaseClasses - -Relates a given ECClassId (Source) to all its base classes (recursively) (Target). For optimization reasons this ECRelationshipClass also holds a row for each ECClass that points to itself. Base class id order: class itself, then base classes in breadth first manner. - -**Class Type:** RelationshipClass - -**Relationship Class:** - -| | ConstraintClasses | Multiplicity | -|:---------|:------------------------|:-----------------------------------| -|**Source**|ECClassDef|(0..*)| -|**Target**|ECClassDef|(0..*)| - -### ClassHasBaseClasses - -**Class Type:** RelationshipClass - -**Relationship Class:** - -| | ConstraintClasses | Multiplicity | -|:---------|:------------------------|:-----------------------------------| -|**Source**|ECClassDef|(0..*)| -|**Target**|ECClassDef|(0..*)| - -**Class Properties:** - -| Name | Description | Type | Extended Type | -|:-----------|:------------------|:-----------|:-----------------------| -|Ordinal||int|| - -### ClassOwnsLocalProperties - -**Class Type:** RelationshipClass +**version:** 4.0.1 -**Relationship Class:** - -| | ConstraintClasses | Multiplicity | -|:---------|:------------------------|:-----------------------------------| -|**Source**|ECClassDef|(1..1)| -|**Target**|ECPropertyDef|(0..*)| +## Entity Classes ### ECClassDef +**typeName:** EntityClass + ECClass -**Class Type:** EntityClass +**displayLabel:** ECClass -**Class Properties:** +**modifier:** Sealed + +#### Properties | Name | Description | Type | Extended Type | |:-----------|:------------------|:-----------|:-----------------------| -|Schema|||| +|Schema||[navigation](ecdbmeta.ecschema.md#schemaownsclasses)|| |Name||string|| |DisplayLabel||string|| |Description||string|| @@ -70,49 +38,41 @@ ECClass ### ECEnumerationDef +**typeName:** EntityClass + ECEnumeration -**Class Type:** EntityClass +**displayLabel:** ECEnumeration -**Class Properties:** +**modifier:** Sealed + +#### Properties | Name | Description | Type | Extended Type | |:-----------|:------------------|:-----------|:-----------------------| -|Schema|||| +|Schema||[navigation](ecdbmeta.ecschema.md#schemaownsenumerations)|| |Name||string|| |DisplayLabel||string|| |Description||string|| |Type||PrimitiveType|| |IsStrict||boolean|| -|EnumValues||[ECEnumeratorDef](#ECEnumeratorDef)[]|| -| | | | | - -### ECEnumeratorDef - -**Class Type:** StructClass - -**Class Properties:** - -| Name | Description | Type | Extended Type | -|:-----------|:------------------|:-----------|:-----------------------| -|Name||string|| -|DisplayLabel||string|| -|Description||string|| -|IntValue||int|| -|StringValue||string|| -| | | | | +|EnumValues||struct array|| ### ECPropertyDef +**typeName:** EntityClass + ECProperty -**Class Type:** EntityClass +**displayLabel:** ECProperty -**Class Properties:** +**modifier:** Sealed + +#### Properties | Name | Description | Type | Extended Type | |:-----------|:------------------|:-----------|:-----------------------| -|Class|||| +|Class||[navigation](ecdbmeta.ecschema.md#classownslocalproperties)|| |Name||string|| |DisplayLabel||string|| |Description||string|| @@ -125,41 +85,45 @@ ECProperty |PrimitiveTypeMaxLength|Maximum length of strings or blobs.|int|| |PrimitiveTypeMinValue|Minimum value of numeric primitives.|double|| |PrimitiveTypeMaxValue|Maximum value of numeric primitives.|double|| -|Enumeration|ECPropertyKind::Primitive: Id of this property's ECEnumerationDef.||| +|Enumeration|ECPropertyKind::Primitive: Id of this property's ECEnumerationDef.|[navigation](ecdbmeta.ecschema.md#propertyhasenumeration)|| |ExtendedTypeName|ECPropertyKind::Primitive or ECPropertyKind::PrimitiveArray: Extended type name further describing PrimitiveType.|string|| -|StructClass|ECPropertyKind::Struct: ECClassId of property's struct type. ECPropertyKind::StructArray: ECClassId of array element struct type.||| -|KindOfQuantity|ECPropertyKind::Primitive or ECPropertyKind::PrimitiveArray: Id of this property's KindOfQuantityDef.||| -|Category|Id of this property's PropertyCategoryDef.||| +|StructClass|ECPropertyKind::Struct: ECClassId of property's struct type. ECPropertyKind::StructArray: ECClassId of array element struct type.|[navigation](ecdbmeta.ecschema.md#propertyhasstructtype)|| +|KindOfQuantity|ECPropertyKind::Primitive or ECPropertyKind::PrimitiveArray: Id of this property's KindOfQuantityDef.|[navigation](ecdbmeta.ecschema.md#propertyhaskindofquantity)|| +|Category|Id of this property's PropertyCategoryDef.|[navigation](ecdbmeta.ecschema.md#propertyhascategory)|| |ArrayMinOccurs|ECPropertyKind::PrimitiveArray or ECPropertyKind::StructArray|int|| |ArrayMaxOccurs|ECPropertyKind::PrimitiveArray or ECPropertyKind::StructArray|int|| -|NavigationRelationshipClass|ECPropertyKind::Navigation: ECClassId of property's relationship class.||| +|NavigationRelationshipClass|ECPropertyKind::Navigation: ECClassId of property's relationship class.|[navigation](ecdbmeta.ecschema.md#propertyhasnavigationrelationshipclassid)|| |NavigationDirection|ECPropertyKind::Navigation: Direction to follow the relationship defined in NonPrimitiveTypeClassId.|ECRelationshipDirection|| -| | | | | ### ECRelationshipConstraintDef -**Class Type:** EntityClass +**typeName:** EntityClass + +**modifier:** Sealed -**Class Properties:** +#### Properties | Name | Description | Type | Extended Type | |:-----------|:------------------|:-----------|:-----------------------| -|RelationshipClass|||| +|RelationshipClass||[navigation](ecdbmeta.ecschema.md#relationshiphasconstraints)|| |RelationshipEnd||ECRelationshipEnd|| |MultiplicityLowerLimit||int|| |MultiplicityUpperLimit||int|| |IsPolymorphic||boolean|| |RoleLabel||string|| -|AbstractConstraintClass|ECClassId of base class for constraint classes of this constraint||| -| | | | | +|AbstractConstraintClass|ECClassId of base class for constraint classes of this constraint|[navigation](ecdbmeta.ecschema.md#relationshipconstrainthasabstractconstraintclass)|| ### ECSchemaDef +**typeName:** EntityClass + ECSchema -**Class Type:** EntityClass +**displayLabel:** ECSchema + +**modifier:** Sealed -**Class Properties:** +#### Properties | Name | Description | Type | Extended Type | |:-----------|:------------------|:-----------|:-----------------------| @@ -170,206 +134,1168 @@ ECSchema |VersionMajor||int|| |VersionWrite||int|| |VersionMinor||int|| -| | | | | +|OriginalECXmlVersionMajor||int|| +|OriginalECXmlVersionMinor||int|| + +### FormatCompositeUnitDef + +**typeName:** EntityClass + +FormatCompositeUnit + +**displayLabel:** FormatCompositeUnit + +**modifier:** Sealed + +#### Properties + +| Name | Description | Type | Extended Type | +|:-----------|:------------------|:-----------|:-----------------------| +|Format||[navigation](ecdbmeta.ecschema.md#formatownscompositeunits)|| +|Label||string|| +|Unit||[navigation](ecdbmeta.ecschema.md#compositeunitreferstounit)|| +|Ordinal|Position of the Composite Unit in its Format|int|| + +### FormatDef + +**typeName:** EntityClass + +Format + +**displayLabel:** Format + +**modifier:** Sealed + +#### Properties + +| Name | Description | Type | Extended Type | +|:-----------|:------------------|:-----------|:-----------------------| +|Schema||[navigation](ecdbmeta.ecschema.md#schemaownsformats)|| +|Name||string|| +|DisplayLabel||string|| +|Description||string|| +|NumericSpec||string|JSON| +|CompositeSpec|CompositeSpec without units. Composite units are held by FormatCompositeUnitDef|string|JSON| ### KindOfQuantityDef +**typeName:** EntityClass + KindOfQuantity -**Class Type:** EntityClass +**displayLabel:** KindOfQuantity -**Class Properties:** +**modifier:** Sealed + +#### Properties | Name | Description | Type | Extended Type | |:-----------|:------------------|:-----------|:-----------------------| -|Schema|||| +|Schema||[navigation](ecdbmeta.ecschema.md#schemaownskindofquantities)|| |Name||string|| |DisplayLabel||string|| |Description||string|| |PersistenceUnit||string|| |RelativeError||double|| -|PresentationUnits|||| -| | | | | +|PresentationUnits||string array|| + +### PhenomenonDef + +**typeName:** EntityClass + +Phenomenon + +**displayLabel:** Phenomenon + +**modifier:** Sealed + +#### Properties + +| Name | Description | Type | Extended Type | +|:-----------|:------------------|:-----------|:-----------------------| +|Schema||[navigation](ecdbmeta.ecschema.md#schemaownsphenomena)|| +|Name||string|| +|DisplayLabel||string|| +|Description||string|| +|Definition||string|| ### PropertyCategoryDef +**typeName:** EntityClass + PropertyCategory -**Class Type:** EntityClass +**displayLabel:** PropertyCategory -**Class Properties:** +**modifier:** Sealed + +#### Properties | Name | Description | Type | Extended Type | |:-----------|:------------------|:-----------|:-----------------------| -|Schema|||| +|Schema||[navigation](ecdbmeta.ecschema.md#schemaownspropertycategories)|| |Name||string|| |DisplayLabel||string|| |Description||string|| |Priority||int|| -| | | | | + +### UnitDef + +**typeName:** EntityClass + +Unit + +**displayLabel:** Unit + +**modifier:** Sealed + +#### Properties + +| Name | Description | Type | Extended Type | +|:-----------|:------------------|:-----------|:-----------------------| +|Schema||[navigation](ecdbmeta.ecschema.md#schemaownsunits)|| +|Name||string|| +|DisplayLabel||string|| +|Description||string|| +|UnitSystem||[navigation](ecdbmeta.ecschema.md#unitsystemhasunits)|| +|Phenomenon||[navigation](ecdbmeta.ecschema.md#phenomenonownsunits)|| +|Definition||string|| +|Numerator||double|| +|Denominator||double|| +|Offset||double|| +|IsConstant||boolean|| +|InvertingUnit|If this property is set, this UnitDef is an inverted unit. It inverts the unit specified by this property.|[navigation](ecdbmeta.ecschema.md#unithasinvertedunit)|| + +### UnitSystemDef + +**typeName:** EntityClass + +UnitSystem + +**displayLabel:** UnitSystem + +**modifier:** Sealed + +#### Properties + +| Name | Description | Type | Extended Type | +|:-----------|:------------------|:-----------|:-----------------------| +|Schema||[navigation](ecdbmeta.ecschema.md#schemaownsunitsystems)|| +|Name||string|| +|DisplayLabel||string|| +|Description||string|| + +## Relationship Classes + +### ClassHasAllBaseClasses + +**typeName:** RelationshipClass + +Relates a given ECClassId (Source) to all its base classes (recursively) (Target). For optimization reasons this ECRelationshipClass also holds a row for each ECClass that points to itself. Base class id order: class itself, then base classes in breadth first manner. + +**modifier:** Sealed + +**Strength:** Referencing + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** is a + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [ECClassDef](ecdbmeta.ecschema.md#ecclassdef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is ancestor of + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [ECClassDef](ecdbmeta.ecschema.md#ecclassdef) + +### ClassHasBaseClasses + +**typeName:** RelationshipClass + +**modifier:** Sealed + +**Strength:** Referencing + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** is subclass of + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [ECClassDef](ecdbmeta.ecschema.md#ecclassdef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is base class of + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [ECClassDef](ecdbmeta.ecschema.md#ecclassdef) + +### ClassOwnsLocalProperties + +**typeName:** RelationshipClass + +**modifier:** Sealed + +**Strength:** Embedding + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** locally defines + +**multiplicity:** (1..1) + +##### Constraint Classes + +- [ECClassDef](ecdbmeta.ecschema.md#ecclassdef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is defined in + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [ECPropertyDef](ecdbmeta.ecschema.md#ecpropertydef) + +### CompositeUnitRefersToUnit + +**typeName:** RelationshipClass + +**modifier:** Sealed + +**Strength:** Referencing + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** refers to + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [FormatCompositeUnitDef](ecdbmeta.ecschema.md#formatcompositeunitdef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is referred to by + +**multiplicity:** (0..1) + +##### Constraint Classes + +- [UnitDef](ecdbmeta.ecschema.md#unitdef) + +### FormatOwnsCompositeUnits + +**typeName:** RelationshipClass + +**modifier:** Sealed + +**Strength:** Embedding + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** contains + +**multiplicity:** (1..1) + +##### Constraint Classes + +- [FormatDef](ecdbmeta.ecschema.md#formatdef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is defined in + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [FormatCompositeUnitDef](ecdbmeta.ecschema.md#formatcompositeunitdef) + +### PhenomenonOwnsUnits + +**typeName:** RelationshipClass + +**modifier:** Sealed + +**Strength:** Embedding + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** has + +**multiplicity:** (1..1) + +##### Constraint Classes + +- [PhenomenonDef](ecdbmeta.ecschema.md#phenomenondef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is of + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [UnitDef](ecdbmeta.ecschema.md#unitdef) ### PropertyHasCategory +**typeName:** RelationshipClass + Relates the property to its PropertyCategory. -**Class Type:** RelationshipClass +**modifier:** Sealed -**Relationship Class:** +**Strength:** Referencing -| | ConstraintClasses | Multiplicity | -|:---------|:------------------------|:-----------------------------------| -|**Source**|ECPropertyDef|(0..*)| -|**Target**|PropertyCategoryDef|(0..1)| -| | | | +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** is in + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [ECPropertyDef](ecdbmeta.ecschema.md#ecpropertydef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is used by + +**multiplicity:** (0..1) + +##### Constraint Classes + +- [PropertyCategoryDef](ecdbmeta.ecschema.md#propertycategorydef) ### PropertyHasEnumeration +**typeName:** RelationshipClass + ECPropertyKind::Enumeration: relates the property to its ECEnumeration. -**Class Type:** RelationshipClass +**modifier:** Sealed + +**Strength:** Referencing + +**strengthDirection:** Forward + +#### Source -**Relationship Class:** +**isPolymorphic:** false -| | ConstraintClasses | Multiplicity | -|:---------|:------------------------|:-----------------------------------| -|**Source**|ECPropertyDef|(0..*)| -|**Target**|ECEnumerationDef|(0..1)| -| | | | +**roleLabel:** is of type + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [ECPropertyDef](ecdbmeta.ecschema.md#ecpropertydef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is type of + +**multiplicity:** (0..1) + +##### Constraint Classes + +- [ECEnumerationDef](ecdbmeta.ecschema.md#ecenumerationdef) ### PropertyHasKindOfQuantity +**typeName:** RelationshipClass + ECPropertyKind::Primitive or ECPropertyKind::PrimitiveArray: relates the property to its KindOfQuantity. -**Class Type:** RelationshipClass +**modifier:** Sealed -**Relationship Class:** +**Strength:** Referencing -| | ConstraintClasses | Multiplicity | -|:---------|:------------------------|:-----------------------------------| -|**Source**|ECPropertyDef|(0..*)| -|**Target**|KindOfQuantityDef|(0..1)| -| | | | +**strengthDirection:** Forward -### PropertyHasNavigationRelationshipClassId +#### Source -ECPropertyKind::Navigation: relates the navigation property to its backing RelationshipECClass. +**isPolymorphic:** false -**Class Type:** RelationshipClass +**roleLabel:** has -**Relationship Class:** +**multiplicity:** (0..*) -| | ConstraintClasses | Multiplicity | -|:---------|:------------------------|:-----------------------------------| -|**Source**|ECPropertyDef|(0..*)| -|**Target**|ECClassDef|(0..1)| -| | | | +##### Constraint Classes -### PropertyHasStructType +- [ECPropertyDef](ecdbmeta.ecschema.md#ecpropertydef) -ECPropertyKind::Struct: relates the struct property to its struct ECClass. ECPropertyKind::StructArray: relates the struct array property to its array element type. +#### Target -**Class Type:** RelationshipClass +**isPolymorphic:** false -**Relationship Class:** +**roleLabel:** is used by -| | ConstraintClasses | Multiplicity | -|:---------|:------------------------|:-----------------------------------| -|**Source**|ECPropertyDef|(0..*)| -|**Target**|ECClassDef|(0..1)| -| | | | +**multiplicity:** (0..1) -### RelationshipConstraintHasAbstractConstraintClass +##### Constraint Classes + +- [KindOfQuantityDef](ecdbmeta.ecschema.md#kindofquantitydef) -**Class Type:** RelationshipClass +### PropertyHasNavigationRelationshipClassId -**Relationship Class:** +**typeName:** RelationshipClass -| | ConstraintClasses | Multiplicity | -|:---------|:------------------------|:-----------------------------------| -|**Source**|ECRelationshipConstraintDef|(0..*)| -|**Target**|ECClassDef|(0..1)| -| | | | +ECPropertyKind::Navigation: relates the navigation property to its backing RelationshipECClass. -### RelationshipConstraintHasClasses +**modifier:** Sealed -**Class Type:** RelationshipClass +**Strength:** Referencing -**Relationship Class:** +**strengthDirection:** Forward -| | ConstraintClasses | Multiplicity | -|:---------|:------------------------|:-----------------------------------| -|**Source**|ECRelationshipConstraintDef|(0..*)| -|**Target**|ECClassDef|(0..*)| -| | | | +#### Source -### RelationshipHasConstraints +**isPolymorphic:** false -**Class Type:** RelationshipClass +**roleLabel:** is backed by -**Relationship Class:** +**multiplicity:** (0..*) -| | ConstraintClasses | Multiplicity | -|:---------|:------------------------|:-----------------------------------| -|**Source**|ECClassDef|(1..1)| -|**Target**|ECRelationshipConstraintDef|(2..2)| -| | | | +##### Constraint Classes -### SchemaHasSchemaReferences +- [ECPropertyDef](ecdbmeta.ecschema.md#ecpropertydef) -**Class Type:** RelationshipClass +#### Target -**Relationship Class:** +**isPolymorphic:** false -| | ConstraintClasses | Multiplicity | -|:---------|:------------------------|:-----------------------------------| -|**Source**|ECSchemaDef|(0..*)| -|**Target**|ECSchemaDef|(0..*)| -| | | | +**roleLabel:** is ECRelationshipClass of -### SchemaOwnsClasses +**multiplicity:** (0..1) -**Class Type:** RelationshipClass +##### Constraint Classes -**Relationship Class:** +- [ECClassDef](ecdbmeta.ecschema.md#ecclassdef) -| | ConstraintClasses | Multiplicity | -|:---------|:------------------------|:-----------------------------------| -|**Source**|ECSchemaDef|(1..1)| -|**Target**|ECClassDef|(0..*)| -| | | | +### PropertyHasStructType -### SchemaOwnsEnumerations +**typeName:** RelationshipClass -**Class Type:** RelationshipClass +ECPropertyKind::Struct: relates the struct property to its struct ECClass. ECPropertyKind::StructArray: relates the struct array property to its array element type. -**Relationship Class:** +**modifier:** Sealed -| | ConstraintClasses | Multiplicity | -|:---------|:------------------------|:-----------------------------------| -|**Source**|ECSchemaDef|(1..1)| -|**Target**|ECEnumerationDef|(0..*)| -| | | | +**Strength:** Referencing -### SchemaOwnsKindOfQuantities +**strengthDirection:** Forward -**Class Type:** RelationshipClass +#### Source -**Relationship Class:** +**isPolymorphic:** false -| | ConstraintClasses | Multiplicity | -|:---------|:------------------------|:-----------------------------------| -|**Source**|ECSchemaDef|(1..1)| -|**Target**|KindOfQuantityDef|(0..*)| -| | | | +**roleLabel:** is of type -### SchemaOwnsPropertyCategories +**multiplicity:** (0..*) + +##### Constraint Classes + +- [ECPropertyDef](ecdbmeta.ecschema.md#ecpropertydef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is type of + +**multiplicity:** (0..1) + +##### Constraint Classes + +- [ECClassDef](ecdbmeta.ecschema.md#ecclassdef) + +### RelationshipConstraintHasAbstractConstraintClass + +**typeName:** RelationshipClass + +**modifier:** Sealed + +**Strength:** Referencing + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** defines + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [ECRelationshipConstraintDef](ecdbmeta.ecschema.md#ecrelationshipconstraintdef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is abstract constraint class of + +**multiplicity:** (0..1) + +##### Constraint Classes + +- [ECClassDef](ecdbmeta.ecschema.md#ecclassdef) + +### RelationshipConstraintHasClasses + +**typeName:** RelationshipClass + +**modifier:** Sealed + +**Strength:** Referencing + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** contains + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [ECRelationshipConstraintDef](ecdbmeta.ecschema.md#ecrelationshipconstraintdef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is constraint class of + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [ECClassDef](ecdbmeta.ecschema.md#ecclassdef) + +### RelationshipHasConstraints + +**typeName:** RelationshipClass + +**modifier:** Sealed + +**Strength:** Referencing + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** defines + +**multiplicity:** (1..1) + +##### Constraint Classes + +- [ECClassDef](ecdbmeta.ecschema.md#ecclassdef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is defined by + +**multiplicity:** (2..2) + +##### Constraint Classes + +- [ECRelationshipConstraintDef](ecdbmeta.ecschema.md#ecrelationshipconstraintdef) + +### SchemaHasSchemaReferences + +**typeName:** RelationshipClass + +**modifier:** Sealed + +**Strength:** Referencing + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** references + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [ECSchemaDef](ecdbmeta.ecschema.md#ecschemadef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is referenced by + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [ECSchemaDef](ecdbmeta.ecschema.md#ecschemadef) + +### SchemaOwnsClasses + +**typeName:** RelationshipClass + +**modifier:** Sealed + +**Strength:** Embedding + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** contains + +**multiplicity:** (1..1) + +##### Constraint Classes + +- [ECSchemaDef](ecdbmeta.ecschema.md#ecschemadef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is defined in + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [ECClassDef](ecdbmeta.ecschema.md#ecclassdef) + +### SchemaOwnsEnumerations + +**typeName:** RelationshipClass + +**modifier:** Sealed + +**Strength:** Embedding + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** contains + +**multiplicity:** (1..1) + +##### Constraint Classes + +- [ECSchemaDef](ecdbmeta.ecschema.md#ecschemadef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is defined in + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [ECEnumerationDef](ecdbmeta.ecschema.md#ecenumerationdef) + +### SchemaOwnsFormats + +**typeName:** RelationshipClass + +**modifier:** Sealed + +**Strength:** Embedding + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** contains + +**multiplicity:** (1..1) + +##### Constraint Classes + +- [ECSchemaDef](ecdbmeta.ecschema.md#ecschemadef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is defined in + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [FormatDef](ecdbmeta.ecschema.md#formatdef) + +### SchemaOwnsKindOfQuantities + +**typeName:** RelationshipClass + +**modifier:** Sealed + +**Strength:** Embedding + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** contains + +**multiplicity:** (1..1) + +##### Constraint Classes + +- [ECSchemaDef](ecdbmeta.ecschema.md#ecschemadef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is defined in + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [KindOfQuantityDef](ecdbmeta.ecschema.md#kindofquantitydef) + +### SchemaOwnsPhenomena + +**typeName:** RelationshipClass + +**modifier:** Sealed + +**Strength:** Embedding + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** contains + +**multiplicity:** (1..1) + +##### Constraint Classes + +- [ECSchemaDef](ecdbmeta.ecschema.md#ecschemadef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is defined in + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [PhenomenonDef](ecdbmeta.ecschema.md#phenomenondef) + +### SchemaOwnsPropertyCategories + +**typeName:** RelationshipClass + +**modifier:** Sealed + +**Strength:** Embedding + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** contains + +**multiplicity:** (1..1) + +##### Constraint Classes + +- [ECSchemaDef](ecdbmeta.ecschema.md#ecschemadef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is defined in + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [PropertyCategoryDef](ecdbmeta.ecschema.md#propertycategorydef) + +### SchemaOwnsUnitSystems + +**typeName:** RelationshipClass + +**modifier:** Sealed + +**Strength:** Embedding + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** contains + +**multiplicity:** (1..1) + +##### Constraint Classes + +- [ECSchemaDef](ecdbmeta.ecschema.md#ecschemadef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is defined in + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [UnitSystemDef](ecdbmeta.ecschema.md#unitsystemdef) + +### SchemaOwnsUnits + +**typeName:** RelationshipClass + +**modifier:** Sealed + +**Strength:** Embedding + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** contains + +**multiplicity:** (1..1) + +##### Constraint Classes + +- [ECSchemaDef](ecdbmeta.ecschema.md#ecschemadef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is defined in + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [UnitDef](ecdbmeta.ecschema.md#unitdef) + +### UnitHasInvertedUnit + +**typeName:** RelationshipClass + +**modifier:** Sealed + +**Strength:** Referencing + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** is inverted by + +**multiplicity:** (0..1) + +##### Constraint Classes + +- [UnitDef](ecdbmeta.ecschema.md#unitdef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** inverts + +**multiplicity:** (0..1) + +##### Constraint Classes + +- [UnitDef](ecdbmeta.ecschema.md#unitdef) + +### UnitSystemHasUnits + +**typeName:** RelationshipClass + +**modifier:** Sealed + +**Strength:** Referencing + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** has + +**multiplicity:** (0..1) + +##### Constraint Classes + +- [UnitSystemDef](ecdbmeta.ecschema.md#unitsystemdef) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is defined in + +**multiplicity:** (0..*) + +##### Constraint Classes + +- [UnitDef](ecdbmeta.ecschema.md#unitdef) + +## Enumerations + +### ECClassModifier + +**typeName:** Enumeration + +**Backing Type:** int + +**Strict:** true + +| Label | Value | +|:------------|:------------| +|None|0| +|Abstract|1| +|Sealed|2| + +### ECClassType + +**typeName:** Enumeration + +**Backing Type:** int + +**Strict:** true + +| Label | Value | +|:------------|:------------| +|Entity|0| +|Relationship|1| +|Struct|2| +|CustomAttribute|3| + +### ECCustomAttributeContainerType + +**typeName:** Enumeration + +**Backing Type:** int + +**Strict:** true + +| Label | Value | +|:------------|:------------| +|Schema|1| +|EntityClass|2| +|CustomAttributeClass|4| +|StructClass|8| +|RelationshipClass|16| +|AnyClass|30| +|PrimitiveProperty|32| +|StructProperty|64| +|PrimitiveArrayProperty|128| +|StructArrayProperty|256| +|NavigationProperty|512| +|AnyProperty|992| +|SourceRelationshipConstraint|1024| +|TargetRelationshipConstraint|2048| +|AnyRelationshipConstraint|3072| +|Any|4095| + +### ECPropertyKind + +**typeName:** Enumeration + +**Backing Type:** int + +**Strict:** true + +| Label | Value | +|:------------|:------------| +|Primitive|0| +|Struct|1| +|PrimitiveArray|2| +|StructArray|3| +|Navigation|4| + +### ECRelationshipDirection + +**typeName:** Enumeration + +**Backing Type:** int + +**Strict:** true + +| Label | Value | +|:------------|:------------| +|Forward|1| +|Backward|2| + +### ECRelationshipEnd + +**typeName:** Enumeration + +**Backing Type:** int + +**Strict:** true + +| Label | Value | +|:------------|:------------| +|Source|0| +|Target|1| + +### ECRelationshipStrength + +**typeName:** Enumeration + +**Backing Type:** int + +**Strict:** true + +| Label | Value | +|:------------|:------------| +|Referencing|0| +|Holding|1| +|Embedding|2| + +### PrimitiveType + +**typeName:** Enumeration + +**Backing Type:** int + +**Strict:** true + +| Label | Value | +|:------------|:------------| +|Binary|257| +|Boolean|513| +|DateTime|769| +|Double|1025| +|Integer|1281| +|Long|1537| +|Point2d|1793| +|Point3d|2049| +|String|2305| +|IGeometry|2561| + +### ECEnumeratorDef + +**typeName:** StructClass -**Class Type:** RelationshipClass +**modifier:** Sealed -**Relationship Class:** +#### Properties -| | ConstraintClasses | Multiplicity | -|:---------|:------------------------|:-----------------------------------| -|**Source**|ECSchemaDef|(1..1)| -|**Target**|PropertyCategoryDef|(0..*)| -| | | | +| Name | Description | Label | Category | Read Only | Priority | +|:-----------|:--------------|:------------|:-----------|:-----------------|:---------------| +|Name||||false|0| +|DisplayLabel||||false|0| +|Description||||false|0| +|IntValue||||false|0| +|StringValue||||false|0| diff --git a/docs/core/learning/ECSQLTutorial/MyDomain.ecschema.md b/docs/core/learning/ECSQLTutorial/MyDomain.ecschema.md index cf30c0e..2e6c352 100644 --- a/docs/core/learning/ECSQLTutorial/MyDomain.ecschema.md +++ b/docs/core/learning/ECSQLTutorial/MyDomain.ecschema.md @@ -1,104 +1,196 @@ --- +noEditThisPage: true Schema: MyDomain This file was automatically generated via ecjson2md. Do not edit this file. Any edits made to this file will be overwritten the next time it is generated --- # MyDomain ECSchema -## Classes +**alias:** mydomain + +**version:** 1.0.0 + +## Entity Classes ### Building -**Class Type:** EntityClass +**typeName:** EntityClass -**Base class:** [BisCore:SpatialLocationElement](../../bis/domains/BisCore.ecschema.md#spatiallocationelement) +**modifier:** Sealed + +**baseClass:** [BisCore:SpatialLocationElement](biscore.ecschema.md#spatiallocationelement) ### Device -**Class Type:** EntityClass +**typeName:** EntityClass + +**modifier:** Sealed -**Base class:** [BisCore:PhysicalElement](../../bis/domains/BisCore.ecschema.md#physicalelement) +**baseClass:** [BisCore:PhysicalElement](biscore.ecschema.md#physicalelement) ### DeviceType -**Class Type:** EntityClass +**typeName:** EntityClass -**Base class:** [BisCore:PhysicalType](../../bis/domains/BisCore.ecschema.md#physicaltype) +**displayLabel:** Device Type -**Class Properties:** +**modifier:** Abstract + +**baseClass:** [BisCore:PhysicalType](biscore.ecschema.md#physicaltype) + +#### Properties | Name | Description | Type | Extended Type | |:-----------|:------------------|:-----------|:-----------------------| |ManufacturerName||string|| -|Description||string|| ### FlameDetectorType -**Class Type:** EntityClass +**typeName:** EntityClass + +**displayLabel:** Flame Detector Type -**Base class:** [MyDomain:DeviceType](./mydomain.ecschema.md#devicetype) +**modifier:** Sealed -**Class Properties:** +**baseClass:** [MyDomain:DeviceType](#devicetype) + +#### Properties | Name | Description | Type | Extended Type | |:-----------|:------------------|:-----------|:-----------------------| |FlameDetectionTechnique||FlameDetectionTechnique|| +### NoteMultiAspect + +**typeName:** EntityClass + +**displayLabel:** Note + +**modifier:** Sealed + +**baseClass:** [BisCore:ElementMultiAspect](biscore.ecschema.md#elementmultiaspect) + +#### Properties + +| Name | Description | Type | Extended Type | +|:-----------|:------------------|:-----------|:-----------------------| +|Note||string|| + ### SmokeDetectorType -**Class Type:** EntityClass +**typeName:** EntityClass + +**displayLabel:** Smoke Detector Type + +**modifier:** Sealed -**Base class:** [MyDomain:DeviceType](./mydomain.ecschema.md#devicetype) +**baseClass:** [MyDomain:DeviceType](#devicetype) -**Class Properties:** +#### Properties | Name | Description | Type | Extended Type | |:-----------|:------------------|:-----------|:-----------------------| |SmokeDetectionTechnique||SmokeDetectionTechnique|| -| | | | | ### Space -**Class Type:** EntityClass +**typeName:** EntityClass -**Base class:** [BisCore:SpatialLocationElement](../../bis/domains/BisCore.ecschema.md#spatiallocationelement) +**modifier:** Sealed + +**baseClass:** [BisCore:SpatialLocationElement](biscore.ecschema.md#spatiallocationelement) ### Story -**Class Type:** EntityClass +**typeName:** EntityClass + +**modifier:** Sealed + +**baseClass:** [BisCore:SpatialLocationElement](biscore.ecschema.md#spatiallocationelement) + +### SyncInfoAspect + +**typeName:** EntityClass + +**displayLabel:** Provenance + +**modifier:** Sealed + +**baseClass:** [BisCore:ElementUniqueAspect](biscore.ecschema.md#elementuniqueaspect) + +#### Properties + +| Name | Description | Type | Extended Type | +|:-----------|:------------------|:-----------|:-----------------------| +|SyncId||string|| +|Checksum||string|| + +## Relationship Classes + +### StoryHasGeneratedDrawing + +**typeName:** RelationshipClass + +Tracks the relationship between a Story and a Drawing generated from that Story. + +**modifier:** None + +**baseClass:** [BisCore:ElementRefersToElements](biscore.ecschema.md#elementreferstoelements) + +**Strength:** Referencing + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** true + +**roleLabel:** has generated + +**multiplicity:** (0..1) + +##### Constraint Classes + +- [Story](mydomain.ecschema.md#story) + +#### Target + +**isPolymorphic:** true + +**roleLabel:** is generated from + +**multiplicity:** (0..1) + +##### Constraint Classes -**Base class:** [BisCore:SpatialLocationElement](../../bis/domains/BisCore.ecschema.md#spatiallocationelement) +- [Drawing](biscore.ecschema.md#drawing) ## Enumerations ### FlameDetectionTechnique -**Enumeration Properties:** +**typeName:** Enumeration -Name | Type | IsStrict ---- | --- | --- -FlameDetectionTechnique | int | true +**Backing Type:** int -**Enumerators:** +**Strict:** true -Name | Value ---- | --- -Optical | 0 -Ultraviolet | 1 -Infrared | 2 +| Label | Value | +|:------------|:------------| +||0| +||1| +||2| ### SmokeDetectionTechnique -**Enumeration Properties:** +**typeName:** Enumeration -Name | Type | IsStrict ---- | --- | --- -SmokeDetectionTechnique | int | true +**Backing Type:** int -**Enumerators:** +**Strict:** true -Name | Value ---- | --- -Photoelectric | 0 -Ionization | 1 -Combination | 2 +| Label | Value | +|:------------|:------------| +||0| +||1| +||2| diff --git a/docs/core/learning/IModelChange.ecschema.md b/docs/core/learning/IModelChange.ecschema.md index 5471fa7..bbfa8b0 100644 --- a/docs/core/learning/IModelChange.ecschema.md +++ b/docs/core/learning/IModelChange.ecschema.md @@ -1,34 +1,66 @@ --- +noEditThisPage: true Schema: IModelChange This file was automatically generated via ecjson2md. Do not edit this file. Any edits made to this file will be overwritten the next time it is generated --- # IModelChange ECSchema -## Classes +**alias:** imodelchange + +**version:** 2.0.0 + +## Entity Classes ### ChangeSet -**Class Type:** EntityClass +**typeName:** EntityClass + +**modifier:** Sealed -**Class Properties:** +#### Properties | Name | Description | Type | Extended Type | |:-----------|:------------------|:-----------|:-----------------------| -|Summary|||| +|Summary||[navigation](imodelchange.ecschema.md#changesummaryisextractedfromchangeset)|| |ParentWsgId||string|| |WsgId||string|| |Description||string|| |PushDate||dateTime|| |UserCreated||string|| +## Relationship Classes + ### ChangeSummaryIsExtractedFromChangeset -**Class Type:** RelationshipClass +**typeName:** RelationshipClass + +**modifier:** Sealed + +**Strength:** Embedding + +**strengthDirection:** Forward + +#### Source + +**isPolymorphic:** false + +**roleLabel:** is created from + +**multiplicity:** (1..1) + +##### Constraint Classes + +- [ChangeSummary](ecdbchange.ecschema.md#changesummary) + +#### Target + +**isPolymorphic:** false + +**roleLabel:** is source of + +**multiplicity:** (0..1) -**Relationship Class:** +##### Constraint Classes -| | ConstraintClasses | Multiplicity | -|:---------|:------------------------|:-----------------------------------| -|**Source**|ChangeSummary|(1..1)| -|**Target**|ChangeSet|(0..1)| +- [ChangeSet](imodelchange.ecschema.md#changeset) diff --git a/docs/core/learning/common/GeometryStream.md b/docs/core/learning/common/GeometryStream.md index 3547b4f..f01268b 100644 --- a/docs/core/learning/common/GeometryStream.md +++ b/docs/core/learning/common/GeometryStream.md @@ -60,11 +60,8 @@ The element aligned bounding box that is part of the placement is computed autom * The [TextStringProps.origin]($common) and [TextStringProps.rotation]($common) specify the relative location from the basis point (typically 0,0,0) used for the GeometricElement's geometry. * [BRepEntity.DataProps]($common) * Raw BRep data, not generally useful outside of a geometry export scenario. - * Must be specifically requested using [ElementLoadProps.wantBRepData]($common). - * When not requested will be returned as a [IModelJson.GeometryProps]($geometry) entry. - * A wire body will be represented as a [IModelJson.CurvePrimitiveProps]($geometry) or [IModelJson.CurveCollectionProps.path]($geometry). - * A planar sheet body will be represented as a [IModelJson.PlanarRegionProps]($geometry). - * A solid body or non-planar sheet body will be represented as a [IModelJson.IndexedMeshProps]($geometry). + * The BRep data must be specifically requested using [ElementLoadProps.wantBRepData]($common). + * The body type, body to local transform, and any face material attachments are returned when requested using [ElementLoadProps.wantGeometry]($common). * [LowAndHighXYZ]($geometry) * Store range of each geometric primitive to allow efficient filtering on range criteria without having to instantiate the geometry. * Useful for GeometryStreams containing more than one geometric primitive; for a single geometric primitive, the geometry range is the placement's bounding box. diff --git a/docs/core/learning/frontend/BuildingIModelJsModules.md b/docs/core/learning/frontend/BuildingIModelJsModules.md new file mode 100644 index 0000000..ec55554 --- /dev/null +++ b/docs/core/learning/frontend/BuildingIModelJsModules.md @@ -0,0 +1,358 @@ +# Building iModel.js Modules + +This article describes the tools provided by iModel.js to build frontend modules. In this context, frontend modules are JavaScript files (transpiled and webpacked from TypeScript source files) that are intended to run in either a browser or in an Electron application. Some background on iModel.js modules is provided in [Modularizing iModel.js](ModularizingIModelJs.md). + +## Types of Modules + +There are four types of modules in the iModel.js frontend ecosystem : + +1. System modules. The iModel.js frontend system is composed of a set of modules that perform functions such as geometry calculations, user interface customization, and coordinating remote procedure calls to iModel.js backend functions. Each such module is built using the technique described here. + +2. Application modules. An iModel.js application consists of a module that contains the code for its unique functionality, with calls into the iModel.js system modules that take advantage of the classes and methods provided by iModel.js. An application module is loaded by the browser (or Electron) at startup. + +3. Plugins. A plugin is a module that is designed to be loaded into an iModel.js application in a browser or Electron application that is already executing. The Plugin registers with the iModel.js system, and then has full access to the iModel.js API. A Plugin can be used in multiple iModel.js applications. See the [iModel.js Plugins](./Plugins.md) article for more information. + +4. Web Workers. [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) are also loaded at runtime, but they run in a separate JavaScript thread in the browser or Electron. Web Workers are used to offload computationally intensive tasks from the main JavaScript thread. They are restricted in the APIs that are available to them, and in particular, they are not allowed access to iModel.js APIs. + +## Copying vs Symlinking + +Applications need files that are built in other packages, including external modules, localization files, cursors, fonts, and other assets. These other files can be either copied or symlinked into the application's webresources directory. The default is to copy those files. However, if you are working on files within the iModel.js mono repository, symlinking is much better because you don't have to remember to rebuild the application after changing one of the modules that it uses. To use symlinking, set the environment variable BUILDIMODEL_SYMLINKS to any value. + +## Building a Module + +All three types of modules are built using the buildIModelJsModule script. The package.json file is used to invoke the buildIModelJsModule script and to configure its operation. + +## Invoking the buildIModelJsModule script + +As you are already aware, your package.json file includes a "scripts" property that is used by the "npm run" command to select a script. The "scripts" property is an object whose properties are the short names of the scripts, each of which has a value that is the actual command that npm launches. To use the buildIModelJsBuild.js script, the "@bentley/webpack-tools" package is included in the "devDependencies" property, and "buildIModelJsModule" is the value of the "build" property. For example: + +```json +{ + "name": "your-app", + ... + "scripts": { + "build": "buildIModelJsModule", + ... + }, + ... + "devDependencies": { + "@bentley/webpack-tools": "0.189.0-dev.16", + ... + }, +... +} +``` + +When you run the "npm install" command, it puts two files, "buildIModelJsConfig" and "buildIModelJsConfig.cmd" in the package's node_modules/.bin directory. That is where npm finds it. To invoke the buildIModelJsModule script to build your module, use the shell command: + +```shell +npm run build +``` + +Note: + +For modules that are in the iModel.js rush repository, using "buildIModelJsModule" alone will not work, because rush's symbolic linking mechanism doesn't put the "./bin" scripts from within the mono repository into the common/temp/node_modules/.bin directory. So the correct build entry for modules within that mono repository is: + +```json + "build": "node ./node_modules/@bentley/webpack-tools/bin/buildIModelJsModule.js" +``` + +In that case, you use "rush build" as you would expect. + +## Module Build Steps + +There are several steps in building a module, which are sequenced by the buildIModelJsModule script: + +1. Transpiling the TypeScript source code to JavaScript. Generally, TypeScript files in the "src" directory are transpiled into a parallel "lib" directory. +2. Copying any resources that are needed by the webpack step into the "lib" directory structure. Examples include style sheets, svg files, etc. +3. Webpacking the module. +4. For application modules, copying the external modules required into the directory from which the web server delivers files in response to HTTP requests. +5. For applications, building any Plugins or other submodules that are in the same package as the main application. +6. For Bentley applications only, creating the configuration file that accompanies the system modules. +7. For application localization testing, create pseudo-localized files to make it easy to spot strings that are not localizable. + +Not all steps are required by every module. In particular, application modules are more complicated, and they are usually the only ones that require the last four steps. + +## Package.json properties that control the module build + +The build is controlled by the contents of an iModelJs.buildModule property in package.json. That property is an object with one required property and a number of optional properties. + +The required property is type, and it must have a string containing "application", "system", "plugin", or "webworker". Here is an example of the start of an iModel.js module build specification in package.json: + +```json +{ + ... + "iModelJs": { + "buildModule": { + "type": "application", + ... + } + } + ... +} +``` + +### Transpiling TypeScript files + +The buildIModelJsModule script always attempts to transpile the typescript files in your project using the "tsc" command. The version of the TypeScript transpiler is defined by the "typescript" property in the "devDependencies" property of your package.json. Bentley recommends that you use the same version of TypeScript as is used by the iModel.js system modules. Generally, the files that are transpiled and the transpiler options are defined in a file called tsconfig.json in the root directory of your package (the directory where package.json resides). Bentley recommends that you examine the tsconfig.cfg files that are used for the iModel.js system modules and adopt the same pattern. To use the same transpile options that iModel.js uses, make an "extends" property in your tsconfig.json and set it to "../node_modules/@bentley/build-tools/tsconfig-base.json". + +```json +tsconfig.json + +{ + "extends": "../node_modules/@bentley/build-tools/tsconfig-base.json", + ... +} +``` + +You can specify arguments to the "tsc" command by adding a "tscOptions" property. For example: + +```json +package.json + +{ + ... + "iModelJs": { + "buildModule": { + ... + "tscOptions": "-b ./src/test", + ... + } + } + }, + ... +} +``` + +could be used to specify that you want to compile the ./src/test/tsconfig.json project with the TypeScript "build" option. Most of the time, all the options to the TypeScript transpiler are contained in tsconfig.json, and the tscOption option is not needed. + +### Copying Source Resources + +Often, some files from the source directory will have to be placed into the directory where the transpiled JavaScript files reside so that webpack can find them in the same relative path. For applications, files may need to be copied from source directories to the directory where web resources are staged. The "sourceResources" property specifies the source and destination for those files, which are either copied or symbolically linked from the source to the destination. The "sourceResources" property is an array of objects, each of which has a "source" and "dest" property. For example: + +```json + "iModelJs": { + "buildModule": { + "type": "application", + "sourceResources": [ + { + "source": "./src/**/*.scss", + "dest": "./lib" + }, + { + "source": "./public/**/*", + "dest": "./lib/webresources" + } + ], + ... + } + }, +``` + +This copies or symlinks all the .scss files in all subdirectories of the src directory to parallel directories in the lib directory, and copies or symlinks all of the files in all subdirectories of the public directory into the lib/webresources directory. In some cases, you might prefer that the resources be copied rather than linked even if the BUILDIMODEL_SYMLINKS environment variable is set. In that case, add a boolean "copy" property alongside the "source" and "dest" properties and set it to true. If the resource specified with a "copy" property would also be included in a sourceResources entry with a glob specification, make sure it appears earlier in the array of sourceResources. + +### Webpacking + +Webpacking consolidates the source files that make up a module into a single file and generates the code necessary to import symbols from external modules. Webpacking has a confusing array of options that control how the code is generated, but the buildIModelJsModule script configures all of those for you. All you have to provide a "webpack" property that specifies the entry point for your module, where the output is to be put, and the name of the bundle created by webpack. For applications, the bundleName should always be "main". For system modules, bundleName matches the name of the "barrel" file that consolidates the imports for the module. The bundleName for plugins should be something descriptive. For example: + +```json + "iModelJs": { + "buildModule": { + "type": "application", + ... + "webpack": { + "entry": "./lib/frontend/index.js", + "dest": "./lib/webresources", + "bundleName": "main", + "styleSheets": true, + "htmlTemplate": "./src/frontend/index.html" + }, + ... + } + } +``` + +In this case, there are two optional properties. "styleSheets" is a boolean property that is usable for all module types. If true, it indicates that webpack should add processing for .scss files. "htmlTemplate" is a property used only by application modules. It must point to the source for an html template file that is processed by webpack to encode the system modules required for the application and their versions (information that is used at runtime to load those system modules). For example, here is a typical index.html file: + +```html +index.html + + + + + + + + + + + + iModel.js Application + + + + + + + + + + +
+ + + + +``` + +As you can see from the comments in the file, the script tag that invokes IModelJsLoader contains a template that is replaced by webpack with a list of iModel.js system modules that are needed and their versions. + +### Copying External Modules to the Web resources directory + +For application modules only, buildIModelJsModule copies or symlinks all the external modules that are needed into the "dest" directory of the webpack property. No specific buildModule properties are needed for this step. + +### Building Submodules in the same package + +Sometimes, it might be desirable to put certain submodules, such as Plugins or Webworkers, in the same package as the application itself. The buildImodelJsModule script addresses this requirement through the use of a subModules property. It is an array of objects, each of which has "dest", "entry", and "bundleName" properties. Optionally, there can be a "type" property that can have the value "plugin" (the default), "webworker", or "system". Another optional property, "styleSheets", can appear as in the "webpack" property above. For example, the following plugins property specifies that a MeasurePoints plugin is built alongside the application. + +```json + "iModelJs": { + "buildModule": { + "type": "application", + ... + "plugins": [ + { + "dest": "./lib/webresources", + "entry": "./lib/frontend/plugins/MeasurePoints.js", + "bundleName": "MeasurePoints", + "type": "plugin" + } + ], + ... + } + }, + ... +``` + +### Creating a Configuration file + +For Bentley applications only, the "makeConfig" property is specified to create a config.json file that configures the server with the variables specified in the unpublished @bentley/imodeljs-config package. See examples in the rush repository for usage. + +### PseudoLocalizing json locale files + +For localization, iModel.js uses the popular i18next package. Localizable strings are defined in json files where the keys are the property names and the localizable strings are the values. See [Localization in iModel.js](./Localization.md) for an explanation. While an application is under development, it is useful to be able to easily tell whether a string that appears in the user interface has been properly set up for localization before sending the localization json files out for translation. A way of doing that while somewhat preserving readability of the UI is called pseudoLocalization. In that process, a separate locale, "en-pseudo" is created and all the vowels in the original strings are replaced with various accented variations. The buildIModelJsModule script performs that step if a "pseudoLocalize" property is provided. It should be an object with a "source" and "dest" property. For example: + +```html + "iModelJs": { + "buildModule": { + "type": "application", + ... + "pseudoLocalize": { + "source": "./lib/webresources/locales/en", + "dest": "./lib/webresources/locales/en-pseudo" + }, + ... + } + }, +``` + +tells the script to process all of the source files in the ./lib/webresources/locales/en directory and put the output files into ./lib/webresource/locales/en-pseudo. Generally, the "pseudoLocalize" property makes sense only for application and plugin modules. + +## Passing Arguments to buildIModelJsModule + +The buildIModelJsModule script takes these arguments + +```shell +Options: + --version Show version number [boolean] + --production, --prod, -p Build production version of module [boolean] + --verbose, -v Show detail level 1 [boolean] + --detail, -d Sets detail level (0 to 4) to reveal more about the build process [number] + --stats, -s Creates the webpack stats json file for system modules [boolean] + --help Show help [boolean] +``` + +Most of these options are self-explanatory. The "--production" argument tells the build script to create the production build of the module. The differences between a production build and the development build (which is the default) arise during the webpack step. Production builds are minified (eliminating comments and carriage returns, shortening variable names, etc.) and all the calls to the assert function imported from @bentleyjs-core are eliminated. Development builds of system modules are put into the "dev" subdirectory of the designated "dest" directory, while production builds are put into the "prod" subdirectory. Builds of application modules copy or symlink the required system modules from the appropriate subdirectory based on build type. + +The "--detail" argument requires a number between 0 and 4, (e.g., --detail=2). Detail level 1 logs the start and end of each build step (although the output can be a little confusing because some steps are executed in parallel). Detail level 2 additionally shows the output of the webpack process, detail level 3 adds a report of skipped steps, and detail level 4 shows substeps such as the copying or symlinking of individual files. Errors are always displayed, and cause the process exit code to be non-zero. + +The "--stats" argument affects the webpack step by passing the "--json" argument to it and directing webpack's output to a file called "webpackStats.json" in the same directory as the module. That can be useful for analyzing the packages used by the module as well as other webpack performance information. A downside to using that argument is that webpack also directs all errors to the webpackStats.json file, so if you encounter a webpack error with the --stats argument, look in webpackStat.json to get more information about the error. + +For packages that are not in the iModel.js mono repository, the build command is generally invoked using the "npm run build" command. To pass arguments to the script that npm invokes rather than to npm inself, you must put in a "separator" argument, "--" before the arguments that should be passed to the builIModelJsModule script. For example, to build a production version and show detail level 1, you would use this command: + +```shell +npm run build -- --production --detail=1 +``` + +For packages that are in the iModel.js rush mono repository, the build process is sequenced by the "rush build" command. In that case, the command line options that can be passed to the build command are determined by the rush configuration file "command-line.json". There are some restrictions in how the arguments can be structured, and arguments cannot be used that conflict with those that are processed by rush itself. That means that you must spell out the full "--production" argument rather than using the "-p" shortcut. To see the arguments using rush, execute "rush build --help", and this message is displayed: + +```shell +Found configuration in D:\bentleyjs\imodeljs\rush.json + + +Rush Multi-Project Build Tool 5.5.4 - https://rushjs.io + +Found configuration in D:\bentleyjs\imodeljs\rush.json + +usage: rush build [-h] [-p COUNT] [-t PROJECT1] + [--to-version-policy VERSION_POLICY_NAME] [-f PROJECT2] [-v] + [-o] [--production] [-s] [-d {0,1,2,3,4}] + + +This command is similar to "rush rebuild", except that "rush build" performs +an incremental build. In other words, it only builds projects whose source +files have changed since the last successful build. The analysis requires a +Git working tree, and only considers source files that are tracked by Git and +whose path is under the project folder. (For more details about this +algorithm, see the documentation for the "package-deps-hash" NPM package.) +The incremental build state is tracked in a file "package-deps.json" which +should NOT be added to Git. The build command is tracked by the "arguments" +field in this JSON file; a full rebuild is forced whenever the command has +changed (e.g. "--production" or not). + +Optional arguments: + -h, --help Show this help message and exit. + -p COUNT, --parallelism COUNT + Specify the number of concurrent build processes The + value "max" can be specified to indicate the number + of CPU cores. If this parameter omitted, the default + value depends on the operating system and number of + CPU cores. + -t PROJECT1, --to PROJECT1 + Run command in the specified project and all of its + dependencies + --to-version-policy VERSION_POLICY_NAME + Run command in all projects with the specified + version policy and all of their dependencies + -f PROJECT2, --from PROJECT2 + Run command in all projects that directly or + indirectly depend on the specified project + -v, --verbose Display the logs during the build, rather than just + displaying the build status summary + -o, --changed-projects-only + If specified, the incremental build will only rebuild + projects that have changed, but not any projects that + directly or indirectly depend on the changed package. + --production Sets production build for iModelJs modules + -s, --stats Stores the webpack json stats for iModelJs system + modules + -d {0,1,2,3,4}, --detail {0,1,2,3,4} + Selects the level of output when building iModelJs + modules. The default value is "0". +``` diff --git a/docs/core/learning/frontend/ExecutingECSQL.md b/docs/core/learning/frontend/ExecutingECSQL.md index 50e9397..1d4d1ff 100644 --- a/docs/core/learning/frontend/ExecutingECSQL.md +++ b/docs/core/learning/frontend/ExecutingECSQL.md @@ -2,7 +2,7 @@ ECSQL by itself is described in detail here: [ECSQL](../ECSQL) -A frontend ECSQL query is executed by calling [IModelConnection.executeQuery]($imodeljs-frontend). +A frontend ECSQL query is executed by calling [IModelConnection.query]($imodeljs-frontend). > On the frontend, only ECSQL SELECT statements can be executed. Data modification must be done through the API. diff --git a/docs/core/learning/frontend/Localization.md b/docs/core/learning/frontend/Localization.md index 2275ad2..3c3ecbb 100644 --- a/docs/core/learning/frontend/Localization.md +++ b/docs/core/learning/frontend/Localization.md @@ -6,7 +6,7 @@ Presenting information to the user in their preferred locale (language, date and String localization is handled in a conventional way. Rather than specifying strings directly, a "key" is passed to the IModelApp.i18n.translate method, which retrieves the corresponding string for the current locale for presentation to the user. -For that to work, the localization system needs a dictionary of key-to-string substitutions for each expected locale. That dictionary is spread over a number of JSON files that are placed into a locale-specific directory in the application's "public" folder on the server. The key consists of a namespace (which identifies the specific JSON file in the locale directory, and thus must be unique across all packages in use), followed by a semicolon, followed by a period delimited tag that identifies the object within the JSON file. +For that to work, the localization system needs a dictionary of key-to-string substitutions for each expected locale. That dictionary is spread over a number of JSON files that are placed into a locale-specific directory in the application's "public" folder on the server. The key consists of a namespace (which identifies the specific JSON file in the locale directory, and thus must be unique across all packages in use), followed by a colon, followed by a period delimited tag that identifies the object within the JSON file. For example, suppose you are developing an application called SafetyBase and you want to group information, warning, and error messages into a localization namespace. Name the JSON file SafetyBaseMessages.json, put it into the public/locales/en directory, and put the following JSON in it: diff --git a/tools/webpack/modules/ModularizingIModelJs.md b/docs/core/learning/frontend/ModularizingIModelJs.md similarity index 100% rename from tools/webpack/modules/ModularizingIModelJs.md rename to docs/core/learning/frontend/ModularizingIModelJs.md diff --git a/docs/core/learning/frontend/Plugins.md b/docs/core/learning/frontend/Plugins.md new file mode 100644 index 0000000..6ed62da --- /dev/null +++ b/docs/core/learning/frontend/Plugins.md @@ -0,0 +1,81 @@ +# iModel.js Plugins + +A [Plugin]($frontend) is a separately webpacked JavaScript module that is loaded on demand into a browser (or Electron) running the the frontend iModel.js environment. + +A [Plugin]($frontend) is typically written in Typescript. It has access to all classes in the iModel.js host environment. In all such hosts, that will always include +bentleyjs-core, geometry-core, imodeljs-i18n, imodeljs-clients, imodeljs-common, imodeljs-quantity, and imodeljs-frontend. In hosts that use the iModel.js user interface +classes, it will also include ui-core, ui-components, ui-ninezone, and ui-framework. In hosts that are designed to format and display EC data, the presentation-common, +presentation-components, and presentation-frontend modules will be available as well. An example host is Design Review, which includes all of the modules above. + +## Loading Plugins + +A [Plugin]($frontend) can be loaded by calling the `loadPlugin` static method of the [PluginAdministrator]($frontend) class. An [ImmediateTool]($frontend) is provided that allows +a user to initiate [Plugin]($frontend) loading using the "Plugin \ [argument list]" keyin. + +When the [PluginAdmin]($frontend) loadPlugin method is called, it initiates loading of the designated [Plugin]($frontend). The [Plugin]($frontend) is loaded as a typical JavaScript script, +so any code that is outside the definition of a function or class is immediately executed. By convention, a [Plugin]($frontend) defines a "main" function and executes that main function as the +last line in the script. Generally, the only responsibility of the main function is to instantiate the subclass of [Plugin]($frontend) defined therein and register it with the [PluginAdmin]($frontend). Here is an example: + +```ts +declare var IMODELJS_VERSIONS_REQUIRED: string; +declare var PLUGIN_NAME: string; + +// define and run the entry point +function main() { + PluginAdmin.register(new MeasurePointsPlugin(PLUGIN_NAME, IMODELJS_VERSIONS_REQUIRED)); +} + +main(); +``` + +In the code above, note the declaration of IMODELJS_VERSIONS_REQUIRED and PLUGIN_NAME. These variables are converted to actual strings when your [Plugin]($frontend) is webpack'ed as described below. +PLUGIN_NAME is used by the [PluginAdmin]($frontend) to keep track of loaded Plugins, and IMODELJS_VERSIONS_REQUIRED is used to validate that the runtime environment has the iModel.js modules +that the [Plugin]($frontend) needs and that the versions loaded are compatible with those required by the [Plugin]($frontend). + +## Building Plugins + +The code for the [Plugin]($frontend) is typically transpiled to JavaScript and then converted to a loadable module with [webpack](https://webpack.js.org). iModel.js provides a +webpack configuration file, webpackModule.config.js, in the modules directory of the webpack-tools package. You provide arguments to webpack-cli that are passed to webpackModule.config.js : + * --env.outdir=[outdir] specifies the output directory for the webpacked [Plugin]($frontend). + * --env.entry=[entryScript] specifies the (transpiled) source for the entry point of the Plugin. + * --env.bundlename=[moduleName] specifies the name of webpacked the output file. + * --env.isplugin tells webpackModule.config.js that you are creating a [Plugin]($frontend), which configures webpack to define the PLUGIN_NAME and IMODELJS_VERSIONS_REQUIRED variables that are used + in the startup code as shown above. + +The versions of iModel.js packages that are required by the [Plugin](%frontend) are extracted from the "dependencies" key in the package.json file that is in the root directory of the package that contains the Plugin source. Each iModel.js module from which classes or functions are imported should appear in that dependencies key, along with the version number as is required by [npm](https://docs.npmjs.org). A string that contains each such dependency is generated by webpack and replaces every occurrence of IMODELJS_VERSIONS_REQUIRED in the output JavaScript module. + +## Plugin startup + +Plugins are loaded by the PluginAdmin class. The "main" function in the [Plugin]($frontend) code should instantiate and register the Plugin as illustrated above. The PluginAdmin.register method +checks whether the iModel.js runtime environment is compatible with the [Plugin]($frontend)'s requirements. If not, an error is displayed to the user (and a list of the errors encountered is +returned from the call to PluginAdmin.register) and the Plugin can proceed no further. If the validation succeeds, the `onLoad` method of your [Plugin]($frontend) subclass is called. The argument to the onLoad and onExecute method is an array of strings. The first member of the array is the plugin name. A typical use of the `onLoad` method is to register any tools that the Plugin provides. Here is a simple subclass of [Plugin]($frontend): + +```ts +class MeasurePointsPlugin extends Plugin { + private _measureNamespace: I18NNamespace | undefined; + + public constructor(name: string, versionsRequired: string) { + super(name, versionsRequired); + this._measureNamespace = undefined; + } + + public onLoad(_args: string[]): void { + // don't register the namespace and the tool until the onLoad method. That's called after we know the versions of the modules required are good. + this._measureNamespace = IModelApp.i18n.registerNamespace("MeasureTool"); + this._measureNamespace.readFinished.then(() => { MeasurePointsTool.register(this._measureNamespace); }) + .catch((err) => { console.log(err); }); + } + + public onExecute(_args: string[]): void { + // don't start the tool until the localized strings are available. + this._measureNamespace!.readFinished.then(() => { + // start the tool. + IModelApp.tools.run("Measure.Points"); + }); + } +} +``` + +The Plugin subclass' onExecute method is called immediately after the call to `onLoad`. The difference between `onLoad` and `onExecute` is that `onExecute` is called each time +[PluginAdmin]($frontend).loadPlugin is called for the same plugin. Therefore, anything that need be done only once (such as registering a Tool), should go into the `onLoad` method and everything that should be done each time the Plugin is loaded (in this case, starting the registered Tool), should be done in the `onExecute` method. + diff --git a/docs/core/learning/frontend/index.md b/docs/core/learning/frontend/index.md index 207cb59..4ecf0b4 100644 --- a/docs/core/learning/frontend/index.md +++ b/docs/core/learning/frontend/index.md @@ -31,6 +31,8 @@ These packages provide the following functions that a frontend requires: * Displaying [Views](./Views.md) of iModels * Executing [ECSQL queries](./ExecutingECSQL.md) on iModels * Storing [Settings](./Settings.md) for Applications, Projects, and iModels. +* Implementing [Plugins](./Plugins.md) +* Building [Modules](./BuildingIModelJsModules.md) ## Web browser compatibility diff --git a/docs/core/learning/guidelines/release-tags-guidelines.md b/docs/core/learning/guidelines/release-tags-guidelines.md new file mode 100644 index 0000000..e0c3589 --- /dev/null +++ b/docs/core/learning/guidelines/release-tags-guidelines.md @@ -0,0 +1,170 @@ +# Release Tags + +Release tags are used to classify API items according to their intended level of support. +Release tags are included in standard documentation comments and are processed by tools such as [api-extractor](https://api-extractor.com/) to produce an API review file. +API review files can then be placed under source code control and compared to future SDK versions to ensure that the API is evolving in compatible or expected ways. + +## Supported Release Tags + +The supported release tags are: + +* `@public` +* `@beta` +* `@alpha` +* `@internal` +* `@deprecated` + +> Note: The `@hidden` release tag is also currently supported, but all uses should transition to `@internal` or `@alpha` so that it can be de-supported. + +Details about each tag are below. + +### @public + +The `@public` release tag indicates that an API item has been officially released. +This means that the API item is part of the *supported contract* and affects the semantic version of the package. +*Public* API items must be supported through the entire major release lifetime and can only evolve in compatible ways (for example, an additional default argument to a function) during the current major release. + +### @beta + +The `@beta` release tag indicates that an API item has been released in an experimental state. +Third parties are encouraged to try it and provide feedback. +However, *beta* API items should **NOT** be used in production as they still may change going forward. +*Beta* API items are not part of the *supported contract* and changes to these API items do not follow the normal semantic versioning rules. +*Beta* API items and the comment after the `@beta` tag are included in the public SDK documentation. + +### @alpha + +The `@alpha` release tag indicates that an API item is eventually intended to be public, but currently is in an early stage of development. +Third parties should not use *alpha* APIs in production or otherwise as they are likely to change going forward. +*Alpha* API items are not part of the *supported contract* and changes to these API items do not follow the normal semantic versioning rules. +*Alpha* API items are intentionally hidden from the public SDK documentation. + +### @internal + +The `@internal` release tag indicates that an API item is **never** meant to be *public* and is meant only for usage by other NPM packages from the same maintainer. +Third parties should never use *internal* APIs. +*Internal* API items are intentionally hidden from the public SDK documentation. +However, *internal* API items are effectively *public* from the maintainers perspective, so should follow the same evolution rules as *public* API items if at all possible. + +> Note: This definition of `@internal` requires us to set the `--stripInternal` [option of the TypeScript compiler](http://www.typescriptlang.org/docs/handbook/compiler-options.html) to `false`. + +### @hidden + +As mentioned above, all uses of the `@hidden` tag should transition to either `@internal` or `@alpha`. +Both `@internal` and `@alpha` will cause the API item to be hidden from the public SDK documentation. +The distinction is that *internal* indicates that the API item is never meant to be public, while *alpha* API items are not yet ready for public feedback but are intended to be public in the future. + +### @deprecated + +The `@deprecated` release tags is used for API items that were formerly `@public` but are no longer optimal. +Third parties should avoid *deprecated* API items if possible as they will likely be removed in the next major release. +From the maintainer perspective, *deprecated* API items follow the same rules as *public* API items within the current major release. +*Deprecated* API items and the comment after the `@deprecated` tag are included in the public SDK documentation. + +### Release Tag Summary + +The following table summarizes the affects of each release tag: + +Release Tag | Affects Package Semantic Version | Included in Public SDK Documentation +------------|----------------------------------|------------------------------------- +`@public` | Yes | Yes +`@beta` | No | Yes +`@alpha` | No | No +`@internal` / `@hidden` | Yes | No +`@deprecated` | Yes | Yes + +## API Items + +An API Item is an **exported** TypeScript item that includes: + +* Classes +* Class Members +* Namespaces +* Namespace Members +* Interfaces +* Types +* Enums +* Enum Members + +Here are the guidelines for when a release tag is needed: + +> Note: Non-exported TypeScript items should not have release tags. + +Exported API Item | Release Tag Guidelines +------------------|----------------------- +Class | Always. The presence of a release tag indicates some thought was given while the absence of a release tag is ambiguous. +Class Member | Only if different than the containing class. +Namespace | Always. +Namespace Member | Only if different than the containing namespace. +Interface | Always. +Type | Always. +Enum | Always. +Enum Member | Only if different than the containing enum. + +> Note: Members cannot *expand* the scope of their container. +For example, it is invalid to have a `@public` member within an `@alpha` or `@beta` container. +It is also invalid to have a `@public` member within a `@deprecated` container. + +## Release Tag Progression for API Items + +### Initial Development Workflow + +It is typical for a new API item to start with the `@alpha` release tag. +This indicates that the API item is in prototype form and is not ready for public feedback. +*Alpha* API items are hidden from the public SDK documentation since public feedback is not desired. +However, private/targeted feedback can be obtained since *alpha* API items are actually included in the published package. + +Once the developer has more confidence in the API item, it can be marked with the `@beta` release tag. +This indicates that public feedback is desired, but that changes may happen based on that feedback. + +Once the API item has been proven in the desired scenarios and the developer is willing to maintain compatibility long term (an absolute minimum of the current major release), the API item can be marked with `@public`. + +> Note: It is not recommended for API items to go straight to `@public` without public feedback. Even if the implementation is straightforward and meets all requirements, there is always the possibility that the wrong name was chosen. +However, skipping the `@alpha` step is common when the level of uncertainty is low. + +### Deprecation Workflow + +Sometimes a better way of doing something is discovered after the initial approach was made public. +This is when the deprecation workflow is applicable. +In this case, the existing `@public` API item is marked `@deprecated` with documentation that specifies the new approach. +The *deprecated* API item must be maintained for the current major release and can only be considered for removal in the next major release. + +## Style Guidelines + +The release tag should be on its own line: + +```ts +/** Sample description of an exported API item. + * @public + */ +``` + +*Internal* API items should include documentation that indicates why it is not *public* and what third parties should use instead. + +```ts +/** Private explanation of why the API item is marked internal as a reminder to the maintainer or notice to someone perusing the source code. + * @see Other public API item that third parties should use instead. + * @internal Comments can also go here. + */ +``` + +Likewise, *deprecated* API items should include documentation that indicates what third parties should use instead. + +```ts +/** Original documentation comment is typically maintained here. + * @see Other public API item that third parties should use instead. + * @deprecated Comment describing reason API item is deprecated and what should be done instead. + */ +``` + +*Beta* and *alpha* API items should include a short reason for that classification. + +```ts +/** Sample description of a beta API item. + * @beta Comment describing reason API item is beta that will be included in the public SDK documentation. + */ +``` + +```ts + /** @alpha Private comment reminding maintainer why API item was marked alpha. */ +``` diff --git a/docs/core/learning/guidelines/semantic-versioning-guidelines.md b/docs/core/learning/guidelines/semantic-versioning-guidelines.md index 355e558..ba20521 100644 --- a/docs/core/learning/guidelines/semantic-versioning-guidelines.md +++ b/docs/core/learning/guidelines/semantic-versioning-guidelines.md @@ -19,9 +19,13 @@ Given a `MAJOR.MINOR.PATCH` version number, a package producer must increment th - `MINOR` version when the public API has changed in a backwards-compatible way - `PATCH` version when you make backwards-compatible bug fixes +> Note: A `MAJOR` version of `0` is for initial development and indicates that the API is not yet stable. +The rules below only apply when the `MAJOR` version is >= 1. + ### When to increment the MAJOR version Examples of incompatible changes that require incrementing the `MAJOR` version include: + - Renaming a class or method - Removing a class or method - Changing the signature of a method @@ -33,6 +37,7 @@ Note that incrementing the `MAJOR` version should reset the `MINOR` and `PATCH` ### When to increment the MINOR version Examples of backwards-compatible changes that require incrementing the `MINOR` version include: + - Adding a new class - Adding a new method - Adding a new optional parameter to a method @@ -42,6 +47,7 @@ Note that incrementing the `MINOR` version should reset the `PATCH` version to ` ### When to increment the PATCH version Examples of backwards-compatible changes that require incrementing the `PATCH` version include: + - Making a bug fix - Updating documentation - Adding a static resource to the published package @@ -58,6 +64,6 @@ These dependencies are maintained in the `package.json` of the consumer. | Dependency Type | Example | Meaning | |-----------------------|--------------------------------|---------| -| MINOR and PATCH range | "example-dependency": "^1.2.3" | Accept version 1.2.3 or greater with a matching `MAJOR` version. | -| PATCH range | "example-dependency": "~1.2.3" | Accept version 1.2.3 or greater with matching `MAJOR` and `MINOR` versions. | +| MINOR and PATCH range | "example-dependency": "^1.2.3" | Accept version 1.2.3 or greater with a matching `MAJOR` version. | +| PATCH range | "example-dependency": "~1.2.3" | Accept version 1.2.3 or greater with matching `MAJOR` and `MINOR` versions. | | Exact | "example-dependency": "1.2.3" | Accept only version 1.2.3 | diff --git a/docs/core/learning/iModelHub/Permissions.md b/docs/core/learning/iModelHub/Permissions.md index 8ff55b5..399a5fa 100644 --- a/docs/core/learning/iModelHub/Permissions.md +++ b/docs/core/learning/iModelHub/Permissions.md @@ -1,5 +1,5 @@ # Permissions -Every iModelHub operation requires that user would be authorized to perform it. iModelHub uses Role Based Access Control (RBAC) service to manage authorization. RBAC allows to create roles with a chosen set of permissions. Every user can be assigned one of these roles. RBAC permissions are configured per [Project]($clients). You can access RBAC permissions management through [CONNECT portal](https://connect.bentley.com). You can also query permissions for a specific Project through [RbacClient]($clients). +Every iModelHub operation requires that user would be authorized to perform it. iModelHub uses Role Based Access Control (RBAC) service to manage authorization. RBAC allows to create roles with a chosen set of permissions. Every user can be assigned one of these roles. RBAC permissions are configured per [Project]($clients). You can access RBAC permissions management through [CONNECT portal](https://connect.bentley.com). iModelHub uses 6 permissions: 1. [Create iModel](#create-imodel) diff --git a/docs/presentation/learning/ECExpressions.md b/docs/presentation/learning/ECExpressions.md index 106c209..97ca68b 100644 --- a/docs/presentation/learning/ECExpressions.md +++ b/docs/presentation/learning/ECExpressions.md @@ -13,6 +13,211 @@ symbol `ContextC`. And the property value is a context that contains a function symbol `DoSomething()`. The latter function might return some value or return nothing, but instead do some action - it all depends on the symbol. +## ECExpression Syntax + +### Data types + +`ECExpressions` contain values of primitive types or data type of `ECClass` or `ECArray`. + +#### Primitive value types + +Type | Description | Example +----------------|-----------------------------|-------------- +Boolean | Boolean (true/false) value | `True`, `False` +DateTime | Date and time in ticks | `@1549278124937` +Integer (Long) | 32-bit (64-bit) number | `1` +Double | Floating point number | `6.84` +String | String of characters | `""` ,`"Dog and Cat"` +Null | Null value | `Null` + +#### Other primitive types + +Type | Description +----------------|----------------------------- +Binary | Array of bytes +Point2d | 2D point containing X and Y components as double values +Point3d | 3D point containing X, Y and Z components as double values +IGeometry | A common geometry value of any type + +#### ECClass data type + +`ECClass` data types define `ECInstances`. `ECClasses` contain `ECProperties` and Methods which can be accessed via dot (`.`) operator. + +#### ECArray data type + +`ECArray` data types define arrays of values. Each value can be accessed via `[]` operator. + +### Expression components + +In addition to values of primitive types `ECExpressions` may contain symbols and operators + +#### Symbols + +Symbols are used to supply values to expression evaluator. Symbols can be supplied from `ECInstance` via an access string, from an application defined value or from application defined method. +Access string is a limited expression that holds no blanks, no operators, and no variable portions. It may contain array indexing and member selection using `[]` or dot `.` operators, but no part of the expression can be variable, and the access string cannot contain embedded blanks. + +##### Property symbols + +`ECInstance` Property symbols supply Property values of some `ECInstance` when the symbol is being evaluated. + +``` +this.PropertyName +this.StructPropertyName.PropertyName +this.StructArray[5].Struct.PropertyName +``` + +##### Value symbols + +Value symbols supply some named predefined value in a context. + +``` +System.Math.PI +``` + +##### Method symbols + +Method symbols supply a named method in a context. + +``` +System.Math.Sin(1.57) +this.IsOfClass("ClassName", "SchemaName") +``` + +#### Operators + +##### Parentheses `(`,`)` + +``` +(2 * (3 + 4)) +``` + +##### Logical operators + +Description | Operator +----------------------|-------------- +Conjunction | `And`*, `AndAlso` +Disjunction | `Or`*, `OrElse` +Exclusive disjunction | `Xor` +Negation | `Not` + +**Note:** *Checks right side of expression even if result value can be deducted from the left side. + +``` +False And True OrElse True +``` + +##### Comparison operators + +Description | Operator +--------------------------|-------------- +Less than | `<` +Less than or equal to | `<=` +Greater than | `>` +Greater than or equal to | `>=` +Equal to | `=` +Not equal to | `<>` + +``` +20 < 10 ==> False +``` + +##### Arithmetic operators + +Description | Operator +------------------|-------------- +Exponentation | `^` +Multiplication | `*` +Double division | `/` +Integer division | `\` +Modular division | `Mod` +Addition | `+` +Subtraction | `-` + +``` +1 + "4" ==> 5 +2.2 * 3 ==> 6.6 +12 / 5 ==> 2.4 +12 \ 5 ==> 2 +25 Mod 3 ==> 1 +``` + +##### Bit Shift operators + +Description | Operator +--------------------|-------------- +Signed shift left | `<<` +Signed shift right | `>>` +Unsigned shift right| `>>>` + +``` +5 << 1 ==> 10 +24 >> 2 ==> 6 +-105 >> 1 ==> -53 +-105 >>> 1 ==> 75 +``` + +##### Conditional operator + +``` +IIf (condition, true-result, false-result) +``` + +Returns result based on given condition. If condition evaluates to true, returns `true-result`. Otherwise returns `false-result`. + +``` +IIf (500>200, "Math ok", "Math wrong") ==> "Math ok" +IIf (500<200, "Math ok", "Math wrong") ==> "Math wrong" +``` + +##### Concatenation operator + +``` +"Dog" & " and " & "Cat" ==> "Dog and Cat" +"1" & 4 ==> "14" +1 & "4" ==> "14" +``` + +##### Lambda operator + +Lambda operator `=>` creates a callback expression for a given symbol. + +``` +MyArray.Find(item => item.IntProperty = 5) +``` + +### Combined expressions + +``` +System.Math.Cos (System.Math.PI * 45.0 / 180.0) ==> 0.707 +System.String.Length ("Dog" & " and " & "Cat") ==> 11 +``` + +### Evaluating if property is `Null` + +Method `IsNull (value)` evaluates if given value is `Null`. + +Method `IfNull (value, value-if-null)` evaluates to `value` if it is not null, otherwise evaluates to `value-if-null`. + +In case `this.MiddleName` is null: + +``` +IfNull (this.MiddleName, "") ==> "" +IIf (Not IsNull(this.MiddleName), " " & this.MiddleName & " ", "") ==> "" +``` + +In case `this.MiddleName` is set to `"Harvey"` + +``` +IfNull (this.MiddleName, "") ==> "Harvey" +IIf (Not IsNull (this.MiddleName), " " & this.MiddleName & " ", "") ==> " Harvey " +``` + +Checking to see if a `this.MiddleName` is `Null` or empty + +``` +IIf (IsNull (this.MiddleName) or this.MiddleName = "", "Is null or empty", "Has a value") +``` + ## ECExpression Contexts ### NavNode diff --git a/docs/presentation/learning/unified-selection/Terminology.md b/docs/presentation/learning/unified-selection/Terminology.md index 54802aa..985a38f 100644 --- a/docs/presentation/learning/unified-selection/Terminology.md +++ b/docs/presentation/learning/unified-selection/Terminology.md @@ -1,25 +1,30 @@ -# Unified Selection -related Terminology - -## Selection Level - -Unified selection API allows having multiple nested selection levels. In all -cases lower levels are somehow related to their above level. As a result, -changing a level clears selection of all lower levels. - -Example: -1. User selects a *Model* node in the tree. That's **selection level 0**. -2. Content components react to selection change: - - Grid shows all model *Elements* - - Graphics view highlights all model *Elements* - - Property pane shows properties of selected *Model* -3. User selects one of the *Element* instances in the grid. That's -**selection level 1**. -4. Content components react to selection change: - - Grid still shows all model *Elements* as it reacts only to top level - selection. - - Graphics view still highlights all model *Elements*, but the one in - 'selection level 1' is distinguished with a border or a different color. - - Property pane shows properties of the *Element* selected with 'selection - level 1'. -5. Selecting a different node in the tree changes 'selection level 0', thus -clearing 'selection level 1'. +# Unified Selection -related Terminology + +## Selection Scope + +Selection scopes allow decoupling of what gets picked and what gets selected. For example, +a *model* selection scope selects all elements in a model when you pick only one of them. + +## Selection Level + +Unified selection API allows having multiple nested selection levels. In all +cases lower levels are somehow related to their above level. As a result, +changing a level clears selection of all lower levels. + +Example: +1. User selects a *Model* node in the tree. That's **selection level 0**. +2. Content components react to selection change: + - Grid shows all model *Elements* + - Graphics view highlights all model *Elements* + - Property pane shows properties of selected *Model* +3. User selects one of the *Element* instances in the grid. That's +**selection level 1**. +4. Content components react to selection change: + - Grid still shows all model *Elements* as it reacts only to top level + selection. + - Graphics view still highlights all model *Elements*, but the one in + 'selection level 1' is distinguished with a border or a different color. + - Property pane shows properties of the *Element* selected with 'selection + level 1'. +5. Selecting a different node in the tree changes 'selection level 0', thus +clearing 'selection level 1'. diff --git a/docs/ui/config/docSites.json b/docs/ui/config/docSites.json index b93f67b..f4bec5a 100644 --- a/docs/ui/config/docSites.json +++ b/docs/ui/config/docSites.json @@ -11,6 +11,10 @@ "docSiteId": 3, "displayName": "API Reference" }, + "demo": { + "docSiteId": 4, + "displayName": "Demo" + }, "metadata": { "home": "getting-started", "logo": "getting-started/assets/imodeljs-logo.svg", @@ -19,19 +23,20 @@ "docSiteOrder": [ "getting-started", "learning", - "reference" + "reference", + "demo" ], "headerTabs": { "Getting Started": "getting-started", "Learning": "learning", - "API Reference": "reference" + "API Reference": "reference", + "Demo": "demo" }, "packageAliases": { "components": "ui-components", "core": "ui-core", "framework": "ui-framework", - "ninezone": "ui-ninezone", - "bwc": "bwc" + "ninezone": "ui-ninezone" } } } \ No newline at end of file diff --git a/docs/ui/config/layouts/demoPage.html b/docs/ui/config/layouts/demoPage.html new file mode 100644 index 0000000..d419702 --- /dev/null +++ b/docs/ui/config/layouts/demoPage.html @@ -0,0 +1,86 @@ +{{>header}} + + +
+ + + + + +{{>footer}} \ No newline at end of file diff --git a/docs/ui/demo/index.md b/docs/ui/demo/index.md new file mode 100644 index 0000000..4746126 --- /dev/null +++ b/docs/ui/demo/index.md @@ -0,0 +1,3 @@ +--- +layout: 'demoPage.html' +--- \ No newline at end of file diff --git a/docs/ui/learning/bwc/index.md b/docs/ui/learning/bwc/index.md deleted file mode 100644 index 574c9fd..0000000 --- a/docs/ui/learning/bwc/index.md +++ /dev/null @@ -1,7 +0,0 @@ -# The BWC Library - -The BWC library package contains a Sass/SCSS library and TypeScript based React components for building standardized Bentley user interfaces. - -Topics: - -* [Placeholder](./Placeholder.md) diff --git a/docs/ui/learning/framework/ToolSettings.md b/docs/ui/learning/framework/ToolSettings.md new file mode 100644 index 0000000..bd60eee --- /dev/null +++ b/docs/ui/learning/framework/ToolSettings.md @@ -0,0 +1,28 @@ +# Tool Settings + +'Tool Settings' refers to the UI widget that is shown in top center zone of the application window and the UI components displayed there. These UI component allow users to control settings/properties used by the "active" tool. Tools have two options for populating the Tool Settings widget. The tool can register their own React-based ToolUiProvider to display the UI components for its settings. An alternate approach is for the tool to publish information about the properties it uses for settings and allow the 'DefaultToolSettings' provider to generate the necessary UI components. + +## Default ToolSettings Provider + +Any Tool derived from Interactive tool can implement the method supplyToolSettingsProperties to supply an array of 'ToolSettingsPropertyRecord' objects that define the property definitions and its position with a grid layout. The default ToolSettings provider will then automatically generate a type editor for the type of data required and show that editor in the row and column specified. Unless suppressed via an EditorParams a label will be generated using the displayLabel for the property and shown in the column to the left of the editor. The editor can occupy multiple columns in the layout grid by specifying a columnSpan value in the properties editor position parameters. The default columnSpan value is 1. + +### Informing Tool of property changes + +When a user interacts with any of the generated type editors, in the Tool Settings Widget, the changes are sent back to the active tool by calling its applyToolSettingPropertyChange method and passing it the name of the property that changed and the new value of the property. + +### Informing the UI of property changes made by the Tool + +If the 'Active' Tool updates a property that is being displayed by a type editor in the Tool Settings Widget, it can call its syncToolSettingsProperties method and supply an array of ToolSettingsPropertySyncItem objects to specify new values to display in the UI. This is commonly done when the Tool is in a dynamics loop and recalculating values to display in the UI. + +### Classes and Interfaces used the the Default Tool Settings Provider + +The following classes defined within the imodeljs-frontend package are used by the Default Tool Settings Provider. + +* ToolSettingsPropertyRecord +* PropertyRecord +* PropertyDescription +* ToolSettingsValue +* ToolSettingsPropertySyncItem +* EditorPosition +* PropertyEditorParamTypes +* PropertyValue \ No newline at end of file diff --git a/docs/ui/learning/framework/index.md b/docs/ui/learning/framework/index.md index 92535d7..99d0caf 100644 --- a/docs/ui/learning/framework/index.md +++ b/docs/ui/learning/framework/index.md @@ -5,3 +5,4 @@ The ui-framework package contains application fragments for Login, Project, iMod Topics: * [SyncUi](./SyncUi.md) +* [ToolSettings](./ToolSettings.md) diff --git a/docs/ui/learning/index.md b/docs/ui/learning/index.md index 6905001..5bba136 100644 --- a/docs/ui/learning/index.md +++ b/docs/ui/learning/index.md @@ -20,7 +20,6 @@ iModel.js UI is compatible with React 16.2 and later * [Components](./components/index) * [Nine Zone](./ninezone/index) * [Framework](./framework/index) -* [BWC](./bwc/index) See also: @@ -37,7 +36,6 @@ The iModel.js UI library is divided into these NPM packages in the `@bentley` sc |**ui‑components**|React components that are data-oriented, such as PropertyGrid, Table, Tree and Breadcrumb. |**ui‑ninezone**|React components for application user interface layouts following the Bentley 9‑Zone pattern. |**ui‑framework**|Application fragments for Login, Project, iModel and View selection, and configuration of the application UI including the Backstage, Frontstages, Widgets, etc. -|**bwc**|Sass/SCSS library and React components for building standardized Bentley user interfaces. ## Application UI Configuration diff --git a/docs/ui/learning/leftNav.md b/docs/ui/learning/leftNav.md index 40334ea..f001cf8 100644 --- a/docs/ui/learning/leftNav.md +++ b/docs/ui/learning/leftNav.md @@ -4,7 +4,6 @@ - [Components](./components/index) - [Nine Zone](./ninezone/index) - [Framework](./framework/index) -- [BWC](./bwc/index)   ### Helpful links diff --git a/docs/ui/reference/leftNav.md b/docs/ui/reference/leftNav.md index c27a9d0..977aec3 100644 --- a/docs/ui/reference/leftNav.md +++ b/docs/ui/reference/leftNav.md @@ -1,93 +1,100 @@ -## iModel.js UI Packages - ---- - -### ($core) - -- [Base]($core:Base) -- [CheckBoxList]($core:CheckBoxList) -- [Common]($core:Common) -- [ContextMenu]($core:ContextMenu) -- [Cube]($core:Cube) -- [Dialog]($core:Dialog) -- [ElementSeparator]($core:ElementSeparator) -- [Expandable]($core:Expandable) -- [MessageBox]($core:MessageBox) -- [Popup]($core:Popup) -- [RadialMenu]($core:RadialMenu) -- [SearchBox]($core:SearchBox) -- [SplitButton]($core:SplitButton) -- [Toggle]($core:Toggle) -- [Tree]($core:Tree) -- [UiSettings]($core:UiSettings) -- [Utilities]($core:Utilities) -- [All]($core:All) - ---- - -### ($components) - -- [Breadcrumb]($components:Breadcrumb) -- [Common]($components:Common) -- [DragDrop]($components:DragDrop) -- [Filtering]($components:Filtering) -- [Properties]($components:Properties) -- [PropertyEditors]($components:PropertyEditors) -- [PropertyGrid]($components:PropertyGrid) -- [Table]($components:Table) -- [Tree]($components:Tree) -- [TypeConverters]($components:TypeConverters) -- [Viewport]($components:Viewport) -- [All]($components:All) - ---- - -### ($ninezone) - -- [App]($ninezone:App) -- [Backstage]($ninezone:Backstage) -- [Base]($ninezone:Base) -- [Button]($ninezone:Button) -- [Footer]($ninezone:Footer) -- [Message]($ninezone:Message) -- [MessageCenter]($ninezone:MessageCenter) -- [Popup]($ninezone:Popup) -- [SnapMode]($ninezone:SnapMode) -- [ToolAssistance]($ninezone:ToolAssistance) -- [Theme]($ninezone:Theme) -- [Toolbar]($ninezone:Toolbar) -- [ToolSettings]($ninezone:ToolSettings) -- [Utilities]($ninezone:Utilities) -- [Widget]($ninezone:Widget) -- [Zone]($ninezone:Zone) -- [All]($ninezone:All) - ---- - -### ($framework) - -- [Backstage]($framework:Backstage) -- [ClientServices]($framework:ClientServices) -- [ConfigurableUi]($framework:ConfigurableUi) -- [ContentView]($framework:ContentView) -- [Dialog]($framework:Dialog) -- [DragDrop]($framework:DragDrop) -- [FrameworkState]($framework:FrameworkState) -- [Frontstage]($framework:Frontstage) -- [Item]($framework:Item) -- [KeyboardShortcut]($framework:KeyboardShortcut) -- [NavigationAids]($framework:NavigationAids) -- [Notification]($framework:Notification) -- [OIDC]($framework:OIDC) -- [OpenIModel]($framework:OpenIModel) -- [OverallContent]($framework:OverallContent) -- [Picker]($framework:Picker) -- [StatusBar]($framework:StatusBar) -- [SyncUi]($framework:SyncUi) -- [Tools]($framework:Tools) -- [ToolSettings]($framework:ToolSettings) -- [Utilities]($framework:Utilities) -- [Widget]($framework:Widget) -- [WorkflowTask]($framework:WorkflowTask) -- [Zone]($framework:Zone) -- [All]($framework:All) +## iModel.js UI Packages + +--- + +### ($core) + +- [Base]($core:Base) +- [Button]($core:Button) +- [CheckBoxList]($core:CheckBoxList) +- [Common]($core:Common) +- [ContextMenu]($core:ContextMenu) +- [Cube]($core:Cube) +- [Dialog]($core:Dialog) +- [ElementSeparator]($core:ElementSeparator) +- [Expandable]($core:Expandable) +- [ImageCheckBox]($core:ImageCheckBox) +- [Inputs]($core:Inputs) +- [Loading]($core:Loading) +- [MessageBox]($core:MessageBox) +- [Popup]($core:Popup) +- [RadialMenu]($core:RadialMenu) +- [SearchBox]($core:SearchBox) +- [SplitButton]($core:SplitButton) +- [Tabs]($core:Tabs) +- [Text]($core:Text) +- [Toggle]($core:Toggle) +- [Tree]($core:Tree) +- [UiSettings]($core:UiSettings) +- [Utilities]($core:Utilities) +- [All]($core:All) + +--- + +### ($components) + +- [Breadcrumb]($components:Breadcrumb) +- [Common]($components:Common) +- [Color]($components:Color) +- [DragDrop]($components:DragDrop) +- [Filtering]($components:Filtering) +- [Properties]($components:Properties) +- [PropertyEditors]($components:PropertyEditors) +- [PropertyGrid]($components:PropertyGrid) +- [Table]($components:Table) +- [Tree]($components:Tree) +- [TypeConverters]($components:TypeConverters) +- [Viewport]($components:Viewport) +- [All]($components:All) + +--- + +### ($ninezone) + +- [App]($ninezone:App) +- [Backstage]($ninezone:Backstage) +- [Base]($ninezone:Base) +- [Button]($ninezone:Button) +- [Footer]($ninezone:Footer) +- [Message]($ninezone:Message) +- [MessageCenter]($ninezone:MessageCenter) +- [Popup]($ninezone:Popup) +- [SnapMode]($ninezone:SnapMode) +- [ToolAssistance]($ninezone:ToolAssistance) +- [Theme]($ninezone:Theme) +- [Toolbar]($ninezone:Toolbar) +- [ToolSettings]($ninezone:ToolSettings) +- [Utilities]($ninezone:Utilities) +- [Widget]($ninezone:Widget) +- [Zone]($ninezone:Zone) +- [All]($ninezone:All) + +--- + +### ($framework) + +- [Backstage]($framework:Backstage) +- [ClientServices]($framework:ClientServices) +- [ConfigurableUi]($framework:ConfigurableUi) +- [ContentView]($framework:ContentView) +- [Dialog]($framework:Dialog) +- [DragDrop]($framework:DragDrop) +- [Frontstage]($framework:Frontstage) +- [Item]($framework:Item) +- [KeyboardShortcut]($framework:KeyboardShortcut) +- [NavigationAids]($framework:NavigationAids) +- [Notification]($framework:Notification) +- [OIDC]($framework:OIDC) +- [OpenIModel]($framework:OpenIModel) +- [OverallContent]($framework:OverallContent) +- [Picker]($framework:Picker) +- [State]($framework:State) +- [StatusBar]($framework:StatusBar) +- [SyncUi]($framework:SyncUi) +- [Tools]($framework:Tools) +- [ToolSettings]($framework:ToolSettings) +- [Utilities]($framework:Utilities) +- [Widget]($framework:Widget) +- [WorkflowTask]($framework:WorkflowTask) +- [Zone]($framework:Zone) +- [All]($framework:All) diff --git a/example-code/app/package.json b/example-code/app/package.json index 16023e3..4a74d4b 100644 --- a/example-code/app/package.json +++ b/example-code/app/package.json @@ -4,7 +4,7 @@ "private": true, "license": "MIT", "scripts": { - "build": "npm run build-code && npm run extract && npm run extract-assets", + "build": "npm run build-code && npm run extract && npm run extract-assets && node ./node_modules/@bentley/build-tools/scripts/ignoreargs.js 1>&2", "build-code": "tsc 1>&2", "clean": "rimraf lib package-deps.json ../../generated-docs", "extract-assets": "cpx \"./src/backend/assets/**/*\" ../../generated-docs/extract", @@ -14,16 +14,17 @@ "docs": "", "lint": "tslint --project . 1>&2", "test": "npm run copy:test-backend-assets && npm run copy:backend-assets && node ./node_modules/@bentley/build-tools/scripts/test.js --defineWindow --testDir=\"./lib/backend/test\"", - "cover": "" + "cover": "npm test" }, "repository": {}, "dependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/geometry-core": "0.187.0", - "@bentley/imodeljs-backend": "0.187.0", - "@bentley/imodeljs-clients": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-frontend": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/imodeljs-backend": "0.189.0", + "@bentley/imodeljs-clients": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-frontend": "0.189.0", + "@bentley/logger-config": "0.189.0", "body-parser": "^1.18.2", "chai": "^4.1.2", "commander": "^2.14.1", @@ -33,24 +34,23 @@ "fuse.js": "^3.3.0", "i18next": "^10.2.2", "i18next-browser-languagedetector": "^2.1.0", - "i18next-xhr-backend": "^1.5.1", + "i18next-xhr-backend": "^2.0.1", "js-base64": "^2.4.5", "save": "^2.3.3", "webpack": "^4.20.2" }, "devDependencies": { - "@bentley/config-loader": "0.187.0", - "@bentley/build-tools": "0.187.0", + "@bentley/config-loader": "0.189.0", + "@bentley/build-tools": "0.189.0", "@types/body-parser": "^1.17.0", "@types/chai": "^4.1.4", - "@types/express": "^4.11.1", + "@types/express": "^4.16.1", "@types/fs-extra": "^4.0.7", "@types/i18next": "^8.4.2", "@types/i18next-browser-languagedetector": "^2.0.1", - "@types/i18next-xhr-backend": "^1.4.1", "@types/js-base64": "^2.3.1", "@types/mocha": "^5.2.5", - "@types/node": "10.10.3", + "@types/node": "10.12.18", "@types/webpack": "^3.8.5", "jsdom": "^11.12.0", "jsdom-global": "3.0.2", @@ -59,7 +59,7 @@ "mocha": "^5.2.0", "rimraf": "^2.6.2", "tslint": "^5.11.0", - "typescript": "~3.1.0", + "typescript": "~3.2.2", "xmlhttprequest": "^1.8.0" } } diff --git a/example-code/app/src/backend/Logging.ts b/example-code/app/src/backend/Logging.ts index 49ac371..956040f 100644 --- a/example-code/app/src/backend/Logging.ts +++ b/example-code/app/src/backend/Logging.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // __PUBLISH_EXTRACT_START__ Logging-configureLoggingAndStreams.example-code import { Logger, LoggerLevelsConfig, EnvMacroSubst, BentleyError, IModelStatus } from "@bentley/bentleyjs-core"; -import { BunyanLoggerConfig } from "@bentley/bentleyjs-core/lib/BunyanLoggerConfig"; -import { SeqLoggerConfig, SeqConfig } from "@bentley/bentleyjs-core/lib/SeqLoggerConfig"; +import { BunyanLoggerConfig, SeqConfig, SeqLoggerConfig } from "@bentley/logger-config"; export function initializeLogging(): void { // Read the configuration parameters for my service. Some config diff --git a/example-code/snippets/package.json b/example-code/snippets/package.json index 7f635d4..ac3f7fc 100644 --- a/example-code/snippets/package.json +++ b/example-code/snippets/package.json @@ -4,7 +4,7 @@ "private": true, "license": "MIT", "scripts": { - "build": "tsc 1>&2 && npm run copy:assets && npm run extract", + "build": "tsc 1>&2 && npm run copy:assets && npm run extract && node ./node_modules/@bentley/build-tools/scripts/ignoreargs.js 1>&2", "clean": "rimraf lib package-deps.json ../../generated-docs", "copy:assets": "cpx \"./src/backend/assets/**/*\" ./lib/backend/assets", "extract": "node ./node_modules/@bentley/build-tools/scripts/extract.js --fileExt=ts --extractFrom=./src --recursive --out=../../generated-docs/extract", @@ -12,16 +12,17 @@ "lint": "tslint --project . 1>&2", "test": "npm run test:backend", "test:backend": "npm run copy:assets && node ./node_modules/@bentley/build-tools/scripts/test.js --testDir=\"./lib\"", - "cover": "" + "cover": "npm test" }, "repository": {}, "dependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/geometry-core": "0.187.0", - "@bentley/imodeljs-backend": "0.187.0", - "@bentley/imodeljs-clients": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-frontend": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/imodeljs-backend": "0.189.0", + "@bentley/imodeljs-clients": "0.189.0", + "@bentley/imodeljs-clients-backend": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-frontend": "0.189.0", "body-parser": "^1.18.2", "chai": "^4.1.2", "commander": "^2.14.1", @@ -31,30 +32,29 @@ "fuse.js": "^3.3.0", "i18next": "^10.2.2", "i18next-browser-languagedetector": "^2.1.0", - "i18next-xhr-backend": "^1.5.1", + "i18next-xhr-backend": "^2.0.1", "js-base64": "^2.4.5", "save": "^2.3.3", "webpack": "^4.20.2" }, "devDependencies": { - "@bentley/config-loader": "0.187.0", - "@bentley/build-tools": "0.187.0", + "@bentley/config-loader": "0.189.0", + "@bentley/build-tools": "0.189.0", "@types/body-parser": "^1.17.0", "@types/chai": "^4.1.4", - "@types/express": "^4.11.1", + "@types/express": "^4.16.1", "@types/fs-extra": "^4.0.7", "@types/i18next": "^8.4.2", "@types/i18next-browser-languagedetector": "^2.0.1", - "@types/i18next-xhr-backend": "^1.4.1", "@types/js-base64": "^2.3.1", "@types/mocha": "^5.2.5", - "@types/node": "10.10.3", + "@types/node": "10.12.18", "@types/webpack": "^3.8.5", "chai": "^4.1.2", "cpx": "^1.5.0", "mocha": "^5.2.0", "rimraf": "^2.6.2", "tslint": "^5.11.0", - "typescript": "~3.1.0" + "typescript": "~3.2.2" } } diff --git a/example-code/snippets/src/backend/ExampleCode.test.ts b/example-code/snippets/src/backend/ExampleCode.test.ts index 4adeffe..bddf928 100644 --- a/example-code/snippets/src/backend/ExampleCode.test.ts +++ b/example-code/snippets/src/backend/ExampleCode.test.ts @@ -5,9 +5,10 @@ import { assert } from "chai"; import { BisCore, ConcurrencyControl, Element, ElementAspect, IModelDb, PhysicalModel } from "@bentley/imodeljs-backend"; import { IModelTestUtils } from "./IModelTestUtils"; -import { ElementAspectProps, AxisAlignedBox3d, CodeSpec, CodeScopeSpec, IModel } from "@bentley/imodeljs-common"; +import { ElementAspectProps, CodeSpec, CodeScopeSpec, IModel } from "@bentley/imodeljs-common"; import { Id64, Id64String, ActivityLoggingContext, Logger } from "@bentley/bentleyjs-core"; import { AccessToken } from "@bentley/imodeljs-clients"; +import { Range3d } from "@bentley/geometry-core"; /** Example code organized as tests to make sure that it builds and runs successfully. */ describe("Example Code", () => { @@ -94,7 +95,7 @@ describe("Example Code", () => { // __PUBLISH_EXTRACT_START__ IModelDb.updateProjectExtents // This is an example of how to expand an iModel's project extents. const originalExtents = iModel.projectExtents; - const newExtents = new AxisAlignedBox3d(originalExtents.low, originalExtents.high); + const newExtents = Range3d.create(originalExtents.low, originalExtents.high); newExtents.low.x -= 50; newExtents.low.y -= 25; newExtents.low.z -= 189; newExtents.high.x += 1087; newExtents.high.y += 19; newExtents.high.z += .001; iModel.updateProjectExtents(newExtents); diff --git a/example-code/snippets/src/clients/IModelHubClient.ts b/example-code/snippets/src/clients/IModelHubClient.ts index 00962ef..0d73249 100644 --- a/example-code/snippets/src/clients/IModelHubClient.ts +++ b/example-code/snippets/src/clients/IModelHubClient.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ // __PUBLISH_EXTRACT_START__ IModelHubClient.example-code -import { IModelHubClient, AzureFileHandler } from "@bentley/imodeljs-clients"; +import { IModelHubClient } from "@bentley/imodeljs-clients"; +import { AzureFileHandler } from "@bentley/imodeljs-clients-backend"; const imodelHubClient = new IModelHubClient(new AzureFileHandler()); // __PUBLISH_EXTRACT_END__ diff --git a/example-code/snippets/src/frontend/ExecutingECSQL.ts b/example-code/snippets/src/frontend/ExecutingECSQL.ts index 5382dfa..b02b8a2 100644 --- a/example-code/snippets/src/frontend/ExecutingECSQL.ts +++ b/example-code/snippets/src/frontend/ExecutingECSQL.ts @@ -10,77 +10,75 @@ import { Id64String } from "@bentley/bentleyjs-core"; async function executeECSql_SampleMethod(iModel: IModelConnection): Promise { { // __PUBLISH_EXTRACT_START__ ExecuteECSql_Binding_Positional - const rows: any[] = await iModel.executeQuery("SELECT ECInstanceId,ECClassId,Parent,LastMod FROM bis.Element WHERE CodeValue=? AND LastMod>=?", - ["MyCode", "2018-01-01T12:00:00Z"]); - // ... + for await (const row of iModel.query("SELECT ECInstanceId,ECClassId,Parent,LastMod FROM bis.Element WHERE CodeValue=? AND LastMod>=?", + ["MyCode", "2018-01-01T12:00:00Z"])) { + console.log(`${row.id}, ${row.className}, ${row.parent}, ${row.lastMod}`); + } // __PUBLISH_EXTRACT_END__ - console.log(rows); + } { // __PUBLISH_EXTRACT_START__ ExecuteECSql_Binding_Named - const rows: any[] = await iModel.executeQuery("SELECT ECInstanceId,ECClassId,Parent,LastMod FROM bis.Element WHERE CodeValue=:code AND LastMod>=:lastmod", - { code: "MyCode", lastmod: "2018-01-01T12:00:00Z" }); - - // ... + for await (const row of iModel.query("SELECT ECInstanceId,ECClassId,Parent,LastMod FROM bis.Element WHERE CodeValue=:code AND LastMod>=:lastmod", + { code: "MyCode", lastmod: "2018-01-01T12:00:00Z" })) { + console.log(`${row.id}, ${row.className}, ${row.parent}, ${row.lastMod}`); + } // __PUBLISH_EXTRACT_END__ - console.log(rows); } { // __PUBLISH_EXTRACT_START__ ExecuteECSql_Binding_Navigation - const rows: any[] = await iModel.executeQuery("SELECT ECInstanceId FROM bis.Element WHERE Parent=?", [{ id: "0x132" }]); - // ... + for await (const row of iModel.query("SELECT ECInstanceId FROM bis.Element WHERE Parent=?", [{ id: "0x132" }])) { + console.log(`${row.id}`); + } // __PUBLISH_EXTRACT_END__ - console.log(rows); } { // __PUBLISH_EXTRACT_START__ ExecuteECSql_Binding_NavigationId - const rows: any[] = await iModel.executeQuery("SELECT ECInstanceId FROM bis.Element WHERE Parent.Id=?", ["0x132"]); - // ... + for await (const row of iModel.query("SELECT ECInstanceId FROM bis.Element WHERE Parent.Id=?", ["0x132"])) { + console.log(`${row.id}`); + } // __PUBLISH_EXTRACT_END__ - console.log(rows); } { // __PUBLISH_EXTRACT_START__ ExecuteECSql_Binding_Struct - const rows: any[] = await iModel.executeQuery("SELECT Name FROM myschema.Company WHERE Location=?", [{ street: "7123 Main Street", zip: 30211 }]); - // ... + for await (const row of iModel.query("SELECT Name FROM myschema.Company WHERE Location=?", [{ street: "7123 Main Street", zip: 30211 }])) { + console.log(`${row.name}`); + } // __PUBLISH_EXTRACT_END__ - console.log(rows); } { // __PUBLISH_EXTRACT_START__ ExecuteECSql_Binding_StructMembers - const rows: any[] = await iModel.executeQuery("SELECT Name FROM myschema.Company WHERE Location.Street=? AND Location.Zip=?", ["7123 Main Street", 32443]); - // ... + for await (const row of iModel.query("SELECT Name FROM myschema.Company WHERE Location.Street=? AND Location.Zip=?", ["7123 Main Street", 32443])) { + console.log(`${row.name}`); + } // __PUBLISH_EXTRACT_END__ - console.log(rows); } { // __PUBLISH_EXTRACT_START__ ExecuteECSql_Binding_Array - const rows: any[] = await iModel.executeQuery("SELECT Name FROM myschema.Company WHERE PhoneNumbers=?", [["+16134584201", "+16134584202", "+16134584222"]]); - // ... + for await (const row of iModel.query("SELECT Name FROM myschema.Company WHERE PhoneNumbers=?", [["+16134584201", "+16134584202", "+16134584222"]])) { + console.log(`${row.name}`); + } // __PUBLISH_EXTRACT_END__ - console.log(rows); } { // __PUBLISH_EXTRACT_START__ ExecuteECSql_IllustrateRowFormat - const rows: any[] = await iModel.executeQuery("SELECT ECInstanceId,ECClassId,Parent,LastMod FROM bis.Element WHERE Model.Id=?", ["0x113"]); - for (const row of rows) { - console.log(JSON.stringify(row)); + for await (const row of iModel.query("SELECT ECInstanceId,ECClassId,Parent,LastMod FROM bis.Element WHERE Model.Id=?", ["0x113"])) { + console.log(`${row.id}, ${row.className}, ${row.parent}, ${row.lastMod}`); } // __PUBLISH_EXTRACT_END__ } { // __PUBLISH_EXTRACT_START__ ExecuteECSql_WorkingWithRowFormat - const rows: any[] = await iModel.executeQuery("SELECT ECInstanceId,ECClassId,Parent,LastMod FROM bis.Element WHERE Model.Id=?", ["0x113"]); console.log("ECInstanceId | ClassName | Parent Id | Parent RelClassName | LastMod"); - for (const row of rows) { + for await (const row of iModel.query("SELECT ECInstanceId,ECClassId,Parent,LastMod FROM bis.Element WHERE Model.Id=?", ["0x113"])) { const id: Id64String = row.id; const className: string = row.className; const parent: NavigationValue = row.parent; diff --git a/plugins/markup/.npmignore b/plugins/markup/.npmignore new file mode 100644 index 0000000..c2e5112 --- /dev/null +++ b/plugins/markup/.npmignore @@ -0,0 +1,9 @@ +# start off ignoring everything, and then add back only the files we want +* +!lib/**/*.d.ts +!lib/**/*.js +test +!public/** +!assets/** +!package.json +!*.md diff --git a/plugins/markup/LICENSE.md b/plugins/markup/LICENSE.md new file mode 100644 index 0000000..a04aa6f --- /dev/null +++ b/plugins/markup/LICENSE.md @@ -0,0 +1,9 @@ +# MIT License + +Copyright © 2019 Bentley Systems, Incorporated. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/plugins/markup/package.json b/plugins/markup/package.json new file mode 100644 index 0000000..d3c74c6 --- /dev/null +++ b/plugins/markup/package.json @@ -0,0 +1,76 @@ +{ + "name": "@bentley/plugin-markup", + "version": "0.189.0-dev.6", + "description": "iModel.js plugin for markup", + "license": "MIT", + "main": "lib/markup.js", + "scripts": { + "build": "node ./node_modules/@bentley/webpack-tools/bin/buildIModelJsModule.js", + "clean": "rimraf lib package-deps.json", + "docs": "", + "lint": "tslint --project . 1>&2", + "test": "", + "cover": "" + }, + "iModelJs": { + "buildModule": { + "type": "plugin", + "webpack": { + "dest": "./lib/webresources", + "entry": "./lib/Markup.js", + "bundleName": "MarkupPlugin" + } + } + }, + "repository": { + "type": "git", + "url": "https://github.com/imodeljs/imodeljs" + }, + "keywords": [ + "Bentley", + "BIM", + "iModel" + ], + "author": { + "name": "Bentley Systems, Inc.", + "url": "http://www.bentley.com" + }, + "//dependencies": [ + "NOTE: these dependencies are specific to imodeljs-common", + "NOTE: these dependencies should be only for things that DO NOT APPEAR IN THE API" + ], + "dependencies": { + "@bentley/imodeljs-frontend": "0.189.0", + "svg.js": "~2.7.1" + }, + "//peerDependencies": [ + "NOTE: peerDependencies are a standard way for npm to perform a module compatibility check" + ], + "peerDependencies": { + "@bentley/bentleyjs-core": "0.189.0" + }, + "//devDependencies": [ + "NOTE: Must include modules mentioned in peerDependencies since those are not auto-installed", + "NOTE: Must include modules used by the scripts section of package.json" + ], + "devDependencies": { + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/build-tools": "0.189.0", + "@bentley/webpack-tools": "0.189.0", + "@types/node": "10.12.18", + "chai": "^4.1.2", + "mocha": "^5.2.0", + "rimraf": "^2.6.2", + "ts-node": "^7.0.1", + "tslint": "^5.11.0", + "typescript": "~3.2.2", + "typedoc": "^0.11.1" + }, + "//optionalDependencies": [ + "NOTE: Rush (as of 4.2.5) does not support optionalDependencies!" + ], + "nyc": { + "nycrc-path": "./node_modules/@bentley/build-tools/.nycrc", + "all": true + } +} diff --git a/plugins/markup/src/Markup.ts b/plugins/markup/src/Markup.ts new file mode 100644 index 0000000..ee2b59c --- /dev/null +++ b/plugins/markup/src/Markup.ts @@ -0,0 +1,137 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { IModelApp, Plugin, PluginAdmin, ScreenViewport } from "@bentley/imodeljs-frontend"; +import * as SVG from "svg.js"; +import { MarkupTool } from "./MarkupTool"; +import { SelectTool } from "./SelectTool"; +import { UndoManager } from "./Undo"; + +/** An SVG.Element with additional properties added for the Markup system. */ +export type MarkupElement = SVG.Element & { + _inSelection?: boolean; + _oldColor?: { fill: any; stroke: any; }; +}; + +// temporary, for testing. +function getSvgFile(uri: string) { + const xhr = new XMLHttpRequest(); + xhr.open("GET", uri, false); + xhr.send(); + return xhr.responseText; +} + +class MarkupApp extends Plugin { + public markup?: Markup; + + public async onExecute(_args: string[]) { + if (this.markup) { + IModelApp.toolAdmin.markupView = undefined; + this.markup.destroy(); + this.markup = undefined; + return; + } + + const view = IModelApp.toolAdmin.markupView = IModelApp.viewManager.selectedView; + if (view) + this.markup = new Markup(view, getSvgFile("DemoMarkup.svg")); + } +} + +/** + * The set of currently selected SVG elements. When elements are added to the set, they are hilited. + */ +export class SelectionSet { + public readonly elements = new Set(); + public get size() { return this.elements.size; } + public get isActive() { return this.size !== 0; } + public has(el: MarkupElement) { return this.elements.has(el); } + public emptyAll(): void { + this.elements.forEach((el) => Markup.unHilite(el)); + this.elements.clear(); + } + public add(el: MarkupElement) { + this.elements.add(el); + Markup.hilite(el); + } + public drop(el: MarkupElement): boolean { + if (!this.elements.delete(el)) + return false; + Markup.unHilite(el); + return true; + } + public deleteAll(undo: UndoManager) { + undo.doGroup(() => this.elements.forEach((el) => { undo.onDelete(el); el.remove(); })); + this.emptyAll(); + } + public reposition(undo: UndoManager, fn: (el: MarkupElement) => void) { + undo.doGroup(() => this.elements.forEach((el) => { + const oldParent = el.parent() as MarkupElement; + const oldPos = el.position(); + fn(el); + undo.onRepositioned(el, oldPos, oldParent); + })); + } +} + +/** + * The current markup being created/edited. Holds the SVG elements, plus the active MarkupTool. + * When starting a Markup, a new Div is added as a child of the ScreenViewport's parentDiv. + */ +export class Markup { + public static hiliteColor = "magenta"; + public static flashColor = "cyan"; + public readonly markupDiv: HTMLDivElement; + public readonly undo = new UndoManager(); + public readonly selected = new SelectionSet(); + public tool!: MarkupTool; + public readonly svgMarkup?: SVG.Nested; + public readonly svgDecorations?: SVG.Nested; + + /** Called when the Markup is destroyed */ + public destroy() { this.markupDiv.parentNode!.removeChild(this.markupDiv); } + public enablePick() { this.markupDiv.style.pointerEvents = "auto"; } + public disablePick() { this.markupDiv.style.pointerEvents = "none"; } + + private static overrideColor(el: MarkupElement, color: string) { + if (undefined === el._oldColor) + el._oldColor = { fill: el.style("fill"), stroke: el.style("stroke") }; + const toColor = (val: string) => (val === "none") ? "none" : color; + el.style({ fill: toColor(el._oldColor.fill), stroke: toColor(el._oldColor.stroke) }); + } + public static resetColor(el: MarkupElement) { if (undefined !== el._oldColor) { el.style(el._oldColor); el._oldColor = undefined; } } + public static hilite(el: MarkupElement) { if (undefined === el._inSelection) { this.overrideColor(el, Markup.hiliteColor); el._inSelection = true; } } + public static unHilite(el: MarkupElement) { if (undefined !== el._inSelection) { this.resetColor(el); el._inSelection = undefined; } } + public static flash(el: MarkupElement) { if (undefined === el._inSelection) this.overrideColor(el, Markup.flashColor); } + public static unFlash(el: MarkupElement) { if (undefined === el._inSelection) this.resetColor(el); } + + /** Delete all the entries in the selection set, then empty it. */ + public deleteSelected() { this.selected.deleteAll(this.undo); } + /** Bring all the entries in the selection set to the front. */ + public bringToFront() { this.selected.reposition(this.undo, (el) => el.front()); } + /** Send all the entries in the selection set to the back. */ + public sendToBack() { this.selected.reposition(this.undo, (el) => el.back()); } + + public makePickable(el: MarkupElement) { + if (el instanceof SVG.Shape) { + el.attr("cursor", "move"); + el.on("mouseenter", (ev: MouseEvent) => this.tool.onMouseEnter(ev, el)); + el.on("mouseleave", (ev: MouseEvent) => this.tool.onMouseLeave(ev, el)); + } + } + + public constructor(vp: ScreenViewport, svgData: string) { + this.markupDiv = vp.addNewDiv("overlay-markup", true, 20); // this div goes on top of the canvas, but behind UI layers + const svgContainer = SVG(this.markupDiv).attr("id", "markup-container"); // SVG container to hold both Markup SVG and svg-based Markup decorators + this.svgMarkup = svgContainer.nested().svg(svgData).attr("id", "markup-svg").each((i, children) => this.makePickable(children[i]), true); // The actual SVG for the markup + this.svgDecorations = svgContainer.nested().attr("id", "markup-decorations"); // only for temporary decorations of SVG graphics. + + new SelectTool(this).run(); + } +} + +declare var IMODELJS_VERSIONS_REQUIRED: string; +declare var PLUGIN_NAME: string; +export const markupApp = new MarkupApp(PLUGIN_NAME, IMODELJS_VERSIONS_REQUIRED); +PluginAdmin.register(markupApp); diff --git a/plugins/markup/src/MarkupTool.ts b/plugins/markup/src/MarkupTool.ts new file mode 100644 index 0000000..6d848e4 --- /dev/null +++ b/plugins/markup/src/MarkupTool.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { MarkupElement, Markup } from "./Markup"; +import { PrimitiveTool } from "@bentley/imodeljs-frontend"; + +export abstract class MarkupTool extends PrimitiveTool { + constructor(public markup: Markup) { super(); } + public onPostInstall(): void { this.markup.tool = this; } + public exitTool(): void { } + public requireWriteableTarget(): boolean { return false; } + public onMouseEnter(_ev: MouseEvent, _el: MarkupElement) { } + public onMouseLeave(_ev: MouseEvent, _el: MarkupElement) { } + public async undoPreviousStep(): Promise { this.markup.undo.doUndo(); return true; } + public async redoPreviousStep(): Promise { this.markup.undo.doRedo(); return true; } +} diff --git a/plugins/markup/src/SelectTool.ts b/plugins/markup/src/SelectTool.ts new file mode 100644 index 0000000..22f2e84 --- /dev/null +++ b/plugins/markup/src/SelectTool.ts @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { BeButtonEvent, EventHandled } from "@bentley/imodeljs-frontend"; +import { Markup, MarkupElement } from "./Markup"; +import { MarkupTool } from "./MarkupTool"; + +export class SelectTool extends MarkupTool { + public static toolId = "Markup.Select"; + private _flashedElement?: MarkupElement; + public get flashedElement(): MarkupElement | undefined { return this._flashedElement; } + public set flashedElement(el: MarkupElement | undefined) { + if (undefined !== this._flashedElement) Markup.unFlash(this._flashedElement); + if (undefined !== el) Markup.flash(el); + this._flashedElement = el; + } + + constructor(markup: Markup) { + super(markup); + markup.enablePick(); + } + public onMouseEnter(_ev: MouseEvent, el: MarkupElement) { this.flashedElement = el; } + public onMouseLeave(_ev: MouseEvent, _el: MarkupElement) { this.flashedElement = undefined; } + public onRestartTool(): void { this.markup.selected.emptyAll(); } + + public async onDataButtonUp(ev: BeButtonEvent): Promise { + const el = this.flashedElement; + const selected = this.markup.selected; + if (ev.isControlKey) { + if (el && selected.drop(el)) + return EventHandled.Yes; + } else { + selected.emptyAll(); + } + + if (el !== undefined) + selected.add(el); + return EventHandled.Yes; + } + + public async onKeyTransition(wentDown: boolean, key: KeyboardEvent): Promise { + if (!wentDown) + return EventHandled.No; + + switch (key.key) { + case "Delete": + case "Backspace": + this.markup.deleteSelected(); + return EventHandled.Yes; + case "f": + this.markup.bringToFront(); + return EventHandled.Yes; + case "b": + this.markup.sendToBack(); + return EventHandled.Yes; + } + return EventHandled.No; + } +} diff --git a/plugins/markup/src/Undo.ts b/plugins/markup/src/Undo.ts new file mode 100644 index 0000000..e83e026 --- /dev/null +++ b/plugins/markup/src/Undo.ts @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { MarkupElement, markupApp } from "./Markup"; + +abstract class UndoAction { + public cmdId: number = 0; + public abstract reverse(): void; + public abstract reinstate(): void; +} + +class AddAction extends UndoAction { + private _parent: MarkupElement; + private _index: number; + constructor(private _elem: MarkupElement) { + super(); + this._parent = _elem.parent() as MarkupElement; + this._index = this._elem.position(); + } + public reinstate() { (this._parent as any).add(this._elem, this._index); } + public reverse() { this._elem.remove(); } +} + +class DeleteAction extends UndoAction { + private _parent: MarkupElement; + private _index: number; + constructor(private _elem: MarkupElement) { + super(); + this._parent = _elem.parent() as MarkupElement; + this._index = this._elem.position(); + } + public reverse() { (this._parent as any).add(this._elem, this._index); } + public reinstate() { markupApp.markup!.selected.drop(this._elem); this._elem.remove(); } +} + +class RepositionAction extends UndoAction { + private _newParent: MarkupElement; + private _newIndex: number; + + constructor(private _elem: MarkupElement, private _oldIndex: number, private _oldParent: MarkupElement) { + super(); + this._newIndex = this._elem.position(); + this._newParent = _elem.parent() as MarkupElement; + } + public reinstate() { (this._newParent as any).add(this._elem, this._newIndex); } + public reverse() { (this._oldParent as any).add(this._elem, this._oldIndex); } +} + +class ModifyAction extends UndoAction { + constructor(private _newElem: MarkupElement, private _oldElement: MarkupElement) { super(); } + public reinstate() { this._oldElement.replace(this._newElem); } + public reverse() { this._newElem.replace(this._oldElement); } +} + +export class UndoManager { + private _currentCmd = 0; + private _grouped = 0; + private _stack: UndoAction[] = []; + private _currentPos = 0; + + private addAction(action: UndoAction) { + this._stack.length = this._currentPos; + action.cmdId = this._currentCmd; + this._stack.push(action); + this._currentPos = this.size; + } + + public get size() { return this._stack.length; } + public startGroup() { this.startCommand(); ++this._grouped; } + public endGroup() { --this._grouped; } + public startCommand() { if (0 === this._grouped)++this._currentCmd; } + public doGroup(fn: VoidFunction) { this.startGroup(); fn(); this.endGroup(); } + public onAdded(elem: MarkupElement) { this.addAction(new AddAction(elem)); } + public onDelete(elem: MarkupElement) { this.addAction(new DeleteAction(elem)); } + public onRepositioned(elem: MarkupElement, oldIndex: number, oldParent: MarkupElement) { this.addAction(new RepositionAction(elem, oldIndex, oldParent)); } + public onModified(newElem: MarkupElement, oldElem: MarkupElement) { this.addAction(new ModifyAction(newElem, oldElem)); } + + public doUndo() { + if (this._currentPos === 0) + return; + + const cmdId = this._stack[this._currentPos - 1].cmdId; + while (this._currentPos > 0 && cmdId === this._stack[this._currentPos - 1].cmdId) + this._stack[--this._currentPos].reverse(); + } + public doRedo() { + if (this._currentPos === this.size) + return; + + const cmdId = this._stack[this._currentPos].cmdId; + while (this._currentPos < this.size && cmdId === this._stack[this._currentPos].cmdId) + this._stack[this._currentPos++].reinstate(); + } +} diff --git a/plugins/markup/tsconfig.json b/plugins/markup/tsconfig.json new file mode 100644 index 0000000..81682d5 --- /dev/null +++ b/plugins/markup/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "./node_modules/@bentley/build-tools/tsconfig-base.json", + "compilerOptions": { + "outDir": "./lib" + }, +} \ No newline at end of file diff --git a/plugins/markup/tslint.json b/plugins/markup/tslint.json new file mode 100644 index 0000000..66909b8 --- /dev/null +++ b/plugins/markup/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": "@bentley/build-tools/tslint.json" +} \ No newline at end of file diff --git a/presentation/backend/CHANGELOG.json b/presentation/backend/CHANGELOG.json index 76c23fe..252fa5c 100644 --- a/presentation/backend/CHANGELOG.json +++ b/presentation/backend/CHANGELOG.json @@ -1,6 +1,45 @@ { "name": "@bentley/presentation-backend", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/presentation-backend_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "Changes package.json to include api-extractor and adds api-extractor.json" + }, + { + "comment": "Wrapped PresentationRpcImpl method response values in PresentationRpcInterface object." + }, + { + "comment": "Use new buildIModelJsBuild script" + }, + { + "comment": "PresentationManager now converts locale string to lower case when creating request parameters." + }, + { + "comment": "Remove uneeded typedoc plugin depedency" + }, + { + "comment": "Add hardcoded implementations for 'element', 'assembly', 'top-assembly', 'category', 'model' selection scopes" + }, + { + "comment": "RPC Interface changes to optimize getting first page of nodes/content" + }, + { + "comment": "upgrade to TypeScript 3.2.2" + } + ] + } + }, + { + "version": "0.188.0", + "tag": "@bentley/presentation-backend_v0.188.0", + "date": "Wed, 16 Jan 2019 16:36:09 GMT", + "comments": {} + }, { "version": "0.187.0", "tag": "@bentley/presentation-backend_v0.187.0", diff --git a/presentation/backend/CHANGELOG.md b/presentation/backend/CHANGELOG.md index 5f9b722..3f67cd5 100644 --- a/presentation/backend/CHANGELOG.md +++ b/presentation/backend/CHANGELOG.md @@ -1,6 +1,25 @@ # Change Log - @bentley/presentation-backend -This log was last generated on Tue, 15 Jan 2019 15:18:59 GMT and should not be manually modified. +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- Changes package.json to include api-extractor and adds api-extractor.json +- Wrapped PresentationRpcImpl method response values in PresentationRpcInterface object. +- Use new buildIModelJsBuild script +- PresentationManager now converts locale string to lower case when creating request parameters. +- Remove uneeded typedoc plugin depedency +- Add hardcoded implementations for 'element', 'assembly', 'top-assembly', 'category', 'model' selection scopes +- RPC Interface changes to optimize getting first page of nodes/content +- upgrade to TypeScript 3.2.2 + +## 0.188.0 +Wed, 16 Jan 2019 16:36:09 GMT + +*Version update only* ## 0.187.0 Tue, 15 Jan 2019 15:18:59 GMT diff --git a/presentation/backend/package.json b/presentation/backend/package.json index 496e089..6281790 100644 --- a/presentation/backend/package.json +++ b/presentation/backend/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/presentation-backend", - "version": "0.187.0", + "version": "0.189.0", "description": "Backend of iModel.js Presentation library", "license": "MIT", "repository": { @@ -21,7 +21,7 @@ "main": "lib/presentation-backend.js", "typings": "lib/presentation-backend", "scripts": { - "build": "tsc -b ./src/test 1>&2 && npm run build:assets && npm run extract", + "build": "tsc -b ./src/test 1>&2 && npm run build:assets && npm run extract && node ./node_modules/@bentley/build-tools/scripts/ignoreargs.js 1>&2", "build:assets": "cpx \"./assets/**/*\" ./lib/assets && npm run pseudolocalize", "build:watch": "npm run build:assets && tsc -b ./src/test -w", "clean": "rimraf lib package-deps.json", @@ -30,6 +30,7 @@ "docs:changelog": "cpx \"./CHANGELOG.md\" ../../generated-docs/presentation/presentation-backend", "docs:reference": "node ./node_modules/@bentley/build-tools/scripts/docs.js --source=./src --includes=../../generated-docs/extract --json=../../generated-docs/presentation/presentation-backend/json/file.json --tsIndexFile=presentation-backend.ts --onlyJson %TYPEDOC_THEME%", "extract": "node ./node_modules/@bentley/build-tools/scripts/extract.js --fileExt=ts --extractFrom=./src/test --recursive --out=../../generated-docs/extract", + "extract-api": "node ./node_modules/@bentley/build-tools/scripts/extract-api.js --entry=presentation-backend --isPresentation=true", "lint": "echo Disabled until https://github.com/palantir/tslint/issues/4148 is fixed", "//lint": "tslint -p ./src 1>&2", "pseudolocalize": "node ./node_modules/@bentley/build-tools/scripts/pseudolocalize.js --englishDir ./assets/locales/en --out ./lib/assets/locales/en-pseudo", @@ -37,17 +38,17 @@ "test:watch": "npm test -- --reporter min --watch-extensions ts --watch" }, "peerDependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-backend": "0.187.0", - "@bentley/presentation-common": "0.187.0" + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-backend": "0.189.0", + "@bentley/presentation-common": "0.189.0" }, "devDependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/build-tools": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-backend": "0.187.0", - "@bentley/presentation-common": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/build-tools": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-backend": "0.189.0", + "@bentley/presentation-common": "0.189.0", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/chai-jest-snapshot": "^1.3.0", @@ -72,9 +73,8 @@ "sinon-chai": "^3.2.0", "tslint": "^5.11.0", "typedoc": "^0.11.1", - "typedoc-plugin-external-module-name": "^1.1.1", "typemoq": "^2.1.0", - "typescript": "~3.1.0" + "typescript": "~3.2.2" }, "nyc": { "extends": "./node_modules/@bentley/build-tools/.nycrc", diff --git a/presentation/backend/src/PresentationManager.ts b/presentation/backend/src/PresentationManager.ts index 2f34e52..400e975 100644 --- a/presentation/backend/src/PresentationManager.ts +++ b/presentation/backend/src/PresentationManager.ts @@ -6,12 +6,15 @@ import * as path from "path"; import { ActivityLoggingContext } from "@bentley/bentleyjs-core"; -import { IModelDb } from "@bentley/imodeljs-backend"; +import { EntityProps } from "@bentley/imodeljs-common"; +import { IModelDb, GeometricElement } from "@bentley/imodeljs-backend"; import { PresentationError, PresentationStatus, HierarchyRequestOptions, NodeKey, Node, NodePathElement, ContentRequestOptions, SelectionInfo, Content, Descriptor, RequestOptions, Paged, KeySet, InstanceKey, + SelectionScopeRequestOptions, SelectionScope, + NodesResponse, ContentResponse, } from "@bentley/presentation-common"; import { listReviver as nodesListReviver } from "@bentley/presentation-common/lib/hierarchy/Node"; import { listReviver as nodePathElementReviver } from "@bentley/presentation-common/lib/hierarchy/NodePathElement"; @@ -127,56 +130,55 @@ export default class PresentationManager { } /** - * Retrieves root nodes. + * Retrieves nodes and node count * @param activityLoggingContext Logging context holding request's ActivityId - * @param requestOptions options for the request - * @return A promise object that returns either an array of nodes on success or an error string on error. + * @param requestOptions Options for the request + * @param parentKey Key of the parentNode + * @return A promise object that returns either a node response containing nodes and node count on success or an error string on error */ - public async getRootNodes(activityLoggingContext: ActivityLoggingContext, requestOptions: Paged>): Promise>> { + public async getNodesAndCount(activityLoggingContext: ActivityLoggingContext, requestOptions: Paged>, parentKey?: Readonly): Promise> { activityLoggingContext.enter(); - const params = this.createRequestParams(NativePlatformRequestTypes.GetRootNodes, requestOptions); - return this.request(activityLoggingContext, requestOptions.imodel, params, nodesListReviver); - } - /** - * Retrieves root nodes count. - * @param activityLoggingContext Logging context holding request's ActivityId - * @param requestOptions options for the request - * @return A promise object that returns the number of root nodes. - */ - public async getRootNodesCount(activityLoggingContext: ActivityLoggingContext, requestOptions: HierarchyRequestOptions): Promise { + const nodesCount = await this.getNodesCount(activityLoggingContext, requestOptions, parentKey); activityLoggingContext.enter(); - const params = this.createRequestParams(NativePlatformRequestTypes.GetRootNodesCount, requestOptions); - return this.request(activityLoggingContext, requestOptions.imodel, params); + + const nodesList = await this.getNodes(activityLoggingContext, requestOptions, parentKey); + activityLoggingContext.enter(); + + return { nodes: nodesList, count: nodesCount }; } /** - * Retrieves children of the specified parent node. + * Retrieves nodes * @param activityLoggingContext Logging context holding request's ActivityId * @param requestOptions options for the request - * @param parentKey Key of the parent node. + * @param parentKey Key of the parent node if requesting for child nodes. * @return A promise object that returns either an array of nodes on success or an error string on error. */ - public async getChildren(activityLoggingContext: ActivityLoggingContext, requestOptions: Paged>, parentKey: Readonly): Promise>> { + public async getNodes(activityLoggingContext: ActivityLoggingContext, requestOptions: Paged>, parentKey?: Readonly): Promise>> { activityLoggingContext.enter(); - const params = this.createRequestParams(NativePlatformRequestTypes.GetChildren, requestOptions, { - nodeKey: parentKey, - }); + let params; + if (parentKey) + params = this.createRequestParams(NativePlatformRequestTypes.GetChildren, requestOptions, { nodeKey: parentKey }); + else + params = this.createRequestParams(NativePlatformRequestTypes.GetRootNodes, requestOptions); return this.request(activityLoggingContext, requestOptions.imodel, params, nodesListReviver); } /** - * Retrieves children count for the specified parent node. + * Retrieves nodes count * @param activityLoggingContext Logging context holding request's ActivityId * @param requestOptions options for the request - * @param parentKey Key of the parent node. - * @return A promise object that returns the number of child nodes. + * @param parentKey Key of the parent node if requesting for child nodes. + * @return A promise object that returns the number of nodes. */ - public async getChildrenCount(activityLoggingContext: ActivityLoggingContext, requestOptions: HierarchyRequestOptions, parentKey: Readonly): Promise { + public async getNodesCount(activityLoggingContext: ActivityLoggingContext, requestOptions: HierarchyRequestOptions, parentKey?: Readonly): Promise { activityLoggingContext.enter(); - const params = this.createRequestParams(NativePlatformRequestTypes.GetChildrenCount, requestOptions, { - nodeKey: parentKey, - }); + let params; + if (parentKey) + params = this.createRequestParams(NativePlatformRequestTypes.GetChildrenCount, requestOptions, { nodeKey: parentKey }); + else + params = this.createRequestParams(NativePlatformRequestTypes.GetRootNodesCount, requestOptions); return this.request(activityLoggingContext, requestOptions.imodel, params); } @@ -233,40 +235,57 @@ export default class PresentationManager { /** * Retrieves the content set size based on the supplied content descriptor override. - * @param activityLoggingContext Logging context holding request's ActivityId - * @param requestOptions options for the request - * @param descriptor Content descriptor which specifies how the content should be returned. - * @param keys Keys of ECInstances to get the content for. + * @param activityLoggingContext Logging context holding request's ActivityId + * @param requestOptions options for the request + * @param descriptorOrDisplayType Content descriptor which specifies how the content should be returned or preferred display type of the content + * @param keys Keys of ECInstances to get the content for. * @return A promise object that returns either a number on success or an error string on error. * Even if concrete implementation returns content in pages, this function returns the total * number of records in the content set. */ - public async getContentSetSize(activityLoggingContext: ActivityLoggingContext, requestOptions: ContentRequestOptions, descriptor: Readonly, keys: Readonly): Promise { + public async getContentSetSize(activityLoggingContext: ActivityLoggingContext, requestOptions: ContentRequestOptions, descriptorOrDisplayType: Readonly | string, keys: Readonly): Promise { activityLoggingContext.enter(); const params = this.createRequestParams(NativePlatformRequestTypes.GetContentSetSize, requestOptions, { keys, - descriptorOverrides: descriptor.createDescriptorOverrides(), + descriptorOverrides: this.createContentDescriptorOverrides(descriptorOrDisplayType), }); return this.request(activityLoggingContext, requestOptions.imodel, params); } /** * Retrieves the content based on the supplied content descriptor override. - * @param activityLoggingContext Logging context holding request's ActivityId - * @param requestOptions options for the request - * @param descriptor Content descriptor which specifies how the content should be returned. - * @param keys Keys of ECInstances to get the content for. + * @param activityLoggingContext Logging context holding request's ActivityId + * @param requestOptions options for the request + * @param descriptorOrDisplayType Content descriptor which specifies how the content should be returned or preferred display type of the content + * @param keys Keys of ECInstances to get the content for. * @return A promise object that returns either content on success or an error string on error. */ - public async getContent(activityLoggingContext: ActivityLoggingContext, requestOptions: Paged>, descriptor: Readonly, keys: Readonly): Promise> { + public async getContent(activityLoggingContext: ActivityLoggingContext, requestOptions: Paged>, descriptorOrDisplayType: Readonly | string, keys: Readonly): Promise> { activityLoggingContext.enter(); const params = this.createRequestParams(NativePlatformRequestTypes.GetContent, requestOptions, { keys, - descriptorOverrides: descriptor.createDescriptorOverrides(), + descriptorOverrides: this.createContentDescriptorOverrides(descriptorOrDisplayType), }); return this.request(activityLoggingContext, requestOptions.imodel, params, Content.reviver); } + /** + * Retrieves the content and content size based on supplied content descriptor override. + * @param activityLoggingContext Logging context holding request's ActivityId. + * @param requestOptions Options for thr request. + * @param descriptorOrDisplayType Content descriptor which specifies how the content should be returned or preferred display type of the content + * @param keys Keys of ECInstances to get the content for + * @return A promise object that returns either content and content set size on success or an error string on error. + */ + public async getContentAndSize(activityLoggingContext: ActivityLoggingContext, requestOptions: Paged>, descriptorOrDisplayType: Readonly | string, keys: Readonly): Promise> { + activityLoggingContext.enter(); + const contentSetSize = await this.getContentSetSize(activityLoggingContext, requestOptions, descriptorOrDisplayType, keys); + activityLoggingContext.enter(); + const contentResult = await this.getContent(activityLoggingContext, requestOptions, descriptorOrDisplayType, keys); + activityLoggingContext.enter(); + return { content: contentResult, size: contentSetSize }; + } + /** * Retrieves distinct values of specific field from the content based on the supplied content descriptor override. * @param activityLoggingContext Logging context holding request's ActivityId @@ -288,6 +307,119 @@ export default class PresentationManager { return this.request(activityLoggingContext, requestOptions.imodel, params); } + /** + * Retrieves available selection scopes. + * @param activityLoggingContext Logging context holding request's ActivityId + * @param requestOptions options for the request + */ + public async getSelectionScopes(activityLoggingContext: ActivityLoggingContext, requestOptions: SelectionScopeRequestOptions): Promise { + activityLoggingContext.enter(); + (requestOptions as any); + + const createSelectionScope = (scopeId: string, label: string, description: string): SelectionScope => ({ + id: scopeId, + label, + description, + }); + + return [ + createSelectionScope("element", "Element", "Select the picked element"), + createSelectionScope("assembly", "Assembly", "Select parent of the picked element"), + createSelectionScope("top-assembly", "Top Assembly", "Select the topmost parent of the picked element"), + createSelectionScope("category", "Category", "Select all elements in the picked element's category"), + createSelectionScope("model", "Model", "Select all elements in the picked element's model"), + ]; + } + + private createContentDescriptorOverrides(descriptorOrDisplayType: Readonly | string) { + if (typeof descriptorOrDisplayType === "string") + return { + displayType: descriptorOrDisplayType, + hiddenFieldNames: [], + contentFlags: 0, + }; + + return descriptorOrDisplayType.createDescriptorOverrides(); + } + + private getParentInstanceKey(imodel: IModelDb, key: InstanceKey): InstanceKey | undefined { + const parentElementProps = imodel.elements.getElement(key.id!).parent; + if (!parentElementProps) + return undefined; + return { + className: parentElementProps.relClassName!, + id: parentElementProps.id, + }; + } + + private computeAssemblySelection(requestOptions: SelectionScopeRequestOptions, keys: EntityProps[]) { + const parentKeys = new KeySet(); + keys.forEach((key) => { + const thisKey = { className: key.classFullName, id: key.id! }; + const parentKey = this.getParentInstanceKey(requestOptions.imodel, thisKey); + if (parentKey) + parentKeys.add(parentKey); + else + parentKeys.add(thisKey); + }); + return parentKeys; + } + + private computeTopAssemblySelection(requestOptions: SelectionScopeRequestOptions, keys: EntityProps[]) { + const parentKeys = new KeySet(); + keys.forEach((key) => { + let curr = { className: key.classFullName, id: key.id! }; + let parent = this.getParentInstanceKey(requestOptions.imodel, curr); + while (parent) { + curr = parent; + parent = this.getParentInstanceKey(requestOptions.imodel, curr); + } + parentKeys.add(curr); + }); + return parentKeys; + } + + private computeCategorySelection(requestOptions: SelectionScopeRequestOptions, keys: EntityProps[]) { + const categoryKeys = new KeySet(); + keys.forEach((key) => { + const el = requestOptions.imodel.elements.getElement(key.id!); + if (el instanceof GeometricElement) + categoryKeys.add({ className: "BisCore:Category", id: el.category }); + }); + return categoryKeys; + } + + private computeModelSelection(requestOptions: SelectionScopeRequestOptions, keys: EntityProps[]) { + const modelKeys = new KeySet(); + keys.forEach((key) => { + const el = requestOptions.imodel.elements.getElement(key.id!); + modelKeys.add({ className: "BisCore:Model", id: el.model }); + }); + return modelKeys; + } + + /** + * Computes selection set based on provided selection scope. + * @param activityLoggingContext Logging context holding request's ActivityId + * @param requestOptions Options for the request + * @param keys Keys of elements to get the content for. + * @param scopeId ID of selection scope to use for computing selection + */ + public async computeSelection(activityLoggingContext: ActivityLoggingContext, requestOptions: SelectionScopeRequestOptions, keys: EntityProps[], scopeId: string): Promise { + activityLoggingContext.enter(); + (requestOptions as any); + + switch (scopeId) { + case "element": return new KeySet(keys); + case "assembly": return this.computeAssemblySelection(requestOptions, keys); + case "top-assembly": return this.computeTopAssemblySelection(requestOptions, keys); + case "category": return this.computeCategorySelection(requestOptions, keys); + case "model": return this.computeModelSelection(requestOptions, keys); + } + + throw new PresentationError(PresentationStatus.InvalidArgument, "scopeId"); + } + private async request(activityLoggingContext: ActivityLoggingContext, imodel: IModelDb, params: string, reviver?: (key: string, value: any) => any): Promise { activityLoggingContext.enter(); const imodelAddon = this.getNativePlatform().getImodelAddon(imodel); @@ -300,10 +432,15 @@ export default class PresentationManager { private createRequestParams(requestId: string, genericOptions: Paged>, additionalOptions?: object): string { const { imodel, locale, ...genericOptionsStripped } = genericOptions; + + let lowerCaseLocale = locale ? locale : this.activeLocale; + if (lowerCaseLocale) + lowerCaseLocale = lowerCaseLocale.toLowerCase(); + const request = { requestId, params: { - locale: locale ? locale : this.activeLocale, + locale: lowerCaseLocale, ...genericOptionsStripped, ...additionalOptions, }, diff --git a/presentation/backend/src/PresentationRpcImpl.ts b/presentation/backend/src/PresentationRpcImpl.ts index f5c93d0..781cfcc 100644 --- a/presentation/backend/src/PresentationRpcImpl.ts +++ b/presentation/backend/src/PresentationRpcImpl.ts @@ -5,7 +5,7 @@ /** @module RPC */ import { ActivityLoggingContext } from "@bentley/bentleyjs-core"; -import { IModelToken } from "@bentley/imodeljs-common"; +import { IModelToken, EntityProps } from "@bentley/imodeljs-common"; import { IModelDb } from "@bentley/imodeljs-backend"; import { PresentationRpcInterface, @@ -15,17 +15,18 @@ import { Paged, RequestOptions, InstanceKey, KeySet, RulesetManagerState, RulesetVariablesState, Omit, + SelectionScope, + NodesResponse, ContentResponse, RpcResponse, + PresentationRpcResponse, RpcRequestOptions, + HierarchyRpcRequestOptions, ContentRpcRequestOptions, + SelectionScopeRpcRequestOptions, ClientStateSyncRequestOptions, } from "@bentley/presentation-common"; -import { - RpcRequestOptions, - HierarchyRpcRequestOptions, - ContentRpcRequestOptions, - ClientStateSyncRequestOptions, -} from "@bentley/presentation-common/lib/PresentationRpcInterface"; import Presentation from "./Presentation"; import PresentationManager from "./PresentationManager"; import RulesetVariablesManager from "./RulesetVariablesManager"; +type ContentGetter = (actx: ActivityLoggingContext, requestOptions: any) => TResult; + /** * The backend implementation of PresentationRpcInterface. All it's basically * responsible for is forwarding calls to [[Presentation.manager]]. @@ -47,6 +48,23 @@ export default class PresentationRpcImpl extends PresentationRpcInterface { this._clientStateIds = new Map(); } + /** Returns an ok response with result inside */ + private successResponse(result: TResult): RpcResponse { + return { + statusCode: PresentationStatus.Success, + result, + }; + } + + /** Returns a bad request response with empty result and an error code */ + private errorResponse(errorCode: PresentationStatus, errorMessage?: string): RpcResponse { + return { + statusCode: errorCode, + result: undefined, + errorMessage, + }; + } + /** * Get the [[PresentationManager]] used by this RPC impl. */ @@ -61,119 +79,155 @@ export default class PresentationRpcImpl extends PresentationRpcInterface { return imodel; } - private toIModelDbOptions, "imodel">)>(token: IModelToken, options: TOptions) { - const { clientId, knownBackendIds, ...requestOptions } = options as any; - return Object.assign({}, requestOptions, { - imodel: this.getIModel(token), - }); + private toIModelDbOptions, "imodel" | "rulesetId">)>(token: IModelToken, options: TOptions) { + const { clientId, clientStateId, ...requestOptions } = options; + + return { ...requestOptions, imodel: this.getIModel(token) }; } private verifyRequest(request: RpcRequestOptions) { if (!request.clientStateId) { // client has no state of its own - return; + return PresentationStatus.Success; } const clientId = request.clientId || ""; const storedClientStateId = this._clientStateIds.get(clientId); if (!storedClientStateId || storedClientStateId !== request.clientStateId) { // client state needs to be synced - throw new PresentationError(PresentationStatus.BackendOutOfSync); + return PresentationStatus.BackendOutOfSync; } + + return PresentationStatus.Success; } - public async getRootNodes(token: IModelToken, requestOptions: Paged): Promise { - const actx = ActivityLoggingContext.current; actx.enter(); - this.verifyRequest(requestOptions); - const rootNodes: Node[] = [...await this.getManager(requestOptions.clientId).getRootNodes(actx, this.toIModelDbOptions(token, requestOptions))]; + private async makeRequest(token: IModelToken, requestOptions: any, request: ContentGetter>): PresentationRpcResponse { + const actx = ActivityLoggingContext.current; actx.enter(); - return rootNodes; - } - public async getRootNodesCount(token: IModelToken, requestOptions: HierarchyRpcRequestOptions): Promise { - const actx = ActivityLoggingContext.current; actx.enter(); - this.verifyRequest(requestOptions); - const rootNodesCount: number = await this.getManager(requestOptions.clientId).getRootNodesCount(actx, this.toIModelDbOptions(token, requestOptions)); + const status = this.verifyRequest(requestOptions); + if (status !== PresentationStatus.Success) + return this.errorResponse(status); + + let options: {}; + try { + options = this.toIModelDbOptions(token, requestOptions); + } catch (e) { + return this.errorResponse((e as PresentationError).errorNumber, (e as PresentationError).message); + } + + const result = await request(actx, options) as TResult; actx.enter(); - return rootNodesCount; + return this.successResponse(result); } - public async getChildren(token: IModelToken, requestOptions: Paged, parentKey: Readonly): Promise { - const actx = ActivityLoggingContext.current; actx.enter(); - this.verifyRequest(requestOptions); - const children: Node[] = [...await this.getManager(requestOptions.clientId).getChildren(actx, this.toIModelDbOptions(token, requestOptions), parentKey)]; - actx.enter(); - return children; + public async getNodesAndCount(token: IModelToken, requestOptions: Paged, parentKey?: Readonly): PresentationRpcResponse { + const contentGetter: ContentGetter> = async (actx, options) => + this.getManager(requestOptions.clientId).getNodesAndCount(actx, options, parentKey); + + return this.makeRequest(token, requestOptions, contentGetter); } - public async getChildrenCount(token: IModelToken, requestOptions: HierarchyRpcRequestOptions, parentKey: Readonly): Promise { - const actx = ActivityLoggingContext.current; actx.enter(); - this.verifyRequest(requestOptions); - const childrenCount: number = await this.getManager(requestOptions.clientId).getChildrenCount(actx, this.toIModelDbOptions(token, requestOptions), parentKey); - actx.enter(); - return childrenCount; + public async getNodes(token: IModelToken, requestOptions: Paged, parentKey?: Readonly): PresentationRpcResponse { + const contentGetter: ContentGetter> = async (actx, options) => [ + ...await this.getManager(requestOptions.clientId).getNodes(actx, options, parentKey), + ]; + + return this.makeRequest(token, requestOptions, contentGetter); } - public async getNodePaths(token: IModelToken, requestOptions: HierarchyRpcRequestOptions, paths: InstanceKey[][], markedIndex: number): Promise { - const actx = ActivityLoggingContext.current; actx.enter(); - this.verifyRequest(requestOptions); - const nodePaths: NodePathElement[] = await this.getManager(requestOptions.clientId).getNodePaths(actx, this.toIModelDbOptions(token, requestOptions), paths, markedIndex); - actx.enter(); - return nodePaths; + public async getNodesCount(token: IModelToken, requestOptions: HierarchyRpcRequestOptions, parentKey?: Readonly): PresentationRpcResponse { + const contentGetter: ContentGetter> = (actx, options) => + this.getManager(requestOptions.clientId).getNodesCount(actx, options, parentKey); + + return this.makeRequest(token, requestOptions, contentGetter); } - public async getFilteredNodePaths(token: IModelToken, requestOptions: HierarchyRpcRequestOptions, filterText: string): Promise { - const actx = ActivityLoggingContext.current; actx.enter(); - this.verifyRequest(requestOptions); - const filteredNodePaths: NodePathElement[] = await this.getManager(requestOptions.clientId).getFilteredNodePaths(actx, this.toIModelDbOptions(token, requestOptions), filterText); - actx.enter(); - return filteredNodePaths; + public async getNodePaths(token: IModelToken, requestOptions: HierarchyRpcRequestOptions, paths: InstanceKey[][], markedIndex: number): PresentationRpcResponse { + const contentGetter: ContentGetter> = (actx, options) => + this.getManager(requestOptions.clientId).getNodePaths(actx, options, paths, markedIndex); + + return this.makeRequest(token, requestOptions, contentGetter); } - public async getContentDescriptor(token: IModelToken, requestOptions: ContentRpcRequestOptions, displayType: string, keys: Readonly, selection: Readonly | undefined): Promise | undefined> { - const actx = ActivityLoggingContext.current; actx.enter(); - this.verifyRequest(requestOptions); - const descriptor = await this.getManager(requestOptions.clientId).getContentDescriptor(actx, this.toIModelDbOptions(token, requestOptions), displayType, keys, selection); - actx.enter(); - if (descriptor) - descriptor.resetParentship(); - return descriptor; + public async getFilteredNodePaths(token: IModelToken, requestOptions: HierarchyRpcRequestOptions, filterText: string): PresentationRpcResponse { + const contentGetter: ContentGetter> = (actx, options) => + this.getManager(requestOptions.clientId).getFilteredNodePaths(actx, options, filterText); + + return this.makeRequest(token, requestOptions, contentGetter); } - public async getContentSetSize(token: IModelToken, requestOptions: ContentRpcRequestOptions, descriptor: Readonly, keys: Readonly): Promise { - const actx = ActivityLoggingContext.current; actx.enter(); - this.verifyRequest(requestOptions); - const contentSetSize: number = await this.getManager(requestOptions.clientId).getContentSetSize(actx, this.toIModelDbOptions(token, requestOptions), descriptor, keys); - actx.enter(); - return contentSetSize; + public async getContentDescriptor(token: IModelToken, requestOptions: ContentRpcRequestOptions, displayType: string, keys: Readonly, selection: Readonly | undefined): PresentationRpcResponse | undefined> { + const contentGetter: ContentGetter | undefined>> = async (actx, options) => { + const descriptor = await this.getManager(requestOptions.clientId).getContentDescriptor(actx, options, displayType, keys, selection); + actx.enter(); + if (descriptor) + descriptor.resetParentship(); + return descriptor; + }; + + return this.makeRequest(token, requestOptions, contentGetter); } - public async getContent(token: IModelToken, requestOptions: Paged, descriptor: Readonly, keys: Readonly): Promise> { - const actx = ActivityLoggingContext.current; actx.enter(); - this.verifyRequest(requestOptions); - const content: Content = await this.getManager(requestOptions.clientId).getContent(actx, this.toIModelDbOptions(token, requestOptions), descriptor, keys); - actx.enter(); - content.descriptor.resetParentship(); - return content; + public async getContentSetSize(token: IModelToken, requestOptions: ContentRpcRequestOptions, descriptorOrDisplayType: Readonly | string, keys: Readonly): PresentationRpcResponse { + const contentGetter: ContentGetter> = (actx, options) => + this.getManager(requestOptions.clientId).getContentSetSize(actx, options, descriptorOrDisplayType, keys); + + return this.makeRequest(token, requestOptions, contentGetter); } - public async getDistinctValues(token: IModelToken, requestOptions: ContentRpcRequestOptions, descriptor: Readonly, keys: Readonly, fieldName: string, maximumValueCount: number): Promise { - const actx = ActivityLoggingContext.current; actx.enter(); - this.verifyRequest(requestOptions); - const distinctValues: string[] = await this.getManager(requestOptions.clientId).getDistinctValues(actx, this.toIModelDbOptions(token, requestOptions), descriptor, keys, fieldName, maximumValueCount); - actx.enter(); - return distinctValues; + public async getContentAndSize(token: IModelToken, requestOptions: ContentRpcRequestOptions, descriptorOrDisplayType: Readonly | string, keys: Readonly): PresentationRpcResponse> { + const contentGetter: ContentGetter>> = async (actx, options) => { + const contentResult = await this.getManager(requestOptions.clientId).getContentAndSize(actx, options, descriptorOrDisplayType, keys); + actx.enter(); + contentResult.content.descriptor.resetParentship(); + return contentResult; + }; + + return this.makeRequest(token, requestOptions, contentGetter); } - public async syncClientState(_token: IModelToken, options: ClientStateSyncRequestOptions): Promise { + public async getContent(token: IModelToken, requestOptions: Paged, descriptorOrDisplayType: Readonly | string, keys: Readonly): PresentationRpcResponse> { + const contentGetter: ContentGetter>> = async (actx, options) => { + const content: Content = await this.getManager(requestOptions.clientId).getContent(actx, options, descriptorOrDisplayType, keys); + actx.enter(); + content.descriptor.resetParentship(); + return content; + }; + + return this.makeRequest(token, requestOptions, contentGetter); + } + + public async getDistinctValues(token: IModelToken, requestOptions: ContentRpcRequestOptions, descriptor: Readonly, keys: Readonly, fieldName: string, maximumValueCount: number): PresentationRpcResponse { + const contentGetter: ContentGetter> = (actx, options) => + this.getManager(requestOptions.clientId).getDistinctValues(actx, options, descriptor, keys, fieldName, maximumValueCount); + + return this.makeRequest(token, requestOptions, contentGetter); + } + + public async getSelectionScopes(token: IModelToken, requestOptions: SelectionScopeRpcRequestOptions): PresentationRpcResponse { + const contentGetter: ContentGetter> = (actx, options) => + this.getManager(requestOptions.clientId).getSelectionScopes(actx, options); + + return this.makeRequest(token, requestOptions, contentGetter); + } + + public async computeSelection(token: IModelToken, requestOptions: SelectionScopeRpcRequestOptions, keys: Readonly, scopeId: string): PresentationRpcResponse { + const contentGetter: ContentGetter> = (actx, options) => + this.getManager(requestOptions.clientId).computeSelection(actx, options, keys, scopeId); + + return this.makeRequest(token, requestOptions, contentGetter); + } + + public async syncClientState(_token: IModelToken, options: ClientStateSyncRequestOptions): PresentationRpcResponse { const actx = ActivityLoggingContext.current; actx.enter(); if (!options.clientStateId) - throw new PresentationError(PresentationStatus.InvalidArgument, "clientStateId must be set when syncing with client state"); + return this.errorResponse(PresentationStatus.InvalidArgument, "clientStateId must be set when syncing with client state"); if (options.state.hasOwnProperty(RulesetManagerState.STATE_ID)) { const rulesetsState = options.state[RulesetManagerState.STATE_ID]; if (!Array.isArray(rulesetsState)) - throw new PresentationError(PresentationStatus.InvalidArgument, "rulesets in client state should be an array"); + return this.errorResponse(PresentationStatus.InvalidArgument, "rulesets in client state should be an array"); await this.syncClientRulesetsState(actx, options.clientId, rulesetsState); actx.enter(); } @@ -181,19 +235,21 @@ export default class PresentationRpcImpl extends PresentationRpcInterface { if (options.state.hasOwnProperty(RulesetVariablesState.STATE_ID)) { const varsState = options.state[RulesetVariablesState.STATE_ID]; if (typeof varsState !== "object") - throw new PresentationError(PresentationStatus.InvalidArgument, "ruleset variables in client state should be an array"); + return this.errorResponse(PresentationStatus.InvalidArgument, "ruleset variables in client state should be an array"); await this.syncClientRulesetVariablesState(actx, options.clientId, varsState as RulesetVariablesState); actx.enter(); } this._clientStateIds.set(options.clientId || "", options.clientStateId); + + return this.successResponse(undefined); } private async syncClientRulesetsState(actx: ActivityLoggingContext, clientId: string | undefined, rulesets: RulesetManagerState): Promise { actx.enter(); const manager = this.getManager(clientId).rulesets(); manager.clear(); - Promise.all(rulesets.map((r) => manager.add(r))); + await Promise.all(rulesets.map((r) => manager.add(r))); } private async syncClientRulesetVariablesState(actx: ActivityLoggingContext, clientId: string | undefined, vars: RulesetVariablesState): Promise { diff --git a/presentation/backend/src/test/PresentationManager.test.snap b/presentation/backend/src/test/PresentationManager.test.snap index d86d209..254203b 100644 --- a/presentation/backend/src/test/PresentationManager.test.snap +++ b/presentation/backend/src/test/PresentationManager.test.snap @@ -28,6 +28,62 @@ Array [ ] `; +exports[`PresentationManager addon results conversion to Presentation objects returns child nodes and child node count when requesting first page 1`] = ` +Array [ + Object { + "key": Object { + "instanceKey": Object { + "className": "sky blue", + "id": "0x32b90000008f73", + }, + "pathFromRoot": Array [ + "p1", + ], + "type": "ECInstanceNode", + }, + "label": "test2", + }, + Object { + "key": Object { + "pathFromRoot": Array [ + "p1", + "p3", + ], + "type": "type 2", + }, + "label": "test3", + }, +] +`; + +exports[`PresentationManager addon results conversion to Presentation objects returns child nodes without child node count when requesting non-first page 1`] = ` +Array [ + Object { + "key": Object { + "instanceKey": Object { + "className": "generating", + "id": "0x1051d00000143b5", + }, + "pathFromRoot": Array [ + "p1", + ], + "type": "ECInstanceNode", + }, + "label": "test2", + }, + Object { + "key": Object { + "pathFromRoot": Array [ + "p1", + "p3", + ], + "type": "type 2", + }, + "label": "test3", + }, +] +`; + exports[`PresentationManager addon results conversion to Presentation objects returns content 1`] = ` Content { "contentSet": Array [ @@ -106,6 +162,84 @@ Content { } `; +exports[`PresentationManager addon results conversion to Presentation objects returns content and content set size when requesting first page 1`] = ` +Content { + "contentSet": Array [ + Item { + "classInfo": Object { + "id": "0x1078000000b2f3", + "label": "Self-enabling Berkshire generate", + "name": "hacking", + }, + "displayValues": Object { + "American Samoa": "Square Orchard transmitter", + }, + "imageId": "abd3d999-d7fc-432f-80b0-433c5521c5e5", + "label": "Road invoice", + "mergedFieldNames": Array [], + "primaryKeys": Array [ + Object { + "className": "copying", + "id": "0xa5170000012d70", + }, + ], + "values": Object { + "American Samoa": "Buckinghamshire aggregate", + }, + }, + ], + "descriptor": Descriptor { + "contentFlags": 0, + "displayType": "voluptatibus neque dolor", + "fields": Array [ + PropertiesField { + "category": Object { + "description": "Consequatur atque architecto.", + "expand": false, + "label": "Functionality USB", + "name": "visualize", + "priority": 94970, + }, + "isReadonly": false, + "label": "Toys Product connecting", + "name": "American Samoa", + "priority": 74427, + "properties": Array [ + Object { + "property": Object { + "classInfo": Object { + "id": "0x16cb60000003d1f", + "label": "Credit Card Account Bedfordshire Senior", + "name": "Forward", + }, + "name": "hardware", + "type": "string", + }, + "relatedClassPath": Array [], + }, + ], + "type": Object { + "typeName": "string", + "valueFormat": "Primitive", + }, + }, + ], + "selectClasses": Array [ + Object { + "isSelectPolymorphic": true, + "pathToPrimaryClass": Array [], + "relatedPropertyPaths": Array [], + "selectClassInfo": Object { + "id": "0x5790000000fdbb", + "label": "grey SMTP", + "name": "Home", + }, + }, + ], + }, +} +`; + exports[`PresentationManager addon results conversion to Presentation objects returns content descriptor 1`] = ` Descriptor { "connectionId": "019bdefa-36ec-4665-86ff-3d07a05f70a7", @@ -357,6 +491,162 @@ Descriptor { } `; +exports[`PresentationManager addon results conversion to Presentation objects returns content when display types is passed in stead of descriptor 1`] = ` +Content { + "contentSet": Array [ + Item { + "classInfo": Object { + "id": "0x149c20000003d04", + "label": "Handcrafted Soft Computer Small Metal Keyboard", + "name": "Soft", + }, + "displayValues": Object { + "Qatar": "North Dakota methodologies", + }, + "imageId": "a5d7cbc0-aba1-4795-8f38-7f091502b73b", + "label": "Rustic purple", + "mergedFieldNames": Array [], + "primaryKeys": Array [ + Object { + "className": "wireless", + "id": "0x74310000016e96", + }, + ], + "values": Object { + "Qatar": "monetize", + }, + }, + ], + "descriptor": Descriptor { + "contentFlags": 0, + "displayType": "et ut veritatis", + "fields": Array [ + PropertiesField { + "category": Object { + "description": "Modi odio et ratione earum nesciunt excepturi tempora.", + "expand": true, + "label": "open architecture green", + "name": "Architect", + "priority": 64996, + }, + "isReadonly": true, + "label": "user-centric Chief Metal", + "name": "Qatar", + "priority": 39335, + "properties": Array [ + Object { + "property": Object { + "classInfo": Object { + "id": "0x748300000056d0", + "label": "Markets deposit", + "name": "invoice", + }, + "name": "maximize", + "type": "string", + }, + "relatedClassPath": Array [], + }, + ], + "type": Object { + "typeName": "string", + "valueFormat": "Primitive", + }, + }, + ], + "selectClasses": Array [ + Object { + "isSelectPolymorphic": true, + "pathToPrimaryClass": Array [], + "relatedPropertyPaths": Array [], + "selectClassInfo": Object { + "id": "0x9ec80000002ca6", + "label": "Borders monitor static", + "name": "embrace", + }, + }, + ], + }, +} +`; + +exports[`PresentationManager addon results conversion to Presentation objects returns content without content set size when requesting non-first page 1`] = ` +Content { + "contentSet": Array [ + Item { + "classInfo": Object { + "id": "0x75c6000000ccc4", + "label": "Money Market Account Marketing", + "name": "Salad", + }, + "displayValues": Object { + "matrix": "lavender Associate", + }, + "imageId": "b9636150-5306-4c7b-b513-10dba199d813", + "label": "Refined Generic Cotton Salad", + "mergedFieldNames": Array [], + "primaryKeys": Array [ + Object { + "className": "Lead", + "id": "0x13b6a000000e575", + }, + ], + "values": Object { + "matrix": "override", + }, + }, + ], + "descriptor": Descriptor { + "contentFlags": 0, + "displayType": "blanditiis dolor similique", + "fields": Array [ + PropertiesField { + "category": Object { + "description": "Blanditiis et deserunt.", + "expand": false, + "label": "Alabama", + "name": "withdrawal", + "priority": 21980, + }, + "isReadonly": false, + "label": "deposit THX", + "name": "matrix", + "priority": 92094, + "properties": Array [ + Object { + "property": Object { + "classInfo": Object { + "id": "0x10494000000edb9", + "label": "discrete Jamaica impactful", + "name": "Latvian Lats", + }, + "name": "backing up", + "type": "string", + }, + "relatedClassPath": Array [], + }, + ], + "type": Object { + "typeName": "string", + "valueFormat": "Primitive", + }, + }, + ], + "selectClasses": Array [ + Object { + "isSelectPolymorphic": true, + "pathToPrimaryClass": Array [], + "relatedPropertyPaths": Array [], + "selectClassInfo": Object { + "id": "0x1698e00000077e3", + "label": "synergistic COM Assistant", + "name": "Money Market Account", + }, + }, + ], + }, +} +`; + exports[`PresentationManager addon results conversion to Presentation objects returns filtered node paths 1`] = ` Array [ Object { @@ -496,3 +786,129 @@ Array [ }, ] `; + +exports[`PresentationManager addon results conversion to Presentation objects returns root nodes and root nodes count when requesting first page 1`] = ` +Array [ + Object { + "backColor": "backColor1", + "description": "description1", + "fontStyle": "fontStyle1", + "foreColor": "foreColor1", + "hasChildren": true, + "imageId": "img_1", + "isCheckboxEnabled": true, + "isCheckboxVisible": true, + "isChecked": true, + "isEditable": true, + "isExpanded": true, + "isSelectionDisabled": true, + "key": Object { + "pathFromRoot": Array [ + "p1", + "p2", + "p3", + ], + "type": "type1", + }, + "label": "test1", + }, + Object { + "backColor": "", + "description": "description2", + "fontStyle": "", + "foreColor": "", + "hasChildren": false, + "imageId": "", + "isCheckboxEnabled": false, + "isCheckboxVisible": false, + "isChecked": false, + "isEditable": false, + "isExpanded": false, + "isSelectionDisabled": false, + "key": Object { + "instanceKey": Object { + "className": "deposit", + "id": "0xe93600000142ea", + }, + "pathFromRoot": Array [ + "p1", + ], + "type": "ECInstanceNode", + }, + "label": "test2", + }, + Object { + "key": Object { + "pathFromRoot": Array [ + "p1", + "p3", + ], + "type": "some node", + }, + "label": "test2", + }, +] +`; + +exports[`PresentationManager addon results conversion to Presentation objects returns root nodes without nodes count when requesting non-first page 1`] = ` +Array [ + Object { + "backColor": "backColor1", + "description": "description1", + "fontStyle": "fontStyle1", + "foreColor": "foreColor1", + "hasChildren": true, + "imageId": "img_1", + "isCheckboxEnabled": true, + "isCheckboxVisible": true, + "isChecked": true, + "isEditable": true, + "isExpanded": true, + "isSelectionDisabled": true, + "key": Object { + "pathFromRoot": Array [ + "p1", + "p2", + "p3", + ], + "type": "type1", + }, + "label": "test1", + }, + Object { + "backColor": "", + "description": "description2", + "fontStyle": "", + "foreColor": "", + "hasChildren": false, + "imageId": "", + "isCheckboxEnabled": false, + "isCheckboxVisible": false, + "isChecked": false, + "isEditable": false, + "isExpanded": false, + "isSelectionDisabled": false, + "key": Object { + "instanceKey": Object { + "className": "Refined Wooden Car", + "id": "0x8f6d000000cfde", + }, + "pathFromRoot": Array [ + "p1", + ], + "type": "ECInstanceNode", + }, + "label": "test2", + }, + Object { + "key": Object { + "pathFromRoot": Array [ + "p1", + "p3", + ], + "type": "some node", + }, + "label": "test2", + }, +] +`; diff --git a/presentation/backend/src/test/PresentationManager.test.ts b/presentation/backend/src/test/PresentationManager.test.ts index 3521c4c..c580f58 100644 --- a/presentation/backend/src/test/PresentationManager.test.ts +++ b/presentation/backend/src/test/PresentationManager.test.ts @@ -12,12 +12,13 @@ import { createRandomECInstanceNodeKeyJSON, createRandomECClassInfoJSON, createRandomRelationshipPathJSON, createRandomECInstanceKeyJSON, createRandomECInstanceKey, - createRandomDescriptor, createRandomCategory, + createRandomDescriptor, createRandomCategory, createRandomEntityProps, createRandomId, } from "@bentley/presentation-common/lib/test/_helpers/random"; import "@bentley/presentation-common/lib/test/_helpers/Promises"; import "./IModelHostSetup"; import { using, ActivityLoggingContext } from "@bentley/bentleyjs-core"; -import { IModelHost, IModelDb } from "@bentley/imodeljs-backend"; +import { RelatedElementProps, EntityMetaData } from "@bentley/imodeljs-common"; +import { IModelHost, IModelDb, Element, DrawingGraphic } from "@bentley/imodeljs-backend"; import { PageOptions, SelectionInfo, KeySet, PresentationError, PropertyInfoJSON, HierarchyRequestOptions, Paged, ContentRequestOptions } from "@bentley/presentation-common"; import { instanceKeyFromJSON } from "@bentley/presentation-common"; import { NodeJSON } from "@bentley/presentation-common"; @@ -128,7 +129,7 @@ describe("PresentationManager", () => { const rulesetId = faker.random.word(); const locale = faker.random.locale(); using(new PresentationManager({ addon: addonMock.object, activeLocale: locale }), async (manager) => { - await manager.getRootNodesCount(ActivityLoggingContext.current, { imodel: imodelMock.object, rulesetId }); + await manager.getNodesCount(ActivityLoggingContext.current, { imodel: imodelMock.object, rulesetId }); addonMock.verify((x) => x.handleRequest(ActivityLoggingContext.current, moq.It.isAny(), moq.It.is((serializedRequest: string): boolean => { const request = JSON.parse(serializedRequest); return request.params.locale === locale; @@ -142,7 +143,7 @@ describe("PresentationManager", () => { const locale = faker.random.locale(); using(new PresentationManager({ addon: addonMock.object, activeLocale: faker.random.locale() }), async (manager) => { expect(manager.activeLocale).to.not.eq(locale); - await manager.getRootNodesCount(ActivityLoggingContext.current, { imodel: imodelMock.object, rulesetId, locale }); + await manager.getNodesCount(ActivityLoggingContext.current, { imodel: imodelMock.object, rulesetId, locale }); addonMock.verify((x) => x.handleRequest(ActivityLoggingContext.current, moq.It.isAny(), moq.It.is((serializedRequest: string): boolean => { const request = JSON.parse(serializedRequest); return request.params.locale === locale; @@ -309,7 +310,7 @@ describe("PresentationManager", () => { rulesetId: testData.rulesetId, paging: testData.pageOptions, }; - const result = await manager.getRootNodes(ActivityLoggingContext.current, options); + const result = await manager.getNodes(ActivityLoggingContext.current, options); verifyWithSnapshot(result, expectedParams); }); @@ -331,10 +332,89 @@ describe("PresentationManager", () => { imodel: imodelMock.object, rulesetId: testData.rulesetId, }; - const result = await manager.getRootNodesCount(ActivityLoggingContext.current, options); + const result = await manager.getNodesCount(ActivityLoggingContext.current, options); verifyWithExpectedResult(result, addonResponse, expectedParams); }); + it("returns root nodes and root nodes count when requesting first page", async () => { + // what the addon receives + const pageOptions = { start: 0, size: 2 }; + const expectedGetRootNodesParams = { + requestId: NativePlatformRequestTypes.GetRootNodes, + params: { + paging: pageOptions, + rulesetId: testData.rulesetId, + }, + }; + const expectedGetRootNodesCountParams = { + requestId: NativePlatformRequestTypes.GetRootNodesCount, + params: { + rulesetId: testData.rulesetId, + paging: pageOptions, + }, + }; + + // what the addon returns + const addonGetRootNodesResponse: NodeJSON[] = [{ + key: { + type: "type1", + pathFromRoot: ["p1", "p2", "p3"], + } as NodeKeyJSON, + label: "test1", + description: "description1", + imageId: "img_1", + foreColor: "foreColor1", + backColor: "backColor1", + fontStyle: "fontStyle1", + hasChildren: true, + isSelectionDisabled: true, + isEditable: true, + isChecked: true, + isCheckboxVisible: true, + isCheckboxEnabled: true, + isExpanded: true, + }, { + key: { + type: "ECInstanceNode", + pathFromRoot: ["p1"], + instanceKey: createRandomECInstanceKeyJSON(), + } as ECInstanceNodeKeyJSON, + label: "test2", + description: "description2", + imageId: "", + foreColor: "", + backColor: "", + fontStyle: "", + hasChildren: false, + isSelectionDisabled: false, + isEditable: false, + isChecked: false, + isCheckboxVisible: false, + isCheckboxEnabled: false, + isExpanded: false, + }, { + key: { + type: "some node", + pathFromRoot: ["p1", "p3"], + } as NodeKeyJSON, + label: "test2", + }]; + const addonGetRootNodesCountResponse = 456; + + setup(addonGetRootNodesCountResponse); + setup(addonGetRootNodesResponse); + + const options: Paged> = { + imodel: imodelMock.object, + rulesetId: testData.rulesetId, + paging: pageOptions, + }; + const result = await manager.getNodesAndCount(ActivityLoggingContext.current, options); + + verifyWithSnapshot(result.nodes, expectedGetRootNodesParams); + verifyWithExpectedResult(result.count, addonGetRootNodesCountResponse, expectedGetRootNodesCountParams); + }); + it("returns child nodes", async () => { // what the addon receives const parentNodeKeyJSON = createRandomECInstanceNodeKeyJSON(); @@ -370,7 +450,7 @@ describe("PresentationManager", () => { rulesetId: testData.rulesetId, paging: testData.pageOptions, }; - const result = await manager.getChildren(ActivityLoggingContext.current, options, nodeKeyFromJSON(parentNodeKeyJSON)); + const result = await manager.getNodes(ActivityLoggingContext.current, options, nodeKeyFromJSON(parentNodeKeyJSON)); verifyWithSnapshot(result, expectedParams); }); @@ -394,10 +474,63 @@ describe("PresentationManager", () => { imodel: imodelMock.object, rulesetId: testData.rulesetId, }; - const result = await manager.getChildrenCount(ActivityLoggingContext.current, options, nodeKeyFromJSON(parentNodeKeyJSON)); + const result = await manager.getNodesCount(ActivityLoggingContext.current, options, nodeKeyFromJSON(parentNodeKeyJSON)); verifyWithExpectedResult(result, addonResponse, expectedParams); }); + it("returns child nodes and child node count when requesting first page", async () => { + // what the addon receives + const pageOptions = { start: 0, size: 2 }; + const parentNodeKeyJSON = createRandomECInstanceNodeKeyJSON(); + const expectedGetChildNodesParams = { + requestId: NativePlatformRequestTypes.GetChildren, + params: { + nodeKey: parentNodeKeyJSON, + rulesetId: testData.rulesetId, + paging: pageOptions, + }, + }; + const expectedGetChildNodeCountParams = { + requestId: NativePlatformRequestTypes.GetChildrenCount, + params: { + nodeKey: parentNodeKeyJSON, + rulesetId: testData.rulesetId, + paging: pageOptions, + }, + }; + + // what the addon returns + const addonGetChildNodesResponse: NodeJSON[] = [{ + key: { + type: "ECInstanceNode", + pathFromRoot: ["p1"], + instanceKey: createRandomECInstanceKeyJSON(), + } as ECInstanceNodeKeyJSON, + label: "test2", + }, { + key: { + type: "type 2", + pathFromRoot: ["p1", "p3"], + } as NodeKeyJSON, + label: "test3", + }]; + const addonGetChildNodeCountResponse = 789; + + setup(addonGetChildNodeCountResponse); + setup(addonGetChildNodesResponse); + + // test + const options: Paged> = { + imodel: imodelMock.object, + rulesetId: testData.rulesetId, + paging: pageOptions, + }; + const result = await manager.getNodesAndCount(ActivityLoggingContext.current, options, nodeKeyFromJSON(parentNodeKeyJSON)); + + verifyWithSnapshot(result.nodes, expectedGetChildNodesParams); + verifyWithExpectedResult(result.count, addonGetChildNodeCountResponse, expectedGetChildNodeCountParams); + }); + it("returns filtered node paths", async () => { // what the addon receives const expectedParams = { @@ -626,6 +759,36 @@ describe("PresentationManager", () => { verifyWithExpectedResult(result, addonResponse, expectedParams); }); + it("returns content set size when display type is passed in stead of descriptor", async () => { + // what the addon receives + const keys = new KeySet([createRandomECInstanceNodeKey(), createRandomECInstanceKey()]); + const descriptor = createRandomDescriptor(); + const expectedParams = { + requestId: NativePlatformRequestTypes.GetContentSetSize, + params: { + keys: keys.toJSON(), + descriptorOverrides: { + displayType: descriptor.displayType, + hiddenFieldNames: [], + contentFlags: 0, + }, + rulesetId: testData.rulesetId, + }, + }; + + // what the addon returns + const addonResponse = faker.random.number(); + setup(addonResponse); + + // test + const options: ContentRequestOptions = { + imodel: imodelMock.object, + rulesetId: testData.rulesetId, + }; + const result = await manager.getContentSetSize(ActivityLoggingContext.current, options, descriptor.displayType, keys); + verifyWithExpectedResult(result, addonResponse, expectedParams); + }); + it("returns content", async () => { // what the addon receives const keys = new KeySet([createRandomECInstanceNodeKey(), createRandomECInstanceKey()]); @@ -698,6 +861,169 @@ describe("PresentationManager", () => { verifyWithSnapshot(result, expectedParams); }); + it("returns content when display types is passed in stead of descriptor", async () => { + // what the addon receives + const keys = new KeySet([createRandomECInstanceNodeKey(), createRandomECInstanceKey()]); + const descriptor = createRandomDescriptor(); + const expectedParams = { + requestId: NativePlatformRequestTypes.GetContent, + params: { + keys: keys.toJSON(), + descriptorOverrides: { + displayType: descriptor.displayType, + hiddenFieldNames: [], + contentFlags: 0, + }, + paging: testData.pageOptions, + rulesetId: testData.rulesetId, + }, + }; + + // what the addon returns + const fieldName = faker.random.word(); + const addonResponse = { + descriptor: { + displayType: descriptor.displayType, + selectClasses: [{ + selectClassInfo: createRandomECClassInfoJSON(), + isSelectPolymorphic: true, + pathToPrimaryClass: [], + relatedPropertyPaths: [], + } as SelectClassInfoJSON], + fields: [{ + name: fieldName, + category: createRandomCategory(), + label: faker.random.words(), + type: { + typeName: "string", + valueFormat: "Primitive", + } as PrimitiveTypeDescription, + isReadonly: faker.random.boolean(), + priority: faker.random.number(), + properties: [{ + property: { + classInfo: createRandomECClassInfoJSON(), + name: faker.random.word(), + type: "string", + } as PropertyInfoJSON, + relatedClassPath: [], + } as PropertyJSON], + } as PropertiesFieldJSON], + contentFlags: 0, + } as DescriptorJSON, + contentSet: [{ + primaryKeys: [createRandomECInstanceKeyJSON()], + classInfo: createRandomECClassInfoJSON(), + label: faker.random.words(), + imageId: faker.random.uuid(), + values: { + [fieldName]: faker.random.words(), + }, + displayValues: { + [fieldName]: faker.random.words(), + }, + mergedFieldNames: [], + } as ItemJSON], + } as ContentJSON; + setup(addonResponse); + + // test + const options: Paged> = { + imodel: imodelMock.object, + rulesetId: testData.rulesetId, + paging: testData.pageOptions, + }; + const result = await manager.getContent(ActivityLoggingContext.current, options, descriptor.displayType, keys); + verifyWithSnapshot(result, expectedParams); + }); + + it("returns content and content set size when requesting first page", async () => { + // what the addon receives + const pageOptions = { start: 0, size: 2 }; + const keys = new KeySet([createRandomECInstanceNodeKey(), createRandomECInstanceKey()]); + const descriptor = createRandomDescriptor(); + const expectedGetContentParams = { + requestId: NativePlatformRequestTypes.GetContent, + params: { + keys: keys.toJSON(), + descriptorOverrides: descriptor.createDescriptorOverrides(), + paging: pageOptions, + rulesetId: testData.rulesetId, + }, + }; + const expectedGetContentSetSizeParams = { + requestId: NativePlatformRequestTypes.GetContentSetSize, + params: { + keys: keys.toJSON(), + descriptorOverrides: descriptor.createDescriptorOverrides(), + rulesetId: testData.rulesetId, + paging: pageOptions, + }, + }; + + // what the addon returns + const fieldName = faker.random.word(); + const addonGetContentResponse = { + descriptor: { + displayType: descriptor.displayType, + selectClasses: [{ + selectClassInfo: createRandomECClassInfoJSON(), + isSelectPolymorphic: true, + pathToPrimaryClass: [], + relatedPropertyPaths: [], + } as SelectClassInfoJSON], + fields: [{ + name: fieldName, + category: createRandomCategory(), + label: faker.random.words(), + type: { + typeName: "string", + valueFormat: "Primitive", + } as PrimitiveTypeDescription, + isReadonly: faker.random.boolean(), + priority: faker.random.number(), + properties: [{ + property: { + classInfo: createRandomECClassInfoJSON(), + name: faker.random.word(), + type: "string", + } as PropertyInfoJSON, + relatedClassPath: [], + } as PropertyJSON], + } as PropertiesFieldJSON], + contentFlags: 0, + } as DescriptorJSON, + contentSet: [{ + primaryKeys: [createRandomECInstanceKeyJSON()], + classInfo: createRandomECClassInfoJSON(), + label: faker.random.words(), + imageId: faker.random.uuid(), + values: { + [fieldName]: faker.random.words(), + }, + displayValues: { + [fieldName]: faker.random.words(), + }, + mergedFieldNames: [], + } as ItemJSON], + } as ContentJSON; + const addonGetContentSetSizeResponse = faker.random.number(); + + setup(addonGetContentSetSizeResponse); + setup(addonGetContentResponse); + + // test + const options: Paged> = { + imodel: imodelMock.object, + rulesetId: testData.rulesetId, + paging: pageOptions, + }; + const result = await manager.getContentAndSize(ActivityLoggingContext.current, options, descriptor, keys); + + verifyWithSnapshot(result.content, expectedGetContentParams); + verifyWithExpectedResult(result.size, addonGetContentSetSizeResponse, expectedGetContentSetSizeParams); + }); + describe("getDistinctValues", () => { it("returns distinct values", async () => { @@ -766,7 +1092,198 @@ describe("PresentationManager", () => { imodel: imodelMock.object, rulesetId: testData.rulesetId, }; - return expect(manager.getRootNodesCount(ActivityLoggingContext.current, options)).to.eventually.be.rejectedWith(Error); + return expect(manager.getNodesCount(ActivityLoggingContext.current, options)).to.eventually.be.rejectedWith(Error); + }); + + }); + + describe("WIP", () => { + + // the below tests are temporary + + const imodelMock = moq.Mock.ofType(); + const addonMock = moq.Mock.ofType(); + let manager: PresentationManager; + + beforeEach(() => { + imodelMock.reset(); + addonMock.reset(); + manager = new PresentationManager({ addon: addonMock.object }); + }); + + describe("getSelectionScopes", () => { + + it("returns expected selection scopes", async () => { + const result = await manager.getSelectionScopes(ActivityLoggingContext.current, { imodel: imodelMock.object }); + expect(result.map((s) => s.id)).to.deep.eq(["element", "assembly", "top-assembly", "category", "model"]); + }); + + }); + + describe("computeSelection", () => { + + const elementsMock = moq.Mock.ofType(); + + const createRandomTopmostElement = (): Element => { + const mock = moq.Mock.ofType(); + mock.setup((x) => x.parent).returns(() => undefined); + return mock.object; + }; + + const createRandomElement = (parentKey?: RelatedElementProps): Element => { + const mock = moq.Mock.ofType(); + if (!parentKey) + parentKey = { relClassName: faker.random.word(), id: createRandomId() }; + mock.setup((x) => x.parent).returns(() => parentKey); + return mock.object; + }; + + beforeEach(() => { + elementsMock.reset(); + imodelMock.setup((x) => x.elements).returns(() => elementsMock.object); + imodelMock.setup((x) => x.getMetaData(moq.It.isAnyString())).returns((className: string) => new EntityMetaData({ + baseClasses: [], + properties: {}, + ecclass: className, + })); + + /* + const meta = iModel.getMetaData(classFullName); // will load if necessary + for (const propName in meta.properties) { + if (propName) { + const propMeta = meta.properties[propName]; + if (includeCustom || !propMeta.isCustomHandled || propMeta.isCustomHandledOrphan) + func(propName, propMeta); + } + } + + if (wantSuper && meta.baseClasses && meta.baseClasses.length > 0) + meta.baseClasses.forEach((baseClass) => this.forEachMetaData(iModel, baseClass, true, func, includeCustom)); + */ + }); + + it("throws on invalid scopeId", async () => { + await expect(manager.computeSelection(ActivityLoggingContext.current, { imodel: imodelMock.object }, [], "invalid")).to.eventually.be.rejected; + }); + + describe("scope: 'element'", () => { + + it("returns entity keys", async () => { + const keys = [createRandomEntityProps(), createRandomEntityProps()]; + const result = await manager.computeSelection(ActivityLoggingContext.current, { imodel: imodelMock.object }, keys, "element"); + expect(result.size).to.eq(2); + expect(result.has({ className: keys[0].classFullName, id: keys[0].id! })).to.be.true; + expect(result.has({ className: keys[1].classFullName, id: keys[1].id! })).to.be.true; + }); + + }); + + describe("scope: 'assembly'", () => { + + it("returns parent keys", async () => { + const keys = [createRandomEntityProps(), createRandomEntityProps()]; + const elements = [createRandomElement(), createRandomElement()]; + elementsMock.setup((x) => x.getElement(keys[0].id!)).returns(() => elements[0]); + elementsMock.setup((x) => x.getElement(keys[1].id!)).returns(() => elements[1]); + + const result = await manager.computeSelection(ActivityLoggingContext.current, { imodel: imodelMock.object }, keys, "assembly"); + expect(result.size).to.eq(2); + expect(result.has({ className: elements[0].parent!.relClassName!, id: elements[0].parent!.id! })).to.be.true; + expect(result.has({ className: elements[1].parent!.relClassName!, id: elements[1].parent!.id! })).to.be.true; + }); + + it("does not duplicate keys", async () => { + const parentKey = { relClassName: faker.random.word(), id: createRandomId() }; + const keys = [createRandomEntityProps(), createRandomEntityProps()]; + const elements = [createRandomElement(parentKey), createRandomElement(parentKey)]; + elementsMock.setup((x) => x.getElement(keys[0].id!)).returns(() => elements[0]); + elementsMock.setup((x) => x.getElement(keys[1].id!)).returns(() => elements[1]); + + const result = await manager.computeSelection(ActivityLoggingContext.current, { imodel: imodelMock.object }, keys, "assembly"); + expect(result.size).to.eq(1); + expect(result.has({ className: parentKey.relClassName, id: parentKey.id })).to.be.true; + }); + + it("returns element key if it has no parent", async () => { + const keys = [createRandomEntityProps()]; + const element = createRandomTopmostElement(); + elementsMock.setup((x) => x.getElement(keys[0].id!)).returns(() => element); + const result = await manager.computeSelection(ActivityLoggingContext.current, { imodel: imodelMock.object }, keys, "assembly"); + expect(result.size).to.eq(1); + expect(result.has({ className: keys[0].classFullName, id: keys[0].id! })).to.be.true; + }); + + }); + + describe("scope: 'top-assembly'", () => { + + it("returns topmost parent key", async () => { + const grandparentKey = { relClassName: faker.random.word(), id: createRandomId() }; + const parentKey = { relClassName: faker.random.word(), id: createRandomId() }; + const elementProps = createRandomEntityProps(); + const grandparent = createRandomTopmostElement(); + const parent = createRandomElement(grandparentKey); + const element = createRandomElement(parentKey); + elementsMock.setup((x) => x.getElement(elementProps.id!)).returns(() => element); + elementsMock.setup((x) => x.getElement(parentKey.id!)).returns(() => parent); + elementsMock.setup((x) => x.getElement(grandparentKey.id!)).returns(() => grandparent); + + const result = await manager.computeSelection(ActivityLoggingContext.current, { imodel: imodelMock.object }, [elementProps], "top-assembly"); + expect(result.size).to.eq(1); + expect(result.has({ className: grandparentKey.relClassName!, id: grandparentKey.id! })).to.be.true; + }); + + }); + + describe("scope: 'category'", () => { + + it("returns category key", async () => { + const elementProps = createRandomEntityProps(); + const element = new DrawingGraphic({ + id: createRandomId(), + classFullName: faker.random.word(), + model: createRandomId(), + category: createRandomId(), + code: { scope: faker.random.word(), spec: faker.random.word() }, + }, imodelMock.object); + elementsMock.setup((x) => x.getElement(elementProps.id!)).returns(() => element); + + const result = await manager.computeSelection(ActivityLoggingContext.current, { imodel: imodelMock.object }, [elementProps], "category"); + expect(result.size).to.eq(1); + expect(result.has({ className: "BisCore:Category", id: element.category! })).to.be.true; + }); + + it("skips non-geometric elements", async () => { + const elementProps = createRandomEntityProps(); + const element = createRandomElement(); + elementsMock.setup((x) => x.getElement(elementProps.id!)).returns(() => element); + + const result = await manager.computeSelection(ActivityLoggingContext.current, { imodel: imodelMock.object }, [elementProps], "category"); + expect(result.isEmpty).to.be.true; + }); + + }); + + describe("scope: 'model'", () => { + + it("returns model key", async () => { + const elementProps = createRandomEntityProps(); + const element = new DrawingGraphic({ + id: createRandomId(), + classFullName: faker.random.word(), + model: createRandomId(), + category: createRandomId(), + code: { scope: faker.random.word(), spec: faker.random.word() }, + }, imodelMock.object); + elementsMock.setup((x) => x.getElement(elementProps.id!)).returns(() => element); + + const result = await manager.computeSelection(ActivityLoggingContext.current, { imodel: imodelMock.object }, [elementProps], "model"); + expect(result.size).to.eq(1); + expect(result.has({ className: "BisCore:Model", id: element.model! })).to.be.true; + }); + + }); + }); }); diff --git a/presentation/backend/src/test/PresentationRpcImpl.test.ts b/presentation/backend/src/test/PresentationRpcImpl.test.ts index 8b2b968..36af248 100644 --- a/presentation/backend/src/test/PresentationRpcImpl.test.ts +++ b/presentation/backend/src/test/PresentationRpcImpl.test.ts @@ -8,21 +8,17 @@ import * as moq from "@bentley/presentation-common/lib/test/_helpers/Mocks"; import { createRandomECInstanceKey, createRandomECInstanceNodeKey, createRandomECInstanceNode, createRandomNodePathElement, - createRandomDescriptor, createRandomRuleset, createRandomId, + createRandomDescriptor, createRandomRuleset, createRandomId, createRandomSelectionScope, createRandomEntityProps, } from "@bentley/presentation-common/lib/test/_helpers/random"; import { ActivityLoggingContext } from "@bentley/bentleyjs-core"; import { IModelToken } from "@bentley/imodeljs-common"; import { IModelDb } from "@bentley/imodeljs-backend"; import { - PageOptions, KeySet, PresentationError, InstanceKey, - Paged, RulesetManagerState, - HierarchyRequestOptions, ContentRequestOptions, RulesetVariablesState, - Omit, -} from "@bentley/presentation-common"; -import { Node, Descriptor, Content } from "@bentley/presentation-common"; -import { VariableValueTypes } from "@bentley/presentation-common"; -import { - RpcRequestOptions, HierarchyRpcRequestOptions, ClientStateSyncRequestOptions, + RpcRequestOptions, ContentRequestOptions, HierarchyRequestOptions, + ClientStateSyncRequestOptions, SelectionScopeRequestOptions, + HierarchyRpcRequestOptions, RulesetVariablesState, + Node, Descriptor, Content, PageOptions, KeySet, InstanceKey, + Paged, RulesetManagerState, VariableValueTypes, Omit, PresentationStatus, } from "@bentley/presentation-common"; import RulesetVariablesManager from "../RulesetVariablesManager"; import PresentationManager from "../PresentationManager"; @@ -77,19 +73,21 @@ describe("PresentationRpcImpl", () => { actx.enter(); }); - it("throws when using invalid imodel token", async () => { + it("returns invalid argument status code when using invalid imodel token", async () => { IModelDb.find = () => undefined as any; const options: Paged = { ...defaultRpcParams, rulesetId: testData.rulesetId, }; - await expect(impl.getRootNodes(testData.imodelToken, options)).to.eventually.be.rejectedWith(PresentationError); + + const response = await impl.getNodes(testData.imodelToken, options); + expect(response.statusCode).to.equal(PresentationStatus.InvalidArgument); }); describe("verifyRequest", () => { beforeEach(() => { - presentationManagerMock.setup((x) => x.getRootNodesCount(ActivityLoggingContext.current, moq.It.isAny())).returns(async () => faker.random.number()); + presentationManagerMock.setup((x) => x.getNodesCount(ActivityLoggingContext.current, moq.It.isAny())).returns(async () => faker.random.number()); }); it("succeeds if request doesn't specify clientStateId", async () => { @@ -98,7 +96,7 @@ describe("PresentationRpcImpl", () => { clientStateId: undefined, rulesetId: testData.rulesetId, }; - await expect(impl.getRootNodesCount(testData.imodelToken, options)).to.eventually.be.fulfilled; + await expect(impl.getNodesCount(testData.imodelToken, options)).to.eventually.be.fulfilled; }); it("succeeds if clientStateId in request matches current client state id", async () => { @@ -108,18 +106,18 @@ describe("PresentationRpcImpl", () => { rulesetId: testData.rulesetId, }; await impl.syncClientState(testData.imodelToken, { ...defaultRpcParams, clientStateId: options.clientStateId, state: {} }); - await expect(impl.getRootNodesCount(testData.imodelToken, options)).to.eventually.be.fulfilled; + await expect(impl.getNodesCount(testData.imodelToken, options)).to.eventually.be.fulfilled; }); - it("throws if clientStateId in request doesn't match current client state id", async () => { + it("returns BackendOutOfSync status code if clientStateId in request doesn't match current client state id", async () => { const options: HierarchyRpcRequestOptions = { ...defaultRpcParams, clientStateId: undefined, rulesetId: testData.rulesetId, }; - await impl.getRootNodesCount(testData.imodelToken, options); // this sets current client state id - const request = impl.getRootNodesCount(testData.imodelToken, { ...options, clientStateId: faker.random.uuid() }); - await expect(request).to.eventually.be.rejectedWith(PresentationError); + await impl.getNodesCount(testData.imodelToken, options); // this sets current client state id + const response = await impl.getNodesCount(testData.imodelToken, { ...options, clientStateId: faker.random.uuid() }); + expect(response.statusCode).to.equal(PresentationStatus.BackendOutOfSync); }); it("handles undefined clientId", async () => { @@ -129,7 +127,7 @@ describe("PresentationRpcImpl", () => { rulesetId: testData.rulesetId, }; await impl.syncClientState(testData.imodelToken, { clientId: "", clientStateId: options.clientStateId, state: {} }); - await expect(impl.getRootNodesCount(testData.imodelToken, options)).to.eventually.be.fulfilled; + await expect(impl.getNodesCount(testData.imodelToken, options)).to.eventually.be.fulfilled; }); }); @@ -149,7 +147,7 @@ describe("PresentationRpcImpl", () => { rulesets.forEach((ruleset) => rulesetsMock.verify((x) => x.add(ruleset), moq.Times.once())); }); - it("throws if rulesets state object is not an array", async () => { + it("returns InvalidArgument status code if rulesets state object is not an array", async () => { const ruleset = await createRandomRuleset(); const options: ClientStateSyncRequestOptions = { clientStateId: faker.random.uuid(), @@ -157,7 +155,8 @@ describe("PresentationRpcImpl", () => { [RulesetManagerState.STATE_ID]: ruleset, }, }; - await expect(impl.syncClientState(testData.imodelToken, options)).to.eventually.be.rejectedWith(PresentationError); + const response = await impl.syncClientState(testData.imodelToken, options); + expect(response.statusCode).to.equal(PresentationStatus.InvalidArgument); }); it("syncs ruleset vars", async () => { @@ -184,93 +183,127 @@ describe("PresentationRpcImpl", () => { variablesMock.verify((x) => x.setValue(values.b[0][0], values.b[0][1], values.b[0][2]), moq.Times.once()); }); - it("throws if ruleset vars state object is not an object", async () => { + it("returns InvalidArgument status code if ruleset vars state object is not an object", async () => { const options: ClientStateSyncRequestOptions = { clientStateId: faker.random.uuid(), state: { [RulesetVariablesState.STATE_ID]: 456, }, }; - await expect(impl.syncClientState(testData.imodelToken, options)).to.eventually.be.rejectedWith(PresentationError); + const response = await impl.syncClientState(testData.imodelToken, options); + expect(response.statusCode).to.equal(PresentationStatus.InvalidArgument); }); - it("throws if clientStateId is not specified", async () => { + it("returns InvalidArgument status code if clientStateId is not specified", async () => { const options: ClientStateSyncRequestOptions = { state: {}, }; - await expect(impl.syncClientState(testData.imodelToken, options)).to.eventually.be.rejectedWith(PresentationError); + const response = await impl.syncClientState(testData.imodelToken, options); + expect(response.statusCode).to.equal(PresentationStatus.InvalidArgument); }); }); - describe("getRootNodes", () => { + describe("getNodesAndCount", () => { - it("calls manager", async () => { - const result: Node[] = [createRandomECInstanceNode(), createRandomECInstanceNode(), createRandomECInstanceNode()]; + it("calls manager for root nodes", async () => { + const getRootNodesResult: Node[] = [createRandomECInstanceNode(), createRandomECInstanceNode(), createRandomECInstanceNode()]; + const getRootNodesCountResult = 999; const options: Paged, "imodel">> = { rulesetId: testData.rulesetId, paging: testData.pageOptions, }; - presentationManagerMock.setup((x) => x.getRootNodes(ActivityLoggingContext.current, { ...options, imodel: testData.imodelMock.object })) - .returns(async () => result) + + presentationManagerMock.setup((x) => x.getNodesAndCount(ActivityLoggingContext.current, { ...options, imodel: testData.imodelMock.object }, undefined)) + .returns(async () => ({ nodes: getRootNodesResult, count: getRootNodesCountResult })) .verifiable(); - const actualResult = await impl.getRootNodes(testData.imodelToken, { ...defaultRpcParams, ...options }); + const actualResult = await impl.getNodesAndCount(testData.imodelToken, { ...defaultRpcParams, ...options }); + presentationManagerMock.verifyAll(); - expect(actualResult).to.deep.eq(result); + expect(actualResult.result!.nodes).to.deep.eq(getRootNodesResult); + expect(actualResult.result!.count).to.eq(getRootNodesCountResult); }); + it("calls manager for child nodes", async () => { + const getChildNodeResult: Node[] = [createRandomECInstanceNode(), createRandomECInstanceNode(), createRandomECInstanceNode()]; + const getChildNodesCountResult = 999; + const parentNodeKey = createRandomECInstanceNodeKey(); + const options: Paged, "imodel">> = { + rulesetId: testData.rulesetId, + paging: testData.pageOptions, + }; + + presentationManagerMock.setup((x) => x.getNodesAndCount(ActivityLoggingContext.current, { ...options, imodel: testData.imodelMock.object }, parentNodeKey)) + .returns(async () => ({ nodes: getChildNodeResult, count: getChildNodesCountResult })) + .verifiable(); + const actualResult = await impl.getNodesAndCount(testData.imodelToken, { ...defaultRpcParams, ...options }, parentNodeKey); + + presentationManagerMock.verifyAll(); + expect(actualResult.result!.nodes).to.deep.eq(getChildNodeResult); + expect(actualResult.result!.count).to.eq(getChildNodesCountResult); + }); }); - describe("getRootNodesCount", () => { + describe("getNodes", () => { - it("calls manager", async () => { - const result = 999; - const options: Omit, "imodel"> = { + it("calls manager for root nodes", async () => { + const result: Node[] = [createRandomECInstanceNode(), createRandomECInstanceNode(), createRandomECInstanceNode()]; + const options: Paged, "imodel">> = { rulesetId: testData.rulesetId, + paging: testData.pageOptions, }; - presentationManagerMock.setup((x) => x.getRootNodesCount(ActivityLoggingContext.current, { ...options, imodel: testData.imodelMock.object })) - .returns(() => Promise.resolve(result)) + presentationManagerMock.setup((x) => x.getNodes(ActivityLoggingContext.current, { ...options, imodel: testData.imodelMock.object }, undefined)) + .returns(async () => result) .verifiable(); - const actualResult = await impl.getRootNodesCount(testData.imodelToken, { ...defaultRpcParams, ...options }); + const actualResult = await impl.getNodes(testData.imodelToken, { ...defaultRpcParams, ...options }); presentationManagerMock.verifyAll(); - expect(actualResult).to.eq(result); + expect(actualResult.result).to.deep.eq(result); }); - }); - - describe("getChildren", () => { - - it("calls manager", async () => { + it("calls manager for child nodes", async () => { const result: Node[] = [createRandomECInstanceNode(), createRandomECInstanceNode(), createRandomECInstanceNode()]; const parentNodeKey = createRandomECInstanceNodeKey(); const options: Paged, "imodel">> = { rulesetId: testData.rulesetId, paging: testData.pageOptions, }; - presentationManagerMock.setup((x) => x.getChildren(ActivityLoggingContext.current, { ...options, imodel: testData.imodelMock.object }, parentNodeKey)) + presentationManagerMock.setup((x) => x.getNodes(ActivityLoggingContext.current, { ...options, imodel: testData.imodelMock.object }, parentNodeKey)) .returns(() => Promise.resolve(result)) .verifiable(); - const actualResult = await impl.getChildren(testData.imodelToken, { ...defaultRpcParams, ...options }, parentNodeKey); + const actualResult = await impl.getNodes(testData.imodelToken, { ...defaultRpcParams, ...options }, parentNodeKey); presentationManagerMock.verifyAll(); - expect(actualResult).to.deep.eq(result); + expect(actualResult.result).to.deep.eq(result); }); }); - describe("getChildrenCount", () => { + describe("getNodesCount", () => { - it("calls manager", async () => { + it("calls manager for root nodes count", async () => { + const result = 999; + const options: Omit, "imodel"> = { + rulesetId: testData.rulesetId, + }; + presentationManagerMock.setup((x) => x.getNodesCount(ActivityLoggingContext.current, { ...options, imodel: testData.imodelMock.object }, undefined)) + .returns(() => Promise.resolve(result)) + .verifiable(); + const actualResult = await impl.getNodesCount(testData.imodelToken, { ...defaultRpcParams, ...options }); + presentationManagerMock.verifyAll(); + expect(actualResult.result).to.eq(result); + }); + + it("calls manager for child nodes count", async () => { const result = 999; const parentNodeKey = createRandomECInstanceNodeKey(); const options: Omit, "imodel"> = { rulesetId: testData.rulesetId, }; - presentationManagerMock.setup((x) => x.getChildrenCount(ActivityLoggingContext.current, { ...options, imodel: testData.imodelMock.object }, parentNodeKey)) + presentationManagerMock.setup((x) => x.getNodesCount(ActivityLoggingContext.current, { ...options, imodel: testData.imodelMock.object }, parentNodeKey)) .returns(() => Promise.resolve(result)) .verifiable(); - const actualResult = await impl.getChildrenCount(testData.imodelToken, { ...defaultRpcParams, ...options }, parentNodeKey); + const actualResult = await impl.getNodesCount(testData.imodelToken, { ...defaultRpcParams, ...options }, parentNodeKey); presentationManagerMock.verifyAll(); - expect(actualResult).to.eq(result); + expect(actualResult.result).to.eq(result); }); }); @@ -287,7 +320,7 @@ describe("PresentationRpcImpl", () => { .verifiable(); const actualResult = await impl.getFilteredNodePaths(testData.imodelToken, { ...defaultRpcParams, ...options }, "filter"); presentationManagerMock.verifyAll(); - expect(actualResult).to.deep.equal(result); + expect(actualResult.result).to.deep.equal(result); }); }); @@ -305,7 +338,7 @@ describe("PresentationRpcImpl", () => { .verifiable(); const actualResult = await impl.getNodePaths(testData.imodelToken, { ...defaultRpcParams, ...options }, keyArray, 1); presentationManagerMock.verifyAll(); - expect(actualResult).to.deep.equal(result); + expect(actualResult.result).to.deep.equal(result); }); }); @@ -330,7 +363,7 @@ describe("PresentationRpcImpl", () => { testData.displayType, testData.inputKeys, undefined); presentationManagerMock.verifyAll(); descriptorMock.verifyAll(); - expect(actualResult).to.eq(result); + expect(actualResult.result).to.eq(result); }); it("handles undefined descriptor response", async () => { @@ -343,11 +376,44 @@ describe("PresentationRpcImpl", () => { const actualResult = await impl.getContentDescriptor(testData.imodelToken, { ...defaultRpcParams, ...options }, testData.displayType, testData.inputKeys, undefined); presentationManagerMock.verifyAll(); - expect(actualResult).to.be.undefined; + expect(actualResult.result).to.be.undefined; }); }); + describe("getContentAndContentSize", () => { + + it("calls manager", async () => { + const contentSizeResult = 789; + + const descriptorMock = moq.Mock.ofType(); + descriptorMock.setup((x) => x.resetParentship).verifiable(); + + const contentMock = moq.Mock.ofType(); + moq.configureForPromiseResult(contentMock); + contentMock.setup((x) => x.descriptor).returns(() => descriptorMock.object); + contentMock.setup((x) => x.contentSet).returns(() => []); + + const options: Paged, "imodel">> = { + rulesetId: testData.rulesetId, + paging: testData.pageOptions, + }; + + presentationManagerMock.setup(async (x) => x.getContentAndSize(ActivityLoggingContext.current, { ...options, imodel: testData.imodelMock.object }, descriptorMock.object, testData.inputKeys)) + .returns(async () => ({ content: contentMock.object, size: contentSizeResult })) + .verifiable(); + + const actualResult = await impl.getContentAndSize(testData.imodelToken, { ...defaultRpcParams, ...options }, + descriptorMock.object, testData.inputKeys); + + presentationManagerMock.verifyAll(); + descriptorMock.verifyAll(); + + expect(actualResult.result!.content).to.eq(contentMock.object); + expect(actualResult.result!.size).to.deep.eq(contentSizeResult); + }); + }); + describe("getContentSetSize", () => { it("calls manager", async () => { @@ -363,7 +429,7 @@ describe("PresentationRpcImpl", () => { const actualResult = await impl.getContentSetSize(testData.imodelToken, { ...defaultRpcParams, ...options }, descriptor, testData.inputKeys); presentationManagerMock.verifyAll(); - expect(actualResult).to.deep.eq(result); + expect(actualResult.result).to.deep.eq(result); }); }); @@ -383,14 +449,14 @@ describe("PresentationRpcImpl", () => { rulesetId: testData.rulesetId, paging: testData.pageOptions, }; - presentationManagerMock.setup((x) => x.getContent(ActivityLoggingContext.current, { ...options, imodel: testData.imodelMock.object }, descriptorMock.object, testData.inputKeys)) + presentationManagerMock.setup(async (x) => x.getContent(ActivityLoggingContext.current, { ...options, imodel: testData.imodelMock.object }, descriptorMock.object, testData.inputKeys)) .returns(async () => contentMock.object) .verifiable(); const actualResult = await impl.getContent(testData.imodelToken, { ...defaultRpcParams, ...options }, descriptorMock.object, testData.inputKeys); presentationManagerMock.verifyAll(); descriptorMock.verifyAll(); - expect(actualResult).to.eq(contentMock.object); + expect(actualResult.result).to.eq(contentMock.object); }); }); @@ -411,7 +477,43 @@ describe("PresentationRpcImpl", () => { const actualResult = await impl.getDistinctValues(testData.imodelToken, { ...defaultRpcParams, ...options }, descriptor, testData.inputKeys, fieldName, maximumValueCount); presentationManagerMock.verifyAll(); - expect(actualResult).to.deep.eq(distinctValues); + expect(actualResult.result).to.deep.eq(distinctValues); + }); + + }); + + describe("getSelectionScopes", () => { + + it("calls manager", async () => { + const options: SelectionScopeRequestOptions = { + imodel: testData.imodelToken, + }; + const result = [createRandomSelectionScope()]; + presentationManagerMock.setup((x) => x.getSelectionScopes(ActivityLoggingContext.current, { ...options, imodel: testData.imodelMock.object })) + .returns(async () => result) + .verifiable(); + const actualResult = await impl.getSelectionScopes(testData.imodelToken, { ...defaultRpcParams, ...options }); + presentationManagerMock.verifyAll(); + expect(actualResult.result).to.deep.eq(result); + }); + + }); + + describe("getSelectionScopes", () => { + + it("calls manager", async () => { + const options: SelectionScopeRequestOptions = { + imodel: testData.imodelToken, + }; + const scope = createRandomSelectionScope(); + const keys = [createRandomEntityProps()]; + const result = new KeySet(); + presentationManagerMock.setup((x) => x.computeSelection(ActivityLoggingContext.current, { ...options, imodel: testData.imodelMock.object }, keys, scope.id)) + .returns(async () => result) + .verifiable(); + const actualResult = await impl.computeSelection(testData.imodelToken, { ...defaultRpcParams, ...options }, keys, scope.id); + presentationManagerMock.verifyAll(); + expect(actualResult.result).to.deep.eq(result); }); }); diff --git a/presentation/backend/src/test/TemporaryStorage.test.ts b/presentation/backend/src/test/TemporaryStorage.test.ts index b865566..98d04ad 100644 --- a/presentation/backend/src/test/TemporaryStorage.test.ts +++ b/presentation/backend/src/test/TemporaryStorage.test.ts @@ -23,21 +23,21 @@ describe("TemporaryStorage", () => { it("doesn't set up timer callback when interval is not set", () => { const s = sinon.spy(clock, "setInterval"); - using(new TemporaryStorage({ factory: () => "" }), () => { + using(new TemporaryStorage({ factory: () => "" }), (_r) => { expect(s).to.not.be.called; }); }); it("doesn't set up timer callback when interval is set to 0", () => { const s = sinon.spy(clock, "setInterval"); - using(new TemporaryStorage({ factory: () => "", cleanupInterval: 0 }), () => { + using(new TemporaryStorage({ factory: () => "", cleanupInterval: 0 }), (_r) => { expect(s).to.not.be.called; }); }); it("sets up timer callback when interval is set to more than 0", () => { const s = sinon.spy(clock, "setInterval"); - using(new TemporaryStorage({ factory: () => "", cleanupInterval: 1 }), () => { + using(new TemporaryStorage({ factory: () => "", cleanupInterval: 1 }), (_r) => { expect(s).to.be.calledOnce; }); }); diff --git a/presentation/common/CHANGELOG.json b/presentation/common/CHANGELOG.json index b36d34a..9ec6ea8 100644 --- a/presentation/common/CHANGELOG.json +++ b/presentation/common/CHANGELOG.json @@ -1,6 +1,57 @@ { "name": "@bentley/presentation-common", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/presentation-common_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "Changes package.json to include api-extractor and adds api-extractor.json" + }, + { + "comment": "Added RpcResponse and PresentationRpcResponse interfaces and changed PresentationRpcInterface to return PresentationRpcResponses instead of raw values." + }, + { + "comment": "Use new buildIModelJsBuild script" + }, + { + "comment": "Remove uneeded typedoc plugin depedency" + }, + { + "comment": "Create RulesetFactory API for creating presentation rulesets targeted towards specific cases, like 'find similar', etc." + }, + { + "comment": "Save BUILD_SEMVER to globally accessible map" + }, + { + "comment": "Change `RulesetsFactory.createSimilarInstancesRuleset` return type from a `Ruleset` to `{ ruleset: Ruleset, description: string }`" + }, + { + "comment": "Fix RPC requests handler to re-request data if frontend got out-of-sync while syncing with the backed." + }, + { + "comment": "Add selection scopes -related RPC handlers" + }, + { + "comment": "RPC Interface changes to optimize getting first page of nodes/content" + }, + { + "comment": "Expose node key type guards through index" + }, + { + "comment": "upgrade to TypeScript 3.2.2" + } + ] + } + }, + { + "version": "0.188.0", + "tag": "@bentley/presentation-common_v0.188.0", + "date": "Wed, 16 Jan 2019 16:36:09 GMT", + "comments": {} + }, { "version": "0.187.0", "tag": "@bentley/presentation-common_v0.187.0", diff --git a/presentation/common/CHANGELOG.md b/presentation/common/CHANGELOG.md index 0f09d06..a53a9c5 100644 --- a/presentation/common/CHANGELOG.md +++ b/presentation/common/CHANGELOG.md @@ -1,6 +1,29 @@ # Change Log - @bentley/presentation-common -This log was last generated on Tue, 15 Jan 2019 15:18:59 GMT and should not be manually modified. +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- Changes package.json to include api-extractor and adds api-extractor.json +- Added RpcResponse and PresentationRpcResponse interfaces and changed PresentationRpcInterface to return PresentationRpcResponses instead of raw values. +- Use new buildIModelJsBuild script +- Remove uneeded typedoc plugin depedency +- Create RulesetFactory API for creating presentation rulesets targeted towards specific cases, like 'find similar', etc. +- Save BUILD_SEMVER to globally accessible map +- Change `RulesetsFactory.createSimilarInstancesRuleset` return type from a `Ruleset` to `{ ruleset: Ruleset, description: string }` +- Fix RPC requests handler to re-request data if frontend got out-of-sync while syncing with the backed. +- Add selection scopes -related RPC handlers +- RPC Interface changes to optimize getting first page of nodes/content +- Expose node key type guards through index +- upgrade to TypeScript 3.2.2 + +## 0.188.0 +Wed, 16 Jan 2019 16:36:09 GMT + +*Version update only* ## 0.187.0 Tue, 15 Jan 2019 15:18:59 GMT diff --git a/presentation/common/package.json b/presentation/common/package.json index 6b18fa1..9276f39 100644 --- a/presentation/common/package.json +++ b/presentation/common/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/presentation-common", - "version": "0.187.0", + "version": "0.189.0", "description": "Common pieces for iModel.js presentation packages", "license": "MIT", "repository": { @@ -20,7 +20,7 @@ "main": "lib/presentation-common.js", "typings": "lib/presentation-common", "scripts": { - "build": "tsc -b ./src/test 1>&2 && npm run ruleset-json-schema && npm run extract && npm run webpackModule-dev", + "build": "npm run ruleset-json-schema && npm run extract && node ./node_modules/@bentley/webpack-tools/bin/buildIModelJsModule.js", "build:watch": "npm run ruleset-json-schema && tsc -b ./src/test -w", "clean": "rimraf lib package-deps.json", "cover": "nyc npm test", @@ -28,25 +28,35 @@ "docs:changelog": "cpx \"./CHANGELOG.md\" ../../generated-docs/presentation/presentation-common", "docs:reference": "node ./node_modules/@bentley/build-tools/scripts/docs.js --source=./src --includes=../../generated-docs/extract --json=../../generated-docs/presentation/presentation-common/json/file.json --tsIndexFile=presentation-common.ts --onlyJson %TYPEDOC_THEME%", "extract": "node ./node_modules/@bentley/build-tools/scripts/extract.js --fileExt=ts --extractFrom=./src/test --recursive --out=../../generated-docs/extract", + "extract-api": "node ./node_modules/@bentley/build-tools/scripts/extract-api.js --entry=presentation-common --isPresentation=true", "ruleset-json-schema": "npm run ruleset-json-schema:generate && npm run ruleset-json-schema:post-process", "ruleset-json-schema:generate": "typescript-json-schema --noExtraProps --required --strictNullChecks --include ./src/rules/**/*.ts --include ./src/rules/*.ts -- ./src/tsconfig.json Ruleset > ./Ruleset.schema.json", "ruleset-json-schema:post-process": "node ./scripts/post-process-json-schema.js --path ./Ruleset.schema.json", "lint": "echo Disabled until https://github.com/palantir/tslint/issues/4148 is fixed", "//lint": "tslint -p ./src/tsconfig.json 1>&2", "test": "mocha --opts ../mocha.opts lib/test/**/*.js", - "test:watch": "npm test -- --reporter min --watch-extensions ts --watch", - "webpackModule-dev": "make-dir ./lib/module/dev/ && webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/presentation-common.js --env.bundlename=presentation-common --json >./lib/module/dev/webpackStats.json", - "webpackModule-prod": "webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/presentation-common.js --env.bundlename=presentation-common --env.prod --env.stylesheets" + "test:watch": "npm test -- --reporter min --watch-extensions ts --watch" + }, + "iModelJs": { + "buildModule": { + "type": "system", + "tscOptions": "-b ./src/test", + "webpack": { + "dest": "./lib/module", + "entry": "./lib/presentation-common.js", + "bundleName": "presentation-common" + } + } }, "peerDependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/imodeljs-common": "0.187.0" + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/imodeljs-common": "0.189.0" }, "devDependencies": { - "@bentley/build-tools": "0.187.0", - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/webpack-tools": "0.187.0", + "@bentley/build-tools": "0.189.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/webpack-tools": "0.189.0", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/chai-jest-snapshot": "^1.3.0", @@ -64,22 +74,17 @@ "deep-equal": "^1", "faker": "^4.1.0", "json-schema-faker": "^0.5.0-rc15", - "make-dir-cli": "^1.0.0", "mocha": "^5.2.0", "nyc": "^13.0.1", "rimraf": "^2.6.2", "sinon": "^7.1.1", "sinon-chai": "^3.2.0", - "source-map-loader": "^0.2.3", "tslint": "^5.11.0", "tslint-consistent-codestyle": "^1.11.0", "typedoc": "^0.11.1", - "typedoc-plugin-external-module-name": "^1.1.1", "typemoq": "^2.1.0", - "typescript": "~3.1.0", + "typescript": "~3.2.2", "typescript-json-schema": "^0.28.0", - "webpack": "^4.20.2", - "webpack-cli": "^3.1.0", "yargs": "^12.0.0" }, "nyc": { diff --git a/presentation/common/src/PresentationManagerOptions.ts b/presentation/common/src/PresentationManagerOptions.ts index 31d80c0..55dffeb 100644 --- a/presentation/common/src/PresentationManagerOptions.ts +++ b/presentation/common/src/PresentationManagerOptions.ts @@ -32,4 +32,13 @@ export interface PageOptions { export type Paged = TOptions & { /** Optional paging parameters */ paging?: PageOptions; -}; \ No newline at end of file +}; + +/** Request options used for selection scope related requests */ +export interface SelectionScopeRequestOptions { + /** iModel to request data for */ + imodel: TIModel; + + /** Optional locale to use when localizing data */ + locale?: string; +} diff --git a/presentation/common/src/PresentationRpcInterface.ts b/presentation/common/src/PresentationRpcInterface.ts index 77338bd..6c98283 100644 --- a/presentation/common/src/PresentationRpcInterface.ts +++ b/presentation/common/src/PresentationRpcInterface.ts @@ -4,29 +4,52 @@ *--------------------------------------------------------------------------------------------*/ /** @module RPC */ -import { RpcInterface, IModelToken } from "@bentley/imodeljs-common"; +import { RpcInterface, IModelToken, EntityProps } from "@bentley/imodeljs-common"; import { NodeKey } from "./hierarchy/Key"; import { default as NodePathElement } from "./hierarchy/NodePathElement"; import { default as Node } from "./hierarchy/Node"; import { SelectionInfo, default as Descriptor } from "./content/Descriptor"; import { default as Content } from "./content/Content"; -import { Field } from "./content/Fields"; +import { Field, PropertiesField, NestedContentField } from "./content/Fields"; import { default as Item } from "./content/Item"; -import { PropertiesField, NestedContentField } from "./content/Fields"; -import { HierarchyRequestOptions, ContentRequestOptions, Paged } from "./PresentationManagerOptions"; +import { HierarchyRequestOptions, ContentRequestOptions, SelectionScopeRequestOptions, Paged } from "./PresentationManagerOptions"; import KeySet from "./KeySet"; import { InstanceKey } from "./EC"; import { Omit } from "./Utils"; +import { SelectionScope } from "./selection/SelectionScope"; +import { PresentationStatus } from "./Error"; export interface RpcRequestOptions { clientId?: string; clientStateId?: string; } + +export interface RpcResponse { + statusCode: PresentationStatus; + errorMessage?: string; + result: TResult; +} + +export type PresentationRpcResponse

= Promise | RpcResponse>; + export type HierarchyRpcRequestOptions = RpcRequestOptions & Omit, "imodel">; export type ContentRpcRequestOptions = RpcRequestOptions & Omit, "imodel">; +export type SelectionScopeRpcRequestOptions = RpcRequestOptions & Omit, "imodel">; export type RulesetVariableRpcRequestOptions = RpcRequestOptions & { rulesetId: string }; export type ClientStateSyncRequestOptions = RpcRequestOptions & { state: { [id: string]: unknown } }; +/** Interface used for receiving nodes and nodes count */ +export interface NodesResponse { + nodes: ReadonlyArray; + count: number; +} + +/** Interface used for receiving content and content set size */ +export interface ContentResponse { + content: Readonly; + size: number; +} + /** Interface used for communication between Presentation backend and frontend. */ export default class PresentationRpcInterface extends RpcInterface { // developer note: It's called an interface but actually it's a real implemented @@ -44,34 +67,37 @@ export default class PresentationRpcInterface extends RpcInterface { ] /** The semantic version of the interface. */ - public static version = "1.0.0"; + public static version = "0.2.0"; /*=========================================================================================== NOTE: Any add/remove/change to the methods below requires an update of the interface version. NOTE: Please consult the README in core/common/src/rpc for the semantic versioning rules. ===========================================================================================*/ - /** See [[PresentationManager.getRootNodes]] */ - public getRootNodes(_token: IModelToken, _options: Paged): Promise { return this.forward(arguments); } - /** See [[PresentationManager.getRootNodesCount]] */ - public getRootNodesCount(_token: IModelToken, _options: HierarchyRpcRequestOptions): Promise { return this.forward(arguments); } - /** See [[PresentationManager.getChildren]] */ - public getChildren(_token: IModelToken, _options: Paged, _parentKey: Readonly): Promise { return this.forward(arguments); } - /** See [[PresentationManager.getChildrenCount]] */ - public getChildrenCount(_token: IModelToken, _options: HierarchyRpcRequestOptions, _parentKey: Readonly): Promise { return this.forward(arguments); } + /** See [[PresentationManager.getNodes]] */ + public async getNodesAndCount(_token: IModelToken, _options: Paged, _parentKey?: Readonly): PresentationRpcResponse { return this.forward(arguments); } + /** See [[PresentationManager.getNodes]] */ + public async getNodes(_token: IModelToken, _options: Paged, _parentKey?: Readonly): PresentationRpcResponse { return this.forward(arguments); } + /** See [[PresentationManager.getNodesCount]] */ + public async getNodesCount(_token: IModelToken, _options: HierarchyRpcRequestOptions, _parentKey?: Readonly): PresentationRpcResponse { return this.forward(arguments); } /** See [[PresentationManager.getNodePaths]] */ - public getNodePaths(_token: IModelToken, _options: HierarchyRpcRequestOptions, _paths: InstanceKey[][], _markedIndex: number): Promise { return this.forward(arguments); } + public async getNodePaths(_token: IModelToken, _options: HierarchyRpcRequestOptions, _paths: InstanceKey[][], _markedIndex: number): PresentationRpcResponse { return this.forward(arguments); } /** See [[PresentationManager.getFilteredNodePaths]] */ - public getFilteredNodePaths(_token: IModelToken, _options: HierarchyRpcRequestOptions, _filterText: string): Promise { return this.forward(arguments); } + public async getFilteredNodePaths(_token: IModelToken, _options: HierarchyRpcRequestOptions, _filterText: string): PresentationRpcResponse { return this.forward(arguments); } /** See [[PresentationManager.getContentDescriptor]] */ - public getContentDescriptor(_token: IModelToken, _options: ContentRpcRequestOptions, _displayType: string, _keys: Readonly, _selection: Readonly | undefined): Promise { return this.forward(arguments); } + public async getContentDescriptor(_token: IModelToken, _options: ContentRpcRequestOptions, _displayType: string, _keys: Readonly, _selection: Readonly | undefined): PresentationRpcResponse { return this.forward(arguments); } /** See [[PresentationManager.getContentSetSize]] */ - public getContentSetSize(_token: IModelToken, _options: ContentRpcRequestOptions, _descriptor: Readonly, _keys: Readonly): Promise { return this.forward(arguments); } + public async getContentSetSize(_token: IModelToken, _options: ContentRpcRequestOptions, _descriptorOrDisplayType: Readonly | string, _keys: Readonly): PresentationRpcResponse { return this.forward(arguments); } /** See [[PresentationManager.getContent]] */ - public getContent(_token: IModelToken, _options: ContentRpcRequestOptions, _descriptor: Readonly, _keys: Readonly): Promise { return this.forward(arguments); } + public async getContent(_token: IModelToken, _options: ContentRpcRequestOptions, _descriptorOrDisplayType: Readonly | string, _keys: Readonly): PresentationRpcResponse { return this.forward(arguments); } + /** See [[PresentationManager.getContentAndContentSize]] */ + public async getContentAndSize(_token: IModelToken, _options: ContentRpcRequestOptions, _descriptorOrDisplayType: Readonly | string, _keys: Readonly): PresentationRpcResponse { return this.forward(arguments); } /** See [[PresentationManager.getDistinctValues]] */ - public getDistinctValues(_token: IModelToken, _options: ContentRpcRequestOptions, _descriptor: Readonly, _keys: Readonly, _fieldName: string, _maximumValueCount: number): Promise { return this.forward(arguments); } + public async getDistinctValues(_token: IModelToken, _options: ContentRpcRequestOptions, _descriptor: Readonly, _keys: Readonly, _fieldName: string, _maximumValueCount: number): PresentationRpcResponse { return this.forward(arguments); } + + public async getSelectionScopes(_token: IModelToken, _options: SelectionScopeRpcRequestOptions): PresentationRpcResponse { return this.forward(arguments); } + public async computeSelection(_token: IModelToken, _options: SelectionScopeRpcRequestOptions, _keys: Readonly, _scopeId: string): PresentationRpcResponse { return this.forward(arguments); } - public syncClientState(_token: IModelToken, _options: ClientStateSyncRequestOptions): Promise { return this.forward(arguments); } + public async syncClientState(_token: IModelToken, _options: ClientStateSyncRequestOptions): PresentationRpcResponse { return this.forward(arguments); } } diff --git a/presentation/common/src/RpcRequestsHandler.ts b/presentation/common/src/RpcRequestsHandler.ts index 7dc6873..c84dddd 100644 --- a/presentation/common/src/RpcRequestsHandler.ts +++ b/presentation/common/src/RpcRequestsHandler.ts @@ -5,17 +5,19 @@ /** @module RPC */ import { Guid, BeEvent, IDisposable } from "@bentley/bentleyjs-core"; -import { IModelToken, RpcManager } from "@bentley/imodeljs-common"; +import { IModelToken, RpcManager, EntityProps } from "@bentley/imodeljs-common"; import KeySet from "./KeySet"; -import { PresentationStatus } from "./Error"; +import { PresentationStatus, PresentationError } from "./Error"; import { InstanceKey } from "./EC"; import { NodeKey } from "./hierarchy/Key"; import { default as Node } from "./hierarchy/Node"; import { default as NodePathElement } from "./hierarchy/NodePathElement"; import { SelectionInfo, default as Descriptor } from "./content/Descriptor"; import { default as Content } from "./content/Content"; -import { HierarchyRequestOptions, ContentRequestOptions, Paged } from "./PresentationManagerOptions"; -import PresentationRpcInterface, { RpcRequestOptions } from "./PresentationRpcInterface"; +import { SelectionScope } from "./selection/SelectionScope"; +import { HierarchyRequestOptions, ContentRequestOptions, Paged, SelectionScopeRequestOptions } from "./PresentationManagerOptions"; +import PresentationRpcInterface, { RpcRequestOptions, NodesResponse, ContentResponse, PresentationRpcResponse } from "./PresentationRpcInterface"; +import { Omit } from "./Utils"; /** * Configuration parameters for [[RpcRequestsHandler]]. @@ -117,6 +119,21 @@ export default class RpcRequestsHandler implements IDisposable { await this.rpcClient.syncClientState(token, this.createRequestOptions({ state: clientState })); } + private async requestRepeatedly(func: (opts: TOptions) => PresentationRpcResponse, options: TOptions, imodelToken: IModelToken): Promise { + const response = await func(options); + + if (response.statusCode === PresentationStatus.Success) + return response.result!; + + if (response.statusCode === PresentationStatus.BackendOutOfSync) { + options.clientStateId = this._clientStateId; + await this.sync(imodelToken); + return this.requestRepeatedly(func, options, imodelToken); + } + + throw new PresentationError(response.statusCode, response.errorMessage); + } + /** * Send request to current backend. If the backend is unknown to the requestor, * the request is rejected with `PresentationStatus.UnknownBackend` status. In @@ -125,66 +142,64 @@ export default class RpcRequestsHandler implements IDisposable { * * @hidden */ - public async request( + public async request( context: any, - func: (token: IModelToken, options: TOptions, ...args: TArg) => Promise, + func: (token: IModelToken, options: Omit, ...args: TArg[]) => PresentationRpcResponse, options: TOptions, - ...args: TArg): Promise { - - const { imodel, ...rpcOptions } = options as (RpcRequestOptions & { imodel: IModelToken }); - const doRequest = () => func.apply(context, [imodel, rpcOptions, ...args]); - try { - return await doRequest(); - } catch (e) { - if (e.errorNumber === PresentationStatus.BackendOutOfSync) { - await this.sync(options.imodel); - return await doRequest(); - } else { - // unknown error - rethrow - throw e; - } - } - } - - public async getRootNodes(options: Paged>): Promise { - return await this.request>, any>( - this.rpcClient, this.rpcClient.getRootNodes, this.createRequestOptions(options)); + ...args: TArg[]): Promise { + type TFuncOptions = Omit; + const { imodel, ...rpcOptions } = (options as (RpcRequestOptions & { imodel: IModelToken })); // TS2700: Rest types may only be created from object types... + const doRequest = async (funcOptions: TFuncOptions) => func.apply(context, [imodel, funcOptions, ...args]); + return this.requestRepeatedly(doRequest, rpcOptions as TFuncOptions, options.imodel); } - public async getRootNodesCount(options: HierarchyRequestOptions): Promise { - return await this.request, any>( - this.rpcClient, this.rpcClient.getRootNodesCount, this.createRequestOptions(options)); + public async getNodesAndCount(options: Paged>, parentKey?: Readonly): Promise { + return this.request>, any>( + this.rpcClient, this.rpcClient.getNodesAndCount, this.createRequestOptions(options), parentKey); } - public async getChildren(options: Paged>, parentKey: Readonly): Promise { - return await this.request>, any>( - this.rpcClient, this.rpcClient.getChildren, this.createRequestOptions(options), parentKey); + public async getNodes(options: Paged>, parentKey?: Readonly): Promise { + return this.request>>( + this.rpcClient, this.rpcClient.getNodes, this.createRequestOptions(options), parentKey); } - public async getChildrenCount(options: HierarchyRequestOptions, parentKey: Readonly): Promise { - return await this.request, any>( - this.rpcClient, this.rpcClient.getChildrenCount, this.createRequestOptions(options), parentKey); + public async getNodesCount(options: HierarchyRequestOptions, parentKey?: Readonly): Promise { + return this.request>( + this.rpcClient, this.rpcClient.getNodesCount, this.createRequestOptions(options), parentKey); } public async getNodePaths(options: HierarchyRequestOptions, paths: InstanceKey[][], markedIndex: number): Promise { - return await this.request, any>( + return this.request>( this.rpcClient, this.rpcClient.getNodePaths, this.createRequestOptions(options), paths, markedIndex); } public async getFilteredNodePaths(options: HierarchyRequestOptions, filterText: string): Promise { - return await this.request, any>( + return this.request>( this.rpcClient, this.rpcClient.getFilteredNodePaths, this.createRequestOptions(options), filterText); } public async getContentDescriptor(options: ContentRequestOptions, displayType: string, keys: Readonly, selection: Readonly | undefined): Promise { - return await this.request, any>( + return this.request>( this.rpcClient, this.rpcClient.getContentDescriptor, this.createRequestOptions(options), displayType, keys, selection); } - public async getContentSetSize(options: ContentRequestOptions, descriptor: Readonly, keys: Readonly): Promise { - return await this.request, any>( - this.rpcClient, this.rpcClient.getContentSetSize, this.createRequestOptions(options), descriptor, keys); + public async getContentSetSize(options: ContentRequestOptions, descriptorOrDisplayType: Readonly | string, keys: Readonly): Promise { + return this.request>( + this.rpcClient, this.rpcClient.getContentSetSize, this.createRequestOptions(options), descriptorOrDisplayType, keys); + } + public async getContent(options: ContentRequestOptions, descriptorOrDisplayType: Readonly | string, keys: Readonly): Promise { + return this.request>( + this.rpcClient, this.rpcClient.getContent, this.createRequestOptions(options), descriptorOrDisplayType, keys); } - public async getContent(options: ContentRequestOptions, descriptor: Readonly, keys: Readonly): Promise { - return await this.request, any>( - this.rpcClient, this.rpcClient.getContent, this.createRequestOptions(options), descriptor, keys); + public async getContentAndSize(options: ContentRequestOptions, descriptorOrDisplayType: Readonly | string, keys: Readonly): Promise { + return this.request, any>( + this.rpcClient, this.rpcClient.getContentAndSize, this.createRequestOptions(options), descriptorOrDisplayType, keys); } public async getDistinctValues(options: ContentRequestOptions, descriptor: Readonly, keys: Readonly, fieldName: string, maximumValueCount: number): Promise { - return await this.request, any>( + return this.request>( this.rpcClient, this.rpcClient.getDistinctValues, this.createRequestOptions(options), descriptor, keys, fieldName, maximumValueCount); } + + public async getSelectionScopes(options: SelectionScopeRequestOptions): Promise { + return this.request>( + this.rpcClient, this.rpcClient.getSelectionScopes, this.createRequestOptions(options)); + } + public async computeSelection(options: SelectionScopeRequestOptions, keys: EntityProps[], scopeId: string): Promise { + return this.request>( + this.rpcClient, this.rpcClient.computeSelection, this.createRequestOptions(options), keys, scopeId); + } } diff --git a/presentation/common/src/RulesetsFactory.ts b/presentation/common/src/RulesetsFactory.ts new file mode 100644 index 0000000..9172d1d --- /dev/null +++ b/presentation/common/src/RulesetsFactory.ts @@ -0,0 +1,191 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module Core */ + +import { Guid } from "@bentley/bentleyjs-core"; +import { Ruleset } from "./rules/Ruleset"; +import { Field, PropertiesField } from "./content/Fields"; +import Item from "./content/Item"; +import { RuleTypes } from "./rules/Rule"; +import { RuleSpecificationTypes } from "./rules/RuleSpecification"; +import { Value, isNestedContentValue, DisplayValue } from "./content/Value"; +import { MultiSchemaClassesSpecification, SingleSchemaClassSpecification } from "./rules/ClassSpecifications"; +import { PropertyValueFormat } from "./content/TypeDescription"; +import { ClassInfo, RelatedClassInfo } from "./EC"; +import { RelatedInstanceSpecification } from "./rules/RelatedInstanceSpecification"; +import { RelationshipDirection } from "./rules/RelationshipDirection"; + +/** + * A factory class that can be used to create presentation rulesets targeted towards + * specific use cases. + */ +export class RulesetsFactory { + /** + * Create a ruleset with content rules for getting instances are of the + * same ECClass and have the same property value as the provided `record`. + * @param field A field identifying which property of the record we should use + * @param record A record whose similar instances should be found + */ + public createSimilarInstancesRuleset(field: Field, record: Item): { ruleset: Ruleset, description: string } { + if (!field.isPropertiesField()) + throw new Error("Can only create 'similar instances' ruleset for properties-based records"); + if (field.type.valueFormat !== PropertyValueFormat.Primitive) + throw new Error("Can only create 'similar instances' ruleset for primitive properties"); + if (field.properties.length === 0) + throw new Error("Invalid properties' field with no properties"); + if (record.isFieldMerged(field.name)) + throw new Error("Can't create 'similar instances' ruleset for merged values"); + if (!record.classInfo) + throw new Error("Can't create 'similar instances' for records based on multiple different ECClass instances"); + const propertyName = getPropertyName(field); + const propertyValue = getPropertyValue(record, field); + const relatedInstances = createRelatedInstanceSpecs(field); + const relatedInstanceSpecs = relatedInstances.map((r) => r.spec); + const ruleset: Ruleset = { + id: `SimilarInstances/${propertyName}/${Guid.createValue()}`, + rules: [], + }; + ruleset.rules.push({ + ruleType: RuleTypes.Content, + specifications: [{ + specType: RuleSpecificationTypes.ContentInstancesOfSpecificClasses, + classes: createMultiClassSpecification(record.classInfo), + arePolymorphic: true, + relatedInstances: relatedInstanceSpecs, + instanceFilter: createInstanceFilter(relatedInstanceSpecs, propertyName, propertyValue.v), + }], + }); + const description = createDescription(record, relatedInstances.map((r) => r.class), field, propertyValue.d); + return { ruleset, description }; + } +} + +const createDescription = (record: Item, relatedClasses: ClassInfo[], field: Field, value: string): string => { + const classes = (relatedClasses.length > 0) ? relatedClasses : [record.classInfo!]; + return classes.reduce((descr, classInfo, index) => { + if (index !== 0) + descr += " OR "; + descr += `[${classInfo.label}].[${field.label}] = ${value}`; + return descr; + }, ""); +}; + +const getPropertyName = (field: PropertiesField): string => { + let name = field.properties[0].property.name; + if (field.type.typeName === "navigation") + name += ".Id"; + return name; +}; + +const toString = (displayValue: DisplayValue): string => { + if (!displayValue) + return "NULL"; + return displayValue.toString(); +}; + +const getPropertyValue = (record: Item, field: Field): { v: Value, d: string } => { + const fieldNamesStack = []; + let currField: Field | undefined = field; + while (currField) { + fieldNamesStack.push(currField.name); + currField = currField.parent; + } + let currFieldName = fieldNamesStack.pop(); + let displayValue: DisplayValue = record.displayValues[currFieldName!]; + let value: Value = record.values[currFieldName!]; + currFieldName = fieldNamesStack.pop(); + while (currFieldName) { + if (!isNestedContentValue(value) || value.length === 0) + throw new Error("Invalid record value"); + if (value.length > 1) + throw new Error("Can't create 'similar instances' for records related through many part of *-to-many relationship"); + if (value[0].mergedFieldNames.indexOf(currFieldName) !== -1) + throw new Error("Can't create 'similar instances' ruleset for merged values"); + displayValue = value[0].displayValues[currFieldName]; + value = value[0].values[currFieldName]; + currFieldName = fieldNamesStack.pop(); + } + return { v: value, d: toString(displayValue) }; +}; + +const createInstanceFilter = (relatedInstances: Array>, propertyName: string, propertyValue: Value): string => { + const aliases = relatedInstances.map((relatedInstanceSpec) => relatedInstanceSpec.alias); + if (aliases.length === 0) + aliases.push("this"); + return aliases.reduce((filter: string, alias: string, index: number): string => { + if (index !== 0) + filter += " OR "; + filter += createComparison(`${alias}.${propertyName}`, "=", propertyValue); + return filter; + }, ""); +}; + +type Operator = "=" | "!=" | ">" | ">=" | "<" | "<="; +const createComparison = (name: string, operator: Operator, value: Value): string => { + let compareValue: string; + if (typeof value === "undefined") + compareValue = "NULL"; + else if (typeof value === "string") + compareValue = `"${value}"`; + else if (typeof value === "boolean") + compareValue = value ? "TRUE" : "FALSE"; + else if (typeof value === "number") + compareValue = value.toString(); + else + throw new Error("Unsupported value format"); + return `${name} ${operator} ${compareValue}`; +}; + +const createMultiClassSpecification = (classInfo: Readonly): MultiSchemaClassesSpecification => { + const [schemaName, className] = classInfo.name.split(":"); + return { schemaName, classNames: [className] }; +}; + +const createSingleClassSpecification = (classInfo: Readonly): SingleSchemaClassSpecification => { + const [schemaName, className] = classInfo.name.split(":"); + return { schemaName, className }; +}; + +const createRelatedInstanceSpec = (relatedClassInfo: RelatedClassInfo, index: number): { spec: RelatedInstanceSpecification, class: ClassInfo } => ({ + spec: { + relationship: createSingleClassSpecification(relatedClassInfo.relationshipInfo), + class: createSingleClassSpecification(relatedClassInfo.isForwardRelationship ? relatedClassInfo.targetClassInfo : relatedClassInfo.sourceClassInfo), + requiredDirection: relatedClassInfo.isForwardRelationship ? RelationshipDirection.Backward : RelationshipDirection.Forward, + isRequired: true, + alias: `related_${index}`, + }, + class: relatedClassInfo.isForwardRelationship ? relatedClassInfo.targetClassInfo : relatedClassInfo.sourceClassInfo, +}); + +const createRelatedInstanceSpecs = (field: PropertiesField): Array<{ spec: RelatedInstanceSpecification, class: ClassInfo }> => { + const specs = new Array(); + field.properties.forEach((property, index) => { + if (property.relatedClassPath.length === 0) { + // not related + return; + } + if (property.relatedClassPath.length > 1) { + // RelatedInstance presentation rule doesn't support multiple step relationships yet + throw new Error("Can't create related instance specification for property related through multiple relationships"); + } + specs.push(createRelatedInstanceSpec(property.relatedClassPath[0], index)); + }); + if (field.parent) { + if (specs.length > 0) { + // note: should prepend field.parent.pathToPrimaryClass to every spec, but + // RelatedInstance presentation rule doesn't support multiple step relationships yet + throw new Error("Can't create related instance specification for property related through multiple relationships"); + } + if (field.parent.pathToPrimaryClass.length === 0) { + throw new Error("Expecting nested fields to always have relationship path to primary class"); + } + if (field.parent.pathToPrimaryClass.length > 1) { + // RelatedInstance presentation rule doesn't support multiple step relationships yet + throw new Error("Can't create related instance specification for property related through multiple relationships"); + } + specs.push(createRelatedInstanceSpec(field.parent.pathToPrimaryClass[0], 0)); + } + return specs; +}; diff --git a/presentation/common/src/content/Descriptor.ts b/presentation/common/src/content/Descriptor.ts index 2439cab..28d34d2 100644 --- a/presentation/common/src/content/Descriptor.ts +++ b/presentation/common/src/content/Descriptor.ts @@ -179,13 +179,11 @@ export default class Descriptor { /** * Get field by its name + * @param name Name of the field to find + * @param recurse Recurse into nested fields */ - public getFieldByName(name: string): Field | undefined { - for (const field of this.fields) { - if (field.name === name) - return field; - } - return undefined; + public getFieldByName(name: string, recurse?: boolean): Field | undefined { + return findField(this.fields, name, recurse); } /** @hidden */ @@ -221,3 +219,17 @@ export default class Descriptor { }); } } + +const findField = (fields: Field[], name: string, recurse?: boolean): Field | undefined => { + for (const field of fields) { + if (field.name === name) + return field; + + if (recurse && field.isNestedContentField()) { + const nested = findField(field.nestedFields, name, recurse); + if (nested) + return nested; + } + } + return undefined; +}; diff --git a/presentation/common/src/presentation-common.ts b/presentation/common/src/presentation-common.ts index 6ea1f48..637210d 100644 --- a/presentation/common/src/presentation-common.ts +++ b/presentation/common/src/presentation-common.ts @@ -10,13 +10,22 @@ export { default as KeySet, Keys } from "./KeySet"; export { default as PersistentKeysContainer } from "./PersistentKeysContainer"; export * from "./content/Value"; export * from "./PresentationManagerOptions"; -export { default as PresentationRpcInterface, RpcRequestOptions, HierarchyRpcRequestOptions, ClientStateSyncRequestOptions } from "./PresentationRpcInterface"; +export { + default as PresentationRpcInterface, + RpcRequestOptions, ClientStateSyncRequestOptions, ContentRpcRequestOptions, + HierarchyRpcRequestOptions, SelectionScopeRpcRequestOptions, + RpcResponse, PresentationRpcResponse, NodesResponse, ContentResponse, +} from "./PresentationRpcInterface"; export { default as RpcRequestsHandler, IClientStateHolder } from "./RpcRequestsHandler"; export { RulesetVariablesState, VariableValueTypes, VariableValue } from "./RulesetVariables"; export * from "./RegisteredRuleset"; +export * from "./RulesetsFactory"; export * from "./Logging"; export * from "./Utils"; +/** @module UnifiedSelection */ +export * from "./selection/SelectionScope"; + /** @module Content */ export { default as CategoryDescription } from "./content/Category"; export { default as Content, ContentJSON } from "./content/Content"; @@ -34,12 +43,15 @@ export { } from "./content/Value"; /** @module Hierarchies */ -export { NodeKey, NodeKeyPath, NodeKeyJSON, nodeKeyFromJSON, StandardNodeTypes } from "./hierarchy/Key"; +export { + NodeKey, NodeKeyPath, NodeKeyJSON, nodeKeyFromJSON, StandardNodeTypes, + isInstanceNodeKey, isClassGroupingNodeKey, isPropertyGroupingNodeKey, + isLabelGroupingNodeKey, isGroupingNodeKey, +} from "./hierarchy/Key"; export { BaseNodeKey, ECInstanceNodeKey, ECInstanceNodeKeyJSON, ECClassGroupingNodeKey, ECPropertyGroupingNodeKey, LabelGroupingNodeKey } from "./hierarchy/Key"; export { default as Node, NodeJSON } from "./hierarchy/Node"; export { default as NodePathElement } from "./hierarchy/NodePathElement"; - /** @module PresentationRules */ export { RootNodeRule } from "./rules/hierarchy/RootNodeRule"; export { ChildNodeRule } from "./rules/hierarchy/ChildNodeRule"; @@ -89,3 +101,12 @@ export { RelatedInstanceSpecification } from "./rules/RelatedInstanceSpecificati export { RelationshipDirection } from "./rules/RelationshipDirection"; export * from "./rules/ClassSpecifications"; export * from "./rules/SchemasSpecification"; + +// Set the version number so it can be found at runtime. BUILD_SEMVER is replaced at build time by the webpack DefinePlugin. +declare var BUILD_SEMVER: string; +/* istanbul ignore next */ +if ((typeof (BUILD_SEMVER) !== "undefined") && (typeof window !== "undefined") && window) { + if (!(window as any).iModelJsVersions) + (window as any).iModelJsVersions = new Map(); + (window as any).iModelJsVersions.set("presentation-common", BUILD_SEMVER); +} diff --git a/presentation/common/src/selection/SelectionScope.ts b/presentation/common/src/selection/SelectionScope.ts new file mode 100644 index 0000000..dd00139 --- /dev/null +++ b/presentation/common/src/selection/SelectionScope.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module UnifiedSelection */ + +/** Data structure that describes a [selection scope]($docs/learning/unified-selection/Terminology#selection-scope) */ +export interface SelectionScope { + id: string; + label: string; + description?: string; +} diff --git a/presentation/common/src/test/PresentationRpcInterface.test.ts b/presentation/common/src/test/PresentationRpcInterface.test.ts index ef053c0..494dce8 100644 --- a/presentation/common/src/test/PresentationRpcInterface.test.ts +++ b/presentation/common/src/test/PresentationRpcInterface.test.ts @@ -7,8 +7,7 @@ import * as faker from "faker"; import * as moq from "./_helpers/Mocks"; import { createRandomDescriptor, createRandomECInstanceNodeKey, createRandomECInstanceKey } from "./_helpers/random"; import { using } from "@bentley/bentleyjs-core"; -import { IModelToken, RpcOperation, RpcRequest, RpcSerializedValue } from "@bentley/imodeljs-common"; -import { RpcRegistry } from "@bentley/imodeljs-common"; +import { IModelToken, RpcRegistry, RpcOperation, RpcRequest, RpcSerializedValue, EntityProps } from "@bentley/imodeljs-common"; import { PresentationRpcInterface, KeySet, Paged, @@ -16,12 +15,13 @@ import { import { RpcRequestOptions, HierarchyRpcRequestOptions, ContentRpcRequestOptions, ClientStateSyncRequestOptions, + SelectionScopeRpcRequestOptions, } from "../PresentationRpcInterface"; describe("PresentationRpcInterface", () => { class TestRpcRequest extends RpcRequest { - protected send(): Promise { throw new Error("Not implemented."); } - protected load(): Promise { throw new Error("Not implemented."); } + protected async send(): Promise { throw new Error("Not implemented."); } + protected async load(): Promise { throw new Error("Not implemented."); } protected setHeader(_name: string, _value: string): void { throw new Error("Not implemented."); } } @@ -33,9 +33,9 @@ describe("PresentationRpcInterface", () => { ]; RpcRegistry.instance.initializeRpcInterface(PresentationRpcInterface); const client = RpcRegistry.instance.getClientForInterface(PresentationRpcInterface); - const operation = RpcOperation.lookup(PresentationRpcInterface, "getRootNodesCount"); + const operation = RpcOperation.lookup(PresentationRpcInterface, "getNodesCount"); const disposableRequest = { - request: new TestRpcRequest(client, "getRootNodesCount", parameters), + request: new TestRpcRequest(client, "getNodesCount", parameters), dispose: () => { // no way to properly destroy the created request... (disposableRequest.request as any).dispose(); @@ -63,42 +63,51 @@ describe("PresentationRpcInterface", () => { rpcInterface.forward = mock.object; }); - it("forwards getRootNodes call", async () => { + it("forwards getNodesAndCount call", async () => { const options: Paged = { ...defaultRpcOptions, rulesetId: faker.random.word(), }; - await rpcInterface.getRootNodes(token, options); - mock.verify((x) => x(toArguments(token, options)), moq.Times.once()); + await rpcInterface.getNodesAndCount(token, options); + mock.verify(async (x) => x(toArguments(token, options)), moq.Times.once()); }); - it("forwards getRootNodesCount call", async () => { - const options: HierarchyRpcRequestOptions = { + it("forwards getNodes call for root nodes", async () => { + const options: Paged = { ...defaultRpcOptions, rulesetId: faker.random.word(), }; - await rpcInterface.getRootNodesCount(token, options); - mock.verify((x) => x(toArguments(token, options)), moq.Times.once()); + await rpcInterface.getNodes(token, options); + mock.verify(async (x) => x(toArguments(token, options)), moq.Times.once()); }); - it("forwards getChildren call", async () => { + it("forwards getNodes call for child nodes", async () => { const options: Paged = { ...defaultRpcOptions, rulesetId: faker.random.word(), }; const parentKey = createRandomECInstanceNodeKey(); - await rpcInterface.getChildren(token, options, parentKey); - mock.verify((x) => x(toArguments(token, options, parentKey)), moq.Times.once()); + await rpcInterface.getNodes(token, options, parentKey); + mock.verify(async (x) => x(toArguments(token, options, parentKey)), moq.Times.once()); + }); + + it("forwards getNodesCount call for root nodes", async () => { + const options: HierarchyRpcRequestOptions = { + ...defaultRpcOptions, + rulesetId: faker.random.word(), + }; + await rpcInterface.getNodesCount(token, options); + mock.verify(async (x) => x(toArguments(token, options)), moq.Times.once()); }); - it("forwards getChildrenCount call", async () => { + it("forwards getNodesCount call for child nodes", async () => { const options: HierarchyRpcRequestOptions = { ...defaultRpcOptions, rulesetId: faker.random.word(), }; const parentKey = createRandomECInstanceNodeKey(); - await rpcInterface.getChildrenCount(token, options, parentKey); - mock.verify((x) => x(toArguments(token, options, parentKey)), moq.Times.once()); + await rpcInterface.getNodesCount(token, options, parentKey); + mock.verify(async (x) => x(toArguments(token, options, parentKey)), moq.Times.once()); }); it("forwards getFilteredNodePaths call", async () => { @@ -107,7 +116,7 @@ describe("PresentationRpcInterface", () => { rulesetId: faker.random.word(), }; await rpcInterface.getFilteredNodePaths(token, options, "filter"); - mock.verify((x) => x(toArguments(token, options, "filter")), moq.Times.once()); + mock.verify(async (x) => x(toArguments(token, options, "filter")), moq.Times.once()); }); it("forwards getNodePaths call", async () => { @@ -117,7 +126,7 @@ describe("PresentationRpcInterface", () => { }; const keys = [[createRandomECInstanceKey(), createRandomECInstanceKey()]]; await rpcInterface.getNodePaths(token, options, keys, 1); - mock.verify((x) => x(toArguments(token, options, keys, 1)), moq.Times.once()); + mock.verify(async (x) => x(toArguments(token, options, keys, 1)), moq.Times.once()); }); it("forwards getContentDescriptor call", async () => { @@ -127,7 +136,7 @@ describe("PresentationRpcInterface", () => { }; const keys = new KeySet(); await rpcInterface.getContentDescriptor(token, options, "test", keys, undefined); - mock.verify((x) => x(toArguments(token, options, "test", keys, undefined)), moq.Times.once()); + mock.verify(async (x) => x(toArguments(token, options, "test", keys, undefined)), moq.Times.once()); }); it("forwards getContentSetSize call", async () => { @@ -138,7 +147,7 @@ describe("PresentationRpcInterface", () => { const descriptor = createRandomDescriptor(); const keys = new KeySet(); await rpcInterface.getContentSetSize(token, options, descriptor, keys); - mock.verify((x) => x(toArguments(token, options, descriptor, keys)), moq.Times.once()); + mock.verify(async (x) => x(toArguments(token, options, descriptor, keys)), moq.Times.once()); }); it("forwards getContent call", async () => { @@ -149,7 +158,18 @@ describe("PresentationRpcInterface", () => { const descriptor = createRandomDescriptor(); const keys = new KeySet(); await rpcInterface.getContent(token, options, descriptor, keys); - mock.verify((x) => x(toArguments(token, options, descriptor, keys)), moq.Times.once()); + mock.verify(async (x) => x(toArguments(token, options, descriptor, keys)), moq.Times.once()); + }); + + it("forwards getContentAndSize call", async () => { + const options: Paged = { + ...defaultRpcOptions, + rulesetId: faker.random.word(), + }; + const descriptor = createRandomDescriptor(); + const keys = new KeySet(); + await rpcInterface.getContentAndSize(token, options, descriptor, keys); + mock.verify(async (x) => x(toArguments(token, options, descriptor, keys)), moq.Times.once()); }); it("forwards getDistinctValues call", async () => { @@ -162,7 +182,25 @@ describe("PresentationRpcInterface", () => { const maximumValueCount = faker.random.number(); const keys = new KeySet(); await rpcInterface.getDistinctValues(token, options, descriptor, keys, fieldName, maximumValueCount); - mock.verify((x) => x(toArguments(token, options, descriptor, keys, fieldName, maximumValueCount)), moq.Times.once()); + mock.verify(async (x) => x(toArguments(token, options, descriptor, keys, fieldName, maximumValueCount)), moq.Times.once()); + }); + + it("forwards getSelectionScopes call", async () => { + const options: SelectionScopeRpcRequestOptions = { + ...defaultRpcOptions, + }; + await rpcInterface.getSelectionScopes(token, options); + mock.verify(async (x) => x(toArguments(token, options)), moq.Times.once()); + }); + + it("forwards computeSelection call", async () => { + const options: SelectionScopeRpcRequestOptions = { + ...defaultRpcOptions, + }; + const keys = new Array(); + const scopeId = faker.random.uuid(); + await rpcInterface.computeSelection(token, options, keys, scopeId); + mock.verify(async (x) => x(toArguments(token, options, keys, scopeId)), moq.Times.once()); }); it("forwards syncClientState call", async () => { @@ -171,7 +209,7 @@ describe("PresentationRpcInterface", () => { state: {}, }; await rpcInterface.syncClientState(token, options); - mock.verify((x) => x(toArguments(token, options)), moq.Times.once()); + mock.verify(async (x) => x(toArguments(token, options)), moq.Times.once()); }); }); diff --git a/presentation/common/src/test/RpcRequestsHandler.test.ts b/presentation/common/src/test/RpcRequestsHandler.test.ts index 2fe4317..cfb3c14 100644 --- a/presentation/common/src/test/RpcRequestsHandler.test.ts +++ b/presentation/common/src/test/RpcRequestsHandler.test.ts @@ -5,20 +5,22 @@ import { expect } from "chai"; import * as faker from "faker"; import * as moq from "typemoq"; +import * as sinon from "sinon"; import { createRandomECInstanceNodeKey, createRandomECInstanceKey, createRandomECInstanceNode, createRandomNodePathElement, createRandomContent, createRandomDescriptor, + createRandomSelectionScope, } from "./_helpers/random"; import { BeEvent, using } from "@bentley/bentleyjs-core"; -import { IModelToken, RpcManager, RpcInterface, RpcInterfaceDefinition } from "@bentley/imodeljs-common"; +import { IModelToken, RpcManager, RpcInterface, RpcInterfaceDefinition, EntityProps } from "@bentley/imodeljs-common"; import { RpcRequestsHandler, PresentationRpcInterface, KeySet, Paged, SelectionInfo, - PresentationError, PresentationStatus, - HierarchyRequestOptions, ContentRequestOptions, + PresentationStatus, + HierarchyRequestOptions, ContentRequestOptions, SelectionScopeRequestOptions, PresentationError, } from "../presentation-common"; -import { RpcRequestOptions, ClientStateSyncRequestOptions } from "../PresentationRpcInterface"; +import { RpcRequestOptions, ClientStateSyncRequestOptions, RpcResponse, PresentationRpcResponse } from "../PresentationRpcInterface"; import { IClientStateHolder } from "../RpcRequestsHandler"; describe("RpcRequestsHandler", () => { @@ -26,6 +28,8 @@ describe("RpcRequestsHandler", () => { let clientId: string; let defaultRpcOptions: RpcRequestOptions & { imodel: IModelToken }; const token = new IModelToken(); + const successResponse = (result?: any): RpcResponse => ({ statusCode: PresentationStatus.Success, result }); + const errorResponse = (statusCode: PresentationStatus, errorMessage?: string): RpcResponse => ({ statusCode, errorMessage, result: undefined }); beforeEach(() => { clientId = faker.random.uuid(); @@ -175,7 +179,7 @@ describe("RpcRequestsHandler", () => { }, }; - rpcInterfaceMock.setup((x) => x.syncClientState(token, expectedSyncOptions)).returns(() => Promise.resolve()).verifiable(); + rpcInterfaceMock.setup(async (x) => x.syncClientState(token, expectedSyncOptions)).returns(async () => successResponse()).verifiable(); await handler.sync(token); rpcInterfaceMock.verifyAll(); }); @@ -197,7 +201,7 @@ describe("RpcRequestsHandler", () => { }, }; - rpcInterfaceMock.setup((x) => x.syncClientState(token, expectedSyncOptions)).returns(() => Promise.resolve()).verifiable(); + rpcInterfaceMock.setup(async (x) => x.syncClientState(token, expectedSyncOptions)).returns(async () => successResponse()).verifiable(); await handler.sync(token); rpcInterfaceMock.verifyAll(); }); @@ -230,7 +234,7 @@ describe("RpcRequestsHandler", () => { }, }; - rpcInterfaceMock.setup((x) => x.syncClientState(token, expectedSyncOptions)).returns(() => Promise.resolve()).verifiable(); + rpcInterfaceMock.setup(async (x) => x.syncClientState(token, expectedSyncOptions)).returns(async () => successResponse()).verifiable(); await handler.sync(token); rpcInterfaceMock.verifyAll(); }); @@ -253,7 +257,7 @@ describe("RpcRequestsHandler", () => { it("returns result of the request", async () => { const result = faker.random.number(); - const actualResult = await handler.request(undefined, async () => result, defaultRpcOptions); + const actualResult = await handler.request(undefined, async () => successResponse(result), defaultRpcOptions); expect(actualResult).to.eq(result); }); @@ -263,34 +267,36 @@ describe("RpcRequestsHandler", () => { it("re-throws exception when request throws unknown exception", async () => { const func = async () => { throw new Error("test"); }; - expect(handler.request(undefined, func, defaultRpcOptions)).to.eventually.be.rejectedWith(Error); + await expect(handler.request(undefined, func, defaultRpcOptions)).to.eventually.be.rejectedWith(Error); }); }); - describe("when request throws BackendOutOfSync exception", () => { + describe("when request returns an unexpected status", () => { - let callsCount: number; - let func: () => Promise; - - beforeEach(() => { - callsCount = 0; - func = async () => { - switch (callsCount++) { - case 0: throw new PresentationError(PresentationStatus.BackendOutOfSync); - default: return faker.random.number(); - } - }; + it("throws an exception", async () => { + const func = async () => Promise.resolve(errorResponse(PresentationStatus.Error)); + await expect(handler.request(undefined, func, defaultRpcOptions)).to.eventually.be.rejectedWith(PresentationError); }); + }); + + describe("when request returns a status of BackendOutOfSync", () => { + it("syncs and repeats request", async () => { - const syncMock = moq.Mock.ofInstance((_token: IModelToken) => Promise.resolve()); - handler.sync = syncMock.object; + const syncStub = sinon.stub(); + handler.sync = syncStub; + + const requestHandlerStub = sinon.stub(); + requestHandlerStub.onFirstCall().returns(Promise.resolve(errorResponse(PresentationStatus.BackendOutOfSync))); + requestHandlerStub.onSecondCall().returns(Promise.resolve(errorResponse(PresentationStatus.BackendOutOfSync))); + requestHandlerStub.onThirdCall().returns(Promise.resolve(successResponse(faker.random.number()))); + const requestHandlerSpy = sinon.spy(() => requestHandlerStub()); - const result = await handler.request(undefined, func, defaultRpcOptions); + const result = await handler.request(undefined, requestHandlerSpy, defaultRpcOptions); expect(result).to.not.be.undefined; - syncMock.verify((x) => x(defaultRpcOptions.imodel), moq.Times.once()); - expect(callsCount).to.eq(2); + expect(syncStub).to.be.calledTwice; + expect(requestHandlerSpy).to.be.calledThrice; }); }); @@ -315,9 +321,10 @@ describe("RpcRequestsHandler", () => { beforeEach(() => { handler = new RpcRequestsHandler({ clientId }); - handler.request = (context: any, func: (token: IModelToken, options: TOptions, ...args: TArg) => Promise, options: TOptions, ...args: TArg): Promise => { + handler.request = async (context: any, func: (token: IModelToken, options: TOptions, ...args: any[]) => PresentationRpcResponse, options: TOptions, ...args: any[]): Promise => { expect(context).to.eq(rpcInterfaceMock.object); - return func.apply(context, [token, options, ...args]); + const result = await func.apply(context, [token, options, ...args]); + return result.result!; }; rpcInterfaceMock.reset(); }); @@ -326,31 +333,31 @@ describe("RpcRequestsHandler", () => { handler.dispose(); }); - it("forwards getRootNodes call", async () => { + it("forwards getNodesAndCount call", async () => { const options: Paged> = { imodel: token, rulesetId: faker.random.word(), }; const rpcOptions = { ...defaultRpcOptions, ...options }; - const result = [createRandomECInstanceNode()]; - rpcInterfaceMock.setup((x) => x.getRootNodes(token, rpcOptions)).returns(async () => result).verifiable(); - expect(await handler.getRootNodes(options)).to.eq(result); + const result = { nodes: [createRandomECInstanceNode()], count: 1 }; + rpcInterfaceMock.setup(async (x) => x.getNodesAndCount(token, rpcOptions, undefined)).returns(async () => successResponse(result)).verifiable(); + expect(await handler.getNodesAndCount(options)).to.eq(result); rpcInterfaceMock.verifyAll(); }); - it("forwards getRootNodesCount call", async () => { - const options: HierarchyRequestOptions = { + it("forwards getNodes call for root nodes", async () => { + const options: Paged> = { imodel: token, rulesetId: faker.random.word(), }; const rpcOptions = { ...defaultRpcOptions, ...options }; - const result = faker.random.number(); - rpcInterfaceMock.setup((x) => x.getRootNodesCount(token, rpcOptions)).returns(async () => result).verifiable(); - expect(await handler.getRootNodesCount(options)).to.eq(result); + const result = [createRandomECInstanceNode()]; + rpcInterfaceMock.setup(async (x) => x.getNodes(token, rpcOptions, undefined)).returns(async () => successResponse(result)).verifiable(); + expect(await handler.getNodes(options)).to.eq(result); rpcInterfaceMock.verifyAll(); }); - it("forwards getChildren call", async () => { + it("forwards getNodes call for child nodes", async () => { const parentKey = createRandomECInstanceNodeKey(); const options: Paged> = { imodel: token, @@ -358,12 +365,24 @@ describe("RpcRequestsHandler", () => { }; const rpcOptions = { ...defaultRpcOptions, ...options }; const result = [createRandomECInstanceNode()]; - rpcInterfaceMock.setup((x) => x.getChildren(token, rpcOptions, parentKey)).returns(async () => result).verifiable(); - expect(await handler.getChildren(options, parentKey)).to.eq(result); + rpcInterfaceMock.setup(async (x) => x.getNodes(token, rpcOptions, parentKey)).returns(async () => successResponse(result)).verifiable(); + expect(await handler.getNodes(options, parentKey)).to.eq(result); rpcInterfaceMock.verifyAll(); }); - it("forwards getChildrenCount call", async () => { + it("forwards getNodesCount call for root nodes", async () => { + const options: HierarchyRequestOptions = { + imodel: token, + rulesetId: faker.random.word(), + }; + const rpcOptions = { ...defaultRpcOptions, ...options }; + const result = faker.random.number(); + rpcInterfaceMock.setup(async (x) => x.getNodesCount(token, rpcOptions, undefined)).returns(async () => successResponse(result)).verifiable(); + expect(await handler.getNodesCount(options)).to.eq(result); + rpcInterfaceMock.verifyAll(); + }); + + it("forwards getNodesCount call for child nodes", async () => { const parentKey = createRandomECInstanceNodeKey(); const options: HierarchyRequestOptions = { imodel: token, @@ -371,8 +390,8 @@ describe("RpcRequestsHandler", () => { }; const rpcOptions = { ...defaultRpcOptions, ...options }; const result = faker.random.number(); - rpcInterfaceMock.setup((x) => x.getChildrenCount(token, rpcOptions, parentKey)).returns(async () => result).verifiable(); - expect(await handler.getChildrenCount(options, parentKey)).to.eq(result); + rpcInterfaceMock.setup(async (x) => x.getNodesCount(token, rpcOptions, parentKey)).returns(async () => successResponse(result)).verifiable(); + expect(await handler.getNodesCount(options, parentKey)).to.eq(result); rpcInterfaceMock.verifyAll(); }); @@ -384,7 +403,7 @@ describe("RpcRequestsHandler", () => { const rpcOptions = { ...defaultRpcOptions, ...options }; const filter = faker.random.word(); const result = [createRandomNodePathElement()]; - rpcInterfaceMock.setup((x) => x.getFilteredNodePaths(token, rpcOptions, filter)).returns(async () => result).verifiable(); + rpcInterfaceMock.setup(async (x) => x.getFilteredNodePaths(token, rpcOptions, filter)).returns(async () => successResponse(result)).verifiable(); expect(await handler.getFilteredNodePaths(options, filter)).to.eq(result); rpcInterfaceMock.verifyAll(); }); @@ -398,7 +417,7 @@ describe("RpcRequestsHandler", () => { const paths = [[createRandomECInstanceKey()]]; const markedIndex = faker.random.number(); const result = [createRandomNodePathElement()]; - rpcInterfaceMock.setup((x) => x.getNodePaths(token, rpcOptions, paths, markedIndex)).returns(async () => result).verifiable(); + rpcInterfaceMock.setup(async (x) => x.getNodePaths(token, rpcOptions, paths, markedIndex)).returns(async () => successResponse(result)).verifiable(); expect(await handler.getNodePaths(options, paths, markedIndex)).to.eq(result); rpcInterfaceMock.verifyAll(); }); @@ -413,7 +432,7 @@ describe("RpcRequestsHandler", () => { const keys = new KeySet(); const selectionInfo: SelectionInfo = { providerName: faker.random.word() }; const result = createRandomDescriptor(); - rpcInterfaceMock.setup((x) => x.getContentDescriptor(token, rpcOptions, displayType, keys, selectionInfo)).returns(async () => result).verifiable(); + rpcInterfaceMock.setup(async (x) => x.getContentDescriptor(token, rpcOptions, displayType, keys, selectionInfo)).returns(async () => successResponse(result)).verifiable(); expect(await handler.getContentDescriptor(options, displayType, keys, selectionInfo)).to.eq(result); rpcInterfaceMock.verifyAll(); }); @@ -427,7 +446,7 @@ describe("RpcRequestsHandler", () => { const descriptor = createRandomDescriptor(); const keys = new KeySet(); const result = faker.random.number(); - rpcInterfaceMock.setup((x) => x.getContentSetSize(token, rpcOptions, descriptor, keys)).returns(async () => result).verifiable(); + rpcInterfaceMock.setup(async (x) => x.getContentSetSize(token, rpcOptions, descriptor, keys)).returns(async () => successResponse(result)).verifiable(); expect(await handler.getContentSetSize(options, descriptor, keys)).to.eq(result); rpcInterfaceMock.verifyAll(); }); @@ -441,11 +460,25 @@ describe("RpcRequestsHandler", () => { const descriptor = createRandomDescriptor(); const keys = new KeySet(); const result = createRandomContent(); - rpcInterfaceMock.setup((x) => x.getContent(token, rpcOptions, descriptor, keys)).returns(async () => result).verifiable(); + rpcInterfaceMock.setup(async (x) => x.getContent(token, rpcOptions, descriptor, keys)).returns(async () => successResponse(result)).verifiable(); expect(await handler.getContent(options, descriptor, keys)).to.eq(result); rpcInterfaceMock.verifyAll(); }); + it("forwards getContentAndSize call", async () => { + const options: Paged> = { + imodel: token, + rulesetId: faker.random.word(), + }; + const rpcOptions = { ...defaultRpcOptions, ...options }; + const descriptor = createRandomDescriptor(); + const keys = new KeySet(); + const result = { content: createRandomContent(), size: 1 }; + rpcInterfaceMock.setup(async (x) => x.getContentAndSize(token, rpcOptions, descriptor, keys)).returns(async () => successResponse(result)).verifiable(); + expect(await handler.getContentAndSize(options, descriptor, keys)).to.eq(result); + rpcInterfaceMock.verifyAll(); + }); + it("forwards getDistinctValues call", async () => { const options: ContentRequestOptions = { imodel: token, @@ -457,11 +490,35 @@ describe("RpcRequestsHandler", () => { const fieldName = faker.random.word(); const maxItems = faker.random.number(); const result = [faker.random.word()]; - rpcInterfaceMock.setup((x) => x.getDistinctValues(token, rpcOptions, descriptor, keys, fieldName, maxItems)).returns(async () => result).verifiable(); + rpcInterfaceMock.setup(async (x) => x.getDistinctValues(token, rpcOptions, descriptor, keys, fieldName, maxItems)).returns(async () => successResponse(result)).verifiable(); expect(await handler.getDistinctValues(options, descriptor, keys, fieldName, maxItems)).to.eq(result); rpcInterfaceMock.verifyAll(); }); + it("forwards getSelectionScopes call", async () => { + const options: SelectionScopeRequestOptions = { + imodel: token, + }; + const rpcOptions = { ...defaultRpcOptions, ...options }; + const result = [createRandomSelectionScope()]; + rpcInterfaceMock.setup(async (x) => x.getSelectionScopes(token, rpcOptions)).returns(async () => successResponse(result)).verifiable(); + expect(await handler.getSelectionScopes(options)).to.eq(result); + rpcInterfaceMock.verifyAll(); + }); + + it("forwards computeSelection call", async () => { + const options: SelectionScopeRequestOptions = { + imodel: token, + }; + const rpcOptions = { ...defaultRpcOptions, ...options }; + const keys = new Array(); + const scopeId = faker.random.uuid(); + const result = new KeySet(); + rpcInterfaceMock.setup(async (x) => x.computeSelection(token, rpcOptions, keys, scopeId)).returns(async () => successResponse(result)).verifiable(); + expect(await handler.computeSelection(options, keys, scopeId)).to.eq(result); + rpcInterfaceMock.verifyAll(); + }); + }); }); diff --git a/presentation/common/src/test/RulesetsFactory.test.ts b/presentation/common/src/test/RulesetsFactory.test.ts new file mode 100644 index 0000000..ed815d2 --- /dev/null +++ b/presentation/common/src/test/RulesetsFactory.test.ts @@ -0,0 +1,1230 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { expect } from "chai"; +import * as faker from "faker"; +import { + createRandomCategory, createRandomPrimitiveTypeDescription, + createRandomId, createRandomECClassInfo, +} from "./_helpers/random"; +import { + RulesetsFactory, Rule, RuleTypes, RuleSpecificationTypes, + Property, PropertiesField, Field, Item, PropertyValueFormat, + PrimitiveTypeDescription, StructTypeDescription, ArrayTypeDescription, + NestedContentField, RelatedClassInfo, NestedContentValue, RelationshipDirection, ClassInfo, +} from "../presentation-common"; + +describe("RulesetsFactory", () => { + + let factory: RulesetsFactory; + + beforeEach(() => { + factory = new RulesetsFactory(); + }); + + describe("createSimilarInstancesRuleset", () => { + + const createStringTypeDescription = (): PrimitiveTypeDescription => ({ + valueFormat: PropertyValueFormat.Primitive, + typeName: "string", + }); + + const createBooleanTypeDescription = (): PrimitiveTypeDescription => ({ + valueFormat: PropertyValueFormat.Primitive, + typeName: "boolean", + }); + + const createIntTypeDescription = (): PrimitiveTypeDescription => ({ + valueFormat: PropertyValueFormat.Primitive, + typeName: "int", + }); + + const createDoubleTypeDescription = (): PrimitiveTypeDescription => ({ + valueFormat: PropertyValueFormat.Primitive, + typeName: "double", + }); + + const createNavigationPropertyTypeDescription = (): PrimitiveTypeDescription => ({ + valueFormat: PropertyValueFormat.Primitive, + typeName: "navigation", + }); + + it("creates a valid ruleset for string record", () => { + const recordClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:MyClass", + label: "My Class", + }; + const property: Property = { + property: { + classInfo: recordClass, + type: "string", + name: "MyProperty", + }, + relatedClassPath: [], + }; + const field = new PropertiesField(createRandomCategory(), "MyProperty", + "My Property", createStringTypeDescription(), true, 1, [property]); + const record = new Item([], faker.random.word(), "", recordClass, + { MyProperty: "test value" }, { MyProperty: "test display value" }, []); + const result = factory.createSimilarInstancesRuleset(field, record); + const expectedRules: Rule[] = [{ + ruleType: RuleTypes.Content, + specifications: [{ + specType: RuleSpecificationTypes.ContentInstancesOfSpecificClasses, + classes: { schemaName: "MySchema", classNames: ["MyClass"] }, + arePolymorphic: true, + relatedInstances: [], + instanceFilter: `this.MyProperty = "test value"`, + }], + }]; + expect(result.ruleset.rules).to.deep.eq(expectedRules); + expect(result.description).to.eq(`[My Class].[My Property] = test display value`); + }); + + it("creates a valid ruleset for boolean `true` record", () => { + const recordClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:MyClass", + label: "My Class", + }; + const property: Property = { + property: { + classInfo: recordClass, + type: "boolean", + name: "MyProperty", + }, + relatedClassPath: [], + }; + const field = new PropertiesField(createRandomCategory(), "MyProperty", + "My Property", createBooleanTypeDescription(), true, 1, [property]); + const record = new Item([], faker.random.word(), "", recordClass, + { MyProperty: true }, { MyProperty: "True" }, []); + const result = factory.createSimilarInstancesRuleset(field, record); + const expectedRules: Rule[] = [{ + ruleType: RuleTypes.Content, + specifications: [{ + specType: RuleSpecificationTypes.ContentInstancesOfSpecificClasses, + classes: { schemaName: "MySchema", classNames: ["MyClass"] }, + arePolymorphic: true, + relatedInstances: [], + instanceFilter: `this.MyProperty = TRUE`, + }], + }]; + expect(result.ruleset.rules).to.deep.eq(expectedRules); + expect(result.description).to.eq(`[My Class].[My Property] = True`); + }); + + it("creates a valid ruleset for boolean `false` record", () => { + const recordClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:MyClass", + label: "My Class", + }; + const property: Property = { + property: { + classInfo: recordClass, + type: "boolean", + name: "MyProperty", + }, + relatedClassPath: [], + }; + const field = new PropertiesField(createRandomCategory(), "MyProperty", + "My Property", createBooleanTypeDescription(), true, 1, [property]); + const record = new Item([], faker.random.word(), "", recordClass, + { MyProperty: false }, { MyProperty: "False" }, []); + const result = factory.createSimilarInstancesRuleset(field, record); + const expectedRules: Rule[] = [{ + ruleType: RuleTypes.Content, + specifications: [{ + specType: RuleSpecificationTypes.ContentInstancesOfSpecificClasses, + classes: { schemaName: "MySchema", classNames: ["MyClass"] }, + arePolymorphic: true, + relatedInstances: [], + instanceFilter: `this.MyProperty = FALSE`, + }], + }]; + expect(result.ruleset.rules).to.deep.eq(expectedRules); + expect(result.description).to.eq(`[My Class].[My Property] = False`); + }); + + it("creates a valid ruleset for int record", () => { + const recordClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:MyClass", + label: "My Class", + }; + const property: Property = { + property: { + classInfo: recordClass, + type: "int", + name: "MyProperty", + }, + relatedClassPath: [], + }; + const field = new PropertiesField(createRandomCategory(), "MyProperty", + "My Property", createIntTypeDescription(), true, 1, [property]); + const record = new Item([], faker.random.word(), "", recordClass, + { MyProperty: 123 }, { MyProperty: "123" }, []); + const result = factory.createSimilarInstancesRuleset(field, record); + const expectedRules: Rule[] = [{ + ruleType: RuleTypes.Content, + specifications: [{ + specType: RuleSpecificationTypes.ContentInstancesOfSpecificClasses, + classes: { schemaName: "MySchema", classNames: ["MyClass"] }, + arePolymorphic: true, + relatedInstances: [], + instanceFilter: `this.MyProperty = 123`, + }], + }]; + expect(result.ruleset.rules).to.deep.eq(expectedRules); + expect(result.description).to.eq(`[My Class].[My Property] = 123`); + }); + + it("creates a valid ruleset for double record", () => { + const recordClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:MyClass", + label: "My Class", + }; + const property: Property = { + property: { + classInfo: recordClass, + type: "double", + name: "MyProperty", + }, + relatedClassPath: [], + }; + const field = new PropertiesField(createRandomCategory(), "MyProperty", + "My Property", createDoubleTypeDescription(), true, 1, [property]); + const record = new Item([], faker.random.word(), "", recordClass, + { MyProperty: 123.456 }, { MyProperty: "123.46" }, []); + const result = factory.createSimilarInstancesRuleset(field, record); + const expectedRules: Rule[] = [{ + ruleType: RuleTypes.Content, + specifications: [{ + specType: RuleSpecificationTypes.ContentInstancesOfSpecificClasses, + classes: { schemaName: "MySchema", classNames: ["MyClass"] }, + arePolymorphic: true, + relatedInstances: [], + instanceFilter: `this.MyProperty = 123.456`, // WIP should this use display value instead? + }], + }]; + expect(result.ruleset.rules).to.deep.eq(expectedRules); + expect(result.description).to.eq(`[My Class].[My Property] = 123.46`); + }); + + it("creates a valid ruleset for null record", () => { + const recordClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:MyClass", + label: "My Class", + }; + const property: Property = { + property: { + classInfo: recordClass, + type: "string", + name: "MyProperty", + }, + relatedClassPath: [], + }; + const field = new PropertiesField(createRandomCategory(), "MyProperty", + "My Property", createStringTypeDescription(), true, 1, [property]); + const record = new Item([], faker.random.word(), "", recordClass, + { MyProperty: undefined }, { MyProperty: "" }, []); + const result = factory.createSimilarInstancesRuleset(field, record); + const expectedRules: Rule[] = [{ + ruleType: RuleTypes.Content, + specifications: [{ + specType: RuleSpecificationTypes.ContentInstancesOfSpecificClasses, + classes: { schemaName: "MySchema", classNames: ["MyClass"] }, + arePolymorphic: true, + relatedInstances: [], + instanceFilter: `this.MyProperty = NULL`, + }], + }]; + expect(result.ruleset.rules).to.deep.eq(expectedRules); + expect(result.description).to.eq(`[My Class].[My Property] = NULL`); + }); + + it("creates a valid ruleset for navigation property record", () => { + const recordClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:MyClass", + label: "My Class", + }; + const property: Property = { + property: { + classInfo: recordClass, + type: "long", + name: "MyProperty", + }, + relatedClassPath: [], + }; + const field = new PropertiesField(createRandomCategory(), "MyProperty", + "My Property", createNavigationPropertyTypeDescription(), true, 1, [property]); + const record = new Item([], faker.random.word(), "", recordClass, + { MyProperty: "0x16" }, { MyProperty: "test display value" }, []); + const result = factory.createSimilarInstancesRuleset(field, record); + const expectedRules: Rule[] = [{ + ruleType: RuleTypes.Content, + specifications: [{ + specType: RuleSpecificationTypes.ContentInstancesOfSpecificClasses, + classes: { schemaName: "MySchema", classNames: ["MyClass"] }, + arePolymorphic: true, + relatedInstances: [], + instanceFilter: `this.MyProperty.Id = "0x16"`, + }], + }]; + expect(result.ruleset.rules).to.deep.eq(expectedRules); + expect(result.description).to.eq(`[My Class].[My Property] = test display value`); + }); + + it("creates a valid ruleset for one-step forward related nested content record", () => { + const recordClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:PrimaryClass", + label: "Primary Class", + }; + const propertyClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:RelatedClass", + label: "Related Class", + }; + const relationshipPath: RelatedClassInfo[] = [{ + sourceClassInfo: propertyClass, + targetClassInfo: recordClass, + relationshipInfo: { + id: createRandomId(), + name: "MySchema:MyRelationship", + label: "My Relationship", + }, + isForwardRelationship: false, + isPolymorphicRelationship: true, + }]; + const property: Property = { + property: { + classInfo: propertyClass, + type: "string", + name: "MyProperty", + }, + relatedClassPath: [], + }; + const field = new PropertiesField(createRandomCategory(), faker.random.word(), + "Related Property", createStringTypeDescription(), true, 1, [property]); + const parentField = new NestedContentField(createRandomCategory(), faker.random.word(), + faker.random.words(), createRandomPrimitiveTypeDescription(), faker.random.boolean(), + faker.random.number(), createRandomECClassInfo(), relationshipPath, [field]); + field.rebuildParentship(parentField); + const values = { + [parentField.name]: [{ + primaryKeys: [], + values: { + [field.name]: "test value", + }, + displayValues: { + [field.name]: "test display value", + }, + mergedFieldNames: [], + }] as NestedContentValue[], + }; + const displayValues = { + [field.name]: undefined, + }; + const record = new Item([], faker.random.words(), "", recordClass, values, displayValues, []); + const result = factory.createSimilarInstancesRuleset(field, record); + const expectedRules: Rule[] = [{ + ruleType: RuleTypes.Content, + specifications: [{ + specType: RuleSpecificationTypes.ContentInstancesOfSpecificClasses, + classes: { schemaName: "MySchema", classNames: ["PrimaryClass"] }, + arePolymorphic: true, + relatedInstances: [{ + relationship: { schemaName: "MySchema", className: "MyRelationship" }, + class: { schemaName: "MySchema", className: "RelatedClass" }, + requiredDirection: RelationshipDirection.Forward, + isRequired: true, + alias: "related_0", + }], + instanceFilter: `related_0.MyProperty = "test value"`, + }], + }]; + expect(result.ruleset.rules).to.deep.eq(expectedRules); + expect(result.description).to.eq(`[Related Class].[Related Property] = test display value`); + }); + + it("creates a valid ruleset for one-step backward related nested content record", () => { + const recordClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:PrimaryClass", + label: "Primary Class", + }; + const propertyClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:RelatedClass", + label: "Related Class", + }; + const relationshipPath: RelatedClassInfo[] = [{ + sourceClassInfo: recordClass, + targetClassInfo: propertyClass, + relationshipInfo: { + id: createRandomId(), + name: "MySchema:MyRelationship", + label: "My Relationship", + }, + isForwardRelationship: true, + isPolymorphicRelationship: true, + }]; + const property: Property = { + property: { + classInfo: propertyClass, + type: "string", + name: "RelatedProperty", + }, + relatedClassPath: [], + }; + const field = new PropertiesField(createRandomCategory(), faker.random.word(), + "Related Property", createStringTypeDescription(), true, 1, [property]); + const parentField = new NestedContentField(createRandomCategory(), faker.random.word(), + faker.random.words(), createRandomPrimitiveTypeDescription(), faker.random.boolean(), + faker.random.number(), createRandomECClassInfo(), relationshipPath, [field]); + field.rebuildParentship(parentField); + const values = { + [parentField.name]: [{ + primaryKeys: [], + values: { + [field.name]: "test value", + }, + displayValues: { + [field.name]: "test display value", + }, + mergedFieldNames: [], + }] as NestedContentValue[], + }; + const displayValues = { + [field.name]: undefined, + }; + const record = new Item([], faker.random.words(), "", recordClass, values, displayValues, []); + const result = factory.createSimilarInstancesRuleset(field, record); + const expectedRules: Rule[] = [{ + ruleType: RuleTypes.Content, + specifications: [{ + specType: RuleSpecificationTypes.ContentInstancesOfSpecificClasses, + classes: { schemaName: "MySchema", classNames: ["PrimaryClass"] }, + arePolymorphic: true, + relatedInstances: [{ + relationship: { schemaName: "MySchema", className: "MyRelationship" }, + class: { schemaName: "MySchema", className: "RelatedClass" }, + requiredDirection: RelationshipDirection.Backward, + isRequired: true, + alias: "related_0", + }], + instanceFilter: `related_0.RelatedProperty = "test value"`, + }], + }]; + expect(result.ruleset.rules).to.deep.eq(expectedRules); + expect(result.description).to.eq(`[Related Class].[Related Property] = test display value`); + }); + + it("creates a valid ruleset for one-step forward related property record", () => { + const recordClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:PrimaryClass", + label: "Primary Class", + }; + const propertyClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:RelatedClass", + label: "Related Class", + }; + const relationshipPath: RelatedClassInfo[] = [{ + sourceClassInfo: propertyClass, + targetClassInfo: recordClass, + relationshipInfo: { + id: createRandomId(), + name: "MySchema:MyRelationship", + label: "My Relationship", + }, + isForwardRelationship: false, + isPolymorphicRelationship: true, + }]; + const property: Property = { + property: { + classInfo: propertyClass, + type: "string", + name: "RelatedProperty", + }, + relatedClassPath: relationshipPath, + }; + const field = new PropertiesField(createRandomCategory(), "RelatedProperty", + "Related Property", createStringTypeDescription(), true, 1, [property]); + const record = new Item([], faker.random.words(), "", recordClass, + { RelatedProperty: "test value" }, { RelatedProperty: "test display value" }, []); + const result = factory.createSimilarInstancesRuleset(field, record); + const expectedRules: Rule[] = [{ + ruleType: RuleTypes.Content, + specifications: [{ + specType: RuleSpecificationTypes.ContentInstancesOfSpecificClasses, + classes: { schemaName: "MySchema", classNames: ["PrimaryClass"] }, + arePolymorphic: true, + relatedInstances: [{ + relationship: { schemaName: "MySchema", className: "MyRelationship" }, + class: { schemaName: "MySchema", className: "RelatedClass" }, + requiredDirection: RelationshipDirection.Forward, + isRequired: true, + alias: "related_0", + }], + instanceFilter: `related_0.RelatedProperty = "test value"`, + }], + }]; + expect(result.ruleset.rules).to.deep.eq(expectedRules); + expect(result.description).to.eq(`[Related Class].[Related Property] = test display value`); + }); + + it("creates a valid ruleset for one-step backward related property record", () => { + const recordClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:PrimaryClass", + label: "Primary Class", + }; + const propertyClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:RelatedClass", + label: "Related Class", + }; + const relationshipPath: RelatedClassInfo[] = [{ + sourceClassInfo: recordClass, + targetClassInfo: propertyClass, + relationshipInfo: { + id: createRandomId(), + name: "MySchema:MyRelationship", + label: "My Relationship", + }, + isForwardRelationship: true, + isPolymorphicRelationship: true, + }]; + const property: Property = { + property: { + classInfo: propertyClass, + type: "string", + name: "RelatedProperty", + }, + relatedClassPath: relationshipPath, + }; + const field = new PropertiesField(createRandomCategory(), "RelatedProperty", + "Related Property", createStringTypeDescription(), true, 1, [property]); + const record = new Item([], faker.random.words(), "", recordClass, + { RelatedProperty: "test value" }, { RelatedProperty: "test display value" }, []); + const result = factory.createSimilarInstancesRuleset(field, record); + const expectedRules: Rule[] = [{ + ruleType: RuleTypes.Content, + specifications: [{ + specType: RuleSpecificationTypes.ContentInstancesOfSpecificClasses, + classes: { schemaName: "MySchema", classNames: ["PrimaryClass"] }, + arePolymorphic: true, + relatedInstances: [{ + relationship: { schemaName: "MySchema", className: "MyRelationship" }, + class: { schemaName: "MySchema", className: "RelatedClass" }, + requiredDirection: RelationshipDirection.Backward, + isRequired: true, + alias: "related_0", + }], + instanceFilter: `related_0.RelatedProperty = "test value"`, + }], + }]; + expect(result.ruleset.rules).to.deep.eq(expectedRules); + expect(result.description).to.eq(`[Related Class].[Related Property] = test display value`); + }); + + it("creates a valid ruleset when related property record is based on multiple properties", () => { + // not sure if this is really a valid case, we can still handle it + const recordClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:PrimaryClass", + label: "Primary Class", + }; + const propertyClass1: ClassInfo = { + id: createRandomId(), + name: "MySchema:RelatedClass1", + label: "Related Class 1", + }; + const relationshipPath1: RelatedClassInfo[] = [{ + sourceClassInfo: propertyClass1, + targetClassInfo: recordClass, + relationshipInfo: { + id: createRandomId(), + name: "MySchema:MyRelationship", + label: "My Relationship", + }, + isForwardRelationship: false, + isPolymorphicRelationship: true, + }]; + const property1: Property = { + property: { + classInfo: propertyClass1, + type: "string", + name: "RelatedProperty", + }, + relatedClassPath: relationshipPath1, + }; + const propertyClass2: ClassInfo = { + id: createRandomId(), + name: "MySchema:RelatedClass2", + label: "Related Class 2", + }; + const relationshipPath2: RelatedClassInfo[] = [{ + sourceClassInfo: propertyClass2, + targetClassInfo: recordClass, + relationshipInfo: { + id: createRandomId(), + name: "MySchema:MyRelationship", + label: "My Relationship", + }, + isForwardRelationship: false, + isPolymorphicRelationship: true, + }]; + const property2: Property = { + property: { + classInfo: propertyClass2, + type: "string", + name: "RelatedProperty", + }, + relatedClassPath: relationshipPath2, + }; + const field = new PropertiesField(createRandomCategory(), "RelatedProperty", + "Related Property", createStringTypeDescription(), true, 1, [property1, property2]); + const record = new Item([], faker.random.words(), "", recordClass, + { RelatedProperty: "test value" }, { RelatedProperty: "test display value" }, []); + const result = factory.createSimilarInstancesRuleset(field, record); + const expectedRules: Rule[] = [{ + ruleType: RuleTypes.Content, + specifications: [{ + specType: RuleSpecificationTypes.ContentInstancesOfSpecificClasses, + classes: { schemaName: "MySchema", classNames: ["PrimaryClass"] }, + arePolymorphic: true, + relatedInstances: [{ + relationship: { schemaName: "MySchema", className: "MyRelationship" }, + class: { schemaName: "MySchema", className: "RelatedClass1" }, + requiredDirection: RelationshipDirection.Forward, + isRequired: true, + alias: "related_0", + }, { + relationship: { schemaName: "MySchema", className: "MyRelationship" }, + class: { schemaName: "MySchema", className: "RelatedClass2" }, + requiredDirection: RelationshipDirection.Forward, + isRequired: true, + alias: "related_1", + }], + instanceFilter: `related_0.RelatedProperty = "test value" OR related_1.RelatedProperty = "test value"`, + }], + }]; + expect(result.ruleset.rules).to.deep.eq(expectedRules); + expect(result.description).to.eq(`[Related Class 1].[Related Property] = test display value OR [Related Class 2].[Related Property] = test display value`); + }); + + describe("invalid conditions", () => { + + it("throws when record contains invalid value", () => { + const recordClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:MyClass", + label: "My Class", + }; + const property: Property = { + property: { + classInfo: recordClass, + type: "boolean", + name: "MyProperty", + }, + relatedClassPath: [], + }; + const field = new PropertiesField(createRandomCategory(), "MyProperty", + faker.random.word(), createBooleanTypeDescription(), true, 1, [property]); + const record = new Item([], faker.random.word(), "", recordClass, + { MyProperty: [] }, { MyProperty: "" }, []); + expect(() => factory.createSimilarInstancesRuleset(field, record)).to.throw(); + }); + + it("throws when properties field contains no properties", () => { + const recordClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:MyClass", + label: "My Class", + }; + const field = new PropertiesField(createRandomCategory(), "MyProperty", + faker.random.word(), createBooleanTypeDescription(), true, 1, []); + const record = new Item([], faker.random.word(), "", recordClass, + { MyProperty: "test value" }, { MyProperty: "test display value" }, []); + expect(() => factory.createSimilarInstancesRuleset(field, record)).to.throw(); + }); + + it("throws when nested content record doesn't have path to primary class", () => { + const recordClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:PrimaryClass", + label: "Primary Class", + }; + const propertyClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:RelatedClass", + label: "Related Class", + }; + const property: Property = { + property: { + classInfo: propertyClass, + type: "string", + name: "MyProperty", + }, + relatedClassPath: [], + }; + const field = new PropertiesField(createRandomCategory(), faker.random.word(), + faker.random.word(), createStringTypeDescription(), true, 1, [property]); + const parentField = new NestedContentField(createRandomCategory(), faker.random.word(), + faker.random.words(), createRandomPrimitiveTypeDescription(), faker.random.boolean(), + faker.random.number(), createRandomECClassInfo(), [], [field]); + field.rebuildParentship(parentField); + const values = { + [parentField.name]: [{ + primaryKeys: [], + values: { + [field.name]: "test value", + }, + displayValues: { + [field.name]: "test display value", + }, + mergedFieldNames: [], + }] as NestedContentValue[], + }; + const displayValues = { + [field.name]: undefined, + }; + const record = new Item([], faker.random.words(), "", recordClass, values, displayValues, []); + expect(() => factory.createSimilarInstancesRuleset(field, record)).to.throw(); + }); + + it("throws when nested content record contains invalid value", () => { + const recordClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:PrimaryClass", + label: "Primary Class", + }; + const propertyClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:RelatedClass", + label: "Related Class", + }; + const relationshipPath: RelatedClassInfo[] = [{ + sourceClassInfo: propertyClass, + targetClassInfo: recordClass, + relationshipInfo: { + id: createRandomId(), + name: "MySchema:MyRelationship", + label: "My Relationship", + }, + isForwardRelationship: false, + isPolymorphicRelationship: true, + }]; + const property: Property = { + property: { + classInfo: propertyClass, + type: "string", + name: "MyProperty", + }, + relatedClassPath: [], + }; + const field = new PropertiesField(createRandomCategory(), faker.random.word(), + faker.random.word(), createStringTypeDescription(), true, 1, [property]); + const parentField = new NestedContentField(createRandomCategory(), faker.random.word(), + faker.random.words(), createRandomPrimitiveTypeDescription(), faker.random.boolean(), + faker.random.number(), createRandomECClassInfo(), relationshipPath, [field]); + field.rebuildParentship(parentField); + const values = { + [parentField.name]: "invalid", + }; + const displayValues = { + [field.name]: undefined, + }; + const record = new Item([], faker.random.words(), "", recordClass, values, displayValues, []); + expect(() => factory.createSimilarInstancesRuleset(field, record)).to.throw(); + }); + + }); + + describe("unsupported conditions", () => { + + it("throws when field is not a properties field", () => { + const field = new Field(createRandomCategory(), faker.random.word(), + faker.random.word(), createRandomPrimitiveTypeDescription(), true, 1); + const record = new Item([], faker.random.word(), "", undefined, {}, {}, []); + expect(() => factory.createSimilarInstancesRuleset(field, record)).to.throw(); + }); + + it("throws when properties field is of point2d type", () => { + const property: Property = { + property: { + classInfo: createRandomECClassInfo(), + name: faker.random.word(), + type: faker.database.type(), + }, + relatedClassPath: [], + }; + const typeDescription: PrimitiveTypeDescription = { + valueFormat: PropertyValueFormat.Primitive, + typeName: "point2d", + }; + const field = new PropertiesField(createRandomCategory(), faker.random.word(), + faker.random.word(), typeDescription, true, 1, [property]); + const values = { + [field.name]: { x: 1, y: 2 }, + }; + const displayValues = { + [field.name]: { x: "one", y: "two" }, + }; + const record = new Item([], faker.random.word(), "", undefined, values, displayValues, []); + expect(() => factory.createSimilarInstancesRuleset(field, record)).to.throw(); + }); + + it("throws when properties field is of point3d type", () => { + const property: Property = { + property: { + classInfo: createRandomECClassInfo(), + name: faker.random.word(), + type: faker.database.type(), + }, + relatedClassPath: [], + }; + const typeDescription: PrimitiveTypeDescription = { + valueFormat: PropertyValueFormat.Primitive, + typeName: "point3d", + }; + const field = new PropertiesField(createRandomCategory(), faker.random.word(), + faker.random.word(), typeDescription, true, 1, [property]); + const values = { + [field.name]: { x: 1, y: 2, z: 3 }, + }; + const displayValues = { + [field.name]: { x: "one", y: "two", z: "three" }, + }; + const record = new Item([], faker.random.word(), "", undefined, values, displayValues, []); + expect(() => factory.createSimilarInstancesRuleset(field, record)).to.throw(); + }); + + it("throws when properties field is of array type", () => { + const property: Property = { + property: { + classInfo: createRandomECClassInfo(), + name: faker.random.word(), + type: faker.database.type(), + }, + relatedClassPath: [], + }; + const typeDescription: ArrayTypeDescription = { + valueFormat: PropertyValueFormat.Array, + typeName: faker.random.word(), + memberType: createRandomPrimitiveTypeDescription(), + }; + const field = new PropertiesField(createRandomCategory(), faker.random.word(), + faker.random.word(), typeDescription, true, 1, [property]); + const values = { + [field.name]: ["some value 1", "some value 2"], + }; + const displayValues = { + [field.name]: ["some display value 1", "some display value 2"], + }; + const record = new Item([], faker.random.word(), "", undefined, values, displayValues, []); + expect(() => factory.createSimilarInstancesRuleset(field, record)).to.throw(); + }); + + it("throws when properties field is of struct type", () => { + const property: Property = { + property: { + classInfo: createRandomECClassInfo(), + name: faker.random.word(), + type: faker.database.type(), + }, + relatedClassPath: [], + }; + const typeDescription: StructTypeDescription = { + valueFormat: PropertyValueFormat.Struct, + typeName: faker.random.word(), + members: [{ + name: faker.random.word(), + label: faker.random.words(), + type: createRandomPrimitiveTypeDescription(), + }], + }; + const field = new PropertiesField(createRandomCategory(), faker.random.word(), + faker.random.word(), typeDescription, true, 1, [property]); + const values = { + [field.name]: { + [typeDescription.members[0].name]: "some value", + }, + }; + const displayValues = { + [field.name]: { + [typeDescription.members[0].name]: "some display value", + }, + }; + const record = new Item([], faker.random.word(), "", undefined, values, displayValues, []); + expect(() => factory.createSimilarInstancesRuleset(field, record)).to.throw(); + }); + + it("throws when record is merged", () => { + const property: Property = { + property: { + classInfo: { + id: createRandomId(), + name: "MySchema:MyClass", + label: "My Class", + }, + type: "string", + name: "MyProperty", + }, + relatedClassPath: [], + }; + const field = new PropertiesField(createRandomCategory(), "MyProperty", + faker.random.word(), createStringTypeDescription(), true, 1, [property]); + const record = new Item([], faker.random.word(), "", undefined, + { MyProperty: "test value" }, { MyProperty: "test value" }, ["MyProperty"]); + expect(() => factory.createSimilarInstancesRuleset(field, record)).to.throw(); + }); + + it("throws when record is based on different classes", () => { + const property: Property = { + property: { + classInfo: { + id: createRandomId(), + name: "MySchema:MyClass", + label: "My Class", + }, + type: "string", + name: "MyProperty", + }, + relatedClassPath: [], + }; + const field = new PropertiesField(createRandomCategory(), "MyProperty", + faker.random.word(), createStringTypeDescription(), true, 1, [property]); + const record = new Item([], faker.random.word(), "", + undefined /* this `undefined` means that record is based on multiple different classes */, + { MyProperty: "test value" }, { MyProperty: "test display value" }, []); + expect(() => factory.createSimilarInstancesRuleset(field, record)).to.throw(); + }); + + it("throws when related property is contained inside nested content record", () => { + const recordClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:PrimaryClass", + label: "Primary Class", + }; + const nestedContentClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:NestedContentClass", + label: "Nested Content Class", + }; + const propertyClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:RelatedClass", + label: "Related Class", + }; + const propertyRelationshipPath: RelatedClassInfo[] = [{ + sourceClassInfo: propertyClass, + targetClassInfo: nestedContentClass, + relationshipInfo: { + id: createRandomId(), + name: "MySchema:MyRelationship1", + label: "My Relationship 1", + }, + isForwardRelationship: false, + isPolymorphicRelationship: true, + }]; + const property: Property = { + property: { + classInfo: propertyClass, + type: "string", + name: "MyProperty", + }, + relatedClassPath: propertyRelationshipPath, + }; + const field = new PropertiesField(createRandomCategory(), faker.random.word(), + faker.random.word(), createStringTypeDescription(), true, 1, [property]); + const nestedContentRelationshipPath: RelatedClassInfo[] = [{ + sourceClassInfo: nestedContentClass, + targetClassInfo: recordClass, + relationshipInfo: { + id: createRandomId(), + name: "MySchema:MyRelationship2", + label: "My Relationship 2", + }, + isForwardRelationship: false, + isPolymorphicRelationship: true, + }]; + const parentField = new NestedContentField(createRandomCategory(), faker.random.word(), + faker.random.words(), createRandomPrimitiveTypeDescription(), faker.random.boolean(), + faker.random.number(), nestedContentClass, nestedContentRelationshipPath, [field]); + field.rebuildParentship(parentField); + const values = { + [parentField.name]: [{ + primaryKeys: [], + values: { + [field.name]: "test value", + }, + displayValues: { + [field.name]: "test display value", + }, + mergedFieldNames: [], + }] as NestedContentValue[], + }; + const displayValues = { + [field.name]: undefined, + }; + const record = new Item([], faker.random.words(), "", recordClass, values, displayValues, []); + expect(() => factory.createSimilarInstancesRuleset(field, record)).to.throw(); + }); + + it("throws when property is related through more than one relationship", () => { + const recordClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:PrimaryClass", + label: "Primary Class", + }; + const intermediateClass: ClassInfo = { + id: createRandomId(), + name: faker.random.word(), + label: faker.random.word(), + }; + const propertyClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:RelatedClass", + label: "Related Class", + }; + const relationshipPath: RelatedClassInfo[] = [{ + sourceClassInfo: propertyClass, + targetClassInfo: intermediateClass, + relationshipInfo: { + id: createRandomId(), + name: "MySchema:MyRelationship1", + label: "My Relationship 1", + }, + isForwardRelationship: false, + isPolymorphicRelationship: true, + }, { + sourceClassInfo: intermediateClass, + targetClassInfo: recordClass, + relationshipInfo: { + id: createRandomId(), + name: "MySchema:MyRelationship 2", + label: "My Relationship 2", + }, + isForwardRelationship: false, + isPolymorphicRelationship: true, + }]; + const property: Property = { + property: { + classInfo: propertyClass, + type: "string", + name: "MyProperty", + }, + relatedClassPath: relationshipPath, + }; + const field = new PropertiesField(createRandomCategory(), "MyProperty", + faker.random.word(), createStringTypeDescription(), true, 1, [property]); + const record = new Item([], faker.random.words(), "", recordClass, + { MyProperty: "test value" }, { MyProperty: "test display value" }, []); + expect(() => factory.createSimilarInstancesRuleset(field, record)).to.throw(); + }); + + it("throws when nested content record's path to primary path is longer than 1 step", () => { + const recordClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:PrimaryClass", + label: "Primary Class", + }; + const intermediateClass: ClassInfo = { + id: createRandomId(), + name: faker.random.word(), + label: faker.random.word(), + }; + const propertyClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:RelatedClass", + label: "Related Class", + }; + const relationshipPath: RelatedClassInfo[] = [{ + sourceClassInfo: propertyClass, + targetClassInfo: intermediateClass, + relationshipInfo: { + id: createRandomId(), + name: "MySchema:MyRelationship1", + label: "My Relationship 1", + }, + isForwardRelationship: false, + isPolymorphicRelationship: true, + }, { + sourceClassInfo: intermediateClass, + targetClassInfo: recordClass, + relationshipInfo: { + id: createRandomId(), + name: "MySchema:MyRelationship 2", + label: "My Relationship 2", + }, + isForwardRelationship: false, + isPolymorphicRelationship: true, + }]; + const property: Property = { + property: { + classInfo: propertyClass, + type: "string", + name: "MyProperty", + }, + relatedClassPath: [], + }; + const field = new PropertiesField(createRandomCategory(), faker.random.word(), + faker.random.word(), createStringTypeDescription(), true, 1, [property]); + const parentField = new NestedContentField(createRandomCategory(), faker.random.word(), + faker.random.words(), createRandomPrimitiveTypeDescription(), faker.random.boolean(), + faker.random.number(), createRandomECClassInfo(), relationshipPath, [field]); + field.rebuildParentship(parentField); + const values = { + [parentField.name]: [{ + primaryKeys: [], + values: { + [field.name]: "test value", + }, + displayValues: { + [field.name]: "test display value", + }, + mergedFieldNames: [], + }] as NestedContentValue[], + }; + const displayValues = { + [field.name]: undefined, + }; + const record = new Item([], faker.random.words(), "", recordClass, values, displayValues, []); + expect(() => factory.createSimilarInstancesRuleset(field, record)).to.throw(); + }); + + it("throws when nested content record contains more than one nested record", () => { + const recordClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:PrimaryClass", + label: "Primary Class", + }; + const propertyClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:RelatedClass", + label: "Related Class", + }; + const relationshipPath: RelatedClassInfo[] = [{ + sourceClassInfo: propertyClass, + targetClassInfo: recordClass, + relationshipInfo: { + id: createRandomId(), + name: "MySchema:MyRelationship", + label: "My Relationship", + }, + isForwardRelationship: false, + isPolymorphicRelationship: true, + }]; + const property: Property = { + property: { + classInfo: propertyClass, + type: "string", + name: "MyProperty", + }, + relatedClassPath: [], + }; + const field = new PropertiesField(createRandomCategory(), faker.random.word(), + faker.random.word(), createStringTypeDescription(), true, 1, [property]); + const parentField = new NestedContentField(createRandomCategory(), faker.random.word(), + faker.random.words(), createRandomPrimitiveTypeDescription(), faker.random.boolean(), + faker.random.number(), createRandomECClassInfo(), relationshipPath, [field]); + field.rebuildParentship(parentField); + const values = { + [parentField.name]: [{ + primaryKeys: [], + values: { + [field.name]: "test value 1", + }, + displayValues: { + [field.name]: "test display value 1", + }, + mergedFieldNames: [], + }, { + primaryKeys: [], + values: { + [field.name]: "test value 2", + }, + displayValues: { + [field.name]: "test display value 2", + }, + mergedFieldNames: [], + }] as NestedContentValue[], + }; + const displayValues = { + [field.name]: undefined, + }; + const record = new Item([], faker.random.words(), "", recordClass, values, displayValues, []); + expect(() => factory.createSimilarInstancesRuleset(field, record)).to.throw(); + }); + + it("throws when nested content record is merged", () => { + const recordClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:PrimaryClass", + label: "Primary Class", + }; + const propertyClass: ClassInfo = { + id: createRandomId(), + name: "MySchema:RelatedClass", + label: "Related Class", + }; + const relationshipPath: RelatedClassInfo[] = [{ + sourceClassInfo: propertyClass, + targetClassInfo: recordClass, + relationshipInfo: { + id: createRandomId(), + name: "MySchema:MyRelationship", + label: "My Relationship", + }, + isForwardRelationship: false, + isPolymorphicRelationship: true, + }]; + const property: Property = { + property: { + classInfo: propertyClass, + type: "string", + name: "MyProperty", + }, + relatedClassPath: [], + }; + const field = new PropertiesField(createRandomCategory(), faker.random.word(), + faker.random.word(), createStringTypeDescription(), true, 1, [property]); + const parentField = new NestedContentField(createRandomCategory(), faker.random.word(), + faker.random.words(), createRandomPrimitiveTypeDescription(), faker.random.boolean(), + faker.random.number(), createRandomECClassInfo(), relationshipPath, [field]); + field.rebuildParentship(parentField); + const values = { + [parentField.name]: [{ + primaryKeys: [], + values: { + [field.name]: "test value", + }, + displayValues: { + [field.name]: "test display value", + }, + mergedFieldNames: [field.name], + }] as NestedContentValue[], + }; + const displayValues = { + [field.name]: undefined, + }; + const record = new Item([], faker.random.words(), "", recordClass, values, displayValues, []); + expect(() => factory.createSimilarInstancesRuleset(field, record)).to.throw(); + }); + + }); + + }); + +}); diff --git a/presentation/common/src/test/_helpers/Promises.ts b/presentation/common/src/test/_helpers/Promises.ts index 75d3cb8..1873b05 100644 --- a/presentation/common/src/test/_helpers/Promises.ts +++ b/presentation/common/src/test/_helpers/Promises.ts @@ -35,5 +35,10 @@ export class ResolvablePromise implements PromiseLike { public then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): PromiseLike { return this._wrapped.then(onfulfilled, onrejected); } - public resolve(result: T) { this._resolve(result); } + public async resolve(result: T) { + this._resolve(result); + await new Promise((resolve: () => void) => { + setImmediate(resolve); + }); + } } diff --git a/presentation/common/src/test/_helpers/random/Ruleset.ts b/presentation/common/src/test/_helpers/random/Ruleset.ts index b1d47da..e4fde4c 100644 --- a/presentation/common/src/test/_helpers/random/Ruleset.ts +++ b/presentation/common/src/test/_helpers/random/Ruleset.ts @@ -9,7 +9,7 @@ import { Ruleset } from "../../../presentation-common"; const hasIndexSignature = (o: any): o is { [key: string]: string } => { return typeof o === "object"; }; -const fixEmptyStrings = (obj: T) => { +const fixEmptyStrings = (obj: T) => { if (Array.isArray(obj) || hasIndexSignature(obj)) { for (const key in obj) { if (!obj.hasOwnProperty(key)) diff --git a/presentation/common/src/test/_helpers/random/Selection.ts b/presentation/common/src/test/_helpers/random/Selection.ts new file mode 100644 index 0000000..2e4e53a --- /dev/null +++ b/presentation/common/src/test/_helpers/random/Selection.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import * as faker from "faker"; +import { SelectionScope } from "../../../presentation-common"; +import { nullable } from "./Misc"; + +export const createRandomSelectionScope = (): SelectionScope => ({ + id: faker.random.uuid(), + label: faker.random.word(), + description: nullable(() => faker.random.words()), +}); diff --git a/presentation/common/src/test/_helpers/random/index.ts b/presentation/common/src/test/_helpers/random/index.ts index 5f78c84..78480b2 100644 --- a/presentation/common/src/test/_helpers/random/index.ts +++ b/presentation/common/src/test/_helpers/random/index.ts @@ -8,3 +8,4 @@ export * from "./Hierarchy"; export * from "./Ruleset"; export * from "./IModelJs"; export * from "./Misc"; +export * from "./Selection"; diff --git a/presentation/common/src/test/content/Descriptor.test.ts b/presentation/common/src/test/content/Descriptor.test.ts index 0788d7e..f027fb6 100644 --- a/presentation/common/src/test/content/Descriptor.test.ts +++ b/presentation/common/src/test/content/Descriptor.test.ts @@ -3,9 +3,10 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; +import * as faker from "faker"; import * as moq from "typemoq"; -import { createRandomDescriptorJSON, createRandomDescriptor } from "../_helpers/random"; -import { Descriptor, Field } from "../../presentation-common"; +import { createRandomDescriptorJSON, createRandomDescriptor, createRandomPrimitiveField, createRandomCategory, createRandomECClassInfo, createRandomRelationshipPath } from "../_helpers/random"; +import { Descriptor, Field, NestedContentField, StructTypeDescription, PropertyValueFormat } from "../../presentation-common"; import { DescriptorJSON } from "../../content/Descriptor"; describe("Descriptor", () => { @@ -45,7 +46,7 @@ describe("Descriptor", () => { it("returns undefined when field is not found", () => { const descriptor = createRandomDescriptor(); const name = descriptor.fields.map((f) => f.name).join(); - expect(descriptor.getFieldByName(name)).to.be.undefined; + expect(descriptor.getFieldByName(name, true)).to.be.undefined; }); it("returns a field", () => { @@ -54,6 +55,44 @@ describe("Descriptor", () => { expect(descriptor.getFieldByName(field.name)).to.eq(field); }); + it("returns undefined when descriptor contains nested fields but field is not found", () => { + const descriptor = createRandomDescriptor(); + const primitiveField = createRandomPrimitiveField(); + const descr: StructTypeDescription = { + valueFormat: PropertyValueFormat.Struct, + typeName: faker.random.word(), + members: [{ + type: primitiveField.type, + label: primitiveField.label, + name: primitiveField.name, + }], + }; + const nestedField = new NestedContentField(createRandomCategory(), faker.random.word(), + faker.random.words(), descr, faker.random.boolean(), faker.random.number(), + createRandomECClassInfo(), createRandomRelationshipPath(), [primitiveField]); + descriptor.fields.push(nestedField); + expect(descriptor.getFieldByName("does not exist", true)).to.be.undefined; + }); + + it("returns a nested field", () => { + const descriptor = createRandomDescriptor(); + const primitiveField = createRandomPrimitiveField(); + const descr: StructTypeDescription = { + valueFormat: PropertyValueFormat.Struct, + typeName: faker.random.word(), + members: [{ + type: primitiveField.type, + label: primitiveField.label, + name: primitiveField.name, + }], + }; + const nestedField = new NestedContentField(createRandomCategory(), faker.random.word(), + faker.random.words(), descr, faker.random.boolean(), faker.random.number(), + createRandomECClassInfo(), createRandomRelationshipPath(), [primitiveField]); + descriptor.fields.push(nestedField); + expect(descriptor.getFieldByName(primitiveField.name, true)).to.eq(primitiveField); + }); + }); describe("createDescriptorOverrides", () => { diff --git a/presentation/components/CHANGELOG.json b/presentation/components/CHANGELOG.json index c18c9ca..742ed87 100644 --- a/presentation/components/CHANGELOG.json +++ b/presentation/components/CHANGELOG.json @@ -1,6 +1,63 @@ { "name": "@bentley/presentation-components", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/presentation-components_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "Changes package.json to include api-extractor and adds api-extractor.json" + }, + { + "comment": "Use new buildIModelJsBuild script" + }, + { + "comment": "Exported ContentBuilder and ContentDataProvider" + }, + { + "comment": "Remove uneeded typedoc plugin depedency" + }, + { + "comment": "Expose presentation-specific content request methods through IContentDataProvider so they're available for provider consumers" + }, + { + "comment": "Save BUILD_SEMVER to globally accessible map" + }, + { + "comment": "Change `DataProvidersFactory.createSimilarInstancesTableDataProvider` to return data provider that also has a description" + }, + { + "comment": "Add DataProvidersFactory API for creating presentation data providers targeted towards specific use cases" + }, + { + "comment": "(breaking) Change PresentationTableDataProvider's constructor to accept a props object instead of multiple arguments" + }, + { + "comment": "Make all content data providers IDisposable. **Important:** providers must be disposed after use." + }, + { + "comment": "Changed the way `0` selection level is handled in unified selection tables. Previously we used to reload table data when selection changed with level below boundary __or level `0`__. Now the __underlined__ part is removed and we only reload data if selection changes with level below boundary (set through props)." + }, + { + "comment": "RPC Interface changes to optimize getting first page of nodes/content" + }, + { + "comment": "Move property definitions to imodeljs-frontend so they could be used by tools to define properties for tool settings." + }, + { + "comment": "upgrade to TypeScript 3.2.2" + } + ] + } + }, + { + "version": "0.188.0", + "tag": "@bentley/presentation-components_v0.188.0", + "date": "Wed, 16 Jan 2019 16:36:09 GMT", + "comments": {} + }, { "version": "0.187.0", "tag": "@bentley/presentation-components_v0.187.0", diff --git a/presentation/components/CHANGELOG.md b/presentation/components/CHANGELOG.md index 140b5b3..6972bb3 100644 --- a/presentation/components/CHANGELOG.md +++ b/presentation/components/CHANGELOG.md @@ -1,6 +1,31 @@ # Change Log - @bentley/presentation-components -This log was last generated on Tue, 15 Jan 2019 15:18:59 GMT and should not be manually modified. +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- Changes package.json to include api-extractor and adds api-extractor.json +- Use new buildIModelJsBuild script +- Exported ContentBuilder and ContentDataProvider +- Remove uneeded typedoc plugin depedency +- Expose presentation-specific content request methods through IContentDataProvider so they're available for provider consumers +- Save BUILD_SEMVER to globally accessible map +- Change `DataProvidersFactory.createSimilarInstancesTableDataProvider` to return data provider that also has a description +- Add DataProvidersFactory API for creating presentation data providers targeted towards specific use cases +- (breaking) Change PresentationTableDataProvider's constructor to accept a props object instead of multiple arguments +- Make all content data providers IDisposable. **Important:** providers must be disposed after use. +- Changed the way `0` selection level is handled in unified selection tables. Previously we used to reload table data when selection changed with level below boundary __or level `0`__. Now the __underlined__ part is removed and we only reload data if selection changes with level below boundary (set through props). +- RPC Interface changes to optimize getting first page of nodes/content +- Move property definitions to imodeljs-frontend so they could be used by tools to define properties for tool settings. +- upgrade to TypeScript 3.2.2 + +## 0.188.0 +Wed, 16 Jan 2019 16:36:09 GMT + +*Version update only* ## 0.187.0 Tue, 15 Jan 2019 15:18:59 GMT diff --git a/presentation/components/package.json b/presentation/components/package.json index 91a7597..9f65659 100644 --- a/presentation/components/package.json +++ b/presentation/components/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/presentation-components", - "version": "0.187.0", + "version": "0.189.0", "description": "React components based on iModel.js Presentation library", "license": "MIT", "repository": { @@ -23,23 +23,44 @@ "main": "lib/presentation-components.js", "typings": "lib/presentation-components", "scripts": { - "build": "tsc -b ./src/test 1>&2 && npm run build:assets && npm run build:scss && npm run extract && npm run webpackModule-dev", - "build:scss": "cpx \"./src/**/*.scss\" ./lib", - "build:assets": "cpx \"./public/**/*\" ./lib/public && npm run pseudolocalize", - "build:watch": "npm run build:assets && npm run build:scss && tsc -b ./src/test -w", + "build": "npm run extract && node ./node_modules/@bentley/webpack-tools/bin/buildIModelJsModule.js", "clean": "rimraf lib package-deps.json", "cover": "nyc npm test", "docs": "npm run docs:reference && npm run docs:changelog", "docs:changelog": "cpx \"./CHANGELOG.md\" ../../generated-docs/presentation/presentation-components", "docs:reference": "node ./node_modules/@bentley/build-tools/scripts/docs.js --source=./src --includes=../../generated-docs/extract --json=../../generated-docs/presentation/presentation-components/json/file.json --onlyJson %TYPEDOC_THEME%", "extract": "node ./node_modules/@bentley/build-tools/scripts/extract.js --fileExt=ts --extractFrom=./src/test --recursive --out=../../generated-docs/extract", + "extract-api": "node ./node_modules/@bentley/build-tools/scripts/extract-api.js --entry=presentation-components --isPresentation=true", "lint": "echo Disabled until https://github.com/palantir/tslint/issues/4148 is fixed", "//lint": "tslint -p ./src 1>&2", - "pseudolocalize": "node ./node_modules/@bentley/build-tools/scripts/pseudolocalize.js --englishDir ./public/locales/en --out ./lib/public/locales/en-pseudo", "test": "mocha --opts ../mocha.opts -r ignore-styles -r jsdom-global/register --file ./lib/test/index.test.js lib/test/**/*.js", - "test:watch": "npm test -- --reporter min --watch-extensions ts,tsx --watch", - "webpackModule-dev": "make-dir ./lib/module/dev/ && webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/presentation-components.js --env.bundlename=presentation-components --env.stylesheets --json >./lib/module/dev/webpackStats.json", - "webpackModule-prod": "webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/presentation-components.js --env.bundlename=presentation-components --env.prod --env.stylesheets" + "test:watch": "npm test -- --reporter min --watch-extensions ts,tsx --watch" + }, + "iModelJs": { + "buildModule": { + "type": "system", + "tscOptions": "-b ./src/test", + "sourceResources": [ + { + "source": "./src/**/*.scss", + "dest": "./lib" + }, + { + "source": "./public/**/*", + "dest": "./lib/public" + } + ], + "webpack": { + "dest": "./lib/module", + "entry": "./lib/presentation-components.js", + "bundleName": "presentation-components", + "styleSheets": true + }, + "pseudoLocalize": { + "source": "./lib/public/locales/en", + "dest": "./lib/public/locales/en-pseudo" + } + } }, "dependencies": { "lodash": "^4.17.10", @@ -47,26 +68,26 @@ "react-dom": "^16.4.2" }, "peerDependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/imodeljs-i18n": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-frontend": "0.187.0", - "@bentley/presentation-common": "0.187.0", - "@bentley/presentation-frontend": "0.187.0", - "@bentley/ui-core": "0.187.0", - "@bentley/ui-components": "0.187.0" + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/imodeljs-i18n": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-frontend": "0.189.0", + "@bentley/presentation-common": "0.189.0", + "@bentley/presentation-frontend": "0.189.0", + "@bentley/ui-core": "0.189.0", + "@bentley/ui-components": "0.189.0" }, "devDependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/build-tools": "0.187.0", - "@bentley/imodeljs-i18n": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-frontend": "0.187.0", - "@bentley/presentation-common": "0.187.0", - "@bentley/presentation-frontend": "0.187.0", - "@bentley/ui-core": "0.187.0", - "@bentley/ui-components": "0.187.0", - "@bentley/webpack-tools": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/build-tools": "0.189.0", + "@bentley/imodeljs-i18n": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-frontend": "0.189.0", + "@bentley/presentation-common": "0.189.0", + "@bentley/presentation-frontend": "0.189.0", + "@bentley/ui-core": "0.189.0", + "@bentley/ui-components": "0.189.0", + "@bentley/webpack-tools": "0.189.0", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/chai-jest-snapshot": "^1.3.0", @@ -74,8 +95,8 @@ "@types/faker": "^4.1.0", "@types/lodash": "^4.14.0", "@types/mocha": "^5.2.5", - "@types/react": "^16.4.14", - "@types/react-dom": "16.0.7", + "@types/react": "16.7.22", + "@types/react-dom": "16.0.11", "@types/sinon": "^5.0.5", "@types/sinon-chai": "^3.2.0", "chai": "^4.1.2", @@ -88,20 +109,15 @@ "enzyme-to-json": "^3.3.4", "faker": "^4.1.0", "ignore-styles": "^5.0.1", - "make-dir-cli": "^1.0.0", "mocha": "^5.2.0", "nyc": "^13.0.1", "rimraf": "^2.6.2", "sinon": "^7.1.1", "sinon-chai": "^3.2.0", - "source-map-loader": "^0.2.3", "tslint": "^5.11.0", "typedoc": "^0.11.1", - "typedoc-plugin-external-module-name": "^1.1.1", "typemoq": "^2.1.0", - "typescript": "~3.1.0", - "webpack": "^4.20.2", - "webpack-cli": "^3.1.0", + "typescript": "~3.2.2", "xmlhttprequest": "^1.8.0" }, "nyc": { diff --git a/presentation/components/src/DataProvidersFactory.ts b/presentation/components/src/DataProvidersFactory.ts new file mode 100644 index 0000000..76ebaf6 --- /dev/null +++ b/presentation/components/src/DataProvidersFactory.ts @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module Core */ + +import { RulesetsFactory, Omit } from "@bentley/presentation-common"; +import { PropertyRecord } from "@bentley/imodeljs-frontend"; +import { IPresentationPropertyDataProvider } from "./propertygrid/DataProvider"; +import { + IPresentationTableDataProvider, PresentationTableDataProvider, + PresentationTableDataProviderProps, +} from "./table/DataProvider"; + +/** + * Data structure holding initialization properties for [[DataProvidersFactory]] + */ +export interface DataProvidersFactoryProps { + rulesetsFactory?: RulesetsFactory; +} + +/** + * A factory class than can be used to create presentation data providers targeted towards + * specific use cases. + */ +export class DataProvidersFactory { + private _rulesetsFactory: RulesetsFactory; + + /** Constructor. */ + public constructor(props?: DataProvidersFactoryProps) { + this._rulesetsFactory = props && props.rulesetsFactory ? props.rulesetsFactory : new RulesetsFactory(); + } + + /** + * Create a table data provider which returns instances of the same class and + * having the same property value as the provided property record. + * @param propertiesProvider A field identifying which property of the record we should use + * @param record A record whose similar instances should be found + * @param props Configuration properties for the created provider + */ + public async createSimilarInstancesTableDataProvider(propertiesProvider: IPresentationPropertyDataProvider, record: PropertyRecord, + props: Omit, + ): Promise { + const content = await propertiesProvider.getContent(); + if (!content || content.contentSet.length === 0) + throw new Error("Properties provider has no content. Where did record come from?"); + + const field = content.descriptor.getFieldByName(record.property.name, true); + if (!field) + throw new Error("Properties provider doesn't have a property with provided record. Where did record come from?"); + + const result = this._rulesetsFactory.createSimilarInstancesRuleset(field, content.contentSet[0]); + return new TableDataProviderWithDescription({ + ...props, + imodel: propertiesProvider.imodel, + ruleset: result.ruleset, + description: result.description, + }); + } +} + +class TableDataProviderWithDescription extends PresentationTableDataProvider { + public readonly description: string; + public constructor(props: (PresentationTableDataProviderProps & { description: string })) { + const { description, ...baseProps } = props; + super(baseProps); + this.description = description; + } +} diff --git a/presentation/components/src/common/ContentBuilder.ts b/presentation/components/src/common/ContentBuilder.ts index bd87077..21dafee 100644 --- a/presentation/components/src/common/ContentBuilder.ts +++ b/presentation/components/src/common/ContentBuilder.ts @@ -12,11 +12,12 @@ import { NestedContentValue, NestedContentField, TypeDescription, StructTypeDescription, ArrayTypeDescription, } from "@bentley/presentation-common"; +import { Omit } from "@bentley/ui-core"; import { PropertyRecord, PropertyValue, PropertyValueFormat as UiPropertyValueFormat, ArrayValue, StructValue, PrimitiveValue, PropertyDescription, PropertyEditorInfo, EnumerationChoicesInfo, -} from "@bentley/ui-components"; +} from "@bentley/imodeljs-frontend"; const createArrayValue = (propertyDescription: PropertyDescription, arrayDescription: ArrayTypeDescription, values: Value[], displayValues: DisplayValue[]): ArrayValue => { const records = new Array(); @@ -87,21 +88,24 @@ const createValue = (propertyDescription: PropertyDescription, typeDescription: return createPrimitiveValue(value, displayValue); }; -const createRecordDescription = (typeDescription: TypeDescription, displayValue: DisplayValue): string | undefined => { +const createRecordDescription = (typeDescription: TypeDescription, displayValue: Omit): string | undefined => { if (PropertyValueFormat.Array === typeDescription.valueFormat || PropertyValueFormat.Struct === typeDescription.valueFormat) return undefined; if (PropertyValueFormat.Primitive !== typeDescription.valueFormat || !isPrimitive(displayValue)) throw new PresentationError(PresentationStatus.InvalidArgument, "displayValue is of wrong type"); - return (undefined !== displayValue) ? displayValue.toString() : undefined; + return displayValue.toString(); }; const createRecord = (propertyDescription: PropertyDescription, typeDescription: TypeDescription, value: Value, displayValue: DisplayValue, isReadOnly: boolean, isMerged: boolean): PropertyRecord => { const valueObj = createValue(propertyDescription, typeDescription, isMerged, value, displayValue); const record = new PropertyRecord(valueObj, propertyDescription); - record.description = createRecordDescription(typeDescription, displayValue); - record.isMerged = isMerged; - record.isReadonly = isReadOnly; + if (displayValue) + record.description = createRecordDescription(typeDescription, displayValue); + if (isMerged) + record.isMerged = true; + if (isReadOnly) + record.isReadonly = true; return record; }; @@ -162,8 +166,10 @@ const createNestedContentRecord = (field: NestedContentField, item: Item, path?: } const record = new PropertyRecord(value, ContentBuilder.createPropertyDescription(field)); - record.isMerged = isMerged; - record.isReadonly = field.isReadonly || isMerged; + if (isMerged) + record.isMerged = true; + if (field.isReadonly || isMerged) + record.isReadonly = true; return record; }; diff --git a/presentation/components/src/common/ContentDataProvider.ts b/presentation/components/src/common/ContentDataProvider.ts index 751bb91..1517d30 100644 --- a/presentation/components/src/common/ContentDataProvider.ts +++ b/presentation/components/src/common/ContentDataProvider.ts @@ -9,9 +9,11 @@ import { IModelConnection } from "@bentley/imodeljs-frontend"; import { KeySet, PageOptions, SelectionInfo, ContentRequestOptions, Content, Descriptor, Field, + Ruleset, RegisteredRuleset, } from "@bentley/presentation-common"; import { Presentation } from "@bentley/presentation-frontend"; import { IPresentationDataProvider } from "./IPresentationDataProvider"; +import { IDisposable } from "@bentley/bentleyjs-core"; /** * Properties for invalidating content cache. @@ -58,43 +60,91 @@ namespace CacheInvalidationProps { /** * Interface for all presentation-driven content providers. */ -export interface IContentDataProvider extends IPresentationDataProvider { +export interface IContentDataProvider extends IPresentationDataProvider, IDisposable { /** Display type used to format content */ readonly displayType: string; /** Keys defining what to request content for */ keys: Readonly; /** Information about selection event that results in content change */ selectionInfo: Readonly | undefined; + + /** + * Get the content descriptor. + */ + getContentDescriptor: () => Promise | undefined>; + + /** + * Get the number of content records. + */ + getContentSetSize: () => Promise; + + /** + * Get the content. + * @param pageOptions Paging options. + */ + getContent: (pageOptions?: PageOptions) => Promise | undefined>; } /** * Base class for all presentation-driven content providers. */ -export abstract class ContentDataProvider implements IContentDataProvider { +export class ContentDataProvider implements IContentDataProvider { private _imodel: IModelConnection; private _rulesetId: string; private _displayType: string; private _keys: Readonly; private _selectionInfo?: Readonly; + private _registeredRuleset?: RegisteredRuleset; + private _isDisposed?: boolean; + private _pagingSize?: number; /** * Constructor. * @param imodel IModel to pull data from. - * @param rulesetId Id of the ruleset to use when requesting content. + * @param ruleset Id of the ruleset to use when requesting content or a ruleset itself. * @param displayType The content display type which this provider is going to * load data for. */ - constructor(imodel: IModelConnection, rulesetId: string, displayType: string) { - this._rulesetId = rulesetId; + constructor(imodel: IModelConnection, ruleset: string | Ruleset, displayType: string) { + this._rulesetId = (typeof ruleset === "string") ? ruleset : ruleset.id; this._displayType = displayType; this._imodel = imodel; this._keys = new KeySet(); this.invalidateCache(CacheInvalidationProps.full()); + if (typeof ruleset === "object") { + this.registerRuleset(ruleset); // tslint:disable-line: no-floating-promises + } + } + + public dispose() { + this._isDisposed = true; + this.disposeRegisteredRuleset(); + } + + private disposeRegisteredRuleset() { + if (!this._registeredRuleset) + return; + + this._registeredRuleset.dispose(); + this._registeredRuleset = undefined; + } + + private async registerRuleset(ruleset: Ruleset) { + this._registeredRuleset = await Presentation.presentation.rulesets().add(ruleset); + if (this._isDisposed) { + // ensure we don't keep a hanging registered ruleset if the data provider + // gets destroyed before the ruleset finishes registration + this.disposeRegisteredRuleset(); + } } /** Display type used to format content */ public get displayType(): string { return this._displayType; } + /** Paging options for obtaining content */ + public get pagingSize(): number | undefined { return this._pagingSize; } + public set pagingSize(value: number | undefined) { this._pagingSize = value; } + /** IModel to pull data from */ public get imodel(): IModelConnection { return this._imodel; } public set imodel(imodel: IModelConnection) { @@ -137,10 +187,8 @@ export abstract class ContentDataProvider implements IContentDataProvider { this.getDefaultContentDescriptor.cache.clear!(); if (props.descriptorConfiguration && this.getContentDescriptor) this.getContentDescriptor.cache.clear!(); - if (props.size && this.getContentSetSize) - this.getContentSetSize.cache.clear!(); - if (props.content && this.getContent) - this.getContent.cache.clear!(); + if ((props.content || props.size) && this._getContentAndSize) + this._getContentAndSize.cache.clear!(); } private createRequestOptions(): ContentRequestOptions { @@ -171,6 +219,12 @@ export abstract class ContentDataProvider implements IContentDataProvider { }); } + /** + * Called to check whether the content descriptor should be configured. + * Not configuring content descriptor saves a backend request. + */ + protected shouldConfigureContentDescriptor(): boolean { return true; } + /** Called to check whether the field should be excluded from the descriptor. */ protected shouldExcludeFromDescriptor(field: Field): boolean { return this.isFieldHidden(field); } @@ -186,7 +240,7 @@ export abstract class ContentDataProvider implements IContentDataProvider { /** * Get the content descriptor. */ - protected getContentDescriptor = _.memoize(async (): Promise | undefined> => { + public getContentDescriptor = _.memoize(async (): Promise | undefined> => { const descriptor = await this.getDefaultContentDescriptor(); if (!descriptor) return undefined; @@ -196,22 +250,43 @@ export abstract class ContentDataProvider implements IContentDataProvider { /** * Get the number of content records. */ - protected getContentSetSize = _.memoize(async (): Promise => { - const descriptor = await this.getContentDescriptor(); - if (!descriptor) - return 0; - return Presentation.presentation.getContentSetSize(this.createRequestOptions(), descriptor, this.keys); - }); + public async getContentSetSize(): Promise { + const paging = undefined !== this.pagingSize ? { start: 0, size: this.pagingSize } : undefined; + const contentAndSize = await this._getContentAndSize(paging); + if (undefined !== contentAndSize) + return contentAndSize.size!; + return 0; + } /** * Get the content. * @param pageOptions Paging options. */ - protected getContent = _.memoize(async (pageOptions?: PageOptions): Promise | undefined> => { - const descriptor = await this.getContentDescriptor(); - if (!descriptor) - return undefined; - return Presentation.presentation.getContent({ ...this.createRequestOptions(), paging: pageOptions }, descriptor, this.keys); + public async getContent(pageOptions?: PageOptions): Promise | undefined> { + const contentAndSize = await this._getContentAndSize(pageOptions); + if (undefined !== contentAndSize) + return contentAndSize.content; + return undefined; + } + + private _getContentAndSize = _.memoize(async (pageOptions?: PageOptions) => { + let descriptorOrDisplayType; + if (this.shouldConfigureContentDescriptor()) { + descriptorOrDisplayType = await this.getContentDescriptor(); + if (!descriptorOrDisplayType) + return undefined; + } else { + descriptorOrDisplayType = this.displayType; + } + + const requestSize = undefined !== pageOptions && 0 === pageOptions.start && undefined !== pageOptions.size; + const options = { ...this.createRequestOptions(), paging: pageOptions }; + if (requestSize) + return Presentation.presentation.getContentAndSize(options, descriptorOrDisplayType, this.keys); + + const responseContent: Content = await Presentation.presentation.getContent(options, descriptorOrDisplayType, this.keys); + const contentSize = undefined === pageOptions || undefined === pageOptions.size ? responseContent.contentSet.length : undefined; + return { content: responseContent, size: contentSize }; }, createKeyForPageOptions); } diff --git a/presentation/components/src/presentation-components.ts b/presentation/components/src/presentation-components.ts index abfa7a6..ab00614 100644 --- a/presentation/components/src/presentation-components.ts +++ b/presentation/components/src/presentation-components.ts @@ -4,6 +4,11 @@ *--------------------------------------------------------------------------------------------*/ /** @module Components */ +/** @module Common */ +export { ContentBuilder } from "./common/ContentBuilder"; +export { ContentDataProvider } from "./common/ContentDataProvider"; +export * from "./DataProvidersFactory"; + /** @module PropertyGrid */ export { IPresentationPropertyDataProvider, PresentationPropertyDataProvider } from "./propertygrid/DataProvider"; export { propertyGridWithUnifiedSelection } from "./propertygrid/WithUnifiedSelection"; @@ -20,3 +25,12 @@ export { treeWithFilteringSupport } from "./tree/WithFilteringSupport"; /** @module Viewport */ export { viewWithUnifiedSelection } from "./viewport/WithUnifiedSelection"; + +// Set the version number so it can be found at runtime. BUILD_SEMVER is replaced at build time by the webpack DefinePlugin. +declare var BUILD_SEMVER: string; +/* istanbul ignore next */ +if ((typeof (BUILD_SEMVER) !== "undefined") && (typeof window !== "undefined") && window) { + if (!(window as any).iModelJsVersions) + (window as any).iModelJsVersions = new Map(); + (window as any).iModelJsVersions.set("presentation-components", BUILD_SEMVER); +} diff --git a/presentation/components/src/propertygrid/DataProvider.ts b/presentation/components/src/propertygrid/DataProvider.ts index e8bedbf..0c30ea6 100644 --- a/presentation/components/src/propertygrid/DataProvider.ts +++ b/presentation/components/src/propertygrid/DataProvider.ts @@ -6,10 +6,9 @@ import * as _ from "lodash"; import { - PropertyRecord, PropertyValueFormat, PropertyValue, PropertyData, PropertyDataChangeEvent, PropertyCategory, IPropertyDataProvider, } from "@bentley/ui-components"; -import { IModelConnection } from "@bentley/imodeljs-frontend"; +import { IModelConnection, PropertyRecord, PropertyValueFormat, PropertyValue } from "@bentley/imodeljs-frontend"; import { CategoryDescription, Descriptor, ContentFlags, Field, NestedContentField, DefaultContentDisplayTypes, Item, diff --git a/presentation/components/src/table/DataProvider.ts b/presentation/components/src/table/DataProvider.ts index 201d3c0..69628d6 100644 --- a/presentation/components/src/table/DataProvider.ts +++ b/presentation/components/src/table/DataProvider.ts @@ -14,7 +14,7 @@ import { IModelConnection } from "@bentley/imodeljs-frontend"; import { PresentationError, PresentationStatus, DefaultContentDisplayTypes, Descriptor, SortDirection, - Content, Field, PropertyValueFormat, Item, + Content, Field, PropertyValueFormat, Item, Ruleset, } from "@bentley/presentation-common"; import { ContentDataProvider, CacheInvalidationProps, IContentDataProvider } from "../common/ContentDataProvider"; import { ContentBuilder } from "../common/ContentBuilder"; @@ -25,11 +25,37 @@ interface PromisedPage extends Page { promise?: Promise; } +/** The default number of rows in a single page requested by [[PresentationTableDataProvider]] */ +export const TABLE_DATA_PROVIDER_DEFAULT_PAGE_SIZE = 20; + +/** The default number of pages cached by [[PresentationTableDataProvider]] */ +export const TABLE_DATA_PROVIDER_DEFAULT_CACHED_PAGES_COUNT = 5; + /** * Interface for presentation rules-driven property data provider. */ export type IPresentationTableDataProvider = ITableDataProvider & IContentDataProvider; +/** + * Initialization properties for [[PresentationTableDataProvider]] + */ +export interface PresentationTableDataProviderProps { + /** IModel to pull data from */ + imodel: IModelConnection; + + /** Ruleset or it's ID to be used for creating the content */ + ruleset: string | Ruleset; + + /** Number of rows in a single page requested from the backend. Defaults to [[TABLE_DATA_PROVIDER_DEFAULT_PAGE_SIZE]] */ + pageSize?: number; + + /** Number of pages cached in the data provider. Defaults to [[TABLE_DATA_PROVIDER_DEFAULT_CACHED_PAGES_COUNT]] */ + cachedPagesCount?: number; + + /** Display type to use when requesting data from the backend. Defaults to [[DefaultContentDisplayTypes.GRID]] */ + displayType?: string; +} + /** * Presentation Rules-driven table data provider. */ @@ -42,11 +68,12 @@ export class PresentationTableDataProvider extends ContentDataProvider implement public onRowsChanged = new TableDataChangeEvent(); /** Constructor. */ - constructor(imodel: IModelConnection, rulesetId: string, pageSize: number = 20, cachedPagesCount: number = 5) { - super(imodel, rulesetId, DefaultContentDisplayTypes.GRID); - this._pages = new PageContainer(pageSize, cachedPagesCount); + constructor(props: PresentationTableDataProviderProps) { + super(props.imodel, props.ruleset, props.displayType || DefaultContentDisplayTypes.GRID); + this._pages = new PageContainer(props.pageSize || TABLE_DATA_PROVIDER_DEFAULT_PAGE_SIZE, + props.cachedPagesCount || TABLE_DATA_PROVIDER_DEFAULT_CACHED_PAGES_COUNT); + this.pagingSize = props.pageSize || TABLE_DATA_PROVIDER_DEFAULT_PAGE_SIZE; } - /** * `ECExpression` for filtering data in the table. */ diff --git a/presentation/components/src/table/WithUnifiedSelection.tsx b/presentation/components/src/table/WithUnifiedSelection.tsx index 642861f..f7ba088 100644 --- a/presentation/components/src/table/WithUnifiedSelection.tsx +++ b/presentation/components/src/table/WithUnifiedSelection.tsx @@ -22,7 +22,19 @@ export interface Props { /** * Boundary level of selection used by the table. The table requests * data for selection changes whose level is less than `level` and changes - * selection at this `level`. Defaults to `1`. + * selection at this `level`. + * + * Examples: + * - `selectionLevel = 0` + * - selection change happens at level `0` - selected rows are adjusted based on new selection at level `0`. + * - selection change happens at level `1` or higher - nothing happens. + * - `selectionLevel = 1` + * - selection change happens at level `0` - `dataProvider.keys` is set to current selection. This + * reloads the data in the table. + * - selection change happens at level `1` - selected rows are adjusted based on new selection at level `1`. + * - selection change happens at level `2` or higher - nothing happens. + * + * Defaults to `1`. */ selectionLevel?: number; @@ -50,7 +62,7 @@ export function tableWithUnifiedSelection

(TableComponent: constructor(props: CombinedProps) { super(props); this._base = React.createRef(); - this._boundarySelectionLevel = getSelectionLevelFromProps(props); + this._boundarySelectionLevel = getBoundarySelectionLevelFromProps(props); } /** Returns the display name of this component */ @@ -82,7 +94,7 @@ export function tableWithUnifiedSelection

(TableComponent: } public componentDidUpdate() { - this._boundarySelectionLevel = getSelectionLevelFromProps(this.props); + this._boundarySelectionLevel = getBoundarySelectionLevelFromProps(this.props); if (this._selectionHandler) { this._selectionHandler.imodel = this.props.dataProvider.imodel; this._selectionHandler.rulesetId = this.props.dataProvider.rulesetId; @@ -126,7 +138,7 @@ export function tableWithUnifiedSelection

(TableComponent: if (undefined === selectionLevel) return; - if (selectionLevel < this._boundarySelectionLevel || selectionLevel === 0) { + if (selectionLevel < this._boundarySelectionLevel) { // we get here when table should react to selection change by reloading the data // based on the new selection this.loadDataForSelection(selectionLevel); @@ -218,6 +230,6 @@ export function tableWithUnifiedSelection

(TableComponent: let counter = 1; -function getSelectionLevelFromProps(props: Props): number { +function getBoundarySelectionLevelFromProps(props: Props): number { return (undefined !== props.selectionLevel) ? props.selectionLevel : 1; } diff --git a/presentation/components/src/test/DataProviderFactory.test.ts b/presentation/components/src/test/DataProviderFactory.test.ts new file mode 100644 index 0000000..271fc65 --- /dev/null +++ b/presentation/components/src/test/DataProviderFactory.test.ts @@ -0,0 +1,94 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/* tslint:disable:no-direct-imports */ + +import "@bentley/presentation-frontend/lib/test/_helpers/MockFrontendEnvironment"; +import { expect } from "chai"; +import * as moq from "@bentley/presentation-common/lib/test/_helpers/Mocks"; +import { createRandomContent, createRandomRuleset, createRandomDescriptor, createRandomPrimitiveField } from "@bentley/presentation-common/lib/test/_helpers/random"; +import { createRandomPropertyRecord } from "./_helpers/UiComponents"; +import { IPresentationPropertyDataProvider, PresentationTableDataProvider } from "../presentation-components"; +import { DataProvidersFactory, DataProvidersFactoryProps } from "../DataProvidersFactory"; +import { RulesetsFactory, Content, Item } from "@bentley/presentation-common"; +import { Presentation, PresentationManager } from "@bentley/presentation-frontend"; +import RulesetManager from "@bentley/presentation-frontend/lib/RulesetManager"; + +describe("DataProvidersFactory", () => { + + const presentationManagerMock = moq.Mock.ofType(); + const propertiesProvider = moq.Mock.ofType(); + let factory: DataProvidersFactory | undefined; + let props: DataProvidersFactoryProps | undefined; + + before(() => { + Presentation.presentation = presentationManagerMock.object; + }); + + beforeEach(() => { + props = undefined; + factory = undefined; + propertiesProvider.reset(); + presentationManagerMock.reset(); + presentationManagerMock.setup((x) => x.rulesets()).returns(() => moq.Mock.ofType().object); + }); + + const getFactory = (): DataProvidersFactory => { + if (!factory) + factory = new DataProvidersFactory(props); + return factory; + }; + + describe("createSimilarInstancesTableDataProvider", () => { + + it("throws when there's no content", async () => { + propertiesProvider.setup(async (x) => x.getContent()).returns(async () => undefined); + await expect(getFactory().createSimilarInstancesTableDataProvider(propertiesProvider.object, createRandomPropertyRecord(), {})).to.eventually.be.rejected; + }); + + it("throws when content has no records", async () => { + const content = createRandomContent(); + content.contentSet = []; + propertiesProvider.setup(async (x) => x.getContent()).returns(async () => content); + await expect(getFactory().createSimilarInstancesTableDataProvider(propertiesProvider.object, createRandomPropertyRecord(), {})).to.eventually.be.rejected; + }); + + it("throws when content descriptor has no field with property record's name", async () => { + const record = createRandomPropertyRecord(); + const content = createRandomContent(); + content.contentSet.push(new Item([], "", "", undefined, {}, {}, [])); + propertiesProvider.setup(async (x) => x.getContent()).returns(async () => content); + await expect(getFactory().createSimilarInstancesTableDataProvider(propertiesProvider.object, record, {})).to.eventually.be.rejected; + }); + + it("creates a provider with similar instances ruleset", async () => { + const ruleset = await createRandomRuleset(); + const description = "Test description"; + + const field = createRandomPrimitiveField(); + const descriptor = createRandomDescriptor(); + descriptor.fields.push(field); + const contentItem = new Item([], "", "", undefined, { [field.name]: "test value" }, { [field.name]: "test display value" }, []); + const content: Content = { + descriptor, + contentSet: [contentItem], + }; + propertiesProvider.setup(async (x) => x.getContent()).returns(async () => content); + + const record = createRandomPropertyRecord(); + record.property.name = field.name; + + const rulesetsFactoryMock = moq.Mock.ofType(); + props = { rulesetsFactory: rulesetsFactoryMock.object }; + rulesetsFactoryMock.setup((x) => x.createSimilarInstancesRuleset(field, contentItem)).returns(() => ({ ruleset, description })); + + const dataProvider = await getFactory().createSimilarInstancesTableDataProvider(propertiesProvider.object, record, {}); + expect(dataProvider).to.be.instanceOf(PresentationTableDataProvider); + expect(dataProvider.rulesetId).to.eq(ruleset.id); + expect(dataProvider.description).to.eq(description); + }); + + }); + +}); diff --git a/presentation/components/src/test/_helpers/UiComponents.ts b/presentation/components/src/test/_helpers/UiComponents.ts index 4377a84..c49ce3c 100644 --- a/presentation/components/src/test/_helpers/UiComponents.ts +++ b/presentation/components/src/test/_helpers/UiComponents.ts @@ -7,6 +7,7 @@ import * as faker from "faker"; import { createRandomECInstanceNodeKey } from "@bentley/presentation-common/lib/test/_helpers/random"; import { DelayLoadedTreeNodeItem } from "@bentley/ui-components"; +import { PropertyRecord, PrimitiveValue, PropertyDescription, PropertyValueFormat } from "@bentley/imodeljs-frontend"; import { NodeKey } from "@bentley/presentation-common"; export const createRandomTreeNodeItem = (key?: NodeKey, parentId?: string): DelayLoadedTreeNodeItem => { @@ -21,3 +22,17 @@ export const createRandomTreeNodeItem = (key?: NodeKey, parentId?: string): Dela }, }; }; + +export const createRandomPropertyRecord = (): PropertyRecord => { + const value: PrimitiveValue = { + valueFormat: PropertyValueFormat.Primitive, + value: faker.random.word(), + displayValue: faker.random.words(), + }; + const descr: PropertyDescription = { + typename: "string", + name: faker.random.word(), + displayLabel: faker.random.word(), + }; + return new PropertyRecord(value, descr); +}; diff --git a/presentation/components/src/test/common/ContentBuilder.test.snap b/presentation/components/src/test/common/ContentBuilder.test.snap index c250c5e..3d1be80 100644 --- a/presentation/components/src/test/common/ContentBuilder.test.snap +++ b/presentation/components/src/test/common/ContentBuilder.test.snap @@ -48,7 +48,6 @@ Object { exports[`ContentBuilder createPropertyRecord creates record with array value 1`] = ` PropertyRecord { "description": undefined, - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "SAS", @@ -59,7 +58,6 @@ PropertyRecord { "items": Array [ PropertyRecord { "description": "some display value 1", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "SAS", @@ -74,7 +72,6 @@ PropertyRecord { }, PropertyRecord { "description": "some display value 2", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "SAS", @@ -114,7 +111,6 @@ PropertyRecord { exports[`ContentBuilder createPropertyRecord creates record with merged inside nested content value 1`] = ` PropertyRecord { - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "payment Unbranded revolutionary", @@ -203,8 +199,6 @@ PropertyRecord { exports[`ContentBuilder createPropertyRecord creates record with multiple nested content values 1`] = ` PropertyRecord { - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Tasty", "name": "knowledge user", @@ -224,7 +218,6 @@ PropertyRecord { "members": Object { "world-class": PropertyRecord { "description": "some display value 1", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "green payment lavender", @@ -257,7 +250,6 @@ PropertyRecord { "members": Object { "world-class": PropertyRecord { "description": "some display value 2", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "green payment lavender", @@ -287,7 +279,6 @@ PropertyRecord { exports[`ContentBuilder createPropertyRecord creates record with only properties in field path 1`] = ` PropertyRecord { - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "backing up orchestration", @@ -297,7 +288,6 @@ PropertyRecord { "value": Object { "members": Object { "Future-proofed": PropertyRecord { - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Dynamic Plastic Estates", @@ -318,8 +308,6 @@ PropertyRecord { "members": Object { "Jamaican Dollar": PropertyRecord { "description": "some display value 1.2", - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "multi-byte Research synthesize", "editor": Object { @@ -351,8 +339,6 @@ PropertyRecord { "members": Object { "Jamaican Dollar": PropertyRecord { "description": "some display value 2.2", - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "multi-byte Research synthesize", "editor": Object { @@ -386,7 +372,6 @@ PropertyRecord { exports[`ContentBuilder createPropertyRecord creates record with primitive value 1`] = ` PropertyRecord { "description": "some display value", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "portals Bond Markets Units European Composite Unit (EURCO) action-items", @@ -403,7 +388,6 @@ PropertyRecord { exports[`ContentBuilder createPropertyRecord creates record with single nested content value 1`] = ` PropertyRecord { - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Branding Cambridgeshire", @@ -414,7 +398,6 @@ PropertyRecord { "members": Object { "functionalities": PropertyRecord { "description": "some display value", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "parse Kansas Ergonomic Granite Tuna", @@ -436,7 +419,6 @@ PropertyRecord { exports[`ContentBuilder createPropertyRecord creates record with struct value 1`] = ` PropertyRecord { "description": undefined, - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "wireless Fantastic Wooden Chips killer", @@ -447,7 +429,6 @@ PropertyRecord { "members": Object { "multi-state": PropertyRecord { "description": "some display value", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Vermont payment", @@ -468,9 +449,6 @@ PropertyRecord { exports[`ContentBuilder createPropertyRecord creates record with undefined array value 1`] = ` PropertyRecord { - "description": undefined, - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "backing up driver e-markets", "name": "optimize", @@ -486,8 +464,6 @@ PropertyRecord { exports[`ContentBuilder createPropertyRecord creates record with undefined primitive value 1`] = ` PropertyRecord { - "description": undefined, - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "discrete Fundamental non-volatile", @@ -504,8 +480,6 @@ PropertyRecord { exports[`ContentBuilder createPropertyRecord creates record with undefined struct value 1`] = ` PropertyRecord { - "description": undefined, - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Tools", diff --git a/presentation/components/src/test/common/ContentBuilder.test.ts b/presentation/components/src/test/common/ContentBuilder.test.ts index 14a020c..c7e9164 100644 --- a/presentation/components/src/test/common/ContentBuilder.test.ts +++ b/presentation/components/src/test/common/ContentBuilder.test.ts @@ -16,7 +16,7 @@ import { ArrayTypeDescription, StructTypeDescription, NestedContentField, NestedContentValue, } from "@bentley/presentation-common"; import { ContentBuilder } from "../../common/ContentBuilder"; -import { PrimitiveValue } from "@bentley/ui-components"; +import { PrimitiveValue } from "@bentley/imodeljs-frontend"; describe("ContentBuilder", () => { diff --git a/presentation/components/src/test/common/ContentDataProvider.test.ts b/presentation/components/src/test/common/ContentDataProvider.test.ts index ad4536d..8fabe97 100644 --- a/presentation/components/src/test/common/ContentDataProvider.test.ts +++ b/presentation/components/src/test/common/ContentDataProvider.test.ts @@ -9,24 +9,28 @@ import { expect } from "chai"; import * as sinon from "sinon"; import * as faker from "faker"; import * as moq from "@bentley/presentation-common/lib/test/_helpers/Mocks"; -import { PromiseContainer } from "@bentley/presentation-common/lib/test/_helpers/Promises"; -import { createRandomDescriptor } from "@bentley/presentation-common/lib/test/_helpers/random"; +import { PromiseContainer, ResolvablePromise } from "@bentley/presentation-common/lib/test/_helpers/Promises"; +import { createRandomDescriptor, createRandomRuleset, createRandomContent } from "@bentley/presentation-common/lib/test/_helpers/random"; import { IModelConnection } from "@bentley/imodeljs-frontend"; import { - Descriptor, Field, Content, + Descriptor, Field, SelectionInfo, Item, - KeySet, PageOptions, + KeySet, Ruleset, RegisteredRuleset, + ContentResponse, + Content, } from "@bentley/presentation-common"; import { Presentation, PresentationManager } from "@bentley/presentation-frontend"; import { ContentDataProvider, CacheInvalidationProps } from "../../common/ContentDataProvider"; +import RulesetManager from "@bentley/presentation-frontend/lib/RulesetManager"; /** * The Provider class is used to make protected ContentDataProvider * function public so the tests can call and spy on them. */ class Provider extends ContentDataProvider { - constructor(imodel: IModelConnection, rulesetId: string, displayType: string) { - super(imodel, rulesetId, displayType); + public overrideShouldConfigureContentDescriptor: boolean = true; + constructor(imodel: IModelConnection, ruleset: string | Ruleset, displayType: string) { + super(imodel, ruleset, displayType); } public invalidateCache(props: CacheInvalidationProps): void { super.invalidateCache(props); @@ -37,19 +41,18 @@ class Provider extends ContentDataProvider { public shouldExcludeFromDescriptor(field: Field): boolean { return super.shouldExcludeFromDescriptor(field); } + public shouldConfigureContentDescriptor(): boolean { + return this.overrideShouldConfigureContentDescriptor; + } public isFieldHidden(field: Field): boolean { return super.isFieldHidden(field); } - public publicGetContentDescriptor: (() => Promise | undefined>) & _.MemoizedFunction = this.getContentDescriptor; - public publicGetContentSetSize: (() => Promise) & _.MemoizedFunction = this.getContentSetSize; - public publicGetContent: ((pageOptions?: PageOptions) => Promise | undefined>) & _.MemoizedFunction = this.getContent; } interface MemoizedCacheSpies { defaultDescriptor: any; descriptor: any; - size: any; - content: any; + sizeAndContent: any; } describe("ContentDataProvider", () => { @@ -70,6 +73,9 @@ describe("ContentDataProvider", () => { provider = new Provider(imodelMock.object, rulesetId, displayType); resetMemoizedCacheSpies(); }); + afterEach(() => { + provider.dispose(); + }); const verifyMemoizedCachesCleared = (expectCleared: boolean = true) => { Object.values(memoizedCacheSpies).forEach((s) => { @@ -83,18 +89,65 @@ describe("ContentDataProvider", () => { sinon.restore(); memoizedCacheSpies = { defaultDescriptor: sinon.spy((provider as any).getDefaultContentDescriptor.cache, "clear"), - descriptor: sinon.spy(provider.publicGetContentDescriptor.cache, "clear"), - size: sinon.spy(provider.publicGetContentSetSize.cache, "clear"), - content: sinon.spy(provider.publicGetContent.cache, "clear"), + descriptor: sinon.spy(provider.getContentDescriptor.cache, "clear"), + sizeAndContent: sinon.spy((provider as any)._getContentAndSize.cache, "clear"), }; }; describe("constructor", () => { + it("sets display type", () => { const type = faker.random.word(); const p = new Provider(imodelMock.object, rulesetId, type); expect(p.displayType).to.eq(type); }); + + it("registers ruleset", async () => { + const rulesetsManagerMock = moq.Mock.ofType(); + rulesetsManagerMock.setup(async (x) => x.add(moq.It.isAny())).returns(async (r) => new RegisteredRuleset(r, "test", () => { })); + presentationManagerMock.setup((x) => x.rulesets()).returns(() => rulesetsManagerMock.object); + + const ruleset = await createRandomRuleset(); + const p = new Provider(imodelMock.object, ruleset, displayType); + expect(p.rulesetId).to.eq(ruleset.id); + rulesetsManagerMock.verify(async (x) => x.add(ruleset), moq.Times.once()); + }); + + it("disposes registered ruleset after provided is disposed before registration completes", async () => { + const registerPromise = new ResolvablePromise(); + const rulesetsManagerMock = moq.Mock.ofType(); + rulesetsManagerMock.setup(async (x) => x.add(moq.It.isAny())).returns(async () => registerPromise); + presentationManagerMock.setup((x) => x.rulesets()).returns(() => rulesetsManagerMock.object); + + const ruleset = await createRandomRuleset(); + const p = new Provider(imodelMock.object, ruleset, displayType); + p.dispose(); + + const rulesetDisposeSpy = sinon.spy(); + await registerPromise.resolve(new RegisteredRuleset(ruleset, "test", rulesetDisposeSpy)) + expect(rulesetDisposeSpy).to.be.calledOnce; + }); + + }); + + describe("dispose", () => { + + it("disposes registered ruleset", async () => { + const registerPromise = new ResolvablePromise(); + const rulesetsManagerMock = moq.Mock.ofType(); + rulesetsManagerMock.setup(async (x) => x.add(moq.It.isAny())).returns(async () => registerPromise); + presentationManagerMock.setup((x) => x.rulesets()).returns(() => rulesetsManagerMock.object); + + const ruleset = await createRandomRuleset(); + const p = new Provider(imodelMock.object, ruleset, displayType); + const rulesetDisposeSpy = sinon.spy(); + await registerPromise.resolve(new RegisteredRuleset(ruleset, "test", rulesetDisposeSpy)) + + expect(rulesetDisposeSpy).to.not.be.called; + p.dispose(); + expect(rulesetDisposeSpy).to.be.calledOnce; + }); + }); describe("rulesetId", () => { @@ -192,12 +245,12 @@ describe("ContentDataProvider", () => { it("clears memoized content set size", () => { provider.invalidateCache({ size: true }); - expect(memoizedCacheSpies.size).to.be.called; + expect(memoizedCacheSpies.sizeAndContent).to.be.called; }); it("clears memoized content", () => { provider.invalidateCache({ content: true }); - expect(memoizedCacheSpies.content).to.be.called; + expect(memoizedCacheSpies.sizeAndContent).to.be.called; }); }); @@ -225,7 +278,7 @@ describe("ContentDataProvider", () => { .returns(async () => result) .verifiable(); provider.selectionInfo = selection; - const descriptor = await provider.publicGetContentDescriptor(); + const descriptor = await provider.getContentDescriptor(); presentationManagerMock.verifyAll(); expect(descriptor).to.not.eq(result); expect(descriptor).to.deep.eq(result); @@ -234,7 +287,7 @@ describe("ContentDataProvider", () => { it("handles undefined descriptor returned by presentation manager", async () => { presentationManagerMock.setup((x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) .returns(async () => undefined); - const descriptor = await provider.publicGetContentDescriptor(); + const descriptor = await provider.getContentDescriptor(); expect(descriptor).to.be.undefined; }); @@ -243,7 +296,7 @@ describe("ContentDataProvider", () => { const result = createRandomDescriptor(displayType); presentationManagerMock.setup((x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) .returns(async () => result); - await provider.publicGetContentDescriptor(); + await provider.getContentDescriptor(); expect(configureSpy).to.be.calledOnce; }); @@ -252,7 +305,7 @@ describe("ContentDataProvider", () => { presentationManagerMock.setup((x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) .returns(() => resultPromiseContainer.promise) .verifiable(moq.Times.once()); - const requests = [provider.publicGetContentDescriptor(), provider.publicGetContentDescriptor()]; + const requests = [provider.getContentDescriptor(), provider.getContentDescriptor()]; const result = createRandomDescriptor(); resultPromiseContainer.resolve(result); const descriptors = await Promise.all(requests); @@ -270,40 +323,97 @@ describe("ContentDataProvider", () => { .verifiable(); presentationManagerMock.setup((x) => x.getContentSetSize({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny())) .verifiable(moq.Times.never()); - const size = await provider.publicGetContentSetSize(); + const size = await provider.getContentSetSize(); presentationManagerMock.verifyAll(); expect(size).to.eq(0); }); it("requests presentation manager for size", async () => { - const result = faker.random.number(); + const result = new PromiseContainer(); presentationManagerMock.setup((x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) .returns(async () => createRandomDescriptor()) .verifiable(); - presentationManagerMock.setup((x) => x.getContentSetSize({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny())) - .returns(async () => result) + presentationManagerMock.setup((x) => x.getContentAndSize({ imodel: imodelMock.object, rulesetId, paging: { start: 0, size: 10 } }, moq.It.isAny(), moq.It.isAny())) + .returns(() => result.promise) .verifiable(); - const size = await provider.publicGetContentSetSize(); - expect(size).to.eq(result); + provider.pagingSize = 10; + const contentAndContentSize = { content: createRandomContent(), size: faker.random.number() }; + result.resolve(contentAndContentSize); + const size = await provider.getContentSetSize(); + expect(size).to.eq(contentAndContentSize.size); presentationManagerMock.verifyAll(); }); it("memoizes result", async () => { - const resultPromiseContainer = new PromiseContainer(); + const resultPromiseContainer = new PromiseContainer(); presentationManagerMock.setup((x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) .returns(async () => createRandomDescriptor()) .verifiable(); - presentationManagerMock.setup((x) => x.getContentSetSize({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny())) + presentationManagerMock.setup((x) => x.getContentAndSize({ imodel: imodelMock.object, rulesetId, paging: { start: 0, size: 10 } }, moq.It.isAny(), moq.It.isAny())) .returns(() => resultPromiseContainer.promise) .verifiable(moq.Times.once()); - const requests = [provider.publicGetContentSetSize(), provider.publicGetContentSetSize()]; - const result = faker.random.number(); + provider.pagingSize = 10; + const requests = [provider.getContentSetSize(), provider.getContentSetSize()]; + const result = { content: createRandomContent(), size: faker.random.number() }; resultPromiseContainer.resolve(result); const sizes = await Promise.all(requests); - sizes.forEach((size) => expect(size).to.eq(result)); + sizes.forEach((size) => expect(size).to.eq(result.size)); presentationManagerMock.verifyAll(); }); + it("requests size and first page when paging size is set", async () => { + const resultPromiseContainer = new PromiseContainer(); // TODO + const pagingSize = 20; + + presentationManagerMock.setup((x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) + .returns(async () => createRandomDescriptor()) + .verifiable(); + presentationManagerMock.setup((x) => x.getContentAndSize({ imodel: imodelMock.object, rulesetId, paging: { start: 0, size: pagingSize } }, moq.It.isAny(), moq.It.isAny())) + .returns(() => resultPromiseContainer.promise) + .verifiable(moq.Times.once()); + + provider.pagingSize = pagingSize; + const result = { content: createRandomContent(), size: faker.random.number() }; + resultPromiseContainer.resolve(result); + const size = await provider.getContentSetSize(); + expect(size).to.eq(result.size); + presentationManagerMock.verifyAll(); + }); + + it("returns content size equal to content set size when page options are undefined", async () => { + const descriptor = createRandomDescriptor(); + const content = { + descriptor, + contentSet: [new Item([], "1", "", undefined, {}, {}, [])], + }; + presentationManagerMock.setup((x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) + .returns(async () => descriptor) + .verifiable(); + presentationManagerMock.setup((x) => x.getContent({ imodel: imodelMock.object, rulesetId, paging: undefined }, moq.It.isAny(), moq.It.isAny())) + .returns(async () => content) + .verifiable(moq.Times.once()); + presentationManagerMock.setup((x) => x.getContentSetSize(moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) + .verifiable(moq.Times.never()); + const size = await provider.getContentSetSize(); + presentationManagerMock.verifyAll(); + expect(size).to.equal(content.contentSet.length); + }); + + it("requests content set size with display type when descriptor should not be configured", async () => { + const result = new PromiseContainer(); + presentationManagerMock.setup((x) => x.getContentDescriptor(moq.It.isAny(), moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) + .verifiable(moq.Times.never()); + presentationManagerMock.setup((x) => x.getContentAndSize({ imodel: imodelMock.object, rulesetId, paging: { start: 0, size: 10 } }, provider.displayType, moq.It.isAny())) + .returns(() => result.promise) + .verifiable(moq.Times.once()); + provider.pagingSize = 10; + provider.overrideShouldConfigureContentDescriptor = false; + const contentAndContentSize = { content: createRandomContent(), size: faker.random.number() }; + result.resolve(contentAndContentSize); + const size = await provider.getContentSetSize(); + expect(size).to.eq(contentAndContentSize.size); + presentationManagerMock.verifyAll(); + }); }); describe("getContent", () => { @@ -314,74 +424,105 @@ describe("ContentDataProvider", () => { .verifiable(); presentationManagerMock.setup((x) => x.getContent({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny())) .verifiable(moq.Times.never()); - const c = await provider.publicGetContent(); + const c = await provider.getContent(); presentationManagerMock.verifyAll(); expect(c).to.be.undefined; }); it("requests presentation manager for content", async () => { const descriptor = createRandomDescriptor(); - const result: Content = { - descriptor, - contentSet: [], + const result: ContentResponse = { + content: { + descriptor, + contentSet: [], + }, + size: 1, }; presentationManagerMock.setup((x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) .returns(async () => descriptor) .verifiable(); - presentationManagerMock.setup((x) => x.getContent({ imodel: imodelMock.object, rulesetId, paging: undefined }, moq.It.isAny(), moq.It.isAny())) + presentationManagerMock.setup((x) => x.getContentAndSize({ imodel: imodelMock.object, rulesetId, paging: { start: 0, size: 10 } }, moq.It.isAny(), moq.It.isAny())) .returns(async () => result) .verifiable(); - const c = await provider.publicGetContent(); + const c = await provider.getContent({ start: 0, size: 10 }); presentationManagerMock.verifyAll(); - expect(c).to.deep.eq(result); + expect(c).to.deep.eq(result.content); }); it("memoizes result", async () => { const descriptor = createRandomDescriptor(); - const resultPromiseContainers = [1, 2, 3].map(() => new PromiseContainer()); + const resultContentFirstPagePromise0 = new PromiseContainer(); + const resultContentFirstPagePromise1 = new PromiseContainer(); + const resultContentNonFirstPagePromise = new PromiseContainer(); presentationManagerMock.setup((x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) .returns(async () => descriptor) .verifiable(); presentationManagerMock.setup((x) => x.getContent({ imodel: imodelMock.object, rulesetId, paging: undefined }, moq.It.isAny(), moq.It.isAny())) - .returns(() => resultPromiseContainers[0].promise) + .returns(() => resultContentFirstPagePromise0.promise) .verifiable(moq.Times.once()); presentationManagerMock.setup((x) => x.getContent({ imodel: imodelMock.object, rulesetId, paging: { start: 0, size: 0 } }, moq.It.isAny(), moq.It.isAny())) .verifiable(moq.Times.never()); - presentationManagerMock.setup((x) => x.getContent({ imodel: imodelMock.object, rulesetId, paging: { start: 0, size: 1 } }, moq.It.isAny(), moq.It.isAny())) - .returns(() => resultPromiseContainers[1].promise) + presentationManagerMock.setup((x) => x.getContentAndSize({ imodel: imodelMock.object, rulesetId, paging: { start: 0, size: 1 } }, moq.It.isAny(), moq.It.isAny())) + .returns(() => resultContentFirstPagePromise1.promise) .verifiable(moq.Times.once()); presentationManagerMock.setup((x) => x.getContent({ imodel: imodelMock.object, rulesetId, paging: { start: 1, size: 0 } }, moq.It.isAny(), moq.It.isAny())) - .returns(() => resultPromiseContainers[2].promise) + .returns(() => resultContentNonFirstPagePromise.promise) .verifiable(moq.Times.once()); const requests = [ - provider.publicGetContent(undefined), - provider.publicGetContent({ start: 0, size: 0 }), - provider.publicGetContent({ start: 0, size: 1 }), - provider.publicGetContent({ start: 1, size: 0 }), + provider.getContent(undefined), + provider.getContent({ start: 0, size: 0 }), + provider.getContent({ start: 0, size: 1 }), + provider.getContent({ start: 1, size: 0 }), ]; - const results: Content[] = [{ - descriptor, - contentSet: [new Item([], "1", "", undefined, {}, {}, [])], + const results: ContentResponse[] = [{ + content: { + descriptor, + contentSet: [new Item([], "1", "", undefined, {}, {}, [])], + }, + size: 1, }, { - descriptor, - contentSet: [new Item([], "2", "", undefined, {}, {}, [])], + content: { + descriptor, + contentSet: [new Item([], "2", "", undefined, {}, {}, [])], + }, + size: 1, }, { - descriptor, - contentSet: [new Item([], "3", "", undefined, {}, {}, [])], + content: { + descriptor, + contentSet: [new Item([], "3", "", undefined, {}, {}, [])], + }, + size: 1, }]; - resultPromiseContainers.forEach((container, index) => container.resolve(results[index])); + resultContentFirstPagePromise0.resolve(results[0].content); + resultContentFirstPagePromise1.resolve(results[1]); + resultContentNonFirstPagePromise.resolve(results[2].content); const responses = await Promise.all(requests); expect(responses[1]) .to.deep.eq(responses[0], "responses[1] should eq responses[0]") - .to.deep.eq(results[0], "both responses[0] and responses[1] should eq results[0]"); - expect(responses[2]).to.deep.eq(results[1], "responses[2] should eq results[1]"); - expect(responses[3]).to.deep.eq(results[2], "responses[3] should eq results[2]"); + .to.deep.eq(results[0].content, "both responses[0] and responses[1] should eq results[0]"); + expect(responses[2]).to.deep.eq(results[1].content, "responses[2] should eq results[1]"); + expect(responses[3]).to.deep.eq(results[2].content, "responses[3] should eq results[2]"); presentationManagerMock.verifyAll(); }); + it("requests content with display type when descriptor should not be configured", async () => { + const result = new PromiseContainer(); + presentationManagerMock.setup((x) => x.getContentDescriptor(moq.It.isAny(), moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) + .verifiable(moq.Times.never()); + presentationManagerMock.setup((x) => x.getContentAndSize({ imodel: imodelMock.object, rulesetId, paging: { start: 0, size: 10 } }, provider.displayType, moq.It.isAny())) + .returns(() => result.promise) + .verifiable(moq.Times.once()); + provider.pagingSize = 10; + provider.overrideShouldConfigureContentDescriptor = false; + const contentAndContentSize = { content: createRandomContent(), size: faker.random.number() }; + result.resolve(contentAndContentSize); + const size = await provider.getContent({ start: 0, size: 10 }); + expect(size).to.eq(contentAndContentSize.content); + presentationManagerMock.verifyAll(); + }); }); }); diff --git a/presentation/components/src/test/propertygrid/DataProvider.test.snap b/presentation/components/src/test/propertygrid/DataProvider.test.snap index 95553bb..d51f571 100644 --- a/presentation/components/src/test/propertygrid/DataProvider.test.snap +++ b/presentation/components/src/test/propertygrid/DataProvider.test.snap @@ -14,9 +14,6 @@ Object { "records": Object { "Inverse": Array [ PropertyRecord { - "description": undefined, - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Manager quantifying", "editor": Object { @@ -56,8 +53,6 @@ Object { "records": Object { "Favorite": Array [ PropertyRecord { - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Afghani", "name": "International", @@ -76,8 +71,6 @@ Object { "value": Object { "members": Object { "Implementation": PropertyRecord { - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "wireless", "name": "Implementation", @@ -87,7 +80,6 @@ Object { "members": Object { "Iran": PropertyRecord { "description": "Tasty", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Garden Ball Towels", @@ -123,8 +115,6 @@ Object { "value": Object { "members": Object { "Implementation": PropertyRecord { - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "wireless", "name": "Implementation", @@ -134,7 +124,6 @@ Object { "members": Object { "Iran": PropertyRecord { "description": "Accounts envisioneer circuit", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Garden Ball Towels", @@ -168,7 +157,6 @@ Object { "functionalities": Array [ PropertyRecord { "description": "Indiana Mobility", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Movies Coordinator", @@ -182,8 +170,6 @@ Object { }, }, PropertyRecord { - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Afghani", "name": "International", @@ -202,8 +188,6 @@ Object { "value": Object { "members": Object { "Implementation": PropertyRecord { - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "wireless", "name": "Implementation", @@ -213,7 +197,6 @@ Object { "members": Object { "Iran": PropertyRecord { "description": "Tasty", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Garden Ball Towels", @@ -232,8 +215,6 @@ Object { }, "synthesize": PropertyRecord { "description": "Bedfordshire plum", - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "El Salvador Colon US Dollar", "name": "synthesize", @@ -251,8 +232,6 @@ Object { }, "black": PropertyRecord { "description": "Paradigm", - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Computer markets", "editor": Object { @@ -283,8 +262,6 @@ Object { "value": Object { "members": Object { "Implementation": PropertyRecord { - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "wireless", "name": "Implementation", @@ -294,7 +271,6 @@ Object { "members": Object { "Iran": PropertyRecord { "description": "Accounts envisioneer circuit", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Garden Ball Towels", @@ -313,8 +289,6 @@ Object { }, "synthesize": PropertyRecord { "description": "California", - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "El Salvador Colon US Dollar", "name": "synthesize", @@ -332,8 +306,6 @@ Object { }, "black": PropertyRecord { "description": "Beauty", - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Computer markets", "editor": Object { @@ -377,8 +349,6 @@ Object { "records": Object { "Adaptive": Array [ PropertyRecord { - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Lead Investment Account Re-contextualized", "name": "overriding", @@ -392,8 +362,6 @@ Object { }, PropertyRecord { "description": "Money Market Account innovate deposit", - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Chair Suriname", "editor": Object { @@ -428,7 +396,6 @@ Object { "records": Object { "lime": Array [ PropertyRecord { - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "1080p copying Persevering", @@ -449,7 +416,6 @@ Object { "members": Object { "National": PropertyRecord { "description": "Concrete Sausages Assurance", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Shoes AI", @@ -463,9 +429,6 @@ Object { }, }, "firewall": PropertyRecord { - "description": undefined, - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Paradigm", "editor": Object { @@ -497,7 +460,6 @@ Object { "members": Object { "National": PropertyRecord { "description": "Mauritius Rupee", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Shoes AI", @@ -511,9 +473,6 @@ Object { }, }, "firewall": PropertyRecord { - "description": undefined, - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Paradigm", "editor": Object { @@ -540,8 +499,6 @@ Object { }, PropertyRecord { "description": "synergies Internal scalable", - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Fantastic embrace functionalities", "name": "redundant", @@ -572,8 +529,6 @@ Object { "records": Object { "Garden": Array [ PropertyRecord { - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Thailand", "name": "Principal", @@ -593,7 +548,6 @@ Object { "members": Object { "Books": PropertyRecord { "description": "Fantastic Shirt", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Investment Account SCSI", @@ -611,9 +565,6 @@ Object { }, }, "circuit": PropertyRecord { - "description": undefined, - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Cambridgeshire Islands", "name": "circuit", @@ -641,7 +592,6 @@ Object { "members": Object { "Books": PropertyRecord { "description": "Manager calculating", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Investment Account SCSI", @@ -659,9 +609,6 @@ Object { }, }, "circuit": PropertyRecord { - "description": undefined, - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Cambridgeshire Islands", "name": "circuit", @@ -702,7 +649,6 @@ Object { "solid state": Array [ PropertyRecord { "description": "Singapore Dollar Plastic", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "XML Designer", @@ -720,8 +666,6 @@ Object { }, }, PropertyRecord { - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Missouri Montana ADP", "name": "Pound Sterling", @@ -731,8 +675,6 @@ Object { "members": Object { "Mountains": PropertyRecord { "description": "disintermediate withdrawal", - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Officer Forest CSS", "name": "Mountains", @@ -745,9 +687,6 @@ Object { }, }, "Yemen": PropertyRecord { - "description": undefined, - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Squares", "editor": Object { @@ -787,8 +726,6 @@ Object { "Computers": Array [ PropertyRecord { "description": "architecture Malagasy Ariary", - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Corner", "editor": Object { @@ -805,9 +742,6 @@ Object { }, }, PropertyRecord { - "description": undefined, - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Investor", "name": "Steel", @@ -839,8 +773,6 @@ Object { "ability": Array [ PropertyRecord { "description": "Switzerland", - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Home Loan Account", "name": "Tools", @@ -853,8 +785,6 @@ Object { }, }, PropertyRecord { - "description": undefined, - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Intelligent Soft", @@ -900,8 +830,6 @@ Object { "needs-based": Array [ PropertyRecord { "description": undefined, - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Singapore Dollar forecast utilize", "name": "Metrics", @@ -911,7 +839,6 @@ Object { "items": Array [ PropertyRecord { "description": "some display value 1", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Singapore Dollar forecast utilize", @@ -926,7 +853,6 @@ Object { }, PropertyRecord { "description": "some display value 2", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Singapore Dollar forecast utilize", @@ -964,8 +890,6 @@ Object { "back-end": Array [ PropertyRecord { "description": "Clothing", - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "Awesome approach", "editor": Object { @@ -1001,7 +925,6 @@ Object { "Identity": Array [ PropertyRecord { "description": undefined, - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "SQL Pants Nebraska", @@ -1012,7 +935,6 @@ Object { "members": Object { "JBOD": PropertyRecord { "description": "some display value", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Fantastic Concrete Chicken backing up", diff --git a/presentation/components/src/test/propertygrid/DataProvider.test.ts b/presentation/components/src/test/propertygrid/DataProvider.test.ts index dc74563..ac43e2c 100644 --- a/presentation/components/src/test/propertygrid/DataProvider.test.ts +++ b/presentation/components/src/test/propertygrid/DataProvider.test.ts @@ -15,8 +15,7 @@ import { createRandomECInstanceKey, createRandomECClassInfo, createRandomRelationshipPath, } from "@bentley/presentation-common/lib/test/_helpers/random"; import { I18N } from "@bentley/imodeljs-i18n"; -import { PropertyRecord } from "@bentley/ui-components"; -import { IModelConnection } from "@bentley/imodeljs-frontend"; +import { IModelConnection, PropertyRecord } from "@bentley/imodeljs-frontend"; import { ValuesDictionary, Descriptor, Field, CategoryDescription, Content, ContentFlags, Item, diff --git a/presentation/components/src/test/table/DataProvider.test.snap b/presentation/components/src/test/table/DataProvider.test.snap index c9b3fe4..81f4881 100644 --- a/presentation/components/src/test/table/DataProvider.test.snap +++ b/presentation/components/src/test/table/DataProvider.test.snap @@ -33,7 +33,6 @@ Object { "key": "deposit", "record": PropertyRecord { "description": "aggregate", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "multi-byte", @@ -51,7 +50,6 @@ Object { "key": "product", "record": PropertyRecord { "description": "Cayman Islands", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "functionalities Intelligent Tasty", @@ -73,8 +71,6 @@ Object { "key": "plum", "record": PropertyRecord { "description": "Mauritius Rupee", - "isMerged": false, - "isReadonly": false, "property": Object { "displayLabel": "bleeding-edge Auto Loan Account", "name": "plum", diff --git a/presentation/components/src/test/table/DataProvider.test.ts b/presentation/components/src/test/table/DataProvider.test.ts index a1d9c66..f0e8689 100644 --- a/presentation/components/src/test/table/DataProvider.test.ts +++ b/presentation/components/src/test/table/DataProvider.test.ts @@ -32,7 +32,7 @@ class Provider extends PresentationTableDataProvider { } interface MemoizedCacheSpies { - getColumns: any; + getColumns: sinon.SinonSpy; } describe("TableDataProvider", () => { @@ -48,15 +48,14 @@ describe("TableDataProvider", () => { }); beforeEach(() => { presentationManagerMock.reset(); - provider = new Provider(imodelMock.object, rulesetId); - resetMemoizedCacheSpies(); - }); - - const resetMemoizedCacheSpies = () => { - sinon.restore(); + provider = new Provider({ imodel: imodelMock.object, ruleset: rulesetId }); memoizedCacheSpies = { getColumns: sinon.spy(provider.getColumns.cache, "clear"), }; + }); + + const resetMemoizedCacheSpies = () => { + memoizedCacheSpies.getColumns.resetHistory(); }; const createEmptyContentItem = (): Item => { @@ -91,14 +90,16 @@ describe("TableDataProvider", () => { describe("invalidateCache", () => { - it("resets filtering, sorting, memoized columns and raises onColumnsChanged event when 'descriptor' flag is set", () => { + it("resets filtering, sorting, memoized columns and raises onColumnsChanged event when 'descriptor' flag is set", async () => { const onColumnsChangedSpy = sinon.spy(provider.onColumnsChanged, "raiseEvent"); - presentationManagerMock.setup((x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) + presentationManagerMock.setup(async (x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) .returns(async () => createRandomDescriptor()); provider.filterExpression = faker.random.words(); - provider.sort(0, SortDirection.Descending); + await provider.sort(0, SortDirection.Descending); + resetMemoizedCacheSpies(); + onColumnsChangedSpy.resetHistory(); provider.invalidateCache({ descriptor: true }); @@ -154,7 +155,7 @@ describe("TableDataProvider", () => { it("sets sorting properties", async () => { const source = createRandomDescriptor(); - presentationManagerMock.setup((x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) + presentationManagerMock.setup(async (x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) .returns(async () => source); await provider.sort(0, SortDirection.Descending); @@ -218,14 +219,14 @@ describe("TableDataProvider", () => { it("throws when trying to sort by invalid column", async () => { const source = createRandomDescriptor(); source.fields = []; - presentationManagerMock.setup((x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) + presentationManagerMock.setup(async (x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) .returns(async () => source); await expect(provider.sort(0, SortDirection.NoSort)).to.eventually.be.rejectedWith(PresentationError); }); it("invalidates descriptor configuration and content", async () => { const source = createRandomDescriptor(); - presentationManagerMock.setup((x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) + presentationManagerMock.setup(async (x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) .returns(async () => source); const invalidateCacheMock = moq.Mock.ofInstance(provider.invalidateCache); provider.invalidateCache = invalidateCacheMock.object; @@ -235,7 +236,7 @@ describe("TableDataProvider", () => { it("sets sorting properties", async () => { const source = createRandomDescriptor(); - presentationManagerMock.setup((x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) + presentationManagerMock.setup(async (x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) .returns(async () => source); await provider.sort(0, SortDirection.Descending); expect(provider.sortColumnKey).to.eq((await provider.getColumns())[0].key); @@ -264,7 +265,7 @@ describe("TableDataProvider", () => { it("returns valid sorting column", async () => { const source = createRandomDescriptor(); - presentationManagerMock.setup((x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) + presentationManagerMock.setup(async (x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, moq.It.isAny(), moq.It.isAny(), moq.It.isAny())) .returns(async () => source); await provider.sort(0, SortDirection.Descending); const sortingColumn = await provider.sortColumn; @@ -292,11 +293,11 @@ describe("TableDataProvider", () => { const descriptor = createRandomDescriptor(); const resultPromiseContainer = new PromiseContainer(); const getContentDescriptorMock = moq.Mock.ofInstance(() => (provider as any).getContentDescriptor); - getContentDescriptorMock.setup((x) => x()).returns(() => resultPromiseContainer.promise).verifiable(moq.Times.once()); + getContentDescriptorMock.setup((x) => x()).returns(async () => resultPromiseContainer.promise).verifiable(moq.Times.once()); getContentDescriptorMock.setup((x) => x()).verifiable(moq.Times.never()); (provider as any).getContentDescriptor = getContentDescriptorMock.object; - const requests = [1, 2].map(() => provider.getColumns()); + const requests = [1, 2].map(async () => provider.getColumns()); resultPromiseContainer.resolve(descriptor); const response = await Promise.all(requests); expect(response[0]).to.deep.eq(response[1], "both responses should be equal"); @@ -341,17 +342,22 @@ describe("TableDataProvider", () => { }); it("requests content in pages", async () => { - provider = new Provider(imodelMock.object, rulesetId, 2, 10); + provider = new Provider({ + imodel: imodelMock.object, + ruleset: rulesetId, + pageSize: 2, + cachedPagesCount: 10, + }); const contentResolver = [0, 1].map(() => new PromiseContainer()); const getContentMock = moq.Mock.ofInstance((provider as any).getContent); (provider as any).getContent = getContentMock.object; - getContentMock.setup((x) => x({ start: 0, size: 2 })).returns(() => contentResolver[0].promise).verifiable(moq.Times.once()); - getContentMock.setup((x) => x({ start: 2, size: 2 })).returns(() => contentResolver[1].promise).verifiable(moq.Times.once()); + getContentMock.setup((x) => x({ start: 0, size: 2 })).returns(async () => contentResolver[0].promise).verifiable(moq.Times.once()); + getContentMock.setup((x) => x({ start: 2, size: 2 })).returns(async () => contentResolver[1].promise).verifiable(moq.Times.once()); // request rows without await to make sure paging is handled properly (new // pages are not created while other pages for the same position are being loaded) - const requests = [0, 1, 2].map((index) => provider.getRow(index)); + const requests = [0, 1, 2].map(async (index) => provider.getRow(index)); contentResolver.forEach((resolver) => resolver.resolve(createContent(2))); const rows = await Promise.all(requests); rows.forEach((row) => expect(row).to.not.be.undefined); diff --git a/presentation/components/src/test/table/WithUnifiedSelection.test.tsx b/presentation/components/src/test/table/WithUnifiedSelection.test.tsx index b23af77..4f9a7d3 100644 --- a/presentation/components/src/test/table/WithUnifiedSelection.test.tsx +++ b/presentation/components/src/test/table/WithUnifiedSelection.test.tsx @@ -30,7 +30,7 @@ describe("Table withUnifiedSelection", () => { let testRulesetId: string; const imodelMock = moq.Mock.ofType(); - const dataProviderMock = moq.Mock.ofType(PresentationTableDataProvider); + const dataProviderMock = moq.Mock.ofType(PresentationTableDataProvider, undefined, undefined, imodelMock.object, "ruleset_id"); const selectionHandlerMock = moq.Mock.ofType(); before(() => { // https://github.com/Microsoft/TypeScript/issues/14151#issuecomment-280812617 @@ -58,11 +58,11 @@ describe("Table withUnifiedSelection", () => { rows = []; providerMock.reset(); providerMock.setup((x) => x.keys).returns(() => new KeySet()); - providerMock.setup((x) => x.getColumns()).returns(async () => columns!); + providerMock.setup(async (x) => x.getColumns()).returns(async () => columns!); providerMock.setup((x) => x.imodel).returns(() => imodel!); providerMock.setup((x) => x.rulesetId).returns(() => rulesetId!); - providerMock.setup((x) => x.getRowsCount()).returns(async () => rows!.length); - providerMock.setup((x) => x.getRow(moq.It.isAnyNumber())).returns(async (i: number) => rows![i]); + providerMock.setup(async (x) => x.getRowsCount()).returns(async () => rows!.length); + providerMock.setup(async (x) => x.getRow(moq.It.isAnyNumber())).returns(async (i: number) => rows![i]); providerMock.setup((x) => x.onColumnsChanged).returns(() => new TableDataChangeEvent()); providerMock.setup((x) => x.onRowsChanged).returns(() => new TableDataChangeEvent()); }; @@ -111,7 +111,7 @@ describe("Table withUnifiedSelection", () => { const presentationManagerMock = moq.Mock.ofType(); presentationManagerMock - .setup((x) => x.getContentDescriptor(moq.It.isAny(), moq.It.isAnyString(), moq.It.isAny(), moq.It.isAny())) + .setup(async (x) => x.getContentDescriptor(moq.It.isAny(), moq.It.isAnyString(), moq.It.isAny(), moq.It.isAny())) .returns(async () => undefined); Presentation.presentation = presentationManagerMock.object; @@ -301,7 +301,7 @@ describe("Table withUnifiedSelection", () => { const rows = [createRandomRowItem(), createRandomRowItem()]; const rowsIter = createAsyncIterator(rows); const callback = moq.Mock.ofType<(rowIterator: AsyncIterableIterator, replace: boolean) => Promise>(); - callback.setup((x) => x(rowsIter, true)).returns(async () => false).verifiable(); + callback.setup(async (x) => x(rowsIter, true)).returns(async () => false).verifiable(); const table = shallow( { const rows = [createRandomRowItem(), createRandomRowItem()]; const rowsIter = createAsyncIterator(rows); const callback = moq.Mock.ofType<(rowIterator: AsyncIterableIterator, replace: boolean) => Promise>(); - callback.setup((x) => x(rowsIter, false)).returns(async () => true).verifiable(); + callback.setup(async (x) => x(rowsIter, false)).returns(async () => true).verifiable(); const table = shallow( { const rows = [createRandomRowItem(), createRandomRowItem()]; const rowsIter = createAsyncIterator(rows); const callback = moq.Mock.ofType<(rowIterator: AsyncIterableIterator) => Promise>(); - callback.setup((x) => x(rowsIter)).returns(async () => true).verifiable(); + callback.setup(async (x) => x(rowsIter)).returns(async () => true).verifiable(); const table = shallow( { const rows = [createRandomRowItem(), createRandomRowItem()]; const rowsIter = createAsyncIterator(rows); const callback = moq.Mock.ofType<(rowIterator: AsyncIterableIterator) => Promise>(); - callback.setup((x) => x(rowsIter)).returns(async () => false).verifiable(); + callback.setup(async (x) => x(rowsIter)).returns(async () => false).verifiable(); const table = shallow( { dataProviderMock.verify((x) => x.keys = keys, moq.Times.once()); }); - it("sets data provider keys to overall selection on selection changes when selection level of table and event is 0", () => { - const keys = new KeySet([createRandomECInstanceKey(), createRandomECInstanceKey()]); - shallow(); - triggerSelectionChange(keys, 0); - dataProviderMock.verify((x) => x.keys = keys, moq.Times.once()); - }); - it("sets data provider keys to an empty KeySet on selection changes with lower selection level when overall selection is empty", () => { const keys = new KeySet(); shallow( { describe("root", () => { it("returns presentation manager result", async () => { - const result = faker.random.number(); + const resultNodes = [createRandomECInstanceNode(), createRandomECInstanceNode()]; presentationManagerMock - .setup((x) => x.getRootNodesCount({ imodel: imodelMock.object, rulesetId })) - .returns(async () => result) + .setup((x) => x.getNodes({ imodel: imodelMock.object, rulesetId, paging: undefined }, undefined)) + .returns(async () => resultNodes) .verifiable(); const actualResult = await provider.getNodesCount(); - expect(actualResult).to.eq(result); + expect(actualResult).to.eq(resultNodes.length); presentationManagerMock.verifyAll(); }); it("memoizes result", async () => { - const resultContainers = [new PromiseContainer(), new PromiseContainer()]; + const resultNodes = [createRandomECInstanceNode(), createRandomECInstanceNode()]; + const resultContainers = [new PromiseContainer(), new PromiseContainer()]; presentationManagerMock - .setup((x) => x.getRootNodesCount({ imodel: imodelMock.object, rulesetId })) + .setup((x) => x.getNodesAndCount({ imodel: imodelMock.object, rulesetId, paging: { start: 0, size: 10 } }, undefined)) .returns(() => resultContainers[0].promise); presentationManagerMock - .setup((x) => x.getRootNodesCount({ imodel: imodelMock.object, rulesetId })) + .setup((x) => x.getNodesAndCount({ imodel: imodelMock.object, rulesetId, paging: { start: 0, size: 10 } }, undefined)) .returns(() => resultContainers[1].promise); + provider.pagingSize = 10; const promises = [provider.getNodesCount(), provider.getNodesCount()]; - resultContainers.forEach((c: PromiseContainer, index: number) => c.resolve(index)); + resultContainers.forEach((c: PromiseContainer, index: number) => c.resolve({ nodes: resultNodes, count: index })); const results = await Promise.all(promises); expect(results[1]).to.eq(results[0]).to.eq(0); - presentationManagerMock.verify((x) => x.getRootNodesCount({ imodel: imodelMock.object, rulesetId }), moq.Times.once()); + presentationManagerMock.verify((x) => x.getNodesAndCount({ imodel: imodelMock.object, rulesetId, paging: { start: 0, size: 10 } }, undefined), moq.Times.once()); }); + it("requests count and first page when paging size is set", async () => { + const result = { nodes: [createRandomECInstanceNode(), createRandomECInstanceNode()], count: 2 + faker.random.number() }; + const pageSize = 20; + presentationManagerMock + .setup((x) => x.getNodesAndCount({ imodel: imodelMock.object, rulesetId, paging: pageOptionsUiToPresentation({ start: 0, size: pageSize }) }, undefined)) + .returns(async () => result) + .verifiable(); + provider.pagingSize = pageSize; + const actualResult = await provider.getNodesCount(); + expect(actualResult).to.eq(result.count); + presentationManagerMock.verifyAll(); + }); + + it("returns nodes count equal to requested nodes list count when page options are undefined", async () => { + const nodes = [createRandomECInstanceNode(), createRandomECInstanceNode(), createRandomECInstanceNode(), createRandomECInstanceNode()]; + presentationManagerMock.setup((x) => x.getNodes({ imodel: imodelMock.object, rulesetId, paging: undefined }, undefined)) + .returns(async () => nodes) + .verifiable(moq.Times.once()); + presentationManagerMock.setup((x) => x.getNodesCount(moq.It.isAny(), moq.It.isAny())) + .verifiable(moq.Times.never()); + const count = await provider.getNodesCount(); + presentationManagerMock.verifyAll(); + expect(count).to.equal(nodes.length); + }); }); describe("children", () => { it("returns presentation manager result", async () => { + const resultNodes = [createRandomECInstanceNode(), createRandomECInstanceNode()]; const parentKey = createRandomECInstanceNodeKey(); const parentNode = createRandomTreeNodeItem(parentKey); - const result = faker.random.number(); presentationManagerMock - .setup((x) => x.getChildrenCount({ imodel: imodelMock.object, rulesetId }, parentKey)) - .returns(async () => result) + .setup((x) => x.getNodes({ imodel: imodelMock.object, rulesetId, paging: undefined }, parentKey)) + .returns(async () => resultNodes) .verifiable(); const actualResult = await provider.getNodesCount(parentNode); - expect(actualResult).to.eq(result); + expect(actualResult).to.eq(resultNodes.length); presentationManagerMock.verifyAll(); }); it("memoizes result", async () => { const parentKeys = [createRandomECInstanceNodeKey(), createRandomECInstanceNodeKey()]; const parentNodes = parentKeys.map((key) => createRandomTreeNodeItem(key)); - const resultContainers = [new PromiseContainer(), new PromiseContainer()]; + const resultContainers = [new PromiseContainer(), new PromiseContainer()]; presentationManagerMock - .setup((x) => x.getChildrenCount({ imodel: imodelMock.object, rulesetId }, parentKeys[0])) + .setup((x) => x.getNodesAndCount({ imodel: imodelMock.object, rulesetId, paging: { start: 0, size: 10 } }, parentKeys[0])) .returns(() => resultContainers[0].promise) .verifiable(moq.Times.once()); presentationManagerMock - .setup((x) => x.getChildrenCount({ imodel: imodelMock.object, rulesetId }, parentKeys[1])) + .setup((x) => x.getNodesAndCount({ imodel: imodelMock.object, rulesetId, paging: { start: 0, size: 10 } }, parentKeys[1])) .returns(() => resultContainers[1].promise) .verifiable(moq.Times.once()); + provider.pagingSize = 10; const promises = [ provider.getNodesCount(parentNodes[0]), provider.getNodesCount(parentNodes[1]), provider.getNodesCount(parentNodes[0]), ]; - resultContainers.forEach((c: PromiseContainer, index: number) => c.resolve(index)); + resultContainers.forEach((c: PromiseContainer, index: number) => c.resolve({ nodes: [createRandomECInstanceNode(), createRandomECInstanceNode()], count: index })); const results = await Promise.all(promises); expect(results[0]).to.eq(results[2]).to.eq(0); expect(results[1]).to.eq(1); @@ -123,6 +152,34 @@ describe("TreeDataProvider", () => { presentationManagerMock.verifyAll(); }); + it("uses default page options", async () => { + const parentKey = createRandomECInstanceNodeKey(); + const parentNode = createRandomTreeNodeItem(parentKey); + const result = { nodes: [], count: faker.random.number() }; + presentationManagerMock + .setup((x) => x.getNodesAndCount({ imodel: imodelMock.object, rulesetId, paging: pageOptionsUiToPresentation({ start: 0, size: 20 }) }, parentKey)) + .returns(async () => result) + .verifiable(); + provider.pagingSize = 20; + const actualResult = await provider.getNodesCount(parentNode); + expect(actualResult).to.eq(result.count); + presentationManagerMock.verifyAll(); + }); + + it("returns nodes count equal to requested nodes list count when page options are undefined", async () => { + const parentKey = createRandomECInstanceNodeKey(); + const parentNode = createRandomTreeNodeItem(parentKey); + const nodes = [createRandomECInstanceNode(), createRandomECInstanceNode(), createRandomECInstanceNode(), createRandomECInstanceNode()]; + presentationManagerMock.setup((x) => x.getNodes({ imodel: imodelMock.object, rulesetId, paging: undefined }, parentKey)) + .returns(async () => nodes) + .verifiable(moq.Times.once()); + presentationManagerMock.setup((x) => x.getNodesCount(moq.It.isAny(), moq.It.isAny())) + .verifiable(moq.Times.never()); + const count = await provider.getNodesCount(parentNode); + presentationManagerMock.verifyAll(); + expect(count).to.equal(nodes.length); + }); + }); }); @@ -132,10 +189,10 @@ describe("TreeDataProvider", () => { describe("root", () => { it("returns presentation manager result", async () => { - const pageOptions: PageOptions = { start: faker.random.number(), size: faker.random.number() }; - const result = [createRandomECInstanceNode(), createRandomECInstanceNode()]; + const pageOptions: PageOptions = { start: 0, size: faker.random.number() }; + const result = { nodes: [createRandomECInstanceNode(), createRandomECInstanceNode()], count: 2 }; presentationManagerMock - .setup((x) => x.getRootNodes({ imodel: imodelMock.object, rulesetId, paging: pageOptionsUiToPresentation(pageOptions) })) + .setup((x) => x.getNodesAndCount({ imodel: imodelMock.object, rulesetId, paging: pageOptionsUiToPresentation(pageOptions) }, undefined)) .returns(async () => result) .verifiable(); const actualResult = await provider.getNodes(undefined, pageOptions); @@ -144,30 +201,34 @@ describe("TreeDataProvider", () => { }); it("memoizes result", async () => { - const resultContainers = [new PromiseContainer(), new PromiseContainer(), new PromiseContainer()]; + const resultnodesFirstPageContainer0 = new PromiseContainer>>(); + const resultnodesFirstPageContainer1 = new PromiseContainer(); + const resultNodesNonFirstPageContainer = new PromiseContainer>>(); presentationManagerMock - .setup((x) => x.getRootNodes({ imodel: imodelMock.object, rulesetId, paging: undefined })) - .returns(() => resultContainers[0].promise) + .setup((x) => x.getNodes({ imodel: imodelMock.object, rulesetId, paging: undefined }, undefined)) + .returns(() => resultnodesFirstPageContainer0.promise) .verifiable(moq.Times.once()); presentationManagerMock - .setup((x) => x.getRootNodes({ imodel: imodelMock.object, rulesetId, paging: { start: 0, size: 0 } })) + .setup((x) => x.getNodes({ imodel: imodelMock.object, rulesetId, paging: { start: 0, size: 0 } }, undefined)) .verifiable(moq.Times.never()); presentationManagerMock - .setup((x) => x.getRootNodes({ imodel: imodelMock.object, rulesetId, paging: { start: 1, size: 0 } })) - .returns(() => resultContainers[1].promise) + .setup((x) => x.getNodes({ imodel: imodelMock.object, rulesetId, paging: { start: 1, size: 0 } }, undefined)) + .returns(() => resultNodesNonFirstPageContainer.promise) .verifiable(moq.Times.once()); presentationManagerMock - .setup((x) => x.getRootNodes({ imodel: imodelMock.object, rulesetId, paging: { start: 0, size: 1 } })) - .returns(() => resultContainers[2].promise) + .setup((x) => x.getNodesAndCount({ imodel: imodelMock.object, rulesetId, paging: { start: 0, size: 1 } }, undefined)) + .returns(() => resultnodesFirstPageContainer1.promise) .verifiable(moq.Times.once()); const promises = [ - provider.getNodes(undefined, undefined), provider.getNodes(undefined, undefined), + provider.getNodes(), provider.getNodes(), provider.getNodes(undefined, { start: 0, size: 0 }), provider.getNodes(undefined, { start: 0, size: 0 }), provider.getNodes(undefined, { start: 1, size: 0 }), provider.getNodes(undefined, { start: 1, size: 0 }), provider.getNodes(undefined, { start: 0, size: 1 }), provider.getNodes(undefined, { start: 0, size: 1 }), ]; - resultContainers.forEach((c: PromiseContainer) => c.resolve([createRandomECInstanceNode()])); + resultnodesFirstPageContainer0.resolve([createRandomECInstanceNode()]); + resultnodesFirstPageContainer1.resolve({ nodes: [createRandomECInstanceNode()], count: 1 }); + resultNodesNonFirstPageContainer.resolve([createRandomECInstanceNode()]); const results = await Promise.all(promises); expect(results[0]).to.eq(results[1], "results[0] should eq results[1]"); @@ -179,7 +240,6 @@ describe("TreeDataProvider", () => { presentationManagerMock.verifyAll(); }); - }); describe("children", () => { @@ -187,10 +247,10 @@ describe("TreeDataProvider", () => { it("returns presentation manager result", async () => { const parentKey = createRandomECInstanceNodeKey(); const parentNode = createRandomTreeNodeItem(parentKey); - const pageOptions: PageOptions = { start: faker.random.number(), size: faker.random.number() }; + const pageOptions: PageOptions = { start: 0, size: faker.random.number() }; presentationManagerMock - .setup((x) => x.getChildren({ imodel: imodelMock.object, rulesetId, paging: pageOptionsUiToPresentation(pageOptions) }, parentKey)) - .returns(async () => [createRandomECInstanceNode(), createRandomECInstanceNode()]) + .setup((x) => x.getNodesAndCount({ imodel: imodelMock.object, rulesetId, paging: pageOptionsUiToPresentation(pageOptions) }, parentKey)) + .returns(async () => ({ nodes: [createRandomECInstanceNode(), createRandomECInstanceNode()], count: 2 })) .verifiable(); const actualResult = await provider.getNodes(parentNode, pageOptions); expect(actualResult).to.matchSnapshot(); @@ -200,31 +260,35 @@ describe("TreeDataProvider", () => { it("memoizes result", async () => { const parentKeys = [createRandomECInstanceNodeKey(), createRandomECInstanceNodeKey()]; const parentNodes = parentKeys.map((key) => createRandomTreeNodeItem(key)); - const resultContainers = [new PromiseContainer(), new PromiseContainer(), new PromiseContainer()]; + const resultnodesFirstPageContainer0 = new PromiseContainer>>(); + const resultnodesFirstPageContainer1 = new PromiseContainer(); + const resultNodesNonFirstPageContainer = new PromiseContainer>>(); presentationManagerMock - .setup((x) => x.getChildren({ imodel: imodelMock.object, rulesetId, paging: undefined }, parentKeys[0])) - .returns(() => resultContainers[0].promise) + .setup((x) => x.getNodes({ imodel: imodelMock.object, rulesetId, paging: undefined }, parentKeys[0])) + .returns(() => resultnodesFirstPageContainer0.promise) .verifiable(moq.Times.once()); presentationManagerMock - .setup((x) => x.getChildren({ imodel: imodelMock.object, rulesetId, paging: { start: 0, size: 0 } }, parentKeys[0])) + .setup((x) => x.getNodes({ imodel: imodelMock.object, rulesetId, paging: { start: 0, size: 0 } }, parentKeys[0])) .verifiable(moq.Times.never()); presentationManagerMock - .setup((x) => x.getChildren({ imodel: imodelMock.object, rulesetId, paging: { start: 1, size: 0 } }, parentKeys[0])) - .returns(() => resultContainers[1].promise) + .setup((x) => x.getNodes({ imodel: imodelMock.object, rulesetId, paging: { start: 1, size: 0 } }, parentKeys[0])) + .returns(() => resultNodesNonFirstPageContainer.promise) .verifiable(moq.Times.once()); presentationManagerMock - .setup((x) => x.getChildren({ imodel: imodelMock.object, rulesetId, paging: undefined }, parentKeys[1])) - .returns(() => resultContainers[2].promise) + .setup((x) => x.getNodesAndCount({ imodel: imodelMock.object, rulesetId, paging: { start: 0, size: 1 } }, parentKeys[1])) + .returns(() => resultnodesFirstPageContainer1.promise) .verifiable(moq.Times.once()); const promises = [ provider.getNodes(parentNodes[0], undefined), provider.getNodes(parentNodes[0], undefined), provider.getNodes(parentNodes[0], { start: 0, size: 0 }), provider.getNodes(parentNodes[0], { start: 0, size: 0 }), provider.getNodes(parentNodes[0], { start: 1, size: 0 }), provider.getNodes(parentNodes[0], { start: 1, size: 0 }), - provider.getNodes(parentNodes[1], undefined), provider.getNodes(parentNodes[1], undefined), + provider.getNodes(parentNodes[1], { start: 0, size: 1 }), provider.getNodes(parentNodes[1], { start: 0, size: 1 }), ]; - resultContainers.forEach((c: PromiseContainer) => c.resolve([createRandomECInstanceNode()])); + resultnodesFirstPageContainer0.resolve([createRandomECInstanceNode()]); + resultnodesFirstPageContainer1.resolve({ nodes: [createRandomECInstanceNode()], count: 1 }); + resultNodesNonFirstPageContainer.resolve([createRandomECInstanceNode()]); const results = await Promise.all(promises); expect(results[0]).to.eq(results[1], "results[0] should eq results[1]"); @@ -239,6 +303,41 @@ describe("TreeDataProvider", () => { }); + it("Logs warning when requesting nodes and pagingSize is not the same as passed pageOptions", async () => { + const pageOptions: PageOptions = { start: 0, size: 10 }; + const loggerSpy = sinon.spy(Logger, "logWarning"); + const result = { nodes: [createRandomECInstanceNode(), createRandomECInstanceNode()], count: 2 }; + presentationManagerMock.setup((x) => x.getNodesAndCount(moq.It.isAny(), moq.It.isAny())).returns(async () => result); + presentationManagerMock.setup((x) => x.getNodes(moq.It.isAny(), moq.It.isAny())).returns(async () => result.nodes); + + // Paging size is not set and no pageOptions are passed + await provider.getNodes(); + expect(loggerSpy.calledOnce).to.be.true; + loggerSpy.resetHistory(); + + // Paging size is not set and pageOptions are passed + await provider.getNodes(undefined, pageOptions); + expect(loggerSpy.calledOnce).to.be.true; + loggerSpy.resetHistory(); + + // Paging size is set and no pageOptions are passed + provider.pagingSize = 10; + await provider.getNodes(); + expect(loggerSpy.notCalled).to.be.true; + loggerSpy.resetHistory(); + + // Paging size is set and pageOptions are passed but not equal to paging size + provider.pagingSize = 20; + await provider.getNodes(undefined, pageOptions); + expect(loggerSpy.calledOnce).to.be.true; + loggerSpy.resetHistory(); + + // Paging size is set and pageOptions are passed and equal to paging size + provider.pagingSize = 10; + await provider.getNodes(undefined, pageOptions); + expect(loggerSpy.notCalled).to.be.true; + }); + }); describe("getFilteredNodes", () => { diff --git a/presentation/components/src/test/tree/FilteredDataProvider.test.snap b/presentation/components/src/test/tree/FilteredDataProvider.test.snap index 19cb223..b36e8a4 100644 --- a/presentation/components/src/test/tree/FilteredDataProvider.test.snap +++ b/presentation/components/src/test/tree/FilteredDataProvider.test.snap @@ -22,9 +22,13 @@ Array [ "id": "142b23f9-5f0d-4b34-b39f-de2c0a107d9c/dcb68625-021e-40e1-839b-08a0214f5943", "isCheckboxVisible": true, "label": "A-2-1", - "labelBackColor": 1531058431, - "labelForeColor": -1590574337, "parentId": "fb7680c8-0a28-4cf0-9ee8-272ec6682ae5/e14e4128-01ae-43e4-8790-9992f94b3186", + "style": Object { + "colorOverrides": Object { + "backgroundColor": 1531058431, + "color": -1590574337, + }, + }, }, Object { "autoExpand": true, @@ -90,8 +94,12 @@ Array [ "hasChildren": true, "id": "fb7680c8-0a28-4cf0-9ee8-272ec6682ae5/e14e4128-01ae-43e4-8790-9992f94b3186", "label": "A-2", - "labelBackColor": 499774463, - "labelForeColor": -284526081, + "style": Object { + "colorOverrides": Object { + "backgroundColor": 499774463, + "color": -284526081, + }, + }, }, ] `; diff --git a/presentation/components/src/test/tree/Utils.test.snap b/presentation/components/src/test/tree/Utils.test.snap index 990f403..4f3b4be 100644 --- a/presentation/components/src/test/tree/Utils.test.snap +++ b/presentation/components/src/test/tree/Utils.test.snap @@ -17,7 +17,11 @@ Object { }, "id": "19fb96ab-449d-4f28-a09d-cff691955c55/b5c02a04-6127-4f62-b128-58488bc1c9d1", "label": "engage", - "labelBackColor": -2027626753, + "style": Object { + "colorOverrides": Object { + "backgroundColor": -2027626753, + }, + }, } `; @@ -40,10 +44,14 @@ Object { "hasChildren": true, "id": "f7dd0ef0-a772-4c3d-b03a-40a84a6a8c62/774cdc48-ca2b-4104-8800-7ce7a4f6d779", "label": "Avon Awesome Plastic Salad", - "labelBackColor": -1829999873, - "labelBold": true, - "labelForeColor": -166204673, - "labelItalic": true, + "style": Object { + "colorOverrides": Object { + "backgroundColor": -1829999873, + "color": -166204673, + }, + "isBold": true, + "isItalic": true, + }, } `; @@ -91,7 +99,11 @@ Array [ "id": "e845ce78-d049-4079-8e52-c1852841f2dd/4dcb61ac-32e0-49da-bf7d-e66ed5ccf6b5", "isCheckboxVisible": true, "label": "intranet Executive", - "labelBackColor": 99406079, + "style": Object { + "colorOverrides": Object { + "backgroundColor": 99406079, + }, + }, }, Object { "extendedData": Object { @@ -136,8 +148,12 @@ Array [ "isCheckboxDisabled": true, "isCheckboxVisible": true, "label": "Haiti", - "labelBackColor": 480516607, "parentId": "Functionality", + "style": Object { + "colorOverrides": Object { + "backgroundColor": 480516607, + }, + }, }, Object { "description": "Voluptas cupiditate rerum consequatur illum unde molestiae quam.", @@ -158,9 +174,13 @@ Array [ "isCheckboxDisabled": true, "isCheckboxVisible": true, "label": "SSL methodologies Berkshire", - "labelBackColor": 1088423167, - "labelForeColor": 944178687, "parentId": "Functionality", + "style": Object { + "colorOverrides": Object { + "backgroundColor": 1088423167, + "color": 944178687, + }, + }, }, ] `; diff --git a/presentation/components/src/test/viewport/WithUnifiedSelection.test.tsx b/presentation/components/src/test/viewport/WithUnifiedSelection.test.tsx index b39a179..cb169ec 100644 --- a/presentation/components/src/test/viewport/WithUnifiedSelection.test.tsx +++ b/presentation/components/src/test/viewport/WithUnifiedSelection.test.tsx @@ -29,6 +29,7 @@ import { import { ViewportComponent } from "@bentley/ui-components"; import { IUnifiedSelectionComponent } from "../../common/IUnifiedSelectionComponent"; import { viewWithUnifiedSelection, ViewportSelectionHandler } from "../../viewport/WithUnifiedSelection"; +import { SelectionScopesManager } from "@bentley/presentation-frontend/lib/selection/SelectionScopesManager"; // tslint:disable-next-line:variable-name naming-convention const PresentationViewport = viewWithUnifiedSelection(ViewportComponent); @@ -295,17 +296,15 @@ describe("ViewportSelectionHandler", () => { presentationManagerMock.setup((x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, DefaultContentDisplayTypes.VIEWPORT, keys, descriptor.selectionInfo)).returns(async () => descriptor); - const content: Content = { + const expectedContent: Content = { descriptor, contentSet: [ new Item([createRandomECInstanceKey(), createRandomECInstanceKey()], "", "", undefined, {}, {}, []), new Item([createRandomECInstanceKey()], "", "", undefined, {}, {}, []), ], }; - presentationManagerMock.setup((x) => x.getContentSetSize({ imodel: imodelMock.object, rulesetId }, descriptor, keys)) - .returns(async () => content.contentSet.length); presentationManagerMock.setup((x) => x.getContent({ imodel: imodelMock.object, rulesetId, paging: undefined }, descriptor, keys)) - .returns(async () => content); + .returns(async () => expectedContent); // trigger the selection change const selectionChangeArgs: SelectionChangeEventArgs = { @@ -323,9 +322,9 @@ describe("ViewportSelectionHandler", () => { // verify viewport selection was changed with expected ids const ids = [ - content.contentSet[0].primaryKeys[0].id, - content.contentSet[0].primaryKeys[1].id, - content.contentSet[1].primaryKeys[0].id, + expectedContent.contentSet[0].primaryKeys[0].id, + expectedContent.contentSet[0].primaryKeys[1].id, + expectedContent.contentSet[1].primaryKeys[0].id, ]; expect(replaceSpy).to.be.calledWith(ids); }); @@ -552,7 +551,7 @@ describe("Integration: ViewportSelectionHandler", () => { let rulesetId: string; let key: InstanceKey; - let selectionManager = new SelectionManager(); + let selectionManager = new SelectionManager({ scopes: moq.Mock.ofType().object }); const presentationManagerMock = moq.Mock.ofType(); const imodelMock = moq.Mock.ofType(); @@ -571,7 +570,7 @@ describe("Integration: ViewportSelectionHandler", () => { beforeEach(() => { presentationManagerMock.reset(); mockIModel(imodelMock); - Presentation.selection = selectionManager = new SelectionManager(); + Presentation.selection = selectionManager = new SelectionManager({ scopes: moq.Mock.ofType().object }); }); describe("multiple ViewportSelectionHandlers", () => { @@ -592,14 +591,14 @@ describe("Integration: ViewportSelectionHandler", () => { level: 0, }; const descriptor = createRandomDescriptor(); - const content: Content = { + const expectedContent: Content = { descriptor, contentSet: [new Item([key], "", "", undefined, {}, {}, [])], }; presentationManagerMock.setup((x) => x.getContentDescriptor({ imodel: imodelMock.object, rulesetId }, DefaultContentDisplayTypes.VIEWPORT, moq.It.isAny(), moq.It.isAny())).returns(async () => descriptor); presentationManagerMock.setup((x) => x.getContent({ imodel: imodelMock.object, rulesetId, paging: undefined }, - moq.It.isAny(), moq.It.isAny())).returns(async () => content); + moq.It.isAny(), moq.It.isAny())).returns(async () => expectedContent); }; const setupViewportSelection = () => { diff --git a/presentation/components/src/tree/DataProvider.ts b/presentation/components/src/tree/DataProvider.ts index 27c7b82..0dea5a2 100644 --- a/presentation/components/src/tree/DataProvider.ts +++ b/presentation/components/src/tree/DataProvider.ts @@ -6,6 +6,7 @@ import * as _ from "lodash"; import { IModelConnection } from "@bentley/imodeljs-frontend"; +import { Logger } from "@bentley/bentleyjs-core"; import { NodeKey, NodePathElement, HierarchyRequestOptions } from "@bentley/presentation-common"; import { Presentation } from "@bentley/presentation-frontend"; import { DelayLoadedTreeNodeItem, TreeNodeItem, PageOptions } from "@bentley/ui-components"; @@ -18,6 +19,7 @@ import { IPresentationTreeDataProvider } from "./IPresentationTreeDataProvider"; export class PresentationTreeDataProvider implements IPresentationTreeDataProvider { private _rulesetId: string; private _imodel: IModelConnection; + private _pagingSize?: number; /** * Constructor. @@ -35,6 +37,10 @@ export class PresentationTreeDataProvider implements IPresentationTreeDataProvid /** [[IModelConnection]] used by this data provider */ public get imodel(): IModelConnection { return this._imodel; } + /** Paging options for obtaining nodes */ + public get pagingSize(): number | undefined { return this._pagingSize; } + public set pagingSize(value: number | undefined) { this._pagingSize = value; } + /** Called to get extended options for node requests */ private createRequestOptions(): HierarchyRequestOptions { return { @@ -56,27 +62,37 @@ export class PresentationTreeDataProvider implements IPresentationTreeDataProvid * @param parentNode The parent node to return children for. * @param pageOptions Information about the requested page of data. */ - public getNodes = _.memoize(async (parentNode?: TreeNodeItem, pageOptions?: PageOptions): Promise => { - if (parentNode) { - const parentKey = this.getNodeKey(parentNode); - const childNodes = await Presentation.presentation.getChildren({ ...this.createRequestOptions(), paging: pageOptionsUiToPresentation(pageOptions) }, parentKey); - return createTreeNodeItems(childNodes, parentNode.id); - } - const rootNodes = await Presentation.presentation.getRootNodes({ ...this.createRequestOptions(), paging: pageOptionsUiToPresentation(pageOptions) }); - return createTreeNodeItems(rootNodes); - }, MemoizationHelpers.getNodesKeyResolver); + public async getNodes(parentNode?: TreeNodeItem, pageOptions?: PageOptions): Promise { + if ((undefined !== pageOptions && pageOptions.size !== this.pagingSize) || (undefined === this.pagingSize)) + Logger.logWarning("Presentation.Components", "Paging size in provider does not match Provider's Paging Size."); + + if (parentNode) + return (await this._getNodesAndCount(parentNode, pageOptions)).nodes; + return (await this._getNodesAndCount(undefined, pageOptions)).nodes; + } /** * Returns the total number of nodes * @param parentNode The parent node to return children count for. */ - public getNodesCount = _.memoize(async (parentNode?: TreeNodeItem): Promise => { - if (parentNode) { - const parentKey = this.getNodeKey(parentNode); - return Presentation.presentation.getChildrenCount(this.createRequestOptions(), parentKey); + public async getNodesCount(parentNode?: TreeNodeItem): Promise { + const pageOptions = undefined !== this.pagingSize ? { start: 0, size: this.pagingSize } : undefined; + return (await this._getNodesAndCount(parentNode, pageOptions)).count!; + } + + private _getNodesAndCount = _.memoize(async (parentNode?: TreeNodeItem, pageOptions?: PageOptions) => { + const requestCount = undefined !== pageOptions && 0 === pageOptions.start && undefined !== pageOptions.size; + const parentKey = parentNode ? this.getNodeKey(parentNode) : undefined; + + if (!requestCount) { + const allNodes = await Presentation.presentation.getNodes({ ...this.createRequestOptions(), paging: pageOptionsUiToPresentation(pageOptions) }, parentKey); + const nodesCount = undefined === pageOptions || undefined === pageOptions.size ? allNodes.length : undefined; + return { nodes: parentNode ? createTreeNodeItems(allNodes, parentNode.id) : createTreeNodeItems(allNodes), count: nodesCount }; } - return Presentation.presentation.getRootNodesCount(this.createRequestOptions()); - }, MemoizationHelpers.getNodesCountKeyResolver); + + const nodesResponse = await Presentation.presentation.getNodesAndCount({ ...this.createRequestOptions(), paging: pageOptionsUiToPresentation(pageOptions) }, parentKey); + return { nodes: parentNode ? createTreeNodeItems(nodesResponse.nodes, parentNode.id) : createTreeNodeItems(nodesResponse.nodes), count: nodesResponse.count }; + }, MemoizationHelpers.getNodesKeyResolver); /** * Returns filtered node paths. @@ -97,5 +113,4 @@ class MemoizationHelpers { public static getNodesKeyResolver(parent?: TreeNodeItem, pageOptions?: PageOptions) { return `${MemoizationHelpers.createKeyForTreeNodeItem(parent)}/${MemoizationHelpers.createKeyForPageOptions(pageOptions)}`; } - public static getNodesCountKeyResolver(parent?: TreeNodeItem) { return MemoizationHelpers.createKeyForTreeNodeItem(parent); } } diff --git a/presentation/components/src/tree/Utils.ts b/presentation/components/src/tree/Utils.ts index 948e313..2405ee7 100644 --- a/presentation/components/src/tree/Utils.ts +++ b/presentation/components/src/tree/Utils.ts @@ -7,7 +7,7 @@ import { StyleHelper } from "../common/StyleHelper"; import { CheckBoxState } from "@bentley/ui-core"; import { Node, PageOptions as PresentationPageOptions } from "@bentley/presentation-common"; -import { DelayLoadedTreeNodeItem, PageOptions as UiPageOptions } from "@bentley/ui-components"; +import { DelayLoadedTreeNodeItem, PageOptions as UiPageOptions, ItemStyle, ItemColorOverrides } from "@bentley/ui-components"; /** @hidden */ export const createTreeNodeItems = (nodes: ReadonlyArray>, parentId?: string): DelayLoadedTreeNodeItem[] => { @@ -24,6 +24,9 @@ export const createTreeNodeItem = (node: Readonly, parentId?: string): Del label: node.label, extendedData: { key: node.key }, }; + const style: ItemStyle = {}; + const colorOverrides: ItemColorOverrides = {}; + if (parentId) item.parentId = parentId; if (node.description) @@ -31,15 +34,15 @@ export const createTreeNodeItem = (node: Readonly, parentId?: string): Del if (node.hasChildren) item.hasChildren = true; if (StyleHelper.isBold(node)) - item.labelBold = true; + style.isBold = true; if (StyleHelper.isItalic(node)) - item.labelItalic = true; + style.isItalic = true; const foreColor = StyleHelper.getForeColor(node); if (foreColor) - item.labelForeColor = foreColor; + colorOverrides.color = foreColor; const backColor = StyleHelper.getBackColor(node); if (backColor) - item.labelBackColor = backColor; + colorOverrides.backgroundColor = backColor; if (node.isCheckboxVisible) { item.isCheckboxVisible = true; if (node.isChecked) @@ -47,6 +50,12 @@ export const createTreeNodeItem = (node: Readonly, parentId?: string): Del if (!node.isCheckboxEnabled) item.isCheckboxDisabled = true; } + + if (Object.keys(colorOverrides).length > 0) + style.colorOverrides = colorOverrides; + if (Object.keys(style).length > 0) + item.style = style; + return item; }; diff --git a/presentation/frontend/CHANGELOG.json b/presentation/frontend/CHANGELOG.json index 85b304b..8445ba5 100644 --- a/presentation/frontend/CHANGELOG.json +++ b/presentation/frontend/CHANGELOG.json @@ -1,6 +1,45 @@ { "name": "@bentley/presentation-frontend", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/presentation-frontend_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "Changes package.json to include api-extractor and adds api-extractor.json" + }, + { + "comment": "Use new buildIModelJsBuild script" + }, + { + "comment": "Remove uneeded typedoc plugin depedency" + }, + { + "comment": "Uncomment and fixed test" + }, + { + "comment": "Save BUILD_SEMVER to globally accessible map" + }, + { + "comment": "Add an API for getting selection scopes and computing selection based on a selection scope." + }, + { + "comment": "RPC Interface changes to optimize getting first page of nodes/content" + }, + { + "comment": "upgrade to TypeScript 3.2.2" + } + ] + } + }, + { + "version": "0.188.0", + "tag": "@bentley/presentation-frontend_v0.188.0", + "date": "Wed, 16 Jan 2019 16:36:09 GMT", + "comments": {} + }, { "version": "0.187.0", "tag": "@bentley/presentation-frontend_v0.187.0", diff --git a/presentation/frontend/CHANGELOG.md b/presentation/frontend/CHANGELOG.md index 2def094..6ae5670 100644 --- a/presentation/frontend/CHANGELOG.md +++ b/presentation/frontend/CHANGELOG.md @@ -1,6 +1,25 @@ # Change Log - @bentley/presentation-frontend -This log was last generated on Tue, 15 Jan 2019 15:18:59 GMT and should not be manually modified. +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- Changes package.json to include api-extractor and adds api-extractor.json +- Use new buildIModelJsBuild script +- Remove uneeded typedoc plugin depedency +- Uncomment and fixed test +- Save BUILD_SEMVER to globally accessible map +- Add an API for getting selection scopes and computing selection based on a selection scope. +- RPC Interface changes to optimize getting first page of nodes/content +- upgrade to TypeScript 3.2.2 + +## 0.188.0 +Wed, 16 Jan 2019 16:36:09 GMT + +*Version update only* ## 0.187.0 Tue, 15 Jan 2019 15:18:59 GMT diff --git a/presentation/frontend/package.json b/presentation/frontend/package.json index 9247960..8808f24 100644 --- a/presentation/frontend/package.json +++ b/presentation/frontend/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/presentation-frontend", - "version": "0.187.0", + "version": "0.189.0", "description": "Frontend of iModel.js Presentation library", "license": "MIT", "repository": { @@ -21,7 +21,7 @@ "main": "lib/presentation-frontend.js", "typings": "lib/presentation-frontend", "scripts": { - "build": "tsc -b ./src/test 1>&2 && npm run extract && npm run webpackModule-dev", + "build": "npm run extract && node ./node_modules/@bentley/webpack-tools/bin/buildIModelJsModule.js", "build:watch": "tsc -b ./src/test -w", "clean": "rimraf lib package-deps.json", "cover": "nyc npm test", @@ -29,28 +29,38 @@ "docs:changelog": "cpx \"./CHANGELOG.md\" ../../generated-docs/presentation/presentation-frontend", "docs:reference": "node ./node_modules/@bentley/build-tools/scripts/docs.js --source=./src --includes=../../generated-docs/extract --json=../../generated-docs/presentation/presentation-frontend/json/file.json --tsIndexFile=presentation-frontend.ts --onlyJson %TYPEDOC_THEME%", "extract": "node ./node_modules/@bentley/build-tools/scripts/extract.js --fileExt=ts --extractFrom=./src/test --recursive --out=../../generated-docs/extract", + "extract-api": "node ./node_modules/@bentley/build-tools/scripts/extract-api.js --entry=presentation-frontend --isPresentation=true", "lint": "echo Disabled until https://github.com/palantir/tslint/issues/4148 is fixed", "//lint": "tslint -p ./src 1>&2", "test": "mocha -r jsdom-global/register --opts ../mocha.opts lib/test/**/*.js", - "test:watch": "npm test -- --reporter min --watch-extensions ts --watch", - "webpackModule-dev": "make-dir ./lib/module/dev/ && webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/presentation-frontend.js --env.bundlename=presentation-frontend --env.stylesheets --json >./lib/module/dev/webpackStats.json", - "webpackModule-prod": "webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/presentation-frontend.js --env.bundlename=presentation-frontend --env.prod --env.stylesheets" + "test:watch": "npm test -- --reporter min --watch-extensions ts --watch" + }, + "iModelJs": { + "buildModule": { + "type": "system", + "tscOptions": "-b ./src/test", + "webpack": { + "dest": "./lib/module", + "entry": "./lib/presentation-frontend.js", + "bundleName": "presentation-frontend" + } + } }, "peerDependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/imodeljs-i18n": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-frontend": "0.187.0", - "@bentley/presentation-common": "0.187.0" + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/imodeljs-i18n": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-frontend": "0.189.0", + "@bentley/presentation-common": "0.189.0" }, "devDependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/build-tools": "0.187.0", - "@bentley/imodeljs-i18n": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-frontend": "0.187.0", - "@bentley/presentation-common": "0.187.0", - "@bentley/webpack-tools": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/build-tools": "0.189.0", + "@bentley/imodeljs-i18n": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-frontend": "0.189.0", + "@bentley/presentation-common": "0.189.0", + "@bentley/webpack-tools": "0.189.0", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/chai-jest-snapshot": "^1.3.0", @@ -67,20 +77,15 @@ "deep-equal": "^1", "faker": "^4.1.0", "jsdom-global": "3.0.2", - "make-dir-cli": "^1.0.0", "mocha": "^5.2.0", "nyc": "^13.0.1", "rimraf": "^2.6.2", "sinon": "^7.1.1", "sinon-chai": "^3.2.0", - "source-map-loader": "^0.2.3", "tslint": "^5.11.0", "typedoc": "^0.11.1", - "typedoc-plugin-external-module-name": "^1.1.1", "typemoq": "^2.1.0", - "typescript": "~3.1.0", - "webpack": "^4.20.2", - "webpack-cli": "^3.1.0", + "typescript": "~3.2.2", "xmlhttprequest": "^1.8.0" }, "nyc": { diff --git a/presentation/frontend/src/PersistenceHelper.ts b/presentation/frontend/src/PersistenceHelper.ts index a8307dc..cd4ee78 100644 --- a/presentation/frontend/src/PersistenceHelper.ts +++ b/presentation/frontend/src/PersistenceHelper.ts @@ -26,7 +26,8 @@ export default class PersistenceHelper { public static async createPersistentKeysContainer(imodel: IModelConnection, keyset: KeySet): Promise { const instanceClassNames = Array.from(keyset.instanceKeys.keys()); const instanceClassNameBindings = instanceClassNames.map(() => "?").join(","); - const modelClassNameObjs = await imodel.executeQuery(` + const modelClassNameObjs = []; + for await (const modelClassNameObj of imodel.query(` SELECT s.Alias || '.' || c.Name AS fullClassName FROM [meta].[ECSchemaDef] s JOIN [meta].[ECClassDef] c ON c.SchemaId = s.ECInstanceId @@ -35,7 +36,9 @@ export default class PersistenceHelper { JOIN [meta].[ECSchemaDef] ms ON ms.ECInstanceId = mc.SchemaId WHERE ms.Alias || '.' || mc.Name = 'bis.Model' AND s.Alias || '.' || c.Name IN (${instanceClassNameBindings}) - `, instanceClassNames); + `, instanceClassNames)) { + modelClassNameObjs.push(modelClassNameObj); + } const modelClassNames = new Set(modelClassNameObjs.map((o: any) => (o.fullClassName as string))); let modelIds = new Array(); let elementIds = new Array(); diff --git a/presentation/frontend/src/Presentation.ts b/presentation/frontend/src/Presentation.ts index 4d6e8ff..30b755a 100644 --- a/presentation/frontend/src/Presentation.ts +++ b/presentation/frontend/src/Presentation.ts @@ -8,7 +8,8 @@ import { I18N } from "@bentley/imodeljs-i18n"; import { IModelApp } from "@bentley/imodeljs-frontend"; import { PresentationError, PresentationStatus } from "@bentley/presentation-common"; import PresentationManager, { Props as PresentationManagerProps } from "./PresentationManager"; -import SelectionManager from "./selection/SelectionManager"; +import { SelectionManager } from "./selection/SelectionManager"; +import { SelectionScopesManager } from "./selection/SelectionScopesManager"; let presentationManager: PresentationManager | undefined; let selectionManager: SelectionManager | undefined; @@ -57,7 +58,13 @@ export default class Presentation { presentationManager = PresentationManager.create(props); } if (!selectionManager) { - selectionManager = new SelectionManager(); + const scopesManager = new SelectionScopesManager({ + rpcRequestsHandler: presentationManager.rpcRequestsHandler, + localeProvider: () => this.presentation.activeLocale, + }); + selectionManager = new SelectionManager({ + scopes: scopesManager, + }); } } diff --git a/presentation/frontend/src/PresentationManager.ts b/presentation/frontend/src/PresentationManager.ts index 9852002..8cea5f6 100644 --- a/presentation/frontend/src/PresentationManager.ts +++ b/presentation/frontend/src/PresentationManager.ts @@ -10,7 +10,7 @@ import { RpcRequestsHandler, HierarchyRequestOptions, Node, NodeKey, NodePathElement, ContentRequestOptions, Content, Descriptor, SelectionInfo, - Paged, RequestOptions, KeySet, InstanceKey, + Paged, RequestOptions, KeySet, InstanceKey, NodesResponse, ContentResponse, } from "@bentley/presentation-common"; import RulesetVariablesManager from "./RulesetVariablesManager"; import RulesetManager from "./RulesetManager"; @@ -101,48 +101,39 @@ export default class PresentationManager implements IDisposable { // 1. put default `locale` // 2. put all `options` members (if `locale` is set, it'll override the default put at #1) // 3. put `imodel` of type `IModelToken` which overwrites the `imodel` from `options` put at #2 - // 4. put `clientId` return Object.assign({}, { locale: this.activeLocale }, options, { imodel: options.imodel.iModelToken, }); } /** - * Retrieves root nodes. + * Retrieves nodes. * @param requestOptions options for the request - * @return A promise object that returns either an array of nodes on success or an error string on error. - */ - public async getRootNodes(requestOptions: Paged>): Promise>> { - return this._requestsHandler.getRootNodes(this.toIModelTokenOptions(requestOptions)); - } - - /** - * Retrieves root nodes count. - * @param requestOptions options for the request - * @return A promise object that returns the number of root nodes. + * @param parentKey Key of the parent node. + * @return A promise object that returns either a nodes response object with nodes and nodes count on success or an error string on error. */ - public async getRootNodesCount(requestOptions: HierarchyRequestOptions): Promise { - return this._requestsHandler.getRootNodesCount(this.toIModelTokenOptions(requestOptions)); + public async getNodesAndCount(requestOptions: Paged>, parentKey?: Readonly): Promise> { + return this._requestsHandler.getNodesAndCount(this.toIModelTokenOptions(requestOptions), parentKey); } /** - * Retrieves children of the specified parent node. + * Retrieves nodes * @param requestOptions options for the request - * @param parentKey Key of the parent node. + * @param parentKey Key of the parent node if requesting for child nodes * @return A promise object that returns either an array of nodes on success or an error string on error. */ - public async getChildren(requestOptions: Paged>, parentKey: Readonly): Promise>> { - return this._requestsHandler.getChildren(this.toIModelTokenOptions(requestOptions), parentKey); + public async getNodes(requestOptions: Paged>, parentKey?: Readonly): Promise>> { + return this._requestsHandler.getNodes(this.toIModelTokenOptions(requestOptions), parentKey); } /** - * Retrieves children count for the specified parent node. + * Retrieves nodes count. * @param requestOptions options for the request - * @param parentKey Key of the parent node. - * @return A promise object that returns the number of child nodes. + * @param parentKey Key of the parent node if requesting for child nodes count. + * @return A promise object that returns the number of nodes. */ - public async getChildrenCount(requestOptions: HierarchyRequestOptions, parentKey: Readonly): Promise { - return this._requestsHandler.getChildrenCount(this.toIModelTokenOptions(requestOptions), parentKey); + public async getNodesCount(requestOptions: HierarchyRequestOptions, parentKey?: Readonly): Promise { + return this._requestsHandler.getNodesCount(this.toIModelTokenOptions(requestOptions), parentKey); } /** @@ -183,30 +174,46 @@ export default class PresentationManager implements IDisposable { /** * Retrieves the content set size based on the supplied content descriptor override. - * @param requestOptions options for the request - * @param descriptor Content descriptor which specifies how the content should be returned. - * @param keys Keys of ECInstances to get the content for. + * @param requestOptions options for the request + * @param descriptorOrDisplayType Content descriptor which specifies how the content should be returned or preferred display type of the content + * @param keys Keys of ECInstances to get the content for. * @return A promise object that returns either a number on success or an error string on error. * Even if concrete implementation returns content in pages, this function returns the total * number of records in the content set. */ - public async getContentSetSize(requestOptions: ContentRequestOptions, descriptor: Readonly, keys: Readonly): Promise { - return this._requestsHandler.getContentSetSize(this.toIModelTokenOptions(requestOptions), descriptor.createStrippedDescriptor(), keys); + public async getContentSetSize(requestOptions: ContentRequestOptions, descriptorOrDisplayType: Readonly | string, keys: Readonly): Promise { + const descriptorParam = (typeof descriptorOrDisplayType === "string") ? descriptorOrDisplayType : descriptorOrDisplayType.createStrippedDescriptor(); + return this._requestsHandler.getContentSetSize(this.toIModelTokenOptions(requestOptions), descriptorParam, keys); } /** * Retrieves the content based on the supplied content descriptor override. - * @param requestOptions options for the request - * @param descriptor Content descriptor which specifies how the content should be returned. - * @param keys Keys of ECInstances to get the content for. + * @param requestOptions options for the request + * @param descriptorOrDisplayType Content descriptor which specifies how the content should be returned or preferred display type of the content + * @param keys Keys of ECInstances to get the content for. * @return A promise object that returns either content on success or an error string on error. */ - public async getContent(requestOptions: Paged>, descriptor: Readonly, keys: Readonly): Promise> { - const content = await this._requestsHandler.getContent(this.toIModelTokenOptions(requestOptions), descriptor.createStrippedDescriptor(), keys); + public async getContent(requestOptions: Paged>, descriptorOrDisplayType: Readonly | string, keys: Readonly): Promise> { + const descriptorParam = (typeof descriptorOrDisplayType === "string") ? descriptorOrDisplayType : descriptorOrDisplayType.createStrippedDescriptor(); + const content = await this._requestsHandler.getContent(this.toIModelTokenOptions(requestOptions), descriptorParam, keys); content.descriptor.rebuildParentship(); return content; } + /** + * Retrieves the content and content set size based on the supplied content descriptor override. + * @param requestOptions Options for the request. + * @param descriptorOrDisplayType Content descriptor which specifies how the content should be returned or preferred display type of the content + * @param keys Keys of ECInstances to get the content for. + * @returns A promise object that returns either content and content set size on success or an error string on error. + */ + public async getContentAndSize(requestOptions: Paged>, descriptorOrDisplayType: Readonly | string, keys: Readonly): Promise> { + const descriptorParam = (typeof descriptorOrDisplayType === "string") ? descriptorOrDisplayType : descriptorOrDisplayType.createStrippedDescriptor(); + const contentResponse = await this._requestsHandler.getContentAndSize(this.toIModelTokenOptions(requestOptions), descriptorParam, keys); + contentResponse.content.descriptor.rebuildParentship(); + return contentResponse; + } + /** * Retrieves distinct values of specific field from the content based on the supplied content descriptor override. * @param requestOptions options for the request diff --git a/presentation/frontend/src/presentation-frontend.ts b/presentation/frontend/src/presentation-frontend.ts index e42475b..2782b2d 100644 --- a/presentation/frontend/src/presentation-frontend.ts +++ b/presentation/frontend/src/presentation-frontend.ts @@ -12,5 +12,14 @@ export { default as RulesetVariablesManager } from "./RulesetVariablesManager"; /** @module UnifiedSelection */ export { default as SelectionChangeEvent, SelectionChangeEventArgs, SelectionChangeType, SelectionChangesListener } from "./selection/SelectionChangeEvent"; export { default as ISelectionProvider } from "./selection/ISelectionProvider"; -export { default as SelectionManager } from "./selection/SelectionManager"; +export { SelectionManager } from "./selection/SelectionManager"; export { default as SelectionHandler } from "./selection/SelectionHandler"; + +// Set the version number so it can be found at runtime. BUILD_SEMVER is replaced at build time by the webpack DefinePlugin. +declare var BUILD_SEMVER: string; +/* istanbul ignore next */ +if ((typeof (BUILD_SEMVER) !== "undefined") && (typeof window !== "undefined") && window) { + if (!(window as any).iModelJsVersions) + (window as any).iModelJsVersions = new Map(); + (window as any).iModelJsVersions.set("presentation-frontend", BUILD_SEMVER); +} diff --git a/presentation/frontend/src/selection/SelectionHandler.ts b/presentation/frontend/src/selection/SelectionHandler.ts index bffd5b5..cbe5c6b 100644 --- a/presentation/frontend/src/selection/SelectionHandler.ts +++ b/presentation/frontend/src/selection/SelectionHandler.ts @@ -8,7 +8,7 @@ import { IDisposable, DisposableList } from "@bentley/bentleyjs-core"; import { IModelConnection } from "@bentley/imodeljs-frontend"; import { Keys, KeySet } from "@bentley/presentation-common"; import { SelectionChangeEventArgs, SelectionChangesListener } from "./SelectionChangeEvent"; -import SelectionManager from "./SelectionManager"; +import { SelectionManager } from "./SelectionManager"; import ISelectionProvider from "./ISelectionProvider"; /** diff --git a/presentation/frontend/src/selection/SelectionManager.ts b/presentation/frontend/src/selection/SelectionManager.ts index 879763d..2bf86cc 100644 --- a/presentation/frontend/src/selection/SelectionManager.ts +++ b/presentation/frontend/src/selection/SelectionManager.ts @@ -4,25 +4,37 @@ *--------------------------------------------------------------------------------------------*/ /** @module UnifiedSelection */ +import { EntityProps } from "@bentley/imodeljs-common"; import { IModelConnection } from "@bentley/imodeljs-frontend"; -import { KeySet, Keys } from "@bentley/presentation-common"; +import { KeySet, Keys, SelectionScope } from "@bentley/presentation-common"; import ISelectionProvider from "./ISelectionProvider"; import SelectionChangeEvent, { SelectionChangeEventArgs, SelectionChangeType } from "./SelectionChangeEvent"; +import { SelectionScopesManager } from "./SelectionScopesManager"; + +/** Properties for creating [[SelectionManager]] */ +export interface SelectionManagerProps { + /** A manager for [selection scopes]($docs/learning/unified-selection/Terminology#selection-scope) */ + scopes: SelectionScopesManager; +} /** * The selection manager which stores the overall selection */ -export default class SelectionManager implements ISelectionProvider { +export class SelectionManager implements ISelectionProvider { private _selectionContainerMap = new Map(); /** An event which gets broadcasted on selection changes */ - public selectionChange: SelectionChangeEvent; + public readonly selectionChange: SelectionChangeEvent; + + /** Manager for [selection scopes]($docs/learning/unified-selection/Terminology#selection-scope) */ + public readonly scopes: SelectionScopesManager; /** * Creates an instance of SelectionManager. */ - constructor() { + constructor(props: SelectionManagerProps) { this.selectionChange = new SelectionChangeEvent(); + this.scopes = props.scopes; IModelConnection.onClose.addListener((imodel: IModelConnection) => { this.onConnectionClose(imodel); }); @@ -164,6 +176,48 @@ export default class SelectionManager implements ISelectionProvider { }; this.handleEvent(evt); } + + /** + * Add keys to selection after applying [selection scope]($docs/learning/unified-selection/Terminology#selection-scope) on them. + * @param source Name of the selection source + * @param imodel iModel associated with the selection + * @param keys Keys to add + * @param scope Selection scope to apply + * @param level Selection level (see [Selection levels]($docs/learning/unified-selection/Terminology#selection-level)) + * @param rulesetId ID of the ruleset in case the selection was changed from a rules-driven control + */ + public async addToSelectionWithScope(source: string, imodel: IModelConnection, keys: EntityProps | EntityProps[], scope: SelectionScope | string, level: number = 0, rulesetId?: string): Promise { + const scopedKeys = await this.scopes.computeSelection(imodel, keys, scope); + this.addToSelection(source, imodel, scopedKeys, level, rulesetId); + } + + /** + * Remove keys from current selection after applying [selection scope]($docs/learning/unified-selection/Terminology#selection-scope) on them. + * @param source Name of the selection source + * @param imodel iModel associated with the selection + * @param keys Keys to remove + * @param scope Selection scope to apply + * @param level Selection level (see [Selection levels]($docs/learning/unified-selection/Terminology#selection-level)) + * @param rulesetId ID of the ruleset in case the selection was changed from a rules-driven control + */ + public async removeFromSelectionWithScope(source: string, imodel: IModelConnection, keys: EntityProps | EntityProps[], scope: SelectionScope | string, level: number = 0, rulesetId?: string): Promise { + const scopedKeys = await this.scopes.computeSelection(imodel, keys, scope); + this.removeFromSelection(source, imodel, scopedKeys, level, rulesetId); + } + + /** + * Replace current selection with keys after applying [selection scope]($docs/learning/unified-selection/Terminology#selection-scope) on them. + * @param source Name of the selection source + * @param imodel iModel associated with the selection + * @param keys Keys to add + * @param scope Selection scope to apply + * @param level Selection level (see [Selection levels]($docs/learning/unified-selection/Terminology#selection-level)) + * @param rulesetId ID of the ruleset in case the selection was changed from a rules-driven control + */ + public async replaceSelectionWithScope(source: string, imodel: IModelConnection, keys: EntityProps | EntityProps[], scope: SelectionScope | string, level: number = 0, rulesetId?: string): Promise { + const scopedKeys = await this.scopes.computeSelection(imodel, keys, scope); + this.replaceSelection(source, imodel, scopedKeys, level, rulesetId); + } } class SelectionContainer { diff --git a/presentation/frontend/src/selection/SelectionScopesManager.ts b/presentation/frontend/src/selection/SelectionScopesManager.ts new file mode 100644 index 0000000..0d4ce66 --- /dev/null +++ b/presentation/frontend/src/selection/SelectionScopesManager.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module UnifiedSelection */ + +import { EntityProps } from "@bentley/imodeljs-common"; +import { IModelConnection } from "@bentley/imodeljs-frontend"; +import { KeySet, SelectionScope, RpcRequestsHandler } from "@bentley/presentation-common"; + +/** Properties for creating [[SelectionScopesManager]] */ +export interface SelectionScopesManagerProps { + /** RPC handler to use for requesting selection scopes */ + rpcRequestsHandler: RpcRequestsHandler; + + /** Provider of active locale to use for localizing scopes */ + localeProvider?: () => string | undefined; +} + +/** + * A manager that knows available [selection scopes]($docs/learning/unified-selection/Terminology#selection-scope) + * and can compute selection based on [[ElementProps]] and [[SelectionScope]] + */ +export class SelectionScopesManager { + + private _rpcRequestsHandler: RpcRequestsHandler; + private _getLocale: () => string | undefined; + + public constructor(props: SelectionScopesManagerProps) { + this._rpcRequestsHandler = props.rpcRequestsHandler; + this._getLocale = props.localeProvider ? props.localeProvider : (() => undefined); + } + + public get activeLocale() { return this._getLocale(); } + + /** + * Get available selection scopes. + * @param imodel The iModel to get selection scopes for + * @param locale Optional locale to use when localizing scopes' label and description + */ + public async getSelectionScopes(imodel: IModelConnection, locale?: string): Promise { + if (!locale) + locale = this._getLocale(); + return this._rpcRequestsHandler.getSelectionScopes({ imodel: imodel.iModelToken, locale }); + } + + /** + * Computes keys that need to be added to selection based on provided selection scope. + * @param keys Identifiers of elements to compute selection for + * @param scope Selection scope to apply + */ + public async computeSelection(imodel: IModelConnection, keys: EntityProps | EntityProps[], scope: SelectionScope | string): Promise { + const scopeId = (typeof scope === "string") ? scope : scope.id; + if (!Array.isArray(keys)) + keys = [keys]; + + return this._rpcRequestsHandler.computeSelection({ imodel: imodel.iModelToken }, keys, scopeId); + } +} diff --git a/presentation/frontend/src/test/PersistenceHelper.test.ts b/presentation/frontend/src/test/PersistenceHelper.test.ts index 75e2c85..c580274 100644 --- a/presentation/frontend/src/test/PersistenceHelper.test.ts +++ b/presentation/frontend/src/test/PersistenceHelper.test.ts @@ -10,7 +10,7 @@ import { createRandomECInstanceNodeKey, createRandomId } from "@bentley/presenta import { Id64 } from "@bentley/bentleyjs-core"; import { RelatedElementProps, ModelProps, ElementProps, Code } from "@bentley/imodeljs-common"; import { IModelConnection } from "@bentley/imodeljs-frontend"; -import { KeySet, PersistentKeysContainer, InstanceKey } from "@bentley/presentation-common"; +import { PersistentKeysContainer, InstanceKey, KeySet } from "@bentley/presentation-common"; import { PersistenceHelper } from "../presentation-frontend"; describe("PersistenceHelper", () => { @@ -70,6 +70,12 @@ describe("PersistenceHelper", () => { }); + async function* createAsyncIterator(items: any[]) { + for (const item of items) { + yield item; + } + } + describe("createPersistentKeysContainer", () => { it("creates PersistentKeysContainer", async () => { @@ -85,8 +91,10 @@ describe("PersistenceHelper", () => { const nodeKey = createRandomECInstanceNodeKey(); // set up the mock const imodelMock = moq.Mock.ofType(); - imodelMock.setup((x) => x.executeQuery(moq.It.isAnyString(), [modelKey.className, elementKey.className])) - .returns(async () => [{ fullClassName: modelKey.className }]) + imodelMock.setup((x) => x.query(moq.It.isAnyString(), [modelKey.className, elementKey.className])) + .returns(() => { + return createAsyncIterator([{ fullClassName: modelKey.className }]); + }) .verifiable(); const imodel = imodelMock.object; @@ -107,7 +115,5 @@ describe("PersistenceHelper", () => { nodes: [nodeKey], }); }); - }); - }); diff --git a/presentation/frontend/src/test/Presentation.test.ts b/presentation/frontend/src/test/Presentation.test.ts index e951c9b..c45cb2a 100644 --- a/presentation/frontend/src/test/Presentation.test.ts +++ b/presentation/frontend/src/test/Presentation.test.ts @@ -12,6 +12,7 @@ import { I18N } from "@bentley/imodeljs-i18n"; import { IModelApp, NoRenderApp } from "@bentley/imodeljs-frontend"; import { PresentationError } from "@bentley/presentation-common"; import { Presentation, SelectionManager } from "../presentation-frontend"; +import { SelectionScopesManager } from "../selection/SelectionScopesManager"; import PresentationManager from "../PresentationManager"; describe("Presentation", () => { @@ -95,6 +96,17 @@ describe("Presentation", () => { i18nMock.verifyAll(); }); + it("initializes SelectionScopesManager's locale callback to return PresentationManager's activeLocale", () => { + Presentation.initialize({ + activeLocale: "test" + }); + expect(Presentation.presentation.activeLocale).to.eq("test"); + expect(Presentation.selection.scopes.activeLocale).to.eq("test"); + Presentation.presentation.activeLocale = "other"; + expect(Presentation.presentation.activeLocale).to.eq("other"); + expect(Presentation.selection.scopes.activeLocale).to.eq("other"); + }); + }); describe("terminate", () => { @@ -135,14 +147,14 @@ describe("Presentation", () => { describe("[set] selection", () => { it("overwrites selection manager instance before initialization", () => { - const manager = new SelectionManager(); + const manager = new SelectionManager({ scopes: moq.Mock.ofType().object }); Presentation.selection = manager; Presentation.initialize(); expect(Presentation.selection).to.eq(manager); }); it("overwrites selection manager instance after initialization", () => { - const otherManager = new SelectionManager(); + const otherManager = new SelectionManager({ scopes: moq.Mock.ofType().object }); Presentation.initialize(); expect(Presentation.selection).to.be.not.null; expect(Presentation.selection).to.not.eq(otherManager); diff --git a/presentation/frontend/src/test/PresentationManager.test.ts b/presentation/frontend/src/test/PresentationManager.test.ts index 370dde6..9a1c001 100644 --- a/presentation/frontend/src/test/PresentationManager.test.ts +++ b/presentation/frontend/src/test/PresentationManager.test.ts @@ -92,29 +92,29 @@ describe("PresentationManager", () => { it("requests with manager's locale if not set in request options", async () => { const locale = faker.random.locale(); manager.activeLocale = locale; - await manager.getRootNodesCount({ + await manager.getNodesCount({ imodel: testData.imodelMock.object, rulesetId: testData.rulesetId, }); - rpcRequestsHandlerMock.verify((x) => x.getRootNodesCount({ + rpcRequestsHandlerMock.verify((x) => x.getNodesCount({ imodel: testData.imodelToken, rulesetId: testData.rulesetId, locale, - }), moq.Times.once()); + }, undefined), moq.Times.once()); }); it("requests with request's locale if set", async () => { const locale = faker.random.locale(); - await manager.getRootNodesCount({ + await manager.getNodesCount({ imodel: testData.imodelMock.object, rulesetId: testData.rulesetId, locale, }); - rpcRequestsHandlerMock.verify((x) => x.getRootNodesCount({ + rpcRequestsHandlerMock.verify((x) => x.getNodesCount({ imodel: testData.imodelToken, rulesetId: testData.rulesetId, locale, - }), moq.Times.once()); + }, undefined), moq.Times.once()); }); }); @@ -140,46 +140,60 @@ describe("PresentationManager", () => { }); - describe("getRootNodes", () => { + describe("getNodesAndCount", () => { it("requests root nodes from proxy", async () => { - const result = [createRandomECInstanceNode(), createRandomECInstanceNode()]; + const result = { nodes: [createRandomECInstanceNode(), createRandomECInstanceNode()], count: 2 }; const options: Paged> = { imodel: testData.imodelMock.object, rulesetId: testData.rulesetId, paging: testData.pageOptions, }; rpcRequestsHandlerMock - .setup((x) => x.getRootNodes(toIModelTokenOptions(options))) + .setup((x) => x.getNodesAndCount(toIModelTokenOptions(options), undefined)) .returns(async () => result) .verifiable(); - const actualResult = await manager.getRootNodes(options); + const actualResult = await manager.getNodesAndCount(options); expect(actualResult).to.eq(result); rpcRequestsHandlerMock.verifyAll(); }); - }); - - describe("getRootNodesCount", () => { - - it("requests root nodes count from proxy", async () => { - const result = faker.random.number(); - const options: HierarchyRequestOptions = { + it("requests child nodes from proxy", async () => { + const parentNodeKey = createRandomECInstanceNodeKey(); + const result = { nodes: [createRandomECInstanceNode(), createRandomECInstanceNode()], count: 2 }; + const options: Paged> = { imodel: testData.imodelMock.object, rulesetId: testData.rulesetId, + paging: testData.pageOptions, }; rpcRequestsHandlerMock - .setup((x) => x.getRootNodesCount(toIModelTokenOptions(options))) + .setup((x) => x.getNodesAndCount(toIModelTokenOptions(options), parentNodeKey)) .returns(async () => result) .verifiable(); - const actualResult = await manager.getRootNodesCount(options); + const actualResult = await manager.getNodesAndCount(options, parentNodeKey); expect(actualResult).to.eq(result); rpcRequestsHandlerMock.verifyAll(); }); }); - describe("getChildren", () => { + describe("getNodes", () => { + + it("requests root nodes from proxy", async () => { + const result = [createRandomECInstanceNode(), createRandomECInstanceNode()]; + const options: Paged> = { + imodel: testData.imodelMock.object, + rulesetId: testData.rulesetId, + paging: testData.pageOptions, + }; + rpcRequestsHandlerMock + .setup((x) => x.getNodes(toIModelTokenOptions(options), undefined)) + .returns(async () => result) + .verifiable(); + const actualResult = await manager.getNodes(options); + expect(actualResult).to.eq(result); + rpcRequestsHandlerMock.verifyAll(); + }); it("requests child nodes from proxy", async () => { const parentNodeKey = createRandomECInstanceNodeKey(); @@ -190,17 +204,32 @@ describe("PresentationManager", () => { paging: testData.pageOptions, }; rpcRequestsHandlerMock - .setup((x) => x.getChildren(toIModelTokenOptions(options), parentNodeKey)) + .setup((x) => x.getNodes(toIModelTokenOptions(options), parentNodeKey)) .returns(async () => result) .verifiable(); - const actualResult = await manager.getChildren(options, parentNodeKey); + const actualResult = await manager.getNodes(options, parentNodeKey); expect(actualResult).to.eq(result); rpcRequestsHandlerMock.verifyAll(); }); }); - describe("getChildrenCount", () => { + describe("getNodesCount", () => { + + it("requests root nodes count from proxy", async () => { + const result = faker.random.number(); + const options: HierarchyRequestOptions = { + imodel: testData.imodelMock.object, + rulesetId: testData.rulesetId, + }; + rpcRequestsHandlerMock + .setup((x) => x.getNodesCount(toIModelTokenOptions(options), undefined)) + .returns(async () => result) + .verifiable(); + const actualResult = await manager.getNodesCount(options); + expect(actualResult).to.eq(result); + rpcRequestsHandlerMock.verifyAll(); + }); it("requests child nodes count from proxy", async () => { const parentNodeKey = createRandomECInstanceNodeKey(); @@ -210,10 +239,10 @@ describe("PresentationManager", () => { rulesetId: testData.rulesetId, }; rpcRequestsHandlerMock - .setup((x) => x.getChildrenCount(toIModelTokenOptions(options), parentNodeKey)) + .setup((x) => x.getNodesCount(toIModelTokenOptions(options), parentNodeKey)) .returns(async () => result) .verifiable(); - const actualResult = await manager.getChildrenCount(options, parentNodeKey); + const actualResult = await manager.getNodesCount(options, parentNodeKey); expect(actualResult).to.eq(result); rpcRequestsHandlerMock.verifyAll(); }); @@ -314,6 +343,23 @@ describe("PresentationManager", () => { rpcRequestsHandlerMock.verifyAll(); }); + it("requests content set size from proxy when display type is passed in stead of descriptor", async () => { + const keyset = new KeySet(); + const descriptor = createRandomDescriptor(); + const result = faker.random.number(); + const options: ContentRequestOptions = { + imodel: testData.imodelMock.object, + rulesetId: testData.rulesetId, + }; + rpcRequestsHandlerMock + .setup((x) => x.getContentSetSize(toIModelTokenOptions(options), descriptor.displayType, keyset)) + .returns(async () => result) + .verifiable(); + const actualResult = await manager.getContentSetSize(options, descriptor.displayType, keyset); + expect(actualResult).to.eq(result); + rpcRequestsHandlerMock.verifyAll(); + }); + }); describe("getContent", () => { @@ -341,6 +387,81 @@ describe("PresentationManager", () => { descriptorMock.verify((x) => x.rebuildParentship(), moq.Times.once()); }); + it("requests content from proxy when display type is passed in stead of descriptor", async () => { + const keyset = new KeySet(); + const randomDescriptor = createRandomDescriptor(); + const result: Content = { + descriptor: randomDescriptor, + contentSet: [], + }; + const options: Paged> = { + imodel: testData.imodelMock.object, + rulesetId: testData.rulesetId, + paging: testData.pageOptions, + }; + rpcRequestsHandlerMock + .setup((x) => x.getContent(toIModelTokenOptions(options), randomDescriptor.displayType, keyset)) + .returns(async () => result) + .verifiable(); + const actualResult = await manager.getContent(options, randomDescriptor.displayType, keyset); + expect(actualResult).to.eq(result); + rpcRequestsHandlerMock.verifyAll(); + }); + + }); + + describe("getContentAndContentSize", () => { + + it("requests content and contentSize from proxy and rebuilds descriptor parentship", async () => { + const keyset = new KeySet(); + const descriptorMock = moq.Mock.ofInstance(createRandomDescriptor()); + descriptorMock.callBase = true; + const result = { + content: { + descriptor: descriptorMock.object, + contentSet: [], + }, + size: 0, + }; + const options: Paged> = { + imodel: testData.imodelMock.object, + rulesetId: testData.rulesetId, + paging: testData.pageOptions, + }; + rpcRequestsHandlerMock + .setup((x) => x.getContentAndSize(toIModelTokenOptions(options), moq.It.is((d) => deepEqual(d, descriptorMock.object.createStrippedDescriptor())), keyset)) + .returns(async () => result) + .verifiable(); + const actualResult = await manager.getContentAndSize(options, descriptorMock.object, keyset); + expect(actualResult).to.eq(result); + rpcRequestsHandlerMock.verifyAll(); + descriptorMock.verify((x) => x.rebuildParentship(), moq.Times.once()); + }); + + it("requests content and content set size from proxy when display type is passed in stead of descriptor", async () => { + const keyset = new KeySet(); + const randomDescriptor = createRandomDescriptor(); + const result = { + content: { + descriptor: randomDescriptor, + contentSet: [], + }, + size: 0, + }; + const options: Paged> = { + imodel: testData.imodelMock.object, + rulesetId: testData.rulesetId, + paging: testData.pageOptions, + }; + rpcRequestsHandlerMock + .setup((x) => x.getContentAndSize(toIModelTokenOptions(options), randomDescriptor.displayType, keyset)) + .returns(async () => result) + .verifiable(); + const actualResult = await manager.getContentAndSize(options, randomDescriptor.displayType, keyset); + expect(actualResult).to.eq(result); + rpcRequestsHandlerMock.verifyAll(); + }); + }); describe("getDistinctValues", () => { diff --git a/presentation/frontend/src/test/selection/SelectionManager.test.ts b/presentation/frontend/src/test/selection/SelectionManager.test.ts index f7a9e1d..feb9e3e 100644 --- a/presentation/frontend/src/test/selection/SelectionManager.test.ts +++ b/presentation/frontend/src/test/selection/SelectionManager.test.ts @@ -7,10 +7,15 @@ import { expect } from "chai"; import * as sinon from "sinon"; import * as moq from "typemoq"; -import { createRandomECInstanceKey } from "@bentley/presentation-common/lib/test/_helpers/random"; +import { + createRandomECInstanceKey, createRandomSelectionScope, + createRandomEntityProps, +} from "@bentley/presentation-common/lib/test/_helpers/random"; +import { EntityProps } from "@bentley/imodeljs-common"; import { IModelConnection } from "@bentley/imodeljs-frontend"; -import { InstanceKey } from "@bentley/presentation-common"; +import { KeySet, InstanceKey, SelectionScope } from "@bentley/presentation-common"; import { SelectionManager } from "../../presentation-frontend"; +import { SelectionScopesManager } from "../../selection/SelectionScopesManager"; const generateSelection = (): InstanceKey[] => { return [ @@ -25,10 +30,12 @@ describe("SelectionManager", () => { let selectionManager: SelectionManager; let baseSelection: InstanceKey[]; const imodelMock = moq.Mock.ofType(); + const scopesMock = moq.Mock.ofType(); const source: string = "test"; beforeEach(() => { - selectionManager = new SelectionManager(); + selectionManager = new SelectionManager({ scopes: scopesMock.object }); + scopesMock.reset(); imodelMock.reset(); baseSelection = generateSelection(); }); @@ -262,7 +269,7 @@ describe("SelectionManager", () => { }); - describe("removeSelection", () => { + describe("removeFromSelection", () => { it("removes part of the selection", () => { selectionManager.addToSelection(source, imodelMock.object, baseSelection); @@ -337,6 +344,78 @@ describe("SelectionManager", () => { }); + describe("addToSelectionWithSelectionScope", () => { + + let scope: SelectionScope; + let ids: EntityProps[]; + + beforeEach(() => { + scope = createRandomSelectionScope(); + ids = [createRandomEntityProps()]; + scopesMock.setup(async (x) => x.getSelectionScopes(imodelMock.object)).returns(async () => [scope]); + scopesMock.setup(async (x) => x.computeSelection(imodelMock.object, ids, scope)).returns(async () => new KeySet(baseSelection)).verifiable(); + }); + + it("adds scoped selection", async () => { + await selectionManager.addToSelectionWithScope(source, imodelMock.object, ids, scope); + const selectedItemsSet = selectionManager.getSelection(imodelMock.object); + expect(selectedItemsSet.size).to.be.equal(baseSelection.length); + for (const key of baseSelection) { + expect(selectedItemsSet.has(key)).true; + } + scopesMock.verifyAll(); + }); + + }); + + describe("replaceSelectionWithSelectionScope", () => { + + let scope: SelectionScope; + let ids: EntityProps[]; + + beforeEach(() => { + scope = createRandomSelectionScope(); + ids = [createRandomEntityProps()]; + scopesMock.setup(async (x) => x.getSelectionScopes(imodelMock.object)).returns(async () => [scope]); + scopesMock.setup(async (x) => x.computeSelection(imodelMock.object, ids, scope)).returns(async () => new KeySet(baseSelection)).verifiable(); + }); + + it("replaces empty selection with scoped selection", async () => { + await selectionManager.replaceSelectionWithScope(source, imodelMock.object, ids, scope); + const selectedItemsSet = selectionManager.getSelection(imodelMock.object); + expect(selectedItemsSet.size).to.be.equal(baseSelection.length); + for (const key of baseSelection) { + expect(selectedItemsSet.has(key)).true; + } + scopesMock.verifyAll(); + }); + + }); + + describe("removeFromSelectionWithSelectionScope", () => { + + let scope: SelectionScope; + let ids: EntityProps[]; + + beforeEach(() => { + scope = createRandomSelectionScope(); + ids = [createRandomEntityProps()]; + scopesMock.setup(async (x) => x.getSelectionScopes(imodelMock.object)).returns(async () => [scope]); + scopesMock.setup(async (x) => x.computeSelection(imodelMock.object, ids, scope)).returns(async () => new KeySet(baseSelection)).verifiable(); + }); + + it("removes scoped selection", async () => { + const additionalKey = createRandomECInstanceKey(); + selectionManager.addToSelection(source, imodelMock.object, [...baseSelection, additionalKey]); + await selectionManager.removeFromSelectionWithScope(source, imodelMock.object, ids, scope); + const selectedItemsSet = selectionManager.getSelection(imodelMock.object); + expect(selectedItemsSet.size).to.equal(1); + expect(selectedItemsSet.has(additionalKey)).true; + scopesMock.verifyAll(); + }); + + }); + describe("handleEvent", () => { it("fires `selectionChange` event after `addToSelection`, `replaceSelection`, `clearSelection`, `removeFromSelection`", () => { diff --git a/presentation/frontend/src/test/selection/SelectionScopesManager.test.ts b/presentation/frontend/src/test/selection/SelectionScopesManager.test.ts new file mode 100644 index 0000000..005be2b --- /dev/null +++ b/presentation/frontend/src/test/selection/SelectionScopesManager.test.ts @@ -0,0 +1,123 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/* tslint:disable:no-direct-imports */ + +import { expect } from "chai"; +import * as moq from "typemoq"; +import { + createRandomSelectionScope, createRandomEntityProps, +} from "@bentley/presentation-common/lib/test/_helpers/random"; +import { IModelToken } from "@bentley/imodeljs-common"; +import { IModelConnection } from "@bentley/imodeljs-frontend"; +import { RpcRequestsHandler, KeySet } from "@bentley/presentation-common"; +import { SelectionScopesManager, SelectionScopesManagerProps } from "../../selection/SelectionScopesManager"; + +describe("SelectionScopesManager", () => { + + const imodelToken = new IModelToken(); + const imodelMock = moq.Mock.ofType(); + const rpcRequestsHandlerMock = moq.Mock.ofType(); + let manager: SelectionScopesManager | undefined; + let managerProps: SelectionScopesManagerProps; + + const getManager = () => { + if (!manager) + manager = new SelectionScopesManager(managerProps); + return manager; + }; + + beforeEach(() => { + imodelMock.reset(); + imodelMock.setup((x) => x.iModelToken).returns(() => imodelToken); + rpcRequestsHandlerMock.reset(); + manager = undefined; + managerProps = { + rpcRequestsHandler: rpcRequestsHandlerMock.object, + }; + }); + + describe("getSelectionScopes", () => { + + it("forwards request to RpcRequestsHandler", async () => { + const result = [createRandomSelectionScope()]; + rpcRequestsHandlerMock + .setup(async (x) => x.getSelectionScopes(moq.It.isObjectWith({ imodel: imodelToken, locale: undefined }))) + .returns(async () => result) + .verifiable(); + expect(await getManager().getSelectionScopes(imodelMock.object)).to.eq(result); + rpcRequestsHandlerMock.verifyAll(); + }); + + it("passes locale provided by localeProvider when request locale is not set", async () => { + managerProps = { + ...managerProps, + localeProvider: () => "lt", + }; + const result = [createRandomSelectionScope()]; + rpcRequestsHandlerMock + .setup(async (x) => x.getSelectionScopes(moq.It.isObjectWith({ imodel: imodelToken, locale: "lt" }))) + .returns(async () => result) + .verifiable(); + expect(await getManager().getSelectionScopes(imodelMock.object)).to.eq(result); + rpcRequestsHandlerMock.verifyAll(); + }); + + it("passes request locale when set", async () => { + managerProps = { + ...managerProps, + localeProvider: () => "lt", + }; + const result = [createRandomSelectionScope()]; + rpcRequestsHandlerMock + .setup(async (x) => x.getSelectionScopes(moq.It.isObjectWith({ imodel: imodelToken, locale: "de" }))) + .returns(async () => result) + .verifiable(); + expect(await getManager().getSelectionScopes(imodelMock.object, "de")).to.eq(result); + rpcRequestsHandlerMock.verifyAll(); + }); + + }); + + describe("computeSelection", () => { + + it("forwards request to RpcRequestsHandler with scope as SelectionScope", async () => { + const keys = [createRandomEntityProps()]; + const scope = createRandomSelectionScope(); + const result = new KeySet(); + rpcRequestsHandlerMock + .setup(async (x) => x.computeSelection(moq.It.isObjectWith({ imodel: imodelToken }), keys, scope.id)) + .returns(async () => result) + .verifiable(); + expect(await getManager().computeSelection(imodelMock.object, keys, scope)).to.eq(result); + rpcRequestsHandlerMock.verifyAll(); + }); + + it("forwards request to RpcRequestsHandler with scope as SelectionScope id", async () => { + const keys = [createRandomEntityProps()]; + const scope = createRandomSelectionScope(); + const result = new KeySet(); + rpcRequestsHandlerMock + .setup(async (x) => x.computeSelection(moq.It.isObjectWith({ imodel: imodelToken }), keys, scope.id)) + .returns(async () => result) + .verifiable(); + expect(await getManager().computeSelection(imodelMock.object, keys, scope.id)).to.eq(result); + rpcRequestsHandlerMock.verifyAll(); + }); + + it("forwards request to RpcRequestsHandler with keys as a single key", async () => { + const keys = [createRandomEntityProps()]; + const scope = createRandomSelectionScope(); + const result = new KeySet(); + rpcRequestsHandlerMock + .setup(async (x) => x.computeSelection(moq.It.isObjectWith({ imodel: imodelToken }), keys, scope.id)) + .returns(async () => result) + .verifiable(); + expect(await getManager().computeSelection(imodelMock.object, keys[0], scope.id)).to.eq(result); + rpcRequestsHandlerMock.verifyAll(); + }); + + }); + +}); diff --git a/presentation/testing/CHANGELOG.json b/presentation/testing/CHANGELOG.json index 84645b1..14b5983 100644 --- a/presentation/testing/CHANGELOG.json +++ b/presentation/testing/CHANGELOG.json @@ -1,6 +1,33 @@ { "name": "@bentley/presentation-testing", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/presentation-testing_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "Use new buildIModelJsBuild script" + }, + { + "comment": "Added ContentBuilder, that allows testing how a given ruleset constructs content from an imodel." + }, + { + "comment": "Move property definitions to imodeljs-frontend so they could be used by tools to define properties for tool settings." + }, + { + "comment": "upgrade to TypeScript 3.2.2" + } + ] + } + }, + { + "version": "0.188.0", + "tag": "@bentley/presentation-testing_v0.188.0", + "date": "Wed, 16 Jan 2019 16:36:09 GMT", + "comments": {} + }, { "version": "0.187.0", "tag": "@bentley/presentation-testing_v0.187.0", diff --git a/presentation/testing/CHANGELOG.md b/presentation/testing/CHANGELOG.md index 78f0543..c29e009 100644 --- a/presentation/testing/CHANGELOG.md +++ b/presentation/testing/CHANGELOG.md @@ -1,6 +1,21 @@ # Change Log - @bentley/presentation-testing -This log was last generated on Tue, 15 Jan 2019 15:18:59 GMT and should not be manually modified. +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- Use new buildIModelJsBuild script +- Added ContentBuilder, that allows testing how a given ruleset constructs content from an imodel. +- Move property definitions to imodeljs-frontend so they could be used by tools to define properties for tool settings. +- upgrade to TypeScript 3.2.2 + +## 0.188.0 +Wed, 16 Jan 2019 16:36:09 GMT + +*Version update only* ## 0.187.0 Tue, 15 Jan 2019 15:18:59 GMT diff --git a/presentation/testing/LICENSE.md b/presentation/testing/LICENSE.md index 3dd31f5..6502769 100644 --- a/presentation/testing/LICENSE.md +++ b/presentation/testing/LICENSE.md @@ -1,6 +1,6 @@ # MIT License -Copyright © 2018 Bentley Systems, Incorporated. All rights reserved. +Copyright © 2019 Bentley Systems, Incorporated. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/presentation/testing/README.md b/presentation/testing/README.md index a9db394..b2ecf55 100644 --- a/presentation/testing/README.md +++ b/presentation/testing/README.md @@ -1,6 +1,6 @@ # @bentley/presentation-testing -Copyright © 2018 Bentley Systems, Incorporated. All rights reserved. +Copyright © 2019 Bentley Systems, Incorporated. All rights reserved. ## Description diff --git a/presentation/testing/package.json b/presentation/testing/package.json index 897aa86..12c9460 100644 --- a/presentation/testing/package.json +++ b/presentation/testing/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/presentation-testing", - "version": "0.187.0", + "version": "0.189.0", "description": "", "license": "MIT", "repository": { @@ -20,7 +20,7 @@ "main": "lib/presentation-testing.js", "typings": "lib/presentation-testing", "scripts": { - "build": "tsc -b", + "build": "tsc -b && node ./node_modules/@bentley/build-tools/scripts/ignoreargs.js 1>&2", "clean": "rimraf lib package-deps.json", "cover": "nyc npm test", "docs": "node ./node_modules/@bentley/build-tools/scripts/docs.js --source=./src --includes=../../generated-docs/extract --json=../../generated-docs/presentation/presentation-testing/json/file.json --tsIndexFile=presentation-testing.ts --onlyJson %TYPEDOC_THEME%", @@ -32,22 +32,25 @@ "ignore-styles": "^5.0.1" }, "dependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/build-tools": "0.187.0", - "@bentley/imodeljs-backend": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-frontend": "0.187.0", - "@bentley/presentation-backend": "0.187.0", - "@bentley/presentation-common": "0.187.0", - "@bentley/presentation-components": "0.187.0", - "@bentley/presentation-frontend": "0.187.0", - "@bentley/ui-components": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/build-tools": "0.189.0", + "@bentley/imodeljs-backend": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-frontend": "0.189.0", + "@bentley/presentation-backend": "0.189.0", + "@bentley/presentation-common": "0.189.0", + "@bentley/presentation-components": "0.189.0", + "@bentley/presentation-frontend": "0.189.0", + "@bentley/ui-components": "0.189.0", "@types/chai": "^4.1.4", + "@types/chai-as-promised": "^7", "@types/chai-jest-snapshot": "^1.3.0", "@types/jsdom": "^12.2.0", "@types/mocha": "^5.2.5", "@types/rimraf": "^2.0.2", + "@types/sinon": "^5.0.5", "chai": "^4.1.2", + "chai-as-promised": "^7", "chai-jest-snapshot": "^2.0.0", "cross-env": "^5.1.4", "jsdom-global": "3.0.2", @@ -55,9 +58,10 @@ "mocha": "^5.2.0", "nyc": "^13.0.1", "rimraf": "^2.6.2", + "sinon": "^7.1.1", "tslint": "^5.11.0", "typemoq": "^2.1.0", - "typescript": "~3.1.0" + "typescript": "~3.2.2" }, "nyc": { "extends": "./node_modules/@bentley/build-tools/.nycrc", diff --git a/presentation/testing/scripts/copy-test-setup.js b/presentation/testing/scripts/copy-test-setup.js index b4e58ab..e6428cd 100644 --- a/presentation/testing/scripts/copy-test-setup.js +++ b/presentation/testing/scripts/copy-test-setup.js @@ -1,5 +1,5 @@ /*--------------------------------------------------------------------------------------------- -* Copyright (c) 2018 Bentley Systems, Incorporated. All rights reserved. +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ const fs = require("fs"); diff --git a/presentation/testing/scripts/setup-tests.js b/presentation/testing/scripts/setup-tests.js index 724442f..2f671f5 100644 --- a/presentation/testing/scripts/setup-tests.js +++ b/presentation/testing/scripts/setup-tests.js @@ -1,5 +1,5 @@ /*--------------------------------------------------------------------------------------------- -* Copyright (c) 2018 Bentley Systems, Incorporated. All rights reserved. +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ const chai = require("chai"); diff --git a/presentation/testing/src/ContentBuilder.ts b/presentation/testing/src/ContentBuilder.ts new file mode 100644 index 0000000..7e0d983 --- /dev/null +++ b/presentation/testing/src/ContentBuilder.ts @@ -0,0 +1,146 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { IModelConnection, PropertyRecord } from "@bentley/imodeljs-frontend"; +import { InstanceKey } from "../../common/lib/EC"; +import { Presentation } from "@bentley/presentation-frontend"; +import { KeySet, Ruleset, RegisteredRuleset, PageOptions, DefaultContentDisplayTypes, Content } from "@bentley/presentation-common"; +import { ContentBuilder as PresentationContentBuilder, ContentDataProvider } from "@bentley/presentation-components"; +import { using, Id64String } from "@bentley/bentleyjs-core"; + +/** Interface for a data provider, which is used by ContentBuilder */ +export interface IContentBuilderDataProvider { + keys: Readonly; + getContentSetSize: () => Promise; + getContent: (options?: PageOptions) => Promise | undefined>; +} + +/** Property records grouped under a single className */ +export interface ContentBuilderResult { + className: string; + records: PropertyRecord[]; +} + +/** + * A class that constructs content from specified imodel and ruleset. + */ +export class ContentBuilder { + private readonly _iModel: IModelConnection; + private _dataProvider: IContentBuilderDataProvider | undefined; + + /** + * Constructor + * @param iModel The iModel to pull data from + * @param dataProvider Custom data provider that allows mocking, what data ContentBuilder receives + */ + constructor(iModel: IModelConnection, dataProvider?: IContentBuilderDataProvider) { + this._iModel = iModel; + this._dataProvider = dataProvider; + } + + private async doCreateContent(rulesetId: string, instanceKeys: InstanceKey[], displayType: string): Promise { + const keyset = new KeySet(instanceKeys); + + const dataProvider = this._dataProvider ? this._dataProvider : new ContentDataProvider(this._iModel, rulesetId, displayType); + dataProvider.keys = keyset; + + const contentCount = await dataProvider.getContentSetSize(); + const content = await dataProvider.getContent({ size: contentCount }); + + if (!content) + return []; + + const records: PropertyRecord[] = []; + + const sortedFields = content.descriptor.fields.sort((f1, f2) => { + if (f1.name > f2.name) + return -1; + if (f1.name < f2.name) + return 1; + return 0; + }); + + for (const field of sortedFields) { + for (const set of content.contentSet) { + const record = PresentationContentBuilder.createPropertyRecord(field, set); + records.push(record); + } + } + + return records; + } + + /** + * Create a list of property records using the supplied presentation ruleset. + * @param rulesetOrId Either a [[Ruleset]] object or a ruleset id. + * @param instanceKeys Keys of instances that should be queried. + * @param displayType Type of content container display. For example: + * "PropertyPane", "Grid", "List" etc. + */ + public async createContent(rulesetOrId: Ruleset | string, instanceKeys: InstanceKey[], displayType: string = DefaultContentDisplayTypes.PROPERTY_PANE) { + if (typeof rulesetOrId === "string") + return this.doCreateContent(rulesetOrId, instanceKeys, displayType); + + return using(await Presentation.presentation.rulesets().add(rulesetOrId), async (ruleset: RegisteredRuleset) => { + return this.doCreateContent(ruleset.id, instanceKeys, displayType); + }); + } + + private async getECClassNames(): Promise> { + return this._iModel.queryPage(` + SELECT s.Name schemaName, c.Name className FROM meta.ECClassDef c + INNER JOIN meta.ECSchemaDef s ON c.Schema.id = s.ECInstanceId + WHERE c.Modifier <> 1 AND c.Type = 0 + ORDER BY s.Name, c.Name + `); + } + + private async createContentForClasses(rulesetOrId: Ruleset | string, limitInstances: boolean, displayType: string) { + const classNameEntries = await this.getECClassNames(); + + const contents: ContentBuilderResult[] = []; + + for (const nameEntry of classNameEntries) { + // try { + const instanceIds = await this._iModel.queryPage(` + SELECT ECInstanceId FROM ONLY "${nameEntry.schemaName}"."${nameEntry.className}" + ORDER BY ECInstanceId`, undefined, { size: limitInstances ? 1 : 4000 }) as Array<{ id: Id64String }>; + + if (!instanceIds.length) + continue; + + const instanceKeys = instanceIds.map((idEntry) => ({ className: `${nameEntry.schemaName}:${nameEntry.className}`, id: idEntry.id } as InstanceKey)); + + contents.push({ + className: `${nameEntry.schemaName}:${nameEntry.className}`, + records: await this.createContent(rulesetOrId, instanceKeys, displayType), + }); + } + + return contents; + } + + /** + * Create a list of grouped property records using the supplied presentation ruleset. + * Each group includes all of the class instances. + * @param rulesetOrId Either a [[Ruleset]] object or a ruleset id. + * @param displayType Type of content container display. For example: + * "PropertyPane", "Grid", "List" etc. + */ + public async createContentForAllInstances(rulesetOrId: Ruleset | string, displayType: string = DefaultContentDisplayTypes.PROPERTY_PANE) { + return this.createContentForClasses(rulesetOrId, false, displayType); + } + + /** + * Create a list of grouped property records using the supplied presentation ruleset. + * Each group includes at most one class instance. + * @param rulesetOrId Either a [[Ruleset]] object or a ruleset id. + * @param displayType Type of content container display. For example: + * "PropertyPane", "Grid", "List" etc. + */ + public async createContentForInstancePerClass(rulesetOrId: Ruleset | string, displayType: string = DefaultContentDisplayTypes.PROPERTY_PANE) { + return this.createContentForClasses(rulesetOrId, true, displayType); + } +} diff --git a/presentation/testing/src/Helpers.ts b/presentation/testing/src/Helpers.ts index 7acc8fd..b8c31d2 100644 --- a/presentation/testing/src/Helpers.ts +++ b/presentation/testing/src/Helpers.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------------------------------------------- -* Copyright (c) 2018 Bentley Systems, Incorporated. All rights reserved. +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ @@ -11,6 +11,8 @@ import { PresentationRpcInterface } from "@bentley/presentation-common"; import { IModelHost, KnownLocations } from "@bentley/imodeljs-backend"; import { Presentation as PresentationBackend } from "@bentley/presentation-backend"; import { Props as PresentationBackendProps } from "@bentley/presentation-backend/lib/Presentation"; +// tslint:disable-next-line:no-direct-imports +import { Props as PresentationFrontendProps } from "@bentley/presentation-frontend/lib/PresentationManager"; // frontend includes import { StandaloneIModelRpcInterface, @@ -42,7 +44,7 @@ function initializeRpcInterfaces(interfaces: RpcInterfaceDefinition[]) { let isInitialized = false; -export const initialize = (backendProps?: PresentationBackendProps, frontendApp = NoRenderApp) => { +export const initialize = (backendProps?: PresentationBackendProps, frontendProps?: PresentationFrontendProps, frontendApp = NoRenderApp) => { if (isInitialized) return; @@ -58,9 +60,11 @@ export const initialize = (backendProps?: PresentationBackendProps, frontendApp // init frontend frontendApp.startup(); - PresentationFrontend.initialize({ + + const defaultFrontendProps: PresentationFrontendProps = { activeLocale: frontendApp.i18n.languageList()[0], - }); + }; + PresentationFrontend.initialize({ ...defaultFrontendProps, ...frontendProps }); isInitialized = true; }; diff --git a/presentation/testing/src/HierarchyBuilder.ts b/presentation/testing/src/HierarchyBuilder.ts index 66cbd9e..d6831a7 100644 --- a/presentation/testing/src/HierarchyBuilder.ts +++ b/presentation/testing/src/HierarchyBuilder.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------------------------------------------- -* Copyright (c) 2018 Bentley Systems, Incorporated. All rights reserved. +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import { using } from "@bentley/bentleyjs-core"; diff --git a/presentation/testing/src/presentation-testing.ts b/presentation/testing/src/presentation-testing.ts index 09aed86..fedc518 100644 --- a/presentation/testing/src/presentation-testing.ts +++ b/presentation/testing/src/presentation-testing.ts @@ -1,7 +1,8 @@ /*--------------------------------------------------------------------------------------------- -* Copyright (c) 2018 Bentley Systems, Incorporated. All rights reserved. +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ export * from "./HierarchyBuilder"; +export * from "./ContentBuilder"; export * from "./Helpers"; diff --git a/presentation/testing/src/test/ContentBuilder.test.ts b/presentation/testing/src/test/ContentBuilder.test.ts new file mode 100644 index 0000000..10ea0a5 --- /dev/null +++ b/presentation/testing/src/test/ContentBuilder.test.ts @@ -0,0 +1,314 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import * as moq from "typemoq"; +import * as sinon from "sinon"; +import { expect, use } from "chai"; +import * as ChaiAsPromised from "chai-as-promised"; +import { PresentationManager, Presentation } from "@bentley/presentation-frontend"; +import { IModelConnection } from "@bentley/imodeljs-frontend"; +import { Content, ContentJSON, Descriptor, DefaultContentDisplayTypes, KeySet, Ruleset, ValuesDictionary, ItemJSON } from "@bentley/presentation-common"; +import { ContentBuilder, IContentBuilderDataProvider } from "../ContentBuilder"; +// tslint:disable-next-line:no-direct-imports +import RulesetManager from "@bentley/presentation-frontend/lib/RulesetManager"; +import { Id64String } from "@bentley/bentleyjs-core"; + +use(ChaiAsPromised); + +class EmptyDataProvider implements IContentBuilderDataProvider { + // Verifies that given keyset matches a template, otherwise it throws an error + private _keyVerificationFunction: ((keyset: KeySet) => void) | undefined; + + constructor(keyVerificationFunction?: (keyset: KeySet) => void) { + this._keyVerificationFunction = keyVerificationFunction; + } + + private _keyset: KeySet | undefined; + public getContentSetSize = async () => 0; + public getContent = async (): Promise | undefined> => undefined; + + public set keys(keyset: KeySet) { + if (this._keyVerificationFunction) + this._keyVerificationFunction(keyset); + this._keyset = keyset; + } + public get keys() { + return this._keyset ? this._keyset : new KeySet(); + } +} + +function createItemJSON(properties: ValuesDictionary): ItemJSON { + const displayValues = { ...properties }; + + for (const key in displayValues) { + if (displayValues.hasOwnProperty(key)) { + displayValues[key] = null; + } + } + + return { + displayValues, + values: properties, + imageId: "", + label: "Test class", + mergedFieldNames: [], + primaryKeys: Object.keys(properties).map((key) => ({ className: "testClass", id: key })), + }; +} + +async function getContent(items: Array>, descriptor: Descriptor) { + const json: ContentJSON = { + contentSet: items.map((item) => createItemJSON(item)), + descriptor, + }; + + return Content.fromJSON(json)!; +} + +class DataProvider extends EmptyDataProvider { + public descriptor: Descriptor = { + connectionId: "a", + inputKeysHash: "a", + contentOptions: {}, + displayType: "Grid", + selectClasses: [], + fields: [ + { name: "width", type: { typeName: "string" } }, + { name: "title", type: { typeName: "string" } }, + { name: "weight", type: { typeName: "string" } }, + { name: "weight", type: { typeName: "string" } }, // Repeated so that sort function could be tested + ], + contentFlags: 1, + } as any; + + public items = [ + { title: "Item", height: 15, width: 16 }, + { title: "Circle", radius: 13 }, + ]; + + public getContentSetSize = async () => this.items.length; + public getContent = async () => getContent(this.items, this.descriptor); +} + +async function getEmptyContent({ }, descriptor: Readonly) { + const json: ContentJSON = { + contentSet: [], + descriptor, + }; + + return Content.fromJSON(json)!; +} + +interface TestInstance { + schemaName: string; + className: string; + ids: Array<{ id: Id64String }>; +} + +function verifyInstanceKey(instanceKey: [string, Set], instances: TestInstance[]) { + const className = instanceKey[0]; + const ids = Array.from(instanceKey[1].values()); + for (const instance of instances) { + if (`${instance.schemaName}:${instance.className}` === className) { + for (const idEntry of instance.ids) { + if (!ids.includes(idEntry.id)) { + throw new Error(`Wrong id provided - '${idEntry.id}'`); + } + } + return; + } + } + throw new Error(`Wrong className provided - '${className}'`); +} + +async function executeQuery(query: string, instances: TestInstance[]): Promise { + if (query.includes("SELECT s.Name")) { + return instances as Array<{ schemaName: string, className: string }>; // ids are returned as well, but that shouldn't be a problem + } + + for (const entry of instances) { + if (query.includes(`"${entry.schemaName}"."${entry.className}"`)) { + return entry.ids; + } + } + + return []; +} + +function verifyKeyset(keyset: KeySet, testInstances: TestInstance[], verificationSpy: sinon.SinonSpy) { + verificationSpy(); + for (const entry of keyset.instanceKeys.entries()) { + verifyInstanceKey(entry, testInstances); + } +} + +describe("ContentBuilder", () => { + const imodelMock = moq.Mock.ofType(); + + describe("createContent", () => { + const presentationManagerMock = moq.Mock.ofType(); + const rulesetMock = moq.Mock.ofType(); + const rulesetManager = new RulesetManager(); + + before(() => { + rulesetMock.setup((ruleset) => ruleset.id).returns(() => "1"); + }); + + beforeEach(() => { + presentationManagerMock.reset(); + presentationManagerMock.setup((manager) => manager.rulesets()).returns(() => rulesetManager); + presentationManagerMock.setup(async (manager) => manager.getContent(moq.It.isAny(), moq.It.isAny(), moq.It.isAny())).returns(getEmptyContent); + Presentation.presentation = presentationManagerMock.object; + }); + + it("returns empty records when there is no content returned from presentation", async () => { + const builder = new ContentBuilder(imodelMock.object); + let content = await builder.createContent("1", []); + expect(content).to.be.empty; + + presentationManagerMock.verify((manager) => manager.rulesets(), moq.Times.never()); + content = await builder.createContent(rulesetMock.object, []); + presentationManagerMock.verify((manager) => manager.rulesets(), moq.Times.once()); + expect(content).to.be.empty; + + content = await builder.createContent("1", [], DefaultContentDisplayTypes.LIST); + expect(content).to.be.empty; + }); + + it("returns empty records when there is no content in the supplied data provider", async () => { + const builder = new ContentBuilder(imodelMock.object, new EmptyDataProvider()); + const content = await builder.createContent("1", []); + expect(content).to.be.empty; + }); + + it("returns correct records when there is content in the supplied data provider", async () => { + const dataProvider = new DataProvider(); + const builder = new ContentBuilder(imodelMock.object, dataProvider); + const content = await builder.createContent("1", []); + expect(content.length).to.equal(dataProvider.items.length * dataProvider.descriptor.fields.length); + }); + }); + + describe("createContentForAllClasses", () => { + const testInstances: TestInstance[] = [ + { + className: "Class1", + schemaName: "Schema1", + ids: [{ id: "0x2" }, { id: "0x3" }], + }, + { + className: "Class2", + schemaName: "Schema2", + ids: [{ id: "0x5" }, { id: "0x6" }], + }, + ]; + + before(() => { + imodelMock.reset(); + imodelMock.setup(async (imodel) => imodel.queryPage(moq.It.isAny(), moq.It.isAny(), moq.It.isAny())).returns(async (query) => executeQuery(query, testInstances)); + }); + + it("returns all required instances with empty records", async () => { + const verificationSpy = sinon.spy(); + + const builder = new ContentBuilder( + imodelMock.object, + new EmptyDataProvider((keyset: KeySet) => verifyKeyset(keyset, testInstances, verificationSpy))); + + const content = await builder.createContentForAllInstances("1"); + + expect(content.length).to.equal(2); + + expect(content.find((c) => c.className === "Schema1:Class1")).to.not.be.undefined; + expect(content.find((c) => c.className === "Schema2:Class2")).to.not.be.undefined; + + expect(content[0].records).to.be.empty; + expect(content[1].records).to.be.empty; + + expect(verificationSpy.calledTwice).to.be.true; + }); + }); + + describe("createContentForInstancePerClass", () => { + context("test instances have ids", () => { + const testInstances: TestInstance[] = [ + { + className: "Class1", + schemaName: "Schema1", + ids: [{ id: "0x1" }], + }, + { + className: "Class2", + schemaName: "Schema2", + ids: [{ id: "0x9" }], + }, + ]; + + it("returns all required instances with empty records", async () => { + imodelMock.reset(); + imodelMock.setup(async (imodel) => imodel.queryPage(moq.It.isAny(), moq.It.isAny(), moq.It.isAny())).returns(async (query) => executeQuery(query, testInstances)); + + const verificationSpy = sinon.spy(); + + const builder = new ContentBuilder( + imodelMock.object, + new EmptyDataProvider((keyset: KeySet) => verifyKeyset(keyset, testInstances, verificationSpy))); + + const content = await builder.createContentForInstancePerClass("1"); + + expect(content.length).to.equal(2); + + expect(content.find((c) => c.className === "Schema1:Class1")).to.not.be.undefined; + expect(content.find((c) => c.className === "Schema2:Class2")).to.not.be.undefined; + + expect(content[0].records).to.be.empty; + expect(content[1].records).to.be.empty; + + expect(verificationSpy.calledTwice).to.be.true; + }); + + it("throws when id query throws an unexpected error", async () => { + function executeQueryAndThrow(query: string, instances: TestInstance[]) { + if (query.includes("SELECT s.Name")) { + return instances as Array<{ schemaName: string, className: string }>; + } + throw new Error("Test error"); + } + + imodelMock.reset(); + imodelMock.setup(async (imodel) => imodel.queryPage(moq.It.isAny(), moq.It.isAny(), moq.It.isAny())).returns(async (query) => executeQueryAndThrow(query, testInstances)); + + const verificationSpy = sinon.spy(); + + const builder = new ContentBuilder( + imodelMock.object, + new EmptyDataProvider((keyset: KeySet) => verifyKeyset(keyset, testInstances, verificationSpy))); + + await expect(builder.createContentForInstancePerClass("1")).to.be.rejectedWith("Test error"); + }); + }); + + context("test instances have no ids", () => { + const testInstances: TestInstance[] = [{ className: "Class1", schemaName: "Schema1", ids: [] }]; + + before(() => { + imodelMock.reset(); + imodelMock.setup(async (imodel) => imodel.queryPage(moq.It.isAny(), moq.It.isAny(), moq.It.isAny())).returns(async (query) => executeQuery(query, testInstances)); + }); + + it("returns an empty list", async () => { + const verificationSpy = sinon.spy(); + + const builder = new ContentBuilder( + imodelMock.object, + new EmptyDataProvider((keyset: KeySet) => verifyKeyset(keyset, testInstances, verificationSpy))); + + const content = await builder.createContentForInstancePerClass("1"); + + expect(content).to.be.empty; + expect(verificationSpy.notCalled).to.be.true; + }); + }); + }); +}); diff --git a/presentation/testing/src/test/HierarchyBuilder.test.ts b/presentation/testing/src/test/HierarchyBuilder.test.ts index 86384d6..922afa5 100644 --- a/presentation/testing/src/test/HierarchyBuilder.test.ts +++ b/presentation/testing/src/test/HierarchyBuilder.test.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------------------------------------------- -* Copyright (c) 2018 Bentley Systems, Incorporated. All rights reserved. +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import * as moq from "typemoq"; @@ -55,7 +55,7 @@ describe("HierarchyBuilder", () => { context("without data", () => { beforeEach(() => { Presentation.presentation = presentationManagerMock.object; - presentationManagerMock.setup(async (manager) => manager.getRootNodes(moq.It.isAny())).returns(async () => []); + presentationManagerMock.setup(async (manager) => manager.getNodes(moq.It.isAny(), undefined)).returns(async () => []); }); it("returns empty list when rulesetId is given", async () => { @@ -73,8 +73,8 @@ describe("HierarchyBuilder", () => { context("with data", () => { beforeEach(() => { - presentationManagerMock.setup(async (manager) => manager.getRootNodes(moq.It.isAny())).returns(getRootNodes); - presentationManagerMock.setup(async (manager) => manager.getChildren(moq.It.isAny(), moq.It.isAny())).returns(getChildrenNodes); + presentationManagerMock.setup(async (manager) => manager.getNodes(moq.It.isAny(), undefined)).returns(getRootNodes); + presentationManagerMock.setup(async (manager) => manager.getNodes(moq.It.isAny(), moq.It.isAny())).returns(getChildrenNodes); Presentation.presentation = presentationManagerMock.object; }); diff --git a/rush.json b/rush.json index 3c85521..10fd345 100644 --- a/rush.json +++ b/rush.json @@ -1,6 +1,6 @@ { "rushVersion": "5.5.4", - "pnpmVersion": "2.17.3", + "pnpmVersion": "2.25.6", "nodeSupportedVersionRange": ">=10.6.0 <11.0.0", "projectFolderMinDepth": 2, "ensureConsistentVersions": true, @@ -101,6 +101,13 @@ "shouldPublish": true, "versionPolicyName": "prerelease-monorepo-lockStep" }, + { + "packageName": "@bentley/logger-config", + "projectFolder": "core/logger-config", + "reviewCategory": "backend", + "shouldPublish": true, + "versionPolicyName": "prerelease-monorepo-lockStep" + }, { "packageName": "@bentley/presentation-common", "projectFolder": "presentation/common", @@ -257,11 +264,23 @@ "reviewCategory": "internal", "shouldPublish": false }, + { + "packageName": "@bentley/plugin-markup", + "projectFolder": "plugins/markup", + "reviewCategory": "frontend", + "shouldPublish": false + }, { "packageName": "agent-test-app", "projectFolder": "test-apps/agent-test-app", "reviewCategory": "internal", "shouldPublish": false + }, + { + "packageName": "webworker-test-app", + "projectFolder": "test-apps/webworker-test-app", + "reviewCategory": "internal", + "shouldPublish": false } ] } \ No newline at end of file diff --git a/test-apps/agent-test-app/package.json b/test-apps/agent-test-app/package.json index c50bc0c..d639df0 100644 --- a/test-apps/agent-test-app/package.json +++ b/test-apps/agent-test-app/package.json @@ -3,7 +3,7 @@ "description": "Demonstrates using iModel.js to create an agent", "license": "MIT", "scripts": { - "build": "tsc --rootDir ./src 1>&2", + "build": "tsc --rootDir ./src 1>&2 && node ./node_modules/@bentley/build-tools/scripts/ignoreargs.js 1>&2", "clean": "rimraf lib", "docs": "", "lint": "tslint --project . 1>&2", @@ -21,12 +21,12 @@ "url": "http://www.bentley.com" }, "dependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/geometry-core": "0.187.0", - "@bentley/imodeljs-backend": "0.187.0", - "@bentley/imodeljs-clients": "0.187.0", - "@bentley/imodeljs-clients-backend": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/imodeljs-backend": "0.189.0", + "@bentley/imodeljs-clients": "0.189.0", + "@bentley/imodeljs-clients-backend": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", "body-parser": "^1.18.2", "cookie-parser": "^1.4.3", "express": "^4.16.3", @@ -38,18 +38,17 @@ "react": "^16.4.2" }, "devDependencies": { - "@bentley/config-loader": "0.187.0", - "@bentley/build-tools": "0.187.0", + "@bentley/config-loader": "0.189.0", + "@bentley/build-tools": "0.189.0", "@types/body-parser": "^1.17.0", "@types/chai": "^4.1.4", - "@types/express": "^4.11.1", + "@types/express": "^4.16.1", "@types/express-session": "^1.15.11", "@types/minimist": "^1.2.0", "@types/mocha": "^5.2.5", - "@types/node": "10.10.3", + "@types/node": "10.12.18", "@types/passport": "^0.4.6", "chai": "^4.1.2", - "cpx": "^1.5.0", "debug": "^2.6.9", "delay-cli": "^1.1.0", "mocha": "^5.2.0", @@ -57,7 +56,7 @@ "nyc": "^13.0.1", "tslint": "^5.11.0", "typemoq": "^2.1.0", - "typescript": "~3.1.0" + "typescript": "~3.2.2" }, "nyc": { "include": [ diff --git a/test-apps/agent-test-app/src/changeSetUtility/ChangesetGenerationHarness.ts b/test-apps/agent-test-app/src/changeSetUtility/ChangesetGenerationHarness.ts index 83a8af5..7a05d73 100644 --- a/test-apps/agent-test-app/src/changeSetUtility/ChangesetGenerationHarness.ts +++ b/test-apps/agent-test-app/src/changeSetUtility/ChangesetGenerationHarness.ts @@ -9,15 +9,15 @@ import { ChangesetGenerator } from "./ChangesetGenerator"; import { TestChangesetSequence } from "./TestChangesetSequence"; import { Id64String, Logger, LogLevel, ActivityLoggingContext } from "@bentley/bentleyjs-core"; import { CategorySelector, DisplayStyle3d, IModelDb, IModelHost, IModelHostConfiguration, KeepBriefcase, ModelSelector, OrthographicViewDefinition, PhysicalModel, SpatialCategory } from "@bentley/imodeljs-backend"; -import { IModel, CodeScopeSpec, ColorDef, AxisAlignedBox3d } from "@bentley/imodeljs-common"; -import { Point3d, Range3d } from "@bentley/geometry-core"; +import { IModel, CodeScopeSpec, ColorDef } from "@bentley/imodeljs-common"; +import { Range3d } from "@bentley/geometry-core"; import { AccessToken } from "@bentley/imodeljs-clients"; import * as fs from "fs"; import * as path from "path"; const actx = new ActivityLoggingContext(""); -/** Harness used to facitilitate changeset generation */ +/** Harness used to facilitate changeset generation */ export class ChangesetGenerationHarness { private _iModelDbHandler: IModelDbHandler; private _localIModelDbPath: string; @@ -83,7 +83,7 @@ export class ChangesetGenerationHarness { const viewRange = new Range3d(0, 0, 0, 50, 50, 50); OrthographicViewDefinition.insert(this._iModelDb, definitionModelId, viewName, modelSelectorId, categorySelectorId, displayStyleId, viewRange); - this._iModelDb.updateProjectExtents(new AxisAlignedBox3d(new Point3d(-1000, -1000, -1000), new Point3d(1000, 1000, 1000))); + this._iModelDb.updateProjectExtents(new Range3d(-1000, -1000, -1000, 1000, 1000, 1000)); this._iModelDb.saveChanges("Setup new iModel"); this._iModelDb.closeStandalone(); this._iModelDb = undefined; diff --git a/test-apps/agent-test-app/src/changeSetUtility/HubUtility.ts b/test-apps/agent-test-app/src/changeSetUtility/HubUtility.ts index 7d48067..c8acf5e 100644 --- a/test-apps/agent-test-app/src/changeSetUtility/HubUtility.ts +++ b/test-apps/agent-test-app/src/changeSetUtility/HubUtility.ts @@ -5,8 +5,9 @@ import { ChangeSetUtilityConfig } from "./ChangeSetUtilityConfig"; import { AuthorizationToken, AccessToken, ImsActiveSecureTokenClient, ImsDelegationSecureTokenClient, ConnectClient, IModelHubClient, - AzureFileHandler, Project, IModelQuery, HubIModel, Version, + Project, IModelQuery, HubIModel, Version, } from "@bentley/imodeljs-clients"; +import { AzureFileHandler } from "@bentley/imodeljs-clients-backend"; import { Logger, assert, ActivityLoggingContext } from "@bentley/bentleyjs-core"; import { IModelVersion } from "@bentley/imodeljs-common"; import * as path from "path"; diff --git a/test-apps/analysis-importer/package.json b/test-apps/analysis-importer/package.json index b0a8044..01d577a 100644 --- a/test-apps/analysis-importer/package.json +++ b/test-apps/analysis-importer/package.json @@ -4,7 +4,7 @@ "private": true, "license": "MIT", "scripts": { - "build": "npm run build-code && npm run extract-assets", + "build": "npm run build-code && npm run extract-assets && node ./node_modules/@bentley/build-tools/scripts/ignoreargs.js 1>&2", "lint": "tslint -p . 1>&2", "build-code": "tsc 1>&2", "clean": "rimraf lib package-deps.json ../../generated-docs", @@ -15,19 +15,19 @@ }, "repository": {}, "dependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/geometry-core": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-backend": "0.187.0", - "@bentley/imodeljs-clients": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-backend": "0.189.0", + "@bentley/imodeljs-clients": "0.189.0", "body-parser": "^1.18.2" }, "devDependencies": { - "@bentley/webpack-tools": "0.187.0", - "@bentley/build-tools": "0.187.0", + "@bentley/webpack-tools": "0.189.0", + "@bentley/build-tools": "0.189.0", "@types/body-parser": "^1.17.0", - "@types/express": "^4.11.1", - "@types/node": "10.10.3", + "@types/express": "^4.16.1", + "@types/node": "10.12.18", "cpx": "^1.5.0", "child_process": "^1.0.2", "express": "^4.16.3", @@ -36,6 +36,6 @@ "popper.js": "^1.14.4", "rimraf": "^2.6.2", "tslint": "^5.11.0", - "typescript": "~3.1.0" + "typescript": "~3.2.2" } } diff --git a/test-apps/analysis-importer/src/AnalysisImporter.ts b/test-apps/analysis-importer/src/AnalysisImporter.ts index 05d69bf..e523cfb 100644 --- a/test-apps/analysis-importer/src/AnalysisImporter.ts +++ b/test-apps/analysis-importer/src/AnalysisImporter.ts @@ -180,7 +180,7 @@ export class AnalysisImporter { const linearHeightData = [], linearSlopeData = [], linearDisplacementData = []; for (let j = 0; j < polyface.data.point.length; j++) { - const point = polyface.data.point.getPoint3dAt(j); + const point = polyface.data.point.getPoint3dAtUncheckedPointIndex(j); const theta = Angle.pi2Radians * (point.x - waveCenter) / waveLength; const height = waveHeight * Math.sin(theta); const slope = Math.abs(Math.cos(theta)); diff --git a/test-apps/display-performance-test-app/package.json b/test-apps/display-performance-test-app/package.json index 6379aca..9e96b85 100644 --- a/test-apps/display-performance-test-app/package.json +++ b/test-apps/display-performance-test-app/package.json @@ -12,51 +12,76 @@ }, "private": true, "scripts": { - "build": "tsc 1>&2 && npm run copy:frontendpublic && npm run copy:ourpublic && npm run webpack:frontend", + "build": "node ./node_modules/@bentley/webpack-tools/bin/buildIModelJsModule", "clean": "rimraf lib package-deps.json", - "copy:frontendpublic": "cpx \"../../core/frontend/src/public/**/*\" ./lib/webresources", - "copy:ourpublic": "cpx \"public/**/*\" ./lib/webresources", "docs": "", "lint": "tslint --project . 1>&2", "mobile": "tsc 1>&2 && webpack --config mobile.backend.webpack.config.js 1>&2 && webpack --config mobile.frontend.webpack.config.js 1>&2 && cpx \"public/**/*\" ./lib/mobile/public && cpx \"assets/**/*\" ./lib/mobile/assets ", "start": "npm run start:electron", - "start:dev-cors-proxy-server": "node ./node_modules/@bentley/dev-cors-proxy-server/server.js", "start:electron": "electron ./lib/backend/ElectronMain.js", - "start:web": "node ./lib/backend/WebMain.js", + "start:web": "node lib/backend/WebMain.js", + "test:chrome": "node ./lib/common/npmCommands.js chrome", + "test:edge": "node ./lib/common/npmCommands.js edge", + "test:firefox": "node ./lib/common/npmCommands.js firefox", "test": "", - "cover": "", - "webpack:frontend": "webpack --config webpack.config.js 1>&2" + "cover": "" + }, + "iModelJs": { + "buildModule": { + "type": "application", + "sourceResources": [ + { + "source": "./public/**/*", + "dest": "./lib/webresources" + } + ], + "webpack": { + "dest": "./lib/webresources", + "entry": "./lib/frontend/DisplayPerformanceTestApp.js", + "bundleName": "main", + "htmlTemplate": "./src/frontend/index.html" + }, + "makeConfig": { + "dest": "./lib/webresources/config.json", + "filter": "^imjs_" + } + } }, "repository": {}, "dependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/electron-manager": "0.187.0", - "@bentley/geometry-core": "0.187.0", - "@bentley/imodeljs-clients": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-frontend": "0.187.0", - "@bentley/imodeljs-backend": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/electron-manager": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/imodeljs-clients": "0.189.0", + "@bentley/imodeljs-clients-backend": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-i18n": "0.189.0", + "@bentley/imodeljs-quantity": "0.189.0", + "@bentley/imodeljs-frontend": "0.189.0", + "@bentley/imodeljs-backend": "0.189.0", + "@bentley/webpack-tools": "0.189.0", "body-parser": "^1.18.2", "tooltip.js": "^1.2.0" }, "devDependencies": { - "@bentley/config-loader": "0.187.0", - "@bentley/build-tools": "0.187.0", + "@bentley/config-loader": "0.189.0", + "@bentley/build-tools": "0.189.0", + "@bentley/webpack-tools": "0.189.0", + "@bentley/imodeljs-webserver": "0.189.0", "@types/body-parser": "^1.17.0", - "@types/express": "^4.11.1", - "@types/node": "10.10.3", - "cpx": "^1.5.0", + "@types/express": "^4.16.1", + "@types/node": "10.12.18", "child_process": "^1.0.2", + "chrome-launcher": "^0.10.5", "electron": "^4.0.1", "express": "^4.16.3", "node-glob": "^1.2.0", + "npm-run-all": "^4.1.5", "null-loader": "^0.1.1", "popper.js": "^1.14.4", "rimraf": "^2.6.2", - "source-map-loader": "^0.2.3", "tslint": "^5.11.0", - "typescript": "~3.1.0", - "webpack": "^4.20.2" + "typescript": "~3.2.2" }, "homepage": "http://localhost:3000/" } diff --git a/test-apps/display-performance-test-app/public/connect-configuration.json b/test-apps/display-performance-test-app/public/connect-configuration.json new file mode 100644 index 0000000..a5846bd --- /dev/null +++ b/test-apps/display-performance-test-app/public/connect-configuration.json @@ -0,0 +1,4 @@ +{ + "projectName": "Cross Product Workflows", + "iModelName": "17August.TS.ABD.Sheet" +} \ No newline at end of file diff --git a/test-apps/display-performance-test-app/public/index.html b/test-apps/display-performance-test-app/public/index.html deleted file mode 100644 index 5397066..0000000 --- a/test-apps/display-performance-test-app/public/index.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - Display Performance Test App - - - - - -

-
-
-
- - - \ No newline at end of file diff --git a/test-apps/display-performance-test-app/src/backend/DefaultConfig.json b/test-apps/display-performance-test-app/src/backend/DefaultConfig.json index 8955b7b..3d06e68 100644 --- a/test-apps/display-performance-test-app/src/backend/DefaultConfig.json +++ b/test-apps/display-performance-test-app/src/backend/DefaultConfig.json @@ -1,18 +1,16 @@ { - "outputName": "performanceResults.csv", - "outputPath": "D:\\output\\performanceData\\", - "iModelLocation": "D:\\models\\TimingTests\\", + "outputName": "_PerformanceResults.csv", + "outputPath": ".\\performanceResults\\", + "iModelHubProject": "DisplayPerformanceTest", "view": { "width": 1000, "height": 1000 }, "testSet": [ { - "iModelName": "TimingTest_01.ibim", + "iModelName": "TimingTest_01.bim", "testName": "Render Mode Images", "testType": "both", - "outputName": "renderModePerformanceResults.csv", - "outputPath": "D:\\output\\performanceData\\renderModes", "viewName": "V0", "tests": [ { @@ -149,14 +147,163 @@ } ] }, + { + "testName": "Light Images", + "testType": "both", + "iModelName": "TimingTest_01.bim", + "viewFlags": { + "renderMode": "SmoothShade", + "dimensions": true, + "patterns": true, + "weights": true, + "styles": true, + "transparency": true, + "fill": true, + "textures": true, + "materials": true, + "visibleEdges": false, + "hiddenEdges": false, + "sourceLights": false, + "cameraLights": false, + "solarLights": false, + "shadows": false, + "clipVolume": true, + "constructions": false, + "monochrome": false, + "noGeometryMap": false, + "backgroundMap": false, + "hLineMaterialColors": false, + "edgeMask": 0 + }, + "tests": [ + { + "viewName": "V0" + }, + { + "viewName": "V0", + "viewFlags": { + "sourceLights": true, + "solarLights": false + } + }, + { + "viewName": "V0", + "viewFlags": { + "sourceLights": false, + "solarLights": true + } + }, + { + "viewName": "V0", + "viewFlags": { + "sourceLights": true, + "solarLights": true + } + }, + { + "viewName": "V1" + }, + { + "viewName": "V1", + "viewFlags": { + "sourceLights": true, + "solarLights": false + } + }, + { + "viewName": "V1", + "viewFlags": { + "sourceLights": false, + "solarLights": true + } + }, + { + "viewName": "V1", + "viewFlags": { + "sourceLights": true, + "solarLights": true + } + }, + { + "viewName": "V2" + }, + { + "viewName": "V2", + "viewFlags": { + "sourceLights": true, + "solarLights": false + } + }, + { + "viewName": "V2", + "viewFlags": { + "sourceLights": false, + "solarLights": true + } + }, + { + "viewName": "V2", + "viewFlags": { + "sourceLights": true, + "solarLights": true + } + }, + { + "viewName": "V3" + }, + { + "viewName": "V3", + "viewFlags": { + "sourceLights": true, + "solarLights": false + } + }, + { + "viewName": "V3", + "viewFlags": { + "sourceLights": false, + "solarLights": true + } + }, + { + "viewName": "V3", + "viewFlags": { + "sourceLights": true, + "solarLights": true + } + }, + { + "viewName": "V4" + }, + { + "viewName": "V4", + "viewFlags": { + "sourceLights": true, + "solarLights": false + } + }, + { + "viewName": "V4", + "viewFlags": { + "sourceLights": false, + "solarLights": true + } + }, + { + "viewName": "V4", + "viewFlags": { + "sourceLights": true, + "solarLights": true + } + } + ] + }, { "testName": "Edge Images", "testType": "both", - "outputName": "edgePerformanceResults.csv", - "outputPath": "D:\\output\\performanceData\\edges\\", "tests": [ { - "iModelName": "TimingTest_01.ibim", + "iModelName": "TimingTest_01.bim", "viewName": "V2", "viewFlags": { "renderMode": "SmoothShade", @@ -184,7 +331,7 @@ } }, { - "iModelName": "TimingTest_01.ibim", + "iModelName": "TimingTest_01.bim", "viewName": "V2", "viewFlags": { "renderMode": "SmoothShade", @@ -212,7 +359,7 @@ } }, { - "iModelName": "TimingTest_01.ibim", + "iModelName": "TimingTest_01.bim", "viewName": "V2", "viewFlags": { "renderMode": "SmoothShade", @@ -240,7 +387,11 @@ } }, { - "iModelName": "TimingTest_02_TranslucencyAll.ibim", + "iModelName": "TimingTest_02_TranslucencyAll.bim", + "viewName": "W0" + }, + { + "iModelName": "TimingTest_02_TranslucencyAll.bim", "viewName": "W0", "viewFlags": { "renderMode": "SmoothShade", @@ -268,7 +419,7 @@ } }, { - "iModelName": "TimingTest_02_TranslucencyAll.ibim", + "iModelName": "TimingTest_02_TranslucencyAll.bim", "viewName": "W0", "viewFlags": { "renderMode": "SmoothShade", @@ -296,7 +447,11 @@ } }, { - "iModelName": "TimingTest_02_TranslucencyAllButOne.ibim", + "iModelName": "TimingTest_02_TranslucencyAllButOne.bim", + "viewName": "W0" + }, + { + "iModelName": "TimingTest_02_TranslucencyAllButOne.bim", "viewName": "W0", "viewFlags": { "renderMode": "SmoothShade", @@ -324,7 +479,7 @@ } }, { - "iModelName": "TimingTest_02_TranslucencyAllButOne.ibim", + "iModelName": "TimingTest_02_TranslucencyAllButOne.bim", "viewName": "W0", "viewFlags": { "renderMode": "SmoothShade", @@ -352,7 +507,11 @@ } }, { - "iModelName": "TimingTest_02_TranslucencyNone.ibim", + "iModelName": "TimingTest_02_TranslucencyNone.bim", + "viewName": "W0" + }, + { + "iModelName": "TimingTest_02_TranslucencyNone.bim", "viewName": "W0", "viewFlags": { "renderMode": "SmoothShade", @@ -380,7 +539,7 @@ } }, { - "iModelName": "TimingTest_02_TranslucencyNone.ibim", + "iModelName": "TimingTest_02_TranslucencyNone.bim", "viewName": "W0", "viewFlags": { "renderMode": "SmoothShade", @@ -408,7 +567,11 @@ } }, { - "iModelName": "TimingTest_02_TranslucencyOne.ibim", + "iModelName": "TimingTest_02_TranslucencyOne.bim", + "viewName": "W0" + }, + { + "iModelName": "TimingTest_02_TranslucencyOne.bim", "viewName": "W0", "viewFlags": { "renderMode": "SmoothShade", @@ -436,7 +599,7 @@ } }, { - "iModelName": "TimingTest_02_TranslucencyOne.ibim", + "iModelName": "TimingTest_02_TranslucencyOne.bim", "viewName": "W0", "viewFlags": { "renderMode": "SmoothShade", @@ -465,233 +628,93 @@ } ] }, - { - "testName": "Light Images", - "testType": "both", - "iModelName": "TimingTest_01.ibim", - "outputName": "lightPerformanceResults.csv", - "outputPath": "D:/output/performanceData/light/", - "viewFlags": { - "renderMode": "SmoothShade", - "dimensions": true, - "patterns": true, - "weights": true, - "styles": true, - "transparency": true, - "fill": true, - "textures": true, - "materials": true, - "visibleEdges": false, - "hiddenEdges": false, - "sourceLights": false, - "cameraLights": false, - "solarLights": false, - "shadows": false, - "clipVolume": true, - "constructions": false, - "monochrome": false, - "noGeometryMap": false, - "backgroundMap": false, - "hLineMaterialColors": false, - "edgeMask": 0 - }, - "tests": [ - { - "viewName": "V0" - }, - { - "viewName": "V0", - "viewFlags": { - "sourceLights": true - } - }, - { - "viewName": "V0", - "viewFlags": { - "sourceLights": true - } - }, - { - "viewName": "V0", - "viewFlags": { - "solarLights": true - } - }, - { - "viewName": "V1" - }, - { - "viewName": "V1", - "viewFlags": { - "sourceLights": true - } - }, - { - "viewName": "V1", - "viewFlags": { - "sourceLights": true - } - }, - { - "viewName": "V1", - "viewFlags": { - "solarLights": true - } - }, - { - "viewName": "V2" - }, - { - "viewName": "V2", - "viewFlags": { - "sourceLights": true - } - }, - { - "viewName": "V2", - "viewFlags": { - "sourceLights": true - } - }, - { - "viewName": "V2", - "viewFlags": { - "solarLights": true - } - }, - { - "viewName": "V3" - }, - { - "viewName": "V3", - "viewFlags": { - "sourceLights": true - } - }, - { - "viewName": "V3", - "viewFlags": { - "sourceLights": true - } - }, - { - "viewName": "V3", - "viewFlags": { - "solarLights": true - } - }, - { - "viewName": "V4" - }, - { - "viewName": "V4", - "viewFlags": { - "sourceLights": true - } - }, - { - "viewName": "V4", - "viewFlags": { - "sourceLights": true - } - }, - { - "viewName": "V4", - "viewFlags": { - "solarLights": true - } - } - ] - }, { "testName": "Text", "testType": "both", - "outputName": "textPerformanceResults.csv", - "outputPath": "D:/output/performanceData/text/", "tests": [ { - "iModelName": "SmallTextTest.ibim", + "iModelName": "SmallTextTest.bim", "viewName": "001" }, { - "iModelName": "SmallTextTest.ibim", + "iModelName": "SmallTextTest.bim", "viewName": "002" }, { - "iModelName": "SmallTextTest.ibim", + "iModelName": "SmallTextTest.bim", "viewName": "004" }, { - "iModelName": "SmallTextTest.ibim", + "iModelName": "SmallTextTest.bim", "viewName": "008" }, { - "iModelName": "SmallTextTest.ibim", + "iModelName": "SmallTextTest.bim", "viewName": "016" }, { - "iModelName": "SmallTextTest.ibim", + "iModelName": "SmallTextTest.bim", "viewName": "032" }, { - "iModelName": "SmallTextTest.ibim", + "iModelName": "SmallTextTest.bim", "viewName": "064" }, { - "iModelName": "SmallTextTest.ibim", + "iModelName": "SmallTextTest.bim", "viewName": "096" }, { - "iModelName": "SmallTextTest.ibim", + "iModelName": "SmallTextTest.bim", "viewName": "127" }, { - "iModelName": "LargeTextTest.ibim", + "iModelName": "LargeTextTest.bim", "viewName": "001" }, { - "iModelName": "LargeTextTest.ibim", + "iModelName": "LargeTextTest.bim", "viewName": "005" }, { - "iModelName": "LargeTextTest.ibim", + "iModelName": "LargeTextTest.bim", "viewName": "025" }, { - "iModelName": "LargeTextTest.ibim", + "iModelName": "LargeTextTest.bim", "viewName": "125" }, { - "iModelName": "LargeTextTest.ibim", + "iModelName": "LargeTextTest.bim", "viewName": "625" }, { - "iModelName": "LargeTextTest.ibim", + "iModelName": "LargeTextTest.bim", "viewName": "781" }, { - "iModelName": "LargeTextTest.ibim", + "iModelName": "LargeTextTest.bim", "viewName": "781", "viewFlags": { "renderMode": "Wireframe" } }, { - "iModelName": "LargeTextTest.ibim", + "iModelName": "LargeTextTest.bim", "viewName": "781", "viewFlags": { "renderMode": "HiddenLine" } }, { - "iModelName": "LargeTextTest.ibim", + "iModelName": "LargeTextTest.bim", "viewName": "781", "viewFlags": { "renderMode": "SolidFill" } }, { - "iModelName": "LargeTextTest.ibim", + "iModelName": "LargeTextTest.bim", "viewName": "781", "viewFlags": { "renderMode": "SmoothShade" @@ -702,9 +725,7 @@ { "testName": "Lines", "testType": "both", - "iModelName": "LinesTest.ibim", - "outputName": "linesPerformanceResults.csv", - "outputPath": "D:/output/performanceData/lines/", + "iModelName": "LinesTest.bim", "tests": [ { "viewName": "001" @@ -766,106 +787,109 @@ ] }, { - "testName": "Regression Tests", - "outputName": "regressionPerformanceResults.csv", - "outputPath": "D:\\output\\performanceData\\regression", - "tests": [] - }, - { - "testName": "Generic Tests", + "testName": "Wraith Tests", + "iModelName": "Wraith2.bim", "testType": "both", - "outputName": "genericPerformanceResults.csv", - "outputPath": "D:\\output\\performanceData\\generic", "tests": [ { - "iModelName": "LinesTest.ibim", - "viewName": "001" - }, - { - "iModelName": "LinesTest.ibim", - "viewName": "351" - }, - { - "iModelName": "SmallTextTest.ibim", - "viewName": "001" - }, - { - "iModelName": "SmallTextTest.ibim", - "viewName": "127" - }, - { - "iModelName": "LargeTextTest.ibim", - "viewName": "001" + "iModelName": "Wraith2.bim", + "viewName": "V0", + "viewFlags": { + "renderMode": "SmoothShade", + "dimensions": true, + "patterns": true, + "weights": true, + "styles": true, + "transparency": true, + "fill": true, + "textures": true, + "materials": true, + "visibleEdges": false, + "hiddenEdges": false, + "sourceLights": false, + "cameraLights": false, + "solarLights": false, + "shadows": false, + "clipVolume": true, + "constructions": false, + "monochrome": false, + "noGeometryMap": false, + "backgroundMap": false, + "hLineMaterialColors": false, + "edgeMask": 0 + }, + "displayStyle": "Filled Hidden Line" }, { - "iModelName": "LargeTextTest.ibim", - "viewName": "781" + "iModelName": "Wraith2.bim", + "viewName": "V0", + "viewFlags": { + "fill": true + }, + "displayStyle": "Filled Hidden Line" }, { - "iModelName": "TimingTest_01.ibim", + "iModelName": "Wraith2.bim", "viewName": "V0" }, { - "iModelName": "TimingTest_01.ibim", + "iModelName": "Wraith2.bim", "viewName": "V1" }, { - "iModelName": "TimingTest_01.ibim", + "iModelName": "Wraith2.bim", "viewName": "V2" }, { - "iModelName": "TimingTest_01.ibim", + "iModelName": "Wraith2.bim", "viewName": "V3" }, { - "iModelName": "TimingTest_01.ibim", + "iModelName": "Wraith2.bim", "viewName": "V4" }, { - "iModelName": "TimingTest_02_TranslucencyAll.ibim", - "viewName": "W0" - }, - { - "iModelName": "TimingTest_02_TranslucencyAllButOne.ibim", - "viewName": "W0" - }, - { - "iModelName": "TimingTest_02_TranslucencyNone.ibim", - "viewName": "W0" - }, - { - "iModelName": "TimingTest_02_TranslucencyOne.ibim", - "viewName": "W0" - }, - { - "iModelName": "Wraith.ibim", - "viewName": "V0" + "iModelName": "Wraith_MultiMulti.bim", + "viewName": "V0", + "displayStyle": "Smooth" }, { - "iModelName": "Wraith.ibim", - "viewName": "V1" + "iModelName": "Wraith_MultiMulti.bim", + "viewName": "V0", + "viewFlags": { + "renderMode": "Wireframe" + } }, { - "iModelName": "Wraith.ibim", - "viewName": "V2" + "iModelName": "Wraith_MultiMulti.bim", + "viewName": "V0", + "viewFlags": { + "renderMode": "HiddenLine" + } }, { - "iModelName": "Wraith.ibim", - "viewName": "V3" + "iModelName": "Wraith_MultiMulti.bim", + "viewName": "V0", + "viewFlags": { + "renderMode": "SolidFill" + } }, { - "iModelName": "Wraith.ibim", - "viewName": "V4" + "iModelName": "Wraith_MultiMulti.bim", + "viewName": "V0", + "viewFlags": { + "renderMode": "SmoothShade" + } } ] }, { - "iModelName": "Wraith.ibim", - "outputName": "wraithPerformanceResults.csv", - "outputPath": "D:\\output\\performanceData\\wraith", + "iModelName": "TimingTest_01.bim", + "testName": "Ambient Occlusion", + "testType": "both", + "viewName": "V0", "tests": [ { - "testType": "timing", "viewName": "V0", "viewFlags": { "renderMode": "SmoothShade", @@ -879,9 +903,9 @@ "materials": true, "visibleEdges": false, "hiddenEdges": false, - "sourceLights": false, - "cameraLights": false, - "solarLights": false, + "sourceLights": true, + "cameraLights": true, + "solarLights": true, "shadows": false, "clipVolume": true, "constructions": false, @@ -889,38 +913,39 @@ "noGeometryMap": false, "backgroundMap": false, "hLineMaterialColors": false, - "edgeMask": 0 - }, - "displayStyle": "Filled Hidden Line" - }, - { - "testType": "timing", - "viewName": "V0", - "viewFlags": { - "fill": true - }, - "displayStyle": "Filled Hidden Line" - } - ] - }, - { - "iModelName": "Wraith_MultiMulti.ibim", - "outputName": "wraithPerformanceResults.csv", - "outputPath": "D:\\output\\performanceData\\wraith", - "tests": [ - { - "testType": "image", - "viewName": "V0", - "displayStyle": "Smooth" + "edgeMask": 0, + "ambientOcclusion": false + } }, { - "testType": "timing", "viewName": "V0", "viewFlags": { - "renderMode": "Wireframe" + "renderMode": "SmoothShade", + "dimensions": true, + "patterns": true, + "weights": true, + "styles": true, + "transparency": true, + "fill": true, + "textures": true, + "materials": true, + "visibleEdges": false, + "hiddenEdges": false, + "sourceLights": true, + "cameraLights": true, + "solarLights": true, + "shadows": false, + "clipVolume": true, + "constructions": false, + "monochrome": false, + "noGeometryMap": false, + "backgroundMap": false, + "hLineMaterialColors": false, + "edgeMask": 0, + "ambientOcclusion": true } } ] } ] -} \ No newline at end of file +} diff --git a/test-apps/display-performance-test-app/src/backend/DisplayPerfRpcImpl.ts b/test-apps/display-performance-test-app/src/backend/DisplayPerfRpcImpl.ts index bb4f8d9..b79f004 100644 --- a/test-apps/display-performance-test-app/src/backend/DisplayPerfRpcImpl.ts +++ b/test-apps/display-performance-test-app/src/backend/DisplayPerfRpcImpl.ts @@ -3,9 +3,11 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import DisplayPerfRpcInterface from "../common/DisplayPerfRpcInterface"; +import { IModelHost } from "@bentley/imodeljs-backend"; import { RpcManager } from "@bentley/imodeljs-common"; -import { addColumnsToCsvFile, addDataToCsvFile, createNewCsvFile } from "./CsvWriter"; +import { addColumnsToCsvFile, addDataToCsvFile, createFilePath, createNewCsvFile } from "./CsvWriter"; import * as fs from "fs"; +import { app } from "electron"; /** The backend implementation of DisplayPerfRpcImpl. */ export default class DisplayPerfRpcImpl extends DisplayPerfRpcInterface { @@ -18,6 +20,19 @@ export default class DisplayPerfRpcImpl extends DisplayPerfRpcInterface { } else if (fs.existsSync(defaultJsonFile)) { jsonStr = fs.readFileSync(defaultJsonFile).toString(); } + let argOutputPath: string | undefined; + process.argv.forEach((arg, index) => { + if (index >= 2 && arg !== "chrome" && arg !== "edge" && arg !== "firefox" && arg.split(".").pop() !== "json") { + while (arg.endsWith("\\") || arg.endsWith("\/")) + arg = arg.slice(0, -1); + argOutputPath = "\"argOutputPath\": \"" + arg + "\","; + } + }); + + if (argOutputPath) { + const firstBraceIndex = jsonStr.indexOf("{") + 1; + jsonStr = jsonStr.slice(0, firstBraceIndex) + argOutputPath + jsonStr.slice(firstBraceIndex); + } return jsonStr; } @@ -34,12 +49,35 @@ export default class DisplayPerfRpcImpl extends DisplayPerfRpcInterface { } } + private getFilePath(fileName: string): string { + const slashIndex = fileName.lastIndexOf("/"); + const backSlashIndex = fileName.lastIndexOf("\\"); + if (slashIndex > backSlashIndex) + return fileName.substring(0, slashIndex); + else + return fileName.substring(0, backSlashIndex); + } + public async savePng(fileName: string, png: string) { + const filePath = this.getFilePath(fileName); + if (!fs.existsSync(filePath)) createFilePath(filePath); if (fs.existsSync(fileName)) fs.unlinkSync(fileName); const buf = Buffer.from(png, "base64"); fs.writeFileSync(fileName, buf); } + public async finishTest() { + IModelHost.shutdown(); + + // Electron only + if (app !== undefined) app.exit(); + + // Browser only + if (DisplayPerfRpcInterface.webServer) DisplayPerfRpcInterface.webServer.close(); + if (DisplayPerfRpcInterface.backendServer) DisplayPerfRpcInterface.backendServer.close(); + if (DisplayPerfRpcInterface.chrome) await DisplayPerfRpcInterface.chrome.kill(); + } + private createFullFilePath(filePath: string | undefined, fileName: string | undefined): string | undefined { if (fileName === undefined) return undefined; diff --git a/test-apps/display-performance-test-app/src/backend/ElectronMain.ts b/test-apps/display-performance-test-app/src/backend/ElectronMain.ts index 8483a84..37dbf99 100644 --- a/test-apps/display-performance-test-app/src/backend/ElectronMain.ts +++ b/test-apps/display-performance-test-app/src/backend/ElectronMain.ts @@ -8,6 +8,7 @@ import { ElectronRpcManager } from "@bentley/imodeljs-common"; import { initializeBackend, getRpcInterfaces } from "./backend"; import { Logger, LogLevel } from "@bentley/bentleyjs-core"; import { IModelJsElectronManager } from "@bentley/electron-manager"; +import DisplayPerfRpcInterface from "../common/DisplayPerfRpcInterface"; import * as electron from "electron"; @@ -22,6 +23,9 @@ const logLevelEnv = process.env.SVT_LOG_LEVEL as string; const logLevel = undefined !== logLevelEnv ? Logger.parseLogLevel(logLevelEnv) : LogLevel.None; Logger.setLevelDefault(logLevel); +if (process.argv.length > 2 && process.argv[2].split(".").pop() === "json") + DisplayPerfRpcInterface.jsonFilePath = process.argv[2]; + // -------------------------------------------------------------------------------------- // ---------------- This part copied from protogist ElectronMain.ts --------------------- const autoOpenDevTools = (undefined === process.env.SVT_NO_DEV_TOOLS); @@ -53,7 +57,8 @@ const maximizeWindow = (undefined !== process.env.SVT_MAXIMIZE_WINDOW); } // tslint:disable-next-line:no-var-requires - const configuration = require(path.join(__dirname, "../webresources", "configuration.json")); + const configPathname = path.normalize(path.join(__dirname, "../webresources", "config.json")); + const configuration = require(configPathname); if (configuration.useIModelBank) { electron.app.on("certificate-error", (event, _webContents, _url, _error, _certificate, callback) => { // (needed temporarily to use self-signed cert to communicate with iModelBank via https) diff --git a/test-apps/display-performance-test-app/src/backend/WebMain.ts b/test-apps/display-performance-test-app/src/backend/WebMain.ts index 1e3fffa..533f806 100644 --- a/test-apps/display-performance-test-app/src/backend/WebMain.ts +++ b/test-apps/display-performance-test-app/src/backend/WebMain.ts @@ -4,9 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as path from "path"; import * as express from "express"; -import * as https from "https"; import * as bodyParser from "body-parser"; import * as fs from "fs"; +import * as child_process from "child_process"; +import * as chromeLauncher from "chrome-launcher"; import { BentleyCloudRpcManager, IModelTileRpcInterface, StandaloneIModelRpcInterface, IModelReadRpcInterface } from "@bentley/imodeljs-common"; import { Logger, LogLevel } from "@bentley/bentleyjs-core"; @@ -31,6 +32,33 @@ function setupStandaloneConfiguration() { } } +// Start the Express web server +function startWebServer() { + // set up the express server. + const appExp = express(); + // Enable CORS for all apis + appExp.all("/*", (_req, res, next) => { + res.header("Access-Control-Allow-Origin", "*"); + res.header("Access-Control-Allow-Methods", "POST, GET"); + res.header("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With, X-Correlation-Id"); + next(); + }); + // All we do is serve out static files, so We have only the simple public path route. + // If args.resources is relative, we expect it to be relative to process.cwd + const resourceRoot = path.resolve(process.cwd(), "./lib/webresources/"); + appExp.use(express.static(resourceRoot)); + appExp.use("*", (_req, resp) => { + resp.sendFile(path.resolve("./lib/webresources/", "index.html")); + }); + // Run the server... + appExp.set("port", 3000); + const announceWebServer = () => { }; // console.log(`***** WebServer listening on http:localHost:${appExp.get("port")}, resource root is ${resourceRoot}`); + DisplayPerfRpcInterface.webServer = appExp.listen(appExp.get("port"), announceWebServer); +} + +// Initialize the webserver +startWebServer(); + // Initialize the backend initializeBackend(); @@ -40,30 +68,19 @@ Logger.setLevel("imodeljs-backend", LogLevel.Trace); Logger.setLevel("SVT", LogLevel.Trace); let serverConfig: any; -let serverOptions: any; - -if (process.argv.length >= 3) { - if (process.argv.length > 3 && process.argv[3].split(".").pop() === "json") - DisplayPerfRpcInterface.jsonFilePath = process.argv[3]; - else if (process.argv[2].split(".").pop() === "json") - DisplayPerfRpcInterface.jsonFilePath = process.argv[2]; - else { - Logger.logTrace("SVT", `reading server config from ${process.argv[2]}`); - try { - // tslint:disable-next-line:no-var-requires - serverConfig = require(process.argv[2]); - serverOptions = { - key: fs.readFileSync(serverConfig.keyFile), - cert: fs.readFileSync(serverConfig.certFile), - }; - } catch (_err) { } - } -} +let browser = ""; + +process.argv.forEach((arg) => { + if (arg.split(".").pop() === "json") + DisplayPerfRpcInterface.jsonFilePath = arg; + else if (arg === "chrome" || arg === "edge" || arg === "firefox") + browser = arg; +}); if (serverConfig === undefined) { setupStandaloneConfiguration(); - serverConfig = { port: 3000, baseUrl: "https://localhost" }; + serverConfig = { port: 3001, baseUrl: "https://localhost" }; } else { } @@ -71,7 +88,7 @@ if (serverConfig === undefined) { Logger.logTrace("SVT", `config = ${JSON.stringify(serverConfig)}`); // Set up the ability to serve the supported rpcInterfaces via web requests -const cloudConfig = BentleyCloudRpcManager.initializeImpl({ info: { title: "SimpleViewApp", version: "v1.0" } }, getRpcInterfaces()); +const cloudConfig = BentleyCloudRpcManager.initializeImpl({ info: { title: "display-performance-test-app", version: "v1.0" } }, getRpcInterfaces()); const app = express(); app.use(bodyParser.text({ limit: "50mb" })); @@ -79,7 +96,8 @@ app.use(bodyParser.text({ limit: "50mb" })); // Enable CORS for all apis app.all("/*", (_req, res, next) => { res.header("Access-Control-Allow-Origin", "*"); - res.header("Access-Control-Allow-Headers", "Authorization, X-Requested-With"); + res.header("Access-Control-Allow-Methods", "POST, GET"); + res.header("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With, X-Correlation-Id"); next(); }); @@ -90,16 +108,25 @@ app.use(express.static(path.resolve(__dirname, "public"))); app.get("/v3/swagger.json", (req, res) => cloudConfig.protocol.handleOpenApiDescriptionRequest(req, res)); app.post("*", async (req, res) => cloudConfig.protocol.handleOperationPostRequest(req, res)); app.get(/\/imodel\//, async (req, res) => cloudConfig.protocol.handleOperationGetRequest(req, res)); +app.use("*", (_req, res) => { res.send("

IModelJs RPC Server

"); }); +app.get("/signin-callback", (_req, res) => { + res.sendFile(path.join(__dirname, "public/index.html")); +}); // --------------------------------------------- // Run the server... // --------------------------------------------- app.set("port", serverConfig.port); - const announce = () => console.log(`***** Display Performance Testing App listening on ${serverConfig.baseUrl}:${app.get("port")}`); -if (serverOptions === undefined) { - app.listen(app.get("port"), announce); -} else { - https.createServer(serverOptions, app).listen(app.get("port"), announce); -} +DisplayPerfRpcInterface.backendServer = app.listen(app.get("port"), announce); + +// --------------------------------------------- +// Start the browser, if given a specific one +// --------------------------------------------- +if (browser === "chrome") + chromeLauncher.launch({ startingUrl: "http://localhost:3000" }).then((val) => { DisplayPerfRpcInterface.chrome = val; }); // tslint:disable-line:no-floating-promises +else if (browser === "firefox") + child_process.execSync("start firefox http://localhost:3000"); +else if (browser === "edge") + child_process.execSync("start microsoft-edge:http://localhost:3000"); diff --git a/test-apps/display-performance-test-app/src/backend/backend.ts b/test-apps/display-performance-test-app/src/backend/backend.ts index bff93cb..068c9ba 100644 --- a/test-apps/display-performance-test-app/src/backend/backend.ts +++ b/test-apps/display-performance-test-app/src/backend/backend.ts @@ -8,12 +8,11 @@ import { IModelTileRpcInterface, StandaloneIModelRpcInterface, IModelReadRpcInte import * as fs from "fs"; import * as path from "path"; import { IModelJsConfig } from "@bentley/config-loader/lib/IModelJsConfig"; -import { IModelBankClient } from "@bentley/imodeljs-clients/lib/IModelBank/IModelBankClient"; -import { UrlFileHandler } from "@bentley/imodeljs-clients/lib/UrlFileHandler"; +import { IModelBankClient, Config } from "@bentley/imodeljs-clients"; +import { UrlFileHandler } from "@bentley/imodeljs-clients-backend"; import { SVTConfiguration } from "../common/SVTConfiguration"; import "./DisplayPerfRpcImpl"; // just to get the RPC implementation registered import DisplayPerfRpcInterface from "../common/DisplayPerfRpcInterface"; -import { Config } from "@bentley/imodeljs-clients"; IModelJsConfig.init(true /* suppress exception */, true /* suppress error message */, Config.App); process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; // (needed temporarily to use self-signed cert to communicate with iModelBank via https) @@ -30,7 +29,8 @@ function setupStandaloneConfiguration() { configuration.standalonePath = process.env.SVT_STANDALONE_FILEPATH; // optional (browser-use only) configuration.viewName = process.env.SVT_STANDALONE_VIEWNAME; // optional configuration.iModelName = filename; - fs.writeFileSync(path.join(__dirname, "../webresources", "configuration.json"), JSON.stringify(configuration), "utf8"); + const configPathname = path.normalize(path.join(__dirname, "../webresources", "configuration.json")); + fs.writeFileSync(configPathname, JSON.stringify(configuration), "utf8"); } } @@ -38,8 +38,10 @@ export function initializeBackend() { setupStandaloneConfiguration(); const hostConfig = new IModelHostConfiguration(); + hostConfig.useTileContentThreadPool = true; // tslint:disable-next-line:no-var-requires - const svtConfig: SVTConfiguration = require("../webresources/configuration.json"); + const configPathname = path.normalize(path.join(__dirname, "../webresources", "configuration.json")); + const svtConfig: SVTConfiguration = require(configPathname); if (svtConfig.customOrchestratorUri) hostConfig.imodelClient = new IModelBankClient(svtConfig.customOrchestratorUri, new UrlFileHandler()); diff --git a/test-apps/display-performance-test-app/src/common/DisplayPerfRpcInterface.ts b/test-apps/display-performance-test-app/src/common/DisplayPerfRpcInterface.ts index add61e8..cfcfaf1 100644 --- a/test-apps/display-performance-test-app/src/common/DisplayPerfRpcInterface.ts +++ b/test-apps/display-performance-test-app/src/common/DisplayPerfRpcInterface.ts @@ -3,6 +3,9 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import { RpcInterface, RpcManager } from "@bentley/imodeljs-common"; +import * as chromeLauncher from "chrome-launcher"; +import * as https from "https"; +import * as http from "http"; /** Display Performance RPC interface. */ export default class DisplayPerfRpcInterface extends RpcInterface { @@ -12,6 +15,13 @@ export default class DisplayPerfRpcInterface extends RpcInterface { /** Full path of the json file; will use the default json file instead if this file cannot be found */ public static jsonFilePath = ""; + /** The backend server, when running on a browser */ + public static backendServer: http.Server | https.Server; + public static webServer: http.Server | https.Server; + + /** A chrome browser window, when testing with chrome */ + public static chrome?: chromeLauncher.LaunchedChrome; + /** The types that can be marshaled by the interface. */ public static types = () => []; @@ -20,4 +30,6 @@ export default class DisplayPerfRpcInterface extends RpcInterface { public async getDefaultConfigs(): Promise { return this.forward(arguments); } public async saveCsv(_outputPath: string, _outputName: string, _rowData: Map): Promise { return this.forward(arguments); } public async savePng(_fileName: string, _png: string): Promise { return this.forward(arguments); } + + public async finishTest(): Promise { return this.forward(arguments); } } diff --git a/test-apps/display-performance-test-app/src/common/npmCommands.ts b/test-apps/display-performance-test-app/src/common/npmCommands.ts new file mode 100644 index 0000000..796bef3 --- /dev/null +++ b/test-apps/display-performance-test-app/src/common/npmCommands.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import * as child_process from "child_process"; + +const execSync = child_process.execSync; +let args = ""; +let browser = ""; +for (let i = 2; i < process.argv.length; i++) { + const curArg = process.argv[i]; + args += curArg + " "; + if (curArg === "chrome" || curArg === "edge" || curArg === "firefox") + browser = curArg; +} +execSync("npm run start:web " + args, { stdio: [0, 1, 2] }); + +if (browser === "edge") + execSync("taskkill /f /im MicrosoftEdge.exe /t >nul"); +else if (browser === "firefox") + execSync("taskkill /f /im firefox.exe /t >nul"); diff --git a/test-apps/display-performance-test-app/src/frontend/ConnectEnv.ts b/test-apps/display-performance-test-app/src/frontend/ConnectEnv.ts index 2cc8dc3..1f22724 100644 --- a/test-apps/display-performance-test-app/src/frontend/ConnectEnv.ts +++ b/test-apps/display-performance-test-app/src/frontend/ConnectEnv.ts @@ -2,13 +2,13 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { ConnectClient, AccessToken, Project, ConnectRequestQueryOptions, IModelHubClient, ImsActiveSecureTokenClient, ImsDelegationSecureTokenClient, AuthorizationToken } from "@bentley/imodeljs-clients"; +import { ConnectClient, AccessToken, Project, IModelHubClient } from "@bentley/imodeljs-clients"; import { ActivityLoggingContext, Guid } from "@bentley/bentleyjs-core"; import { IModelApp } from "@bentley/imodeljs-frontend"; import { showStatus } from "./Utils"; import { SimpleViewState } from "./SimpleViewState"; -/** Parameters for starting DisplayPerformanceTestApp with a specified initial configuration */ +/** Parameters for starting SimpleViewTest with a specified initial configuration */ export enum ProjectScope { Favorites, @@ -20,57 +20,23 @@ export enum ProjectScope { // Logic to establish a connection to a Connect-hosted project and iModel let _connectClient!: ConnectClient; -async function getProjectByName(accessToken: AccessToken, projectScope: ProjectScope, projectName: string): Promise { - const alctx = new ActivityLoggingContext(Guid.createValue()); - - const queryOptions: ConnectRequestQueryOptions = { - $select: "*", // TODO: Get Name,Number,AssetType to work - $top: 100, - $skip: 0, - }; - - let projectList: Project[] = []; - if (projectScope === ProjectScope.Invited) { - projectList = await _connectClient.getInvitedProjects(alctx, accessToken, queryOptions); - } - - if (projectScope === ProjectScope.Favorites) { - queryOptions.isFavorite = true; - } else if (projectScope === ProjectScope.MostRecentlyUsed) { - queryOptions.isMRU = true; +async function getProjectByName(accessToken: AccessToken, projectName: string): Promise { + let project: Project; + try { + project = await _connectClient.getProject(new ActivityLoggingContext(Guid.createValue()), accessToken, { $filter: `Name+eq+'${projectName}'` }); + } catch (e) { + console.log(`Project with name "${projectName}" does not exist`); // tslint:disable-line:no-console + return undefined; } - projectList = await _connectClient.getProjects(alctx, accessToken, queryOptions); - - for (const thisProject of projectList) { - if (thisProject.name === projectName) - return thisProject; - } - return undefined; -} - -// log in to connect -async function loginToConnect(state: SimpleViewState, userName: string, password: string) { - const alctx = new ActivityLoggingContext(Guid.createValue()); - alctx.enter(); - // tslint:disable-next-line:no-console - console.log("Attempting login with userName", userName, "password", password); - - const authClient = new ImsActiveSecureTokenClient(); - const accessClient = new ImsDelegationSecureTokenClient(); - - const authToken: AuthorizationToken = await authClient.getToken(alctx, userName, password); - state.accessToken = await accessClient.getToken(alctx, authToken); + return project; } export async function initializeIModelHub(state: SimpleViewState): Promise { - showStatus("logging in as", state.projectConfig!.userName); - await loginToConnect(state, state.projectConfig!.userName, state.projectConfig!.password); - _connectClient = new ConnectClient(); showStatus("opening Project", state.projectConfig!.projectName); - state.project = await getProjectByName(state.accessToken!, ProjectScope.Invited, state.projectConfig!.projectName); + state.project = await getProjectByName(state.accessToken!, state.projectConfig!.projectName); IModelApp.iModelClient = new IModelHubClient(); } diff --git a/test-apps/display-performance-test-app/src/frontend/CustomCloudEnv.ts b/test-apps/display-performance-test-app/src/frontend/CustomCloudEnv.ts index f97b0fa..62883e9 100644 --- a/test-apps/display-performance-test-app/src/frontend/CustomCloudEnv.ts +++ b/test-apps/display-performance-test-app/src/frontend/CustomCloudEnv.ts @@ -11,7 +11,7 @@ import { ActivityLoggingContext } from "@bentley/bentleyjs-core"; // A connection to a non-Connect-hosted project and iModel export async function initializeCustomCloudEnv(state: SimpleViewState, url: string): Promise { - const id = state.projectConfig!.userName; + const id = "user"; const email = { id: "email@organization.org" }; const profile = { firstName: "first", lastName: "last" }; const organization = { id: "organizationId", name: "organization" }; diff --git a/test-apps/display-performance-test-app/src/frontend/DefaultConfig.json b/test-apps/display-performance-test-app/src/frontend/DefaultConfig.json deleted file mode 100644 index 8955b7b..0000000 --- a/test-apps/display-performance-test-app/src/frontend/DefaultConfig.json +++ /dev/null @@ -1,926 +0,0 @@ -{ - "outputName": "performanceResults.csv", - "outputPath": "D:\\output\\performanceData\\", - "iModelLocation": "D:\\models\\TimingTests\\", - "view": { - "width": 1000, - "height": 1000 - }, - "testSet": [ - { - "iModelName": "TimingTest_01.ibim", - "testName": "Render Mode Images", - "testType": "both", - "outputName": "renderModePerformanceResults.csv", - "outputPath": "D:\\output\\performanceData\\renderModes", - "viewName": "V0", - "tests": [ - { - "viewName": "V0", - "viewFlags": { - "renderMode": "SmoothShade", - "dimensions": true, - "patterns": true, - "weights": true, - "styles": true, - "transparency": true, - "fill": true, - "textures": true, - "materials": true, - "visibleEdges": false, - "hiddenEdges": false, - "sourceLights": false, - "cameraLights": false, - "solarLights": false, - "shadows": false, - "clipVolume": true, - "constructions": false, - "monochrome": false, - "noGeometryMap": false, - "backgroundMap": false, - "hLineMaterialColors": false, - "edgeMask": 0 - } - }, - { - "viewName": "V0", - "viewFlags": { - "renderMode": "Wireframe", - "dimensions": true, - "patterns": true, - "weights": true, - "styles": true, - "transparency": true, - "fill": true, - "textures": true, - "materials": true, - "visibleEdges": false, - "hiddenEdges": false, - "sourceLights": false, - "cameraLights": false, - "solarLights": false, - "shadows": false, - "clipVolume": true, - "constructions": false, - "monochrome": false, - "noGeometryMap": false, - "backgroundMap": false, - "hLineMaterialColors": false, - "edgeMask": 0 - } - }, - { - "viewName": "V0", - "viewFlags": { - "renderMode": "HiddenLine", - "dimensions": true, - "patterns": true, - "weights": true, - "styles": true, - "transparency": true, - "fill": true, - "textures": true, - "materials": true, - "visibleEdges": false, - "hiddenEdges": false, - "sourceLights": false, - "cameraLights": false, - "solarLights": false, - "shadows": false, - "clipVolume": true, - "constructions": false, - "monochrome": false, - "noGeometryMap": false, - "backgroundMap": false, - "hLineMaterialColors": false, - "edgeMask": 0 - } - }, - { - "viewName": "V0", - "viewFlags": { - "renderMode": "SolidFill", - "dimensions": true, - "patterns": true, - "weights": true, - "styles": true, - "transparency": true, - "fill": true, - "textures": true, - "materials": true, - "visibleEdges": false, - "hiddenEdges": false, - "sourceLights": false, - "cameraLights": false, - "solarLights": false, - "shadows": false, - "clipVolume": true, - "constructions": false, - "monochrome": false, - "noGeometryMap": false, - "backgroundMap": false, - "hLineMaterialColors": false, - "edgeMask": 0 - } - }, - { - "viewName": "V0", - "viewFlags": { - "renderMode": "Wireframe" - } - }, - { - "viewName": "V0", - "viewFlags": { - "renderMode": "HiddenLine" - } - }, - { - "viewName": "V0", - "viewFlags": { - "renderMode": "SolidFill" - } - }, - { - "viewName": "V0", - "viewFlags": { - "renderMode": "SmoothShade" - } - } - ] - }, - { - "testName": "Edge Images", - "testType": "both", - "outputName": "edgePerformanceResults.csv", - "outputPath": "D:\\output\\performanceData\\edges\\", - "tests": [ - { - "iModelName": "TimingTest_01.ibim", - "viewName": "V2", - "viewFlags": { - "renderMode": "SmoothShade", - "dimensions": true, - "patterns": true, - "weights": true, - "styles": true, - "transparency": true, - "fill": true, - "textures": true, - "materials": true, - "visibleEdges": true, - "hiddenEdges": false, - "sourceLights": true, - "cameraLights": false, - "solarLights": false, - "shadows": false, - "clipVolume": true, - "constructions": false, - "monochrome": false, - "noGeometryMap": false, - "backgroundMap": false, - "hLineMaterialColors": false, - "edgeMask": 0 - } - }, - { - "iModelName": "TimingTest_01.ibim", - "viewName": "V2", - "viewFlags": { - "renderMode": "SmoothShade", - "dimensions": true, - "patterns": true, - "weights": true, - "styles": true, - "transparency": true, - "fill": true, - "textures": true, - "materials": true, - "visibleEdges": true, - "hiddenEdges": true, - "sourceLights": true, - "cameraLights": false, - "solarLights": false, - "shadows": false, - "clipVolume": true, - "constructions": false, - "monochrome": false, - "noGeometryMap": false, - "backgroundMap": false, - "hLineMaterialColors": false, - "edgeMask": 0 - } - }, - { - "iModelName": "TimingTest_01.ibim", - "viewName": "V2", - "viewFlags": { - "renderMode": "SmoothShade", - "dimensions": true, - "patterns": true, - "weights": true, - "styles": true, - "transparency": true, - "fill": true, - "textures": true, - "materials": true, - "visibleEdges": false, - "hiddenEdges": true, - "sourceLights": true, - "cameraLights": false, - "solarLights": false, - "shadows": false, - "clipVolume": true, - "constructions": false, - "monochrome": false, - "noGeometryMap": false, - "backgroundMap": false, - "hLineMaterialColors": false, - "edgeMask": 0 - } - }, - { - "iModelName": "TimingTest_02_TranslucencyAll.ibim", - "viewName": "W0", - "viewFlags": { - "renderMode": "SmoothShade", - "dimensions": true, - "patterns": true, - "weights": true, - "styles": true, - "transparency": true, - "fill": true, - "textures": true, - "materials": true, - "visibleEdges": true, - "hiddenEdges": false, - "sourceLights": true, - "cameraLights": false, - "solarLights": false, - "shadows": false, - "clipVolume": true, - "constructions": false, - "monochrome": false, - "noGeometryMap": false, - "backgroundMap": false, - "hLineMaterialColors": false, - "edgeMask": 0 - } - }, - { - "iModelName": "TimingTest_02_TranslucencyAll.ibim", - "viewName": "W0", - "viewFlags": { - "renderMode": "SmoothShade", - "dimensions": true, - "patterns": true, - "weights": true, - "styles": true, - "transparency": true, - "fill": true, - "textures": true, - "materials": true, - "visibleEdges": true, - "hiddenEdges": true, - "sourceLights": true, - "cameraLights": false, - "solarLights": false, - "shadows": false, - "clipVolume": true, - "constructions": false, - "monochrome": false, - "noGeometryMap": false, - "backgroundMap": false, - "hLineMaterialColors": false, - "edgeMask": 0 - } - }, - { - "iModelName": "TimingTest_02_TranslucencyAllButOne.ibim", - "viewName": "W0", - "viewFlags": { - "renderMode": "SmoothShade", - "dimensions": true, - "patterns": true, - "weights": true, - "styles": true, - "transparency": true, - "fill": true, - "textures": true, - "materials": true, - "visibleEdges": true, - "hiddenEdges": false, - "sourceLights": true, - "cameraLights": false, - "solarLights": false, - "shadows": false, - "clipVolume": true, - "constructions": false, - "monochrome": false, - "noGeometryMap": false, - "backgroundMap": false, - "hLineMaterialColors": false, - "edgeMask": 0 - } - }, - { - "iModelName": "TimingTest_02_TranslucencyAllButOne.ibim", - "viewName": "W0", - "viewFlags": { - "renderMode": "SmoothShade", - "dimensions": true, - "patterns": true, - "weights": true, - "styles": true, - "transparency": true, - "fill": true, - "textures": true, - "materials": true, - "visibleEdges": true, - "hiddenEdges": true, - "sourceLights": true, - "cameraLights": false, - "solarLights": false, - "shadows": false, - "clipVolume": true, - "constructions": false, - "monochrome": false, - "noGeometryMap": false, - "backgroundMap": false, - "hLineMaterialColors": false, - "edgeMask": 0 - } - }, - { - "iModelName": "TimingTest_02_TranslucencyNone.ibim", - "viewName": "W0", - "viewFlags": { - "renderMode": "SmoothShade", - "dimensions": true, - "patterns": true, - "weights": true, - "styles": true, - "transparency": true, - "fill": true, - "textures": true, - "materials": true, - "visibleEdges": true, - "hiddenEdges": false, - "sourceLights": true, - "cameraLights": false, - "solarLights": false, - "shadows": false, - "clipVolume": true, - "constructions": false, - "monochrome": false, - "noGeometryMap": false, - "backgroundMap": false, - "hLineMaterialColors": false, - "edgeMask": 0 - } - }, - { - "iModelName": "TimingTest_02_TranslucencyNone.ibim", - "viewName": "W0", - "viewFlags": { - "renderMode": "SmoothShade", - "dimensions": true, - "patterns": true, - "weights": true, - "styles": true, - "transparency": true, - "fill": true, - "textures": true, - "materials": true, - "visibleEdges": true, - "hiddenEdges": true, - "sourceLights": true, - "cameraLights": false, - "solarLights": false, - "shadows": false, - "clipVolume": true, - "constructions": false, - "monochrome": false, - "noGeometryMap": false, - "backgroundMap": false, - "hLineMaterialColors": false, - "edgeMask": 0 - } - }, - { - "iModelName": "TimingTest_02_TranslucencyOne.ibim", - "viewName": "W0", - "viewFlags": { - "renderMode": "SmoothShade", - "dimensions": true, - "patterns": true, - "weights": true, - "styles": true, - "transparency": true, - "fill": true, - "textures": true, - "materials": true, - "visibleEdges": true, - "hiddenEdges": false, - "sourceLights": true, - "cameraLights": false, - "solarLights": false, - "shadows": false, - "clipVolume": true, - "constructions": false, - "monochrome": false, - "noGeometryMap": false, - "backgroundMap": false, - "hLineMaterialColors": false, - "edgeMask": 0 - } - }, - { - "iModelName": "TimingTest_02_TranslucencyOne.ibim", - "viewName": "W0", - "viewFlags": { - "renderMode": "SmoothShade", - "dimensions": true, - "patterns": true, - "weights": true, - "styles": true, - "transparency": true, - "fill": true, - "textures": true, - "materials": true, - "visibleEdges": true, - "hiddenEdges": true, - "sourceLights": true, - "cameraLights": false, - "solarLights": false, - "shadows": false, - "clipVolume": true, - "constructions": false, - "monochrome": false, - "noGeometryMap": false, - "backgroundMap": false, - "hLineMaterialColors": false, - "edgeMask": 0 - } - } - ] - }, - { - "testName": "Light Images", - "testType": "both", - "iModelName": "TimingTest_01.ibim", - "outputName": "lightPerformanceResults.csv", - "outputPath": "D:/output/performanceData/light/", - "viewFlags": { - "renderMode": "SmoothShade", - "dimensions": true, - "patterns": true, - "weights": true, - "styles": true, - "transparency": true, - "fill": true, - "textures": true, - "materials": true, - "visibleEdges": false, - "hiddenEdges": false, - "sourceLights": false, - "cameraLights": false, - "solarLights": false, - "shadows": false, - "clipVolume": true, - "constructions": false, - "monochrome": false, - "noGeometryMap": false, - "backgroundMap": false, - "hLineMaterialColors": false, - "edgeMask": 0 - }, - "tests": [ - { - "viewName": "V0" - }, - { - "viewName": "V0", - "viewFlags": { - "sourceLights": true - } - }, - { - "viewName": "V0", - "viewFlags": { - "sourceLights": true - } - }, - { - "viewName": "V0", - "viewFlags": { - "solarLights": true - } - }, - { - "viewName": "V1" - }, - { - "viewName": "V1", - "viewFlags": { - "sourceLights": true - } - }, - { - "viewName": "V1", - "viewFlags": { - "sourceLights": true - } - }, - { - "viewName": "V1", - "viewFlags": { - "solarLights": true - } - }, - { - "viewName": "V2" - }, - { - "viewName": "V2", - "viewFlags": { - "sourceLights": true - } - }, - { - "viewName": "V2", - "viewFlags": { - "sourceLights": true - } - }, - { - "viewName": "V2", - "viewFlags": { - "solarLights": true - } - }, - { - "viewName": "V3" - }, - { - "viewName": "V3", - "viewFlags": { - "sourceLights": true - } - }, - { - "viewName": "V3", - "viewFlags": { - "sourceLights": true - } - }, - { - "viewName": "V3", - "viewFlags": { - "solarLights": true - } - }, - { - "viewName": "V4" - }, - { - "viewName": "V4", - "viewFlags": { - "sourceLights": true - } - }, - { - "viewName": "V4", - "viewFlags": { - "sourceLights": true - } - }, - { - "viewName": "V4", - "viewFlags": { - "solarLights": true - } - } - ] - }, - { - "testName": "Text", - "testType": "both", - "outputName": "textPerformanceResults.csv", - "outputPath": "D:/output/performanceData/text/", - "tests": [ - { - "iModelName": "SmallTextTest.ibim", - "viewName": "001" - }, - { - "iModelName": "SmallTextTest.ibim", - "viewName": "002" - }, - { - "iModelName": "SmallTextTest.ibim", - "viewName": "004" - }, - { - "iModelName": "SmallTextTest.ibim", - "viewName": "008" - }, - { - "iModelName": "SmallTextTest.ibim", - "viewName": "016" - }, - { - "iModelName": "SmallTextTest.ibim", - "viewName": "032" - }, - { - "iModelName": "SmallTextTest.ibim", - "viewName": "064" - }, - { - "iModelName": "SmallTextTest.ibim", - "viewName": "096" - }, - { - "iModelName": "SmallTextTest.ibim", - "viewName": "127" - }, - { - "iModelName": "LargeTextTest.ibim", - "viewName": "001" - }, - { - "iModelName": "LargeTextTest.ibim", - "viewName": "005" - }, - { - "iModelName": "LargeTextTest.ibim", - "viewName": "025" - }, - { - "iModelName": "LargeTextTest.ibim", - "viewName": "125" - }, - { - "iModelName": "LargeTextTest.ibim", - "viewName": "625" - }, - { - "iModelName": "LargeTextTest.ibim", - "viewName": "781" - }, - { - "iModelName": "LargeTextTest.ibim", - "viewName": "781", - "viewFlags": { - "renderMode": "Wireframe" - } - }, - { - "iModelName": "LargeTextTest.ibim", - "viewName": "781", - "viewFlags": { - "renderMode": "HiddenLine" - } - }, - { - "iModelName": "LargeTextTest.ibim", - "viewName": "781", - "viewFlags": { - "renderMode": "SolidFill" - } - }, - { - "iModelName": "LargeTextTest.ibim", - "viewName": "781", - "viewFlags": { - "renderMode": "SmoothShade" - } - } - ] - }, - { - "testName": "Lines", - "testType": "both", - "iModelName": "LinesTest.ibim", - "outputName": "linesPerformanceResults.csv", - "outputPath": "D:/output/performanceData/lines/", - "tests": [ - { - "viewName": "001" - }, - { - "viewName": "050" - }, - { - "viewName": "100" - }, - { - "viewName": "200" - }, - { - "viewName": "351" - }, - { - "viewName": "001", - "viewFlags": { - "weights": false, - "styles": true - } - }, - { - "viewName": "001", - "viewFlags": { - "weights": true, - "styles": false - } - }, - { - "viewName": "001", - "viewFlags": { - "weights": false, - "styles": false - } - }, - { - "viewName": "351", - "viewFlags": { - "weights": false, - "styles": true - } - }, - { - "viewName": "351", - "viewFlags": { - "weights": true, - "styles": false - } - }, - { - "viewName": "351", - "viewFlags": { - "weights": false, - "styles": false - } - } - ] - }, - { - "testName": "Regression Tests", - "outputName": "regressionPerformanceResults.csv", - "outputPath": "D:\\output\\performanceData\\regression", - "tests": [] - }, - { - "testName": "Generic Tests", - "testType": "both", - "outputName": "genericPerformanceResults.csv", - "outputPath": "D:\\output\\performanceData\\generic", - "tests": [ - { - "iModelName": "LinesTest.ibim", - "viewName": "001" - }, - { - "iModelName": "LinesTest.ibim", - "viewName": "351" - }, - { - "iModelName": "SmallTextTest.ibim", - "viewName": "001" - }, - { - "iModelName": "SmallTextTest.ibim", - "viewName": "127" - }, - { - "iModelName": "LargeTextTest.ibim", - "viewName": "001" - }, - { - "iModelName": "LargeTextTest.ibim", - "viewName": "781" - }, - { - "iModelName": "TimingTest_01.ibim", - "viewName": "V0" - }, - { - "iModelName": "TimingTest_01.ibim", - "viewName": "V1" - }, - { - "iModelName": "TimingTest_01.ibim", - "viewName": "V2" - }, - { - "iModelName": "TimingTest_01.ibim", - "viewName": "V3" - }, - { - "iModelName": "TimingTest_01.ibim", - "viewName": "V4" - }, - { - "iModelName": "TimingTest_02_TranslucencyAll.ibim", - "viewName": "W0" - }, - { - "iModelName": "TimingTest_02_TranslucencyAllButOne.ibim", - "viewName": "W0" - }, - { - "iModelName": "TimingTest_02_TranslucencyNone.ibim", - "viewName": "W0" - }, - { - "iModelName": "TimingTest_02_TranslucencyOne.ibim", - "viewName": "W0" - }, - { - "iModelName": "Wraith.ibim", - "viewName": "V0" - }, - { - "iModelName": "Wraith.ibim", - "viewName": "V1" - }, - { - "iModelName": "Wraith.ibim", - "viewName": "V2" - }, - { - "iModelName": "Wraith.ibim", - "viewName": "V3" - }, - { - "iModelName": "Wraith.ibim", - "viewName": "V4" - } - ] - }, - { - "iModelName": "Wraith.ibim", - "outputName": "wraithPerformanceResults.csv", - "outputPath": "D:\\output\\performanceData\\wraith", - "tests": [ - { - "testType": "timing", - "viewName": "V0", - "viewFlags": { - "renderMode": "SmoothShade", - "dimensions": true, - "patterns": true, - "weights": true, - "styles": true, - "transparency": true, - "fill": true, - "textures": true, - "materials": true, - "visibleEdges": false, - "hiddenEdges": false, - "sourceLights": false, - "cameraLights": false, - "solarLights": false, - "shadows": false, - "clipVolume": true, - "constructions": false, - "monochrome": false, - "noGeometryMap": false, - "backgroundMap": false, - "hLineMaterialColors": false, - "edgeMask": 0 - }, - "displayStyle": "Filled Hidden Line" - }, - { - "testType": "timing", - "viewName": "V0", - "viewFlags": { - "fill": true - }, - "displayStyle": "Filled Hidden Line" - } - ] - }, - { - "iModelName": "Wraith_MultiMulti.ibim", - "outputName": "wraithPerformanceResults.csv", - "outputPath": "D:\\output\\performanceData\\wraith", - "tests": [ - { - "testType": "image", - "viewName": "V0", - "displayStyle": "Smooth" - }, - { - "testType": "timing", - "viewName": "V0", - "viewFlags": { - "renderMode": "Wireframe" - } - } - ] - } - ] -} \ No newline at end of file diff --git a/test-apps/display-performance-test-app/src/frontend/DisplayPerformanceTestApp.ts b/test-apps/display-performance-test-app/src/frontend/DisplayPerformanceTestApp.ts index 8aeafc6..4961bab 100644 --- a/test-apps/display-performance-test-app/src/frontend/DisplayPerformanceTestApp.ts +++ b/test-apps/display-performance-test-app/src/frontend/DisplayPerformanceTestApp.ts @@ -3,21 +3,18 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { OpenMode, StopWatch } from "@bentley/bentleyjs-core"; -import { Config, AccessToken, HubIModel, Project } from "@bentley/imodeljs-clients"; +import { ActivityLoggingContext, Guid, OpenMode, StopWatch } from "@bentley/bentleyjs-core"; +import { Config, AccessToken, HubIModel, Project, OidcFrontendClientConfiguration } from "@bentley/imodeljs-clients"; import { - BentleyCloudRpcManager, ElectronRpcConfiguration, ElectronRpcManager, IModelReadRpcInterface, - IModelTileRpcInterface, IModelToken, RpcConfiguration, - RpcOperation, StandaloneIModelRpcInterface, - ViewDefinitionProps, ViewFlag, RenderMode, DisplayStyleProps, + BentleyCloudRpcManager, DisplayStyleProps, ElectronRpcConfiguration, ElectronRpcManager, IModelReadRpcInterface, + IModelTileRpcInterface, IModelToken, RpcConfiguration, RpcOperation, RenderMode, StandaloneIModelRpcInterface, + ViewDefinitionProps, ViewFlag, MobileRpcConfiguration, MobileRpcManager, } from "@bentley/imodeljs-common"; -import { MobileRpcConfiguration, MobileRpcManager } from "@bentley/imodeljs-common/lib/rpc/mobile/MobileRpcManager"; -import { SVTConfiguration } from "../common/SVTConfiguration"; +import { ConnectProjectConfiguration, SVTConfiguration } from "../common/SVTConfiguration"; import DisplayPerfRpcInterface from "../common/DisplayPerfRpcInterface"; -import { DisplayStyleState, DisplayStyle3dState, IModelApp, IModelConnection, Viewport, ViewState, ScreenViewport } from "@bentley/imodeljs-frontend"; -import { PerformanceMetrics, /*System,*/ Target } from "@bentley/imodeljs-frontend/lib/webgl"; +import { DisplayStyleState, DisplayStyle3dState, IModelApp, IModelConnection, Viewport, ViewState, ScreenViewport, OidcClientWrapper, PerformanceMetrics, Target } from "@bentley/imodeljs-frontend"; import { IModelApi } from "./IModelApi"; -import { ProjectApi } from "./ProjectApi"; +import { initializeIModelHub } from "./ConnectEnv"; // Retrieve default config data from json file async function getDefaultConfigs(): Promise { @@ -57,6 +54,18 @@ function removeFilesFromDir(_startPath: string, _filter: string) { // }); } +function combineFilePaths(additionalPath: string, initPath?: string) { + if (initPath === undefined || additionalPath[1] === ":") // if additionalPath is full path (like D:), ignore the initial path + return additionalPath; + let combined = initPath; + while (combined.endsWith("\\") || combined.endsWith("\/")) + combined = combined.slice(0, -1); + if (additionalPath[0] !== "\\" && additionalPath[0] !== "\/") + combined += "\\"; + combined += additionalPath; + return combined; +} + function setViewFlagOverrides(vf: any, vfo?: ViewFlag.Overrides): ViewFlag.Overrides { if (!vfo) vfo = new ViewFlag.Overrides(); if (vf) { @@ -171,6 +180,7 @@ function getViewFlagsString(): string { if (vf.hLineMaterialColors) vfString += "+hln"; if (vf.edgeMask === 1) vfString += "+genM"; if (vf.edgeMask === 2) vfString += "+useM"; + if (vf.ambientOcclusion) vfString += "+ao"; return vfString; } @@ -195,7 +205,12 @@ async function waitForTilesToLoad(modelLocation?: string) { sceneContext.requestMissingTiles(); // The scene is ready when (1) all required TileTree roots have been created and (2) all required tiles have finished loading - haveNewTiles = !(activeViewState.viewState!.areAllTileTreesLoaded) || sceneContext.hasMissingTiles; + haveNewTiles = !(activeViewState.viewState!.areAllTileTreesLoaded) || sceneContext.hasMissingTiles || 0 < sceneContext.missingTiles.size; + + // NB: The viewport is NOT added to the ViewManager's render loop, therefore we must manually pump the tile request scheduler... + if (haveNewTiles) + IModelApp.tileAdmin.process(); + // debugPrint(haveNewTiles ? "Awaiting tile loads..." : "...All tiles loaded."); await resolveAfterXMilSeconds(100); @@ -225,22 +240,10 @@ function getRowData(finalFrameTimings: Array>, configs: Defa }); rowData.set(colName, sum / finalFrameTimings.length); } + rowData.set("Effective FPS", (1000.0 / Number(rowData.get("Total Time w/ GPU"))).toFixed(2)); return rowData; } -function printResults(_configs: DefaultConfigs, _rowData: Map) { - // debugPrint("outputFile: " + configs.outputFile); - // if (fs.existsSync(configs.outputFile!)) { - // addColumnsToCsvFile(configs.outputFile!, rowData); - // debugPrint("outputFile: " + configs.outputFile); - // } else { - // debugPrint("outputPath: " + configs.outputPath); - // debugPrint("outputName: " + configs.outputName); - // createNewCsvFile(configs.outputPath!, configs.outputName!, rowData); - // } - // addDataToCsvFile(configs.outputFile!, rowData); -} - function getImageString(configs: DefaultConfigs): string { let output = configs.outputPath ? configs.outputPath : ""; const lastChar = output[output.length - 1]; @@ -276,18 +279,20 @@ class DefaultConfigs { public outputPath?: string; public iModelLocation?: string; public iModelName?: string; + public iModelHubProject?: string; public viewName?: string; public testType?: string; public displayStyle?: string; public viewFlags?: ViewFlag.Overrides; + public aoEnabled = false; public constructor(jsonData: any, prevConfigs?: DefaultConfigs, useDefaults = false) { if (useDefaults) { this.view = new ViewSize(1000, 1000); this.outputName = "performanceResults.csv"; this.outputPath = "D:\\output\\performanceData\\"; - this.iModelLocation = "D:\\models\\TimingTests\\"; this.iModelName = "Wraith.ibim"; + this.iModelHubProject = "DisplayPerformanceTest"; this.viewName = "V0"; this.testType = "timing"; } @@ -297,20 +302,24 @@ class DefaultConfigs { if (prevConfigs.outputPath) this.outputPath = prevConfigs.outputPath; if (prevConfigs.iModelLocation) this.iModelLocation = prevConfigs.iModelLocation; if (prevConfigs.iModelName) this.iModelName = prevConfigs.iModelName; + if (prevConfigs.iModelHubProject) this.iModelHubProject = prevConfigs.iModelHubProject; if (prevConfigs.viewName) this.viewName = prevConfigs.viewName; if (prevConfigs.testType) this.testType = prevConfigs.testType; if (prevConfigs.displayStyle) this.displayStyle = prevConfigs.displayStyle; if (prevConfigs.viewFlags) this.viewFlags = prevConfigs.viewFlags; - } + } else if (jsonData.argOutputPath) + this.outputPath = jsonData.argOutputPath; if (jsonData.view) this.view = new ViewSize(jsonData.view.width, jsonData.view.height); if (jsonData.outputName) this.outputName = jsonData.outputName; - if (jsonData.outputPath) this.outputPath = jsonData.outputPath; - if (jsonData.iModelLocation) this.iModelLocation = jsonData.iModelLocation; + if (jsonData.outputPath) this.outputPath = combineFilePaths(jsonData.outputPath, this.outputPath); + if (jsonData.iModelLocation) this.iModelLocation = combineFilePaths(jsonData.iModelLocation, this.iModelLocation); if (jsonData.iModelName) this.iModelName = jsonData.iModelName; + if (jsonData.iModelHubProject) this.iModelHubProject = jsonData.iModelHubProject; if (jsonData.viewName) this.viewName = jsonData.viewName; if (jsonData.testType) this.testType = jsonData.testType; if (jsonData.displayStyle) this.displayStyle = jsonData.displayStyle; if (jsonData.viewFlags) this.viewFlags = setViewFlagOverrides(jsonData.viewFlags, this.viewFlags); + this.aoEnabled = undefined !== jsonData.viewFlags && !!jsonData.viewFlags.ambientOcclusion; debugPrint("view: " + this.view ? (this.view!.width + "X" + this.view!.height) : "undefined"); debugPrint("outputFile: " + this.outputFile); @@ -319,6 +328,7 @@ class DefaultConfigs { debugPrint("iModelFile: " + this.iModelFile); debugPrint("iModelLocation: " + this.iModelLocation); debugPrint("iModelName: " + this.iModelName); + debugPrint("iModelHubProject: " + this.iModelHubProject); debugPrint("viewName: " + this.viewName); debugPrint("testType: " + this.testType); debugPrint("displayStyle: " + this.displayStyle); @@ -351,6 +361,7 @@ class SimpleViewState { public viewDefinition?: ViewDefinitionProps; public viewState?: ViewState; public viewPort?: Viewport; + public projectConfig?: ConnectProjectConfiguration; constructor() { } } @@ -365,10 +376,17 @@ async function _changeView(view: ViewState) { // opens the view and connects it to the HTML canvas element. async function openView(state: SimpleViewState, viewSize: ViewSize) { + if (undefined !== theViewport) { + theViewport.dispose(); + theViewport = undefined; + } + // find the canvas. const vpDiv = document.getElementById("imodel-viewport") as HTMLDivElement; if (vpDiv) { + vpDiv.style.width = String(viewSize.width) + "px"; + vpDiv.style.height = String(viewSize.height) + "px"; theViewport = ScreenViewport.create(vpDiv, state.viewState!); debugPrint("theViewport: " + theViewport); const canvas = theViewport.canvas as HTMLCanvasElement; @@ -382,18 +400,77 @@ async function openView(state: SimpleViewState, viewSize: ViewSize) { } } -async function loadIModel(testConfig: DefaultConfigs) { - // start the app. - IModelApp.startup(); +async function initializeOidc(actx: ActivityLoggingContext) { + actx.enter(); + const clientId = Config.App.get((ElectronRpcConfiguration.isElectron) ? "imjs_electron_test_client_id" : "imjs_browser_test_client_id"); + const redirectUri = Config.App.get((ElectronRpcConfiguration.isElectron) ? "imjs_electron_test_redirect_uri" : "imjs_browser_test_redirect_uri"); + const oidcConfig: OidcFrontendClientConfiguration = { clientId, redirectUri, scope: "openid email profile organization imodelhub context-registry-service imodeljs-router reality-data:read" }; - // initialize the Project and IModel Api - await ProjectApi.init(); - await IModelApi.init(); + await OidcClientWrapper.initialize(actx, oidcConfig); + actx.enter(); + OidcClientWrapper.oidcClient.onUserStateChanged.addListener((accessToken: AccessToken | undefined) => { + activeViewState.accessToken = accessToken; + }); + activeViewState.accessToken = await OidcClientWrapper.oidcClient.getAccessToken(actx); + actx.enter(); +} + +// Retrieves the configuration for which project and imodel to open from connect-configuration.json file located in the built public folder +async function retrieveProjectConfiguration(): Promise { + return new Promise((resolve, _reject) => { + const request: XMLHttpRequest = new XMLHttpRequest(); + request.open("GET", "connect-configuration.json", false); + request.setRequestHeader("Cache-Control", "no-cache"); + request.onreadystatechange = ((_event: Event) => { + if (request.readyState === XMLHttpRequest.DONE) { + if (request.status === 200) { + activeViewState.projectConfig = JSON.parse(request.responseText); + resolve(); + } + } + }); + request.send(); + }); +} + +async function loadIModel(testConfig: DefaultConfigs) { activeViewState = new SimpleViewState(); activeViewState.viewState; - await openStandaloneIModel(activeViewState, testConfig.iModelFile!); + // Open an iModel from a local file + let openLocalIModel = (testConfig.iModelLocation !== undefined); + if (openLocalIModel) { + try { + activeViewState.iModelConnection = await IModelConnection.openStandalone(testConfig.iModelFile!); + } catch (err) { + debugPrint("openStandalone on local iModel failed: " + err.toString()); + openLocalIModel = false; + } + } + + // Open an iModel from the iModelHub + if (!openLocalIModel && testConfig.iModelHubProject !== undefined) { + const actx = new ActivityLoggingContext(Guid.createValue()); + actx.enter(); + + // Connected to hub + await initializeOidc(actx); + actx.enter(); + + if (!activeViewState.accessToken) + OidcClientWrapper.oidcClient.signIn(actx); + else { + await retrieveProjectConfiguration(); + activeViewState.projectConfig!.projectName = testConfig.iModelHubProject; + activeViewState.projectConfig!.iModelName = testConfig.iModelName!.replace(".ibim", "").replace(".bim", ""); + await initializeIModelHub(activeViewState); + activeViewState.iModel = await IModelApi.getIModelByName(activeViewState.accessToken!, activeViewState.project!.wsgId, activeViewState.projectConfig!.iModelName); + if (activeViewState.iModel === undefined) + throw new Error(`${activeViewState.projectConfig!.iModelName} - IModel not found in project ${activeViewState.project!.name}`); + activeViewState.iModelConnection = await IModelApi.openIModel(activeViewState.accessToken!, activeViewState.project!.wsgId, activeViewState.iModel!.wsgId, undefined, OpenMode.Readonly); + } + } // open the specified view await loadView(activeViewState, testConfig.viewName!); @@ -406,27 +483,30 @@ async function loadIModel(testConfig: DefaultConfigs) { const iModCon = activeViewState.iModelConnection; if (iModCon && testConfig.displayStyle) { const displayStyleProps = await iModCon.elements.queryProps({ from: DisplayStyleState.sqlName, where: "CodeValue = '" + testConfig.displayStyle + "'" }); - // const displayStyleProps = await iModCon.elements.queryProps({ from: DisplayStyleState.sqlName }); - // for (const prop of displayStyleProps) { - // debugPrint("code: " + prop.code); - // debugPrint("value: " + prop.code!.value); - // } if (displayStyleProps.length >= 1) theViewport!.view.setDisplayStyle(new DisplayStyle3dState(displayStyleProps[0] as DisplayStyleProps, iModCon)); } // Set the viewFlags (including the render mode) - if (activeViewState.viewState !== undefined && testConfig.viewFlags) - testConfig.viewFlags.apply(activeViewState.viewState.displayStyle.viewFlags); + if (undefined !== activeViewState.viewState) { + activeViewState.viewState.displayStyle.viewFlags.ambientOcclusion = testConfig.aoEnabled; + if (testConfig.viewFlags) + testConfig.viewFlags.apply(activeViewState.viewState.displayStyle.viewFlags); + } // Load all tiles - await waitForTilesToLoad(testConfig.iModelLocation!); + await waitForTilesToLoad(testConfig.iModelLocation); } -async function closeIModel() { +async function closeIModel(standalone: boolean) { debugPrint("start closeIModel" + activeViewState.iModelConnection); - if (activeViewState.iModelConnection) await activeViewState.iModelConnection.closeStandalone(); - IModelApp.shutdown(); + if (activeViewState.iModelConnection) { + if (standalone) + await activeViewState.iModelConnection.closeStandalone(); + else + await activeViewState.iModelConnection!.close(activeViewState.accessToken!); + } + debugPrint("end closeIModel"); } @@ -456,22 +536,23 @@ async function runTest(testConfig: DefaultConfigs) { finalFrameTimings[i] = (theViewport!.target as Target).performanceMetrics!.frameTimings; } timer.stop(); - debugPrint("------------ Elapsed Time: " + timer.elapsed.milliseconds + " = " + timer.elapsed.milliseconds / numToRender + "ms per frame"); - debugPrint("Tile Loading Time: " + curTileLoadingTime); - for (const t of finalFrameTimings) { - let timingsString = "["; - t.forEach((val) => { - timingsString += val + ", "; - }); - debugPrint(timingsString + "]"); + if (wantConsoleOutput) { + debugPrint("------------ Elapsed Time: " + timer.elapsed.milliseconds + " = " + timer.elapsed.milliseconds / numToRender + "ms per frame"); + debugPrint("Tile Loading Time: " + curTileLoadingTime); + for (const t of finalFrameTimings) { + let timingsString = "["; + t.forEach((val) => { + timingsString += val + ", "; + }); + debugPrint(timingsString + "]"); + } } - - printResults(testConfig, getRowData(finalFrameTimings, testConfig)); - await saveCsv(testConfig.outputPath!, testConfig.outputName!, getRowData(finalFrameTimings, testConfig)); + const rowData = getRowData(finalFrameTimings, testConfig); + await saveCsv(testConfig.outputPath!, testConfig.outputName!, rowData); } // Close the imodel - await closeIModel(); + await closeIModel(testConfig.iModelLocation !== undefined); } // selects the configured view. @@ -484,16 +565,6 @@ async function loadView(state: SimpleViewState, viewName: string) { debugPrint("Error: failed to load view by name"); } -// opens the configured iModel from disk -async function openStandaloneIModel(state: SimpleViewState, filename: string) { - try { - state.iModelConnection = await IModelConnection.openStandalone(filename); - } catch (err) { - debugPrint("openStandaloneIModel failed: " + err.toString()); - throw err; - } -} - async function testModel(configs: DefaultConfigs, modelData: any) { // Create DefaultModelConfigs const modConfigs = new DefaultConfigs(modelData, configs); @@ -517,16 +588,27 @@ async function testModel(configs: DefaultConfigs, modelData: any) { } async function main() { + IModelApp.startup(); + // Retrieve DefaultConfigs const defaultConfigStr = await getDefaultConfigs(); const jsonData = JSON.parse(defaultConfigStr); - for (const i in jsonData.testSet) { if (i) { const modelData = jsonData.testSet[i]; await testModel(new DefaultConfigs(jsonData), modelData); } } + + const topdiv = document.getElementById("topdiv")!; + topdiv.style.display = "block"; + topdiv.innerText = "Tests Completed."; + + document.getElementById("imodel-viewport")!.style.display = "hidden"; + + DisplayPerfRpcInterface.getClient().finishTest(); // tslint:disable-line:no-floating-promises + + IModelApp.shutdown(); } window.onload = () => { @@ -537,12 +619,11 @@ window.onload = () => { if (ElectronRpcConfiguration.isElectron) { rpcConfiguration = ElectronRpcManager.initializeClient({}, [DisplayPerfRpcInterface, IModelTileRpcInterface, StandaloneIModelRpcInterface, IModelReadRpcInterface]); } else if (MobileRpcConfiguration.isMobileFrontend) { - // Object.assign(configuration, { standalone: true, iModelName: "sample_documents/04_Plant.i.ibim" }); rpcConfiguration = MobileRpcManager.initializeClient([DisplayPerfRpcInterface, IModelTileRpcInterface, StandaloneIModelRpcInterface, IModelReadRpcInterface]); } else { - const uriPrefix = configuration.customOrchestratorUri; - rpcConfiguration = BentleyCloudRpcManager.initializeClient({ info: { title: "SimpleViewApp", version: "v1.0" }, uriPrefix }, [DisplayPerfRpcInterface, IModelTileRpcInterface, StandaloneIModelRpcInterface, IModelReadRpcInterface]); - Config.App.set("imjs_dev_cors_proxy_server", "https://localhost:3001"); + const uriPrefix = configuration.customOrchestratorUri || "http://localhost:3001"; + rpcConfiguration = BentleyCloudRpcManager.initializeClient({ info: { title: "DisplayPerformanceTestApp", version: "v1.0" }, uriPrefix }, [DisplayPerfRpcInterface, IModelTileRpcInterface, StandaloneIModelRpcInterface, IModelReadRpcInterface]); + // WIP: WebAppRpcProtocol seems to require an IModelToken for every RPC request. ECPresentation initialization tries to set active locale using // RPC without any imodel and fails... for (const definition of rpcConfiguration.interfaces()) diff --git a/test-apps/display-performance-test-app/src/frontend/IModelApi.ts b/test-apps/display-performance-test-app/src/frontend/IModelApi.ts index 8653bd5..ec6094b 100644 --- a/test-apps/display-performance-test-app/src/frontend/IModelApi.ts +++ b/test-apps/display-performance-test-app/src/frontend/IModelApi.ts @@ -2,33 +2,24 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { IModelHubClient, AccessToken, HubIModel, Version, IModelQuery, VersionQuery } from "@bentley/imodeljs-clients"; -import { OpenMode } from "@bentley/bentleyjs-core/lib/BeSQLite"; -import { IModelConnection } from "@bentley/imodeljs-frontend"; - -// import GatewayProxyApi from "./gatewayProxy"; +import { HubIModel, AccessToken, IModelQuery, Version, VersionQuery } from "@bentley/imodeljs-clients"; +import { ActivityLoggingContext, Guid, OpenMode } from "@bentley/bentleyjs-core"; +import { IModelConnection, IModelApp } from "@bentley/imodeljs-frontend"; import { IModelVersion } from "@bentley/imodeljs-common"; -import { ActivityLoggingContext, Guid } from "@bentley/bentleyjs-core"; export class IModelApi { - private static _hubClient: IModelHubClient; - - /** Initialize the iModelHub Api */ - public static async init(): Promise { - IModelApi._hubClient = new IModelHubClient(); - } /** Get all iModels in a project */ public static async getIModelByName(accessToken: AccessToken, projectId: string, iModelName: string): Promise { const alctx = new ActivityLoggingContext(Guid.createValue()); const queryOptions = new IModelQuery(); queryOptions.select("*").top(100).skip(0); - const iModels: HubIModel[] = await IModelApi._hubClient.iModels.get(alctx, accessToken, projectId, queryOptions); + const iModels: HubIModel[] = await IModelApp.iModelClient.iModels.get(alctx, accessToken, projectId, queryOptions); if (iModels.length < 1) return undefined; for (const thisIModel of iModels) { if (!!thisIModel.id && thisIModel.name === iModelName) { - const versions: Version[] = await IModelApi._hubClient.versions.get(alctx, accessToken, thisIModel.id!, new VersionQuery().select("Name,ChangeSetId").top(1)); + const versions: Version[] = await IModelApp.iModelClient.versions.get(alctx, accessToken, thisIModel.id!, new VersionQuery().select("Name,ChangeSetId").top(1)); if (versions.length > 0) { thisIModel.latestVersionName = versions[0].name; thisIModel.latestVersionChangeSetId = versions[0].changeSetId; diff --git a/test-apps/display-performance-test-app/src/frontend/README.md b/test-apps/display-performance-test-app/src/frontend/README.md index 71dafa5..b52bd55 100644 --- a/test-apps/display-performance-test-app/src/frontend/README.md +++ b/test-apps/display-performance-test-app/src/frontend/README.md @@ -1,16 +1,25 @@ # How to Utilize the Performance Tests +## To run a performance test with electron: +* Run the command "npm run start:electron". + ## To run a performance test on a web browser: -1. run the command "npm run start:web" or, to specify a json config file that you wish to use, add the full file path to the command. For example, run the command "npm run start:web D:/timingTests.json" or "npm run start:web C:\\wireframeTimings.json". If no valid json config file has been specified, the default json config file (DefaultConfig.json) will be used. +* Run the command "npm run start:web" to just run the backend. Then, go to a web browser and go to the site http:\\\\localhost:3000 to start the test. +* Run the command "npm run test:chrome" to run the backend and automatically bring up a Google Chrome browser window to start the test. This will also automatically kill ALL Google Chrome browser windows once the test has completed. +* Run the command "npm run test:edge" to run the backend and automatically bring up an Edge browser window to start the test. This will also automatically kill ALL Edge browser windows once the test has completed. +* Run the command "npm run test:firefox" to run the backend and automatically bring up a FireFox browser window to start the test. This will also automatically kill ALL FireFox browser windows once the test has completed. -## To run a performance test with electron: -1. run the command "npm run start:electron" or, to specify a json config file that you wish to use, add the full file path to the command. For example, run the command "npm run start:electron D:/timingTests.json" or "npm run start:electron C:\\wireframeTimings.json". If no valid json config file has been specified, the default json config file (DefaultConfig.json) will be used. +## Options available for a performance test run: +These options will work for running with electron or for a browser (though specifying a specific browser when running with electron will have no effect). Always use a double backslash when specifying a file path. All of these options can be specified in any order, and you can use any number of these options. For example, to specify everything, your command could look like this: "npm run start:web chrome d:\\\\develop\\\\testConfig.json c:\\\\performanceOutput\\\\". +* To specify a json config file that you wish to use, add the full file path to the command. For example, run the command "npm run start:web D:/timingTests.json" or "npm run start:web C:\\\\wireframeTimings.json". If no valid json config file has been specified, the default json config file (DefaultConfig.json) will be used. +* To specify an output path, simply add the output path to the command. For example, run the command "npm run start:web C:\\\\output\\\\performanceData\\\\" to use this as the base path for where the output will be stored. Keep in mind that any outputPath variable specified in your json config file will be assumed to be a subdirectory of the output path specified when you called your command unless it starts with the name of a drive (i.e. C:\\\\, D:\\\\, etc.). +* To specify a particular browser that you wish to run in, you can add the option "chrome", "edge", or "firefox" to your command. If you are running intending to use a browser for the frontend, this will cause a new browser window to open to http:\\\\localhost:3000 and begin running the performance test once the backend has finished getting started. However, this option will have no effect if you are running in electron. ## Configuration json file The default configuration file allows you to specify the following: * where you want to output the files created by the test program * what you want the test file(s) created to be named -* where the imodels you want to use are located +* where the imodels you want to use are located (i.e. using a local file path or using the iModelHub) * what size you want the view screen to be * what type of test you wish to perform (either a timing test or saving an image of what has been rendered) * what models to use for testing @@ -43,6 +52,11 @@ The types of view flags that can be specified are as follows: If any settings are not specified, the program will not change these settings. For example: if no view flags were specified, the program will not specifically alter the view flags. (though the view flags may be altered depending on what settings the chosen view has applied or if a specific display style has been chosen that affects the view flags). +Specifying where the imodels are located: +If given the option of using a local file path or using the iModelHub, the program will first attempt to access the imodel using the local file path; if that fails, the program will then attempt to use the iModelHub location to access the imodel. +* To specify a local file path to use for accessing an imodel, set the "iModelLocation" setting in the json configuration file (ex. "iModelLocation": "D:/models/"). +* To specify an iModelHub project to use, set the "iModelHubProject" setting in the json configuration file (ex. "iModelHubProject": "DisplayPerformanceTest"). + The json config file allows you to specify settings for the entire test run, for a specific model, and for a specific test performed on a given model. Priority for settings will be given first to those for a specific test, then for a specific model, and finally for the entire test run. For example: if transparency is set to true for the entire test run, but a specific test changes transparency to false, that specific test will NOT have transparency even though the rest of the tests run WILL have transparency. @@ -160,6 +174,7 @@ The performance data file may also contain any or all of the following column he * Total RenderFrame Time - the time it takes to call everything in the renderFrame() function (in ms); this is the sum of all of the previous times (excluding the Tile Loading Time) * Finish GPU Queue - the time it takes to call the gl.readPixels() function (in ms); i.e. the time it takes the GPU to finish all of the tasks currently in its queue * Total Time w/ GPU - the time it takes to call everything in the renderFrame() function and for the GPU to finish all of the tasks currently in its queue (in ms); i.e. the sum of the 'Total RenderFrame Time' and the 'Finish GPU Queue' times +* Effective FPS - the average effective FPS (frames per second) value based on the calculated 'total time w/ gpu'. Keep in mind that this is the FPS rate when forcing the GPU to finish all of it's current tasks before the CPU recieves the next frame to draw. Therefore, the actual FPS value when running the program outside of this testing environment may be slightly different (it should generally be faster, as the CPU normally starts rendering the next frame while the GPU finishes up; however, it may be slower if the rendering system intentionally throttles back the fps to a set maximum--for example, 60fps) The 'View Flags' column contains a string representation of view flag specifications that differ from those defaults found in the ViewFlags class. This string representation may consist of any or all of the following: * -dim - dimensions are hidden diff --git a/test-apps/display-performance-test-app/src/frontend/index.html b/test-apps/display-performance-test-app/src/frontend/index.html new file mode 100644 index 0000000..fbcc3df --- /dev/null +++ b/test-apps/display-performance-test-app/src/frontend/index.html @@ -0,0 +1,54 @@ + + + + + Display Performance Test App + + + + + + +
+
+
+
+
+
+ + + diff --git a/test-apps/display-performance-test-app/webpack.config.js b/test-apps/display-performance-test-app/webpack.config.js deleted file mode 100644 index 0be5cd1..0000000 --- a/test-apps/display-performance-test-app/webpack.config.js +++ /dev/null @@ -1,51 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ - -const path = require("path"); -const webpack = require("webpack"); -const configLoader = require("@bentley/config-loader/lib/IModelJsConfig") -const configEnv = configLoader.IModelJsConfig.init(true /*suppress error*/, true /* suppress message */); -if (configEnv) { - Object.assign(process.env, configEnv); -} else { - console.error("Webpack failed to locate iModelJs configuration"); -} - - -const raw = process.env; -module.exports = { - mode: "development", - entry: "./lib/frontend/DisplayPerformanceTestApp.js", - output: { - path: path.resolve(__dirname, "./lib/webresources"), - filename: '[name].bundle.js', - devtoolModuleFilenameTemplate: "file:///[absolute-resource-path]" - }, - devtool: "cheap-module-source-map", - module: { - rules: [ - { - test: /\.js$/, - use: "source-map-loader", - enforce: "pre" - } - ] - }, - node: { - fs: "empty" - }, - stats: "errors-only", - externals: { - electron: "require('electron')" - }, - plugins: [ - new webpack.DefinePlugin({ - "process.env": Object.keys(raw).filter(v => v.match(/^imjs_/i)).reduce((env, key) => { - env[key] = JSON.stringify(raw[key]); - return env; - }, {}) - }) - ] -}; diff --git a/test-apps/display-test-app/README.md b/test-apps/display-test-app/README.md index a8aaaf8..0ac65f7 100644 --- a/test-apps/display-test-app/README.md +++ b/test-apps/display-test-app/README.md @@ -30,9 +30,9 @@ The application may be ran as an Electron app, Mobile app or within a browser. T npm run start:electron ``` -* To start the application in a browser, run the following command, and then navigate to the URL for display-test-app that prints to the console: +* To start the application in a browser, run the following command, and then navigate to localhost:3000 in any supported browser (not Internet Explorer): ``` - npm run start:web + npm run start:servers ``` ## Using display-test-app @@ -76,3 +76,5 @@ Debugging display-test-app can be accomplished using the following procedures, d * If defined, do not open the electron dev tools on startup * SVT_LOG_LEVEL * If defined, the minimum logging level will be set to this value. Log messages are output to the terminal from which display-test-app was run. Example log levels include "debug", "error", "warning", etc - see Logger.ParseLogLevel() for the complete list. +* SVT_DISABLE_DIAGNOSTICS + * By default, all debug-only code paths are enabled. These include assertions, console output, and potentially-expensive WebGL state checks like checkFramebufferStatus(). If this environment variable is defined (value does not matter), all of these debug-only code paths will be disabled. Note that this *only* affects assertions and console output produced within the rendering code. diff --git a/test-apps/display-test-app/linkplugins.js b/test-apps/display-test-app/linkplugins.js new file mode 100644 index 0000000..ac00e51 --- /dev/null +++ b/test-apps/display-test-app/linkplugins.js @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +const fs = require("fs-extra") +const path = require("path"); + +function linkFile(srcpath, destpath, filename) { + try { + fs.symlinkSync(path.join(srcpath, filename), path.join(destpath, filename), "file"); + } catch (e) { + console.log(filename + " is already linked"); + } +} + +// create a symbolic link to the Markup.js plugin, simulating how the plugin will be delivered in production +const dest = path.resolve("lib", "webresources"); + +let src = path.resolve("..", "..", "plugins", "markup", "lib", "webresources"); +linkFile(src, dest, "MarkupPlugin.js"); +linkFile(src, dest, "MarkupPlugin.js.map"); + +// create a symbolic link to the Markup.js plugin, simulating how the plugin will be delivered in production +src = path.resolve("..", "webworker-test-app", "lib", "webresources"); +linkFile(src, dest, "startWebWorkerPlugin.js"); +linkFile(src, dest, "startWebWorkerPlugin.js.map"); + +linkFile(src, dest, "testWebWorker.js"); +linkFile(src, dest, "testWebWorker.js.map"); + +src = path.resolve("..", "webworker-test-app", "assets"); +linkFile(src, dest, "galvanized03.jpg"); + +src = path.resolve("..", "..", "docs/core/learning/frontend"); +linkFile(src, dest, "accudraw.png"); + + + diff --git a/test-apps/display-test-app/package.json b/test-apps/display-test-app/package.json index 2632048..4f12aec 100644 --- a/test-apps/display-test-app/package.json +++ b/test-apps/display-test-app/package.json @@ -12,13 +12,10 @@ }, "private": true, "scripts": { - "build": "tsc 1>&2 && npm run copy:public && npm run copy:modules && npm run makeconfig && npm run webpack:main", + "build": "node ./node_modules/@bentley/webpack-tools/bin/buildIModelJsModule", "clean": "rimraf lib package-deps.json", - "copy:public": "cpx \"public/**/*\" ./lib/webresources", - "copy:modules": "node ./node_modules/@bentley/webpack-tools/modules/copyExternalModules.js --packageFile=./package.json --destDir=./lib/webresources --type=dev", - "copy:modulesprod": "node ./node_modules/@bentley/webpack-tools/modules/copyExternalModules.js --packageFile=./package.json --destDir=./lib/webresources --type=prod", - "makeconfig": "node ./node_modules/@bentley/config-loader/scripts/write.js ./lib/webresources/config.json ^imjs_", "docs": "", + "linkplugins": "node linkplugins.js", "lint": "tslint --project . 1>&2", "mobile": "tsc 1>&2 && webpack --config mobile.backend.webpack.config.js 1>&2 && webpack --config mobile.frontend.webpack.config.js 1>&2 && cpx \"public/**/*\" ./lib/mobile/public && cpx \"assets/**/*\" ./lib/mobile/assets ", "start": "npm run start:electron", @@ -27,44 +24,69 @@ "start:backend": "node lib/backend/WebMain.js", "start:servers": "run-p \"start:webserver\" \"start:backend\"", "test": "", - "cover": "", - "webpack:main": "webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.outdir=./lib/webresources --env.entry=./lib/frontend/SimpleViewTest.js --env.bundlename=main" + "cover": "" + }, + "iModelJs": { + "buildModule": { + "type": "application", + "sourceResources": [ + { + "source": "./public/configuration.json", + "dest": "./lib/webresources", + "copy": true + }, + { + "source": "./public/**/*", + "dest": "./lib/webresources" + } + ], + "webpack": { + "dest": "./lib/webresources", + "entry": "./lib/frontend/SimpleViewTest.js", + "bundleName": "main", + "htmlTemplate": "./src/frontend/index.html" + }, + "makeConfig": { + "dest": "./lib/webresources/config.json", + "filter": "^imjs_" + } + } }, "repository": {}, "dependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/electron-manager": "0.187.0", - "@bentley/geometry-core": "0.187.0", - "@bentley/imodeljs-clients": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-i18n": "0.187.0", - "@bentley/imodeljs-quantity": "0.187.0", - "@bentley/imodeljs-frontend": "0.187.0", - "@bentley/imodeljs-backend": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/electron-manager": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/imodeljs-clients": "0.189.0", + "@bentley/imodeljs-clients-backend": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-i18n": "0.189.0", + "@bentley/imodeljs-quantity": "0.189.0", + "@bentley/imodeljs-frontend": "0.189.0", + "@bentley/imodeljs-backend": "0.189.0", "body-parser": "^1.18.2", "tooltip.js": "^1.2.0" }, "devDependencies": { - "@bentley/config-loader": "0.187.0", - "@bentley/build-tools": "0.187.0", - "@bentley/webpack-tools": "0.187.0", - "@bentley/imodeljs-webserver": "0.187.0", + "@bentley/config-loader": "0.189.0", + "@bentley/build-tools": "0.189.0", + "@bentley/webpack-tools": "0.189.0", + "@bentley/imodeljs-webserver": "0.189.0", "@types/body-parser": "^1.17.0", - "@types/express": "^4.11.1", - "@types/node": "10.10.3", - "cpx": "^1.5.0", + "@types/express": "^4.16.1", + "@types/node": "10.12.18", "child_process": "^1.0.2", + "cpx": "^1.5.0", "electron": "^4.0.1", "express": "^4.16.3", + "fs-extra": "^6.0.1", "node-glob": "^1.2.0", "npm-run-all": "^4.1.5", "null-loader": "^0.1.1", "popper.js": "^1.14.4", "rimraf": "^2.6.2", - "source-map-loader": "^0.2.3", "tslint": "^5.11.0", - "typescript": "~3.1.0", - "webpack": "^4.20.2" + "typescript": "~3.2.2" }, "homepage": "http://localhost:3000/" } diff --git a/test-apps/display-test-app/public/DemoMarkup.svg b/test-apps/display-test-app/public/DemoMarkup.svg new file mode 100644 index 0000000..b063a7f --- /dev/null +++ b/test-apps/display-test-app/public/DemoMarkup.svg @@ -0,0 +1,635 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test-apps/display-test-app/public/Hazard_biological.svg b/test-apps/display-test-app/public/Hazard_biological.svg index 0ef34a2..8cd4cce 100644 --- a/test-apps/display-test-app/public/Hazard_biological.svg +++ b/test-apps/display-test-app/public/Hazard_biological.svg @@ -1,7 +1,7 @@ - + @@ -45,17 +45,8 @@ - - - - - - - - - - + diff --git a/test-apps/display-test-app/public/configuration.json b/test-apps/display-test-app/public/configuration.json index 9e26dfe..7320a4a 100644 --- a/test-apps/display-test-app/public/configuration.json +++ b/test-apps/display-test-app/public/configuration.json @@ -1 +1 @@ -{} \ No newline at end of file +{"standalone":true,"iModelName":"d:\\bim\\bimium\\i3dmtest.bim ","enableDiagnostics":true} \ No newline at end of file diff --git a/test-apps/display-test-app/public/index.css b/test-apps/display-test-app/public/index.css index c0862e4..ca99971 100644 --- a/test-apps/display-test-app/public/index.css +++ b/test-apps/display-test-app/public/index.css @@ -296,6 +296,9 @@ .bim-icon-stop:before { content: "\ea34"; } +.bim-icon-work:before { + content: "\e9b2"; +} .toolMenu { position:absolute; diff --git a/test-apps/display-test-app/public/markup.svg b/test-apps/display-test-app/public/markup.svg new file mode 100644 index 0000000..e1dbdd6 --- /dev/null +++ b/test-apps/display-test-app/public/markup.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + diff --git a/test-apps/display-test-app/src/backend/ElectronMain.ts b/test-apps/display-test-app/src/backend/ElectronMain.ts index 3f867c3..e6b69cb 100644 --- a/test-apps/display-test-app/src/backend/ElectronMain.ts +++ b/test-apps/display-test-app/src/backend/ElectronMain.ts @@ -29,6 +29,20 @@ const maximizeWindow = (undefined !== process.env.SVT_MAXIMIZE_WINDOW); (async () => { // tslint:disable-line:no-floating-promises const manager = new IModelJsElectronManager(path.join(__dirname, "..", "webresources")); + + // Handle custom keyboard shortcuts + electron.app.on("web-contents-created", (_e, wc) => { + wc.on("before-input-event", (event, input) => { + // CTRL + SHIFT + I ==> Toggle DevTools + if (input.key === "I" && input.control && !input.alt && !input.meta && input.shift) { + if (manager.mainWindow) + manager.mainWindow.webContents.toggleDevTools(); + + event.preventDefault(); + } + }); + }); + await manager.initialize({ width: 1280, height: 800, @@ -61,17 +75,4 @@ const maximizeWindow = (undefined !== process.env.SVT_MAXIMIZE_WINDOW); callback(true); }); } - - // Handle custom keyboard shortcuts - electron.app.on("web-contents-created", (_e, wc) => { - wc.on("before-input-event", (event, input) => { - // CTRL + SHIFT + I ==> Toggle DevTools - if (input.key === "I" && input.control && !input.alt && !input.meta && input.shift) { - if (manager.mainWindow) - manager.mainWindow.webContents.toggleDevTools(); - - event.preventDefault(); - } - }); - }); })(); diff --git a/test-apps/display-test-app/src/backend/backend.ts b/test-apps/display-test-app/src/backend/backend.ts index 4c2cfd9..745e9fd 100644 --- a/test-apps/display-test-app/src/backend/backend.ts +++ b/test-apps/display-test-app/src/backend/backend.ts @@ -9,7 +9,7 @@ import * as fs from "fs"; import * as path from "path"; import { IModelJsConfig } from "@bentley/config-loader/lib/IModelJsConfig"; import { IModelBankClient, Config } from "@bentley/imodeljs-clients"; -import { UrlFileHandler } from "@bentley/imodeljs-clients/lib/UrlFileHandler"; +import { UrlFileHandler } from "@bentley/imodeljs-clients-backend"; import { SVTConfiguration } from "../common/SVTConfiguration"; IModelJsConfig.init(true /* suppress exception */, true /* suppress error message */, Config.App); @@ -27,6 +27,7 @@ function setupStandaloneConfiguration() { configuration.standalonePath = process.env.SVT_STANDALONE_FILEPATH; // optional (browser-use only) configuration.viewName = process.env.SVT_STANDALONE_VIEWNAME; // optional configuration.iModelName = filename; + configuration.enableDiagnostics = undefined === process.env.SVT_DISABLE_DIAGNOSTICS; if (undefined !== process.env.SVT_STANDALONE_SIGNIN) configuration.signInForStandalone = true; @@ -39,6 +40,8 @@ export function initializeBackend() { setupStandaloneConfiguration(); const hostConfig = new IModelHostConfiguration(); + hostConfig.useTileContentThreadPool = true; + // tslint:disable-next-line:no-var-requires const configPathname = path.normalize(path.join(__dirname, "../webresources", "configuration.json")); const svtConfig: SVTConfiguration = require(configPathname); diff --git a/test-apps/display-test-app/src/common/SVTConfiguration.ts b/test-apps/display-test-app/src/common/SVTConfiguration.ts index df23b96..8044edb 100644 --- a/test-apps/display-test-app/src/common/SVTConfiguration.ts +++ b/test-apps/display-test-app/src/common/SVTConfiguration.ts @@ -12,6 +12,7 @@ export interface SVTConfiguration { filename?: string; standalonePath?: string; // Used when run in the browser - a common base path for all standalone imodels signInForStandalone?: boolean; // If true, and standalone is true, then sign in. Required when opening local files containing reality models. + enableDiagnostics?: boolean; // If true, all RenderDiagnostics will be enabled (assertions, debug output, GL state checks). } export interface ConnectProjectConfiguration { diff --git a/test-apps/display-test-app/src/frontend/App.ts b/test-apps/display-test-app/src/frontend/App.ts index ea71871..4ba29e4 100644 --- a/test-apps/display-test-app/src/frontend/App.ts +++ b/test-apps/display-test-app/src/frontend/App.ts @@ -4,23 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import { XAndY } from "@bentley/geometry-core"; -import ToolTip from "tooltip.js"; import { - AccuSnap, - IModelApp, - MessageBoxIconType, - MessageBoxType, - MessageBoxValue, - NotificationManager, - NotifyMessageDetails, - SnapMode, - ToolTipOptions, + AccuSnap, IModelApp, MessageBoxIconType, MessageBoxType, MessageBoxValue, NotificationManager, NotifyMessageDetails, + SnapMode, ToolTipOptions, TileAdmin, } from "@bentley/imodeljs-frontend"; +import ToolTip from "tooltip.js"; import { DrawingAidTestTool } from "./DrawingAidTestTool"; -import { showStatus, showError } from "./Utils"; +import { showError, showStatus } from "./Utils"; class DisplayTestAppAccuSnap extends AccuSnap { - private readonly _activeSnaps: SnapMode[] = [SnapMode.NearestKeypoint]; + private readonly _activeSnaps: SnapMode[] = [SnapMode.NearestKeypoint]; public get keypointDivisor() { return 2; } public getActiveSnapModes(): SnapMode[] { return this._activeSnaps; } @@ -76,8 +69,8 @@ class Notifications extends NotificationManager { return Promise.resolve(MessageBoxValue.Ok); } - public get isToolTipSupported(): boolean { return true; } - public get isToolTipOpen(): boolean { return undefined !== this._toolTip; } + public get isToolTipSupported() { return true; } + public get isToolTipOpen() { return undefined !== this._toolTip; } public clearToolTip(): void { if (!this.isToolTipOpen) @@ -93,9 +86,10 @@ class Notifications extends NotificationManager { protected _showToolTip(el: HTMLElement, message: HTMLElement | string, pt?: XAndY, options?: ToolTipOptions): void { this.clearToolTip(); - const rect = el.getBoundingClientRect(); - if (undefined === pt) + if (undefined === pt) { + const rect = el.getBoundingClientRect(); pt = { x: rect.width / 2, y: rect.height / 2 }; + } const location = document.createElement("div"); const height = 20; @@ -119,6 +113,10 @@ export class DisplayTestApp extends IModelApp { protected static onStartup(): void { IModelApp.accuSnap = new DisplayTestAppAccuSnap(); IModelApp.notifications = new Notifications(); + IModelApp.tileAdmin = TileAdmin.create({ + retryInterval: 50, + }); + const svtToolNamespace = IModelApp.i18n.registerNamespace("SVTTools"); DrawingAidTestTool.register(svtToolNamespace); } diff --git a/test-apps/display-test-app/src/frontend/Button.ts b/test-apps/display-test-app/src/frontend/Button.ts index 17182cb..523082d 100644 --- a/test-apps/display-test-app/src/frontend/Button.ts +++ b/test-apps/display-test-app/src/frontend/Button.ts @@ -5,30 +5,32 @@ export interface Button { button: HTMLInputElement; - div: HTMLDivElement; + div: HTMLElement; } export type ButtonHandler = (button: HTMLInputElement) => void; export interface ButtonProps { handler: ButtonHandler; - id: string; + id?: string; parent?: HTMLElement; value: string; + inline?: boolean; + tooltip?: string; } export function createButton(props: ButtonProps): Button { - const div = document.createElement("div"); + const div = document.createElement(props.inline ? "span" : "div"); const button = document.createElement("input") as HTMLInputElement; button.type = "button"; - button.id = props.id; button.value = props.value; button.addEventListener("click", () => props.handler(button)); div.appendChild(button); - if (undefined !== props.parent) - props.parent.appendChild(div); + if (undefined !== props.id) button.id = props.id; + if (undefined !== props.tooltip) div.title = props.tooltip; + if (undefined !== props.parent) props.parent.appendChild(div); return { button, div }; } diff --git a/test-apps/display-test-app/src/frontend/CategoryPicker.ts b/test-apps/display-test-app/src/frontend/CategoryPicker.ts index 5886ecc..2026923 100644 --- a/test-apps/display-test-app/src/frontend/CategoryPicker.ts +++ b/test-apps/display-test-app/src/frontend/CategoryPicker.ts @@ -41,8 +41,7 @@ export class CategoryPicker extends ToolBarDropDown { const toggleAll = this.addCheckbox("Toggle All", "cat_toggleAll", false, (enabled: boolean) => this.toggleAll(enabled)); const ecsql = "SELECT ECInstanceId as id, CodeValue as code, UserLabel as label FROM " + (view.is3d() ? "BisCore.SpatialCategory" : "BisCore.DrawingCategory"); - const rows = await view.iModel.executeQuery(ecsql); - + const rows = Array.from(await view.iModel.queryPage(ecsql, undefined, { size: 1000 })); // max rows to return after which result will be truncated. rows.sort((lhs, rhs) => { const lhName = getCategoryName(lhs); const rhName = getCategoryName(rhs); diff --git a/test-apps/display-test-app/src/frontend/CustomCloudEnv.ts b/test-apps/display-test-app/src/frontend/CustomCloudEnv.ts index 2f89522..f43bc32 100644 --- a/test-apps/display-test-app/src/frontend/CustomCloudEnv.ts +++ b/test-apps/display-test-app/src/frontend/CustomCloudEnv.ts @@ -20,8 +20,12 @@ export async function initializeCustomCloudEnv(state: SimpleViewState, url: stri foreignAccessTokenWrapper[AccessToken.foreignProjectAccessTokenJsonProperty] = { userInfo }; state.accessToken = AccessToken.fromForeignProjectAccessTokenJson(JSON.stringify(foreignAccessTokenWrapper)); + let projectName = "iModelJsTest"; + if (state.projectConfig !== undefined && state.projectConfig.projectName !== undefined) + projectName = state.projectConfig!.projectName; + const bankContextClient = new IModelBankFileSystemContextClient(url); - state.project = await bankContextClient.queryContextByName(new ActivityLoggingContext(""), state.accessToken!, state.projectConfig!.projectName); + state.project = await bankContextClient.queryContextByName(new ActivityLoggingContext(""), state.accessToken!, projectName); IModelApp.iModelClient = new IModelBankClient(url, undefined); } diff --git a/test-apps/display-test-app/src/frontend/DebugPanel.ts b/test-apps/display-test-app/src/frontend/DebugPanel.ts index 03cebb2..c306375 100644 --- a/test-apps/display-test-app/src/frontend/DebugPanel.ts +++ b/test-apps/display-test-app/src/frontend/DebugPanel.ts @@ -76,6 +76,7 @@ export class DebugPanel extends ToolBarDropDown { { name: "None", value: Tile.DebugBoundingBoxes.None }, { name: "Volume", value: Tile.DebugBoundingBoxes.Volume }, { name: "Content", value: Tile.DebugBoundingBoxes.Content }, + { name: "Both", value: Tile.DebugBoundingBoxes.Both }, ], }); } diff --git a/test-apps/display-test-app/src/frontend/FeatureOverrides.ts b/test-apps/display-test-app/src/frontend/FeatureOverrides.ts new file mode 100644 index 0000000..3411205 --- /dev/null +++ b/test-apps/display-test-app/src/frontend/FeatureOverrides.ts @@ -0,0 +1,346 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { + dispose, + Id64String, + IDisposable, +} from "@bentley/bentleyjs-core"; +import { + ColorByName, + ColorDef, + LinePixels, + RgbColor, +} from "@bentley/imodeljs-common"; +import { + EmphasizeElements, + FeatureOverrideProvider, + FeatureSymbology, + Viewport, +} from "@bentley/imodeljs-frontend"; +import { ToolBarDropDown } from "./ToolBar"; +import { createButton } from "./Button"; +import { createNumericInput } from "./NumericInput"; +import { createCheckBox } from "./CheckBox"; +import { createComboBox } from "./ComboBox"; + +export function emphasizeSelectedElements(vp: Viewport): void { + const emph = EmphasizeElements.getOrCreate(vp); + if (emph.overrideSelectedElements(vp, new ColorDef(ColorByName.orange), undefined, true, false) // replace existing; don't clear selection set... + && emph.emphasizeSelectedElements(vp, undefined, true)) { // ...replace existing; now clear selection set + vp.isFadeOutActive = true; + } else { + EmphasizeElements.clear(vp); // clear any previous overrides + vp.isFadeOutActive = false; + } +} + +export class Provider implements FeatureOverrideProvider { + private readonly _elementOvrs = new Map(); + private _defaultOvrs: FeatureSymbology.Appearance | undefined; + private readonly _vp: Viewport; + + private constructor(vp: Viewport) { this._vp = vp; } + + public addFeatureOverrides(ovrs: FeatureSymbology.Overrides, _vp: Viewport): void { + this._elementOvrs.forEach((value, key) => ovrs.overrideElement(key, value)); + if (undefined !== this._defaultOvrs) + ovrs.setDefaultOverrides(this._defaultOvrs); + } + + public overrideElements(app: FeatureSymbology.Appearance): void { + for (const id of this._vp.iModel.selectionSet.elements) + this._elementOvrs.set(id, app); + + this.sync(); + } + + public clear(): void { + this._elementOvrs.clear(); + this._defaultOvrs = undefined; + this.sync(); + } + + public set defaults(value: FeatureSymbology.Appearance | undefined) { + this._defaultOvrs = value; + this.sync(); + } + + private sync(): void { this._vp.view.setFeatureOverridesDirty(); } + + public static get(vp: Viewport): Provider | undefined { + return vp.featureOverrideProvider instanceof Provider ? vp.featureOverrideProvider : undefined; + } + + public static remove(vp: Viewport): void { + if (undefined !== this.get(vp)) + vp.featureOverrideProvider = undefined; + } + + public static getOrCreate(vp: Viewport): Provider { + let provider = this.get(vp); + if (undefined === provider) { + provider = new Provider(vp); + vp.featureOverrideProvider = provider; + } + + return provider; + } +} + +export class Settings implements IDisposable { + private _appearance = FeatureSymbology.Appearance.defaults; + private readonly _vp: Viewport; + private readonly _parent: HTMLElement; + private readonly _element: HTMLElement; + + public constructor(vp: Viewport, parent: HTMLElement) { + this._vp = vp; + this._parent = parent; + + this._element = document.createElement("div"); + this._element.className = "toolMenu"; + this._element.style.cssFloat = "left"; + this._element.style.display = "block"; + + this.addColor(this._element); + this.addTransparency(this._element); + this.addWeight(this._element); + this.addStyle(this._element); + + createCheckBox({ + parent: this._element, + name: "Ignore Material", + id: "ovr_ignoreMaterial", + handler: (cb) => this.updateIgnoreMaterial(cb.checked ? true : undefined), + }); + + createCheckBox({ + parent: this._element, + name: "Non-locatable", + id: "ovr_nonLocatable", + handler: (cb) => this.updateNonLocatable(cb.checked ? true : undefined), + }); + + const buttonDiv = document.createElement("div"); + buttonDiv.style.textAlign = "center"; + createButton({ + value: "Apply", + handler: () => this._provider.overrideElements(this._appearance), + parent: buttonDiv, + inline: true, + tooltip: "Apply overrides to selection set", + }); + createButton({ + value: "Default", + handler: () => this._provider.defaults = this._appearance, + parent: buttonDiv, + inline: true, + tooltip: "Set as default overrides", + }); + createButton({ + value: "Clear", + handler: () => this._provider.clear(), + parent: buttonDiv, + inline: true, + tooltip: "Remove all overrides", + }); + + this._element.appendChild(document.createElement("hr")); + this._element.appendChild(buttonDiv); + + parent.appendChild(this._element); + } + + public dispose(): void { + this._parent.removeChild(this._element); + } + + private get _provider() { return Provider.getOrCreate(this._vp); } + + // private reset() { this._appearance = FeatureSymbology.Appearance.defaults; } + + private updateAppearance(field: "rgb" | "transparency" | "linePixels" | "weight" | "ignoresMaterial" | "nonLocatable", value: any): void { + const props = this._appearance.toJSON(); + props[field] = value; + this._appearance = FeatureSymbology.Appearance.fromJSON(props); + } + + private updateColor(rgb: RgbColor | undefined): void { this.updateAppearance("rgb", rgb); } + private updateTransparency(transparency: number | undefined): void { this.updateAppearance("transparency", transparency); } + private updateWeight(weight: number | undefined): void { this.updateAppearance("weight", weight); } + private updateIgnoreMaterial(ignoresMaterial: true | undefined): void { this.updateAppearance("ignoresMaterial", ignoresMaterial); } + private updateNonLocatable(nonLocatable: true | undefined): void { this.updateAppearance("nonLocatable", nonLocatable); } + private updateStyle(style: LinePixels): void { + const linePixels = LinePixels.Invalid !== style ? style : undefined; + this.updateAppearance("linePixels", linePixels); + } + + private addTransparency(parent: HTMLElement): void { + const div = document.createElement("div"); + + const cb = document.createElement("input"); + cb.type = "checkbox"; + cb.id = "cb_ovrTrans"; + div.appendChild(cb); + + const label = document.createElement("label"); + label.htmlFor = "cb_ovrTrans"; + label.innerText = "Transparency "; + div.appendChild(label); + + const slider = document.createElement("input"); + slider.type = "range"; + slider.className = "slider"; + slider.min = "0.0"; + slider.max = "1.0"; + slider.step = "0.05"; + slider.value = "0.0"; + slider.disabled = true; + div.appendChild(slider); + + slider.addEventListener("input", () => this.updateTransparency(parseFloat(slider.value))); + cb.addEventListener("click", () => { + slider.disabled = !cb.checked; + this.updateTransparency(cb.checked ? parseFloat(slider.value) : undefined); + }); + + parent.appendChild(div); + } + + private addWeight(parent: HTMLElement): void { + const div = document.createElement("div"); + + const cb = document.createElement("input"); + cb.type = "checkbox"; + cb.id = "cb_ovrWeight"; + div.appendChild(cb); + + const label = document.createElement("label"); + label.htmlFor = "cb_ovrWeight"; + label.innerText = "Weight "; + div.appendChild(label); + + const num = createNumericInput({ + parent: div, + value: 1, + disabled: true, + min: 1, + max: 31, + step: 1, + handler: (value) => this.updateWeight(value), + }); + div.appendChild(num); + + cb.addEventListener("click", () => { + num.disabled = !cb.checked; + this.updateWeight(cb.checked ? parseInt(num.value, 10) : undefined); + }); + + parent.appendChild(div); + } + + private addStyle(parent: HTMLElement): void { + const entries = [ + { name: "Not overridden", value: LinePixels.Invalid }, + { name: "Solid", value: LinePixels.Solid }, + { name: "Hidden Line", value: LinePixels.HiddenLine }, + { name: "Invisible", value: LinePixels.Invisible }, + { name: "Code1", value: LinePixels.Code1 }, + { name: "Code2", value: LinePixels.Code2 }, + { name: "Code3", value: LinePixels.Code3 }, + { name: "Code4", value: LinePixels.Code4 }, + { name: "Code5", value: LinePixels.Code5 }, + { name: "Code6", value: LinePixels.Code6 }, + { name: "Code7", value: LinePixels.Code7 }, + ]; + + createComboBox({ + parent, + entries, + id: "ovr_Style", + name: "Style ", + value: LinePixels.Invalid, + handler: (select) => this.updateStyle(parseInt(select.value, 10)), + }); + } + + private addColor(parent: HTMLElement): void { + const div = document.createElement("div"); + + const cb = document.createElement("input"); + cb.type = "checkbox"; + cb.id = "cb_ovrColor"; + div.appendChild(cb); + + const label = document.createElement("label"); + label.htmlFor = "cb_ovrColor"; + label.innerText = "Color "; + div.appendChild(label); + + const colorDiv = document.createElement("div"); + const inputs: HTMLInputElement[] = []; + const update = () => this.updateColor(new RgbColor(parseInt(inputs[0].value, 10), parseInt(inputs[1].value, 10), parseInt(inputs[2].value, 10))); + const props = { + parent: colorDiv, + value: 255, + disabled: true, + min: 0, + max: 255, + step: 1, + handler: () => update(), + }; + + inputs.push(createNumericInput(props), createNumericInput(props), createNumericInput(props)); + const labels = [ "R", "G", "B" ]; + for (let i = 0; i < 3; i++) { + const colorName = labels[i]; + const id = "ovr_Color" + colorName; + const colorLabel = document.createElement("label"); + colorLabel.htmlFor = id; + colorLabel.innerText = colorName + " "; + colorDiv.appendChild(colorLabel); + + const colorInput = inputs[i]; + colorInput.id = id; + colorDiv.appendChild(colorInput); + } + + cb.addEventListener("click", () => { + for (const input of inputs) + input.disabled = !cb.checked; + + if (cb.checked) + update(); + else + this.updateColor(undefined); + }); + + div.appendChild(colorDiv); + parent.appendChild(div); + } +} + +export class FeatureOverridesPanel extends ToolBarDropDown { + private readonly _vp: Viewport; + private readonly _parent: HTMLElement; + private _settings?: Settings; + + public constructor(vp: Viewport, parent: HTMLElement) { + super(); + this._vp = vp; + this._parent = parent; + this.open(); + } + + public get onViewChanged(): Promise { + Provider.remove(this._vp); + return Promise.resolve(); + } + + protected _open(): void { this._settings = new Settings(this._vp, this._parent); } + protected _close(): void { this._settings = dispose(this._settings); } + public get isOpen(): boolean { return undefined !== this._settings; } +} diff --git a/test-apps/display-test-app/src/frontend/IModelApi.ts b/test-apps/display-test-app/src/frontend/IModelApi.ts index c5237f4..ec6094b 100644 --- a/test-apps/display-test-app/src/frontend/IModelApi.ts +++ b/test-apps/display-test-app/src/frontend/IModelApi.ts @@ -34,5 +34,4 @@ export class IModelApi { public static async openIModel(accessToken: AccessToken, projectId: string, iModelId: string, changeSetId: string | undefined, openMode: OpenMode): Promise { return IModelConnection.open(accessToken!, projectId, iModelId, openMode, changeSetId ? IModelVersion.asOfChangeSet(changeSetId) : IModelVersion.latest()); } - } diff --git a/test-apps/display-test-app/src/frontend/IncidentMarkerDemo.ts b/test-apps/display-test-app/src/frontend/IncidentMarkerDemo.ts index bb5c115..14f0b71 100644 --- a/test-apps/display-test-app/src/frontend/IncidentMarkerDemo.ts +++ b/test-apps/display-test-app/src/frontend/IncidentMarkerDemo.ts @@ -2,34 +2,12 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ - +import { AngleSweep, Arc3d, Point2d, Point3d, XAndY, XYAndZ } from "@bentley/geometry-core"; +import { AxisAlignedBox3d, ColorByName, ColorDef } from "@bentley/imodeljs-common"; import { - imageElementFromUrl, - BeButton, - BeButtonEvent, - Cluster, - DecorateContext, - GraphicType, - IModelApp, - Marker, - MarkerImage, - MarkerSet, - MessageBoxIconType, - MessageBoxType, + BeButton, BeButtonEvent, Cluster, DecorateContext, GraphicType, imageElementFromUrl, + IModelApp, Marker, MarkerImage, MarkerSet, MessageBoxIconType, MessageBoxType, } from "@bentley/imodeljs-frontend"; -import { - AxisAlignedBox3d, - ColorByName, - ColorDef, -} from "@bentley/imodeljs-common"; -import { - AngleSweep, - Arc3d, - Point2d, - Point3d, - XAndY, - XYAndZ, -} from "@bentley/geometry-core"; /** Example Marker to show an *incident*. Each incident has an *id*, a *severity*, and an *icon*. */ class IncidentMarker extends Marker { @@ -58,16 +36,6 @@ class IncidentMarker extends Marker { return true; } - // /** draw a filled square with the incident color and a white outline */ - // public drawFunc(ctx: CanvasRenderingContext2D) { - // ctx.beginPath(); - // ctx.fillStyle = this._color.toHexString(); - // ctx.rect(-11, -11, 20, 20); - // ctx.fill(); - // ctx.strokeStyle = "white"; - // ctx.stroke(); - // } - /** Create a new IncidentMarker */ constructor(location: XYAndZ, public severity: number, public id: number, icon: Promise) { super(location, IncidentMarker._size); @@ -160,12 +128,12 @@ class IncidentMarkerSet extends MarkerSet { * "incidents" at random locations within the ProjectExtents. For each incident, it creates an IncidentMarker with an Id and * with a random value between 1-30 for "severity", and one of 5 possible icons. */ -class IncidentMarkerDemo { +export class IncidentMarkerDemo { public static warningSign?: HTMLImageElement; private _incidents = new IncidentMarkerSet(); private static _decorator?: IncidentMarkerDemo; // static variable just so we can tell if the demo is active. - public constructor(extents: AxisAlignedBox3d) { + private constructor(extents: AxisAlignedBox3d) { const markerIcons = [ imageElementFromUrl("Hazard_biological.svg"), imageElementFromUrl("Hazard_electric.svg"), @@ -195,7 +163,7 @@ class IncidentMarkerDemo { /** Turn the markers on and off. Each time it runs it creates a new random set of incidents. */ public static toggle(extents: AxisAlignedBox3d) { if (undefined === IncidentMarkerDemo._decorator) { - // start the demo by creating the demo object and adding it as a ViewManager decorator. + // start the demo by creating the IncidentMarkerDemo object and adding it as a ViewManager decorator. IncidentMarkerDemo._decorator = new IncidentMarkerDemo(extents); IModelApp.viewManager.addDecorator(IncidentMarkerDemo._decorator); } else { @@ -205,7 +173,3 @@ class IncidentMarkerDemo { } } } - -export function toggleIncidentMarkers(extents: AxisAlignedBox3d): void { - IncidentMarkerDemo.toggle(extents); -} diff --git a/test-apps/display-test-app/src/frontend/MemoryTracker.ts b/test-apps/display-test-app/src/frontend/MemoryTracker.ts index ab9cea2..386a1d1 100644 --- a/test-apps/display-test-app/src/frontend/MemoryTracker.ts +++ b/test-apps/display-test-app/src/frontend/MemoryTracker.ts @@ -136,7 +136,7 @@ export class MemoryTracker { this.addSelector(parent); this._textures = new MemoryPanel(this._div, "Textures", ["Surface Textures", "Vertex Tables", "Feature Tables", "Feature Overrides", "Clip Volumes"]); - this._buffers = new MemoryPanel(this._div, "Buffers", ["Surfaces", "Visible Edges", "Silhouettes", "Polyline Edges", "Polylines", "Point Strings", "Point Clouds"]); + this._buffers = new MemoryPanel(this._div, "Buffers", ["Surfaces", "Visible Edges", "Silhouettes", "Polyline Edges", "Polylines", "Point Strings", "Point Clouds", "Instances"]); this._totalElem = this.addStatistics(this._div); this._purgeButton = this.addPurgeButton(this._div); diff --git a/test-apps/display-test-app/src/frontend/NumericInput.ts b/test-apps/display-test-app/src/frontend/NumericInput.ts new file mode 100644 index 0000000..6017df1 --- /dev/null +++ b/test-apps/display-test-app/src/frontend/NumericInput.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +export type NumericInputHandler = (value: number, input: HTMLInputElement) => void; + +export interface NumericInputProps { + handler: NumericInputHandler; + id?: string; + parent?: HTMLElement; + value: number; + display?: "inline" | "none" | "block"; + disabled?: boolean; + min?: number; + max?: number; + step?: number; + tooltip?: string; +} + +export function createNumericInput(props: NumericInputProps): HTMLInputElement { + const input = document.createElement("input"); + input.type = "number"; + input.value = props.value.toString(); + + input.onchange = () => { + try { + const value = parseInt(input.value, 10); + props.handler(value, input); + } catch (_ex) { + // + } + }; + + if (undefined !== props.id) input.id = props.id; + if (undefined !== props.display) input.style.display = props.display; + if (undefined !== props.min) input.min = props.min.toString(); + if (undefined !== props.max) input.max = props.max.toString(); + if (undefined !== props.step) input.step = props.step.toString(); + if (undefined !== props.tooltip) input.title = props.tooltip; + if (undefined !== props.disabled) input.disabled = props.disabled; + if (undefined !== props.parent) props.parent.appendChild(input); + + return input; +} diff --git a/test-apps/display-test-app/src/frontend/RealityModelPicker.ts b/test-apps/display-test-app/src/frontend/RealityModelPicker.ts index 158593c..0b9bfb6 100644 --- a/test-apps/display-test-app/src/frontend/RealityModelPicker.ts +++ b/test-apps/display-test-app/src/frontend/RealityModelPicker.ts @@ -5,6 +5,7 @@ import { ContextRealityModelProps, + CartographicRange, } from "@bentley/imodeljs-common"; import { ContextRealityModelState, @@ -20,7 +21,7 @@ export class RealityModelPicker extends ToolBarDropDown { private readonly _element: HTMLElement; private readonly _parent: HTMLElement; private readonly _models: ContextRealityModelState[] = []; - private readonly _availableModels: ContextRealityModelProps[] = []; + private readonly _availableModels: Promise; public constructor(vp: Viewport, parent: HTMLElement) { super(); @@ -28,7 +29,13 @@ export class RealityModelPicker extends ToolBarDropDown { this._vp = vp; this._parent = parent; - this._availableModels = ContextRealityModelState.findAvailableRealityModels(); + if (this._vp.iModel.ecefLocation) { + const modelCartographicRange = new CartographicRange(this._vp.iModel.projectExtents, this._vp.iModel.ecefLocation.getTransform()); + + this._availableModels = ContextRealityModelState.findAvailableRealityModels("fb1696c8-c074-4c76-a539-a5546e048cc6", modelCartographicRange); + } else { + this._availableModels = ContextRealityModelState.findAvailableRealityModels("fb1696c8-c074-4c76-a539-a5546e048cc6", undefined); + } this._element = document.createElement("div"); this._element.className = "scrollingToolMenu"; @@ -47,7 +54,7 @@ export class RealityModelPicker extends ToolBarDropDown { while (this._element.hasChildNodes()) this._element.removeChild(this._element.firstChild!); - let visible = this._vp.view.isSpatialView() && this._availableModels.length > 0; + let visible = this._vp.view.isSpatialView() && (await this._availableModels).length > 0; if (visible) { await this.populateModels(); visible = this._models.length > 0; @@ -70,10 +77,13 @@ export class RealityModelPicker extends ToolBarDropDown { } private async populateModels(): Promise { - for (const props of this._availableModels) { - const model = new ContextRealityModelState(props, this._vp.iModel); - if (await model.intersectsProjectExtents()) - this._models.push(model); + for (const props of await this._availableModels) { + try { + const model = new ContextRealityModelState(props, this._vp.iModel); + if (await model.intersectsProjectExtents()) + this._models.push(model); + } catch (e) { + } } } diff --git a/test-apps/display-test-app/src/frontend/SimpleViewTest.ts b/test-apps/display-test-app/src/frontend/SimpleViewTest.ts index 18db4b2..48201b8 100644 --- a/test-apps/display-test-app/src/frontend/SimpleViewTest.ts +++ b/test-apps/display-test-app/src/frontend/SimpleViewTest.ts @@ -21,12 +21,14 @@ import { IModelApp, IModelConnection, OidcClientWrapper, + RenderDiagnostics, } from "@bentley/imodeljs-frontend"; import { SimpleViewState } from "./SimpleViewState"; import { showStatus } from "./Utils"; import { SVTConfiguration } from "../common/SVTConfiguration"; import { DisplayTestApp } from "./App"; import { Viewer } from "./Viewer"; +import { initializeCustomCloudEnv } from "./CustomCloudEnv"; const activeViewState: SimpleViewState = new SimpleViewState(); const configuration = {} as SVTConfiguration; @@ -94,9 +96,14 @@ async function main() { // retrieve, set, and output the global configuration variable await retrieveConfiguration(); // (does a fetch) console.log("Configuration", JSON.stringify(configuration)); // tslint:disable-line:no-console + if (configuration.customOrchestratorUri) + await initializeCustomCloudEnv(activeViewState, configuration.customOrchestratorUri); } + // Start the app. (This tries to fetch a number of localization json files from the origin.) DisplayTestApp.startup(); + if (configuration.enableDiagnostics) + DisplayTestApp.renderSystem.enableDiagnostics(RenderDiagnostics.All); // Choose RpcConfiguration based on whether we are in electron or browser let rpcConfiguration: RpcConfiguration; @@ -114,7 +121,7 @@ async function main() { RpcOperation.forEach(definition, (operation) => operation.policy.token = (_request) => new IModelToken("test", "test", "test", "test", OpenMode.Readonly)); } - if (!configuration.standalone) { + if (!configuration.standalone && !configuration.customOrchestratorUri) { alert("Standalone iModel required. Set SVT_STANDALONE_FILENAME in environment"); return; } diff --git a/test-apps/display-test-app/src/frontend/StandardRotations.ts b/test-apps/display-test-app/src/frontend/StandardRotations.ts index bb675ef..607f125 100644 --- a/test-apps/display-test-app/src/frontend/StandardRotations.ts +++ b/test-apps/display-test-app/src/frontend/StandardRotations.ts @@ -3,21 +3,18 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { Transform } from "@bentley/geometry-core"; -import { StandardViewId, Viewport, AccuDraw } from "@bentley/imodeljs-frontend"; +import { IModelApp } from "@bentley/imodeljs-frontend"; import { ToolBarDropDown, createToolButton } from "./ToolBar"; // Indexed by StandardViewId enum -const entries = [ "top", "bottom", "left", "right", "front", "back", "isoleft", "isoright" ]; +const entries = ["top", "bottom", "left", "right", "front", "back", "isoleft", "isoright"]; export class StandardRotations extends ToolBarDropDown { - private readonly _vp: Viewport; private readonly _element: HTMLElement; private readonly _parent: HTMLElement; - public constructor(vp: Viewport, parent: HTMLElement) { + public constructor(parent: HTMLElement) { super(); - this._vp = vp; this._parent = parent; this._element = document.createElement("div"); @@ -37,7 +34,7 @@ export class StandardRotations extends ToolBarDropDown { div.appendChild(createToolButton({ className: "bim-icon-view" + entries[i], - click: () => this.apply(i), + click: () => IModelApp.tools.run("View.Standard", IModelApp.viewManager.selectedView!, i), })); } @@ -49,24 +46,7 @@ export class StandardRotations extends ToolBarDropDown { protected _close() { this._element.style.display = "none"; } public get onViewChanged(): Promise { - this._parent.style.display = this._vp.view.allow3dManipulations() ? "block" : "none"; + this._parent.style.display = IModelApp.viewManager.selectedView!.view.allow3dManipulations() ? "block" : "none"; return Promise.resolve(); } - - private apply(id: StandardViewId): void { - const rMatrix = AccuDraw.getStandardRotation(id, this._vp, this._vp.isContextRotationRequired); - const inverse = rMatrix.inverse(); - if (undefined === inverse) - return; - - const targetMatrix = inverse.multiplyMatrixMatrix(this._vp.rotation); - const rotateTransform = Transform.createFixedPointAndMatrix(this._vp.view.getTargetPoint(), targetMatrix); - const startFrustum = this._vp.getFrustum(); - const newFrustum = startFrustum.clone(); - newFrustum.multiply(rotateTransform); - - this._vp.animateFrustumChange(startFrustum, newFrustum); - this._vp.view.setupFromFrustum(newFrustum); - this._vp.synchWithView(true); - } } diff --git a/test-apps/display-test-app/src/frontend/TileStatisticsTracker.ts b/test-apps/display-test-app/src/frontend/TileStatisticsTracker.ts index ec3df9c..ceef4d7 100644 --- a/test-apps/display-test-app/src/frontend/TileStatisticsTracker.ts +++ b/test-apps/display-test-app/src/frontend/TileStatisticsTracker.ts @@ -9,6 +9,7 @@ import { Viewport, } from "@bentley/imodeljs-frontend"; import { createCheckBox } from "./CheckBox"; +import { createNumericInput } from "./NumericInput"; const enum StatIndex { Active, @@ -94,28 +95,21 @@ export class StatsTracker { label.innerText = "Max Active Requests: "; div.appendChild(label); - const input = document.createElement("input") as HTMLInputElement; - input.style.display = "inline"; - input.type = "number"; - input.min = "0"; - input.id = "maxActiveRequests"; - input.step = "1"; - input.value = IModelApp.tileAdmin.maxActiveRequests.toString(); - input.onchange = () => this.updateMaxActive(input); - div.appendChild(input); + createNumericInput({ + parent: div, + id: "maxActiveRequests", + display: "inline", + min: 0, + step: 1, + value: IModelApp.tileAdmin.maxActiveRequests, + handler: (value, _input) => this.updateMaxActive(value), + }); parent.appendChild(div); } - private updateMaxActive(input: HTMLInputElement): void { - const admin = IModelApp.tileAdmin; - try { - admin.maxActiveRequests = Number.parseInt(input.value, 10); - } catch (_ex) { - // - } - - input.value = admin.maxActiveRequests.toString(); + private updateMaxActive(value: number): void { + IModelApp.tileAdmin.maxActiveRequests = value; } private clearInterval(): void { diff --git a/test-apps/display-test-app/src/frontend/ToolBar.ts b/test-apps/display-test-app/src/frontend/ToolBar.ts index ebb156f..7c2588f 100644 --- a/test-apps/display-test-app/src/frontend/ToolBar.ts +++ b/test-apps/display-test-app/src/frontend/ToolBar.ts @@ -8,6 +8,7 @@ import { assert } from "@bentley/bentleyjs-core"; export interface ToolButtonProps { className: string; click: (ev: Event) => void; + tooltip?: string; } export function createToolButton(props: ToolButtonProps): HTMLElement { @@ -17,6 +18,8 @@ export function createToolButton(props: ToolButtonProps): HTMLElement { const div = document.createElement("div"); div.className = "simpleicon"; div.addEventListener("click", (ev: Event) => props.click(ev)); + if (undefined !== props.tooltip) + div.title = props.tooltip; div.appendChild(img); return div; @@ -24,6 +27,7 @@ export function createToolButton(props: ToolButtonProps): HTMLElement { export interface ImageButtonProps { src: string; + tooltip?: string; click: (ev: Event) => void; } @@ -31,6 +35,8 @@ export function createImageButton(props: ImageButtonProps): HTMLElement { const img = document.createElement("img"); img.className = "simpleicon"; img.src = props.src; + if (undefined !== props.tooltip) + img.title = props.tooltip; img.addEventListener("click", (ev: Event) => props.click(ev)); return img; } diff --git a/test-apps/display-test-app/src/frontend/ViewAttributes.ts b/test-apps/display-test-app/src/frontend/ViewAttributes.ts index b86e007..fafc364 100644 --- a/test-apps/display-test-app/src/frontend/ViewAttributes.ts +++ b/test-apps/display-test-app/src/frontend/ViewAttributes.ts @@ -80,6 +80,9 @@ export class ViewAttributes { this.addViewFlagAttribute(flagsDiv, "Line Styles", "styles"); this.addViewFlagAttribute(flagsDiv, "Clip Volume", "clipVolume", true); + this.addLightingToggle(flagsDiv); + this.addCameraToggle(flagsDiv); + this.addEnvAttribute(flagsDiv, "Sky Box", "sky"); this.addEnvAttribute(flagsDiv, "Ground Plane", "ground"); @@ -122,6 +125,45 @@ export class ViewAttributes { this._updates.push(update); } + private addLightingToggle(parent: HTMLElement): void { + const elems = this.addCheckbox("Lights", (enabled: boolean) => { + const vf = this._vp.viewFlags.clone(this._scratchViewFlags); + vf.solarLight = vf.cameraLights = vf.sourceLights = enabled; + this._vp.view.viewFlags = vf; + this.sync(); + }, parent); + + const update = (view: ViewState) => { + const vf = view.viewFlags; + const visible = view.is3d() && RenderMode.SmoothShade === vf.renderMode; + elems.div.style.display = visible ? "block" : "none"; + if (visible) + elems.checkbox.checked = vf.solarLight || vf.cameraLights || vf.sourceLights; + }; + + this._updates.push(update); + } + + private addCameraToggle(parent: HTMLElement): void { + const elems = this.addCheckbox("Camera", (enabled: boolean) => { + if (enabled) + this._vp.turnCameraOn(); + else + (this._vp.view as ViewState3d).turnCameraOff(); + + this.sync(); + }, parent); + + const update = (view: ViewState) => { + const visible = view.is3d() && view.allow3dManipulations(); + elems.div.style.display = visible ? "block" : "none"; + if (visible) + elems.checkbox.checked = this._vp.isCameraOn; + }; + + this._updates.push(update); + } + private addEnvAttribute(parent: HTMLElement, label: string, aspect: EnvironmentAspect): void { const elems = this.addCheckbox(label, (enabled: boolean) => { const view3d = this._vp.view as ViewState3d; diff --git a/test-apps/display-test-app/src/frontend/ViewPicker.ts b/test-apps/display-test-app/src/frontend/ViewPicker.ts index dd56a54..516f144 100644 --- a/test-apps/display-test-app/src/frontend/ViewPicker.ts +++ b/test-apps/display-test-app/src/frontend/ViewPicker.ts @@ -108,11 +108,15 @@ export class ViewPicker { while (this._select.hasChildNodes()) this._select.removeChild(this._select.firstChild!); + let index = 0; for (const spec of views) { const option = document.createElement("option") as HTMLOptionElement; option.innerText = spec.name; option.value = spec.id; this._select.appendChild(option); + if (spec.id === views.defaultViewId) + this._select.selectedIndex = index; + index++; } } } diff --git a/test-apps/display-test-app/src/frontend/Viewer.ts b/test-apps/display-test-app/src/frontend/Viewer.ts index 0515aa0..da86426 100644 --- a/test-apps/display-test-app/src/frontend/Viewer.ts +++ b/test-apps/display-test-app/src/frontend/Viewer.ts @@ -2,37 +2,24 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ - -import { - Id64String, - OpenMode, -} from "@bentley/bentleyjs-core"; -import { - ElectronRpcConfiguration, - MobileRpcConfiguration, -} from "@bentley/imodeljs-common"; -import { - imageBufferToPngDataUrl, - IModelApp, - IModelConnection, - ScreenViewport, - Viewport, - ViewState, -} from "@bentley/imodeljs-frontend"; -import { ViewList, ViewPicker } from "./ViewPicker"; -import { ToolBar, ToolBarDropDown, createImageButton, createToolButton } from "./ToolBar"; +import { Id64String, OpenMode } from "@bentley/bentleyjs-core"; +import { ElectronRpcConfiguration, MobileRpcConfiguration } from "@bentley/imodeljs-common"; +import { imageBufferToPngDataUrl, IModelApp, IModelConnection, ScreenViewport, Viewport, ViewState, PluginAdmin } from "@bentley/imodeljs-frontend"; +import { AnimationPanel } from "./AnimationPanel"; +import { CategoryPicker } from "./CategoryPicker"; +import { createComboBox } from "./ComboBox"; import { DebugPanel } from "./DebugPanel"; +import { IncidentMarkerDemo } from "./IncidentMarkerDemo"; import { ModelPicker } from "./ModelPicker"; +import { toggleProjectExtents } from "./ProjectExtents"; import { RealityModelPicker } from "./RealityModelPicker"; -import { CategoryPicker } from "./CategoryPicker"; -import { ViewAttributesPanel } from "./ViewAttributes"; +import { addSnapModes } from "./SnapModes"; import { StandardRotations } from "./StandardRotations"; import { TileLoadIndicator } from "./TileLoadIndicator"; -import { addSnapModes } from "./SnapModes"; -import { createComboBox } from "./ComboBox"; -import { toggleIncidentMarkers } from "./IncidentMarkerDemo"; -import { toggleProjectExtents } from "./ProjectExtents"; -import { AnimationPanel } from "./AnimationPanel"; +import { FeatureOverridesPanel, emphasizeSelectedElements } from "./FeatureOverrides"; +import { ToolBar, ToolBarDropDown, createImageButton, createToolButton } from "./ToolBar"; +import { ViewList, ViewPicker } from "./ViewPicker"; +import { ViewAttributesPanel } from "./ViewAttributes"; // ###TODO: I think the picker populates correctly, but I have no way to test - and if no reality models are available, // the button doesn't disappear until you click on it. Revisit when Alain has something useful for us. @@ -57,7 +44,7 @@ function saveImage(vp: Viewport) { class DebugTools extends ToolBarDropDown { private readonly _element: HTMLElement; - public constructor(vp: Viewport, parent: HTMLElement) { + public constructor(parent: HTMLElement) { super(); this._element = document.createElement("div"); @@ -67,24 +54,47 @@ class DebugTools extends ToolBarDropDown { this._element.appendChild(createImageButton({ src: "Warning_sign.svg", - click: () => toggleIncidentMarkers(vp.iModel.projectExtents), + click: () => IncidentMarkerDemo.toggle(IModelApp.viewManager.selectedView!.iModel.projectExtents), + tooltip: "Test incident markers", })); this._element.appendChild(createToolButton({ className: "bim-icon-viewbottom", - click: () => toggleProjectExtents(vp.iModel), + click: () => toggleProjectExtents(IModelApp.viewManager.selectedView!.iModel), + tooltip: "Toggle project extents", })); this._element.appendChild(createToolButton({ className: "bim-icon-savedview", - click: () => saveImage(vp), + click: () => saveImage(IModelApp.viewManager.selectedView!), + tooltip: "Save view as image", })); this._element.appendChild(createToolButton({ className: "rd-icon-measure-distance", - click: () => IModelApp.tools.run("DrawingAidTest.Points", vp), // ###TODO Fix the drop-down... + click: () => IModelApp.tools.run("DrawingAidTest.Points", IModelApp.viewManager.selectedView!), // ###TODO Fix the drop-down... + tooltip: "Test drawing aid tools", })); + const wantEmphasize = false; + if (wantEmphasize) { + this._element.appendChild(createToolButton({ + className: "bim-icon-cancel", + click: () => emphasizeSelectedElements(IModelApp.viewManager.selectedView!), + tooltip: "Emphasize selected elements", + })); + } + + this._element.appendChild(createImageButton({ + src: "Markup.svg", + click: async () => PluginAdmin.loadPlugin("MarkupPlugin.js"), + tooltip: "Create Markup for View", + })); + this._element.appendChild(createToolButton({ + className: "bim-icon-work", + click: async () => PluginAdmin.loadPlugin("startWebWorkerPlugin.js"), + tooltip: "Start Web Worker Test", + })); parent.appendChild(this._element); } @@ -218,13 +228,6 @@ export class Viewer { createDropDown: async (container: HTMLElement) => Promise.resolve(new ViewAttributesPanel(this.viewport, container)), }); - const toggleCamera = createImageButton({ - src: "toggle-camera.svg", - click: () => IModelApp.tools.run("View.ToggleCamera", this.viewport), - }); - this._3dOnly.push(toggleCamera); - this.toolBar.addItem(toggleCamera); - this.toolBar.addItem(createImageButton({ src: "fit-to-view.svg", click: () => IModelApp.tools.run("View.Fit", this.viewport, true), @@ -249,7 +252,7 @@ export class Viewer { this.toolBar.addDropDown({ className: "bim-icon-gyroscope", - createDropDown: async (container: HTMLElement) => Promise.resolve(new StandardRotations(this.viewport, container)), + createDropDown: async (container: HTMLElement) => Promise.resolve(new StandardRotations(container)), }); this.toolBar.addItem(createToolButton({ @@ -267,9 +270,14 @@ export class Viewer { createDropDown: async (container: HTMLElement) => new AnimationPanel(this.viewport, container), }); + this.toolBar.addDropDown({ + className: "bim-icon-isolate", + createDropDown: async (container: HTMLElement) => new FeatureOverridesPanel(this.viewport, container), + }); + this.toolBar.addDropDown({ className: "bim-icon-appicon", - createDropDown: async (container: HTMLElement) => new DebugTools(this.viewport, container), + createDropDown: async (container: HTMLElement) => new DebugTools(container), }); const fileSelector = document.getElementById("browserFileSelector") as HTMLInputElement; diff --git a/test-apps/display-test-app/public/index.html b/test-apps/display-test-app/src/frontend/index.html similarity index 94% rename from test-apps/display-test-app/public/index.html rename to test-apps/display-test-app/src/frontend/index.html index 620ed72..fc604bc 100644 --- a/test-apps/display-test-app/public/index.html +++ b/test-apps/display-test-app/src/frontend/index.html @@ -23,8 +23,7 @@ - + diff --git a/test-apps/imodel-from-geojson/package.json b/test-apps/imodel-from-geojson/package.json index 299a3ab..b2721d5 100644 --- a/test-apps/imodel-from-geojson/package.json +++ b/test-apps/imodel-from-geojson/package.json @@ -4,7 +4,7 @@ "private": true, "license": "MIT", "scripts": { - "build": "tsc 1>&2", + "build": "tsc 1>&2 && node ./node_modules/@bentley/build-tools/scripts/ignoreargs.js 1>&2", "clean": "rimraf lib package-deps.json", "cover": "", "docs": "", @@ -13,22 +13,22 @@ }, "repository": {}, "dependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/geometry-core": "0.187.0", - "@bentley/imodeljs-clients": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-backend": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/imodeljs-clients": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-backend": "0.189.0", "fs-extra": "^6.0.1", "yargs": "^12.0.0" }, "devDependencies": { - "@bentley/build-tools": "0.187.0", + "@bentley/build-tools": "0.189.0", "@types/fs-extra": "^4.0.7", "@types/lodash": "^4.14.0", - "@types/node": "10.10.3", + "@types/node": "10.12.18", "@types/yargs": "^12.0.5", "rimraf": "^2.6.2", "tslint": "^5.11.0", - "typescript": "~3.1.0" + "typescript": "~3.2.2" } } diff --git a/test-apps/imodel-from-geojson/src/GeoJsonImporter.ts b/test-apps/imodel-from-geojson/src/GeoJsonImporter.ts index b23b93d..c203f2e 100644 --- a/test-apps/imodel-from-geojson/src/GeoJsonImporter.ts +++ b/test-apps/imodel-from-geojson/src/GeoJsonImporter.ts @@ -145,7 +145,7 @@ export class GeoJsonImporter { for (const inPoint of inLoop) { cartographic.longitude = Angle.degreesToRadians(inPoint[0]); cartographic.latitude = Angle.degreesToRadians(inPoint[1]); - outPoints.push(this.iModelDb.cartographicToSpatial(cartographic)); + outPoints.push(this.iModelDb.cartographicToSpatialFromEcef(cartographic)); } return Loop.create(LineString3d.createPoints(outPoints)); } diff --git a/test-apps/imodel-from-reality-model/LICENSE.md b/test-apps/imodel-from-reality-model/LICENSE.md index e197de4..c963a4b 100644 --- a/test-apps/imodel-from-reality-model/LICENSE.md +++ b/test-apps/imodel-from-reality-model/LICENSE.md @@ -1,6 +1,6 @@ # MIT License -Copyright © 2018 Bentley Systems, Inc. +Copyright © 2019 Bentley Systems, Inc. All rights reserved. diff --git a/test-apps/imodel-from-reality-model/README.md b/test-apps/imodel-from-reality-model/README.md index 6f82970..4979fed 100644 --- a/test-apps/imodel-from-reality-model/README.md +++ b/test-apps/imodel-from-reality-model/README.md @@ -1,6 +1,6 @@ # imodel-from-reality-model -Copyright © 2018 Bentley Systems, Incorporated. All rights reserved. +Copyright © 2019 Bentley Systems, Incorporated. All rights reserved. ## Description diff --git a/test-apps/imodel-from-reality-model/package.json b/test-apps/imodel-from-reality-model/package.json index c36a80c..fb5a03d 100644 --- a/test-apps/imodel-from-reality-model/package.json +++ b/test-apps/imodel-from-reality-model/package.json @@ -4,7 +4,7 @@ "private": true, "license": "MIT", "scripts": { - "build": "tsc 1>&2", + "build": "tsc 1>&2 && node ./node_modules/@bentley/build-tools/scripts/ignoreargs.js 1>&2", "clean": "rimraf lib package-deps.json", "cover": "", "docs": "", @@ -13,25 +13,25 @@ }, "repository": {}, "dependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/geometry-core": "0.187.0", - "@bentley/imodeljs-clients": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-backend": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/imodeljs-clients": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-backend": "0.189.0", "fs-extra": "^6.0.1", "request-promise-native": "^1.0.5", "request": "^2.88.0", "yargs": "^12.0.0" }, "devDependencies": { - "@bentley/build-tools": "0.187.0", + "@bentley/build-tools": "0.189.0", "@types/fs-extra": "^4.0.7", "@types/lodash": "^4.14.0", - "@types/node": "10.10.3", + "@types/node": "10.12.18", "@types/yargs": "^12.0.5", "@types/request-promise-native": "^1.0.15", "rimraf": "^2.6.2", "tslint": "^5.11.0", - "typescript": "~3.1.0" + "typescript": "~3.2.2" } } diff --git a/test-apps/imodel-from-reality-model/src/Main.ts b/test-apps/imodel-from-reality-model/src/Main.ts index 0a37e19..b6a661b 100644 --- a/test-apps/imodel-from-reality-model/src/Main.ts +++ b/test-apps/imodel-from-reality-model/src/Main.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------------------------------------------- -* Copyright (c) 2018 Bentley Systems, Incorporated. All rights reserved. +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import * as yargs from "yargs"; diff --git a/test-apps/imodel-from-reality-model/src/RealityModelContextIModelCreator.ts b/test-apps/imodel-from-reality-model/src/RealityModelContextIModelCreator.ts index c1b14b7..651f272 100644 --- a/test-apps/imodel-from-reality-model/src/RealityModelContextIModelCreator.ts +++ b/test-apps/imodel-from-reality-model/src/RealityModelContextIModelCreator.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------------------------------------------- -* Copyright (c) 2018 Bentley Systems, Incorporated. All rights reserved. +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import { Id64, Id64String, JsonUtils } from "@bentley/bentleyjs-core"; @@ -11,22 +11,30 @@ import * as fs from "fs"; class RealityModelTileUtils { public static rangeFromBoundingVolume(boundingVolume: any): Range3d | undefined { - if (undefined === boundingVolume || !Array.isArray(boundingVolume.box)) + if (undefined === boundingVolume) return undefined; - const box: number[] = boundingVolume.box; - const center = Point3d.create(box[0], box[1], box[2]); - const ux = Vector3d.create(box[3], box[4], box[5]); - const uy = Vector3d.create(box[6], box[7], box[8]); - const uz = Vector3d.create(box[9], box[10], box[11]); - const corners: Point3d[] = []; - for (let j = 0; j < 2; j++) { - for (let k = 0; k < 2; k++) { - for (let l = 0; l < 2; l++) { - corners.push(center.plus3Scaled(ux, (j ? -1.0 : 1.0), uy, (k ? -1.0 : 1.0), uz, (l ? -1.0 : 1.0))); + if (Array.isArray(boundingVolume.box)) { + const box: number[] = boundingVolume.box; + const center = Point3d.create(box[0], box[1], box[2]); + const ux = Vector3d.create(box[3], box[4], box[5]); + const uy = Vector3d.create(box[6], box[7], box[8]); + const uz = Vector3d.create(box[9], box[10], box[11]); + const corners: Point3d[] = []; + for (let j = 0; j < 2; j++) { + for (let k = 0; k < 2; k++) { + for (let l = 0; l < 2; l++) { + corners.push(center.plus3Scaled(ux, (j ? -1.0 : 1.0), uy, (k ? -1.0 : 1.0), uz, (l ? -1.0 : 1.0))); + } } } + return Range3d.createArray(corners); + } else if (Array.isArray(boundingVolume.sphere)) { + const sphere: number[] = boundingVolume.sphere; + const center = Point3d.create(sphere[0], sphere[1], sphere[2]); + const radius = sphere[3]; + return Range3d.createXYZXYZ(center.x - radius, center.y - radius, center.z - radius, center.x + radius, center.y + radius, center.z + radius); } - return Range3d.createArray(corners); + return undefined; } public static maximumSizeFromGeometricTolerance(range: Range3d, geometricError: number): number { @@ -55,64 +63,103 @@ export class RealityModelContextIModelCreator { this.iModelDb = IModelDb.createStandalone(iModelFileName, { rootSubject: { name: "Reality Model Context" } }); this.url = url; } + private realityModelFromJson(json: any, worldRange: AxisAlignedBox3d): { realityModel: ContextRealityModelProps | undefined, geoLocated: boolean } { + let geoLocated = true; + if (undefined !== json.root.boundingVolume.region) { + const region = JsonUtils.asArray(json.root.boundingVolume.region); + if (undefined === region) + throw new TypeError("Unable to determine GeoLocation - no root Transform or Region on root."); + const ecefLow = (new Cartographic(region[0], region[1], region[4])).toEcef(); + const ecefHigh = (new Cartographic(region[2], region[3], region[5])).toEcef(); + const ecefRange = Range3d.create(ecefLow, ecefHigh); + const cartoCenter = new Cartographic((region[0] + region[2]) / 2.0, (region[1] + region[3]) / 2.0, (region[4] + region[5]) / 2.0); + const ecefLocation = EcefLocation.createFromCartographicOrigin(cartoCenter!); + this.iModelDb.setEcefLocation(ecefLocation); + const ecefToWorld = ecefLocation.getTransform().inverse()!; + worldRange.extendRange(Range3d.fromJSON(ecefToWorld.multiplyRange(ecefRange))); + } else { + let rootTransform = RealityModelTileUtils.transformFromJson(json.root.transform); + const range = RealityModelTileUtils.rangeFromBoundingVolume(json.root.boundingVolume)!; + if (undefined === rootTransform) + rootTransform = Transform.createIdentity(); + const tileRange = rootTransform.multiplyRange(range); + if (rootTransform.matrix.isIdentity && range.center.magnitude() < 1.0E5) { + geoLocated = false; + worldRange.extendRange(Range3d.fromJSON(tileRange)); + } else { + const ecefCenter = tileRange.localXYZToWorld(.5, .5, .5)!; + const cartoCenter = Cartographic.fromEcef(ecefCenter); + const ecefLocation = EcefLocation.createFromCartographicOrigin(cartoCenter!); + this.iModelDb.setEcefLocation(ecefLocation); + const ecefToWorld = ecefLocation.getTransform().inverse()!; + worldRange.extendRange(Range3d.fromJSON(ecefToWorld.multiplyRange(tileRange))); + } + } + return { realityModel: { tilesetUrl: this.url, name: this.url }, geoLocated }; + } /** Perform the import */ public async create(): Promise { this.definitionModelId = DefinitionModel.insert(this.iModelDb, IModelDb.rootSubjectId, "Definitions"); this.physicalModelId = PhysicalModel.insert(this.iModelDb, IModelDb.rootSubjectId, "Empty Model"); + let geoLocated = false; + const worldRange = new Range3d(); + const realityModels: ContextRealityModelProps[] = []; requestPromise(this.url, { json: true }).then((json: any) => { - const rootTransform = RealityModelTileUtils.transformFromJson(json.root.transform); - let geoLocated = true; - let worldRange: AxisAlignedBox3d; - if (undefined === rootTransform) { - const region = JsonUtils.asArray(json.root.boundingVolume.region); - if (undefined === region) - throw new TypeError("Unable to determine GeoLocation - no root Transform or Region on root."); - const ecefLow = (new Cartographic(region[0], region[1], region[4])).toEcef(); - const ecefHigh = (new Cartographic(region[2], region[3], region[5])).toEcef(); - const ecefRange = Range3d.create(ecefLow, ecefHigh); - const cartoCenter = new Cartographic((region[0] + region[2]) / 2.0, (region[1] + region[3]) / 2.0, (region[4] + region[5]) / 2.0); - const ecefLocation = EcefLocation.createFromCartographicOrigin(cartoCenter!); - this.iModelDb.setEcefLocation(ecefLocation); - const ecefToWorld = ecefLocation.getTransform().inverse()!; - worldRange = AxisAlignedBox3d.fromJSON(ecefToWorld.multiplyRange(ecefRange)); + if (this.url.endsWith("_AppData.json")) { + const nameIndex = this.url.lastIndexOf("TileSets"); + const prefix = this.url.substr(0, nameIndex); + let worldToEcef: Transform | undefined; + for (const modelValue of Object.values(json.models)) { + const model = modelValue as any; + if (model.tilesetUrl !== undefined && + model.type === "spatial") { + let modelUrl = prefix + model.tilesetUrl.replace(/\/\//g, "/"); + modelUrl = modelUrl.replace(/ /g, "%20"); + const ecefRange = Range3d.fromJSON(model.extents); + if (!worldToEcef) { + worldToEcef = RealityModelTileUtils.transformFromJson(model.transform)!; + const ecefCenter = ecefRange.localXYZToWorld(.5, .5, .5)!; + const cartoCenter = Cartographic.fromEcef(ecefCenter); + const ecefLocation = EcefLocation.createFromCartographicOrigin(cartoCenter!); + this.iModelDb.setEcefLocation(ecefLocation); + geoLocated = true; + } + worldRange.extendRange(worldToEcef.inverse()!.multiplyRange(ecefRange)); + realityModels.push({ tilesetUrl: modelUrl, name: model.name }); + } + } } else { - const range = RealityModelTileUtils.rangeFromBoundingVolume(json.root.boundingVolume); - if (undefined === rootTransform || undefined === range) - return; + const result = this.realityModelFromJson(json, worldRange); + if (result.realityModel) { + realityModels.push(result.realityModel); + if (result.geoLocated) + geoLocated = true; - const tileRange = rootTransform.multiplyRange(range); - if (rootTransform.matrix.isIdentity) { - geoLocated = false; - worldRange = AxisAlignedBox3d.fromJSON(tileRange); - } else { - const ecefCenter = tileRange.localXYZToWorld(.5, .5, .5)!; - const cartoCenter = Cartographic.fromEcef(ecefCenter); - const ecefLocation = EcefLocation.createFromCartographicOrigin(cartoCenter!); - this.iModelDb.setEcefLocation(ecefLocation); - const ecefToWorld = ecefLocation.getTransform().inverse()!; - worldRange = AxisAlignedBox3d.fromJSON(ecefToWorld.multiplyRange(tileRange)); + realityModels.push(); } } - this.insertSpatialView("Reality Model View", worldRange, { tilesetUrl: this.url, name: this.url }, geoLocated); + this.insertSpatialView("Reality Model View", worldRange, realityModels, geoLocated); this.iModelDb.updateProjectExtents(worldRange); this.iModelDb.saveChanges(); }) - .catch(() => { }); + .catch((error) => { + process.stdout.write("Error occurred requesting data from: " + this.url + "Error: " + error + "\n"); + }); } /** Insert a SpatialView configured to display the GeoJSON data that was converted/imported. */ - protected insertSpatialView(viewName: string, range: AxisAlignedBox3d, realityModel: ContextRealityModelProps, geoLocated: boolean): Id64String { + protected insertSpatialView(viewName: string, range: AxisAlignedBox3d, realityModels: ContextRealityModelProps[], geoLocated: boolean): Id64String { const modelSelectorId: Id64String = ModelSelector.insert(this.iModelDb, this.definitionModelId, viewName, [this.physicalModelId]); const categorySelectorId: Id64String = CategorySelector.insert(this.iModelDb, this.definitionModelId, viewName, []); const vf = new ViewFlags(); vf.backgroundMap = geoLocated; vf.renderMode = RenderMode.SmoothShade; vf.cameraLights = true; - const displayStyleId: Id64String = DisplayStyle3d.insert(this.iModelDb, this.definitionModelId, viewName, { viewFlags: vf, contextRealityModels: [realityModel] }); + const displayStyleId: Id64String = DisplayStyle3d.insert(this.iModelDb, this.definitionModelId, viewName, { viewFlags: vf, contextRealityModels: realityModels }); return OrthographicViewDefinition.insert(this.iModelDb, this.definitionModelId, viewName, modelSelectorId, categorySelectorId, displayStyleId, range, StandardViewIndex.Iso); } } diff --git a/test-apps/presentation-integration-tests/package.json b/test-apps/presentation-integration-tests/package.json index 9c3e356..b9ea5c5 100644 --- a/test-apps/presentation-integration-tests/package.json +++ b/test-apps/presentation-integration-tests/package.json @@ -9,31 +9,31 @@ }, "private": true, "scripts": { - "build": "tsc 1>&2 && npm run extract", + "build": "tsc 1>&2 && npm run extract && node ./node_modules/@bentley/build-tools/scripts/ignoreargs.js 1>&2", "clean": "rimraf lib", "docs": "", "extract": "node ./node_modules/@bentley/build-tools/scripts/extract.js --fileExt=ts --extractFrom=./src --recursive --out=../../generated-docs/extract", "lint": "tslint --project . 1>&2", "test": "mocha --opts ./mocha.opts ./lib/**/*.js", "test:watch": "npm test -- --reporter min --watch-extensions ts --watch", - "cover": "" + "cover": "npm test" }, "dependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/geometry-core": "0.187.0", - "@bentley/imodeljs-i18n": "0.187.0", - "@bentley/imodeljs-clients": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-backend": "0.187.0", - "@bentley/imodeljs-frontend": "0.187.0", - "@bentley/presentation-common": "0.187.0", - "@bentley/presentation-backend": "0.187.0", - "@bentley/presentation-frontend": "0.187.0", - "@bentley/presentation-components": "0.187.0", - "@bentley/presentation-testing": "0.187.0", - "@bentley/ui-core": "0.187.0", - "@bentley/ui-components": "0.187.0", - "@bentley/ui-framework": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/imodeljs-i18n": "0.189.0", + "@bentley/imodeljs-clients": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-backend": "0.189.0", + "@bentley/imodeljs-frontend": "0.189.0", + "@bentley/presentation-common": "0.189.0", + "@bentley/presentation-backend": "0.189.0", + "@bentley/presentation-frontend": "0.189.0", + "@bentley/presentation-components": "0.189.0", + "@bentley/presentation-testing": "0.189.0", + "@bentley/ui-core": "0.189.0", + "@bentley/ui-components": "0.189.0", + "@bentley/ui-framework": "0.189.0", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/chai-jest-snapshot": "^1.3.0", @@ -58,16 +58,15 @@ "xmlhttprequest": "^1.8.0" }, "devDependencies": { - "@bentley/config-loader": "0.187.0", - "@bentley/build-tools": "0.187.0", + "@bentley/config-loader": "0.189.0", + "@bentley/build-tools": "0.189.0", "cache-require-paths": "^0.3.0", "cross-env": "^5.1.4", "ignore-styles": "^5.0.1", "jsdom-global": "3.0.2", - "nyc": "^13.0.1", "ts-node": "^7.0.1", "tslint": "^5.11.0", - "typescript": "~3.1.0" + "typescript": "~3.2.2" }, "nyc": { "nycrc-path": "./node_modules/@bentley/build-tools/.nycrc", diff --git a/test-apps/presentation-integration-tests/scripts/copy-test-setup.js b/test-apps/presentation-integration-tests/scripts/copy-test-setup.js index 37f0ed9..0f5c747 100644 --- a/test-apps/presentation-integration-tests/scripts/copy-test-setup.js +++ b/test-apps/presentation-integration-tests/scripts/copy-test-setup.js @@ -1,5 +1,5 @@ /*--------------------------------------------------------------------------------------------- -* Copyright (c) 2018 Bentley Systems, Incorporated. All rights reserved. +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ const fs = require("fs"); diff --git a/test-apps/presentation-integration-tests/scripts/setup-tests.js b/test-apps/presentation-integration-tests/scripts/setup-tests.js index 0cd6f7a..105c88b 100644 --- a/test-apps/presentation-integration-tests/scripts/setup-tests.js +++ b/test-apps/presentation-integration-tests/scripts/setup-tests.js @@ -1,5 +1,5 @@ /*--------------------------------------------------------------------------------------------- -* Copyright (c) 2018 Bentley Systems, Incorporated. All rights reserved. +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ const chai = require("chai"); diff --git a/test-apps/presentation-integration-tests/src/IntegrationTests.ts b/test-apps/presentation-integration-tests/src/IntegrationTests.ts index e40bf56..93ddbbc 100644 --- a/test-apps/presentation-integration-tests/src/IntegrationTests.ts +++ b/test-apps/presentation-integration-tests/src/IntegrationTests.ts @@ -2,15 +2,17 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ +// tslint:disable:no-direct-imports import * as fs from "fs"; import * as path from "path"; import * as cpx from "cpx"; -// tslint:disable-next-line:no-direct-imports import "@bentley/presentation-frontend/lib/test/_helpers/MockFrontendEnvironment"; // common includes import { I18NOptions } from "@bentley/imodeljs-i18n"; import { Logger, LogLevel } from "@bentley/bentleyjs-core"; import { LoggingNamespaces } from "@bentley/presentation-common"; +import { Props as PresentationBackendProps } from "@bentley/presentation-backend/lib/Presentation"; +import { Props as PresentationFrontendProps } from "@bentley/presentation-frontend/lib/PresentationManager"; import { NoRenderApp } from "@bentley/imodeljs-frontend"; import { initialize as initializeTesting, terminate as terminateTesting } from "@bentley/presentation-testing"; @@ -42,7 +44,7 @@ const copyBentleyFrontendAssets = (outputDir: string) => { class IntegrationTestsApp extends NoRenderApp { protected static supplyI18NOptions(): I18NOptions { - const urlTemplate = `file://${path.resolve("lib/public/locales")}/{{lng}}/{{ns}}.json`; + const urlTemplate = "file://" + path.join(path.resolve("lib/public/locales"), "{{lng}}/{{ns}}.json").replace(/\\/g, "/"); return { urlTemplate }; } protected static onStartup(): void { @@ -58,9 +60,15 @@ export const initialize = () => { Logger.setLevel(LoggingNamespaces.ECObjects_ECExpressions, LogLevel.None); Logger.setLevel(LoggingNamespaces.ECPresentation, LogLevel.None); - initializeTesting( - { rulesetDirectories: ["lib/assets/rulesets"], localeDirectories: ["lib/assets/locales"] }, - IntegrationTestsApp); + const backendInitProps: PresentationBackendProps = { + rulesetDirectories: ["lib/assets/rulesets"], + localeDirectories: ["lib/assets/locales"], + activeLocale: "en-PSEUDO", + }; + const frontendInitProps: PresentationFrontendProps = { + activeLocale: "en-PSEUDO", + }; + initializeTesting(backendInitProps, frontendInitProps, IntegrationTestsApp); }; export const terminate = () => { diff --git a/test-apps/presentation-integration-tests/src/backend/RulesetsEmbedding.tests.ts b/test-apps/presentation-integration-tests/src/backend/RulesetsEmbedding.tests.ts index ad9e6de..5c88ea1 100644 --- a/test-apps/presentation-integration-tests/src/backend/RulesetsEmbedding.tests.ts +++ b/test-apps/presentation-integration-tests/src/backend/RulesetsEmbedding.tests.ts @@ -112,7 +112,7 @@ describe("RulesEmbedding", () => { expect(Id64.isValid(insertId)).true; // Try getting root node to confirm embedded ruleset is being located - const rootNodes = await Presentation.getManager().getRootNodes(ActivityLoggingContext.current, { imodel, rulesetId: rulesetToLocate.id }); + const rootNodes = await Presentation.getManager().getNodes(ActivityLoggingContext.current, { imodel, rulesetId: rulesetToLocate.id }); expect(rootNodes.length).to.be.equal(1); }); @@ -123,14 +123,14 @@ describe("RulesEmbedding", () => { expect(Id64.isValid(insertId)).true; // Try getting root node to confirm embedded ruleset is being located - let rootNodes = await Presentation.getManager().getRootNodes(ActivityLoggingContext.current, { imodel, rulesetId: rulesetToLocate.id }); + let rootNodes = await Presentation.getManager().getNodes(ActivityLoggingContext.current, { imodel, rulesetId: rulesetToLocate.id }); expect(rootNodes.length).to.be.equal(1); const rulesetElement = imodel.elements.getElement(insertId); rulesetElement.setJsonProperty("id", faker.random.uuid()); imodel.elements.updateElement(rulesetElement); - rootNodes = await Presentation.getManager().getRootNodes(ActivityLoggingContext.current, { imodel, rulesetId: rulesetToLocate.id }); + rootNodes = await Presentation.getManager().getNodes(ActivityLoggingContext.current, { imodel, rulesetId: rulesetToLocate.id }); expect(rootNodes.length).to.be.equal(1); }); diff --git a/test-apps/presentation-integration-tests/src/backend/RulesetsRoundtrip.test.ts b/test-apps/presentation-integration-tests/src/backend/RulesetsRoundtrip.test.ts index 458a9f8..30093d4 100644 --- a/test-apps/presentation-integration-tests/src/backend/RulesetsRoundtrip.test.ts +++ b/test-apps/presentation-integration-tests/src/backend/RulesetsRoundtrip.test.ts @@ -43,7 +43,7 @@ describe("Rulesets roundtrip", () => { it(`ruleset stays the same after roundtrip to/from native platform ${i + 1}`, async () => { const sourceRuleset: Ruleset = await createRandomRuleset(); try { - await using(rulesets.add(sourceRuleset), async () => { + await using(rulesets.add(sourceRuleset), async (_r) => { const afterRoundtripRuleset = await getRoundtripRuleset(sourceRuleset); expect(afterRoundtripRuleset).to.deep.equal(sourceRuleset, `Before: \r\n${JSON.stringify(sourceRuleset)} \r\nAfter: \r\n${JSON.stringify(afterRoundtripRuleset)}`); diff --git a/test-apps/presentation-integration-tests/src/frontend/Content.test.ts b/test-apps/presentation-integration-tests/src/frontend/Content.test.ts index f5eda7b..d3ed664 100644 --- a/test-apps/presentation-integration-tests/src/frontend/Content.test.ts +++ b/test-apps/presentation-integration-tests/src/frontend/Content.test.ts @@ -29,7 +29,7 @@ describe("Content", () => { it("gets distinct content values", async () => { const ruleset: Ruleset = require("../../test-rulesets/DistinctValues/getRelatedDistinctValues"); - await using(await Presentation.presentation.rulesets().add(ruleset), async () => { + await using(await Presentation.presentation.rulesets().add(ruleset), async (_r) => { const key1: InstanceKey = { id: Id64.fromString("0x1"), className: "BisCore:Subject" }; const key2: InstanceKey = { id: Id64.fromString("0x17"), className: "BisCore:SpatialCategory" }; const keys = new KeySet([key1, key2]); diff --git a/test-apps/presentation-integration-tests/src/frontend/FindSimilar.test.ts b/test-apps/presentation-integration-tests/src/frontend/FindSimilar.test.ts new file mode 100644 index 0000000..b7bdc01 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/FindSimilar.test.ts @@ -0,0 +1,150 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { expect } from "chai"; +import * as faker from "faker"; +import { initialize, terminate } from "../IntegrationTests"; +import { OpenMode, Id64String, using } from "@bentley/bentleyjs-core"; +import { IModelConnection, PropertyRecord } from "@bentley/imodeljs-frontend"; +import { KeySet, Ruleset, RuleTypes, RuleSpecificationTypes, RegisteredRuleset, InstanceKey, instanceKeyFromJSON } from "@bentley/presentation-common"; +import { Presentation } from "@bentley/presentation-frontend"; +import { + PresentationPropertyDataProvider, IPresentationPropertyDataProvider, + DataProvidersFactory, IPresentationTableDataProvider, +} from "@bentley/presentation-components"; +import { PropertyData, RowItem } from "@bentley/ui-components"; + +describe("Find Similar", () => { + + let imodel: IModelConnection; + + before(async () => { + initialize(); + const testIModelName: string = "assets/datasets/Properties_60InstancesWithUrl2.ibim"; + imodel = await IModelConnection.openStandalone(testIModelName, OpenMode.Readonly); + expect(imodel).is.not.null; + }); + + after(async () => { + await imodel.closeStandalone(); + terminate(); + }); + + let propertiesRuleset: RegisteredRuleset; + let propertiesDataProvider: IPresentationPropertyDataProvider; + let factory: DataProvidersFactory; + + beforeEach(async () => { + const ruleset: Ruleset = { + id: faker.random.uuid(), + rules: [{ + ruleType: RuleTypes.Content, + specifications: [{ + specType: RuleSpecificationTypes.SelectedNodeInstances, + }], + }], + }; + propertiesRuleset = await Presentation.presentation.rulesets().add(ruleset); + propertiesDataProvider = new PresentationPropertyDataProvider(imodel, propertiesRuleset.id); + factory = new DataProvidersFactory(); + }); + + afterEach(async () => { + await Presentation.presentation.rulesets().remove(propertiesRuleset); + }); + + const getPropertyRecordByLabel = (props: PropertyData, label: string): PropertyRecord | undefined => { + for (const category of props.categories) { + const record = props.records[category.name].find((r) => r.property.displayLabel === label); + if (record) + return record; + } + return undefined; + }; + + const getAllRows = async (provider: IPresentationTableDataProvider): Promise => { + const rows: RowItem[] = []; + const count = await provider.getRowsCount(); + for (let i = 0; i < count; ++i) { + rows.push(await provider.getRow(i)); + } + return rows; + }; + + const getAllRowsInstanceKeys = async (provider: IPresentationTableDataProvider): Promise => { + return (await getAllRows(provider)).map((r) => instanceKeyFromJSON(JSON.parse(r.key))); + }; + + const getAllRowsInstanceIds = async (provider: IPresentationTableDataProvider): Promise => { + return (await getAllRowsInstanceKeys(provider)).map((k) => k.id); + }; + + it("creates a valid 'similar instances' data provider for primary instance primitive property", async () => { + // get properties for one of the elements + propertiesDataProvider.keys = new KeySet([{ className: "PCJ_TestSchema:TestClass", id: "0x38" }]); + const propertyData = await propertiesDataProvider.getData(); + + // find the property record to request similar instances for + const record = getPropertyRecordByLabel(propertyData, "Country")!; + + // create a 'similar instances' data provider and validate + await using(await factory.createSimilarInstancesTableDataProvider(propertiesDataProvider, record, {}), async (provider) => { + expect(await provider.getRowsCount()).to.eq(10); + expect(await getAllRowsInstanceKeys(provider)).to.deep.include.members([ + { className: "PCJ_TestSchema:TestClass", id: "0x38" }, + { className: "PCJ_TestSchema:TestClass", id: "0x39" }, + { className: "PCJ_TestSchema:TestClass", id: "0x3a" }, + { className: "PCJ_TestSchema:TestClass", id: "0x3b" }, + { className: "PCJ_TestSchema:TestClass", id: "0x3c" }, + { className: "PCJ_TestSchema:TestClass", id: "0x3d" }, + { className: "PCJ_TestSchema:TestClass", id: "0x3e" }, + { className: "PCJ_TestSchema:TestClass", id: "0x3f" }, + { className: "PCJ_TestSchema:TestClass", id: "0x40" }, + { className: "PCJ_TestSchema:TestClass", id: "0x6e" }, + ]); + }); + }); + + it("creates a valid 'similar instances' data provider for primary instance navigation property", async () => { + // get properties for one of the elements + propertiesDataProvider.keys = new KeySet([{ className: "PCJ_TestSchema:TestClass", id: "0x38" }]); + const propertyData = await propertiesDataProvider.getData(); + + // find the property record to request similar instances for + const record = getPropertyRecordByLabel(propertyData, "Category")!; + + // create a 'similar instances' data provider and validate + await using(await factory.createSimilarInstancesTableDataProvider(propertiesDataProvider, record, {}), async (provider) => { + expect(await provider.getRowsCount()).to.eq(60); + expect(await getAllRowsInstanceIds(provider)).to.deep.include.members([ + "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "0x3f", "0x40", "0x41", + "0x42", "0x43", "0x44", "0x45", "0x46", "0x47", "0x48", "0x49", "0x4a", "0x4b", + "0x4c", "0x4d", "0x4e", "0x4f", "0x50", "0x51", "0x52", "0x53", "0x54", "0x55", + "0x56", "0x57", "0x58", "0x59", "0x5a", "0x5b", "0x5c", "0x5d", "0x5e", "0x5f", + "0x60", "0x61", "0x62", "0x63", "0x64", "0x65", "0x66", "0x67", "0x68", "0x69", + "0x6a", "0x6b", "0x6c", "0x6d", "0x6e", "0x6f", "0x70", "0x71", "0x72", "0x73", + ]); + }); + }); + + // WIP: fails when run after the above tests due to issue in imodeljs-native, succeeds when run isolated + it.skip("creates a valid 'similar instances' data provider for related instance property", async () => { + // get properties for one of the elements + propertiesDataProvider.keys = new KeySet([{ className: "Generic:PhysicalObject", id: "0x74" }]); + const propertyData = await propertiesDataProvider.getData(); + + // find the property record to request similar instances for + const record = getPropertyRecordByLabel(propertyData, "Angle")!; + + // create a 'similar instances' data provider and validate + await using(await factory.createSimilarInstancesTableDataProvider(propertiesDataProvider, record, {}), async (provider) => { + expect(await provider.getRowsCount()).to.eq(2); + expect(await getAllRowsInstanceKeys(provider)).to.deep.include.members([ + { className: "Generic:PhysicalObject", id: "0x74" }, + { className: "Generic:PhysicalObject", id: "0x75" }, + ]); + }); + }); + +}); diff --git a/test-apps/presentation-integration-tests/src/frontend/Hierarchies.test.snap b/test-apps/presentation-integration-tests/src/frontend/Hierarchies.test.snap index b8765e1..180032e 100644 --- a/test-apps/presentation-integration-tests/src/frontend/Hierarchies.test.snap +++ b/test-apps/presentation-integration-tests/src/frontend/Hierarchies.test.snap @@ -10,7 +10,7 @@ Array [ "className": "BisCore:DefinitionModel", "groupedInstancesCount": 1, "pathFromRoot": Array [ - "d205cc53ce3a99f4c93b3b4b475db351", + "3bbaeeb5df0fbdd911684fcaf3dce295", ], "type": "ECClassGroupingNode", }, @@ -24,7 +24,7 @@ Array [ "className": "BisCore:DictionaryModel", "groupedInstancesCount": 1, "pathFromRoot": Array [ - "4e96fd6b23631d9692f86cb1b3925d2d", + "2bf13dbc39a40a1611e57470761ee14b", ], "type": "ECClassGroupingNode", }, @@ -38,7 +38,7 @@ Array [ "className": "BisCore:DocumentListModel", "groupedInstancesCount": 2, "pathFromRoot": Array [ - "df9bbb2513d56b50c686f94c9699313f", + "f17f86c0171b4ad94629b1e47326731f", ], "type": "ECClassGroupingNode", }, @@ -51,7 +51,7 @@ Array [ "className": "Generic:GroupModel", "groupedInstancesCount": 1, "pathFromRoot": Array [ - "51cb0ccec89fc2eca4df5e4d7a05c9a9", + "7a434b3ba061a765bc3b15b53ffd228f", ], "type": "ECClassGroupingNode", }, @@ -65,7 +65,7 @@ Array [ "className": "BisCore:LinkModel", "groupedInstancesCount": 1, "pathFromRoot": Array [ - "7900c27f8e9f8f7f809a54513b34ddaf", + "5c2cc76b3cd286c72fbe50a0e407183b", ], "type": "ECClassGroupingNode", }, @@ -79,7 +79,7 @@ Array [ "className": "BisCore:PhysicalModel", "groupedInstancesCount": 1, "pathFromRoot": Array [ - "0d047b58c7cabbb4cc55118d004b1532", + "4ac438f813655bf7194bc4df105738c8", ], "type": "ECClassGroupingNode", }, @@ -93,7 +93,7 @@ Array [ "className": "BisCore:RepositoryModel", "groupedInstancesCount": 1, "pathFromRoot": Array [ - "9a24241760b66ffd7981bd5849a1cbc5", + "4c73690da111037c6dbb5471158c75f7", ], "type": "ECClassGroupingNode", }, @@ -255,22 +255,22 @@ Array [ "index": 0, "isMarked": false, "node": Object { - "imageId": "ECInstanceImage://BisCore:DefinitionPartition", + "imageId": "ECLiteralImage://ELEMENT", "key": Object { "instanceKey": Object { "className": "BisCore:DefinitionPartition", "id": "0x10", }, "pathFromRoot": Array [ - "4e074cc7a6fd65b2a738d22f8b912299", - "7f68095dd510724c73d10956820f7921", - "bd4972ffb7cd9bc2e1493436df5c921b", - "5ae7367cae801dda838b167a98c08320", - "f58b45a0b1dcc1005d366672a876535f", + "93e6fbdb6109b5bf3d681f2fc9b47383", + "1d76581c875199a8c9f9dbd065549e8d", + "d109d878ba523718bcc184d190936cfe", + "8ceffa84f66407f0eff6642b920539a4", + "8164457fc310b9ca17c1d5036000ea9d", ], "type": "ECInstanceNode", }, - "label": "Definition Partition-0-G", + "label": "BisCore.DictionaryModel", }, }, ], @@ -288,10 +288,10 @@ Array [ "className": "BisCore:DefinitionPartition", "groupedInstancesCount": 1, "pathFromRoot": Array [ - "4e074cc7a6fd65b2a738d22f8b912299", - "7f68095dd510724c73d10956820f7921", - "bd4972ffb7cd9bc2e1493436df5c921b", - "5ae7367cae801dda838b167a98c08320", + "93e6fbdb6109b5bf3d681f2fc9b47383", + "1d76581c875199a8c9f9dbd065549e8d", + "d109d878ba523718bcc184d190936cfe", + "8ceffa84f66407f0eff6642b920539a4", ], "type": "ECClassGroupingNode", }, @@ -309,22 +309,22 @@ Array [ "index": 0, "isMarked": true, "node": Object { - "imageId": "ECInstanceImage://BisCore:LinkPartition", + "imageId": "ECLiteralImage://ELEMENT", "key": Object { "instanceKey": Object { "className": "BisCore:LinkPartition", "id": "0xe", }, "pathFromRoot": Array [ - "4e074cc7a6fd65b2a738d22f8b912299", - "7f68095dd510724c73d10956820f7921", - "bd4972ffb7cd9bc2e1493436df5c921b", - "ab9d6344c1e60c3a68b8dea279224873", - "2befd7b22098b671c658a3d15b904677", + "93e6fbdb6109b5bf3d681f2fc9b47383", + "1d76581c875199a8c9f9dbd065549e8d", + "d109d878ba523718bcc184d190936cfe", + "ca05d6adfdf2c19a31059933f72e6f3c", + "5aaadfb2b7429c5ffd1871ad162f80a0", ], "type": "ECInstanceNode", }, - "label": "Link Partition-0-E", + "label": "BisCore.RealityDataSources", }, }, ], @@ -342,10 +342,10 @@ Array [ "className": "BisCore:LinkPartition", "groupedInstancesCount": 1, "pathFromRoot": Array [ - "4e074cc7a6fd65b2a738d22f8b912299", - "7f68095dd510724c73d10956820f7921", - "bd4972ffb7cd9bc2e1493436df5c921b", - "ab9d6344c1e60c3a68b8dea279224873", + "93e6fbdb6109b5bf3d681f2fc9b47383", + "1d76581c875199a8c9f9dbd065549e8d", + "d109d878ba523718bcc184d190936cfe", + "ca05d6adfdf2c19a31059933f72e6f3c", ], "type": "ECClassGroupingNode", }, @@ -361,20 +361,20 @@ Array [ "isMarked": false, "node": Object { "hasChildren": true, - "imageId": "ECInstanceImage://BisCore:Subject", + "imageId": "ECLiteralImage://ELEMENT", "key": Object { "instanceKey": Object { "className": "BisCore:Subject", "id": "0x1", }, "pathFromRoot": Array [ - "4e074cc7a6fd65b2a738d22f8b912299", - "7f68095dd510724c73d10956820f7921", - "bd4972ffb7cd9bc2e1493436df5c921b", + "93e6fbdb6109b5bf3d681f2fc9b47383", + "1d76581c875199a8c9f9dbd065549e8d", + "d109d878ba523718bcc184d190936cfe", ], "type": "ECInstanceNode", }, - "label": "Subject-0-1", + "label": "DgnV8Bridge", }, }, ], @@ -393,12 +393,12 @@ Array [ "id": "0x1", }, "pathFromRoot": Array [ - "4e074cc7a6fd65b2a738d22f8b912299", - "7f68095dd510724c73d10956820f7921", + "93e6fbdb6109b5bf3d681f2fc9b47383", + "1d76581c875199a8c9f9dbd065549e8d", ], "type": "ECInstanceNode", }, - "label": "Repository Model-0-1", + "label": "DgnV8Bridge", }, }, ] diff --git a/test-apps/presentation-integration-tests/src/frontend/Hierarchies.test.ts b/test-apps/presentation-integration-tests/src/frontend/Hierarchies.test.ts index 3200dd9..1128409 100644 --- a/test-apps/presentation-integration-tests/src/frontend/Hierarchies.test.ts +++ b/test-apps/presentation-integration-tests/src/frontend/Hierarchies.test.ts @@ -9,7 +9,7 @@ import { OpenMode, Id64, using } from "@bentley/bentleyjs-core"; import { IModelConnection } from "@bentley/imodeljs-frontend"; import { InstanceKey, Ruleset, RuleTypes, RuleSpecificationTypes, - KeySet, ECInstanceNodeKey, getInstancesCount, + KeySet, ECInstanceNodeKey, getInstancesCount, RegisteredRuleset, } from "@bentley/presentation-common"; import { Presentation } from "@bentley/presentation-frontend"; @@ -44,7 +44,7 @@ describe("Hierarchies", () => { other ch5 filter ch6 */ - await using(await Presentation.presentation.rulesets().add(ruleset), async () => { + await using(await Presentation.presentation.rulesets().add(ruleset), async (_r) => { const result = await Presentation.presentation.getFilteredNodePaths({ imodel, rulesetId: ruleset.id }, "filter"); expect(result).to.matchSnapshot(); }); @@ -60,7 +60,7 @@ describe("Hierarchies", () => { [BisCore:LinkPartition] ECClassGroupingNode [BisCore:LinkPartition] 0xe */ - await using(await Presentation.presentation.rulesets().add(ruleset), async () => { + await using>(await Presentation.presentation.rulesets().add(ruleset), async () => { const key1: InstanceKey = { id: Id64.fromString("0x1"), className: "BisCore:RepositoryModel" }; const key2: InstanceKey = { id: Id64.fromString("0x1"), className: "BisCore:Subject" }; const key3: InstanceKey = { id: Id64.fromString("0x10"), className: "BisCore:DefinitionPartition" }; @@ -91,8 +91,8 @@ describe("Hierarchies", () => { }], }], }; - await using(await Presentation.presentation.rulesets().add(ruleset), async () => { - const rootNodes = await Presentation.presentation.getRootNodes({ imodel, rulesetId: ruleset.id }); + await using>(await Presentation.presentation.rulesets().add(ruleset), async () => { + const rootNodes = await Presentation.presentation.getNodes({ imodel, rulesetId: ruleset.id }); expect(rootNodes).to.matchSnapshot(); /* The result should look like this (all grouping nodes): @@ -112,9 +112,9 @@ describe("Hierarchies", () => { the result should be 1 + 1 + 2 = 4 */ - const definitionModelNodes = await Presentation.presentation.getChildren( + const definitionModelNodes = await Presentation.presentation.getNodes( { imodel, rulesetId: ruleset.id }, rootNodes[0].key); - const dictionaryModelNodes = await Presentation.presentation.getChildren( + const dictionaryModelNodes = await Presentation.presentation.getNodes( { imodel, rulesetId: ruleset.id }, rootNodes[1].key); const keys = new KeySet([ diff --git a/test-apps/presentation-integration-tests/src/frontend/Localization.test.ts b/test-apps/presentation-integration-tests/src/frontend/Localization.test.ts index 09785fb..acc65f1 100644 --- a/test-apps/presentation-integration-tests/src/frontend/Localization.test.ts +++ b/test-apps/presentation-integration-tests/src/frontend/Localization.test.ts @@ -25,7 +25,7 @@ describe("Localization", async () => { }); it("localizes using app/test supplied localized strings", async () => { - const nodes = await Presentation.presentation.getRootNodes({ imodel, rulesetId: "LocalizationTest" }); + const nodes = await Presentation.presentation.getNodes({ imodel, rulesetId: "LocalizationTest" }); expect(nodes.length).to.eq(1); expect(nodes[0].label).to.eq("test value"); expect(nodes[0].description).to.eq("test nested value"); diff --git a/test-apps/presentation-integration-tests/src/frontend/RulesetTesting.test.ts b/test-apps/presentation-integration-tests/src/frontend/RulesetTesting.test.ts index 460f949..47cff6a 100644 --- a/test-apps/presentation-integration-tests/src/frontend/RulesetTesting.test.ts +++ b/test-apps/presentation-integration-tests/src/frontend/RulesetTesting.test.ts @@ -1,17 +1,28 @@ /*--------------------------------------------------------------------------------------------- -* Copyright (c) 2018 Bentley Systems, Incorporated. All rights reserved. +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; -import { HierarchyBuilder } from "@bentley/presentation-testing"; +import { HierarchyBuilder, ContentBuilder, ContentBuilderResult } from "@bentley/presentation-testing"; import { IModelConnection } from "@bentley/imodeljs-frontend"; import { initialize, terminate } from "../IntegrationTests"; +import * as ChaiJestSnapshot from "chai-jest-snapshot"; +import path from "path"; + +function configureSnapshotLocation(test: Mocha.Runnable, subdirectory: string, instance: ContentBuilderResult) { + let fileName = path.join( + path.dirname(test.file!).replace(/(?!\\|\/)(lib)(?=\\|\/)/g, "src"), + subdirectory, + `${instance.className.replace(":", ".")}.snap`); + + fileName = fileName.replace(/__x0020__/g, "_"); + ChaiJestSnapshot.setFilename(fileName); + ChaiJestSnapshot.setTestName(`${test.fullTitle()}. ClassName: '${instance.className}'`); +} // __PUBLISH_EXTRACT_START__ Presentation.Testing.Rulesets describe("RulesetTesting", () => { - let imodel: IModelConnection; - let builder: HierarchyBuilder; const imodelPath = "assets/datasets/Properties_60InstancesWithUrl2.ibim"; before(() => { @@ -27,7 +38,6 @@ describe("RulesetTesting", () => { beforeEach(async () => { // set up for testing imodel presentation data imodel = await IModelConnection.openStandalone(imodelPath); - builder = new HierarchyBuilder(imodel); }); afterEach(async () => { @@ -35,13 +45,32 @@ describe("RulesetTesting", () => { }); it("generates correct hierarchy for 'Items' ruleset", async () => { + const builder = new HierarchyBuilder(imodel); // generate the hierarchy using a ruleset id const hierarchy = await builder.createHierarchy("Items"); // verify through snapshot expect(hierarchy).to.matchSnapshot(); }); + // tslint:disable-next-line:only-arrow-functions + it("generates correct content for 'Items' ruleset", async function () { + const builder = new ContentBuilder(imodel); + // generate content using ruleset id + const instances = await builder.createContentForInstancePerClass("Items"); + + // verify through snapshot + // we loop through each instance and create a separate snapshot file + // because snapshot engine has difficulties parsing big files + for (const instance of instances) { + // not providing filename and snapshot name to the 'matchSnapshot', because it seems + // to ignore them when ChaiJestSnapshot.setFilename and setTestName is used + configureSnapshotLocation(this.test!, "ruleset-testing-content-snaps", instance); + expect(instance.records).to.matchSnapshot(); + } + }); + it("generates correct hierarchy for 'default' ruleset", async () => { + const builder = new HierarchyBuilder(imodel); // import ruleset from file const ruleset = require("../../test-rulesets/Rulesets/default.json"); // generate the hierarchy diff --git a/test-apps/presentation-integration-tests/src/frontend/Rulesets.test.ts b/test-apps/presentation-integration-tests/src/frontend/Rulesets.test.ts index c876e0a..bc6e7dd 100644 --- a/test-apps/presentation-integration-tests/src/frontend/Rulesets.test.ts +++ b/test-apps/presentation-integration-tests/src/frontend/Rulesets.test.ts @@ -7,7 +7,7 @@ import { initialize, terminate } from "../IntegrationTests"; import { OpenMode, using } from "@bentley/bentleyjs-core"; import { IModelConnection } from "@bentley/imodeljs-frontend"; import { Presentation } from "@bentley/presentation-frontend"; -import { Ruleset, RootNodeRule, CustomNodeSpecification } from "@bentley/presentation-common"; +import { Ruleset, RootNodeRule, CustomNodeSpecification, RegisteredRuleset } from "@bentley/presentation-common"; describe("Rulesets", async () => { @@ -28,8 +28,8 @@ describe("Rulesets", async () => { it("creates ruleset from json and gets root node using it", async () => { const spec = ((ruleset.rules![0] as RootNodeRule).specifications![0] as CustomNodeSpecification); - await using(await Presentation.presentation.rulesets().add(ruleset), async () => { - const rootNodes = await Presentation.presentation.getRootNodes({ imodel, rulesetId: ruleset.id }); + await using>(await Presentation.presentation.rulesets().add(ruleset), async () => { + const rootNodes = await Presentation.presentation.getNodes({ imodel, rulesetId: ruleset.id }); expect(rootNodes.length).to.be.equal(1); expect(rootNodes[0].label).to.be.equal(spec.label); }); @@ -38,11 +38,11 @@ describe("Rulesets", async () => { it("removes ruleset", async () => { const registeredRuleset = await Presentation.presentation.rulesets().add(ruleset); - let rootNodes = await Presentation.presentation.getRootNodes({ imodel, rulesetId: ruleset.id }); + let rootNodes = await Presentation.presentation.getNodes({ imodel, rulesetId: ruleset.id }); expect(rootNodes.length).to.be.equal(1); expect(await Presentation.presentation.rulesets().remove(registeredRuleset)).to.be.true; - rootNodes = await Presentation.presentation.getRootNodes({ imodel, rulesetId: ruleset.id }); + rootNodes = await Presentation.presentation.getNodes({ imodel, rulesetId: ruleset.id }); expect(rootNodes.length).to.be.equal(0); expect(await Presentation.presentation.rulesets().remove(registeredRuleset)).to.be.false; @@ -59,11 +59,11 @@ describe("Rulesets", async () => { it("clears rulesets from frontend", async () => { await Presentation.presentation.rulesets().add(ruleset); - let rootNodes = await Presentation.presentation.getRootNodes({ imodel, rulesetId: ruleset.id }); + let rootNodes = await Presentation.presentation.getNodes({ imodel, rulesetId: ruleset.id }); expect(rootNodes.length).to.be.equal(1); await Presentation.presentation.rulesets().clear(); - rootNodes = await Presentation.presentation.getRootNodes({ imodel, rulesetId: ruleset.id }); + rootNodes = await Presentation.presentation.getNodes({ imodel, rulesetId: ruleset.id }); expect(rootNodes.length).to.be.equal(0); }); diff --git a/test-apps/presentation-integration-tests/src/frontend/SelectionScopes.test.snap b/test-apps/presentation-integration-tests/src/frontend/SelectionScopes.test.snap new file mode 100644 index 0000000..3fd77b5 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/SelectionScopes.test.snap @@ -0,0 +1,31 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Selection Scopes returns hardcoded selection scopes from the backend 1`] = ` +Array [ + Object { + "description": "Select the picked element", + "id": "element", + "label": "Element", + }, + Object { + "description": "Select parent of the picked element", + "id": "assembly", + "label": "Assembly", + }, + Object { + "description": "Select the topmost parent of the picked element", + "id": "top-assembly", + "label": "Top Assembly", + }, + Object { + "description": "Select all elements in the picked element's category", + "id": "category", + "label": "Category", + }, + Object { + "description": "Select all elements in the picked element's model", + "id": "model", + "label": "Model", + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/SelectionScopes.test.ts b/test-apps/presentation-integration-tests/src/frontend/SelectionScopes.test.ts new file mode 100644 index 0000000..f5606bf --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/SelectionScopes.test.ts @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { expect } from "chai"; +import { initialize, terminate } from "../IntegrationTests"; +import { OpenMode, Id64 } from "@bentley/bentleyjs-core"; +import { IModelConnection } from "@bentley/imodeljs-frontend"; +import { Presentation } from "@bentley/presentation-frontend"; + +describe("Selection Scopes", () => { + + let imodel: IModelConnection; + + before(async () => { + initialize(); + const testIModelName: string = "assets/datasets/Properties_60InstancesWithUrl2.ibim"; + imodel = await IModelConnection.openStandalone(testIModelName, OpenMode.Readonly); + expect(imodel).is.not.null; + }); + + after(async () => { + await imodel.closeStandalone(); + terminate(); + }); + + beforeEach(() => { + Presentation.selection.clearSelection("", imodel); + }); + + it("returns hardcoded selection scopes from the backend", async () => { + const scopes = await Presentation.selection.scopes.getSelectionScopes(imodel); + expect(scopes).to.matchSnapshot(); + }); + + it("sets correct selection with 'element' selection scope", async () => { + const elementProps = await imodel.elements.getProps(Id64.fromUint32Pair(116, 0)); + await Presentation.selection.addToSelectionWithScope("", imodel, elementProps, "element"); + const selection = Presentation.selection.getSelection(imodel); + expect(selection.size).to.eq(1); + expect(selection.has({ className: elementProps[0].classFullName, id: elementProps[0].id! })); + }); + + it("sets correct selection with 'assembly' selection scope", async () => { + const elementProps = await imodel.elements.getProps(Id64.fromUint32Pair(28, 0)); + await Presentation.selection.addToSelectionWithScope("", imodel, elementProps, "assembly"); + const selection = Presentation.selection.getSelection(imodel); + expect(selection.size).to.eq(1); + expect(selection.has({ className: "BisCore:Subject", id: Id64.fromUint32Pair(27, 0) })); + }); + + it("sets correct selection with 'top-assembly' selection scope", async () => { + const elementProps = await imodel.elements.getProps(Id64.fromUint32Pair(28, 0)); + await Presentation.selection.addToSelectionWithScope("", imodel, elementProps, "top-assembly"); + const selection = Presentation.selection.getSelection(imodel); + expect(selection.size).to.eq(1); + expect(selection.has({ className: "BisCore:Subject", id: Id64.fromUint32Pair(1, 0) })); + }); + + it("sets correct selection with 'category' selection scope", async () => { + const elementProps = await imodel.elements.getProps(Id64.fromUint32Pair(116, 0)); + await Presentation.selection.addToSelectionWithScope("", imodel, elementProps, "element"); + const selection = Presentation.selection.getSelection(imodel); + expect(selection.size).to.eq(1); + expect(selection.has({ className: "BisCore:Category", id: Id64.fromUint32Pair(23, 0) })); + }); + + it("sets correct selection with 'model' selection scope", async () => { + const elementProps = await imodel.elements.getProps(Id64.fromUint32Pair(116, 0)); + await Presentation.selection.addToSelectionWithScope("", imodel, elementProps, "element"); + const selection = Presentation.selection.getSelection(imodel); + expect(selection.size).to.eq(1); + expect(selection.has({ className: "BisCore:Model", id: Id64.fromUint32Pair(28, 0) })); + }); + +}); diff --git a/test-apps/presentation-integration-tests/src/frontend/multiple-backends-for-single-frontend/Hierarchies.test.ts b/test-apps/presentation-integration-tests/src/frontend/multiple-backends-for-single-frontend/Hierarchies.test.ts index 0b796aa..cac4814 100644 --- a/test-apps/presentation-integration-tests/src/frontend/multiple-backends-for-single-frontend/Hierarchies.test.ts +++ b/test-apps/presentation-integration-tests/src/frontend/multiple-backends-for-single-frontend/Hierarchies.test.ts @@ -33,13 +33,13 @@ describe("Multiple backends for one frontend", async () => { it("Gets child nodes after backend is reset", async () => { const props = { imodel, rulesetId: "SimpleHierarchy" }; - const rootNodes = await frontend.getRootNodes(props); + const rootNodes = await frontend.getNodes(props); expect(rootNodes.length).to.eq(1); expect(rootNodes[0].key.type).to.eq("root"); resetBackend(); - const childNodes = await frontend.getChildren(props, rootNodes[0].key); + const childNodes = await frontend.getNodes(props, rootNodes[0].key); expect(childNodes.length).to.eq(1); expect(childNodes[0].key.type).to.eq("child"); }); diff --git a/test-apps/presentation-integration-tests/src/frontend/multiple-backends-for-single-frontend/Rulesets.test.ts b/test-apps/presentation-integration-tests/src/frontend/multiple-backends-for-single-frontend/Rulesets.test.ts index 63ca734..09ce20e 100644 --- a/test-apps/presentation-integration-tests/src/frontend/multiple-backends-for-single-frontend/Rulesets.test.ts +++ b/test-apps/presentation-integration-tests/src/frontend/multiple-backends-for-single-frontend/Rulesets.test.ts @@ -7,7 +7,7 @@ import { initialize, terminate } from "../../IntegrationTests"; import { resetBackend } from "./Helpers"; import { OpenMode, using } from "@bentley/bentleyjs-core"; import { IModelConnection } from "@bentley/imodeljs-frontend"; -import { Ruleset, RootNodeRule, CustomNodeSpecification } from "@bentley/presentation-common"; +import { Ruleset, RootNodeRule, CustomNodeSpecification, RegisteredRuleset } from "@bentley/presentation-common"; import { PresentationManager } from "@bentley/presentation-frontend"; describe("Multiple backends for one frontend", async () => { @@ -37,14 +37,14 @@ describe("Multiple backends for one frontend", async () => { const props = { imodel, rulesetId: ruleset.id }; const spec = ((ruleset.rules![0] as RootNodeRule).specifications![0] as CustomNodeSpecification); - await using(await frontend.rulesets().add(ruleset), async () => { - const rootNodes1 = await frontend.getRootNodes(props); + await using>(await frontend.rulesets().add(ruleset), async () => { + const rootNodes1 = await frontend.getNodes(props); expect(rootNodes1.length).to.be.equal(1); expect(rootNodes1[0].label).to.be.equal(spec.label); resetBackend(); - const rootNodes2 = await frontend.getRootNodes(props); + const rootNodes2 = await frontend.getNodes(props); expect(rootNodes2.length).to.be.equal(1); expect(rootNodes2[0].label).to.be.equal(spec.label); expect(rootNodes2).to.deep.eq(rootNodes1); diff --git a/test-apps/presentation-integration-tests/src/frontend/multiple-frontends-for-single-backend/Localization.test.ts b/test-apps/presentation-integration-tests/src/frontend/multiple-frontends-for-single-backend/Localization.test.ts index 26654af..1640c36 100644 --- a/test-apps/presentation-integration-tests/src/frontend/multiple-frontends-for-single-backend/Localization.test.ts +++ b/test-apps/presentation-integration-tests/src/frontend/multiple-frontends-for-single-backend/Localization.test.ts @@ -34,8 +34,8 @@ describe("Multiple frontends for one backend", async () => { it("Handles multiple simultaneous requests from different frontends with different locales", async () => { for (let i = 0; i < 100; ++i) { const nodes = { - en: await frontends[0].getRootNodes({ imodel, rulesetId: "Localization" }), - test: await frontends[1].getRootNodes({ imodel, rulesetId: "Localization" }), + en: await frontends[0].getNodes({ imodel, rulesetId: "Localization" }), + test: await frontends[1].getNodes({ imodel, rulesetId: "Localization" }), }; expect(nodes.en[0].label).to.eq("test value"); diff --git a/test-apps/presentation-integration-tests/src/frontend/multiple-frontends-for-single-backend/RulesetVariables.test.ts b/test-apps/presentation-integration-tests/src/frontend/multiple-frontends-for-single-backend/RulesetVariables.test.ts index b4f99da..58eee1d 100644 --- a/test-apps/presentation-integration-tests/src/frontend/multiple-frontends-for-single-backend/RulesetVariables.test.ts +++ b/test-apps/presentation-integration-tests/src/frontend/multiple-frontends-for-single-backend/RulesetVariables.test.ts @@ -35,7 +35,7 @@ describe("Multiple frontends for one backend", async () => { const rulesetId = "RulesetVariables"; for (let i = 0; i < 100; ++i) { frontends.forEach(async (f, fi) => f.vars(rulesetId).setString("variable_id", `${i}_${fi}`)); - const nodes = await Promise.all(frontends.map(async (f) => f.getRootNodes({ imodel, rulesetId }))); + const nodes = await Promise.all(frontends.map(async (f) => f.getNodes({ imodel, rulesetId }))); frontends.forEach((_f, fi) => { expect(nodes[fi][0].label).to.eq(`${i}_${fi}`); }); diff --git a/test-apps/presentation-integration-tests/src/frontend/multiple-frontends-for-single-backend/Rulesets.test.ts b/test-apps/presentation-integration-tests/src/frontend/multiple-frontends-for-single-backend/Rulesets.test.ts index 7c6c0f3..ffef205 100644 --- a/test-apps/presentation-integration-tests/src/frontend/multiple-frontends-for-single-backend/Rulesets.test.ts +++ b/test-apps/presentation-integration-tests/src/frontend/multiple-frontends-for-single-backend/Rulesets.test.ts @@ -63,7 +63,7 @@ describe("Multiple frontends for one backend", async () => { const registeredRulesets = await Promise.all(frontends.map(async (f, i) => f.rulesets().add(rulesets[i]))); - const nodes = await Promise.all(frontends.map(async (f) => f.getRootNodes({ imodel, rulesetId: "test" }))); + const nodes = await Promise.all(frontends.map(async (f) => f.getNodes({ imodel, rulesetId: "test" }))); frontends.forEach((_f, i) => { expect(nodes[i][0].label).to.eq(`label ${i}`); }); diff --git a/test-apps/presentation-integration-tests/src/frontend/providers/PropertyPaneDataProvider.test.snap b/test-apps/presentation-integration-tests/src/frontend/providers/PropertyPaneDataProvider.test.snap index f6932fa..8a6316d 100644 --- a/test-apps/presentation-integration-tests/src/frontend/providers/PropertyPaneDataProvider.test.snap +++ b/test-apps/presentation-integration-tests/src/frontend/providers/PropertyPaneDataProvider.test.snap @@ -15,7 +15,6 @@ Object { "Miscellaneous": Array [ PropertyRecord { "description": "Properties_60InstancesWithUrl2", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "£ábêl", @@ -30,7 +29,6 @@ Object { }, PropertyRecord { "description": "Properties_60InstancesWithUrl2", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Modeled Element", @@ -68,7 +66,6 @@ Object { "Favorite": Array [ PropertyRecord { "description": "Properties_60InstancesWithUrl2", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "£ábêl", @@ -83,7 +80,6 @@ Object { }, PropertyRecord { "description": "Properties_60InstancesWithUrl2", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Modeled Element", @@ -100,7 +96,6 @@ Object { "Miscellaneous": Array [ PropertyRecord { "description": "Properties_60InstancesWithUrl2", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "£ábêl", @@ -115,7 +110,6 @@ Object { }, PropertyRecord { "description": "Properties_60InstancesWithUrl2", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Modeled Element", diff --git a/test-apps/presentation-integration-tests/src/frontend/providers/TableDataProvider.test.snap b/test-apps/presentation-integration-tests/src/frontend/providers/TableDataProvider.test.snap index a5268bc..d937b1c 100644 --- a/test-apps/presentation-integration-tests/src/frontend/providers/TableDataProvider.test.snap +++ b/test-apps/presentation-integration-tests/src/frontend/providers/TableDataProvider.test.snap @@ -45,7 +45,6 @@ Object { "key": "/DisplayLabel/", "record": PropertyRecord { "description": "Properties_60InstancesWithUrl2", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "£ábêl", @@ -63,7 +62,6 @@ Object { "key": "PhysicalModel_ModeledElement", "record": PropertyRecord { "description": "Properties_60InstancesWithUrl2", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Modeled Element", @@ -90,7 +88,6 @@ Array [ "key": "/DisplayLabel/", "record": PropertyRecord { "description": "BisCore.DictionaryModel", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "£ábêl", @@ -108,7 +105,6 @@ Array [ "key": "DictionaryModel_PhysicalModel_RepositoryModel_ModeledElement", "record": PropertyRecord { "description": "BisCore.DictionaryModel", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Modeled Element", @@ -131,7 +127,6 @@ Array [ "key": "/DisplayLabel/", "record": PropertyRecord { "description": "DgnV8Bridge", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "£ábêl", @@ -149,7 +144,6 @@ Array [ "key": "DictionaryModel_PhysicalModel_RepositoryModel_ModeledElement", "record": PropertyRecord { "description": "Ñót spêçìfíêd", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Modeled Element", @@ -172,7 +166,6 @@ Array [ "key": "/DisplayLabel/", "record": PropertyRecord { "description": "Properties_60InstancesWithUrl2", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "£ábêl", @@ -190,7 +183,6 @@ Array [ "key": "DictionaryModel_PhysicalModel_RepositoryModel_ModeledElement", "record": PropertyRecord { "description": "Properties_60InstancesWithUrl2", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Modeled Element", @@ -218,7 +210,6 @@ Array [ "key": "/DisplayLabel/", "record": PropertyRecord { "description": "Properties_60InstancesWithUrl2", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "£ábêl", @@ -236,7 +227,6 @@ Array [ "key": "DictionaryModel_PhysicalModel_RepositoryModel_ModeledElement", "record": PropertyRecord { "description": "Properties_60InstancesWithUrl2", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Modeled Element", @@ -259,7 +249,6 @@ Array [ "key": "/DisplayLabel/", "record": PropertyRecord { "description": "DgnV8Bridge", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "£ábêl", @@ -277,7 +266,6 @@ Array [ "key": "DictionaryModel_PhysicalModel_RepositoryModel_ModeledElement", "record": PropertyRecord { "description": "Ñót spêçìfíêd", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Modeled Element", @@ -300,7 +288,6 @@ Array [ "key": "/DisplayLabel/", "record": PropertyRecord { "description": "BisCore.DictionaryModel", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "£ábêl", @@ -318,7 +305,6 @@ Array [ "key": "DictionaryModel_PhysicalModel_RepositoryModel_ModeledElement", "record": PropertyRecord { "description": "BisCore.DictionaryModel", - "isMerged": false, "isReadonly": true, "property": Object { "displayLabel": "Modeled Element", diff --git a/test-apps/presentation-integration-tests/src/frontend/providers/TableDataProvider.test.ts b/test-apps/presentation-integration-tests/src/frontend/providers/TableDataProvider.test.ts index 03baab7..91dcaea 100644 --- a/test-apps/presentation-integration-tests/src/frontend/providers/TableDataProvider.test.ts +++ b/test-apps/presentation-integration-tests/src/frontend/providers/TableDataProvider.test.ts @@ -3,12 +3,14 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; +import * as sinon from "sinon"; import { initialize, terminate } from "../../IntegrationTests"; import { OpenMode, Id64 } from "@bentley/bentleyjs-core"; import { ModelProps } from "@bentley/imodeljs-common"; import { IModelConnection } from "@bentley/imodeljs-frontend"; import { KeySet, instanceKeyFromJSON } from "@bentley/presentation-common"; import { PresentationTableDataProvider } from "@bentley/presentation-components"; +import { Presentation } from "@bentley/presentation-frontend"; import { SortDirection } from "@bentley/ui-core"; interface MeaningfulInstances { @@ -35,7 +37,10 @@ describe("TableDataProvider", async () => { const testIModelName: string = "assets/datasets/Properties_60InstancesWithUrl2.ibim"; imodel = await IModelConnection.openStandalone(testIModelName, OpenMode.Readonly); instances = await createMeaningfulInstances(imodel); - provider = new PresentationTableDataProvider(imodel, "SimpleContent", 10); + }); + + beforeEach(async () => { + provider = new PresentationTableDataProvider({ imodel, ruleset: "SimpleContent", pageSize: 10 }); }); after(async () => { @@ -92,6 +97,20 @@ describe("TableDataProvider", async () => { }); + it("requests backend only once to get first page", async () => { + const getContentAndContentSizeSpy = sinon.spy(Presentation.presentation.rpcRequestsHandler, "getContentAndSize"); + provider.keys = new KeySet([instances.physicalModel]); + provider.pagingSize = 10; + + // request count and first page + const count = await provider.getRowsCount(); + const row = await provider.getRow(0); + + expect(count).to.not.eq(0); + expect(row).to.not.be.undefined; + expect(getContentAndContentSizeSpy.calledOnce).to.be.true; + }); + describe("sorting", () => { it("sorts instances ascending", async () => { diff --git a/test-apps/presentation-integration-tests/src/frontend/providers/TreeDataProvider.test.snap b/test-apps/presentation-integration-tests/src/frontend/providers/TreeDataProvider.test.snap index 6fd21d7..00525d4 100644 --- a/test-apps/presentation-integration-tests/src/frontend/providers/TreeDataProvider.test.snap +++ b/test-apps/presentation-integration-tests/src/frontend/providers/TreeDataProvider.test.snap @@ -20,6 +20,26 @@ Array [ ] `; +exports[`TreeDataProvider returns child nodes with paging 1`] = ` +Array [ + Object { + "description": "child description", + "extendedData": Object { + "key": Object { + "pathFromRoot": Array [ + "64e31cdc690e6d628b36f761e4074754", + "8ca7a2f0af9eb677d127a4d2d299bc9b", + ], + "type": "child", + }, + }, + "id": "8ca7a2f0af9eb677d127a4d2d299bc9b/64e31cdc690e6d628b36f761e4074754", + "label": "child label", + "parentId": "64e31cdc690e6d628b36f761e4074754", + }, +] +`; + exports[`TreeDataProvider returns root nodes 1`] = ` Array [ Object { @@ -38,10 +58,44 @@ Array [ "isCheckboxDisabled": true, "isCheckboxVisible": true, "label": "root label", - "labelBackColor": 8388863, - "labelBold": true, - "labelForeColor": 4278190335, - "labelItalic": true, + "style": Object { + "colorOverrides": Object { + "backgroundColor": 8388863, + "color": 4278190335, + }, + "isBold": true, + "isItalic": true, + }, + }, +] +`; + +exports[`TreeDataProvider returns root nodes with paging 1`] = ` +Array [ + Object { + "checkBoxState": 1, + "description": "root description", + "extendedData": Object { + "key": Object { + "pathFromRoot": Array [ + "64e31cdc690e6d628b36f761e4074754", + ], + "type": "root", + }, + }, + "hasChildren": true, + "id": "64e31cdc690e6d628b36f761e4074754", + "isCheckboxDisabled": true, + "isCheckboxVisible": true, + "label": "root label", + "style": Object { + "colorOverrides": Object { + "backgroundColor": 8388863, + "color": 4278190335, + }, + "isBold": true, + "isItalic": true, + }, }, ] `; diff --git a/test-apps/presentation-integration-tests/src/frontend/providers/TreeDataProvider.test.ts b/test-apps/presentation-integration-tests/src/frontend/providers/TreeDataProvider.test.ts index 656b082..ff90a72 100644 --- a/test-apps/presentation-integration-tests/src/frontend/providers/TreeDataProvider.test.ts +++ b/test-apps/presentation-integration-tests/src/frontend/providers/TreeDataProvider.test.ts @@ -3,10 +3,12 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; +import * as sinon from "sinon"; import { initialize, terminate } from "../../IntegrationTests"; import { OpenMode } from "@bentley/bentleyjs-core"; import { IModelConnection } from "@bentley/imodeljs-frontend"; import { PresentationTreeDataProvider } from "@bentley/presentation-components"; +import { Presentation } from "@bentley/presentation-frontend"; describe("TreeDataProvider", async () => { @@ -36,6 +38,17 @@ describe("TreeDataProvider", async () => { expect(nodes).to.matchSnapshot(); }); + it("returns root nodes with paging", async () => { + const nodes = await provider.getNodes(undefined, { start: 0, size: 5 }); + expect(nodes.length).to.eq(1); + expect(nodes).to.matchSnapshot(); + }); + + it("does not return root nodes with invalid paging", async () => { + const nodes = await provider.getNodes(undefined, { start: 1, size: 5 }); + expect(nodes.length).to.eq(0); + }); + it("returns child nodes count", async () => { const rootNodes = await provider.getNodes(); const count = await provider.getNodesCount(rootNodes[0]); @@ -48,4 +61,30 @@ describe("TreeDataProvider", async () => { expect(childNodes).to.matchSnapshot(); }); + it("returns child nodes with paging", async () => { + const rootNodes = await provider.getNodes(); + const nodes = await provider.getNodes(rootNodes[0], { start: 0, size: 5 }); + expect(nodes.length).to.eq(1); + expect(nodes).to.matchSnapshot(); + }); + + it("does not return child nodes with invalid paging", async () => { + const rootNodes = await provider.getNodes(); + const nodes = await provider.getNodes(rootNodes[0], { start: 1, size: 5 }); + expect(nodes.length).to.eq(0); + }); + + it("requests backend only once to get first page", async () => { + const getNodesSpy = sinon.spy(Presentation.presentation.rpcRequestsHandler, "getNodesAndCount"); + provider.pagingSize = 10; + + // request count and first page + const count = await provider.getNodesCount(); + const nodes = await provider.getNodes(undefined, { start: 0, size: 10 }); + + expect(count).to.not.eq(0); + expect(nodes).to.not.be.undefined; + expect(getNodesSpy.calledOnce).to.be.true; + }); + }); diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.AuxCoordSystemSpatial.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.AuxCoordSystemSpatial.snap new file mode 100644 index 0000000..7ab00be --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.AuxCoordSystemSpatial.snap @@ -0,0 +1,139 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:AuxCoordSystemSpatial' 1`] = ` +Array [ + PropertyRecord { + "description": "-5.8361314584487598", + "property": Object { + "displayLabel": "Yaw", + "name": "AuxCoordSystemSpatial_Yaw", + "typename": "double", + }, + "value": Object { + "displayValue": "-5.8361314584487598", + "value": -5.83613145844876, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "User Label", + "name": "AuxCoordSystemSpatial_UserLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "1", + "property": Object { + "displayLabel": "Type", + "name": "AuxCoordSystemSpatial_Type", + "typename": "int", + }, + "value": Object { + "displayValue": "1", + "value": 1, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "-107.41900335275101", + "property": Object { + "displayLabel": "Roll", + "name": "AuxCoordSystemSpatial_Roll", + "typename": "double", + }, + "value": Object { + "displayValue": "-107.41900335275101", + "value": -107.419003352751, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "-160.98686924400599", + "property": Object { + "displayLabel": "Pitch", + "name": "AuxCoordSystemSpatial_Pitch", + "typename": "double", + }, + "value": Object { + "displayValue": "-160.98686924400599", + "value": -160.986869244006, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "0.30496800000000002,1.4094100000000001,0", + "property": Object { + "displayLabel": "Origin", + "name": "AuxCoordSystemSpatial_Origin", + "typename": "point3d", + }, + "value": Object { + "displayValue": "0.30496800000000002,1.4094100000000001,0", + "value": Object { + "x": 0.304968, + "y": 1.40941, + "z": 0, + }, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Definition Model For DgnV8Bridge:D:\\\\Temp\\\\Properties_60InstancesWithUrl2.dgn, Default", + "isReadonly": true, + "property": Object { + "displayLabel": "Model", + "name": "AuxCoordSystemSpatial_Model", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Definition Model For DgnV8Bridge:D:\\\\Temp\\\\Properties_60InstancesWithUrl2.dgn, Default", + "value": 22, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "False", + "property": Object { + "displayLabel": "Is Private", + "name": "AuxCoordSystemSpatial_IsPrivate", + "typename": "boolean", + }, + "value": Object { + "displayValue": "False", + "value": false, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "Description", + "name": "AuxCoordSystemSpatial_Description", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Default - View 1", + "property": Object { + "displayLabel": "Code", + "name": "AuxCoordSystemSpatial_CodeValue", + "typename": "string", + }, + "value": Object { + "displayValue": "Default - View 1", + "value": "Default - View 1", + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.CategorySelector.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.CategorySelector.snap new file mode 100644 index 0000000..8023d00 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.CategorySelector.snap @@ -0,0 +1,58 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:CategorySelector' 1`] = ` +Array [ + PropertyRecord { + "property": Object { + "displayLabel": "User Label", + "name": "CategorySelector_UserLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Definition Model For DgnV8Bridge:D:\\\\Temp\\\\Properties_60InstancesWithUrl2.dgn, Default", + "isReadonly": true, + "property": Object { + "displayLabel": "Model", + "name": "CategorySelector_Model", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Definition Model For DgnV8Bridge:D:\\\\Temp\\\\Properties_60InstancesWithUrl2.dgn, Default", + "value": 22, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "False", + "property": Object { + "displayLabel": "Is Private", + "name": "CategorySelector_IsPrivate", + "typename": "boolean", + }, + "value": Object { + "displayValue": "False", + "value": false, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Default - View 1", + "property": Object { + "displayLabel": "Code", + "name": "CategorySelector_CodeValue", + "typename": "string", + }, + "value": Object { + "displayValue": "Default - View 1", + "value": "Default - View 1", + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.CodeSpec.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.CodeSpec.snap new file mode 100644 index 0000000..7a784fc --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.CodeSpec.snap @@ -0,0 +1,32 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:CodeSpec' 1`] = ` +Array [ + PropertyRecord { + "description": "bis:NullCodeSpec", + "property": Object { + "displayLabel": "Name", + "name": "CodeSpec_Name", + "typename": "string", + }, + "value": Object { + "displayValue": "bis:NullCodeSpec", + "value": "bis:NullCodeSpec", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "{\\"scopeSpec\\":{\\"type\\":1},\\"version\\":\\"1.0\\"}", + "property": Object { + "displayLabel": "JsonProperties", + "name": "CodeSpec_JsonProperties", + "typename": "string", + }, + "value": Object { + "displayValue": "{\\"scopeSpec\\":{\\"type\\":1},\\"version\\":\\"1.0\\"}", + "value": "{\\"scopeSpec\\":{\\"type\\":1},\\"version\\":\\"1.0\\"}", + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.DefinitionModel.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.DefinitionModel.snap new file mode 100644 index 0000000..c1eba96 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.DefinitionModel.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:DefinitionModel' 1`] = ` +Array [ + PropertyRecord { + "description": "Definition Model For DgnV8Bridge:D:\\\\Temp\\\\Properties_60InstancesWithUrl2.dgn, Default", + "isReadonly": true, + "property": Object { + "displayLabel": "Modeled Element", + "name": "DefinitionModel_ModeledElement", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Definition Model For DgnV8Bridge:D:\\\\Temp\\\\Properties_60InstancesWithUrl2.dgn, Default", + "value": 22, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.DefinitionPartition.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.DefinitionPartition.snap new file mode 100644 index 0000000..277859f --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.DefinitionPartition.snap @@ -0,0 +1,57 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:DefinitionPartition' 1`] = ` +Array [ + PropertyRecord { + "property": Object { + "displayLabel": "User Label", + "name": "DefinitionPartition_UserLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "DgnV8Bridge", + "isReadonly": true, + "property": Object { + "displayLabel": "Model", + "name": "DefinitionPartition_Model", + "typename": "navigation", + }, + "value": Object { + "displayValue": "DgnV8Bridge", + "value": 1, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "Description", + "name": "DefinitionPartition_Description", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "BisCore.DictionaryModel", + "property": Object { + "displayLabel": "Code", + "name": "DefinitionPartition_CodeValue", + "typename": "string", + }, + "value": Object { + "displayValue": "BisCore.DictionaryModel", + "value": "BisCore.DictionaryModel", + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.DictionaryModel.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.DictionaryModel.snap new file mode 100644 index 0000000..709c7cf --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.DictionaryModel.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:DictionaryModel' 1`] = ` +Array [ + PropertyRecord { + "description": "BisCore.DictionaryModel", + "isReadonly": true, + "property": Object { + "displayLabel": "Modeled Element", + "name": "DictionaryModel_ModeledElement", + "typename": "navigation", + }, + "value": Object { + "displayValue": "BisCore.DictionaryModel", + "value": 16, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.DisplayStyle3d.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.DisplayStyle3d.snap new file mode 100644 index 0000000..a87249a --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.DisplayStyle3d.snap @@ -0,0 +1,58 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:DisplayStyle3d' 1`] = ` +Array [ + PropertyRecord { + "property": Object { + "displayLabel": "User Label", + "name": "DisplayStyle3d_UserLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Definition Model For DgnV8Bridge:D:\\\\Temp\\\\Properties_60InstancesWithUrl2.dgn, Default", + "isReadonly": true, + "property": Object { + "displayLabel": "Model", + "name": "DisplayStyle3d_Model", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Definition Model For DgnV8Bridge:D:\\\\Temp\\\\Properties_60InstancesWithUrl2.dgn, Default", + "value": 22, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "False", + "property": Object { + "displayLabel": "Is Private", + "name": "DisplayStyle3d_IsPrivate", + "typename": "boolean", + }, + "value": Object { + "displayValue": "False", + "value": false, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Illustration", + "property": Object { + "displayLabel": "Code", + "name": "DisplayStyle3d_CodeValue", + "typename": "string", + }, + "value": Object { + "displayValue": "Illustration", + "value": "Illustration", + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.DocumentListModel.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.DocumentListModel.snap new file mode 100644 index 0000000..d659808 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.DocumentListModel.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:DocumentListModel' 1`] = ` +Array [ + PropertyRecord { + "description": "Converted Drawings", + "isReadonly": true, + "property": Object { + "displayLabel": "Modeled Element", + "name": "DocumentListModel_ModeledElement", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Converted Drawings", + "value": 20, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.DocumentPartition.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.DocumentPartition.snap new file mode 100644 index 0000000..dbd5311 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.DocumentPartition.snap @@ -0,0 +1,57 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:DocumentPartition' 1`] = ` +Array [ + PropertyRecord { + "property": Object { + "displayLabel": "User Label", + "name": "DocumentPartition_UserLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "DgnV8Bridge", + "isReadonly": true, + "property": Object { + "displayLabel": "Model", + "name": "DocumentPartition_Model", + "typename": "navigation", + }, + "value": Object { + "displayValue": "DgnV8Bridge", + "value": 1, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "Description", + "name": "DocumentPartition_Description", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Converted Drawings", + "property": Object { + "displayLabel": "Code", + "name": "DocumentPartition_CodeValue", + "typename": "string", + }, + "value": Object { + "displayValue": "Converted Drawings", + "value": "Converted Drawings", + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.DrawingCategory.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.DrawingCategory.snap new file mode 100644 index 0000000..c458bbf --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.DrawingCategory.snap @@ -0,0 +1,58 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:DrawingCategory' 1`] = ` +Array [ + PropertyRecord { + "property": Object { + "displayLabel": "User Label", + "name": "DrawingCategory_UserLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Definition Model For DgnV8Bridge:D:\\\\Temp\\\\Properties_60InstancesWithUrl2.dgn, Default", + "isReadonly": true, + "property": Object { + "displayLabel": "Model", + "name": "DrawingCategory_Model", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Definition Model For DgnV8Bridge:D:\\\\Temp\\\\Properties_60InstancesWithUrl2.dgn, Default", + "value": 22, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "False", + "property": Object { + "displayLabel": "Is Private", + "name": "DrawingCategory_IsPrivate", + "typename": "boolean", + }, + "value": Object { + "displayValue": "False", + "value": false, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Uncategorized", + "property": Object { + "displayLabel": "Code", + "name": "DrawingCategory_CodeValue", + "typename": "string", + }, + "value": Object { + "displayValue": "Uncategorized", + "value": "Uncategorized", + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.GroupInformationPartition.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.GroupInformationPartition.snap new file mode 100644 index 0000000..8a9d89c --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.GroupInformationPartition.snap @@ -0,0 +1,57 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:GroupInformationPartition' 1`] = ` +Array [ + PropertyRecord { + "property": Object { + "displayLabel": "User Label", + "name": "GroupInformationPartition_UserLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "DgnV8Bridge", + "isReadonly": true, + "property": Object { + "displayLabel": "Model", + "name": "GroupInformationPartition_Model", + "typename": "navigation", + }, + "value": Object { + "displayValue": "DgnV8Bridge", + "value": 1, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "Description", + "name": "GroupInformationPartition_Description", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Converted Groups", + "property": Object { + "displayLabel": "Code", + "name": "GroupInformationPartition_CodeValue", + "typename": "string", + }, + "value": Object { + "displayValue": "Converted Groups", + "value": "Converted Groups", + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.LineStyle.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.LineStyle.snap new file mode 100644 index 0000000..d0d421f --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.LineStyle.snap @@ -0,0 +1,83 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:LineStyle' 1`] = ` +Array [ + PropertyRecord { + "property": Object { + "displayLabel": "User Label", + "name": "LineStyle_UserLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "BisCore.DictionaryModel", + "isReadonly": true, + "property": Object { + "displayLabel": "Model", + "name": "LineStyle_Model", + "typename": "navigation", + }, + "value": Object { + "displayValue": "BisCore.DictionaryModel", + "value": 16, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "False", + "property": Object { + "displayLabel": "Is Private", + "name": "LineStyle_IsPrivate", + "typename": "boolean", + }, + "value": Object { + "displayValue": "False", + "value": false, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "Description", + "name": "LineStyle_Description", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "{\\"compId\\":1,\\"compType\\":6,\\"flags\\":6,\\"unitDef\\":1.0}", + "property": Object { + "displayLabel": "Data", + "name": "LineStyle_Data", + "typename": "string", + }, + "value": Object { + "displayValue": "{\\"compId\\":1,\\"compType\\":6,\\"flags\\":6,\\"unitDef\\":1.0}", + "value": "{\\"compId\\":1,\\"compType\\":6,\\"flags\\":6,\\"unitDef\\":1.0}", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "lc1", + "property": Object { + "displayLabel": "Code", + "name": "LineStyle_CodeValue", + "typename": "string", + }, + "value": Object { + "displayValue": "lc1", + "value": "lc1", + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.LinkModel.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.LinkModel.snap new file mode 100644 index 0000000..06bbcc9 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.LinkModel.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:LinkModel' 1`] = ` +Array [ + PropertyRecord { + "description": "BisCore.RealityDataSources", + "isReadonly": true, + "property": Object { + "displayLabel": "Modeled Element", + "name": "LinkModel_ModeledElement", + "typename": "navigation", + }, + "value": Object { + "displayValue": "BisCore.RealityDataSources", + "value": 14, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.LinkPartition.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.LinkPartition.snap new file mode 100644 index 0000000..3312fa0 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.LinkPartition.snap @@ -0,0 +1,57 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:LinkPartition' 1`] = ` +Array [ + PropertyRecord { + "property": Object { + "displayLabel": "User Label", + "name": "LinkPartition_UserLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "DgnV8Bridge", + "isReadonly": true, + "property": Object { + "displayLabel": "Model", + "name": "LinkPartition_Model", + "typename": "navigation", + }, + "value": Object { + "displayValue": "DgnV8Bridge", + "value": 1, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "Description", + "name": "LinkPartition_Description", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "BisCore.RealityDataSources", + "property": Object { + "displayLabel": "Code", + "name": "LinkPartition_CodeValue", + "typename": "string", + }, + "value": Object { + "displayValue": "BisCore.RealityDataSources", + "value": "BisCore.RealityDataSources", + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.ModelSelector.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.ModelSelector.snap new file mode 100644 index 0000000..58b284a --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.ModelSelector.snap @@ -0,0 +1,58 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:ModelSelector' 1`] = ` +Array [ + PropertyRecord { + "property": Object { + "displayLabel": "User Label", + "name": "ModelSelector_UserLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Definition Model For DgnV8Bridge:D:\\\\Temp\\\\Properties_60InstancesWithUrl2.dgn, Default", + "isReadonly": true, + "property": Object { + "displayLabel": "Model", + "name": "ModelSelector_Model", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Definition Model For DgnV8Bridge:D:\\\\Temp\\\\Properties_60InstancesWithUrl2.dgn, Default", + "value": 22, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "False", + "property": Object { + "displayLabel": "Is Private", + "name": "ModelSelector_IsPrivate", + "typename": "boolean", + }, + "value": Object { + "displayValue": "False", + "value": false, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Default - View 1", + "property": Object { + "displayLabel": "Code", + "name": "ModelSelector_CodeValue", + "typename": "string", + }, + "value": Object { + "displayValue": "Default - View 1", + "value": "Default - View 1", + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.PhysicalModel.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.PhysicalModel.snap new file mode 100644 index 0000000..7daf6c4 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.PhysicalModel.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:PhysicalModel' 1`] = ` +Array [ + PropertyRecord { + "description": "Properties_60InstancesWithUrl2", + "isReadonly": true, + "property": Object { + "displayLabel": "Modeled Element", + "name": "PhysicalModel_ModeledElement", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Properties_60InstancesWithUrl2", + "value": 28, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.PhysicalPartition.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.PhysicalPartition.snap new file mode 100644 index 0000000..2f17171 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.PhysicalPartition.snap @@ -0,0 +1,57 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:PhysicalPartition' 1`] = ` +Array [ + PropertyRecord { + "property": Object { + "displayLabel": "User Label", + "name": "PhysicalPartition_UserLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "DgnV8Bridge", + "isReadonly": true, + "property": Object { + "displayLabel": "Model", + "name": "PhysicalPartition_Model", + "typename": "navigation", + }, + "value": Object { + "displayValue": "DgnV8Bridge", + "value": 1, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "Description", + "name": "PhysicalPartition_Description", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Properties_60InstancesWithUrl2", + "property": Object { + "displayLabel": "Code", + "name": "PhysicalPartition_CodeValue", + "typename": "string", + }, + "value": Object { + "displayValue": "Properties_60InstancesWithUrl2", + "value": "Properties_60InstancesWithUrl2", + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.RepositoryLink.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.RepositoryLink.snap new file mode 100644 index 0000000..8a808fc --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.RepositoryLink.snap @@ -0,0 +1,71 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:RepositoryLink' 1`] = ` +Array [ + PropertyRecord { + "description": "Properties_60InstancesWithUrl2.dgn", + "property": Object { + "displayLabel": "User Label", + "name": "RepositoryLink_UserLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "Properties_60InstancesWithUrl2.dgn", + "value": "Properties_60InstancesWithUrl2.dgn", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "file:///d|/temp/properties_60instanceswithurl2.dgn", + "property": Object { + "displayLabel": "URL", + "name": "RepositoryLink_Url", + "typename": "string", + }, + "value": Object { + "displayValue": "file:///d|/temp/properties_60instanceswithurl2.dgn", + "value": "file:///d|/temp/properties_60instanceswithurl2.dgn", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "DgnV8Bridge", + "isReadonly": true, + "property": Object { + "displayLabel": "Model", + "name": "RepositoryLink_Model", + "typename": "navigation", + }, + "value": Object { + "displayValue": "DgnV8Bridge", + "value": 1, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "Description", + "name": "RepositoryLink_Description", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "file:///d|/temp/properties_60instanceswithurl2.dgn", + "property": Object { + "displayLabel": "Code", + "name": "RepositoryLink_CodeValue", + "typename": "string", + }, + "value": Object { + "displayValue": "file:///d|/temp/properties_60instanceswithurl2.dgn", + "value": "file:///d|/temp/properties_60instanceswithurl2.dgn", + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.RepositoryModel.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.RepositoryModel.snap new file mode 100644 index 0000000..0232838 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.RepositoryModel.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:RepositoryModel' 1`] = ` +Array [ + PropertyRecord { + "description": "Ñót spêçìfíêd", + "isReadonly": true, + "property": Object { + "displayLabel": "Modeled Element", + "name": "RepositoryModel_ModeledElement", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Ñót spêçìfíêd", + "value": undefined, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.SpatialCategory.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.SpatialCategory.snap new file mode 100644 index 0000000..368cc81 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.SpatialCategory.snap @@ -0,0 +1,58 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:SpatialCategory' 1`] = ` +Array [ + PropertyRecord { + "property": Object { + "displayLabel": "User Label", + "name": "SpatialCategory_UserLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Definition Model For DgnV8Bridge:D:\\\\Temp\\\\Properties_60InstancesWithUrl2.dgn, Default", + "isReadonly": true, + "property": Object { + "displayLabel": "Model", + "name": "SpatialCategory_Model", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Definition Model For DgnV8Bridge:D:\\\\Temp\\\\Properties_60InstancesWithUrl2.dgn, Default", + "value": 22, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "False", + "property": Object { + "displayLabel": "Is Private", + "name": "SpatialCategory_IsPrivate", + "typename": "boolean", + }, + "value": Object { + "displayValue": "False", + "value": false, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Uncategorized", + "property": Object { + "displayLabel": "Code", + "name": "SpatialCategory_CodeValue", + "typename": "string", + }, + "value": Object { + "displayValue": "Uncategorized", + "value": "Uncategorized", + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.SpatialIndex.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.SpatialIndex.snap new file mode 100644 index 0000000..c130f52 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.SpatialIndex.snap @@ -0,0 +1,90 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:SpatialIndex' 1`] = ` +Array [ + PropertyRecord { + "description": "-0.53096020221710205", + "isReadonly": true, + "property": Object { + "displayLabel": "Min Z", + "name": "SpatialIndex_MinZ", + "typename": "double", + }, + "value": Object { + "displayValue": "-0.53096020221710205", + "value": -0.530960202217102, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "1.3836752176284799", + "isReadonly": true, + "property": Object { + "displayLabel": "Min Y", + "name": "SpatialIndex_MinY", + "typename": "double", + }, + "value": Object { + "displayValue": "1.3836752176284799", + "value": 1.38367521762848, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "-2.8011031150817902", + "isReadonly": true, + "property": Object { + "displayLabel": "Min X", + "name": "SpatialIndex_MinX", + "typename": "double", + }, + "value": Object { + "displayValue": "-2.8011031150817902", + "value": -2.80110311508179, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "0.53096020221710205", + "isReadonly": true, + "property": Object { + "displayLabel": "Max Z", + "name": "SpatialIndex_MaxZ", + "typename": "double", + }, + "value": Object { + "displayValue": "0.53096020221710205", + "value": 0.530960202217102, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "2.44559597969055", + "isReadonly": true, + "property": Object { + "displayLabel": "Max Y", + "name": "SpatialIndex_MaxY", + "typename": "double", + }, + "value": Object { + "displayValue": "2.44559597969055", + "value": 2.44559597969055, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "-1.7391824722289999", + "isReadonly": true, + "property": Object { + "displayLabel": "Max X", + "name": "SpatialIndex_MaxX", + "typename": "double", + }, + "value": Object { + "displayValue": "-1.7391824722289999", + "value": -1.739182472229, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.SpatialViewDefinition.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.SpatialViewDefinition.snap new file mode 100644 index 0000000..4a45bbe --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.SpatialViewDefinition.snap @@ -0,0 +1,241 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:SpatialViewDefinition' 1`] = ` +Array [ + PropertyRecord { + "description": "-5.8361314584487598", + "property": Object { + "displayLabel": "Yaw", + "name": "SpatialViewDefinition_Yaw", + "typename": "double", + }, + "value": Object { + "displayValue": "-5.8361314584487598", + "value": -5.83613145844876, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "User Label", + "name": "SpatialViewDefinition_UserLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "-107.41900335275101", + "property": Object { + "displayLabel": "Roll", + "name": "SpatialViewDefinition_Roll", + "typename": "double", + }, + "value": Object { + "displayValue": "-107.41900335275101", + "value": -107.419003352751, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "-160.98686924400599", + "property": Object { + "displayLabel": "Pitch", + "name": "SpatialViewDefinition_Pitch", + "typename": "double", + }, + "value": Object { + "displayValue": "-160.98686924400599", + "value": -160.986869244006, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "16.459990999999999,-6.7779389999999999,-8.1429329999999993", + "property": Object { + "displayLabel": "Origin", + "name": "SpatialViewDefinition_Origin", + "typename": "point3d", + }, + "value": Object { + "displayValue": "16.459990999999999,-6.7779389999999999,-8.1429329999999993", + "value": Object { + "x": 16.459991, + "y": -6.777939, + "z": -8.142933, + }, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Default - View 1", + "isReadonly": true, + "property": Object { + "displayLabel": "Model Selector", + "name": "SpatialViewDefinition_ModelSelector", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Default - View 1", + "value": 38, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Definition Model For DgnV8Bridge:D:\\\\Temp\\\\Properties_60InstancesWithUrl2.dgn, Default", + "isReadonly": true, + "property": Object { + "displayLabel": "Model", + "name": "SpatialViewDefinition_Model", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Definition Model For DgnV8Bridge:D:\\\\Temp\\\\Properties_60InstancesWithUrl2.dgn, Default", + "value": 22, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "0", + "property": Object { + "displayLabel": "Lens Angle", + "name": "SpatialViewDefinition_LensAngle", + "typename": "double", + }, + "value": Object { + "displayValue": "0", + "value": 0, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "False", + "property": Object { + "displayLabel": "Is Private", + "name": "SpatialViewDefinition_IsPrivate", + "typename": "boolean", + }, + "value": Object { + "displayValue": "False", + "value": false, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "False", + "property": Object { + "displayLabel": "Is Camera On", + "name": "SpatialViewDefinition_IsCameraOn", + "typename": "boolean", + }, + "value": Object { + "displayValue": "False", + "value": false, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "0", + "property": Object { + "displayLabel": "Focus Distance", + "name": "SpatialViewDefinition_FocusDistance", + "typename": "double", + }, + "value": Object { + "displayValue": "0", + "value": 0, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "0,0,0", + "property": Object { + "displayLabel": "Eye Point", + "name": "SpatialViewDefinition_EyePoint", + "typename": "point3d", + }, + "value": Object { + "displayValue": "0,0,0", + "value": Object { + "x": 0, + "y": 0, + "z": 0, + }, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "19.325673999999999,10.797912,19.557385", + "property": Object { + "displayLabel": "Extents", + "name": "SpatialViewDefinition_Extents", + "typename": "point3d", + }, + "value": Object { + "displayValue": "19.325673999999999,10.797912,19.557385", + "value": Object { + "x": 19.325674, + "y": 10.797912, + "z": 19.557385, + }, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Illustration", + "isReadonly": true, + "property": Object { + "displayLabel": "Display Style", + "name": "SpatialViewDefinition_DisplayStyle", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Illustration", + "value": 39, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "Description", + "name": "SpatialViewDefinition_Description", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": "", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Default - View 1", + "property": Object { + "displayLabel": "Code", + "name": "SpatialViewDefinition_CodeValue", + "typename": "string", + }, + "value": Object { + "displayValue": "Default - View 1", + "value": "Default - View 1", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Default - View 1", + "isReadonly": true, + "property": Object { + "displayLabel": "Category Selector", + "name": "SpatialViewDefinition_CategorySelector", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Default - View 1", + "value": 40, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.SubCategory.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.SubCategory.snap new file mode 100644 index 0000000..27b1c43 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.SubCategory.snap @@ -0,0 +1,83 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:SubCategory' 1`] = ` +Array [ + PropertyRecord { + "property": Object { + "displayLabel": "User Label", + "name": "SubCategory_UserLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "{\\"color\\":16777215}", + "property": Object { + "displayLabel": "Properties", + "name": "SubCategory_Properties", + "typename": "string", + }, + "value": Object { + "displayValue": "{\\"color\\":16777215}", + "value": "{\\"color\\":16777215}", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Definition Model For DgnV8Bridge:D:\\\\Temp\\\\Properties_60InstancesWithUrl2.dgn, Default", + "isReadonly": true, + "property": Object { + "displayLabel": "Model", + "name": "SubCategory_Model", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Definition Model For DgnV8Bridge:D:\\\\Temp\\\\Properties_60InstancesWithUrl2.dgn, Default", + "value": 22, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "False", + "property": Object { + "displayLabel": "Is Private", + "name": "SubCategory_IsPrivate", + "typename": "boolean", + }, + "value": Object { + "displayValue": "False", + "value": false, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "Description", + "name": "SubCategory_Description", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Uncategorized", + "property": Object { + "displayLabel": "Code", + "name": "SubCategory_CodeValue", + "typename": "string", + }, + "value": Object { + "displayValue": "Uncategorized", + "value": "Uncategorized", + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.Subject.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.Subject.snap new file mode 100644 index 0000000..49995e0 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/BisCore.Subject.snap @@ -0,0 +1,57 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'BisCore:Subject' 1`] = ` +Array [ + PropertyRecord { + "property": Object { + "displayLabel": "User Label", + "name": "Subject_UserLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "DgnV8Bridge", + "isReadonly": true, + "property": Object { + "displayLabel": "Model", + "name": "Subject_Model", + "typename": "navigation", + }, + "value": Object { + "displayValue": "DgnV8Bridge", + "value": 1, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "Description", + "name": "Subject_Description", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": "", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "DgnV8Bridge", + "property": Object { + "displayLabel": "Code", + "name": "Subject_CodeValue", + "typename": "string", + }, + "value": Object { + "displayValue": "DgnV8Bridge", + "value": "DgnV8Bridge", + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.areaElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.areaElementAspect.snap new file mode 100644 index 0000000..db51158 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.areaElementAspect.snap @@ -0,0 +1,227 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:areaElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "150.1235 thousand ft²", + "property": Object { + "displayLabel": "thousand ft2", + "name": "areaElementAspect_thousand__x0020__ft2", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 thousand ft²", + "value": 150.1235, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "150.1235 mm·km", + "property": Object { + "displayLabel": "mm km", + "name": "areaElementAspect_mm__x0020__km", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 mm·km", + "value": 150.1235, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "150.1235 mi²", + "property": Object { + "displayLabel": "mi2", + "name": "areaElementAspect_mi2", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 mi²", + "value": 150.1235, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "150.1235 mm·m", + "property": Object { + "displayLabel": "m m", + "name": "areaElementAspect_m__x0020__m", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 mm·m", + "value": 150.1235, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "150.1235 m·km", + "property": Object { + "displayLabel": "m km", + "name": "areaElementAspect_m__x0020__km", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 m·km", + "value": 150.1235, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "150.1235 m²", + "property": Object { + "displayLabel": "m2", + "name": "areaElementAspect_m2", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 m²", + "value": 150.1235, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "150.1235 km²", + "property": Object { + "displayLabel": "km2", + "name": "areaElementAspect_km2", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 km²", + "value": 150.1235, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "150.1235 in·mi", + "property": Object { + "displayLabel": "in mi", + "name": "areaElementAspect_in__x0020__mi", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 in·mi", + "value": 150.1235, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "150.1235 in·m", + "property": Object { + "displayLabel": "in m", + "name": "areaElementAspect_in__x0020__m", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 in·m", + "value": 150.1235, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "150.1235 in·ft", + "property": Object { + "displayLabel": "in ft", + "name": "areaElementAspect_in__x0020__ft", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 in·ft", + "value": 150.1235, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "150.1235 in²", + "property": Object { + "displayLabel": "in2", + "name": "areaElementAspect_in2", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 in²", + "value": 150.1235, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "150.1235 ha", + "property": Object { + "displayLabel": "ha", + "name": "areaElementAspect_ha", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 ha", + "value": 150.1235, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "150.1235 ft·mi", + "property": Object { + "displayLabel": "ft mi", + "name": "areaElementAspect_ft__x0020__mi", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 ft·mi", + "value": 150.1235, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "150.1235 ft·ft", + "property": Object { + "displayLabel": "ft ft", + "name": "areaElementAspect_ft__x0020__ft", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 ft·ft", + "value": 150.1235, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "150.1235 ft²", + "property": Object { + "displayLabel": "ft2", + "name": "areaElementAspect_ft2", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 ft²", + "value": 150.1235, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "150.1235 cm²", + "property": Object { + "displayLabel": "cm2", + "name": "areaElementAspect_cm2", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 cm²", + "value": 150.1235, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "150.1235 acres", + "property": Object { + "displayLabel": "Acres", + "name": "areaElementAspect_Acres", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 acres", + "value": 150.1235, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.area_per_timeElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.area_per_timeElementAspect.snap new file mode 100644 index 0000000..8926e03 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.area_per_timeElementAspect.snap @@ -0,0 +1,32 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:area__x0020__per__x0020__timeElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "150.1235 ft²/s", + "property": Object { + "displayLabel": "ft2 s", + "name": "area__x0020__per__x0020__timeElementAspect_ft2__x0020__s", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 ft²/s", + "value": 150.1235, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "150.1235 cSt", + "property": Object { + "displayLabel": "cSt", + "name": "area__x0020__per__x0020__timeElementAspect_cSt", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 cSt", + "value": 150.1235, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.area_per_time_squaredElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.area_per_time_squaredElementAspect.snap new file mode 100644 index 0000000..402fc44 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.area_per_time_squaredElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:area__x0020__per__x0020__time__x0020__squaredElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "1475.699 Btu/lb", + "property": Object { + "displayLabel": "Btu/lb [Btu per pound mass]", + "name": "area__x0020__per__x0020__time__x0020__squaredElementAspect_Btu__x002F__lb__x0020____x005B__Btu__x0020__per__x0020__pound__x0020__mass__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "1475.699 Btu/lb", + "value": 1475.699, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.area_per_time_squared_temperatureElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.area_per_time_squared_temperatureElementAspect.snap new file mode 100644 index 0000000..f8da719 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.area_per_time_squared_temperatureElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:area__x0020__per__x0020__time__x0020__squared__x0020__temperatureElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "14759.0 Btu/(lbm·Δ°R)", + "property": Object { + "displayLabel": "Btu/(lbm·Δ°R) [btu per pound mass per delta degree rankine]", + "name": "area__x0020__per__x0020__time__x0020__squared__x0020__temperatureElementAspect_Btu__x002F____x0028__lbm__x00B7____x0394____x00B0__R__x0029____x0020____x005B__btu__x0020__per__x0020__pound__x0020__mass__x0020__per__x0020__delta__x0020__degree__x0020__rankine__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "14759.0 Btu/(lbm·Δ°R)", + "value": 14759, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.cube_root_length_per_timeElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.cube_root_length_per_timeElementAspect.snap new file mode 100644 index 0000000..ff47dc1 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.cube_root_length_per_timeElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:cube__x0020__root__x0020__length__x0020__per__x0020__timeElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "14758.985000000001", + "property": Object { + "displayLabel": "ft^(1/3)/s [ft^(1/3)/s]", + "name": "cube__x0020__root__x0020__length__x0020__per__x0020__timeElementAspect_ft__x005E____x0028__1__x002F__3__x0029____x002F__s__x0020____x005B__ft__x005E____x0028__1__x002F__3__x0029____x002F__s__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "14758.985000000001", + "value": 14758.985, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.dimensionlessElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.dimensionlessElementAspect.snap new file mode 100644 index 0000000..d25734e --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.dimensionlessElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:dimensionlessElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "15845.0 $", + "property": Object { + "displayLabel": "$ [dollar]", + "name": "dimensionlessElementAspect___x0024____x0020____x005B__dollar__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "15845.0 $", + "value": 15845, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.electric_currentElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.electric_currentElementAspect.snap new file mode 100644 index 0000000..be54cb2 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.electric_currentElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:electric__x0020__currentElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "1475.65 KA", + "property": Object { + "displayLabel": "KA [kiloampere]", + "name": "electric__x0020__currentElementAspect_KA__x0020____x005B__kiloampere__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "1475.65 KA", + "value": 1475.65, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.firstTryElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.firstTryElementAspect.snap new file mode 100644 index 0000000..0d6e3eb --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.firstTryElementAspect.snap @@ -0,0 +1,71 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:firstTryElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "gmail.com", + "property": Object { + "displayLabel": "without www", + "name": "firstTryElementAspect_without__x0020__www", + "typename": "string", + }, + "value": Object { + "displayValue": "gmail.com", + "value": "gmail.com", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "www.bbc.co.uk", + "property": Object { + "displayLabel": "without http", + "name": "firstTryElementAspect_without__x0020__http", + "typename": "string", + }, + "value": Object { + "displayValue": "www.bbc.co.uk", + "value": "www.bbc.co.uk", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "mailto:justas.lauzadis@bentley.com", + "property": Object { + "displayLabel": "mailto", + "name": "firstTryElementAspect_mailto", + "typename": "string", + }, + "value": Object { + "displayValue": "mailto:justas.lauzadis@bentley.com", + "value": "mailto:justas.lauzadis@bentley.com", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "http://www.google.lt", + "property": Object { + "displayLabel": "full URL", + "name": "firstTryElementAspect_full__x0020__URL", + "typename": "string", + }, + "value": Object { + "displayValue": "http://www.google.lt", + "value": "http://www.google.lt", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "justas.lauzadis@bentley.com", + "property": Object { + "displayLabel": "email", + "name": "firstTryElementAspect_email", + "typename": "string", + }, + "value": Object { + "displayValue": "justas.lauzadis@bentley.com", + "value": "justas.lauzadis@bentley.com", + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.lengthElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.lengthElementAspect.snap new file mode 100644 index 0000000..444a75c --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.lengthElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:lengthElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "1475.96 µin", + "property": Object { + "displayLabel": "µin [microinch]", + "name": "lengthElementAspect___x00B5__in__x0020____x005B__microinch__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "1475.96 µin", + "value": 1475.96, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.length_per_plane_angleElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.length_per_plane_angleElementAspect.snap new file mode 100644 index 0000000..17b43fa --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.length_per_plane_angleElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:length__x0020__per__x0020__plane__x0020__angleElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "123654.47 in/degree", + "property": Object { + "displayLabel": "in/deg [inch per degree]", + "name": "length__x0020__per__x0020__plane__x0020__angleElementAspect_in__x002F__deg__x0020____x005B__inch__x0020__per__x0020__degree__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "123654.47 in/degree", + "value": 123654.47, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.length_per_timeElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.length_per_timeElementAspect.snap new file mode 100644 index 0000000..85e2e4e --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.length_per_timeElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:length__x0020__per__x0020__timeElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "15896.698 ft³/(ft²·min)", + "property": Object { + "displayLabel": "ft³/(ft²·min) [Foot cubed per foot squared per minute]", + "name": "length__x0020__per__x0020__timeElementAspect_ft__x00B3____x002F____x0028__ft__x00B2____x00B7__min__x0029____x0020____x005B__Foot__x0020__cubed__x0020__per__x0020__foot__x0020__squared__x0020__per__x0020__minute__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "15896.698 ft³/(ft²·min)", + "value": 15896.698, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.length_time_squared_per_massElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.length_time_squared_per_massElementAspect.snap new file mode 100644 index 0000000..1d7c590 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.length_time_squared_per_massElementAspect.snap @@ -0,0 +1,31 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:length__x0020__time__x0020__squared__x0020__per__x0020__massElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "14851", + "property": Object { + "displayLabel": "1/Bar [1/Bar]", + "name": "length__x0020__time__x0020__squared__x0020__per__x0020__massElementAspect___x0031____x002F__Bar__x0020____x005B__1__x002F__Bar__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "14851", + "value": 14851, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "Untitled", + "name": "length__x0020__time__x0020__squared__x0020__per__x0020__massElementAspect_Untitled", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": "", + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.length_to_the_six_powerElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.length_to_the_six_powerElementAspect.snap new file mode 100644 index 0000000..76efebb --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.length_to_the_six_powerElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:length__x0020__to__x0020__the__x0020__six__x0020__powerElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "14758.964 mm⁶", + "property": Object { + "displayLabel": "mm⁶ [millimeter to the sixth]", + "name": "length__x0020__to__x0020__the__x0020__six__x0020__powerElementAspect_mm__x2076____x0020____x005B__millimeter__x0020__to__x0020__the__x0020__sixth__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "14758.964 mm⁶", + "value": 14758.964, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.lenic_massElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.lenic_massElementAspect.snap new file mode 100644 index 0000000..7924092 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.lenic_massElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:lenic__x0020__massElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "147.158 lb/ft", + "property": Object { + "displayLabel": "lb/ft [pound per foot]", + "name": "lenic__x0020__massElementAspect_lb__x002F__ft__x0020____x005B__pound__x0020__per__x0020__foot__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "147.158 lb/ft", + "value": 147.158, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.luminous_fluxElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.luminous_fluxElementAspect.snap new file mode 100644 index 0000000..ba95199 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.luminous_fluxElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:luminous__x0020__fluxElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "1589.741 lm", + "property": Object { + "displayLabel": "lm [lumen]", + "name": "luminous__x0020__fluxElementAspect_lm__x0020____x005B__lumen__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "1589.741 lm", + "value": 1589.741, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.luminous_flux_per_areaElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.luminous_flux_per_areaElementAspect.snap new file mode 100644 index 0000000..c8668e9 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.luminous_flux_per_areaElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:luminous__x0020__flux__x0020__per__x0020__areaElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "1485.9 lm/ft²", + "property": Object { + "displayLabel": "lm/ft² [lumen per foot squared]", + "name": "luminous__x0020__flux__x0020__per__x0020__areaElementAspect_lm__x002F__ft__x00B2____x0020____x005B__lumen__x0020__per__x0020__foot__x0020__squared__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "1485.9 lm/ft²", + "value": 1485.9, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.massElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.massElementAspect.snap new file mode 100644 index 0000000..cb364f9 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.massElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:massElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "14785.5896 µg", + "property": Object { + "displayLabel": "µg [microgram]", + "name": "massElementAspect___x00B5__g__x0020____x005B__microgram__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "14785.5896 µg", + "value": 14785.5896, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mass_area_per_time_cubed_electric_currentElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mass_area_per_time_cubed_electric_currentElementAspect.snap new file mode 100644 index 0000000..f466d6d --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mass_area_per_time_cubed_electric_currentElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:mass__x0020__area__x0020__per__x0020__time__x0020__cubed__x0020__electric__x0020__currentElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "1478.5 KV", + "property": Object { + "displayLabel": "KV [kilovolt]", + "name": "mass__x0020__area__x0020__per__x0020__time__x0020__cubed__x0020__electric__x0020__currentElementAspect_KV__x0020____x005B__kilovolt__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "1478.5 KV", + "value": 1478.5, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mass_area_per_time_squared_plane_angleElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mass_area_per_time_squared_plane_angleElementAspect.snap new file mode 100644 index 0000000..fa7d2b6 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mass_area_per_time_squared_plane_angleElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:mass__x0020__area__x0020__per__x0020__time__x0020__squared__x0020__plane__x0020__angleElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "14758.36 N·m/deg", + "property": Object { + "displayLabel": "N·m/deg [newton meter per degree]", + "name": "mass__x0020__area__x0020__per__x0020__time__x0020__squared__x0020__plane__x0020__angleElementAspect_N__x00B7__m__x002F__deg__x0020____x005B__newton__x0020__meter__x0020__per__x0020__degree__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "14758.36 N·m/deg", + "value": 14758.36, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mass_area_per_time_squared_temperature_moleElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mass_area_per_time_squared_temperature_moleElementAspect.snap new file mode 100644 index 0000000..af94391 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mass_area_per_time_squared_temperature_moleElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:mass__x0020__area__x0020__per__x0020__time__x0020__squared__x0020__temperature__x0020__moleElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "14758.5 Btu/(lb-mol·Δ°R)", + "property": Object { + "displayLabel": "Btu/(lb-mol·Δ°R) [btu per pound mole per delta degree rankine]", + "name": "mass__x0020__area__x0020__per__x0020__time__x0020__squared__x0020__temperature__x0020__moleElementAspect_Btu__x002F____x0028__lb__x002D__mol__x00B7____x0394____x00B0__R__x0029____x0020____x005B__btu__x0020__per__x0020__pound__x0020__mole__x0020__per__x0020__delta__x0020__degree__x0020__rankine__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "14758.5 Btu/(lb-mol·Δ°R)", + "value": 14758.5, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mass_length_per_time_cubed_temperatureElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mass_length_per_time_cubed_temperatureElementAspect.snap new file mode 100644 index 0000000..5390459 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mass_length_per_time_cubed_temperatureElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:mass__x0020__length__x0020__per__x0020__time__x0020__cubed__x0020__temperatureElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "123456799", + "property": Object { + "displayLabel": "(in·lbf)/(s·in·°F) [inch pound force per second inch fahrenheit]", + "name": "mass__x0020__length__x0020__per__x0020__time__x0020__cubed__x0020__temperatureElementAspect___x0028__in__x00B7__lbf__x0029____x002F____x0028__s__x00B7__in__x00B7____x00B0__F__x0029____x0020____x005B__inch__x0020__pound__x0020__force__x0020__per__x0020__second__x0020__inch__x0020__fahrenheit__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "123456799", + "value": 123456799, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mass_per_time_squaredElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mass_per_time_squaredElementAspect.snap new file mode 100644 index 0000000..caf187b --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mass_per_time_squaredElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:mass__x0020__per__x0020__time__x0020__squaredElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "1232584.897489 lbf/in", + "property": Object { + "displayLabel": "lbf/in [pound force per inch]", + "name": "mass__x0020__per__x0020__time__x0020__squaredElementAspect_lbf__x002F__in__x0020____x005B__pound__x0020__force__x0020__per__x0020__inch__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "1232584.897489 lbf/in", + "value": 1232584.897489, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mass_per_volumeElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mass_per_volumeElementAspect.snap new file mode 100644 index 0000000..b062a4a --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mass_per_volumeElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:mass__x0020__per__x0020__volumeElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "45896318.0 µg/L", + "property": Object { + "displayLabel": "µg/L [micrograms per liter]", + "name": "mass__x0020__per__x0020__volumeElementAspect___x00B5__g__x002F__L__x0020____x005B__micrograms__x0020__per__x0020__liter__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "45896318.0 µg/L", + "value": 45896318, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mass_volume_per_time_squaredElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mass_volume_per_time_squaredElementAspect.snap new file mode 100644 index 0000000..e43b59e --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mass_volume_per_time_squaredElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:mass__x0020__volume__x0020__per__x0020__time__x0020__squaredElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "15789.458000000001", + "property": Object { + "displayLabel": "lbf·ft² [pound force foot squared]", + "name": "mass__x0020__volume__x0020__per__x0020__time__x0020__squaredElementAspect_lbf__x00B7__ft__x00B2____x0020____x005B__pound__x0020__force__x0020__foot__x0020__squared__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "15789.458000000001", + "value": 15789.458, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.molar_volumeElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.molar_volumeElementAspect.snap new file mode 100644 index 0000000..3ac733b --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.molar_volumeElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:molar__x0020__volumeElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "0.5689 ft³/lb-mol", + "property": Object { + "displayLabel": "ft³/lb-mol [foot cubed per pound mole]", + "name": "molar__x0020__volumeElementAspect_ft__x00B3____x002F__lb__x002D__mol__x0020____x005B__foot__x0020__cubed__x0020__per__x0020__pound__x0020__mole__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "0.5689 ft³/lb-mol", + "value": 0.5689, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mole_per_timeElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mole_per_timeElementAspect.snap new file mode 100644 index 0000000..b96b7e6 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mole_per_timeElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:mole__x0020__per__x0020__timeElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "123456.78 kmol/s", + "property": Object { + "displayLabel": "kmol/s [kilomole per second]", + "name": "mole__x0020__per__x0020__timeElementAspect_kmol__x002F__s__x0020____x005B__kilomole__x0020__per__x0020__second__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "123456.78 kmol/s", + "value": 123456.78, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mole_per_volumeElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mole_per_volumeElementAspect.snap new file mode 100644 index 0000000..467ee3b --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.mole_per_volumeElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:mole__x0020__per__x0020__volumeElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "0.123456", + "property": Object { + "displayLabel": "lb-mol/ft³ [pound mole per foot cubed]", + "name": "mole__x0020__per__x0020__volumeElementAspect_lb__x002D__mol__x002F__ft__x00B3____x0020____x005B__pound__x0020__mole__x0020__per__x0020__foot__x0020__cubed__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "0.123456", + "value": 0.123456, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.plane_angleElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.plane_angleElementAspect.snap new file mode 100644 index 0000000..b2b2b4e --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.plane_angleElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:plane__x0020__angleElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "0.01233 quadrants", + "property": Object { + "displayLabel": "quadrants [quadrants]", + "name": "plane__x0020__angleElementAspect_quadrants__x0020____x005B__quadrants__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "0.01233 quadrants", + "value": 0.01233, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.plane_angle_per_timeElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.plane_angle_per_timeElementAspect.snap new file mode 100644 index 0000000..78d0e26 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.plane_angle_per_timeElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:plane__x0020__angle__x0020__per__x0020__timeElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "0.02 cycle/sec", + "property": Object { + "displayLabel": "cycle/sec [cycle per second]", + "name": "plane__x0020__angle__x0020__per__x0020__timeElementAspect_cycle__x002F__sec__x0020____x005B__cycle__x0020__per__x0020__second__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "0.02 cycle/sec", + "value": 0.02, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.quartic_lengthElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.quartic_lengthElementAspect.snap new file mode 100644 index 0000000..7783ccd --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.quartic_lengthElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:quartic__x0020__lengthElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "123456789.12345 ft⁴", + "property": Object { + "displayLabel": "ft⁴ [foot to the fourth]", + "name": "quartic__x0020__lengthElementAspect_ft__x2074____x0020____x005B__foot__x0020__to__x0020__the__x0020__fourth__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "123456789.12345 ft⁴", + "value": 123456789.12345, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.reciprocal_areaElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.reciprocal_areaElementAspect.snap new file mode 100644 index 0000000..7540bea --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.reciprocal_areaElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:reciprocal__x0020__areaElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "1234567.89 p/ft²", + "property": Object { + "displayLabel": "p/ft² [person per foot squared]", + "name": "reciprocal__x0020__areaElementAspect_p__x002F__ft__x00B2____x0020____x005B__person__x0020__per__x0020__foot__x0020__squared__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "1234567.89 p/ft²", + "value": 1234567.89, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.reciprocal_lengthElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.reciprocal_lengthElementAspect.snap new file mode 100644 index 0000000..95f831d --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.reciprocal_lengthElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:reciprocal__x0020__lengthElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "12345.6789 1/mile", + "property": Object { + "displayLabel": "1/mile [1/mile]", + "name": "reciprocal__x0020__lengthElementAspect___x0031____x002F__mile__x0020____x005B__1__x002F__mile__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "12345.6789 1/mile", + "value": 12345.6789, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.reciprocal_massElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.reciprocal_massElementAspect.snap new file mode 100644 index 0000000..dc1a93f --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.reciprocal_massElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:reciprocal__x0020__massElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "123.456789", + "property": Object { + "displayLabel": "1/tn(short) [one per short ton]", + "name": "reciprocal__x0020__massElementAspect___x0031____x002F__tn__x0028__short__x0029____x0020____x005B__one__x0020__per__x0020__short__x0020__ton__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "123.456789", + "value": 123.456789, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.reciprocal_temperatureElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.reciprocal_temperatureElementAspect.snap new file mode 100644 index 0000000..544bff3 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.reciprocal_temperatureElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:reciprocal__x0020__temperatureElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "0.0 1/Δ°R", + "property": Object { + "displayLabel": "1/Δ°R [reciprocal delta degree rankine]", + "name": "reciprocal__x0020__temperatureElementAspect___x0031____x002F____x0394____x00B0__R__x0020____x005B__reciprocal__x0020__delta__x0020__degree__x0020__rankine__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "0.0 1/Δ°R", + "value": 0, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.reciprocal_timeElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.reciprocal_timeElementAspect.snap new file mode 100644 index 0000000..7b3295f --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.reciprocal_timeElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:reciprocal__x0020__timeElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "123456.789", + "property": Object { + "displayLabel": "grf/(h·ft²·inHg) [Grain mass per hour per foot squared per inch Mercury conventional]", + "name": "reciprocal__x0020__timeElementAspect_grf__x002F____x0028__h__x00B7__ft__x00B2____x00B7__inHg__x0029____x0020____x005B__Grain__x0020__mass__x0020__per__x0020__hour__x0020__per__x0020__foot__x0020__squared__x0020__per__x0020__inch__x0020__Mercury__x0020__conventional__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "123456.789", + "value": 123456.789, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.reciprocal_volumeElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.reciprocal_volumeElementAspect.snap new file mode 100644 index 0000000..f8e0606 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.reciprocal_volumeElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:reciprocal__x0020__volumeElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "1.2345999999999999", + "property": Object { + "displayLabel": "1/ million gal", + "name": "reciprocal__x0020__volumeElementAspect___x0031____x002F____x0020__million__x0020__gal", + "typename": "double", + }, + "value": Object { + "displayValue": "1.2345999999999999", + "value": 1.2346, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.square_root_length_per_timeElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.square_root_length_per_timeElementAspect.snap new file mode 100644 index 0000000..1b89800 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.square_root_length_per_timeElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:square__x0020__root__x0020__length__x0020__per__x0020__timeElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "1234.56789", + "property": Object { + "displayLabel": "ft^(1/2)/s [ft^(1/2)/s]", + "name": "square__x0020__root__x0020__length__x0020__per__x0020__timeElementAspect_ft__x005E____x0028__1__x002F__2__x0029____x002F__s__x0020____x005B__ft__x005E____x0028__1__x002F__2__x0029____x002F__s__x005D__", + "typename": "double", + }, + "value": Object { + "displayValue": "1234.56789", + "value": 1234.56789, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.square_root_length_to_the_seventh_power_per_square_root_massElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.square_root_length_to_the_seventh_power_per_square_root_massElementAspect.snap new file mode 100644 index 0000000..63a4347 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.square_root_length_to_the_seventh_power_per_square_root_massElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:square__x0020__root__x0020__length__x0020__to__x0020__the__x0020__seventh__x0020__power__x0020__per__x0020__square__x0020__root__x0020__massElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "1.2345999999999999", + "property": Object { + "displayLabel": "Gallon per minute per square root pounds per inch squared", + "name": "square__x0020__root__x0020__length__x0020__to__x0020__the__x0020__seventh__x0020__power__x0020__per__x0020__square__x0020__root__x0020__massElementAspect_Gallon__x0020__per__x0020__minute__x0020__per__x0020__square__x0020__root__x0020__pounds__x0020__per__x0020__inch__x0020__squared", + "typename": "double", + }, + "value": Object { + "displayValue": "1.2345999999999999", + "value": 1.2346, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.temperatureElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.temperatureElementAspect.snap new file mode 100644 index 0000000..992e81c --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.temperatureElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:temperatureElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "0.0 Δ°C", + "property": Object { + "displayLabel": "Delta celsius", + "name": "temperatureElementAspect_Delta__x0020__celsius", + "typename": "double", + }, + "value": Object { + "displayValue": "0.0 Δ°C", + "value": 0, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.temperature_per_lengthElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.temperature_per_lengthElementAspect.snap new file mode 100644 index 0000000..17c6bbc --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.temperature_per_lengthElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:temperature__x0020__per__x0020__lengthElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "123.456789 ΔK/m", + "property": Object { + "displayLabel": "delta degree Kelvin per meter", + "name": "temperature__x0020__per__x0020__lengthElementAspect_delta__x0020__degree__x0020__Kelvin__x0020__per__x0020__meter", + "typename": "double", + }, + "value": Object { + "displayValue": "123.456789 ΔK/m", + "value": 123.456789, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.timeElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.timeElementAspect.snap new file mode 100644 index 0000000..51b9506 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.timeElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:timeElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "1234.56789 days", + "property": Object { + "displayLabel": "days", + "name": "timeElementAspect_days", + "typename": "double", + }, + "value": Object { + "displayValue": "1234.56789 days", + "value": 1234.56789, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.time_cubed_per_mass_areaElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.time_cubed_per_mass_areaElementAspect.snap new file mode 100644 index 0000000..3cfb5d0 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.time_cubed_per_mass_areaElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:time__x0020__cubed__x0020__per__x0020__mass__x0020__areaElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "1234.56789", + "property": Object { + "displayLabel": "one per kilowat", + "name": "time__x0020__cubed__x0020__per__x0020__mass__x0020__areaElementAspect_one__x0020__per__x0020__kilowat", + "typename": "double", + }, + "value": Object { + "displayValue": "1234.56789", + "value": 1234.56789, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.time_cubed_temperatureElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.time_cubed_temperatureElementAspect.snap new file mode 100644 index 0000000..769d6f3 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.time_cubed_temperatureElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:time__x0020__cubed__x0020__temperatureElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "1.2346 ft²·h·Δ°F/Btu", + "property": Object { + "displayLabel": "foot squared hour delta degree Fahrenheit per Btu", + "name": "time__x0020__cubed__x0020__temperatureElementAspect_foot__x0020__squared__x0020__hour__x0020__delta__x0020__degree__x0020__Fahrenheit__x0020__per__x0020__Btu", + "typename": "double", + }, + "value": Object { + "displayValue": "1.2346 ft²·h·Δ°F/Btu", + "value": 1.2346, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.time_squared_per_lengthElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.time_squared_per_lengthElementAspect.snap new file mode 100644 index 0000000..a69da5d --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.time_squared_per_lengthElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:time__x0020__squared__x0020__per__x0020__lengthElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "1.2345999999999999", + "property": Object { + "displayLabel": "kg/kW h", + "name": "time__x0020__squared__x0020__per__x0020__lengthElementAspect_kg__x002F__kW__x0020__h", + "typename": "double", + }, + "value": Object { + "displayValue": "1.2345999999999999", + "value": 1.2346, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.time_squared_per_massElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.time_squared_per_massElementAspect.snap new file mode 100644 index 0000000..d80a9d8 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.time_squared_per_massElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:time__x0020__squared__x0020__per__x0020__massElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "1.2345999999999999", + "property": Object { + "displayLabel": "1/Btu", + "name": "time__x0020__squared__x0020__per__x0020__massElementAspect___x0031____x002F__Btu", + "typename": "double", + }, + "value": Object { + "displayValue": "1.2345999999999999", + "value": 1.2346, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.volumeElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.volumeElementAspect.snap new file mode 100644 index 0000000..1a003af --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.volumeElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:volumeElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "1.2346 thousand gal", + "property": Object { + "displayLabel": "thousand gal", + "name": "volumeElementAspect_thousand__x0020__gal", + "typename": "double", + }, + "value": Object { + "displayValue": "1.2346 thousand gal", + "value": 1.2346, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.volume_per_TimeElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.volume_per_TimeElementAspect.snap new file mode 100644 index 0000000..247ee57 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.volume_per_TimeElementAspect.snap @@ -0,0 +1,214 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:volume__x0020__per__x0020__TimeElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "123456.789", + "property": Object { + "displayLabel": "million gal(impl)/day", + "name": "volume__x0020__per__x0020__TimeElementAspect_million__x0020__gal__x0028__impl__x0029____x002F__day", + "typename": "double", + }, + "value": Object { + "displayValue": "123456.789", + "value": 123456.789, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "12345.6789 m³/min", + "property": Object { + "displayLabel": "m3/min", + "name": "volume__x0020__per__x0020__TimeElementAspect_m3__x002F__min", + "typename": "double", + }, + "value": Object { + "displayValue": "12345.6789 m³/min", + "value": 12345.6789, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "123.456789", + "property": Object { + "displayLabel": "gpd/capita", + "name": "volume__x0020__per__x0020__TimeElementAspect_gpd__x002F__capita", + "typename": "double", + }, + "value": Object { + "displayValue": "123.456789", + "value": 123.456789, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "123456789.0 gal/s", + "property": Object { + "displayLabel": "gal/s", + "name": "volume__x0020__per__x0020__TimeElementAspect_gal__x002F__s", + "typename": "double", + }, + "value": Object { + "displayValue": "123456789.0 gal/s", + "value": 123456789, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "123456789.0 gal (imp)/s", + "property": Object { + "displayLabel": "gal(imp)/s", + "name": "volume__x0020__per__x0020__TimeElementAspect_gal__x0028__imp__x0029____x002F__s", + "typename": "double", + }, + "value": Object { + "displayValue": "123456789.0 gal (imp)/s", + "value": 123456789, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "123456789.0 gal (imp)/day", + "property": Object { + "displayLabel": "gal(imp)/day", + "name": "volume__x0020__per__x0020__TimeElementAspect_gal__x0028__imp__x0029____x002F__day", + "typename": "double", + }, + "value": Object { + "displayValue": "123456789.0 gal (imp)/day", + "value": 123456789, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "123456789.0 ft³/s", + "property": Object { + "displayLabel": "ft3/s", + "name": "volume__x0020__per__x0020__TimeElementAspect_ft3__x002F__s", + "typename": "double", + }, + "value": Object { + "displayValue": "123456789.0 ft³/s", + "value": 123456789, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "123456789.0 ft³/min", + "property": Object { + "displayLabel": "ft3/min", + "name": "volume__x0020__per__x0020__TimeElementAspect_ft3__x002F__min", + "typename": "double", + }, + "value": Object { + "displayValue": "123456789.0 ft³/min", + "value": 123456789, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "123456789.0 ft³/day", + "property": Object { + "displayLabel": "ft3/day", + "name": "volume__x0020__per__x0020__TimeElementAspect_ft3__x002F__day", + "typename": "double", + }, + "value": Object { + "displayValue": "123456789.0 ft³/day", + "value": 123456789, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "123456789.0 acre·in/min", + "property": Object { + "displayLabel": "acre in/min", + "name": "volume__x0020__per__x0020__TimeElementAspect_acre__x0020__in__x002F__min", + "typename": "double", + }, + "value": Object { + "displayValue": "123456789.0 acre·in/min", + "value": 123456789, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "123456789.0 acre·in/h", + "property": Object { + "displayLabel": "acre in/h", + "name": "volume__x0020__per__x0020__TimeElementAspect_acre__x0020__in__x002F__h", + "typename": "double", + }, + "value": Object { + "displayValue": "123456789.0 acre·in/h", + "value": 123456789, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "123456789.0 acre·ft/min", + "property": Object { + "displayLabel": "acre ft/min", + "name": "volume__x0020__per__x0020__TimeElementAspect_acre__x0020__ft__x002F__min", + "typename": "double", + }, + "value": Object { + "displayValue": "123456789.0 acre·ft/min", + "value": 123456789, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "123456789.0 acre·ft/h", + "property": Object { + "displayLabel": "acre ft/h", + "name": "volume__x0020__per__x0020__TimeElementAspect_acre__x0020__ft__x002F__h", + "typename": "double", + }, + "value": Object { + "displayValue": "123456789.0 acre·ft/h", + "value": 123456789, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "123456789.0 acre·ft/day", + "property": Object { + "displayLabel": "acre ft/day", + "name": "volume__x0020__per__x0020__TimeElementAspect_acre__x0020__ft__x002F__day", + "typename": "double", + }, + "value": Object { + "displayValue": "123456789.0 acre·ft/day", + "value": 123456789, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "1234567.8899999999", + "property": Object { + "displayLabel": "ML/day", + "name": "volume__x0020__per__x0020__TimeElementAspect_ML__x002F__day", + "typename": "double", + }, + "value": Object { + "displayValue": "1234567.8899999999", + "value": 1234567.89, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "123456.789 L/min", + "property": Object { + "displayLabel": "L/min", + "name": "volume__x0020__per__x0020__TimeElementAspect_L__x002F__min", + "typename": "double", + }, + "value": Object { + "displayValue": "123456.789 L/min", + "value": 123456.789, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.volume_per_massElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.volume_per_massElementAspect.snap new file mode 100644 index 0000000..d2398c7 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.volume_per_massElementAspect.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:volume__x0020__per__x0020__massElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "1.234568 ft³/lm", + "property": Object { + "displayLabel": "ft3/lm", + "name": "volume__x0020__per__x0020__massElementAspect_ft3__x002F__lm", + "typename": "double", + }, + "value": Object { + "displayValue": "1.234568 ft³/lm", + "value": 1.23456789, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.workingUnitsPropElementAspect.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.workingUnitsPropElementAspect.snap new file mode 100644 index 0000000..225306a --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/DgnCustomItemTypes_MyProp.workingUnitsPropElementAspect.snap @@ -0,0 +1,58 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'DgnCustomItemTypes_MyProp:workingUnitsPropElementAspect' 1`] = ` +Array [ + PropertyRecord { + "description": "150.123456", + "property": Object { + "displayLabel": "Volume", + "name": "workingUnitsPropElementAspect_Volume", + "typename": "double", + }, + "value": Object { + "displayValue": "150.123456", + "value": 150.123456, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "150.123456", + "property": Object { + "displayLabel": "Distance", + "name": "workingUnitsPropElementAspect_Distance", + "typename": "double", + }, + "value": Object { + "displayValue": "150.123456", + "value": 150.123456, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "150.123456", + "property": Object { + "displayLabel": "Area", + "name": "workingUnitsPropElementAspect_Area", + "typename": "double", + }, + "value": Object { + "displayValue": "150.123456", + "value": 150.123456, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "2.62014859167284", + "property": Object { + "displayLabel": "Angle", + "name": "workingUnitsPropElementAspect_Angle", + "typename": "double", + }, + "value": Object { + "displayValue": "2.62014859167284", + "value": 2.62014859167284, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbFileInfo.EmbeddedFileInfo.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbFileInfo.EmbeddedFileInfo.snap new file mode 100644 index 0000000..ae7c276 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbFileInfo.EmbeddedFileInfo.snap @@ -0,0 +1,69 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'ECDbFileInfo:EmbeddedFileInfo' 1`] = ` +Array [ + PropertyRecord { + "description": "PresentationRuleSet", + "property": Object { + "displayLabel": "Type", + "name": "EmbeddedFileInfo_Type", + "typename": "string", + }, + "value": Object { + "displayValue": "PresentationRuleSet", + "value": "PresentationRuleSet", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "13287", + "property": Object { + "displayLabel": "Size", + "name": "EmbeddedFileInfo_Size", + "typename": "long", + }, + "value": Object { + "displayValue": "13287", + "value": 13287, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Properties_60InstancesWithUrl2_supplemental_DgnCustomItemTypes_MyProp specific.01.00", + "property": Object { + "displayLabel": "Name", + "name": "EmbeddedFileInfo_Name", + "typename": "string", + }, + "value": Object { + "displayValue": "Properties_60InstancesWithUrl2_supplemental_DgnCustomItemTypes_MyProp specific.01.00", + "value": "Properties_60InstancesWithUrl2_supplemental_DgnCustomItemTypes_MyProp specific.01.00", + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "LastModified", + "name": "EmbeddedFileInfo_LastModified", + "typename": "dateTime", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "Description", + "name": "EmbeddedFileInfo_Description", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbMeta.ECClassDef.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbMeta.ECClassDef.snap new file mode 100644 index 0000000..c9e688a --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbMeta.ECClassDef.snap @@ -0,0 +1,257 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'ECDbMeta:ECClassDef' 1`] = ` +Array [ + PropertyRecord { + "description": "CustomAttribute", + "property": Object { + "displayLabel": "Type", + "enum": Object { + "choices": Array [ + Object { + "label": "Entity", + "value": 0, + }, + Object { + "label": "Relationship", + "value": 1, + }, + Object { + "label": "Struct", + "value": 2, + }, + Object { + "label": "CustomAttribute", + "value": 3, + }, + ], + "isStrict": true, + }, + "name": "ECClassDef_Type", + "typename": "enum", + }, + "value": Object { + "displayValue": "CustomAttribute", + "value": 3, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "ECSchema-0-1", + "isReadonly": true, + "property": Object { + "displayLabel": "Schema", + "name": "ECClassDef_Schema", + "typename": "navigation", + }, + "value": Object { + "displayValue": "ECSchema-0-1", + "value": 1, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "RelationshipStrengthDirection", + "enum": Object { + "choices": Array [ + Object { + "label": "Forward", + "value": 1, + }, + Object { + "label": "Backward", + "value": 2, + }, + ], + "isStrict": true, + }, + "name": "ECClassDef_RelationshipStrengthDirection", + "typename": "enum", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "RelationshipStrength", + "enum": Object { + "choices": Array [ + Object { + "label": "Referencing", + "value": 0, + }, + Object { + "label": "Holding", + "value": 1, + }, + Object { + "label": "Embedding", + "value": 2, + }, + ], + "isStrict": true, + }, + "name": "ECClassDef_RelationshipStrength", + "typename": "enum", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "ClassHasCurrentTimeStampProperty", + "property": Object { + "displayLabel": "Name", + "name": "ECClassDef_Name", + "typename": "string", + }, + "value": Object { + "displayValue": "ClassHasCurrentTimeStampProperty", + "value": "ClassHasCurrentTimeStampProperty", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Sealed", + "property": Object { + "displayLabel": "Modifier", + "enum": Object { + "choices": Array [ + Object { + "label": "None", + "value": 0, + }, + Object { + "label": "Abstract", + "value": 1, + }, + Object { + "label": "Sealed", + "value": 2, + }, + ], + "isStrict": true, + }, + "name": "ECClassDef_Modifier", + "typename": "enum", + }, + "value": Object { + "displayValue": "Sealed", + "value": 2, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "DisplayLabel", + "name": "ECClassDef_DisplayLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "Description", + "name": "ECClassDef_Description", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "EntityClass", + "property": Object { + "displayLabel": "CustomAttributeContainerType", + "enum": Object { + "choices": Array [ + Object { + "label": "Schema", + "value": 1, + }, + Object { + "label": "EntityClass", + "value": 2, + }, + Object { + "label": "CustomAttributeClass", + "value": 4, + }, + Object { + "label": "StructClass", + "value": 8, + }, + Object { + "label": "RelationshipClass", + "value": 16, + }, + Object { + "label": "AnyClass", + "value": 30, + }, + Object { + "label": "PrimitiveProperty", + "value": 32, + }, + Object { + "label": "StructProperty", + "value": 64, + }, + Object { + "label": "PrimitiveArrayProperty", + "value": 128, + }, + Object { + "label": "StructArrayProperty", + "value": 256, + }, + Object { + "label": "NavigationProperty", + "value": 512, + }, + Object { + "label": "AnyProperty", + "value": 992, + }, + Object { + "label": "SourceRelationshipConstraint", + "value": 1024, + }, + Object { + "label": "TargetRelationshipConstraint", + "value": 2048, + }, + Object { + "label": "AnyRelationshipConstraint", + "value": 3072, + }, + Object { + "label": "Any", + "value": 4095, + }, + ], + "isStrict": true, + }, + "name": "ECClassDef_CustomAttributeContainerType", + "typename": "enum", + }, + "value": Object { + "displayValue": "EntityClass", + "value": 2, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbMeta.ECEnumerationDef.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbMeta.ECEnumerationDef.snap new file mode 100644 index 0000000..c131254 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbMeta.ECEnumerationDef.snap @@ -0,0 +1,250 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'ECDbMeta:ECEnumerationDef' 1`] = ` +Array [ + PropertyRecord { + "description": "String", + "property": Object { + "displayLabel": "Type", + "enum": Object { + "choices": Array [ + Object { + "label": "Binary", + "value": 257, + }, + Object { + "label": "Boolean", + "value": 513, + }, + Object { + "label": "DateTime", + "value": 769, + }, + Object { + "label": "Double", + "value": 1025, + }, + Object { + "label": "Integer", + "value": 1281, + }, + Object { + "label": "Long", + "value": 1537, + }, + Object { + "label": "Point2D", + "value": 1793, + }, + Object { + "label": "Point3D", + "value": 2049, + }, + Object { + "label": "String", + "value": 2305, + }, + Object { + "label": "IGeometry", + "value": 2561, + }, + ], + "isStrict": true, + }, + "name": "ECEnumerationDef_Type", + "typename": "enum", + }, + "value": Object { + "displayValue": "String", + "value": 2305, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "ECSchema-0-1", + "isReadonly": true, + "property": Object { + "displayLabel": "Schema", + "name": "ECEnumerationDef_Schema", + "typename": "navigation", + }, + "value": Object { + "displayValue": "ECSchema-0-1", + "value": 1, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "DateTimeComponent", + "property": Object { + "displayLabel": "Name", + "name": "ECEnumerationDef_Name", + "typename": "string", + }, + "value": Object { + "displayValue": "DateTimeComponent", + "value": "DateTimeComponent", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "True", + "property": Object { + "displayLabel": "IsStrict", + "name": "ECEnumerationDef_IsStrict", + "typename": "boolean", + }, + "value": Object { + "displayValue": "True", + "value": true, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": undefined, + "property": Object { + "displayLabel": "EnumValues", + "name": "ECEnumerationDef_EnumValues", + "typename": "ECEnumeratorDef[]", + }, + "value": Object { + "items": Array [ + PropertyRecord { + "description": undefined, + "isReadonly": true, + "property": Object { + "displayLabel": "EnumValues", + "name": "ECEnumerationDef_EnumValues", + "typename": "ECEnumeratorDef", + }, + "value": Object { + "members": Object { + "DisplayLabel": PropertyRecord { + "isReadonly": true, + "property": Object { + "displayLabel": "DisplayLabel", + "name": "DisplayLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + "IntValue": PropertyRecord { + "isReadonly": true, + "property": Object { + "displayLabel": "IntValue", + "name": "IntValue", + "typename": "int", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + "StringValue": PropertyRecord { + "description": "DateTime", + "isReadonly": true, + "property": Object { + "displayLabel": "StringValue", + "name": "StringValue", + "typename": "string", + }, + "value": Object { + "displayValue": "DateTime", + "value": "DateTime", + "valueFormat": 0, + }, + }, + }, + "valueFormat": 2, + }, + }, + PropertyRecord { + "description": undefined, + "isReadonly": true, + "property": Object { + "displayLabel": "EnumValues", + "name": "ECEnumerationDef_EnumValues", + "typename": "ECEnumeratorDef", + }, + "value": Object { + "members": Object { + "DisplayLabel": PropertyRecord { + "isReadonly": true, + "property": Object { + "displayLabel": "DisplayLabel", + "name": "DisplayLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + "IntValue": PropertyRecord { + "isReadonly": true, + "property": Object { + "displayLabel": "IntValue", + "name": "IntValue", + "typename": "int", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + "StringValue": PropertyRecord { + "description": "Date", + "isReadonly": true, + "property": Object { + "displayLabel": "StringValue", + "name": "StringValue", + "typename": "string", + }, + "value": Object { + "displayValue": "Date", + "value": "Date", + "valueFormat": 0, + }, + }, + }, + "valueFormat": 2, + }, + }, + ], + "itemsTypeName": "ECEnumeratorDef", + "valueFormat": 1, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "DisplayLabel", + "name": "ECEnumerationDef_DisplayLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "Description", + "name": "ECEnumerationDef_Description", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbMeta.ECPropertyDef.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbMeta.ECPropertyDef.snap new file mode 100644 index 0000000..c6b75a7 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbMeta.ECPropertyDef.snap @@ -0,0 +1,370 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'ECDbMeta:ECPropertyDef' 1`] = ` +Array [ + PropertyRecord { + "description": "Ñót spêçìfíêd", + "isReadonly": true, + "property": Object { + "displayLabel": "StructClass", + "name": "ECPropertyDef_StructClass", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Ñót spêçìfíêd", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "Priority", + "name": "ECPropertyDef_Priority", + "typename": "int", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "PrimitiveTypeMinValue", + "name": "ECPropertyDef_PrimitiveTypeMinValue", + "typename": "double", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "PrimitiveTypeMinLength", + "name": "ECPropertyDef_PrimitiveTypeMinLength", + "typename": "int", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "PrimitiveTypeMaxValue", + "name": "ECPropertyDef_PrimitiveTypeMaxValue", + "typename": "double", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "PrimitiveTypeMaxLength", + "name": "ECPropertyDef_PrimitiveTypeMaxLength", + "typename": "int", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "String", + "property": Object { + "displayLabel": "PrimitiveType", + "enum": Object { + "choices": Array [ + Object { + "label": "Binary", + "value": 257, + }, + Object { + "label": "Boolean", + "value": 513, + }, + Object { + "label": "DateTime", + "value": 769, + }, + Object { + "label": "Double", + "value": 1025, + }, + Object { + "label": "Integer", + "value": 1281, + }, + Object { + "label": "Long", + "value": 1537, + }, + Object { + "label": "Point2D", + "value": 1793, + }, + Object { + "label": "Point3D", + "value": 2049, + }, + Object { + "label": "String", + "value": 2305, + }, + Object { + "label": "IGeometry", + "value": 2561, + }, + ], + "isStrict": true, + }, + "name": "ECPropertyDef_PrimitiveType", + "typename": "enum", + }, + "value": Object { + "displayValue": "String", + "value": 2305, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "0", + "property": Object { + "displayLabel": "Ordinal", + "name": "ECPropertyDef_Ordinal", + "typename": "int", + }, + "value": Object { + "displayValue": "0", + "value": 0, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Ñót spêçìfíêd", + "isReadonly": true, + "property": Object { + "displayLabel": "NavigationRelationshipClass", + "name": "ECPropertyDef_NavigationRelationshipClass", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Ñót spêçìfíêd", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "NavigationDirection", + "enum": Object { + "choices": Array [ + Object { + "label": "Forward", + "value": 1, + }, + Object { + "label": "Backward", + "value": 2, + }, + ], + "isStrict": true, + }, + "name": "ECPropertyDef_NavigationDirection", + "typename": "enum", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "PropertyName", + "property": Object { + "displayLabel": "Name", + "name": "ECPropertyDef_Name", + "typename": "string", + }, + "value": Object { + "displayValue": "PropertyName", + "value": "PropertyName", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Ñót spêçìfíêd", + "isReadonly": true, + "property": Object { + "displayLabel": "KindOfQuantity", + "name": "ECPropertyDef_KindOfQuantity", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Ñót spêçìfíêd", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Primitive", + "property": Object { + "displayLabel": "Kind", + "enum": Object { + "choices": Array [ + Object { + "label": "Primitive", + "value": 0, + }, + Object { + "label": "Struct", + "value": 1, + }, + Object { + "label": "PrimitiveArray", + "value": 2, + }, + Object { + "label": "StructArray", + "value": 3, + }, + Object { + "label": "Navigation", + "value": 4, + }, + ], + "isStrict": true, + }, + "name": "ECPropertyDef_Kind", + "typename": "enum", + }, + "value": Object { + "displayValue": "Primitive", + "value": 0, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "False", + "property": Object { + "displayLabel": "IsReadonly", + "name": "ECPropertyDef_IsReadonly", + "typename": "boolean", + }, + "value": Object { + "displayValue": "False", + "value": false, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "ExtendedTypeName", + "name": "ECPropertyDef_ExtendedTypeName", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Ñót spêçìfíêd", + "isReadonly": true, + "property": Object { + "displayLabel": "Enumeration", + "name": "ECPropertyDef_Enumeration", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Ñót spêçìfíêd", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "DisplayLabel", + "name": "ECPropertyDef_DisplayLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "Description", + "name": "ECPropertyDef_Description", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "ECClass-0-1", + "isReadonly": true, + "property": Object { + "displayLabel": "Class", + "name": "ECPropertyDef_Class", + "typename": "navigation", + }, + "value": Object { + "displayValue": "ECClass-0-1", + "value": 1, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Ñót spêçìfíêd", + "isReadonly": true, + "property": Object { + "displayLabel": "Category", + "name": "ECPropertyDef_Category", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Ñót spêçìfíêd", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "ArrayMinOccurs", + "name": "ECPropertyDef_ArrayMinOccurs", + "typename": "int", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "ArrayMaxOccurs", + "name": "ECPropertyDef_ArrayMaxOccurs", + "typename": "int", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbMeta.ECRelationshipConstraintDef.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbMeta.ECRelationshipConstraintDef.snap new file mode 100644 index 0000000..31782c6 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbMeta.ECRelationshipConstraintDef.snap @@ -0,0 +1,111 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'ECDbMeta:ECRelationshipConstraintDef' 1`] = ` +Array [ + PropertyRecord { + "description": "is a", + "property": Object { + "displayLabel": "RoleLabel", + "name": "ECRelationshipConstraintDef_RoleLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "is a", + "value": "is a", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Source", + "property": Object { + "displayLabel": "RelationshipEnd", + "enum": Object { + "choices": Array [ + Object { + "label": "Source", + "value": 0, + }, + Object { + "label": "Target", + "value": 1, + }, + ], + "isStrict": true, + }, + "name": "ECRelationshipConstraintDef_RelationshipEnd", + "typename": "enum", + }, + "value": Object { + "displayValue": "Source", + "value": 0, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "ECClass-0-S", + "isReadonly": true, + "property": Object { + "displayLabel": "RelationshipClass", + "name": "ECRelationshipConstraintDef_RelationshipClass", + "typename": "navigation", + }, + "value": Object { + "displayValue": "ECClass-0-S", + "value": 28, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "MultiplicityUpperLimit", + "name": "ECRelationshipConstraintDef_MultiplicityUpperLimit", + "typename": "int", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "0", + "property": Object { + "displayLabel": "MultiplicityLowerLimit", + "name": "ECRelationshipConstraintDef_MultiplicityLowerLimit", + "typename": "int", + }, + "value": Object { + "displayValue": "0", + "value": 0, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "False", + "property": Object { + "displayLabel": "IsPolymorphic", + "name": "ECRelationshipConstraintDef_IsPolymorphic", + "typename": "boolean", + }, + "value": Object { + "displayValue": "False", + "value": false, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Ñót spêçìfíêd", + "isReadonly": true, + "property": Object { + "displayLabel": "AbstractConstraintClass", + "name": "ECRelationshipConstraintDef_AbstractConstraintClass", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Ñót spêçìfíêd", + "value": undefined, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbMeta.ECSchemaDef.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbMeta.ECSchemaDef.snap new file mode 100644 index 0000000..ed2af63 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbMeta.ECSchemaDef.snap @@ -0,0 +1,97 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'ECDbMeta:ECSchemaDef' 1`] = ` +Array [ + PropertyRecord { + "description": "0", + "property": Object { + "displayLabel": "VersionWrite", + "name": "ECSchemaDef_VersionWrite", + "typename": "int", + }, + "value": Object { + "displayValue": "0", + "value": 0, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "0", + "property": Object { + "displayLabel": "VersionMinor", + "name": "ECSchemaDef_VersionMinor", + "typename": "int", + }, + "value": Object { + "displayValue": "0", + "value": 0, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "1", + "property": Object { + "displayLabel": "VersionMajor", + "name": "ECSchemaDef_VersionMajor", + "typename": "int", + }, + "value": Object { + "displayValue": "1", + "value": 1, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "CoreCustomAttributes", + "property": Object { + "displayLabel": "Name", + "name": "ECSchemaDef_Name", + "typename": "string", + }, + "value": Object { + "displayValue": "CoreCustomAttributes", + "value": "CoreCustomAttributes", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Core Custom Attributes", + "property": Object { + "displayLabel": "DisplayLabel", + "name": "ECSchemaDef_DisplayLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "Core Custom Attributes", + "value": "Core Custom Attributes", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Custom attributes to indicate core EC concepts, may include struct classes intended for use in core custom attributes.", + "property": Object { + "displayLabel": "Description", + "name": "ECSchemaDef_Description", + "typename": "string", + }, + "value": Object { + "displayValue": "Custom attributes to indicate core EC concepts, may include struct classes intended for use in core custom attributes.", + "value": "Custom attributes to indicate core EC concepts, may include struct classes intended for use in core custom attributes.", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "CoreCA", + "property": Object { + "displayLabel": "Alias", + "name": "ECSchemaDef_Alias", + "typename": "string", + }, + "value": Object { + "displayValue": "CoreCA", + "value": "CoreCA", + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbMeta.KindOfQuantityDef.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbMeta.KindOfQuantityDef.snap new file mode 100644 index 0000000..e7fb0bc --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbMeta.KindOfQuantityDef.snap @@ -0,0 +1,95 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'ECDbMeta:KindOfQuantityDef' 1`] = ` +Array [ + PropertyRecord { + "description": "ECSchema-0-H", + "isReadonly": true, + "property": Object { + "displayLabel": "Schema", + "name": "KindOfQuantityDef_Schema", + "typename": "navigation", + }, + "value": Object { + "displayValue": "ECSchema-0-H", + "value": 17, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "0.0001", + "property": Object { + "displayLabel": "RelativeError", + "name": "KindOfQuantityDef_RelativeError", + "typename": "double", + }, + "value": Object { + "displayValue": "0.0001", + "value": 0.0001, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "PresentationUnits", + "name": "KindOfQuantityDef_PresentationUnits", + "typename": "string[]", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "ARC_QUADRANT(DefaultRealU)", + "property": Object { + "displayLabel": "PersistenceUnit", + "name": "KindOfQuantityDef_PersistenceUnit", + "typename": "string", + }, + "value": Object { + "displayValue": "ARC_QUADRANT(DefaultRealU)", + "value": "ARC_QUADRANT(DefaultRealU)", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "ANGLE", + "property": Object { + "displayLabel": "Name", + "name": "KindOfQuantityDef_Name", + "typename": "string", + }, + "value": Object { + "displayValue": "ANGLE", + "value": "ANGLE", + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "DisplayLabel", + "name": "KindOfQuantityDef_DisplayLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "Description", + "name": "KindOfQuantityDef_Description", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbMeta.PropertyCategoryDef.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbMeta.PropertyCategoryDef.snap new file mode 100644 index 0000000..7160f87 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/ECDbMeta.PropertyCategoryDef.snap @@ -0,0 +1,71 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'ECDbMeta:PropertyCategoryDef' 1`] = ` +Array [ + PropertyRecord { + "description": "ECSchema-0-H", + "isReadonly": true, + "property": Object { + "displayLabel": "Schema", + "name": "PropertyCategoryDef_Schema", + "typename": "navigation", + }, + "value": Object { + "displayValue": "ECSchema-0-H", + "value": 17, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "100000", + "property": Object { + "displayLabel": "Priority", + "name": "PropertyCategoryDef_Priority", + "typename": "int", + }, + "value": Object { + "displayValue": "100000", + "value": 100000, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "DgnCustomItemTypes_MyProp__x003A__area", + "property": Object { + "displayLabel": "Name", + "name": "PropertyCategoryDef_Name", + "typename": "string", + }, + "value": Object { + "displayValue": "DgnCustomItemTypes_MyProp__x003A__area", + "value": "DgnCustomItemTypes_MyProp__x003A__area", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "area", + "property": Object { + "displayLabel": "DisplayLabel", + "name": "PropertyCategoryDef_DisplayLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "area", + "value": "area", + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "Description", + "name": "PropertyCategoryDef_Description", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/Generic.GroupModel.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/Generic.GroupModel.snap new file mode 100644 index 0000000..a52fc86 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/Generic.GroupModel.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'Generic:GroupModel' 1`] = ` +Array [ + PropertyRecord { + "description": "Converted Groups", + "isReadonly": true, + "property": Object { + "displayLabel": "Modeled Element", + "name": "GroupModel_ModeledElement", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Converted Groups", + "value": 19, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/Generic.PhysicalObject.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/Generic.PhysicalObject.snap new file mode 100644 index 0000000..d6b847e --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/Generic.PhysicalObject.snap @@ -0,0 +1,435 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'Generic:PhysicalObject' 1`] = ` +Array [ + PropertyRecord { + "isReadonly": true, + "property": Object { + "displayLabel": "workingUnitsProp", + "name": "PhysicalObject_workingUnitsPropElementAspect", + "typename": "workingUnitsProp", + }, + "value": Object { + "members": Object { + "workingUnitsPropElementAspect_Angle": PropertyRecord { + "description": "2.6201485916728364", + "property": Object { + "displayLabel": "Angle", + "name": "workingUnitsPropElementAspect_Angle", + "typename": "double", + }, + "value": Object { + "displayValue": "2.6201485916728364", + "value": 2.6201485916728364, + "valueFormat": 0, + }, + }, + "workingUnitsPropElementAspect_Area": PropertyRecord { + "description": "150.123456", + "property": Object { + "displayLabel": "Area", + "name": "workingUnitsPropElementAspect_Area", + "typename": "double", + }, + "value": Object { + "displayValue": "150.123456", + "value": 150.123456, + "valueFormat": 0, + }, + }, + "workingUnitsPropElementAspect_Distance": PropertyRecord { + "description": "150.123456", + "property": Object { + "displayLabel": "Distance", + "name": "workingUnitsPropElementAspect_Distance", + "typename": "double", + }, + "value": Object { + "displayValue": "150.123456", + "value": 150.123456, + "valueFormat": 0, + }, + }, + "workingUnitsPropElementAspect_Volume": PropertyRecord { + "description": "150.123456", + "property": Object { + "displayLabel": "Volume", + "name": "workingUnitsPropElementAspect_Volume", + "typename": "double", + }, + "value": Object { + "displayValue": "150.123456", + "value": 150.123456, + "valueFormat": 0, + }, + }, + }, + "valueFormat": 2, + }, + }, + PropertyRecord { + "isReadonly": true, + "property": Object { + "displayLabel": "firstTry", + "name": "PhysicalObject_firstTryElementAspect", + "typename": "firstTry", + }, + "value": Object { + "members": Object { + "firstTryElementAspect_email": PropertyRecord { + "description": "justas.lauzadis@bentley.com", + "property": Object { + "displayLabel": "email", + "name": "firstTryElementAspect_email", + "typename": "string", + }, + "value": Object { + "displayValue": "justas.lauzadis@bentley.com", + "value": "justas.lauzadis@bentley.com", + "valueFormat": 0, + }, + }, + "firstTryElementAspect_full__x0020__URL": PropertyRecord { + "description": "http://www.google.lt", + "property": Object { + "displayLabel": "full URL", + "name": "firstTryElementAspect_full__x0020__URL", + "typename": "string", + }, + "value": Object { + "displayValue": "http://www.google.lt", + "value": "http://www.google.lt", + "valueFormat": 0, + }, + }, + "firstTryElementAspect_mailto": PropertyRecord { + "description": "mailto:justas.lauzadis@bentley.com", + "property": Object { + "displayLabel": "mailto", + "name": "firstTryElementAspect_mailto", + "typename": "string", + }, + "value": Object { + "displayValue": "mailto:justas.lauzadis@bentley.com", + "value": "mailto:justas.lauzadis@bentley.com", + "valueFormat": 0, + }, + }, + "firstTryElementAspect_without__x0020__http": PropertyRecord { + "description": "www.bbc.co.uk", + "property": Object { + "displayLabel": "without http", + "name": "firstTryElementAspect_without__x0020__http", + "typename": "string", + }, + "value": Object { + "displayValue": "www.bbc.co.uk", + "value": "www.bbc.co.uk", + "valueFormat": 0, + }, + }, + "firstTryElementAspect_without__x0020__www": PropertyRecord { + "description": "gmail.com", + "property": Object { + "displayLabel": "without www", + "name": "firstTryElementAspect_without__x0020__www", + "typename": "string", + }, + "value": Object { + "displayValue": "gmail.com", + "value": "gmail.com", + "valueFormat": 0, + }, + }, + }, + "valueFormat": 2, + }, + }, + PropertyRecord { + "isReadonly": true, + "property": Object { + "displayLabel": "area", + "name": "PhysicalObject_areaElementAspect", + "typename": "area", + }, + "value": Object { + "members": Object { + "areaElementAspect_Acres": PropertyRecord { + "description": "150.1235 acres", + "property": Object { + "displayLabel": "Acres", + "name": "areaElementAspect_Acres", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 acres", + "value": 150.1235, + "valueFormat": 0, + }, + }, + "areaElementAspect_cm2": PropertyRecord { + "description": "150.1235 cm²", + "property": Object { + "displayLabel": "cm2", + "name": "areaElementAspect_cm2", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 cm²", + "value": 150.1235, + "valueFormat": 0, + }, + }, + "areaElementAspect_ft2": PropertyRecord { + "description": "150.1235 ft²", + "property": Object { + "displayLabel": "ft2", + "name": "areaElementAspect_ft2", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 ft²", + "value": 150.1235, + "valueFormat": 0, + }, + }, + "areaElementAspect_ft__x0020__ft": PropertyRecord { + "description": "150.1235 ft·ft", + "property": Object { + "displayLabel": "ft ft", + "name": "areaElementAspect_ft__x0020__ft", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 ft·ft", + "value": 150.1235, + "valueFormat": 0, + }, + }, + "areaElementAspect_ft__x0020__mi": PropertyRecord { + "description": "150.1235 ft·mi", + "property": Object { + "displayLabel": "ft mi", + "name": "areaElementAspect_ft__x0020__mi", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 ft·mi", + "value": 150.1235, + "valueFormat": 0, + }, + }, + "areaElementAspect_ha": PropertyRecord { + "description": "150.1235 ha", + "property": Object { + "displayLabel": "ha", + "name": "areaElementAspect_ha", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 ha", + "value": 150.1235, + "valueFormat": 0, + }, + }, + "areaElementAspect_in2": PropertyRecord { + "description": "150.1235 in²", + "property": Object { + "displayLabel": "in2", + "name": "areaElementAspect_in2", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 in²", + "value": 150.1235, + "valueFormat": 0, + }, + }, + "areaElementAspect_in__x0020__ft": PropertyRecord { + "description": "150.1235 in·ft", + "property": Object { + "displayLabel": "in ft", + "name": "areaElementAspect_in__x0020__ft", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 in·ft", + "value": 150.1235, + "valueFormat": 0, + }, + }, + "areaElementAspect_in__x0020__m": PropertyRecord { + "description": "150.1235 in·m", + "property": Object { + "displayLabel": "in m", + "name": "areaElementAspect_in__x0020__m", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 in·m", + "value": 150.1235, + "valueFormat": 0, + }, + }, + "areaElementAspect_in__x0020__mi": PropertyRecord { + "description": "150.1235 in·mi", + "property": Object { + "displayLabel": "in mi", + "name": "areaElementAspect_in__x0020__mi", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 in·mi", + "value": 150.1235, + "valueFormat": 0, + }, + }, + "areaElementAspect_km2": PropertyRecord { + "description": "150.1235 km²", + "property": Object { + "displayLabel": "km2", + "name": "areaElementAspect_km2", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 km²", + "value": 150.1235, + "valueFormat": 0, + }, + }, + "areaElementAspect_m2": PropertyRecord { + "description": "150.1235 m²", + "property": Object { + "displayLabel": "m2", + "name": "areaElementAspect_m2", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 m²", + "value": 150.1235, + "valueFormat": 0, + }, + }, + "areaElementAspect_m__x0020__km": PropertyRecord { + "description": "150.1235 m·km", + "property": Object { + "displayLabel": "m km", + "name": "areaElementAspect_m__x0020__km", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 m·km", + "value": 150.1235, + "valueFormat": 0, + }, + }, + "areaElementAspect_m__x0020__m": PropertyRecord { + "description": "150.1235 mm·m", + "property": Object { + "displayLabel": "m m", + "name": "areaElementAspect_m__x0020__m", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 mm·m", + "value": 150.1235, + "valueFormat": 0, + }, + }, + "areaElementAspect_mi2": PropertyRecord { + "description": "150.1235 mi²", + "property": Object { + "displayLabel": "mi2", + "name": "areaElementAspect_mi2", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 mi²", + "value": 150.1235, + "valueFormat": 0, + }, + }, + "areaElementAspect_mm__x0020__km": PropertyRecord { + "description": "150.1235 mm·km", + "property": Object { + "displayLabel": "mm km", + "name": "areaElementAspect_mm__x0020__km", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 mm·km", + "value": 150.1235, + "valueFormat": 0, + }, + }, + "areaElementAspect_thousand__x0020__ft2": PropertyRecord { + "description": "150.1235 thousand ft²", + "property": Object { + "displayLabel": "thousand ft2", + "name": "areaElementAspect_thousand__x0020__ft2", + "typename": "double", + }, + "value": Object { + "displayValue": "150.1235 thousand ft²", + "value": 150.1235, + "valueFormat": 0, + }, + }, + }, + "valueFormat": 2, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "User Label", + "name": "PhysicalObject_UserLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Properties_60InstancesWithUrl2", + "isReadonly": true, + "property": Object { + "displayLabel": "Model", + "name": "PhysicalObject_Model", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Properties_60InstancesWithUrl2", + "value": 28, + "valueFormat": 0, + }, + }, + PropertyRecord { + "property": Object { + "displayLabel": "Code", + "name": "PhysicalObject_CodeValue", + "typename": "string", + }, + "value": Object { + "displayValue": "", + "value": undefined, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Uncategorized", + "isReadonly": true, + "property": Object { + "displayLabel": "Category", + "name": "PhysicalObject_Category", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Uncategorized", + "value": 23, + "valueFormat": 0, + }, + }, +] +`; diff --git a/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/PCJ_TestSchema.TestClass.snap b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/PCJ_TestSchema.TestClass.snap new file mode 100644 index 0000000..19afb27 --- /dev/null +++ b/test-apps/presentation-integration-tests/src/frontend/ruleset-testing-content-snaps/PCJ_TestSchema.TestClass.snap @@ -0,0 +1,279 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RulesetTesting generates correct content for 'Items' ruleset. ClassName: 'PCJ_TestSchema:TestClass' 1`] = ` +Array [ + PropertyRecord { + "description": "TestClass", + "property": Object { + "displayLabel": "User Label", + "name": "TestClass_UserLabel", + "typename": "string", + }, + "value": Object { + "displayValue": "TestClass", + "value": "TestClass", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Obi-Wan Kenobi", + "property": Object { + "displayLabel": "Star Wars", + "name": "TestClass_String_Property_4", + "typename": "string", + }, + "value": Object { + "displayValue": "Obi-Wan Kenobi", + "value": "Obi-Wan Kenobi", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Star Wars - A New Hope", + "property": Object { + "displayLabel": "Movies", + "name": "TestClass_String_Property_3", + "typename": "string", + }, + "value": Object { + "displayValue": "Star Wars - A New Hope", + "value": "Star Wars - A New Hope", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Lionel Messi", + "property": Object { + "displayLabel": "Sports Stars", + "name": "TestClass_String_Property_2", + "typename": "string", + }, + "value": Object { + "displayValue": "Lionel Messi", + "value": "Lionel Messi", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Argentina", + "property": Object { + "displayLabel": "Country", + "name": "TestClass_String_Property_1", + "typename": "string", + }, + "value": Object { + "displayValue": "Argentina", + "value": "Argentina", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "#$%^", + "property": Object { + "displayLabel": "Special Characters", + "name": "TestClass_StringA", + "typename": "string", + }, + "value": Object { + "displayValue": "#$%^", + "value": "#$%^", + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Red", + "property": Object { + "displayLabel": "Color", + "enum": Object { + "choices": Array [ + Object { + "label": "Red", + "value": 0, + }, + Object { + "label": "Orange", + "value": 1, + }, + Object { + "label": "Yellow", + "value": 2, + }, + Object { + "label": "Blue", + "value": 3, + }, + Object { + "label": "Purple", + "value": 4, + }, + ], + "isStrict": true, + }, + "name": "TestClass_PicklistColor", + "typename": "enum", + }, + "value": Object { + "displayValue": "Red", + "value": 0, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "Properties_60InstancesWithUrl2", + "isReadonly": true, + "property": Object { + "displayLabel": "Model", + "name": "TestClass_Model", + "typename": "navigation", + }, + "value": Object { + "displayValue": "Properties_60InstancesWithUrl2", + "value": 28, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "-1122", + "property": Object { + "displayLabel": "Negative Numbers", + "name": "TestClass_Integer_4", + "typename": "int", + }, + "value": Object { + "displayValue": "-1122", + "value": -1122, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "123456789", + "property": Object { + "displayLabel": "0-Infinity", + "name": "TestClass_Integer_3", + "typename": "int", + }, + "value": Object { + "displayValue": "123456789", + "value": 123456789, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "999", + "property": Object { + "displayLabel": "0-1000", + "name": "TestClass_Integer_2", + "typename": "int", + }, + "value": Object { + "displayValue": "999", + "value": 999, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "10", + "property": Object { + "displayLabel": "0-100", + "name": "TestClass_Integer_1", + "typename": "int", + }, + "value": Object { + "displayValue": "10", + "value": 10, + "valueFormat": 0, + }, + }, + PropertyRecord { + "description": "1000000.001", + "property": Object { + "displayLabel": " { token = new IModelToken(faker.random.uuid(), faker.random.uuid(), faker.random.uuid()); }); - it("creates valid request for getRootNodesCount", () => { + it("creates valid request for getNodesCount", () => { const request = () => { const params = [{ imodel: token, knownBackendIds: [], rulesetId: faker.random.word() }]; - const r = new WebAppRpcRequest(client, "getRootNodesCount", params); + const r = new WebAppRpcRequest(client, "getNodesCount", params); (r as any).dispose(); // no way to properly destroy the created request... }; expect(request).to.not.throw(); diff --git a/test-apps/presentation-test-app/package.json b/test-apps/presentation-test-app/package.json index ed82ea5..fcc5409 100644 --- a/test-apps/presentation-test-app/package.json +++ b/test-apps/presentation-test-app/package.json @@ -8,12 +8,11 @@ }, "version": "0.0.0", "engines": { - "node": "8.9.1" + "node": ">=10.14.0 <11.0" }, "private": true, "scripts": { - "build": "tsc 1>&2 && npm run copy:css && npm run copy:public && npm run webpack:modules && npm run extract", - "copy:css": "cpx \"./src/**/*.css\" ./lib", + "build": "npm run extract && node ./node_modules/@bentley/webpack-tools/bin/buildIModelJsModule", "clean": "rimraf lib", "docs": "", "extract": "node ./node_modules/@bentley/build-tools/scripts/extract.js --fileExt=ts,tsx --extractFrom=./src --recursive --out=../../generated-docs/extract", @@ -22,80 +21,78 @@ "start:webserver": "node ./node_modules/@bentley/imodeljs-webserver/lib/webserver.js --port=3000 --resources=./lib/webresources/", "start:backend": "node lib/backend/main.js", "start:servers": "run-p start:webserver start:backend", - "copy:public": "cpx \"public/**/*\" ./lib/webresources", - "copy:modules": "node ./node_modules/@bentley/webpack-tools/modules/copyExternalModules.js --packageFile=./package.json --destDir=./lib/webresources --type=dev", - "copy:modulesprod": "node ./node_modules/@bentley/webpack-tools/modules/copyExternalModules.js --packageFile=./package.json --destDir=./lib/webresources --type=prod", - "makeconfig": "node ./node_modules/@bentley/config-loader/scripts/write.js ./lib/webresources/config.json ^imjs_", - "webpack:main": "webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.outdir=./lib/webresources --env.entry=./lib/frontend/index.js --env.bundlename=main --env.stylesheets", - "webpack:modules": "npm run copy:modules && npm run webpack:main && npm run makeconfig", "test": "", "cover": "" }, + "iModelJs": { + "buildModule": { + "type": "application", + "sourceResources": [ + { + "source": "./src/**/*.css", + "dest": "./lib" + }, + { + "source": "./public/**/*", + "dest": "./lib/webresources" + } + ], + "webpack": { + "dest": "./lib/webresources", + "entry": "./lib/frontend/index.js", + "bundleName": "main", + "styleSheets": true, + "htmlTemplate": "./src/frontend/index.html" + }, + "makeConfig": { + "dest": "./lib/webresources/config.json", + "filter": "^imjs_" + } + } + }, "dependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/bwc": "^7.1.0", - "@bentley/electron-manager": "0.187.0", - "@bentley/geometry-core": "0.187.0", - "@bentley/icons-generic-webfont": "^0.0.6", - "@bentley/imodeljs-i18n": "0.187.0", - "@bentley/imodeljs-clients": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-backend": "0.187.0", - "@bentley/imodeljs-frontend": "0.187.0", - "@bentley/imodeljs-quantity": "0.187.0", - "@bentley/presentation-common": "0.187.0", - "@bentley/presentation-backend": "0.187.0", - "@bentley/presentation-frontend": "0.187.0", - "@bentley/presentation-components": "0.187.0", - "@bentley/ui-core": "0.187.0", - "@bentley/ui-components": "0.187.0", - "@bentley/ui-framework": "0.187.0", - "@bentley/ui-ninezone": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/electron-manager": "0.189.0", + "@bentley/icons-generic-webfont": "^0.0.9", + "@bentley/imodeljs-clients": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-backend": "0.189.0", + "@bentley/imodeljs-frontend": "0.189.0", + "@bentley/imodeljs-i18n": "0.189.0", + "@bentley/imodeljs-quantity": "0.189.0", + "@bentley/presentation-common": "0.189.0", + "@bentley/presentation-backend": "0.189.0", + "@bentley/presentation-frontend": "0.189.0", + "@bentley/presentation-components": "0.189.0", + "@bentley/ui-core": "0.189.0", + "@bentley/ui-components": "0.189.0", "body-parser": "^1.18.2", "express": "^4.16.3", - "inspire-tree": "^5.0.1", - "lodash": "^4.17.10", - "mobx": "^5.8.0", - "mobx-react": "^5.4.3", "react": "^16.4.2", "react-dom": "^16.4.2", - "react-dnd": "^5.0.0", - "react-dnd-html5-backend": "^5.0.1", - "react-redux": "^5.0.7", - "redux": "^4.0.0", "semver": "^5.5.0", "immutable": "^3.8.2" }, "devDependencies": { - "@bentley/config-loader": "0.187.0", - "@bentley/build-tools": "0.187.0", - "@bentley/bwc": "^7.1.0", - "@bentley/imodeljs-webserver": "0.187.0", - "@bentley/webpack-tools": "0.187.0", + "@bentley/config-loader": "0.189.0", + "@bentley/build-tools": "0.189.0", + "@bentley/imodeljs-webserver": "0.189.0", + "@bentley/webpack-tools": "0.189.0", "@types/body-parser": "^1.17.0", "@types/bunyan": "^1.8.4", - "@types/express": "^4.11.1", - "@types/react": "^16.4.14", - "@types/react-dom": "16.0.7", + "@types/express": "^4.16.1", + "@types/react": "16.7.22", + "@types/react-dom": "16.0.11", "autoprefixer": "^8.6.5", - "css-loader": "^0.28.11", - "cpx": "^1.5.0", "electron": "^4.0.1", "electron-chromedriver": "^2.0.0", - "file-loader": "^1.1.11", "npm-run-all": "^4.1.5", "postcss-flexbugs-fixes": "^3.3.1", "postcss-loader": "^2.1.6", "rimraf": "^2.6.2", - "sass-loader": "^7.1.0", - "source-map-loader": "^0.2.3", - "style-loader": "^0.21.0", - "svg-sprite-loader": "^3.8.0", "tslint": "^5.11.0", - "typescript": "~3.1.0", - "uglifyjs-webpack-plugin": "^1.2.5", - "url-loader": "^1.0.1", - "webpack": "^4.20.2" + "typescript": "~3.2.2" }, "proxy": "http://localhost:5000" } diff --git a/test-apps/presentation-test-app/public/locales/en/Sample.json b/test-apps/presentation-test-app/public/locales/en/Sample.json index 31ba9b0..50d8a82 100644 --- a/test-apps/presentation-test-app/public/locales/en/Sample.json +++ b/test-apps/presentation-test-app/public/locales/en/Sample.json @@ -1,16 +1,30 @@ -{ - "welcome-message": "Welcome to iModel.js Presentation Test App", - "controls": { - "grid": "Grid Widget", - "properties": "Properties Widget", - "tree": "Tree Widget", - "notifications": { - "error": "Error", - "nothing-selected": "Nothing selected", - "no-data": "No data", - "select-imodel": "Select an iModel", - "no-available-rulesets": "No available rulesets", - "select-ruleset": "Select a ruleset" - } - } -} \ No newline at end of file +{ + "welcome-message": "Welcome to iModel.js Presentation Test App", + "controls": { + "grid": "Grid Widget", + "properties": { + "widget-label": "Properties Widget", + "context-menu": { + "find-similar": { + "label": "Find Similar", + "description": "Find elements with similar properties" + } + } + }, + "tree": "Tree Widget", + "find-similar": { + "results": "Similar Instances", + "dismiss-button": { + "label": "Dismiss" + } + }, + "notifications": { + "error": "Error", + "nothing-selected": "Nothing selected", + "no-data": "No data", + "select-imodel": "Select an iModel", + "no-available-rulesets": "No available rulesets", + "select-ruleset": "Select a ruleset" + } + } +} diff --git a/test-apps/presentation-test-app/src/backend/web/BackendServer.ts b/test-apps/presentation-test-app/src/backend/web/BackendServer.ts index 996a68e..bd6afb5 100644 --- a/test-apps/presentation-test-app/src/backend/web/BackendServer.ts +++ b/test-apps/presentation-test-app/src/backend/web/BackendServer.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------------------------------------------- -* Copyright (c) 2018 Bentley Systems, Incorporated. All rights reserved. +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ // tslint:disable:no-console diff --git a/test-apps/presentation-test-app/src/frontend/components/app/App.css b/test-apps/presentation-test-app/src/frontend/components/app/App.css index 346edec..0cd1c70 100644 --- a/test-apps/presentation-test-app/src/frontend/components/app/App.css +++ b/test-apps/presentation-test-app/src/frontend/components/app/App.css @@ -44,3 +44,12 @@ .app-content-left-top { position: relative; /* Because viewport component is absolute */ } + +.app-content-left-bottom { + display: flex; + flex-direction: row; +} + +.app-content-left-bottom > * { + flex: 1 1; +} diff --git a/test-apps/presentation-test-app/src/frontend/components/app/App.tsx b/test-apps/presentation-test-app/src/frontend/components/app/App.tsx index 4550fca..d2d0396 100644 --- a/test-apps/presentation-test-app/src/frontend/components/app/App.tsx +++ b/test-apps/presentation-test-app/src/frontend/components/app/App.tsx @@ -2,20 +2,24 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ - import * as React from "react"; import { Id64String } from "@bentley/bentleyjs-core"; import { ViewQueryParams } from "@bentley/imodeljs-common"; -import { IModelApp, IModelConnection } from "@bentley/imodeljs-frontend"; +import { IModelApp, IModelConnection, PropertyRecord } from "@bentley/imodeljs-frontend"; +import { DefaultContentDisplayTypes } from "@bentley/presentation-common"; import { Presentation } from "@bentley/presentation-frontend"; +import { ElementSeparator, Orientation } from "@bentley/ui-core"; +import { IPresentationTableDataProvider, IPresentationPropertyDataProvider, DataProvidersFactory } from "@bentley/presentation-components"; import IModelSelector from "../imodel-selector/IModelSelector"; import PropertiesWidget from "../properties-widget/PropertiesWidget"; import GridWidget from "../grid-widget/GridWidget"; +import FindSimilarWidget from "../find-similar-widget/FindSimilarWidget"; import TreeWidget from "../tree-widget/TreeWidget"; import RulesetSelector from "../ruleset-selector/RulesetSelector"; import ViewportContentControl from "../viewport/ViewportContentControl"; + import "./App.css"; -import { ElementSeparator, Orientation } from "@bentley/ui-core"; +import "@bentley/icons-generic-webfont/dist/bentley-icons-generic-webfont.css"; export interface State { imodel?: IModelConnection; @@ -25,6 +29,7 @@ export interface State { rightPaneHeight?: number; contentRatio: number; contentWidth?: number; + similarInstancesProvider?: IPresentationTableDataProvider; } export default class App extends React.Component<{}, State> { @@ -76,6 +81,23 @@ export default class App extends React.Component<{}, State> { this.setState({ contentRatio: ratio }); } + private _onFindSimilar = async (provider: IPresentationPropertyDataProvider, record: PropertyRecord) => { + try { + const factory = new DataProvidersFactory(); + const similarInstancesProvider = await factory.createSimilarInstancesTableDataProvider(provider, + record, { displayType: DefaultContentDisplayTypes.LIST }); + this.setState({ similarInstancesProvider }); + } catch (e) { + console.log(e); // tslint:disable-line:no-console + alert(`Can't find similar instances for the selected property`); + this.setState({ similarInstancesProvider: undefined }); + } + } + + private _onSimilarInstancesResultsDismissed = () => { + this.setState({ similarInstancesProvider: undefined }); + } + private renderIModelComponents(imodel: IModelConnection, rulesetId: string, viewDefinitionId: Id64String) { return (
{
-
- +
+ { + + } +
+ { + this.state.similarInstancesProvider ? + + : undefined + }
{ movableArea={this.state.rightPaneHeight} onRatioChanged={this._onTreePaneRatioChanged} />
- +
); diff --git a/test-apps/presentation-test-app/src/frontend/components/find-similar-widget/FindSimilarWidget.css b/test-apps/presentation-test-app/src/frontend/components/find-similar-widget/FindSimilarWidget.css new file mode 100644 index 0000000..81c8334 --- /dev/null +++ b/test-apps/presentation-test-app/src/frontend/components/find-similar-widget/FindSimilarWidget.css @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +.find-similar-widget { + height: 100%; +} + +.find-similar-header { + display: flex; + flex-direction: row; +} + +.find-similar-header h3 { + margin: 0; + height: 25px; +} + +.find-similar-header button { + margin-left: auto; +} + +.find-similar-widget-content { + height: calc(100% - 25px); +} diff --git a/test-apps/presentation-test-app/src/frontend/components/find-similar-widget/FindSimilarWidget.tsx b/test-apps/presentation-test-app/src/frontend/components/find-similar-widget/FindSimilarWidget.tsx new file mode 100644 index 0000000..51c906b --- /dev/null +++ b/test-apps/presentation-test-app/src/frontend/components/find-similar-widget/FindSimilarWidget.tsx @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import * as React from "react"; +import { IModelApp } from "@bentley/imodeljs-frontend"; +import { tableWithUnifiedSelection, IPresentationTableDataProvider } from "@bentley/presentation-components"; +import { Table } from "@bentley/ui-components"; +import "./FindSimilarWidget.css"; + +// tslint:disable-next-line:variable-name naming-convention +const SampleTable = tableWithUnifiedSelection(Table); + +export interface Props { + dataProvider: IPresentationTableDataProvider; + onDismissed?: () => void; +} + +export default class FindSimilarWidget extends React.PureComponent { + constructor(props: Props, context?: any) { + super(props, context); + this.state = { prevProps: props }; + } + public componentWillUnmount() { + this.props.dataProvider.dispose(); + } + public componentDidUpdate(prevProps: Props) { + if (this.props.dataProvider !== prevProps.dataProvider) + prevProps.dataProvider.dispose(); + } + private _onDismissClicked = () => { + if (this.props.onDismissed) + this.props.onDismissed(); + } + public render() { + return ( +
+
+

{IModelApp.i18n.translate("Sample:controls.find-similar.results")}

+ +
+
+ +
+
+ ); + } +} diff --git a/test-apps/presentation-test-app/src/frontend/components/grid-widget/GridWidget.tsx b/test-apps/presentation-test-app/src/frontend/components/grid-widget/GridWidget.tsx index d9430cb..7e6c796 100644 --- a/test-apps/presentation-test-app/src/frontend/components/grid-widget/GridWidget.tsx +++ b/test-apps/presentation-test-app/src/frontend/components/grid-widget/GridWidget.tsx @@ -5,7 +5,7 @@ import * as React from "react"; import { IModelApp, IModelConnection } from "@bentley/imodeljs-frontend"; -import { PresentationTableDataProvider, tableWithUnifiedSelection } from "@bentley/presentation-components"; +import { PresentationTableDataProvider, tableWithUnifiedSelection, IPresentationTableDataProvider } from "@bentley/presentation-components"; import { Table } from "@bentley/ui-components"; import "./GridWidget.css"; @@ -17,19 +17,38 @@ export interface Props { rulesetId: string; } -export default class GridWidget extends React.Component { +export interface State { + dataProvider: IPresentationTableDataProvider; +} + +export default class GridWidget extends React.PureComponent { constructor(props: Props, context?: any) { super(props, context); - this.state = {}; + this.state = { dataProvider: createDataProviderFromProps(props) }; + } + public static getDerivedStateFromProps(props: Props, state: State): State | null { + const needsDataProviderRecreated = (props.imodel !== state.dataProvider.imodel || props.rulesetId !== state.dataProvider.rulesetId); + if (needsDataProviderRecreated) + state.dataProvider = createDataProviderFromProps(props); + return state; + } + public componentWillUnmount() { + this.state.dataProvider.dispose(); + } + public componentDidUpdate(_prevProps: Props, prevState: State) { + if (this.state.dataProvider !== prevState.dataProvider) + prevState.dataProvider.dispose(); } public render() { return (

{IModelApp.i18n.translate("Sample:controls.grid")}

- +
); } } + +const createDataProviderFromProps = (props: Props) => new PresentationTableDataProvider({ imodel: props.imodel, ruleset: props.rulesetId }); diff --git a/test-apps/presentation-test-app/src/frontend/components/properties-widget/PropertiesWidget.tsx b/test-apps/presentation-test-app/src/frontend/components/properties-widget/PropertiesWidget.tsx index b3299b4..af113b5 100644 --- a/test-apps/presentation-test-app/src/frontend/components/properties-widget/PropertiesWidget.tsx +++ b/test-apps/presentation-test-app/src/frontend/components/properties-widget/PropertiesWidget.tsx @@ -4,9 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as React from "react"; -import { IModelApp, IModelConnection } from "@bentley/imodeljs-frontend"; -import { PresentationPropertyDataProvider, propertyGridWithUnifiedSelection } from "@bentley/presentation-components"; -import { PropertyGrid, PropertyData, PropertyCategory } from "@bentley/ui-components"; +import { IModelApp, IModelConnection, PropertyRecord } from "@bentley/imodeljs-frontend"; +import { + PresentationPropertyDataProvider, propertyGridWithUnifiedSelection, + IPresentationPropertyDataProvider, +} from "@bentley/presentation-components"; +import { GlobalContextMenu, ContextMenuItem } from "@bentley/ui-core"; +import { PropertyGrid, PropertyData, PropertyCategory, PropertyGridContextMenuArgs } from "@bentley/ui-components"; import "./PropertiesWidget.css"; // tslint:disable-next-line:variable-name naming-convention @@ -15,9 +19,11 @@ const SamplePropertyGrid = propertyGridWithUnifiedSelection(PropertyGrid); export interface Props { imodel: IModelConnection; rulesetId: string; + onFindSimilar?: (propertiesProvider: IPresentationPropertyDataProvider, record: PropertyRecord) => void; } export interface State { dataProvider: PresentationPropertyDataProvider; + contextMenu?: PropertyGridContextMenuArgs; } export default class PropertiesWidget extends React.Component { constructor(props: Props, context?: any) { @@ -31,15 +37,66 @@ export default class PropertiesWidget extends React.Component { return { ...state, dataProvider: createDataProvider(props.imodel, props.rulesetId) }; return null; } + private _onFindSimilar = (property: PropertyRecord) => { + if (this.props.onFindSimilar) + this.props.onFindSimilar(this.state.dataProvider, property); + this.setState({ contextMenu: undefined }); + } + private _onPropertyContextMenu = (args: PropertyGridContextMenuArgs) => { + args.event.persist(); + this.setState({ contextMenu: args }); + } + private _onContextMenuOutsideClick = () => { + this.setState({ contextMenu: undefined }); + } + private _onContextMenuEsc = () => { + this.setState({ contextMenu: undefined }); + } + private buildContextMenu(args: PropertyGridContextMenuArgs) { + const items = new Array(); + if (this.props.onFindSimilar) { + items.push( + this._onFindSimilar(args.propertyRecord)} + title={IModelApp.i18n.translate("Sample:controls.properties.context-menu.find-similar.description")} + > + {IModelApp.i18n.translate("Sample:controls.properties.context-menu.find-similar.label")} + , + ); + } + if (items.length === 0) + return undefined; + + return ( + + {items} + + ); + } public render() { + let contextMenu: React.ReactNode; + if (this.state.contextMenu) + contextMenu = this.buildContextMenu(this.state.contextMenu); + return (
-

{IModelApp.i18n.translate("Sample:controls.properties")}

+

{IModelApp.i18n.translate("Sample:controls.properties.widget-label")}

+ {contextMenu}
); } diff --git a/test-apps/presentation-test-app/src/frontend/components/tree-widget/TreeWidget.tsx b/test-apps/presentation-test-app/src/frontend/components/tree-widget/TreeWidget.tsx index e6c4805..8da13b8 100644 --- a/test-apps/presentation-test-app/src/frontend/components/tree-widget/TreeWidget.tsx +++ b/test-apps/presentation-test-app/src/frontend/components/tree-widget/TreeWidget.tsx @@ -13,10 +13,13 @@ import { PageOptions } from "@bentley/presentation-common"; // tslint:disable-next-line:variable-name naming-convention const SampleTree = treeWithFilteringSupport(treeWithUnifiedSelection(Tree)); +const pagingSize = 5; + class SampleDataProvider implements IPresentationTreeDataProvider { private _wrapped: PresentationTreeDataProvider; public constructor(imodel: IModelConnection, rulesetId: string) { this._wrapped = new PresentationTreeDataProvider(imodel, rulesetId); + this._wrapped.pagingSize = pagingSize; } public get imodel() { return this._wrapped.imodel; } public get rulesetId() { return this._wrapped.rulesetId; } @@ -28,8 +31,11 @@ class SampleDataProvider implements IPresentationTreeDataProvider { } public async getNodes(parentNode?: TreeNodeItem, page?: PageOptions) { const result = await this._wrapped.getNodes(parentNode, page); - result.forEach((n) => { - n.labelItalic = true; + result.forEach((node) => { + if (!node.style) + node.style = {}; + + node.style.isItalic = true; }); return result; } @@ -130,7 +136,8 @@ export default class TreeWidget extends React.Component { }} /> iModel.js Presentation Test App - + diff --git a/test-apps/presentation-test-app/src/frontend/index.tsx b/test-apps/presentation-test-app/src/frontend/index.tsx index 489a126..3f81f63 100644 --- a/test-apps/presentation-test-app/src/frontend/index.tsx +++ b/test-apps/presentation-test-app/src/frontend/index.tsx @@ -5,7 +5,7 @@ import * as React from "react"; import * as ReactDOM from "react-dom"; -import { Logger } from "@bentley/bentleyjs-core"; +import { Logger, LogLevel } from "@bentley/bentleyjs-core"; import { IModelApp } from "@bentley/imodeljs-frontend"; import { Config } from "@bentley/imodeljs-clients"; import { @@ -25,6 +25,7 @@ import "./index.css"; // initialize logging Logger.initializeToConsole(); +Logger.setLevelDefault(LogLevel.Warning); // initialize RPC (function initRpc() { diff --git a/test-apps/synchro-schedule-importer/package.json b/test-apps/synchro-schedule-importer/package.json index b75a930..17475bd 100644 --- a/test-apps/synchro-schedule-importer/package.json +++ b/test-apps/synchro-schedule-importer/package.json @@ -4,7 +4,7 @@ "private": true, "license": "MIT", "scripts": { - "build": "npm run build-code && npm run extract-assets", + "build": "npm run build-code && npm run extract-assets && node ./node_modules/@bentley/build-tools/scripts/ignoreargs.js 1>&2", "lint": "tslint -p . 1>&2", "build-code": "tsc 1>&2", "clean": "rimraf lib package-deps.json ../../generated-docs", @@ -15,23 +15,23 @@ }, "repository": {}, "dependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/geometry-core": "0.187.0", - "@bentley/imodeljs-clients": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-backend": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/imodeljs-clients": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-backend": "0.189.0", "fs-extra": "^6.0.1", "yargs": "^12.0.0" }, "devDependencies": { - "@bentley/build-tools": "0.187.0", + "@bentley/build-tools": "0.189.0", "@types/fs-extra": "^4.0.7", "@types/lodash": "^4.14.0", - "@types/node": "10.10.3", + "@types/node": "10.12.18", "@types/yargs": "^12.0.5", "rimraf": "^2.6.2", "tslint": "^5.11.0", - "typescript": "~3.1.0", + "typescript": "~3.2.2", "cpx": "^1.5.0" } } diff --git a/test-apps/synchro-schedule-importer/src/SynchroScheduleImporter.ts b/test-apps/synchro-schedule-importer/src/SynchroScheduleImporter.ts index 03c652a..0f69cf5 100644 --- a/test-apps/synchro-schedule-importer/src/SynchroScheduleImporter.ts +++ b/test-apps/synchro-schedule-importer/src/SynchroScheduleImporter.ts @@ -2,10 +2,10 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { IModelHost, IModelHostConfiguration, IModelDb, ECSqlStatement, IModelJsFs, ViewDefinition, DisplayStyle3d, OrthographicViewDefinition } from "@bentley/imodeljs-backend"; +import { IModelHost, IModelHostConfiguration, IModelDb, ECSqlStatement, IModelJsFs, ViewDefinition, DisplayStyle3d, OrthographicViewDefinition, GeometricElement3d } from "@bentley/imodeljs-backend"; import { OpenMode, DbResult, Id64String } from "@bentley/bentleyjs-core"; -import { Placement3d, ElementAlignedBox3d, AxisAlignedBox3d, RenderMode, ViewFlags, ColorDef } from "@bentley/imodeljs-common"; -import { YawPitchRollAngles, Point3d } from "@bentley/geometry-core"; +import { Placement3d, RenderMode, ViewFlags, ColorDef, ElementAlignedBox3d } from "@bentley/imodeljs-common"; +import { YawPitchRollAngles, Point3d, Vector3d, Range3d } from "@bentley/geometry-core"; import * as Yargs from "yargs"; import { readFileSync, writeFileSync, unlinkSync } from "fs"; @@ -19,13 +19,13 @@ interface ImportInputArgs { } function doFixRange(iModel: IModelDb) { - const totalRange = new AxisAlignedBox3d(); + const totalRange = Range3d.createNull() as ElementAlignedBox3d; iModel.withPreparedStatement("SELECT ECInstanceId,Category.Id,Origin,Yaw,Pitch,Roll,BBoxLow,BBoxHigh FROM bis.GeometricElement3d", (stmt: ECSqlStatement) => { while (DbResult.BE_SQLITE_ROW === stmt.step()) { const row = stmt.getRow(); if (undefined !== row.bBoxLow && undefined !== row.bBoxHigh && undefined !== row.origin) { - const box = ElementAlignedBox3d.createFromPoints(row.bBoxLow, row.bBoxHigh); + const box = Range3d.create(row.bBoxLow, row.bBoxHigh) as ElementAlignedBox3d; const placement = new Placement3d(Point3d.fromJSON(row.origin), YawPitchRollAngles.createDegrees(row.yaw, row.pitch, row.roll), box); const range = placement.calculateRange(); totalRange.extendRange(range); @@ -41,14 +41,54 @@ function doFixRange(iModel: IModelDb) { class ScriptEntry { public elementIds: Id64String[] = []; public data: any; - constructor(ids: Id64String[], data: any) { this.elementIds = ids, this.data = data; } + public batchId: number; + constructor(ids: Id64String[], data: any, batchId: number) { this.elementIds = ids, this.data = data; this.batchId = batchId; } public getJSON(): any { - const json: any = { elementIds: this.elementIds }; + const json: any = { elementIds: this.elementIds, batchId: this.batchId }; for (const [key, value] of Object.entries(this.data)) json[key] = value; return json; } + public setCuttingPlaneLimits(iModel: IModelDb) { + for (let [key, value] of Object.entries(this.data)) { + if (key === "cuttingPlaneTimeline") { + if (Array.isArray(value)) { + const range = Range3d.createNull() as ElementAlignedBox3d; + this.elementIds.forEach((elementId) => { + const element = iModel.elements.getElement(elementId); + if (element) + range.extendRange(element.placement.calculateRange()); + }); + if (range.isNull) { + value = []; // No graphic elements? -- Should never happen. + } else { + value.forEach((entry) => { + if (entry.value && entry.value.position && entry.value.direction) { + const position = Point3d.create(entry.value.position[0], entry.value.position[1], entry.value.position[2]); + const normal = Vector3d.create(entry.value.direction[0], entry.value.direction[1], entry.value.direction[2]); + normal.normalizeInPlace(); + const corners = range.corners(); + let min = 0, max = 0; + for (let i = 0; i < 8; i++) { + const distance = normal.dotProductStartEnd(position, corners[i]); + if (i) { + if (distance < min) min = distance; + if (distance > max) max = distance; + } else { + min = max = distance; + } + } + const tolerance = 1.0E-3 * (max - min); + if (max < tolerance) entry.value.hidden = true; + if (min > -tolerance) entry.value.visible = true; + } + }); + } + } + } + } + } } class ModelScript { @@ -68,7 +108,7 @@ class ModelScript { const key = JSON.stringify(data); let value: any; if (undefined === (value = this.entries.get(key))) { - this.entries.set(key, new ScriptEntry(ids, data)); + this.entries.set(key, new ScriptEntry(ids, data, 1 + this.entries.size)); } else { for (const id of ids) value.elementIds.push(id); @@ -79,6 +119,9 @@ class ModelScript { this.entries.forEach((entry) => json.elementTimelines.push(entry.getJSON())); return json; } + public setCuttingPlaneLimits(iModel: IModelDb) { + this.entries.forEach((entry) => entry.setCuttingPlaneLimits(iModel)); + } } function transformTimelineIsIdentity(transformTimeline: any) { @@ -148,6 +191,7 @@ function animationScriptFromSynchro(synchroJson: object, iModel: IModelDb): any }); const script: object[] = []; modelScripts.forEach((modelScript) => { + modelScript.setCuttingPlaneLimits(iModel); script.push(modelScript.getJSON()); }); return script; @@ -172,8 +216,6 @@ function doAddAnimationScript(iModel: IModelDb, animationScript: string, createS const bgColor = new ColorDef("rgb(127, 127, 127)"); const displayStyleId = DisplayStyle3d.insert(iModel, view.model, "Schedule View Style", { viewFlags: vf, backgroundColor: bgColor, scheduleScript: script }); - const displayStyleProps = iModel.elements.getElementProps(displayStyleId); - iModel.elements.updateElement(displayStyleProps); iModel.views.setDefaultViewId(OrthographicViewDefinition.insert(iModel, view.model, "Schedule View", view.modelSelectorId, view.categorySelectorId, displayStyleId, iModel.projectExtents)); return true; }); diff --git a/core/backend/src/rpc-impl/IModelUnitTestRpcImpl.ts b/test-apps/testbed/backend/IModelUnitTestRpcImpl.ts similarity index 72% rename from core/backend/src/rpc-impl/IModelUnitTestRpcImpl.ts rename to test-apps/testbed/backend/IModelUnitTestRpcImpl.ts index 00c3dd1..7d92d52 100644 --- a/core/backend/src/rpc-impl/IModelUnitTestRpcImpl.ts +++ b/test-apps/testbed/backend/IModelUnitTestRpcImpl.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ /** @module RpcInterface */ import { RpcInterface, RpcManager, IModelToken } from "@bentley/imodeljs-common"; -import { IModelUnitTestRpcInterface } from "@bentley/imodeljs-common/lib/rpc/IModelUnitTestRpcInterface"; // not part of the "barrel" -import { IModelDb } from "../IModelDb"; +import { IModelUnitTestRpcInterface } from "../common/IModelUnitTestRpcInterface"; // not part of the "barrel" +import { IModelDb } from "@bentley/imodeljs-backend"; /** * The backend implementation of IModelUnitTestRpcInterface. @@ -13,5 +13,5 @@ import { IModelDb } from "../IModelDb"; */ export class IModelUnitTestRpcImpl extends RpcInterface implements IModelUnitTestRpcInterface { public static register() { RpcManager.registerImpl(IModelUnitTestRpcInterface, IModelUnitTestRpcImpl); } - public async executeTest(iModelToken: IModelToken, testName: string, params: any): Promise { return IModelDb.find(iModelToken).executeTest(testName, params); } + public async executeTest(iModelToken: IModelToken, testName: string, params: any): Promise { return JSON.parse(IModelDb.find(iModelToken).nativeDb.executeTest(testName, JSON.stringify(params))); } } diff --git a/test-apps/testbed/backend/TestRpcImpl.ts b/test-apps/testbed/backend/TestRpcImpl.ts index 3ac4535..263fb98 100644 --- a/test-apps/testbed/backend/TestRpcImpl.ts +++ b/test-apps/testbed/backend/TestRpcImpl.ts @@ -32,6 +32,14 @@ export class TestRpcImpl extends RpcInterface implements TestRpcInterface { RpcManager.registerImpl(TestRpcInterface, TestRpcImpl); } + public async interceptSendUnknownStatus(): Promise { + throw new Error("Not intercepted."); + } + + public async interceptSendTimeoutStatus(): Promise { + throw new Error("Not intercepted."); + } + public async op1(params: TestOp1Params): Promise { const activityContext = ActivityLoggingContext.current; activityContext.enter(); const sum = params.sum(); @@ -192,4 +200,16 @@ export class TestRpcImpl3 extends RpcInterface implements TestRpcInterface3 { const val = input; return val; } + + public async op2(size: number, fill: boolean): Promise { + const data = new Uint8Array(size); + + if (fill) { + for (let i = 0; i !== size; ++i) { + data[i] = i % 2; + } + } + + return data; + } } diff --git a/test-apps/testbed/backend/index.ts b/test-apps/testbed/backend/index.ts index ad26d51..59552f8 100644 --- a/test-apps/testbed/backend/index.ts +++ b/test-apps/testbed/backend/index.ts @@ -12,7 +12,8 @@ import * as WebSocket from "ws"; const mobilePort = process.env.MOBILE_PORT ? parseInt(process.env.MOBILE_PORT, 10) : 4000; setupMobileMock(); -import { IModelHost } from "@bentley/imodeljs-backend"; +import { IModelHost, IModelHostConfiguration } from "@bentley/imodeljs-backend"; +import { IModelUnitTestRpcImpl } from "./IModelUnitTestRpcImpl"; import { TestbedConfig, TestbedIpcMessage } from "../common/TestbedConfig"; import { TestRpcImpl, TestRpcImpl2, TestRpcImpl3, resetOp8Initializer, TestZeroMajorRpcImpl } from "./TestRpcImpl"; import { CONSTANTS } from "../common/Testbed"; @@ -34,8 +35,11 @@ const { ipcMain } = require("electron"); ipcMain.on("testbed", handleTestbedCommand); // Start the backend -IModelHost.startup(); +const hostConfig = new IModelHostConfiguration(); +hostConfig.useTileContentThreadPool = true; +IModelHost.startup(hostConfig); +IModelUnitTestRpcImpl.register(); TestRpcImpl.register(); TestRpcImpl3.register(); TestZeroMajorRpcImpl.register(); @@ -64,6 +68,10 @@ if (TestbedConfig.cloudRpc) { app.get(TestbedConfig.swaggerURI, (req, res) => TestbedConfig.cloudRpc.protocol.handleOpenApiDescriptionRequest(req, res)); app.post("*", (req, res) => { + if (handleIntercept(req, res)) { + return; + } + if (handlePending(req, res)) { return; } @@ -72,6 +80,10 @@ if (TestbedConfig.cloudRpc) { }); app.get(/\/imodel\//, (req, res) => { + if (handleIntercept(req, res)) { + return; + } + TestbedConfig.cloudRpc.protocol.handleOperationGetRequest(req, res); // tslint:disable-line:no-floating-promises }); @@ -85,6 +97,10 @@ function handleHttp2Get(req2: http2.Http2ServerRequest, res2: http2.Http2ServerR if (req2.url.indexOf("/v3/swagger.json") === 0) { TestbedConfig.cloudRpc.protocol.handleOpenApiDescriptionRequest(req, res); } else if (req2.url.match(/\/imodel\//)) { + if (handleIntercept(req, res)) { + return; + } + TestbedConfig.cloudRpc.protocol.handleOperationGetRequest(req, res); // tslint:disable-line:no-floating-promises } else { // serve static assets... @@ -111,9 +127,27 @@ function handlePending(_req: HttpServerRequest, res: HttpServerResponse) { } } +function handleIntercept(req: HttpServerRequest, res: HttpServerResponse) { + if (req.path.indexOf("interceptSendTimeoutStatus") !== -1) { + res.status(504).end(""); + return true; + } + + if (req.path.indexOf("interceptSendUnknownStatus") !== -1) { + res.status(567).end(""); + return true; + } + + return false; +} + async function handleHttp2Post(req2: http2.Http2ServerRequest, res2: http2.Http2ServerResponse) { const { req, res } = wrapHttp2API(req2, res2); + if (handleIntercept(req, res)) { + return; + } + if (handlePending(req, res)) { return; } @@ -198,6 +232,9 @@ function handleTestbedCommand(event: any, arg: any) { } else if (msg.name === CONSTANTS.RESET_OP8_INITIALIZER) { resetOp8Initializer(); event.returnValue = true; + } else if (msg.name === CONSTANTS.SET_CHUNK_THRESHOLD) { + TestbedConfig.electronRpc.protocol.transferChunkThreshold = msg.value; + event.returnValue = true; } } diff --git a/core/common/src/rpc/IModelUnitTestRpcInterface.ts b/test-apps/testbed/common/IModelUnitTestRpcInterface.ts similarity index 89% rename from core/common/src/rpc/IModelUnitTestRpcInterface.ts rename to test-apps/testbed/common/IModelUnitTestRpcInterface.ts index 3ddb90d..984cbbe 100644 --- a/core/common/src/rpc/IModelUnitTestRpcInterface.ts +++ b/test-apps/testbed/common/IModelUnitTestRpcInterface.ts @@ -4,9 +4,7 @@ *--------------------------------------------------------------------------------------------*/ /** @module RpcInterface */ -import { RpcInterface } from "../RpcInterface"; -import { RpcManager } from "../RpcManager"; -import { IModelToken } from "../IModel"; +import { RpcInterface, RpcManager, IModelToken } from "@bentley/imodeljs-common"; /** * For unit testing purposes only. This interface should not be registered by real products. diff --git a/test-apps/testbed/common/TestRpcInterface.ts b/test-apps/testbed/common/TestRpcInterface.ts index b43d554..c87c56e 100644 --- a/test-apps/testbed/common/TestRpcInterface.ts +++ b/test-apps/testbed/common/TestRpcInterface.ts @@ -67,6 +67,14 @@ export abstract class TestRpcInterface extends RpcInterface { return RpcManager.getClientForInterface(TestRpcInterface); } + public async interceptSendUnknownStatus(): Promise { + return this.forward(arguments); + } + + public async interceptSendTimeoutStatus(): Promise { + return this.forward(arguments); + } + public async op1(_params: TestOp1Params): Promise { return this.forward(arguments); } @@ -160,6 +168,10 @@ export abstract class TestRpcInterface3 extends RpcInterface { public async op1(_input: number): Promise { return this.forward(arguments); } + + public async op2(_size: number, _fill: boolean): Promise { + return this.forward(arguments); + } } export abstract class RpcTransportTest extends RpcInterface { diff --git a/test-apps/testbed/common/Testbed.ts b/test-apps/testbed/common/Testbed.ts index 01893d6..a896221 100644 --- a/test-apps/testbed/common/Testbed.ts +++ b/test-apps/testbed/common/Testbed.ts @@ -14,4 +14,5 @@ export const CONSTANTS = { RESTORE_COMPATIBLE_INTERFACE_VERSION: "restoreIncompatibleInterfaceVersion", RESTART_BACKEND: "restartBackend", RESET_OP8_INITIALIZER: "resetOp8Initializer", + SET_CHUNK_THRESHOLD: "setChunkThreshold", }; diff --git a/test-apps/testbed/common/TestbedConfig.ts b/test-apps/testbed/common/TestbedConfig.ts index 1ae14ab..89a8f2c 100644 --- a/test-apps/testbed/common/TestbedConfig.ts +++ b/test-apps/testbed/common/TestbedConfig.ts @@ -14,9 +14,10 @@ import { StandaloneIModelRpcInterface, RpcManager, MobileRpcManager, - IModelUnitTestRpcInterface, WipRpcInterface, + ElectronRpcConfiguration, } from "@bentley/imodeljs-common"; +import { IModelUnitTestRpcInterface } from "./IModelUnitTestRpcInterface"; import { TestRpcInterface, TestRpcInterface2, @@ -61,6 +62,7 @@ export class TestbedConfig { public static mobilePort = process.env.MOBILE_PORT ? parseInt(process.env.MOBILE_PORT, 10) : 4000; public static swaggerURI = "/v3/swagger.json"; public static cloudRpc: BentleyCloudRpcConfiguration; + public static electronRpc: ElectronRpcConfiguration; public static get ipc(): any { return ___TESTBED_IPC_RENDERER___; } public static useHttp2 = false; @@ -95,7 +97,7 @@ export class TestbedConfig { TestbedConfig.initializeBentleyCloudCommon(); } - ElectronRpcManager.initializeClient({}, [TestRpcInterface3]); + TestbedConfig.electronRpc = ElectronRpcManager.initializeClient({}, [TestRpcInterface3]); // RPC transport testing window.location.hash = TestbedConfig.mobilePort.toString(); @@ -122,7 +124,7 @@ export class TestbedConfig { TestbedConfig.initializeBentleyCloudCommon(); } - ElectronRpcManager.initializeImpl({}, [TestRpcInterface3]); + TestbedConfig.electronRpc = ElectronRpcManager.initializeImpl({}, [TestRpcInterface3]); // RPC transport testing RpcManager.registerImpl(RpcWebTransportTest, RpcTransportTestImpl); diff --git a/test-apps/testbed/frontend/ChangeSummary.test.ts b/test-apps/testbed/frontend/ChangeSummary.test.ts index 0b46c3a..6c3aeef 100644 --- a/test-apps/testbed/frontend/ChangeSummary.test.ts +++ b/test-apps/testbed/frontend/ChangeSummary.test.ts @@ -7,8 +7,15 @@ import { OpenMode, Logger, LogLevel } from "@bentley/bentleyjs-core"; import { AccessToken } from "@bentley/imodeljs-clients"; import { TestData } from "./TestData"; import { TestRpcInterface } from "../common/TestRpcInterface"; -import { IModelConnection } from "@bentley/imodeljs-frontend"; -import { MockRender } from "./MockRender"; +import { IModelConnection, MockRender } from "@bentley/imodeljs-frontend"; + +async function executeQuery(iModel: IModelConnection, ecsql: string, bindings?: any[] | object): Promise { + const rows: any[] = []; + for await (const row of iModel.query(ecsql, bindings)) { + rows.push(row); + } + return rows; +} describe("ChangeSummary (#integration)", () => { let iModel: IModelConnection; @@ -39,10 +46,10 @@ describe("ChangeSummary (#integration)", () => { assert.exists(iModel); await TestRpcInterface.getClient().deleteChangeCache(iModel.iModelToken); await iModel.attachChangeCache(); - const changeSummaryRows: any[] = await iModel.executeQuery("SELECT count(*) cnt FROM change.ChangeSummary"); + const changeSummaryRows: any[] = await executeQuery(iModel, "SELECT count(*) cnt FROM change.ChangeSummary"); assert.equal(changeSummaryRows.length, 1); assert.equal(changeSummaryRows[0].cnt, 0); - const changeSetRows = await iModel.executeQuery("SELECT count(*) cnt FROM imodelchange.ChangeSet"); + const changeSetRows = await executeQuery(iModel, "SELECT count(*) cnt FROM imodelchange.ChangeSet"); assert.equal(changeSetRows.length, 1); assert.equal(changeSetRows[0].cnt, 0); }).timeout(99999); @@ -58,9 +65,9 @@ describe("ChangeSummary (#integration)", () => { await TestRpcInterface.getClient().extractChangeSummaries(accessToken, testIModel.iModelToken, { currentChangeSetOnly: true }); await testIModel.attachChangeCache(); - const changeSummaryRows: any[] = await testIModel.executeQuery("SELECT count(*) cnt FROM change.ChangeSummary"); + const changeSummaryRows: any[] = await executeQuery(testIModel, "SELECT count(*) cnt FROM change.ChangeSummary"); assert.equal(changeSummaryRows.length, 1); - const changeSetRows = await testIModel.executeQuery("SELECT count(*) cnt FROM imodelchange.ChangeSet"); + const changeSetRows = await executeQuery(testIModel, "SELECT count(*) cnt FROM imodelchange.ChangeSet"); assert.equal(changeSetRows.length, 1); assert.equal(changeSetRows[0].cnt, changeSummaryRows[0].cnt); } finally { diff --git a/test-apps/testbed/frontend/ColorMap.test.ts b/test-apps/testbed/frontend/ColorMap.test.ts index 9df81e7..df4ed80 100644 --- a/test-apps/testbed/frontend/ColorMap.test.ts +++ b/test-apps/testbed/frontend/ColorMap.test.ts @@ -126,11 +126,11 @@ describe("ColorMap", () => { it("test toColorIndex function", () => { /** Test toColorIndex function */ let a: ColorMap = new ColorMap(); - const uint16: Uint16Array = new Uint16Array(2); + const indices = [0, 0]; let colorIndex = new ColorIndex(); a.insert(0xFFFFFF); - a.toColorIndex(colorIndex, uint16); + a.toColorIndex(colorIndex, indices); expect(colorIndex.uniform!.tbgr).to.equal(0xFFFFFF); assert.isTrue(colorIndex.numColors === 1); @@ -139,7 +139,7 @@ describe("ColorMap", () => { expect(colorIndex.uniform!.tbgr).to.equal(ColorDef.white.tbgr); assert.isTrue(colorIndex.numColors === 1); a.insert(0x0000FFFF); - a.toColorIndex(colorIndex, uint16); + a.toColorIndex(colorIndex, indices); expect(colorIndex.isUniform).to.equal(true); assert.isTrue(colorIndex.uniform!.tbgr === 0x0000FFFF); assert.isTrue(colorIndex.numColors === 1); @@ -150,7 +150,7 @@ describe("ColorMap", () => { colorIndex = new ColorIndex(); colorIndex.initUniform(0x00FF00FF); assert.isTrue(colorIndex.numColors === 1); - a.toColorIndex(colorIndex, uint16); + a.toColorIndex(colorIndex, indices); assert.isFalse(colorIndex.isUniform); assert.isTrue(colorIndex.nonUniform && colorIndex.nonUniform.colors.length === 2); let values = colorIndex.nonUniform ? colorIndex.nonUniform.colors.values() : undefined; @@ -165,7 +165,7 @@ describe("ColorMap", () => { a.insert(0x000000FF); colorIndex = new ColorIndex(); assert.isTrue(colorIndex.numColors === 1); - a.toColorIndex(colorIndex, uint16); + a.toColorIndex(colorIndex, indices); assert.isFalse(colorIndex.isUniform); assert.isTrue(colorIndex.nonUniform && colorIndex.nonUniform.colors.length === 3); values = colorIndex.nonUniform ? colorIndex.nonUniform.colors.values() : undefined; diff --git a/test-apps/testbed/frontend/Disposable.test.ts b/test-apps/testbed/frontend/Disposable.test.ts index 360d20c..ea634f8 100644 --- a/test-apps/testbed/frontend/Disposable.test.ts +++ b/test-apps/testbed/frontend/Disposable.test.ts @@ -13,7 +13,7 @@ import { OnScreenTarget, Target, Batch, WorldDecorations, TextureHandle } from " import { Point3d, Range3d, Arc3d } from "@bentley/geometry-core"; import { FakeGMState, FakeModelProps, FakeREProps } from "./TileIO.test"; import { TileIO, IModelTileIO } from "@bentley/imodeljs-frontend/lib/tile"; -import { TileData } from "./TileIO.data"; +import { TILE_DATA_1_1 } from "./TileIO.data.1.1"; import { TestData } from "./TestData"; /* tslint:disable:no-console */ @@ -207,7 +207,7 @@ describe("Disposal of WebGL Resources (#integration)", () => { // Get a render graphic from tile reader const model = new FakeGMState(new FakeModelProps(new FakeREProps()), imodel0); - const stream = new TileIO.StreamBuffer(TileData.triangles.buffer); + const stream = new TileIO.StreamBuffer(TILE_DATA_1_1.triangles.bytes.buffer); const reader = IModelTileIO.Reader.create(stream, model.iModel, model.id, model.is3d, system); expect(reader).not.to.be.undefined; const readerRes = await reader!.read(); diff --git a/test-apps/testbed/frontend/ECSqlQuery.test.ts b/test-apps/testbed/frontend/ECSqlQuery.test.ts new file mode 100644 index 0000000..0925226 --- /dev/null +++ b/test-apps/testbed/frontend/ECSqlQuery.test.ts @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { assert } from "chai"; +import * as path from "path"; +import { IModelConnection } from "@bentley/imodeljs-frontend"; +import { CONSTANTS } from "../common/Testbed"; + +const iModelLocation = path.join(CONSTANTS.IMODELJS_CORE_DIRNAME, "core/backend/lib/test/assets/"); + +describe("ECSql Query", () => { + let imodel1: IModelConnection; + let imodel2: IModelConnection; + let imodel3: IModelConnection; + let imodel4: IModelConnection; + let imodel5: IModelConnection; + + before(async () => { + imodel1 = await IModelConnection.openStandalone(iModelLocation + "test.bim"); + imodel2 = await IModelConnection.openStandalone(iModelLocation + "CompatibilityTestSeed.bim"); + imodel3 = await IModelConnection.openStandalone(iModelLocation + "GetSetAutoHandledStructProperties.bim"); + imodel4 = await IModelConnection.openStandalone(iModelLocation + "GetSetAutoHandledArrayProperties.bim"); + imodel5 = await IModelConnection.openStandalone(iModelLocation + "mirukuru.ibim"); + }); + + after(async () => { + if (imodel1) await imodel1.closeStandalone(); + if (imodel2) await imodel2.closeStandalone(); + if (imodel3) await imodel3.closeStandalone(); + if (imodel4) await imodel4.closeStandalone(); + if (imodel5) await imodel5.closeStandalone(); + + }); + + it("Paging Results", async () => { + const getRowPerPage = (nPageSize: number, nRowCount: number) => { + const nRowPerPage = nRowCount / nPageSize; + const nPages = Math.ceil(nRowPerPage); + const nRowOnLastPage = nRowCount - (Math.floor(nRowPerPage) * pageSize); + const pages = new Array(nPages).fill(pageSize); + if (nRowPerPage) { + pages[nPages - 1] = nRowOnLastPage; + } + return pages; + }; + + const pageSize = 5; + const query = "SELECT ECInstanceId as Id, Parent.Id as ParentId FROM BisCore.element"; + const dbs = [imodel1, imodel2, imodel3, imodel4, imodel5]; + const pendingRowCount = []; + for (const db of dbs) { + pendingRowCount.push(db.queryRowCount(query)); + } + + const rowCounts = await Promise.all(pendingRowCount); + const expected = [46, 62, 7, 7, 28]; + assert.equal(rowCounts.length, expected.length); + for (let i = 0; i < expected.length; i++) { + assert.equal(rowCounts[i], expected[i]); + } + // verify row per page + for (const db of dbs) { + const i = dbs.indexOf(db); + const rowPerPage = getRowPerPage(pageSize, expected[i]); + for (let k = 0; k < rowPerPage.length; k++) { + const row = await db.queryPage(query, undefined, { size: pageSize, start: k }); + assert.equal(row.length, rowPerPage[k]); + } + } + + // verify async iterator + for (const db of dbs) { + const resultSet = []; + for await (const row of db.query(query, undefined, { size: pageSize })) { + resultSet.push(row); + assert.isTrue(Reflect.has(row, "id")); + if (Reflect.ownKeys(row).length > 1) { + assert.isTrue(Reflect.has(row, "parentId")); + const parentId: string = row.parentId; + assert.isTrue(parentId.startsWith("0x")); + } + const id: string = row.id; + assert.isTrue(id.startsWith("0x")); + } + const entry = dbs.indexOf(db); + assert.equal(rowCounts[entry], resultSet.length); + } + }); +}); diff --git a/test-apps/testbed/frontend/FeatureOverrides.test.ts b/test-apps/testbed/frontend/FeatureOverrides.test.ts index 1d2a382..c8dbe4e 100644 --- a/test-apps/testbed/frontend/FeatureOverrides.test.ts +++ b/test-apps/testbed/frontend/FeatureOverrides.test.ts @@ -50,7 +50,7 @@ describe("FeatureOverrides tests", () => { return; } - const vpView = spatialView.clone(); + const vpView = spatialView.clone(); vp = ScreenViewport.create(viewDiv!, vpView); vp.target.setHiliteSet(new Set()); @@ -75,7 +75,7 @@ describe("FeatureOverrides tests", () => { if (!IModelApp.hasRenderSystem) return; - const vpView = spatialView.clone(); + const vpView = spatialView.clone(); vp = ScreenViewport.create(viewDiv!, vpView); vp.target.setHiliteSet(new Set()); diff --git a/test-apps/testbed/frontend/FeatureSymbology.test.ts b/test-apps/testbed/frontend/FeatureSymbology.test.ts index 3557558..3445b30 100644 --- a/test-apps/testbed/frontend/FeatureSymbology.test.ts +++ b/test-apps/testbed/frontend/FeatureSymbology.test.ts @@ -93,14 +93,6 @@ describe("FeatureSymbology.Overrides", () => { }); it("constructor with ViewState parameter works as expected", () => { - // load viewState Special Elements - const neverDrawn = new Set(); - const alwaysDrawn = new Set(); - neverDrawn.add("0x123"); - alwaysDrawn.add("0x124"); - viewState.setNeverDrawn(neverDrawn); - viewState.setAlwaysDrawn(alwaysDrawn); - // init overrides from ViewState overrides = new Overrides(viewState); @@ -108,8 +100,6 @@ describe("FeatureSymbology.Overrides", () => { expect(overrides.isClassVisible(GeometryClass.Dimension)).to.equal(viewState.viewFlags.dimensions); expect(overrides.isClassVisible(GeometryClass.Pattern)).to.equal(viewState.viewFlags.patterns); expect(overrides.lineWeights).to.equal(viewState.viewFlags.weights); - expect(overrides.neverDrawn.toId64Array()).to.deep.equals(Array.from(viewState.neverDrawn!)); - expect(overrides.alwaysDrawn.toId64Array()).to.deep.equals(Array.from(viewState.alwaysDrawn!)); }); it("isClassVisible works as expected", () => { @@ -186,7 +176,7 @@ describe("FeatureSymbology.Overrides", () => { viewState.displayStyle.viewFlags = vf; overrides = new Overrides(viewState); overrides.setVisibleSubCategory(subCategoryId); - assert.isFalse(overrides.isFeatureVisible(feature), "if geometryClass and subCategory are visible, feature is visible"); + assert.isTrue(overrides.isFeatureVisible(feature), "if geometryClass and subCategory are visible, feature is visible"); }); it("getFeatureAppearance works as expected", () => { diff --git a/test-apps/testbed/frontend/FrameBuffer.test.ts b/test-apps/testbed/frontend/FrameBuffer.test.ts index 53ad98d..f155df3 100644 --- a/test-apps/testbed/frontend/FrameBuffer.test.ts +++ b/test-apps/testbed/frontend/FrameBuffer.test.ts @@ -6,7 +6,7 @@ import { expect, assert } from "chai"; import { WebGLTestContext } from "./WebGLTestContext"; import { IModelApp } from "@bentley/imodeljs-frontend"; -import { Capabilities, System, RenderBuffer, TextureHandle, FrameBuffer, GL } from "@bentley/imodeljs-frontend/lib/webgl"; +import { Capabilities, System, RenderBuffer, TextureHandle, FrameBuffer, GL, Debug } from "@bentley/imodeljs-frontend/lib/webgl"; describe("FrameBuffer tests", () => { before(() => WebGLTestContext.startup()); @@ -29,7 +29,7 @@ describe("FrameBuffer tests", () => { } expect(fb.bind()).to.be.true; - expect(fb.isValid).to.be.true; + expect(Debug.isValidFrameBuffer).to.be.true; fb.unbind(); }); @@ -61,7 +61,7 @@ describe("FrameBuffer tests", () => { } expect(fb.bind()).to.be.true; - expect(fb.isValid).to.be.true; + expect(Debug.isValidFrameBuffer).to.be.true; fb.unbind(); }); @@ -99,7 +99,7 @@ describe("FrameBuffer tests", () => { } expect(fb.bind()).to.be.true; - expect(fb.isValid).to.be.true; + expect(Debug.isValidFrameBuffer).to.be.true; fb.unbind(); }); }); diff --git a/test-apps/testbed/frontend/IModelApp.test.ts b/test-apps/testbed/frontend/IModelApp.test.ts index b88258c..e0fcd36 100644 --- a/test-apps/testbed/frontend/IModelApp.test.ts +++ b/test-apps/testbed/frontend/IModelApp.test.ts @@ -3,11 +3,10 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import { assert, expect } from "chai"; -import { IModelApp, Tool, AccuDraw, IdleTool, RotateViewTool, PanViewTool, SelectionTool } from "@bentley/imodeljs-frontend"; +import { IModelApp, MockRender, Tool, AccuDraw, IdleTool, RotateViewTool, PanViewTool, SelectionTool } from "@bentley/imodeljs-frontend"; import { I18NNamespace } from "@bentley/imodeljs-i18n"; import { TestbedConfig } from "../common/TestbedConfig"; import { WebGLTestContext } from "./WebGLTestContext"; -import { MockRender } from "./MockRender"; /** class to simulate overriding the default AccuDraw */ class TestAccuDraw extends AccuDraw { } diff --git a/test-apps/testbed/frontend/IModelConnection.test.ts b/test-apps/testbed/frontend/IModelConnection.test.ts index 4ed1534..32ed95d 100644 --- a/test-apps/testbed/frontend/IModelConnection.test.ts +++ b/test-apps/testbed/frontend/IModelConnection.test.ts @@ -9,11 +9,18 @@ import { BisCodeSpec, CodeSpec, NavigationValue, RelatedElement, IModelVersion } import { TestData } from "./TestData"; import { DrawingViewState, OrthographicViewState, ViewState, IModelConnection, - ModelSelectorState, DisplayStyle3dState, DisplayStyle2dState, CategorySelectorState, + ModelSelectorState, DisplayStyle3dState, DisplayStyle2dState, CategorySelectorState, MockRender, } from "@bentley/imodeljs-frontend"; import { TestbedConfig } from "../common/TestbedConfig"; import { CONSTANTS } from "../common/Testbed"; -import { MockRender } from "./MockRender"; + +async function executeQuery(iModel: IModelConnection, ecsql: string, bindings?: any[] | object): Promise { + const rows: any[] = []; + for await (const row of iModel.query(ecsql, bindings)) { + rows.push(row); + } + return rows; +} describe("IModelConnection (#integration)", () => { let iModel: IModelConnection; @@ -50,16 +57,13 @@ describe("IModelConnection (#integration)", () => { const queryElementIds = await iModel.elements.queryIds({ from: "BisCore.Category", limit: 20, offset: 0 }); assert.isAtLeast(queryElementIds.size, 1); - const formatObjs: any[] = await iModel.elements.formatElements(queryElementIds); - assert.isAtLeast(formatObjs.length, 1); - const modelProps = await iModel.models.getProps(iModel.models.repositoryModelId); assert.exists(modelProps); assert.equal(modelProps.length, 1); assert.equal(modelProps[0].id, iModel.models.repositoryModelId); assert.equal(iModel.models.repositoryModelId, modelProps[0].id); - const rows: any[] = await iModel.executeQuery("SELECT CodeValue AS code FROM BisCore.Category LIMIT 20"); + const rows: any[] = await executeQuery(iModel, "SELECT CodeValue AS code FROM BisCore.Category LIMIT 20"); assert.isAtLeast(rows.length, 1); assert.exists(rows[0].code); assert.equal(rows.length, queryElementIds.size); @@ -201,7 +205,7 @@ describe("IModelConnection (#integration)", () => { it("ECSQL with BLOB", async () => { assert.exists(iModel); - let rows = await iModel.executeQuery("SELECT ECInstanceId,GeometryStream FROM bis.GeometricElement3d WHERE GeometryStream IS NOT NULL LIMIT 1"); + let rows = await executeQuery(iModel, "SELECT ECInstanceId,GeometryStream FROM bis.GeometricElement3d WHERE GeometryStream IS NOT NULL LIMIT 1"); assert.equal(rows.length, 1); const row: any = rows[0]; @@ -211,13 +215,13 @@ describe("IModelConnection (#integration)", () => { const geomStream: Uint8Array = row.geometryStream; assert.isAtLeast(geomStream.byteLength, 1); - rows = await iModel.executeQuery("SELECT 1 FROM bis.GeometricElement3d WHERE GeometryStream=?", [geomStream]); + rows = await executeQuery(iModel, "SELECT 1 FROM bis.GeometricElement3d WHERE GeometryStream=?", [geomStream]); assert.equal(rows.length, 1); }); it("Parameterized ECSQL", async () => { assert.exists(iModel); - let rows = await iModel.executeQuery("SELECT ECInstanceId,Model,LastMod,CodeValue,FederationGuid,Origin FROM bis.GeometricElement3d LIMIT 1"); + let rows = await executeQuery(iModel, "SELECT ECInstanceId,Model,LastMod,CodeValue,FederationGuid,Origin FROM bis.GeometricElement3d LIMIT 1"); assert.equal(rows.length, 1); let expectedRow = rows[0]; const expectedId = Id64.fromJSON(expectedRow.id); @@ -228,11 +232,11 @@ describe("IModelConnection (#integration)", () => { const expectedFedGuid: string | undefined = !!expectedRow.federationGuid ? expectedRow.federationGuid : undefined; const expectedOrigin: XYAndZ = expectedRow.origin; - let actualRows = await iModel.executeQuery("SELECT 1 FROM bis.GeometricElement3d WHERE ECInstanceId=? AND Model=? OR (LastMod=? AND CodeValue=? AND FederationGuid=? AND Origin=?)", + let actualRows = await executeQuery(iModel, "SELECT 1 FROM bis.GeometricElement3d WHERE ECInstanceId=? AND Model=? OR (LastMod=? AND CodeValue=? AND FederationGuid=? AND Origin=?)", [expectedId, expectedModel, expectedLastMod, expectedRow.codeValue, expectedFedGuid, expectedOrigin]); assert.equal(actualRows.length, 1); - actualRows = await iModel.executeQuery("SELECT 1 FROM bis.GeometricElement3d WHERE ECInstanceId=:id AND Model=:model OR (LastMod=:lastmod AND CodeValue=:codevalue AND FederationGuid=:fedguid AND Origin=:origin)", + actualRows = await executeQuery(iModel, "SELECT 1 FROM bis.GeometricElement3d WHERE ECInstanceId=:id AND Model=:model OR (LastMod=:lastmod AND CodeValue=:codevalue AND FederationGuid=:fedguid AND Origin=:origin)", { id: expectedId, model: expectedModel, lastmod: expectedLastMod, codevalue: expectedRow.codeValue, fedguid: expectedFedGuid, origin: expectedOrigin, @@ -240,22 +244,22 @@ describe("IModelConnection (#integration)", () => { assert.equal(actualRows.length, 1); // single parameter query - actualRows = await iModel.executeQuery("SELECT 1 FROM bis.Element WHERE LastMod=?", [expectedLastMod]); + actualRows = await executeQuery(iModel, "SELECT 1 FROM bis.Element WHERE LastMod=?", [expectedLastMod]); assert.isTrue(actualRows.length >= 1); - actualRows = await iModel.executeQuery("SELECT 1 FROM bis.Element WHERE LastMod=:lastmod", { lastmod: expectedLastMod }); + actualRows = await executeQuery(iModel, "SELECT 1 FROM bis.Element WHERE LastMod=:lastmod", { lastmod: expectedLastMod }); assert.isTrue(actualRows.length >= 1); // New query with point2d parameter - rows = await iModel.executeQuery("SELECT ECInstanceId,Origin FROM bis.GeometricElement2d LIMIT 1"); + rows = await executeQuery(iModel, "SELECT ECInstanceId,Origin FROM bis.GeometricElement2d LIMIT 1"); assert.equal(rows.length, 1); expectedRow = rows[0]; - actualRows = await iModel.executeQuery("SELECT 1 FROM bis.GeometricElement2d WHERE ECInstanceId=? AND Origin=?", + actualRows = await executeQuery(iModel, "SELECT 1 FROM bis.GeometricElement2d WHERE ECInstanceId=? AND Origin=?", [Id64.fromJSON(expectedRow.id), expectedRow.origin]); assert.equal(actualRows.length, 1); - actualRows = await iModel.executeQuery("SELECT 1 FROM bis.GeometricElement2d WHERE ECInstanceId=:id AND Origin=:origin", + actualRows = await executeQuery(iModel, "SELECT 1 FROM bis.GeometricElement2d WHERE ECInstanceId=:id AND Origin=:origin", { id: expectedRow.id, origin: expectedRow.origin }); assert.equal(actualRows.length, 1); }).timeout(99999); diff --git a/test-apps/testbed/frontend/Matrix.test.ts b/test-apps/testbed/frontend/Matrix.test.ts index 49dcb25..cc0040e 100644 --- a/test-apps/testbed/frontend/Matrix.test.ts +++ b/test-apps/testbed/frontend/Matrix.test.ts @@ -118,9 +118,6 @@ describe("Matrix4", () => { assert.isTrue(mat4.data[15] === 1, "(3,3) --> 1"); }); it("toTransform works as expected", () => { - const invalidMat = Matrix4.fromValues(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - // throws error when (3,0) !== 0 && (3,1) !== 0 && (3,2) !== 0 && (3,3) !== 1 - assert.throw(invalidMat.toTransform.bind(invalidMat)); const validMat = Matrix4.fromValues(1, 2, 3, 10, 4, 5, 6, 11, 7, 8, 9, 12, 0, 0, 0, 1); const tran = validMat.toTransform(); const mat = tran.matrix; diff --git a/test-apps/testbed/frontend/MeshBuilder.test.ts b/test-apps/testbed/frontend/MeshBuilder.test.ts index 0612cb6..15e80ca 100644 --- a/test-apps/testbed/frontend/MeshBuilder.test.ts +++ b/test-apps/testbed/frontend/MeshBuilder.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import { expect, assert } from "chai"; -import { IModelConnection, SpatialViewState, StandardViewId, ScreenViewport } from "@bentley/imodeljs-frontend"; +import { MockRender, IModelConnection, SpatialViewState, StandardViewId, ScreenViewport } from "@bentley/imodeljs-frontend"; import { GraphicParams, ColorDef } from "@bentley/imodeljs-common"; import { Range3d, @@ -31,7 +31,6 @@ import { import { System } from "@bentley/imodeljs-frontend/lib/webgl"; import { FakeDisplayParams } from "./DisplayParams.test"; import { CONSTANTS } from "../common/Testbed"; -import { MockRender } from "./MockRender"; const iModelLocation = path.join(CONSTANTS.IMODELJS_CORE_DIRNAME, "core/backend/lib/test/assets/test.bim"); diff --git a/test-apps/testbed/frontend/MeshBuilderMap.test.ts b/test-apps/testbed/frontend/MeshBuilderMap.test.ts index 9643dc4..4671d17 100644 --- a/test-apps/testbed/frontend/MeshBuilderMap.test.ts +++ b/test-apps/testbed/frontend/MeshBuilderMap.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import { expect, assert } from "chai"; -import { IModelConnection, ScreenViewport, SpatialViewState, StandardViewId } from "@bentley/imodeljs-frontend"; +import { MockRender, IModelConnection, ScreenViewport, SpatialViewState, StandardViewId } from "@bentley/imodeljs-frontend"; import { Range3d, Point3d, Arc3d, LineString3d, Loop, Transform } from "@bentley/geometry-core"; import * as path from "path"; import { @@ -22,7 +22,6 @@ import { import { System } from "@bentley/imodeljs-frontend/lib/webgl"; import { FakeDisplayParams } from "./DisplayParams.test"; import { CONSTANTS } from "../common/Testbed"; -import { MockRender } from "./MockRender"; import { ColorDef, GraphicParams } from "@bentley/imodeljs-common"; const iModelLocation = path.join(CONSTANTS.IMODELJS_CORE_DIRNAME, "core/backend/lib/test/assets/test.bim"); diff --git a/test-apps/testbed/frontend/MeshPrimitives.test.ts b/test-apps/testbed/frontend/MeshPrimitives.test.ts index af8dbf2..53d334b 100644 --- a/test-apps/testbed/frontend/MeshPrimitives.test.ts +++ b/test-apps/testbed/frontend/MeshPrimitives.test.ts @@ -3,13 +3,12 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import { expect, assert } from "chai"; -import { IModelConnection, SpatialViewState, StandardViewId } from "@bentley/imodeljs-frontend"; +import { MockRender, IModelConnection, SpatialViewState, StandardViewId } from "@bentley/imodeljs-frontend"; import { Range3d, Point3d, Point2d } from "@bentley/geometry-core"; import * as path from "path"; import { Mesh, Triangle } from "@bentley/imodeljs-frontend/lib/rendering"; import { FakeDisplayParams } from "./DisplayParams.test"; import { CONSTANTS } from "../common/Testbed"; -import { MockRender } from "./MockRender"; import { MeshPolyline, QPoint3d, ColorDef, OctEncodedNormal } from "@bentley/imodeljs-common"; const iModelLocation = path.join(CONSTANTS.IMODELJS_CORE_DIRNAME, "core/backend/lib/test/assets/test.bim"); diff --git a/test-apps/testbed/frontend/MockRender.ts b/test-apps/testbed/frontend/MockRender.ts index e3db926..3b3bfd1 100644 --- a/test-apps/testbed/frontend/MockRender.ts +++ b/test-apps/testbed/frontend/MockRender.ts @@ -5,148 +5,13 @@ import { IModelApp, - Viewport, - GraphicList, - RenderClipVolume, - Decorations, - RenderPlan, ViewRect, - Pixel, - GraphicType, RenderGraphic, - GraphicBranch, - PackedFeatureTable, - IModelConnection, + MockRender, RenderTarget, - RenderSystem, } from "@bentley/imodeljs-frontend"; -import { - PrimitiveBuilder, - MeshParams, - PolylineParams, - PointStringParams, - PointCloudArgs, - RenderMemory, -} from "@bentley/imodeljs-frontend/lib/rendering"; -import { ElementAlignedBox3d } from "@bentley/imodeljs-common"; -import { Transform } from "@bentley/geometry-core"; -import { Id64String, dispose } from "@bentley/bentleyjs-core"; import { expect } from "chai"; -// Contains extensible mock implementations of the various components of a RenderSystem. -// Use these instead of the default RenderSystem wherever possible because: -// (1) Electron has a bug on Windows in which it fails to obtain a WebGLRenderingContext when running inside a VM (e.g., during CI job); and -// (2) To decouple the logic which uses aspects of the RenderSystem from the full implementation. -// Any and all of these types can be extended for the purposes of specific tests. -// To use this: -// (1) If overriding anything in the implementation supplied herein, pass a SystemFactory function to MockRender.App.systemFactory. -// (2) Call MockRender.App.startup() instead of IModelApp.startup() or MaybeRenderApp.startup() before tests begin. -// (3) Likewise call MockRender.App.shutdown() when finished. This resets the SystemFactory to its default. -export namespace MockRender { - export abstract class Target extends RenderTarget { - protected constructor(private readonly _system: System) { super(); } - - public get renderSystem(): RenderSystem { return this._system; } - public get cameraFrustumNearScaleLimit() { return 0; } - public get wantInvertBlackBackground() { return false; } - public get animationFraction() { return 0; } - public set animationFraction(_fraction: number) { } - public changeScene(_scene: GraphicList, _activeVolume?: RenderClipVolume) { } - public changeTerrain(_terrain: GraphicList) { } - public changeDynamics(_dynamics?: GraphicList) { } - public changeDecorations(_decs: Decorations) { } - public changeRenderPlan(_plan: RenderPlan) { } - public drawFrame(_sceneTime?: number) { } - public updateViewRect() { return false; } - public readPixels(_rect: ViewRect, _selector: Pixel.Selector, receiver: Pixel.Receiver) { receiver(undefined); } - } - - export class OnScreenTarget extends Target { - public constructor(system: System, private readonly _canvas: HTMLCanvasElement) { super(system); } - - public get viewRect() { return new ViewRect(0, 0, this._canvas.clientWidth, this._canvas.clientHeight); } - public setViewRect(_rect: ViewRect, _temp: boolean) { } - } - - export class OffScreenTarget extends Target { - public constructor(system: System, private readonly _viewRect: ViewRect) { super(system); } - - public get viewRect() { return this._viewRect; } - public setViewRect(rect: ViewRect, _temp: boolean) { this._viewRect.setFrom(rect); } - } - - export class Builder extends PrimitiveBuilder { - public constructor(system: System, placement: Transform = Transform.identity, type: GraphicType, viewport: Viewport, pickId?: Id64String) { - super(system, type, viewport, placement, pickId); - } - } - - export class Graphic extends RenderGraphic { - public constructor() { super(); } - - public dispose() { } - public collectStatistics(_stats: RenderMemory.Statistics): void { } - } - - export class List extends Graphic { - public constructor(public readonly graphics: RenderGraphic[]) { super(); } - - public dispose() { - for (const graphic of this.graphics) - dispose(graphic); - - this.graphics.length = 0; - } - } - - export class Branch extends Graphic { - public constructor(public readonly branch: GraphicBranch, public readonly transform: Transform, public readonly clips?: RenderClipVolume) { super(); } - - public dispose() { this.branch.dispose(); } - } - - export class Batch extends Graphic { - public constructor(public readonly graphic: RenderGraphic, public readonly featureTable: PackedFeatureTable, public readonly range: ElementAlignedBox3d) { super(); } - - public dispose() { - dispose(this.graphic); - } - } - - export class System extends RenderSystem { - public get isValid() { return true; } - public dispose(): void { } - public get maxTextureSize() { return 4096; } - - public createTarget(canvas: HTMLCanvasElement) { return new OnScreenTarget(this, canvas); } - public createOffscreenTarget(rect: ViewRect): RenderTarget { return new OffScreenTarget(this, rect); } - - public createGraphicBuilder(placement: Transform, type: GraphicType, viewport: Viewport, pickableId?: Id64String) { return new Builder(this, placement, type, viewport, pickableId); } - public createGraphicList(primitives: RenderGraphic[]) { return new List(primitives); } - public createBranch(branch: GraphicBranch, transform: Transform, clips?: RenderClipVolume) { return new Branch(branch, transform, clips); } - public createBatch(graphic: RenderGraphic, features: PackedFeatureTable, range: ElementAlignedBox3d) { return new Batch(graphic, features, range); } - - public createMesh(_params: MeshParams) { return new Graphic(); } - public createPolyline(_params: PolylineParams) { return new Graphic(); } - public createPointString(_params: PointStringParams) { return new Graphic(); } - public createPointCloud(_args: PointCloudArgs, _imodel: IModelConnection) { return new Graphic(); } - } - - export type SystemFactory = () => RenderSystem; - - export class App extends IModelApp { - public static systemFactory: SystemFactory = () => App.createDefaultRenderSystem(); - - protected static supplyRenderSystem(): RenderSystem { return this.systemFactory(); } - public static shutdown(): void { - this.systemFactory = () => App.createDefaultRenderSystem(); - super.shutdown(); - } - - protected static createDefaultRenderSystem() { return new System(); } - } -} - class MyTarget extends MockRender.OffScreenTarget { } class MyList extends MockRender.List { } class MySystem extends MockRender.System { diff --git a/test-apps/testbed/frontend/ModelState.test.ts b/test-apps/testbed/frontend/ModelState.test.ts index 696a5eb..6951d4a 100644 --- a/test-apps/testbed/frontend/ModelState.test.ts +++ b/test-apps/testbed/frontend/ModelState.test.ts @@ -4,11 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { expect, assert } from "chai"; import * as path from "path"; -import { ModelSelectorState, IModelConnection, DrawingModelState, SheetModelState, SpatialModelState, GeometricModelState } from "@bentley/imodeljs-frontend"; +import { MockRender, ModelSelectorState, IModelConnection, DrawingModelState, SheetModelState, SpatialModelState, GeometricModelState } from "@bentley/imodeljs-frontend"; import { Id64 } from "@bentley/bentleyjs-core"; import { Code, ModelSelectorProps } from "@bentley/imodeljs-common"; import { CONSTANTS } from "../common/Testbed"; -import { MockRender } from "./MockRender"; const iModelLocation = path.join(CONSTANTS.IMODELJS_CORE_DIRNAME, "core/backend/lib/test/assets/"); @@ -67,6 +66,16 @@ describe("ModelState", () => { await imodel.models.load(["0x24", "0x28", "0x2c", "0x11", "0x34", "0x24", "nonsense"]); assert.equal(models.size, 5); + const testDrawing = models.get("0x24") as DrawingModelState; + let testSpatial = models.get("0x11") as SpatialModelState; + + let range = await testDrawing.queryModelRange(); + assert.isTrue(range.low.isAlmostEqual({ x: 0, y: 0, z: -1 })); + assert.isTrue(range.high.isAlmostEqual({ x: 5, y: 5, z: 1 })); + + range = await testSpatial.queryModelRange(); + assert.isTrue(range.isNull); + const modelProps = await imodel.models.queryProps({ from: SpatialModelState.sqlName }); assert.isAtLeast(modelProps.length, 2); @@ -75,6 +84,11 @@ describe("ModelState", () => { const scalableMesh = imodel2.models.getLoaded("0x28"); assert.instanceOf(scalableMesh, SpatialModelState, "ScalableMeshModel should be SpatialModel"); assert.equal(scalableMesh!.classFullName, "ScalableMesh:ScalableMeshModel"); + + testSpatial = imodel2.models.getLoaded("0x1c") as SpatialModelState; + range = await testSpatial.queryModelRange(); + assert.isTrue(range.low.isAlmostEqual({ x: 288874.1174466432, y: 3803761.1888925503, z: -0.0005 })); + assert.isTrue(range.high.isAlmostEqual({ x: 289160.8417204395, y: 3803959.118535, z: 0.0005 })); }); it("view thumbnails", async () => { diff --git a/test-apps/testbed/frontend/Render.test.ts b/test-apps/testbed/frontend/Render.test.ts index 5093b1a..99912e4 100644 --- a/test-apps/testbed/frontend/Render.test.ts +++ b/test-apps/testbed/frontend/Render.test.ts @@ -15,6 +15,8 @@ import { SpatialViewState, ViewRect, FeatureSymbology, + FeatureOverrideProvider, + Viewport, } from "@bentley/imodeljs-frontend"; // Mirukuru contains a single view, looking at a single design model containing a single white rectangle (element ID 41 (0x29), subcategory ID = 24 (0x18)). @@ -72,7 +74,7 @@ describe("Render mirukuru", () => { // Out-of-bounds pixels are in "unknown" state const unknownPixel = new Pixel.Data(); - const coords = [ [ -1, -1 ], [0, -1], [rect.width, 0], [rect.width - 1, rect.height * 2] ]; + const coords = [[-1, -1], [0, -1], [rect.width, 0], [rect.width - 1, rect.height * 2]]; for (const coord of coords) { const oob = vp.readPixel(coord[0], coord[1]); expect(comparePixelData(unknownPixel, oob)).to.equal(0); @@ -167,8 +169,20 @@ describe("Render mirukuru", () => { const vf = vp.view.viewFlags; vf.visibleEdges = vf.hiddenEdges = vf.sourceLights = vf.cameraLights = vf.solarLight = false; + type AddFeatureOverrides = (overrides: FeatureSymbology.Overrides, viewport: Viewport) => void; + class RenderTestOverrideProvider implements FeatureOverrideProvider { + public ovrFunc?: AddFeatureOverrides; + public addFeatureOverrides(overrides: FeatureSymbology.Overrides, viewport: Viewport): void { + if (undefined !== this.ovrFunc) + this.ovrFunc(overrides, viewport); + } + } + const ovrProvider = new RenderTestOverrideProvider(); + vp.featureOverrideProvider = ovrProvider; + // Specify element is never drawn. - vp.addFeatureOverrides = (ovrs, _) => ovrs.setNeverDrawn(elemId); + ovrProvider.ovrFunc = (ovrs, _) => ovrs.setNeverDrawn(elemId); + vp.view.setFeatureOverridesDirty(true); await vp.waitForAllTilesToRender(); const bgColor = Color.fromRgba(0, 0, 0, 0xff); @@ -179,25 +193,39 @@ describe("Render mirukuru", () => { let pixels = vp.readUniquePixelData(); expect(pixels.length).to.equal(1); + // Specify element is nonLocatable + ovrProvider.ovrFunc = (ovrs, _) => ovrs.overrideElement(elemId, FeatureSymbology.Appearance.fromJSON({ nonLocatable: true })); + vp.view.setFeatureOverridesDirty(true); + await vp.drawFrame(); + pixels = vp.readUniquePixelData(undefined, true); // Exclude non-locatable elements + expect(pixels.length).to.equal(1); + expect(pixels.containsElement(elemId)).to.be.false; + pixels = vp.readUniquePixelData(); // Include non-locatable elements + expect(pixels.length).to.equal(2); + expect(pixels.containsElement(elemId)).to.be.true; + // Specify element is drawn blue - vp.addFeatureOverrides = (ovrs, _) => ovrs.overrideElement(elemId, FeatureSymbology.Appearance.fromRgb(ColorDef.blue)); + ovrProvider.ovrFunc = (ovrs, _) => ovrs.overrideElement(elemId, FeatureSymbology.Appearance.fromRgb(ColorDef.blue)); + vp.view.setFeatureOverridesDirty(true); await vp.drawFrame(); colors = vp.readUniqueColors(); expect(colors.length).to.equal(2); expect(colors.contains(Color.fromRgba(0, 0, 0xff, 0xff))).to.be.true; // Specify default overrides - vp.addFeatureOverrides = (ovrs, _) => ovrs.setDefaultOverrides(FeatureSymbology.Appearance.fromRgb(ColorDef.red)); + ovrProvider.ovrFunc = (ovrs, _) => ovrs.setDefaultOverrides(FeatureSymbology.Appearance.fromRgb(ColorDef.red)); + vp.view.setFeatureOverridesDirty(true); await vp.drawFrame(); colors = vp.readUniqueColors(); expect(colors.length).to.equal(2); expect(colors.contains(Color.fromRgba(0xff, 0, 0, 0xff))).to.be.true; // Specify default overrides, but also override element color - vp.addFeatureOverrides = (ovrs, _) => { + ovrProvider.ovrFunc = (ovrs, _) => { ovrs.setDefaultOverrides(FeatureSymbology.Appearance.fromRgb(ColorDef.green)); ovrs.overrideElement(elemId, FeatureSymbology.Appearance.fromRgb(new ColorDef(0x7f0000))); // blue = 0x7f... }; + vp.view.setFeatureOverridesDirty(true); await vp.drawFrame(); colors = vp.readUniqueColors(); expect(colors.length).to.equal(2); @@ -205,22 +233,25 @@ describe("Render mirukuru", () => { expect(colors.contains(Color.fromRgba(0xff, 0, 0, 0xff))).to.be.false; // Override by subcategory - vp.addFeatureOverrides = (ovrs, _) => ovrs.overrideSubCategory(subcatId, FeatureSymbology.Appearance.fromRgb(ColorDef.red)); + ovrProvider.ovrFunc = (ovrs, _) => ovrs.overrideSubCategory(subcatId, FeatureSymbology.Appearance.fromRgb(ColorDef.red)); + vp.view.setFeatureOverridesDirty(true); await vp.drawFrame(); colors = vp.readUniqueColors(); expect(colors.contains(Color.fromRgba(0xff, 0, 0, 0xff))).to.be.true; // Override color for element and subcategory - element wins - vp.addFeatureOverrides = (ovrs, _) => { + ovrProvider.ovrFunc = (ovrs, _) => { ovrs.overrideSubCategory(subcatId, FeatureSymbology.Appearance.fromRgb(ColorDef.blue)); ovrs.overrideElement(elemId, FeatureSymbology.Appearance.fromRgb(ColorDef.red)); }; + vp.view.setFeatureOverridesDirty(true); await vp.drawFrame(); colors = vp.readUniqueColors(); expect(colors.contains(Color.fromRgba(0xff, 0, 0, 0xff))).to.be.true; // Override to be fully transparent - element should not draw at all - vp.addFeatureOverrides = (ovrs, _) => ovrs.overrideElement(elemId, FeatureSymbology.Appearance.fromTransparency(1.0)); + ovrProvider.ovrFunc = (ovrs, _) => ovrs.overrideElement(elemId, FeatureSymbology.Appearance.fromTransparency(1.0)); + vp.view.setFeatureOverridesDirty(true); await vp.drawFrame(); colors = vp.readUniqueColors(); expect(colors.length).to.equal(1); @@ -233,7 +264,8 @@ describe("Render mirukuru", () => { // Set bg color to red, elem color to 50% transparent blue => expect blending vp.view.displayStyle.backgroundColor = ColorDef.red; vp.invalidateRenderPlan(); - vp.addFeatureOverrides = (ovrs, _) => ovrs.overrideElement(elemId, FeatureSymbology.Appearance.fromJSON({ rgb: new RgbColor(0, 0, 1), transparency: 0.5 })); + ovrProvider.ovrFunc = (ovrs, _) => ovrs.overrideElement(elemId, FeatureSymbology.Appearance.fromJSON({ rgb: new RgbColor(0, 0, 1), transparency: 0.5 })); + vp.view.setFeatureOverridesDirty(true); await vp.drawFrame(); colors = vp.readUniqueColors(); expect(colors.length).to.equal(2); diff --git a/test-apps/testbed/frontend/Rpc.ElectronProtocol.test.ts b/test-apps/testbed/frontend/Rpc.ElectronProtocol.test.ts index 6a9465d..49b63d5 100644 --- a/test-apps/testbed/frontend/Rpc.ElectronProtocol.test.ts +++ b/test-apps/testbed/frontend/Rpc.ElectronProtocol.test.ts @@ -5,14 +5,16 @@ import { RpcProtocolEvent, RpcRequest } from "@bentley/imodeljs-common"; import { TestRpcInterface3 } from "../common/TestRpcInterface"; import { assert } from "chai"; +import { TestbedConfig } from "../common/TestbedConfig"; +import { CONSTANTS } from "../common/Testbed"; describe("Rpc.ElectronProtocol", () => { - it("generate one response per request", async () => { + it("should generate one response per request", async () => { let received = 0; let request: RpcRequest; const client = TestRpcInterface3.getClient(); - client.configuration.protocol.events.addListener((type, object) => { + const removeListener = client.configuration.protocol.events.addListener((type, object) => { if (type !== RpcProtocolEvent.ResponseLoaded) return; @@ -28,7 +30,29 @@ describe("Rpc.ElectronProtocol", () => { assert.equal(response, 1); return new Promise((resolve, reject) => setTimeout(() => { + removeListener(); (received === 1) ? resolve() : reject(new Error(`Received ${received} responses for 1 request`)); }, 2000)); }); + + it("should chunk data larger than 64mb", async () => { + const client = TestRpcInterface3.getClient(); + const threshold = client.configuration.protocol.transferChunkThreshold; + + let size = 2 * 1024 * 1024; + TestbedConfig.sendToMainSync({ name: CONSTANTS.SET_CHUNK_THRESHOLD, value: size }); + let data = await client.op2(size * 2, true); + assert.equal(data.byteLength, size * 2); + + for (let i = 0; i !== (size * 2); ++i) { + assert.equal(data[i], i % 2); + } + + size = 48 * 1024 * 1024; + TestbedConfig.sendToMainSync({ name: CONSTANTS.SET_CHUNK_THRESHOLD, value: size }); + data = await client.op2(size * 2, false); + assert.equal(data.byteLength, size * 2); + + TestbedConfig.sendToMainSync({ name: CONSTANTS.SET_CHUNK_THRESHOLD, value: threshold }); + }); }); diff --git a/test-apps/testbed/frontend/Rpc.HttpProtocol.test.ts b/test-apps/testbed/frontend/Rpc.HttpProtocol.test.ts index 3be41cb..daf9fce 100644 --- a/test-apps/testbed/frontend/Rpc.HttpProtocol.test.ts +++ b/test-apps/testbed/frontend/Rpc.HttpProtocol.test.ts @@ -2,7 +2,7 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { RpcRequestEvent, RpcRequest, RpcOperation } from "@bentley/imodeljs-common"; +import { RpcRequestEvent, RpcRequest, RpcOperation, ServerError, ServerTimeoutError } from "@bentley/imodeljs-common"; import { TestRpcInterface, TestOp1Params } from "../common/TestRpcInterface"; import { assert } from "chai"; import { TestbedConfig } from "../common/TestbedConfig"; @@ -44,5 +44,21 @@ if (TestbedConfig.cloudRpc) { assert.equal(pendingsReceived, expectedPendings); assert.equal(remoteSum, params.sum()); }); + + it("should reject an unknown status code", async () => { + try { + await TestRpcInterface.getClient().interceptSendTimeoutStatus(); + assert(false); + } catch (err) { + assert(err instanceof ServerTimeoutError && err.errorNumber === 504); + } + + try { + await TestRpcInterface.getClient().interceptSendUnknownStatus(); + assert(false); + } catch (err) { + assert(err instanceof ServerError && err.errorNumber === 567); + } + }); }); } diff --git a/test-apps/testbed/frontend/RpcInterface.test.ts b/test-apps/testbed/frontend/RpcInterface.test.ts index 430d038..3be8ab3 100644 --- a/test-apps/testbed/frontend/RpcInterface.test.ts +++ b/test-apps/testbed/frontend/RpcInterface.test.ts @@ -406,7 +406,7 @@ describe("RpcInterface", () => { const incMinorZ = semver.parse(realVersionZ)!; incMinorZ.inc("minor"); - await (test(incMinor.format(), true, client)); + await (test(incMinor.format(), false, client)); await (test(incMinorZ.format(), false, clientZ)); const incPatch = semver.parse(realVersion)!; @@ -416,7 +416,7 @@ describe("RpcInterface", () => { incPatchZ.inc("patch"); await (test(incPatch.format(), true, client)); - await (test(incPatchZ.format(), true, clientZ)); + await (test(incPatchZ.format(), false, clientZ)); const incPre = semver.parse(realVersion)!; incPre.inc("prerelease"); @@ -442,7 +442,7 @@ describe("RpcInterface", () => { const decMinorZ = semver.parse(realVersionZ)!; --decMinorZ.minor; - await (test(decMinor.format(), false, client)); + await (test(decMinor.format(), true, client)); await (test(decMinorZ.format(), false, clientZ)); const decPatch = semver.parse(realVersion)!; @@ -452,7 +452,7 @@ describe("RpcInterface", () => { --decPatchZ.patch; await (test(decPatch.format(), true, client)); - await (test(decPatchZ.format(), false, clientZ)); + await (test(decPatchZ.format(), true, clientZ)); }); it("should validate all transport methods", async () => { diff --git a/test-apps/testbed/frontend/ShaderBuilder.test.ts b/test-apps/testbed/frontend/ShaderBuilder.test.ts index 2c22534..19a7b9c 100644 --- a/test-apps/testbed/frontend/ShaderBuilder.test.ts +++ b/test-apps/testbed/frontend/ShaderBuilder.test.ts @@ -85,7 +85,7 @@ describe("Test shader compilation", () => { after(() => WebGLTestContext.shutdown()); it.skip("should build and compile a simple shader program", () => { - const builder = new ProgramBuilder(false); + const builder = new ProgramBuilder(); builder.vert.set(VertexShaderComponent.ComputePosition, "return vec4(0.0);"); builder.frag.set(FragmentShaderComponent.ComputeBaseColor, "return vec4(1.0);"); builder.frag.set(FragmentShaderComponent.AssignFragData, "FragColor = baseColor;"); diff --git a/test-apps/testbed/frontend/Technique.test.ts b/test-apps/testbed/frontend/Technique.test.ts index 280bd25..a533afd 100644 --- a/test-apps/testbed/frontend/Technique.test.ts +++ b/test-apps/testbed/frontend/Technique.test.ts @@ -21,7 +21,7 @@ import { } from "@bentley/imodeljs-frontend/lib/webgl"; function createPurpleQuadTechnique(target: Target): TechniqueId { - const builder = new ProgramBuilder(false); + const builder = new ProgramBuilder(); builder.vert.set(VertexShaderComponent.ComputePosition, "return rawPos;"); builder.frag.set(FragmentShaderComponent.ComputeBaseColor, "return vec4(1.0, 0.0, 0.5, 1.0);"); builder.frag.set(FragmentShaderComponent.AssignFragData, "FragColor = baseColor;"); @@ -85,7 +85,7 @@ describe("Technique tests", () => { // NEEDS_WORK: Paul to look into making a fix for Linux failures // Clipping planes add an extra varying vec4 which was causing surface shaders to exceed max varying vectors (capped at min guaranteed by spec, primarily because iOS). // Verify this no longer occurs. - it.skip("should successfully compile surface shader with clipping planes", () => { + it("should successfully compile surface shader with clipping planes", () => { if (!WebGLTestContext.isInitialized) return; @@ -98,4 +98,29 @@ describe("Technique tests", () => { const prog = tech.getShader(flags); expect(prog.compile()).to.be.true; }); + + it("should successfully compile animation shaders", () => { + if (!WebGLTestContext.isInitialized) + return; + + const flags = new TechniqueFlags(); + flags.setAnimated(true); + let tech = System.instance.techniques.getTechnique(TechniqueId.Edge); + let prog = tech.getShader(flags); + expect(prog.compile()).to.be.true; + + tech = System.instance.techniques.getTechnique(TechniqueId.Surface); + prog = tech.getShader(flags); + expect(prog.compile()).to.be.true; + + flags.isTranslucent = true; + flags.featureMode = FeatureMode.Overrides; + prog = tech.getShader(flags); + expect(prog.compile()).to.be.true; + + flags.clip.type = ClippingType.Planes; + flags.clip.numberOfPlanes = 6; + prog = tech.getShader(flags); + expect(prog.compile()).to.be.true; + }); }); diff --git a/test-apps/testbed/frontend/TestViewport.ts b/test-apps/testbed/frontend/TestViewport.ts index 6c33263..136278f 100644 --- a/test-apps/testbed/frontend/TestViewport.ts +++ b/test-apps/testbed/frontend/TestViewport.ts @@ -104,7 +104,7 @@ export class ColorSet extends SortedArray { } // Read depth, geometry type, and feature for each pixel. Return only the unique ones. -function readUniquePixelData(vp: Viewport, readRect?: ViewRect): PixelDataSet { +function readUniquePixelData(vp: Viewport, readRect?: ViewRect, excludeNonLocatable = false): PixelDataSet { const rect = undefined !== readRect ? readRect : vp.viewRect; const set = new PixelDataSet(); vp.readPixels(rect, Pixel.Selector.All, (pixels: Pixel.Buffer | undefined) => { @@ -114,7 +114,7 @@ function readUniquePixelData(vp: Viewport, readRect?: ViewRect): PixelDataSet { for (let x = rect.left; x < rect.right; x++) for (let y = rect.top; y < rect.bottom; y++) set.insert(pixels.getPixel(x, y)); - }); + }, excludeNonLocatable); return set; } @@ -132,12 +132,12 @@ function readUniqueColors(vp: Viewport, readRect?: ViewRect): ColorSet { return colors; } -function readPixel(vp: Viewport, x: number, y: number): Pixel.Data { +function readPixel(vp: Viewport, x: number, y: number, excludeNonLocatable = false): Pixel.Data { let pixel = new Pixel.Data(); vp.readPixels(new ViewRect(x, y, x + 1, y + 1), Pixel.Selector.All, (pixels: Pixel.Buffer | undefined) => { if (undefined !== pixels) pixel = pixels.getPixel(x, y); - }); + }, excludeNonLocatable); return pixel; } @@ -159,7 +159,7 @@ export interface TestableViewport { // Asynchronously draw one frame. In the case of an on-screen viewport, this blocks until the next tick of the ViewManager's render loop. drawFrame(): Promise; // Read pixel data within rectangular region and return unique pixels. - readUniquePixelData(readRect?: ViewRect): PixelDataSet; + readUniquePixelData(readRect?: ViewRect, excludeNonLocatable?: boolean): PixelDataSet; // Read pixel colors within rectangular region and return unique colors. readUniqueColors(readRect?: ViewRect): ColorSet; // Return the data associated with the pixel at (x, y). @@ -171,7 +171,7 @@ export interface TestableViewport { } class OffScreenTestViewport extends OffScreenViewport implements TestableViewport { - public readUniquePixelData(readRect?: ViewRect): PixelDataSet { return readUniquePixelData(this, readRect); } + public readUniquePixelData(readRect?: ViewRect, excludeNonLocatable = false): PixelDataSet { return readUniquePixelData(this, readRect, excludeNonLocatable); } public readUniqueColors(readRect?: ViewRect): ColorSet { return readUniqueColors(this, readRect); } public readPixel(x: number, y: number): Pixel.Data { return readPixel(this, x, y); } public readColor(x: number, y: number): Color { return readColor(this, x, y); } @@ -210,7 +210,7 @@ class OffScreenTestViewport extends OffScreenViewport implements TestableViewpor class ScreenTestViewport extends ScreenViewport implements TestableViewport { private _frameRendered: boolean = false; - public readUniquePixelData(readRect?: ViewRect): PixelDataSet { return readUniquePixelData(this, readRect); } + public readUniquePixelData(readRect?: ViewRect, excludeNonLocatable = false): PixelDataSet { return readUniquePixelData(this, readRect, excludeNonLocatable); } public readUniqueColors(readRect?: ViewRect): ColorSet { return readUniqueColors(this, readRect); } public readPixel(x: number, y: number): Pixel.Data { return readPixel(this, x, y); } public readColor(x: number, y: number): Color { return readColor(this, x, y); } @@ -284,8 +284,7 @@ export async function createOnScreenTestViewport(viewId: Id64String, imodel: IMo return ScreenTestViewport.createTestViewport(viewId, imodel, width, height); } -// Execute a test against both an off-screen and on-screen viewport. -export async function testViewports(viewId: Id64String, imodel: IModelConnection, width: number, height: number, test: (vp: TestViewport) => Promise): Promise { +export async function testOnScreenViewport(viewId: Id64String, imodel: IModelConnection, width: number, height: number, test: (vp: TestViewport) => Promise): Promise { if (!WebGLTestContext.isInitialized) return Promise.resolve(); @@ -299,6 +298,16 @@ export async function testViewports(viewId: Id64String, imodel: IModelConnection onscreen.dispose(); } + return Promise.resolve(); +} + +// Execute a test against both an off-screen and on-screen viewport. +export async function testViewports(viewId: Id64String, imodel: IModelConnection, width: number, height: number, test: (vp: TestViewport) => Promise): Promise { + if (!WebGLTestContext.isInitialized) + return Promise.resolve(); + + await testOnScreenViewport(viewId, imodel, width, height, test); + const offscreen = await createOffScreenTestViewport(viewId, imodel, width, height); try { await test(offscreen); diff --git a/test-apps/testbed/frontend/TileIO.data.1.1.ts b/test-apps/testbed/frontend/TileIO.data.1.1.ts new file mode 100644 index 0000000..db8730f --- /dev/null +++ b/test-apps/testbed/frontend/TileIO.data.1.1.ts @@ -0,0 +1,1871 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { IModelTileIO } from "@bentley/imodeljs-frontend/lib/tile"; + +// Binary tile data produced using version 1.1 of the tile format. +export const TILE_DATA_1_1 = { + versionMajor: 1, + versionMinor: 1, + headerLength: 84, + rectangle: { + flags: IModelTileIO.Flags.None, + bytes: new Uint8Array([ + 0x69, 0x4d, 0x64, 0x6c, 0x01, 0x00, 0x01, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x4b, 0x37, 0x89, 0x41, + 0x00, 0x04, 0xc0, 0xc7, 0x4b, 0x37, 0x89, 0x41, 0x00, 0x14, 0xc0, 0x00, 0x00, 0xbd, 0x9a, 0xf2, 0xd7, 0x5a, 0xbe, 0x39, 0xb4, + 0xc8, 0x76, 0xbe, 0xff, 0x03, 0x40, 0x39, 0xb4, 0xc8, 0x76, 0xbe, 0xff, 0x13, 0x40, 0x00, 0x00, 0xbd, 0x9a, 0xf2, 0xd7, 0x5a, + 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0x0a, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, 0x02, 0x00, 0x00, 0x00, 0x60, 0x0a, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, + 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x50, 0x6f, 0x6c, 0x79, + 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, + 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, + 0x3a, 0x34, 0x39, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, + 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, + 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x36, 0x34, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, + 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, + 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, + 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x35, 0x36, 0x7d, 0x2c, + 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, + 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, + 0x37, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x32, 0x35, 0x32, 0x7d, 0x2c, + 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, + 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x37, 0x32, 0x2c, 0x22, 0x62, + 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x38, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, + 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x38, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x22, 0x3a, 0x36, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, + 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, + 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x33, 0x39, 0x36, 0x7d, 0x2c, + 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, + 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, + 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, + 0x74, 0x68, 0x22, 0x3a, 0x37, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x33, + 0x32, 0x34, 0x7d, 0x7d, 0x2c, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, + 0x3a, 0x5b, 0x22, 0x4b, 0x48, 0x52, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, + 0x57, 0x45, 0x42, 0x33, 0x44, 0x5f, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, + 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x63, 0x61, 0x74, + 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x37, 0x22, 0x2c, 0x22, 0x63, 0x6c, 0x61, 0x73, + 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, 0x38, + 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, + 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, + 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, + 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, + 0x31, 0x2c, 0x22, 0x73, 0x75, 0x62, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, + 0x31, 0x38, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x7d, 0x7d, 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, + 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, + 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x65, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, + 0x70, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, + 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, + 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, + 0x73, 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, + 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, + 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x70, 0x72, + 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, + 0x65, 0x73, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x65, 0x6e, 0x64, + 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, + 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x22, 0x7d, 0x7d, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x74, 0x72, 0x75, 0x65, + 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x30, 0x22, 0x2c, 0x22, 0x73, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, + 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x7d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x2c, + 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, + 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x44, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, + 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x68, + 0x61, 0x73, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, + 0x2c, 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, + 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x37, 0x2e, 0x36, 0x33, + 0x30, 0x36, 0x35, 0x35, 0x33, 0x37, 0x34, 0x39, 0x39, 0x30, 0x34, 0x36, 0x31, 0x31, 0x65, 0x2d, 0x30, 0x35, 0x2c, 0x30, 0x2e, + 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x31, 0x35, + 0x32, 0x36, 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, 0x32, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, + 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, 0x31, 0x33, 0x31, 0x30, 0x37, + 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x2d, 0x32, 0x2e, 0x35, + 0x30, 0x30, 0x33, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x36, 0x2c, 0x2d, 0x35, 0x2e, 0x30, 0x30, + 0x30, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x31, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, + 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, + 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x32, 0x2e, 0x35, 0x30, 0x30, 0x33, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x36, 0x2c, 0x35, 0x2e, 0x30, 0x30, 0x30, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x31, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, + 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x32, 0x2e, 0x35, 0x30, 0x30, 0x33, 0x37, 0x34, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x36, 0x2c, 0x2d, 0x35, 0x2e, 0x30, 0x30, 0x30, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x31, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, + 0x7d, 0x2c, 0x22, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, 0x38, + 0x30, 0x2c, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x6e, 0x6f, + 0x64, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4e, 0x6f, 0x64, 0x65, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x22, 0x4d, 0x65, + 0x73, 0x68, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x22, 0x64, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x6e, 0x6f, 0x64, + 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x5d, 0x7d, 0x7d, 0x7d, 0x0a, 0x20, + 0x03, 0x00, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xf8, 0xff, 0x03, 0x00, 0xfe, + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xf8, 0xff, 0xf8, 0xff, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x03, 0x00, 0xf8, 0xff, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, + 0xcc, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, + 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x4e, + 0x02, 0x00, 0x00, 0x1e, 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x36, 0x01, + 0x00, 0x00, 0x4e, 0x03, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x02, 0x00, + 0x00, 0x36, 0x02, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x1e, 0x03, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x03, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x4e, 0x00, 0x00, 0xf0, 0x41, 0xfd, 0xff, 0x0b, 0x42, 0x00, 0x00, 0xf0, 0x41, + 0x00, 0x00, 0xf0, 0x41, 0xfd, 0xff, 0x0b, 0x42, 0xfd, 0xff, 0x0b, 0x42, 0xfd, 0xff, 0x0b, 0x42, 0xf6, 0xff, 0x33, 0x42, 0xfd, + 0xff, 0x0b, 0x42, 0xfd, 0xff, 0x0b, 0x42, 0xf6, 0xff, 0x33, 0x42, 0xf6, 0xff, 0x33, 0x42, 0xf6, 0xff, 0x33, 0x42, 0xf2, 0xff, + 0x47, 0x42, 0xf6, 0xff, 0x33, 0x42, 0xf6, 0xff, 0x33, 0x42, 0xf2, 0xff, 0x47, 0x42, 0xf2, 0xff, 0x47, 0x42, 0xf2, 0xff, 0x47, + 0x42, 0xeb, 0xff, 0x6f, 0x42, 0xf2, 0xff, 0x47, 0x42, 0xf2, 0xff, 0x47, 0x42, 0xeb, 0xff, 0x6f, 0x42, 0xeb, 0xff, 0x6f, 0x42, + ]), + }, + + lineString: { + flags: IModelTileIO.Flags.None, + bytes: new Uint8Array([ + 0x69, 0x4d, 0x64, 0x6c, 0x01, 0x00, 0x01, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xf1, 0xd2, 0x4d, 0x62, + 0x00, 0x1e, 0xc0, 0xc7, 0x4b, 0x37, 0x89, 0x41, 0x00, 0x24, 0xc0, 0x00, 0x00, 0xbd, 0x9a, 0xf2, 0xd7, 0x5a, 0xbe, 0x55, 0x0e, + 0x2d, 0xb2, 0x9d, 0xff, 0x1d, 0x40, 0x39, 0xb4, 0xc8, 0x76, 0xbe, 0xff, 0x23, 0x40, 0x00, 0x00, 0xbd, 0x9a, 0xf2, 0xd7, 0x5a, + 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, 0x02, 0x00, 0x00, 0x00, 0x8c, 0x0c, + 0x00, 0x00, 0xf0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, + 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x22, 0x3a, 0x7b, 0x22, + 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, + 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x34, 0x35, 0x36, 0x2c, 0x22, 0x62, 0x79, + 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x32, 0x31, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x56, 0x65, + 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, + 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, + 0x22, 0x3a, 0x37, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x30, 0x7d, 0x2c, + 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, + 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, + 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x34, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x22, 0x3a, 0x37, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, + 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, + 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x34, 0x35, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x22, 0x3a, 0x37, 0x36, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, + 0x33, 0x34, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x34, 0x31, 0x36, 0x7d, + 0x7d, 0x2c, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, + 0x4b, 0x48, 0x52, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x57, 0x45, 0x42, + 0x33, 0x44, 0x5f, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, + 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, + 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x37, 0x22, 0x2c, 0x22, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x3a, + 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x35, 0x33, 0x35, 0x2c, 0x22, + 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, + 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, + 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x35, 0x33, 0x35, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, + 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x2c, 0x22, + 0x73, 0x75, 0x62, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x38, 0x22, + 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x7d, 0x7d, 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, + 0x69, 0x76, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, + 0x62, 0x76, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, + 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, + 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, + 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x22, 0x2c, 0x22, + 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x76, 0x65, + 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, + 0x3a, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, + 0x36, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x44, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, 0x56, + 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x33, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, + 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x32, + 0x38, 0x39, 0x31, 0x39, 0x36, 0x36, 0x31, 0x32, 0x34, 0x39, 0x37, 0x31, 0x33, 0x38, 0x37, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, + 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x33, 0x30, 0x35, 0x32, 0x32, + 0x36, 0x32, 0x31, 0x34, 0x39, 0x39, 0x39, 0x36, 0x31, 0x38, 0x34, 0x34, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, + 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, + 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, + 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, + 0x30, 0x37, 0x35, 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, + 0x22, 0x3a, 0x5b, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, + 0x2c, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x30, + 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, + 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x7d, 0x2c, 0x22, + 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x35, 0x33, 0x35, 0x2c, 0x22, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x38, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, + 0x22, 0x3a, 0x7b, 0x22, 0x4e, 0x6f, 0x64, 0x65, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, + 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, + 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, + 0x3a, 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x5d, 0x7d, 0x7d, 0x7d, 0x0a, 0x20, 0x03, 0x00, 0xf8, + 0xff, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x55, 0x55, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0xf8, 0xff, 0xfe, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf8, 0xff, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xfe, 0x7f, 0xfe, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x39, + 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, + 0x00, 0x90, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x0e, 0x02, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x36, + 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x09, 0x02, 0x00, 0x00, 0x09, 0x01, + 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, + 0x00, 0x60, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, + 0x0e, 0x03, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x39, + 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x02, + 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x51, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, + 0x00, 0x90, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, + 0x0e, 0x04, 0x00, 0x00, 0x21, 0x03, 0x00, 0x00, 0x39, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x39, + 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x09, 0x03, + 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x51, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x0c, 0x04, 0x00, + 0x00, 0x60, 0x04, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0f, 0x04, 0x00, 0x00, + 0x0e, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0e, + 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x21, 0x04, + 0x00, 0x00, 0x33, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x33, 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, + 0x00, 0x60, 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x09, 0x05, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, + 0x4b, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x0c, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0e, + 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0f, 0x05, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xe5, + 0xff, 0x9f, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0xff, 0x9f, 0x41, 0xe5, 0xff, 0x9f, 0x41, 0x00, 0x00, + 0x00, 0x00, 0xe5, 0xff, 0x9f, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0xff, 0x9f, 0x41, 0xe5, 0xff, 0x9f, + 0x41, 0xe5, 0xff, 0x9f, 0x41, 0xe5, 0xff, 0x9f, 0x41, 0xe5, 0xff, 0x9f, 0x41, 0xe5, 0xff, 0x9f, 0x41, 0xe5, 0xff, 0x9f, 0x41, + 0xe5, 0xff, 0x9f, 0x41, 0xe5, 0xff, 0x9f, 0x41, 0xe5, 0xff, 0x9f, 0x41, 0xe5, 0xff, 0x9f, 0x41, 0xe5, 0xff, 0x9f, 0x41, 0x85, + 0x71, 0xf9, 0x41, 0xe5, 0xff, 0x9f, 0x41, 0xe5, 0xff, 0x9f, 0x41, 0x85, 0x71, 0xf9, 0x41, 0x85, 0x71, 0xf9, 0x41, 0xe5, 0xff, + 0x9f, 0x41, 0x85, 0x71, 0xf9, 0x41, 0xe5, 0xff, 0x9f, 0x41, 0xe5, 0xff, 0x9f, 0x41, 0x85, 0x71, 0xf9, 0x41, 0x85, 0x71, 0xf9, + 0x41, 0xe5, 0xff, 0x9f, 0x41, 0xe5, 0xff, 0x9f, 0x41, 0xe5, 0xff, 0x9f, 0x41, 0xe5, 0xff, 0x9f, 0x41, 0xe5, 0xff, 0x9f, 0x41, + 0xe5, 0xff, 0x9f, 0x41, 0xe5, 0xff, 0x9f, 0x41, 0xe5, 0xff, 0x9f, 0x41, 0xe5, 0xff, 0x9f, 0x41, 0x85, 0x71, 0xf9, 0x41, 0x94, + 0xb8, 0x24, 0x42, 0x85, 0x71, 0xf9, 0x41, 0x85, 0x71, 0xf9, 0x41, 0x94, 0xb8, 0x24, 0x42, 0x94, 0xb8, 0x24, 0x42, 0x85, 0x71, + 0xf9, 0x41, 0x94, 0xb8, 0x24, 0x42, 0x85, 0x71, 0xf9, 0x41, 0x85, 0x71, 0xf9, 0x41, 0x94, 0xb8, 0x24, 0x42, 0x94, 0xb8, 0x24, + 0x42, 0x94, 0xb8, 0x24, 0x42, 0x94, 0xb8, 0x24, 0x42, 0x94, 0xb8, 0x24, 0x42, 0x94, 0xb8, 0x24, 0x42, 0x94, 0xb8, 0x24, 0x42, + 0x94, 0xb8, 0x24, 0x42, 0x94, 0xb8, 0x24, 0x42, 0x94, 0xb8, 0x24, 0x42, 0x94, 0xb8, 0x24, 0x42, 0x94, 0xb8, 0x24, 0x42, 0xd1, + 0x29, 0x7e, 0x42, 0x94, 0xb8, 0x24, 0x42, 0x94, 0xb8, 0x24, 0x42, 0xd1, 0x29, 0x7e, 0x42, 0xd1, 0x29, 0x7e, 0x42, 0x94, 0xb8, + 0x24, 0x42, 0xd1, 0x29, 0x7e, 0x42, 0x94, 0xb8, 0x24, 0x42, 0x94, 0xb8, 0x24, 0x42, 0xd1, 0x29, 0x7e, 0x42, 0xd1, 0x29, 0x7e, + 0x42, 0x94, 0xb8, 0x24, 0x42, 0x94, 0xb8, 0x24, 0x42, 0x94, 0xb8, 0x24, 0x42, 0x94, 0xb8, 0x24, 0x42, 0x94, 0xb8, 0x24, 0x42, + 0x94, 0xb8, 0x24, 0x42, 0x94, 0xb8, 0x24, 0x42, 0x94, 0xb8, 0x24, 0x42, 0x94, 0xb8, 0x24, 0x42, 0xd1, 0x29, 0x7e, 0x42, 0xd1, + 0x29, 0x7e, 0x42, 0xd1, 0x29, 0x7e, 0x42, 0xd1, 0x29, 0x7e, 0x42, 0xd1, 0x29, 0x7e, 0x42, 0xd1, 0x29, 0x7e, 0x42, 0xd1, 0x29, + 0x7e, 0x42, 0xd1, 0x29, 0x7e, 0x42, 0xd1, 0x29, 0x7e, 0x42, 0xd1, 0x29, 0x7e, 0x42, 0xf9, 0x14, 0x93, 0x42, 0xd1, 0x29, 0x7e, + 0x42, 0xd1, 0x29, 0x7e, 0x42, 0xf9, 0x14, 0x93, 0x42, 0xf9, 0x14, 0x93, 0x42, 0xd1, 0x29, 0x7e, 0x42, 0xf9, 0x14, 0x93, 0x42, + 0xd1, 0x29, 0x7e, 0x42, 0xd1, 0x29, 0x7e, 0x42, 0xf9, 0x14, 0x93, 0x42, 0xf9, 0x14, 0x93, 0x42, 0xd1, 0x29, 0x7e, 0x42, 0xd1, + 0x29, 0x7e, 0x42, 0xd1, 0x29, 0x7e, 0x42, 0xd1, 0x29, 0x7e, 0x42, 0xd1, 0x29, 0x7e, 0x42, 0xd1, 0x29, 0x7e, 0x42, 0xd1, 0x29, + 0x7e, 0x42, 0xd1, 0x29, 0x7e, 0x42, 0xd1, 0x29, 0x7e, 0x42, + ]), + }, + + lineStrings: { + flags: IModelTileIO.Flags.None, + bytes: new Uint8Array([ + 0x69, 0x4d, 0x64, 0x6c, 0x01, 0x00, 0x01, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xf1, 0xd2, 0x4d, 0x62, + 0x00, 0x1e, 0xc0, 0xab, 0xf1, 0xd2, 0x4d, 0x62, 0x00, 0x3e, 0xc0, 0x00, 0x00, 0xbd, 0x9a, 0xf2, 0xd7, 0x5a, 0xbe, 0x55, 0x0e, + 0x2d, 0xb2, 0x9d, 0xff, 0x1d, 0x40, 0x55, 0x0e, 0x2d, 0xb2, 0x9d, 0xff, 0x3d, 0x40, 0x00, 0x00, 0xbd, 0x9a, 0xf2, 0xd7, 0x5a, + 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x1e, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, 0x02, 0x00, 0x00, + 0x00, 0x68, 0x1e, 0x00, 0x00, 0xbc, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, + 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x22, + 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, + 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x34, 0x35, 0x36, 0x2c, + 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x32, 0x31, 0x36, 0x7d, 0x2c, 0x22, 0x62, + 0x76, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, + 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x31, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x22, 0x3a, 0x34, 0x31, 0x30, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x3a, + 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, + 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x37, 0x32, 0x2c, 0x22, 0x62, + 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, + 0x65, 0x78, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, + 0x31, 0x35, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x36, 0x37, 0x32, + 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, + 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, + 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x34, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x37, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, + 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, + 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x36, 0x38, 0x34, + 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x38, 0x32, 0x34, 0x7d, 0x2c, 0x22, + 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, + 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x34, + 0x35, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x37, 0x36, 0x30, 0x7d, 0x2c, + 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, + 0x39, 0x31, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x33, 0x31, 0x39, 0x32, + 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, + 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, + 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x34, 0x32, 0x2c, 0x22, 0x62, 0x79, + 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x34, 0x31, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, + 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, + 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x36, 0x38, 0x34, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x22, 0x3a, 0x32, 0x35, 0x30, 0x38, 0x7d, 0x7d, 0x2c, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, + 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4b, 0x48, 0x52, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, + 0x46, 0x22, 0x2c, 0x22, 0x57, 0x45, 0x42, 0x33, 0x44, 0x5f, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x3a, 0x7b, + 0x22, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x37, 0x22, 0x2c, 0x22, + 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, + 0x31, 0x36, 0x37, 0x31, 0x31, 0x39, 0x33, 0x35, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3a, + 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, + 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x31, 0x36, 0x37, 0x31, 0x31, + 0x39, 0x33, 0x35, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6c, + 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x2c, 0x22, 0x73, 0x75, 0x62, 0x43, 0x61, 0x74, 0x65, 0x67, + 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x38, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, + 0x31, 0x7d, 0x2c, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x63, 0x61, 0x74, 0x65, + 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x37, 0x22, 0x2c, 0x22, 0x63, 0x6c, 0x61, 0x73, 0x73, + 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x31, 0x35, 0x37, 0x39, 0x30, + 0x30, 0x38, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, + 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, + 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x31, 0x35, 0x37, 0x39, 0x30, 0x30, 0x38, 0x30, 0x2c, 0x22, + 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x34, 0x31, 0x37, 0x37, 0x30, 0x36, 0x36, 0x32, 0x33, + 0x32, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x2c, 0x22, 0x73, 0x75, 0x62, 0x43, + 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x38, 0x22, 0x2c, 0x22, 0x74, 0x79, + 0x70, 0x65, 0x22, 0x3a, 0x31, 0x7d, 0x7d, 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x65, + 0x73, 0x68, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, + 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x44, 0x69, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, + 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, + 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, 0x61, + 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, + 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x30, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, + 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, + 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x36, 0x2c, 0x22, 0x66, + 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x44, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, + 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, + 0x78, 0x22, 0x3a, 0x33, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, + 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x32, 0x38, 0x39, 0x31, 0x39, + 0x36, 0x36, 0x31, 0x32, 0x34, 0x39, 0x37, 0x31, 0x33, 0x38, 0x37, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, + 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x39, 0x31, 0x35, 0x36, 0x37, 0x38, 0x36, 0x34, 0x34, + 0x39, 0x39, 0x38, 0x38, 0x35, 0x35, 0x35, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, + 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, + 0x38, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x2d, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, + 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x37, + 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x33, 0x30, 0x2e, + 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, + 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, + 0x3a, 0x5b, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, + 0x2c, 0x2d, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, + 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x7d, 0x2c, 0x22, 0x75, 0x6e, 0x69, 0x66, + 0x6f, 0x72, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x31, 0x36, 0x37, 0x31, 0x31, 0x39, 0x33, 0x35, 0x2c, 0x22, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x38, 0x7d, 0x7d, 0x2c, 0x7b, 0x22, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x31, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x22, 0x2c, 0x22, 0x69, + 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, + 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x31, 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, + 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, + 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, + 0x31, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x70, + 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, + 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, + 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x31, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x22, 0x3a, 0x31, 0x32, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, + 0x79, 0x70, 0x65, 0x22, 0x3a, 0x32, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, + 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, + 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x33, 0x2c, + 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, + 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x32, 0x38, 0x39, 0x31, 0x39, 0x36, 0x36, 0x31, 0x32, 0x34, + 0x39, 0x37, 0x31, 0x33, 0x38, 0x37, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, + 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x39, 0x31, 0x35, 0x36, 0x37, 0x38, 0x36, 0x34, 0x34, 0x39, 0x39, 0x38, 0x38, 0x35, + 0x35, 0x35, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x31, + 0x2e, 0x35, 0x32, 0x36, 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, 0x2d, 0x30, 0x38, + 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x32, 0x2c, 0x2d, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x37, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, + 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, + 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x34, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, + 0x35, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x37, 0x2e, + 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x2d, 0x33, 0x30, 0x2e, + 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, + 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x7d, 0x2c, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x38, + 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4e, 0x6f, 0x64, 0x65, 0x5f, + 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x7d, 0x2c, 0x22, 0x73, + 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, + 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, + 0x6e, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, + 0x64, 0x65, 0x22, 0x5d, 0x7d, 0x7d, 0x7d, 0x0a, 0x20, 0x20, 0x03, 0x00, 0x55, 0x55, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0xac, 0x2a, 0xfe, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x03, 0x00, 0xfe, + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xac, 0x2a, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x39, 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x51, 0x00, + 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x0d, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, + 0x36, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x60, + 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x09, 0x02, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x4e, 0x02, + 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, + 0x00, 0x0d, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, + 0x39, 0x03, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x39, 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x60, + 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x51, 0x02, + 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, + 0x00, 0x0d, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x21, 0x03, 0x00, 0x00, + 0x39, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x60, + 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x51, 0x04, + 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x0c, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0e, 0x04, 0x00, + 0x00, 0x0d, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0f, 0x04, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, + 0x0d, 0x03, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x90, + 0x03, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x21, 0x04, 0x00, 0x00, 0x33, 0x05, 0x00, 0x00, 0x60, 0x05, + 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x33, 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, + 0x00, 0x09, 0x05, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x4b, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, + 0x0d, 0x05, 0x00, 0x00, 0x0c, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x60, + 0x05, 0x00, 0x00, 0x0f, 0x05, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0xa0, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0xa0, 0x41, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, + 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, + 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x25, 0x72, 0xf9, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, + 0x00, 0xa0, 0x41, 0x25, 0x72, 0xf9, 0x41, 0x25, 0x72, 0xf9, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x25, 0x72, 0xf9, 0x41, 0x85, 0x00, + 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x25, 0x72, 0xf9, 0x41, 0x25, 0x72, 0xf9, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, + 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, + 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x25, 0x72, 0xf9, 0x41, 0x34, 0xb9, 0x24, 0x42, 0x25, 0x72, 0xf9, 0x41, 0x25, + 0x72, 0xf9, 0x41, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x25, 0x72, 0xf9, 0x41, 0x34, 0xb9, 0x24, 0x42, 0x25, 0x72, + 0xf9, 0x41, 0x25, 0x72, 0xf9, 0x41, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, + 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, + 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, + 0xb9, 0x24, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0x34, 0xb9, 0x24, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0x34, 0xb9, + 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, + 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, + 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, + 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, + 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0x6d, 0x15, 0x93, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0x6d, 0x15, 0x93, + 0x42, 0x6d, 0x15, 0x93, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0x6d, 0x15, 0x93, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, + 0x6d, 0x15, 0x93, 0x42, 0x6d, 0x15, 0x93, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, + 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, + 0x7e, 0x42, 0x03, 0x00, 0xa7, 0xaa, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x55, 0x55, 0xfe, 0x7f, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x55, 0x55, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x55, 0x55, 0xa7, 0xaa, + 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x55, 0x55, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf8, + 0xff, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0xf8, 0xff, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0xa7, 0xaa, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x55, 0x55, 0x50, 0xd5, 0xfe, 0x7f, 0x01, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x55, 0x55, 0xf8, 0xff, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xa7, 0xaa, + 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x50, 0xd5, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0xf0, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x09, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x09, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, + 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0b, 0x00, + 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, + 0x60, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x90, + 0x01, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x90, 0x00, + 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, + 0x00, 0x90, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, + 0x60, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x90, + 0x02, 0x00, 0x00, 0x09, 0x02, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x60, 0x02, + 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, + 0x00, 0x60, 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, + 0x60, 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x39, 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x90, + 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x51, 0x02, 0x00, 0x00, 0x90, 0x02, + 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, + 0x00, 0x90, 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x21, 0x03, 0x00, 0x00, 0x39, 0x04, 0x00, 0x00, + 0x60, 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x90, + 0x04, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x51, 0x04, 0x00, 0x00, 0x60, 0x04, + 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x0c, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, + 0x00, 0x60, 0x04, 0x00, 0x00, 0x0f, 0x04, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, + 0x0c, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0f, + 0x03, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x21, 0x04, 0x00, 0x00, 0x33, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x60, 0x04, + 0x00, 0x00, 0x33, 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x09, 0x05, 0x00, + 0x00, 0x09, 0x04, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x4b, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, + 0x0c, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0f, + 0x05, 0x00, 0x00, 0x0e, 0x07, 0x00, 0x00, 0x1b, 0x06, 0x00, 0x00, 0x39, 0x07, 0x00, 0x00, 0x60, 0x07, 0x00, 0x00, 0x60, 0x06, + 0x00, 0x00, 0x39, 0x06, 0x00, 0x00, 0x90, 0x07, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x90, 0x07, 0x00, 0x00, 0x03, 0x07, 0x00, + 0x00, 0x03, 0x06, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x51, 0x06, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x0d, 0x06, 0x00, 0x00, + 0x0c, 0x06, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x0e, 0x06, 0x00, 0x00, 0x0d, 0x06, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x0f, + 0x06, 0x00, 0x00, 0x0e, 0x08, 0x00, 0x00, 0x21, 0x07, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x60, 0x07, + 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x60, 0x07, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x09, 0x08, 0x00, + 0x00, 0x09, 0x07, 0x00, 0x00, 0x90, 0x07, 0x00, 0x00, 0x4e, 0x08, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x0d, 0x08, 0x00, 0x00, + 0x0c, 0x08, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x0e, 0x08, 0x00, 0x00, 0x0d, 0x08, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x0f, + 0x08, 0x00, 0x00, 0x0e, 0x09, 0x00, 0x00, 0x1e, 0x08, 0x00, 0x00, 0x39, 0x09, 0x00, 0x00, 0x60, 0x09, 0x00, 0x00, 0x60, 0x08, + 0x00, 0x00, 0x39, 0x08, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x06, 0x09, 0x00, + 0x00, 0x06, 0x08, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x51, 0x08, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x0d, 0x08, 0x00, 0x00, + 0x0c, 0x08, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x0e, 0x08, 0x00, 0x00, 0x0d, 0x08, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x0f, + 0x08, 0x00, 0x00, 0x0e, 0x0a, 0x00, 0x00, 0x21, 0x09, 0x00, 0x00, 0x39, 0x0a, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x60, 0x09, + 0x00, 0x00, 0x39, 0x09, 0x00, 0x00, 0x90, 0x0a, 0x00, 0x00, 0x60, 0x09, 0x00, 0x00, 0x90, 0x0a, 0x00, 0x00, 0x09, 0x0a, 0x00, + 0x00, 0x09, 0x09, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x51, 0x0a, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x0d, 0x0a, 0x00, 0x00, + 0x0c, 0x0a, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x0e, 0x0a, 0x00, 0x00, 0x0d, 0x0a, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x0f, + 0x0a, 0x00, 0x00, 0x0e, 0x09, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x0d, 0x09, 0x00, 0x00, 0x0c, 0x09, 0x00, 0x00, 0x90, 0x09, + 0x00, 0x00, 0x0e, 0x09, 0x00, 0x00, 0x0d, 0x09, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x0f, 0x09, 0x00, 0x00, 0x0e, 0x0b, 0x00, + 0x00, 0x21, 0x0a, 0x00, 0x00, 0x33, 0x0b, 0x00, 0x00, 0x60, 0x0b, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x33, 0x0a, 0x00, 0x00, + 0x90, 0x0b, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x90, 0x0b, 0x00, 0x00, 0x09, 0x0b, 0x00, 0x00, 0x09, 0x0a, 0x00, 0x00, 0x90, + 0x0a, 0x00, 0x00, 0x4b, 0x0b, 0x00, 0x00, 0x60, 0x0b, 0x00, 0x00, 0x0d, 0x0b, 0x00, 0x00, 0x0c, 0x0b, 0x00, 0x00, 0x60, 0x0b, + 0x00, 0x00, 0x0e, 0x0b, 0x00, 0x00, 0x0d, 0x0b, 0x00, 0x00, 0x60, 0x0b, 0x00, 0x00, 0x0f, 0x0b, 0x00, 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0x85, 0x00, 0xa0, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, + 0x41, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0xa0, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0xa0, 0x41, + 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, + 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, + 0xa0, 0x41, 0x25, 0x72, 0xf9, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x25, 0x72, 0xf9, 0x41, 0x25, 0x72, 0xf9, + 0x41, 0x85, 0x00, 0xa0, 0x41, 0x25, 0x72, 0xf9, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x25, 0x72, 0xf9, 0x41, + 0x25, 0x72, 0xf9, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, + 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x85, 0x00, 0xa0, 0x41, 0x25, 0x72, + 0xf9, 0x41, 0x34, 0xb9, 0x24, 0x42, 0x25, 0x72, 0xf9, 0x41, 0x25, 0x72, 0xf9, 0x41, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, + 0x42, 0x25, 0x72, 0xf9, 0x41, 0x34, 0xb9, 0x24, 0x42, 0x25, 0x72, 0xf9, 0x41, 0x25, 0x72, 0xf9, 0x41, 0x34, 0xb9, 0x24, 0x42, + 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, + 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, + 0x24, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, + 0x42, 0x34, 0xb9, 0x24, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0xb9, 0x2a, 0x7e, 0x42, + 0xb9, 0x2a, 0x7e, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, + 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0x34, 0xb9, 0x24, 0x42, 0xb9, 0x2a, + 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, + 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0x6d, 0x15, 0x93, 0x42, + 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0x6d, 0x15, 0x93, 0x42, 0x6d, 0x15, 0x93, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0x6d, + 0x15, 0x93, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0x6d, 0x15, 0x93, 0x42, 0x6d, 0x15, 0x93, 0x42, 0xb9, 0x2a, + 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, + 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0xb9, 0x2a, 0x7e, 0x42, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xfe, 0x9f, 0x41, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xfe, 0x9f, 0x41, 0xa5, 0xfe, 0x9f, 0x41, 0x00, 0x00, 0x00, 0x00, 0xa5, + 0xfe, 0x9f, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xfe, 0x9f, 0x41, 0xa5, 0xfe, 0x9f, 0x41, 0xa5, 0xfe, + 0x9f, 0x41, 0xa5, 0xfe, 0x9f, 0x41, 0xa5, 0xfe, 0x9f, 0x41, 0xa5, 0xfe, 0x9f, 0x41, 0xa5, 0xfe, 0x9f, 0x41, 0xa5, 0xfe, 0x9f, + 0x41, 0xa5, 0xfe, 0x9f, 0x41, 0xa5, 0xfe, 0x9f, 0x41, 0xa5, 0xfe, 0x9f, 0x41, 0xa5, 0xfe, 0x9f, 0x41, 0x45, 0x70, 0xf9, 0x41, + 0xa5, 0xfe, 0x9f, 0x41, 0xa5, 0xfe, 0x9f, 0x41, 0x45, 0x70, 0xf9, 0x41, 0x45, 0x70, 0xf9, 0x41, 0xa5, 0xfe, 0x9f, 0x41, 0x45, + 0x70, 0xf9, 0x41, 0xa5, 0xfe, 0x9f, 0x41, 0xa5, 0xfe, 0x9f, 0x41, 0x45, 0x70, 0xf9, 0x41, 0x45, 0x70, 0xf9, 0x41, 0xa5, 0xfe, + 0x9f, 0x41, 0xa5, 0xfe, 0x9f, 0x41, 0xa5, 0xfe, 0x9f, 0x41, 0xa5, 0xfe, 0x9f, 0x41, 0xa5, 0xfe, 0x9f, 0x41, 0xa5, 0xfe, 0x9f, + 0x41, 0xa5, 0xfe, 0x9f, 0x41, 0xa5, 0xfe, 0x9f, 0x41, 0xa5, 0xfe, 0x9f, 0x41, 0x45, 0x70, 0xf9, 0x41, 0x53, 0xb7, 0x24, 0x42, + 0x45, 0x70, 0xf9, 0x41, 0x45, 0x70, 0xf9, 0x41, 0x53, 0xb7, 0x24, 0x42, 0x53, 0xb7, 0x24, 0x42, 0x45, 0x70, 0xf9, 0x41, 0x53, + 0xb7, 0x24, 0x42, 0x45, 0x70, 0xf9, 0x41, 0x45, 0x70, 0xf9, 0x41, 0x53, 0xb7, 0x24, 0x42, 0x53, 0xb7, 0x24, 0x42, 0x53, 0xb7, + 0x24, 0x42, 0x53, 0xb7, 0x24, 0x42, 0x53, 0xb7, 0x24, 0x42, 0x53, 0xb7, 0x24, 0x42, 0x53, 0xb7, 0x24, 0x42, 0x53, 0xb7, 0x24, + 0x42, 0x53, 0xb7, 0x24, 0x42, 0x53, 0xb7, 0x24, 0x42, 0x53, 0xb7, 0x24, 0x42, 0x53, 0xb7, 0x24, 0x42, 0x02, 0x28, 0x7e, 0x42, + 0x53, 0xb7, 0x24, 0x42, 0x53, 0xb7, 0x24, 0x42, 0x02, 0x28, 0x7e, 0x42, 0x02, 0x28, 0x7e, 0x42, 0x53, 0xb7, 0x24, 0x42, 0x02, + 0x28, 0x7e, 0x42, 0x53, 0xb7, 0x24, 0x42, 0x53, 0xb7, 0x24, 0x42, 0x02, 0x28, 0x7e, 0x42, 0x02, 0x28, 0x7e, 0x42, 0x53, 0xb7, + 0x24, 0x42, 0x53, 0xb7, 0x24, 0x42, 0x53, 0xb7, 0x24, 0x42, 0x53, 0xb7, 0x24, 0x42, 0x53, 0xb7, 0x24, 0x42, 0x53, 0xb7, 0x24, + 0x42, 0x53, 0xb7, 0x24, 0x42, 0x53, 0xb7, 0x24, 0x42, 0x53, 0xb7, 0x24, 0x42, 0x02, 0x28, 0x7e, 0x42, 0x02, 0x28, 0x7e, 0x42, + 0x02, 0x28, 0x7e, 0x42, 0x02, 0x28, 0x7e, 0x42, 0x02, 0x28, 0x7e, 0x42, 0x02, 0x28, 0x7e, 0x42, 0x02, 0x28, 0x7e, 0x42, 0x02, + 0x28, 0x7e, 0x42, 0x02, 0x28, 0x7e, 0x42, 0x02, 0x28, 0x7e, 0x42, 0x12, 0x14, 0x93, 0x42, 0x02, 0x28, 0x7e, 0x42, 0x02, 0x28, + 0x7e, 0x42, 0x12, 0x14, 0x93, 0x42, 0x12, 0x14, 0x93, 0x42, 0x02, 0x28, 0x7e, 0x42, 0x12, 0x14, 0x93, 0x42, 0x02, 0x28, 0x7e, + 0x42, 0x02, 0x28, 0x7e, 0x42, 0x12, 0x14, 0x93, 0x42, 0x12, 0x14, 0x93, 0x42, 0x02, 0x28, 0x7e, 0x42, 0x02, 0x28, 0x7e, 0x42, + 0x02, 0x28, 0x7e, 0x42, 0x02, 0x28, 0x7e, 0x42, 0x02, 0x28, 0x7e, 0x42, 0x02, 0x28, 0x7e, 0x42, 0x02, 0x28, 0x7e, 0x42, 0x02, + 0x28, 0x7e, 0x42, 0x02, 0x28, 0x7e, 0x42, + ]), + }, + + triangles: { + flags: IModelTileIO.Flags.None, + bytes: new Uint8Array([ + 0x69, 0x4d, 0x64, 0x6c, 0x01, 0x00, 0x01, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xf1, 0xd2, 0x4d, 0x62, + 0x00, 0x1e, 0xc0, 0xc7, 0x4b, 0x37, 0x89, 0x41, 0x00, 0x24, 0xc0, 0x00, 0x00, 0xbd, 0x9a, 0xf2, 0xd7, 0x5a, 0xbe, 0x55, 0x0e, + 0x2d, 0xb2, 0x9d, 0xff, 0x1d, 0x40, 0x39, 0xb4, 0xc8, 0x76, 0xbe, 0xff, 0x23, 0x40, 0x00, 0x00, 0xbd, 0x9a, 0xf2, 0xd7, 0x5a, + 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0x19, 0x00, 0x00, + 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x06, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, 0x02, 0x00, 0x00, 0x00, 0x24, 0x19, 0x00, 0x00, 0xb8, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, + 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, + 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, + 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x22, 0x3a, 0x32, 0x31, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, + 0x31, 0x30, 0x38, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x31, 0x50, 0x6f, 0x6c, 0x79, + 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, + 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x31, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x22, 0x3a, 0x32, 0x34, 0x33, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x3a, 0x7b, + 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, + 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x35, 0x36, 0x2c, 0x22, 0x62, + 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, + 0x65, 0x78, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, + 0x31, 0x35, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x33, 0x32, 0x34, + 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, + 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x31, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, + 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x33, 0x34, 0x38, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, + 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x65, 0x67, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, + 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, + 0x22, 0x3a, 0x32, 0x31, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x36, + 0x37, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, + 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, + 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, + 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, + 0x35, 0x36, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, + 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x38, 0x34, 0x7d, + 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, + 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, + 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x37, 0x2c, 0x22, 0x62, + 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x35, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, + 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x32, 0x2c, 0x22, + 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x38, 0x38, 0x38, 0x7d, 0x2c, 0x22, 0x62, 0x76, + 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, + 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, + 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, + 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x35, 0x30, 0x38, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, + 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x37, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x22, 0x3a, 0x31, 0x34, 0x38, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, + 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, + 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, + 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, + 0x22, 0x3a, 0x32, 0x31, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x38, 0x39, + 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x31, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, + 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x31, 0x36, 0x2c, 0x22, + 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x32, 0x32, 0x31, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, + 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, + 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, + 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x37, 0x32, 0x38, + 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x50, 0x6f, 0x6c, 0x79, + 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, + 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x22, 0x3a, 0x32, 0x30, 0x35, 0x32, 0x7d, 0x7d, 0x2c, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, + 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4b, 0x48, 0x52, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, + 0x46, 0x22, 0x2c, 0x22, 0x57, 0x45, 0x42, 0x33, 0x44, 0x5f, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x3a, 0x7b, + 0x22, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x37, 0x22, 0x2c, 0x22, + 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, + 0x32, 0x35, 0x35, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, + 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, + 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x32, 0x35, 0x35, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, + 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, + 0x31, 0x2c, 0x22, 0x73, 0x75, 0x62, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, + 0x31, 0x38, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x7d, 0x2c, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, + 0x61, 0x6c, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, + 0x78, 0x31, 0x37, 0x22, 0x2c, 0x22, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, + 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x32, 0x31, 0x33, 0x30, 0x37, 0x30, 0x36, 0x36, 0x38, 0x37, 0x2c, 0x22, 0x66, 0x69, 0x6c, + 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, + 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, + 0x72, 0x22, 0x3a, 0x32, 0x31, 0x33, 0x30, 0x37, 0x30, 0x36, 0x36, 0x38, 0x37, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, + 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, + 0x2c, 0x22, 0x73, 0x75, 0x62, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, + 0x38, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x7d, 0x7d, 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, + 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, + 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x65, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x70, + 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, + 0x22, 0x3a, 0x22, 0x62, 0x76, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, + 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, + 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, + 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, + 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, + 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, + 0x73, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x65, 0x6e, 0x64, 0x50, + 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, + 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, + 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x22, 0x7d, 0x7d, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x74, 0x72, 0x75, 0x65, 0x2c, + 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, + 0x22, 0x2c, 0x22, 0x73, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, + 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x7d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22, + 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, + 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x22, 0x3a, 0x39, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, + 0x22, 0x3a, 0x32, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, + 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, + 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x70, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, + 0x3a, 0x5b, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x32, 0x38, 0x39, 0x31, 0x39, 0x36, 0x36, 0x31, 0x32, 0x34, 0x39, 0x37, 0x31, + 0x33, 0x38, 0x37, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, + 0x2e, 0x30, 0x30, 0x30, 0x33, 0x30, 0x35, 0x32, 0x32, 0x36, 0x32, 0x31, 0x34, 0x39, 0x39, 0x39, 0x36, 0x31, 0x38, 0x34, 0x34, + 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, + 0x36, 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, + 0x30, 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, + 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, + 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, 0x22, 0x64, + 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, + 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, + 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, + 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, + 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x7d, 0x2c, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x39, 0x7d, 0x7d, 0x2c, + 0x7b, 0x22, 0x65, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x22, + 0x3a, 0x7b, 0x22, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x44, 0x69, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x31, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, + 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x50, + 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, + 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x31, 0x50, 0x6f, + 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, + 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x65, 0x67, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, + 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, + 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x65, 0x67, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x7d, 0x7d, 0x2c, 0x22, 0x69, 0x73, 0x50, + 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x31, 0x22, 0x2c, 0x22, 0x73, 0x75, 0x72, 0x66, 0x61, 0x63, + 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, + 0x63, 0x65, 0x73, 0x31, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, + 0x7d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x22, + 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, + 0x74, 0x65, 0x78, 0x31, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x39, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x32, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x22, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, 0x56, 0x65, + 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, + 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x32, 0x38, + 0x39, 0x31, 0x39, 0x36, 0x36, 0x31, 0x32, 0x34, 0x39, 0x37, 0x31, 0x33, 0x38, 0x37, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, + 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x33, 0x30, 0x35, 0x32, 0x32, 0x36, + 0x32, 0x31, 0x34, 0x39, 0x39, 0x39, 0x36, 0x31, 0x38, 0x34, 0x34, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, + 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, + 0x30, 0x39, 0x32, 0x38, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, + 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, + 0x37, 0x35, 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, + 0x3a, 0x5b, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, + 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x30, 0x2e, + 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, + 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x7d, 0x2c, 0x22, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x39, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, + 0x3a, 0x7b, 0x22, 0x4e, 0x6f, 0x64, 0x65, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, + 0x6f, 0x6f, 0x74, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, + 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, + 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x5d, 0x7d, 0x7d, 0x7d, 0x0a, 0xa7, 0xaa, 0xfe, 0x7f, 0xfe, + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xa7, 0xaa, 0xf8, 0xff, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xf8, 0xff, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, + 0xcc, 0x55, 0x55, 0xfe, 0x7f, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0xf8, 0xff, + 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xa7, 0xaa, 0xfe, 0x7f, 0xfe, 0x7f, 0x01, 0x00, 0x02, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, 0xfe, 0x7f, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xcc, 0xcc, 0x03, 0x00, 0xf8, 0xff, 0xfe, 0x7f, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0xfe, + 0x7f, 0xfe, 0x7f, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, 0x05, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, + 0x03, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x02, + 0x04, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x01, 0x06, + 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, 0x08, 0x00, + 0x00, 0x01, 0x06, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, + 0x01, 0x08, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x1e, 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x06, 0x02, + 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x36, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x4e, 0x04, 0x00, 0x00, 0x1e, 0x03, 0x00, 0x00, + 0x36, 0x04, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x4e, 0x05, 0x00, 0x00, 0x1e, + 0x04, 0x00, 0x00, 0x36, 0x05, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x4e, 0x03, + 0x00, 0x00, 0x1e, 0x05, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x36, 0x05, 0x00, + 0x00, 0x4e, 0x07, 0x00, 0x00, 0x1e, 0x06, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x06, 0x07, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, + 0x36, 0x06, 0x00, 0x00, 0x4e, 0x08, 0x00, 0x00, 0x1e, 0x07, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x06, 0x08, 0x00, 0x00, 0x06, + 0x07, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x4e, 0x06, 0x00, 0x00, 0x1e, 0x08, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x06, 0x06, + 0x00, 0x00, 0x06, 0x08, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x4e, 0x56, 0x71, 0xd1, 0x41, 0x7c, 0xb8, 0x10, 0x42, 0x56, 0x71, + 0xd1, 0x41, 0x56, 0x71, 0xd1, 0x41, 0x7c, 0xb8, 0x10, 0x42, 0x7c, 0xb8, 0x10, 0x42, 0x7c, 0xb8, 0x10, 0x42, 0xea, 0x70, 0x3d, + 0x42, 0x7c, 0xb8, 0x10, 0x42, 0x7c, 0xb8, 0x10, 0x42, 0xea, 0x70, 0x3d, 0x42, 0xea, 0x70, 0x3d, 0x42, 0xea, 0x70, 0x3d, 0x42, + 0xbe, 0x70, 0x51, 0x42, 0xea, 0x70, 0x3d, 0x42, 0xea, 0x70, 0x3d, 0x42, 0xbe, 0x70, 0x51, 0x42, 0xbe, 0x70, 0x51, 0x42, 0x56, + 0x71, 0xd1, 0x41, 0x7c, 0xb8, 0x10, 0x42, 0x56, 0x71, 0xd1, 0x41, 0x56, 0x71, 0xd1, 0x41, 0x7c, 0xb8, 0x10, 0x42, 0x7c, 0xb8, + 0x10, 0x42, 0x7c, 0xb8, 0x10, 0x42, 0x05, 0x71, 0x3d, 0x42, 0x7c, 0xb8, 0x10, 0x42, 0x7c, 0xb8, 0x10, 0x42, 0x05, 0x71, 0x3d, + 0x42, 0x05, 0x71, 0x3d, 0x42, 0x05, 0x71, 0x3d, 0x42, 0x15, 0x71, 0x51, 0x42, 0x05, 0x71, 0x3d, 0x42, 0x05, 0x71, 0x3d, 0x42, + 0x15, 0x71, 0x51, 0x42, 0x15, 0x71, 0x51, 0x42, 0x56, 0x71, 0xd1, 0x41, 0x7c, 0xb8, 0x10, 0x42, 0x56, 0x71, 0xd1, 0x41, 0x56, + 0x71, 0xd1, 0x41, 0x7c, 0xb8, 0x10, 0x42, 0x7c, 0xb8, 0x10, 0x42, 0x7c, 0xb8, 0x10, 0x42, 0x05, 0x71, 0x3d, 0x42, 0x7c, 0xb8, + 0x10, 0x42, 0x7c, 0xb8, 0x10, 0x42, 0x05, 0x71, 0x3d, 0x42, 0x05, 0x71, 0x3d, 0x42, 0x05, 0x71, 0x3d, 0x42, 0x15, 0x71, 0x51, + 0x42, 0x05, 0x71, 0x3d, 0x42, 0x05, 0x71, 0x3d, 0x42, 0x15, 0x71, 0x51, 0x42, 0x15, 0x71, 0x51, 0x42, 0xa7, 0xaa, 0x03, 0x00, + 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xa7, 0xaa, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xf8, 0xff, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xcc, 0xcc, 0x55, 0x55, 0x03, 0x00, 0xfe, 0x7f, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0xfe, + 0x7f, 0xfe, 0x7f, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xa7, 0xaa, 0x03, 0x00, 0xfe, 0x7f, 0x01, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x03, 0x00, 0x03, 0x00, 0xfe, 0x7f, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, 0xfe, 0x7f, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, + 0x03, 0x00, 0xfe, 0x7f, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x00, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00, + 0x80, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, + 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, 0x05, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, + 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, + 0x02, 0x04, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x01, + 0x06, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, 0x08, + 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x02, 0x08, 0x00, + 0x00, 0x01, 0x08, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x1e, 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x06, + 0x02, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x36, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x4e, 0x04, 0x00, 0x00, 0x1e, 0x03, 0x00, + 0x00, 0x36, 0x04, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x4e, 0x05, 0x00, 0x00, + 0x1e, 0x04, 0x00, 0x00, 0x36, 0x05, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x4e, + 0x03, 0x00, 0x00, 0x1e, 0x05, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x36, 0x05, + 0x00, 0x00, 0x4e, 0x07, 0x00, 0x00, 0x1e, 0x06, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x06, 0x07, 0x00, 0x00, 0x06, 0x06, 0x00, + 0x00, 0x36, 0x06, 0x00, 0x00, 0x4e, 0x08, 0x00, 0x00, 0x1e, 0x07, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x06, 0x08, 0x00, 0x00, + 0x06, 0x07, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x4e, 0x06, 0x00, 0x00, 0x1e, 0x08, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x06, + 0x06, 0x00, 0x00, 0x06, 0x08, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x4e, 0x56, 0x71, 0xd1, 0x41, 0xcc, 0xb8, 0x10, 0x42, 0x56, + 0x71, 0xd1, 0x41, 0x56, 0x71, 0xd1, 0x41, 0xcc, 0xb8, 0x10, 0x42, 0xcc, 0xb8, 0x10, 0x42, 0xcc, 0xb8, 0x10, 0x42, 0x81, 0x71, + 0x3d, 0x42, 0xcc, 0xb8, 0x10, 0x42, 0xcc, 0xb8, 0x10, 0x42, 0x81, 0x71, 0x3d, 0x42, 0x81, 0x71, 0x3d, 0x42, 0x81, 0x71, 0x3d, + 0x42, 0x56, 0x71, 0x51, 0x42, 0x81, 0x71, 0x3d, 0x42, 0x81, 0x71, 0x3d, 0x42, 0x56, 0x71, 0x51, 0x42, 0x56, 0x71, 0x51, 0x42, + 0x56, 0x71, 0xd1, 0x41, 0xcc, 0xb8, 0x10, 0x42, 0x56, 0x71, 0xd1, 0x41, 0x56, 0x71, 0xd1, 0x41, 0xcc, 0xb8, 0x10, 0x42, 0xcc, + 0xb8, 0x10, 0x42, 0xcc, 0xb8, 0x10, 0x42, 0x9c, 0x71, 0x3d, 0x42, 0xcc, 0xb8, 0x10, 0x42, 0xcc, 0xb8, 0x10, 0x42, 0x9c, 0x71, + 0x3d, 0x42, 0x9c, 0x71, 0x3d, 0x42, 0x9c, 0x71, 0x3d, 0x42, 0xad, 0x71, 0x51, 0x42, 0x9c, 0x71, 0x3d, 0x42, 0x9c, 0x71, 0x3d, + 0x42, 0xad, 0x71, 0x51, 0x42, 0xad, 0x71, 0x51, 0x42, 0x56, 0x71, 0xd1, 0x41, 0xcc, 0xb8, 0x10, 0x42, 0x56, 0x71, 0xd1, 0x41, + 0x56, 0x71, 0xd1, 0x41, 0xcc, 0xb8, 0x10, 0x42, 0xcc, 0xb8, 0x10, 0x42, 0xcc, 0xb8, 0x10, 0x42, 0x9c, 0x71, 0x3d, 0x42, 0xcc, + 0xb8, 0x10, 0x42, 0xcc, 0xb8, 0x10, 0x42, 0x9c, 0x71, 0x3d, 0x42, 0x9c, 0x71, 0x3d, 0x42, 0x9c, 0x71, 0x3d, 0x42, 0xad, 0x71, + 0x51, 0x42, 0x9c, 0x71, 0x3d, 0x42, 0x9c, 0x71, 0x3d, 0x42, 0xad, 0x71, 0x51, 0x42, 0xad, 0x71, 0x51, 0x42, + ]), + }, + + cylinder: { + flags: IModelTileIO.Flags.ContainsCurves, + bytes: new Uint8Array([ + 0x69, 0x4d, 0x64, 0x6c, 0x01, 0x00, 0x01, 0x00, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x39, 0xd6, 0xc5, 0x6d, 0x34, + 0x00, 0x00, 0xc0, 0x39, 0xd6, 0xc5, 0x6d, 0x34, 0x00, 0x00, 0xc0, 0x56, 0xc1, 0xa8, 0xa4, 0x4e, 0x00, 0x08, 0xc0, 0x8e, 0x53, + 0x74, 0x24, 0x97, 0xff, 0xff, 0x3f, 0x8e, 0x53, 0x74, 0x24, 0x97, 0xff, 0xff, 0x3f, 0xaa, 0x3e, 0x57, 0x5b, 0xb1, 0xff, 0x07, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x45, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, 0x02, 0x00, 0x00, 0x00, 0xd8, 0x44, + 0x00, 0x00, 0xdc, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, + 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x50, 0x6f, 0x6c, 0x79, + 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, + 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x37, 0x32, 0x38, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x22, 0x3a, 0x31, 0x33, 0x33, 0x35, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, + 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, + 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x33, 0x33, 0x36, + 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x65, + 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, + 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x37, 0x32, 0x38, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x22, 0x3a, 0x34, 0x39, 0x32, 0x38, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, + 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, + 0x74, 0x74, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, + 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x38, 0x36, 0x34, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x37, 0x33, 0x30, + 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, + 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, + 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x22, 0x3a, 0x31, 0x32, 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, + 0x39, 0x30, 0x33, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, + 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x31, 0x32, 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x33, 0x36, + 0x33, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, 0x75, + 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, + 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, + 0x22, 0x3a, 0x36, 0x34, 0x38, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x36, 0x36, + 0x35, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, + 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, + 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x32, + 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x32, 0x33, 0x33, 0x36, 0x7d, + 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, + 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, + 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x37, 0x32, 0x38, 0x2c, 0x22, 0x62, + 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x31, 0x36, 0x32, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, + 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, + 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, + 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x38, + 0x36, 0x34, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x38, 0x31, 0x36, 0x38, 0x7d, + 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, + 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, + 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x32, 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x22, 0x3a, 0x31, 0x30, 0x33, 0x32, 0x38, 0x7d, 0x7d, 0x2c, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, + 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4b, 0x48, 0x52, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, + 0x54, 0x46, 0x22, 0x2c, 0x22, 0x57, 0x45, 0x42, 0x33, 0x44, 0x5f, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, 0x6d, 0x61, 0x74, + 0x65, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x3a, + 0x7b, 0x22, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x37, 0x22, 0x2c, + 0x22, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, + 0x3a, 0x36, 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, + 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, + 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, + 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, + 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x73, 0x75, 0x62, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, + 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x38, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x7d, 0x7d, 0x2c, 0x22, + 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, + 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x65, 0x64, 0x67, 0x65, + 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x69, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x50, + 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, + 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, + 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, + 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, + 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, + 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, + 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, + 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, + 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x2c, 0x22, + 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, + 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, + 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, + 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, + 0x74, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x6e, 0x6f, + 0x72, 0x6d, 0x61, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x50, + 0x61, 0x69, 0x72, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x7d, 0x7d, 0x2c, 0x22, + 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x2c, 0x22, 0x73, 0x75, + 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, + 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, + 0x65, 0x22, 0x3a, 0x31, 0x7d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, + 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, + 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x31, 0x34, 0x36, + 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x44, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, 0x56, 0x65, + 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, + 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x36, 0x2e, 0x31, 0x30, 0x34, 0x35, 0x32, 0x34, + 0x32, 0x39, 0x39, 0x39, 0x39, 0x32, 0x33, 0x37, 0x30, 0x30, 0x65, 0x2d, 0x30, 0x35, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, + 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x36, 0x2e, 0x31, 0x30, 0x34, 0x35, 0x32, 0x34, 0x32, 0x39, 0x39, + 0x39, 0x39, 0x32, 0x33, 0x37, 0x30, 0x30, 0x65, 0x2d, 0x30, 0x35, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, + 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x39, 0x2e, 0x31, 0x35, 0x36, 0x37, 0x38, 0x36, 0x34, 0x34, 0x39, 0x39, 0x38, 0x38, + 0x35, 0x35, 0x35, 0x30, 0x65, 0x2d, 0x30, 0x35, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x2d, 0x32, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x2d, 0x32, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x2d, 0x33, 0x2e, 0x30, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, + 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x32, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x32, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x37, 0x2c, 0x33, 0x2e, 0x30, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x38, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x32, 0x2e, 0x30, + 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x2d, 0x32, 0x2e, 0x30, 0x30, + 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x2d, 0x33, 0x2e, 0x30, 0x30, 0x30, + 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x5d, 0x7d, 0x2c, 0x22, 0x75, 0x6e, 0x69, 0x66, + 0x6f, 0x72, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x77, 0x69, 0x64, 0x74, + 0x68, 0x22, 0x3a, 0x35, 0x38, 0x34, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x7b, + 0x22, 0x4e, 0x6f, 0x64, 0x65, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, 0x6f, 0x6f, + 0x74, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, + 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, + 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x5d, 0x7d, 0x7d, 0x7d, 0x0a, 0xf8, 0xff, 0xfe, 0x7f, 0xf8, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xfe, 0x7f, 0xfe, 0x7f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0x07, 0xfe, 0xc5, 0x69, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x41, + 0xf8, 0x38, 0x54, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xd3, 0xee, 0x01, 0x40, 0xf8, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x07, 0xe2, 0xba, 0x2d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x41, 0xd2, 0xf4, 0x1d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0xfb, 0xbf, 0x29, 0x11, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xc3, 0xab, 0xbb, 0x07, 0xf8, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x37, 0x96, 0xf5, 0x01, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xfe, 0x7f, 0x03, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, + 0xcc, 0xc5, 0x69, 0xf5, 0x01, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x38, 0x54, 0xbb, 0x07, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x01, 0x40, 0x29, 0x11, 0xf8, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xba, 0x2d, 0xf4, 0x1d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, + 0xcc, 0xcc, 0xf4, 0x1d, 0xba, 0x2d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x29, 0x11, 0x01, + 0x40, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xbb, 0x07, 0x38, 0x54, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xf5, 0x01, 0xc5, 0x69, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x80, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xf5, 0x01, + 0x37, 0x96, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xbb, 0x07, 0xc3, 0xab, 0xf8, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x29, 0x11, 0xfb, 0xbf, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0xf4, 0x1d, 0x41, 0xd2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xba, + 0x2d, 0x07, 0xe2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x01, 0x40, 0xd3, 0xee, 0xf8, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x38, 0x54, 0x41, 0xf8, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xc5, 0x69, 0x07, 0xfe, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0xfe, 0x7f, 0xf8, 0xff, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x37, 0x96, 0x07, 0xfe, 0xf8, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xc3, 0xab, 0x41, 0xf8, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xfb, 0xbf, 0xd3, 0xee, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, + 0xcc, 0x41, 0xd2, 0x07, 0xe2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x07, 0xe2, 0x41, 0xd2, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xd3, 0xee, 0xfb, 0xbf, 0xf8, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x41, 0xf8, 0xc3, 0xab, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, + 0xcc, 0xcc, 0x07, 0xfe, 0x37, 0x96, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xf8, 0xff, 0xfe, + 0x7f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xfe, 0x7f, 0xfe, 0x7f, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x07, 0xfe, 0x37, 0x96, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xcc, 0xcc, 0x41, 0xf8, 0xc3, 0xab, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xd3, 0xee, + 0xfb, 0xbf, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x07, 0xe2, 0x41, 0xd2, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x41, 0xd2, 0x07, 0xe2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xfb, 0xbf, 0xd3, 0xee, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xc3, + 0xab, 0x41, 0xf8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x37, 0x96, 0x07, 0xfe, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xfe, 0x7f, 0xf8, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xc5, 0x69, 0x07, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0x38, 0x54, 0x41, 0xf8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x01, 0x40, 0xd3, 0xee, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xba, 0x2d, 0x07, 0xe2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xf4, 0x1d, 0x41, 0xd2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, + 0xcc, 0x29, 0x11, 0xfb, 0xbf, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xbb, 0x07, 0xc3, 0xab, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xf5, 0x01, 0x37, 0x96, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xcc, 0xcc, 0xf5, 0x01, 0xc5, 0x69, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xbb, 0x07, 0x38, + 0x54, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x29, 0x11, 0x01, 0x40, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xf4, 0x1d, 0xba, 0x2d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xcc, 0xcc, 0xba, 0x2d, 0xf4, 0x1d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x01, 0x40, + 0x29, 0x11, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x38, 0x54, 0xbb, 0x07, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xc5, 0x69, 0xf5, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xfe, 0x7f, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x37, + 0x96, 0xf5, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xc3, 0xab, 0xbb, 0x07, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xfb, 0xbf, 0x29, 0x11, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x41, 0xd2, 0xf4, 0x1d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0x07, 0xe2, 0xba, 0x2d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xd3, 0xee, 0x01, 0x40, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x41, 0xf8, 0x38, 0x54, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x07, 0xfe, 0xc5, 0x69, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, + 0xcc, 0xf8, 0xff, 0xfe, 0x7f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x80, 0xcc, 0xcc, 0xf8, 0xff, 0xfe, 0x7f, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x80, 0xcc, 0xcc, 0x07, 0xfe, 0x37, 0x96, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xec, 0x93, 0xcc, 0xcc, 0x07, 0xfe, 0x37, 0x96, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x93, + 0xcc, 0xcc, 0x41, 0xf8, 0xc3, 0xab, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0xa2, 0xcc, 0xcc, 0x41, 0xf8, 0xc3, + 0xab, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0xa2, 0xcc, 0xcc, 0xd3, 0xee, 0xfb, 0xbf, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xd0, 0xae, 0xcc, 0xcc, 0xd3, 0xee, 0xfb, 0xbf, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, + 0xae, 0xcc, 0xcc, 0x07, 0xe2, 0x41, 0xd2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0xba, 0xcc, 0xcc, 0x07, 0xe2, + 0x41, 0xd2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0xba, 0xcc, 0xcc, 0x41, 0xd2, 0x07, 0xe2, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xc5, 0xcc, 0xcc, 0x41, 0xd2, 0x07, 0xe2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xba, 0xc5, 0xcc, 0xcc, 0xfb, 0xbf, 0xd3, 0xee, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0xd0, 0xcc, 0xcc, 0xfb, + 0xbf, 0xd3, 0xee, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0xd0, 0xcc, 0xcc, 0xc3, 0xab, 0x41, 0xf8, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0xdd, 0xcc, 0xcc, 0xc3, 0xab, 0x41, 0xf8, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xa2, 0xdd, 0xcc, 0xcc, 0x37, 0x96, 0x07, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0xec, 0xcc, 0xcc, + 0x37, 0x96, 0x07, 0xfe, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0xec, 0xcc, 0xcc, 0xfe, 0x7f, 0xf8, 0xff, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xcc, 0xcc, 0xfe, 0x7f, 0xf8, 0xff, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xff, 0xcc, 0xcc, 0xc5, 0x69, 0x07, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xec, 0xcc, + 0xcc, 0xc5, 0x69, 0x07, 0xfe, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xec, 0xcc, 0xcc, 0x38, 0x54, 0x41, 0xf8, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xdd, 0xcc, 0xcc, 0x38, 0x54, 0x41, 0xf8, 0xf8, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x5d, 0xdd, 0xcc, 0xcc, 0x01, 0x40, 0xd3, 0xee, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0xd0, + 0xcc, 0xcc, 0x01, 0x40, 0xd3, 0xee, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0xd0, 0xcc, 0xcc, 0xba, 0x2d, 0x07, + 0xe2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xc5, 0xcc, 0xcc, 0xba, 0x2d, 0x07, 0xe2, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x45, 0xc5, 0xcc, 0xcc, 0xf4, 0x1d, 0x41, 0xd2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, + 0xba, 0xcc, 0xcc, 0xf4, 0x1d, 0x41, 0xd2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0xba, 0xcc, 0xcc, 0x29, 0x11, + 0xfb, 0xbf, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0xae, 0xcc, 0xcc, 0x29, 0x11, 0xfb, 0xbf, 0xf8, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0xae, 0xcc, 0xcc, 0xbb, 0x07, 0xc3, 0xab, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x22, 0xa2, 0xcc, 0xcc, 0xbb, 0x07, 0xc3, 0xab, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xa2, 0xcc, 0xcc, 0xf5, + 0x01, 0x37, 0x96, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x93, 0xcc, 0xcc, 0xf5, 0x01, 0x37, 0x96, 0xf8, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x93, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcc, 0xcc, + 0xf5, 0x01, 0xc5, 0x69, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x6c, 0xcc, 0xcc, 0xf5, 0x01, 0xc5, 0x69, 0xf8, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x6c, 0xcc, 0xcc, 0xbb, 0x07, 0x38, 0x54, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x5d, 0xcc, 0xcc, 0xbb, 0x07, 0x38, 0x54, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x5d, 0xcc, + 0xcc, 0x29, 0x11, 0x01, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x51, 0xcc, 0xcc, 0x29, 0x11, 0x01, 0x40, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x51, 0xcc, 0xcc, 0xf4, 0x1d, 0xba, 0x2d, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3a, 0x45, 0xcc, 0xcc, 0xf4, 0x1d, 0xba, 0x2d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x45, + 0xcc, 0xcc, 0xba, 0x2d, 0xf4, 0x1d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x3a, 0xcc, 0xcc, 0xba, 0x2d, 0xf4, + 0x1d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x3a, 0xcc, 0xcc, 0x01, 0x40, 0x29, 0x11, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x2f, 0xcc, 0xcc, 0x01, 0x40, 0x29, 0x11, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, + 0x2f, 0xcc, 0xcc, 0x38, 0x54, 0xbb, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x22, 0xcc, 0xcc, 0x38, 0x54, + 0xbb, 0x07, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x22, 0xcc, 0xcc, 0xc5, 0x69, 0xf5, 0x01, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x13, 0xcc, 0xcc, 0xc5, 0x69, 0xf5, 0x01, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0x13, 0xcc, 0xcc, 0xfe, 0x7f, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0xcc, 0xcc, 0xfe, + 0x7f, 0x03, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0xcc, 0xcc, 0x37, 0x96, 0xf5, 0x01, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x13, 0xcc, 0xcc, 0x37, 0x96, 0xf5, 0x01, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x93, 0x13, 0xcc, 0xcc, 0xc3, 0xab, 0xbb, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0x22, 0xcc, 0xcc, + 0xc3, 0xab, 0xbb, 0x07, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0x22, 0xcc, 0xcc, 0xfb, 0xbf, 0x29, 0x11, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0x2f, 0xcc, 0xcc, 0xfb, 0xbf, 0x29, 0x11, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xae, 0x2f, 0xcc, 0xcc, 0x41, 0xd2, 0xf4, 0x1d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x3a, 0xcc, + 0xcc, 0x41, 0xd2, 0xf4, 0x1d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x3a, 0xcc, 0xcc, 0x07, 0xe2, 0xba, 0x2d, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0x45, 0xcc, 0xcc, 0x07, 0xe2, 0xba, 0x2d, 0xf8, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc5, 0x45, 0xcc, 0xcc, 0xd3, 0xee, 0x01, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x51, + 0xcc, 0xcc, 0xd3, 0xee, 0x01, 0x40, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x51, 0xcc, 0xcc, 0x41, 0xf8, 0x38, + 0x54, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x5d, 0xcc, 0xcc, 0x41, 0xf8, 0x38, 0x54, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdd, 0x5d, 0xcc, 0xcc, 0x07, 0xfe, 0xc5, 0x69, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, + 0x6c, 0xcc, 0xcc, 0x07, 0xfe, 0xc5, 0x69, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x6c, 0xcc, 0xcc, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0d, 0x00, + 0x00, 0x0d, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x14, 0x00, + 0x00, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x01, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1b, 0x00, + 0x00, 0x1b, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x01, 0x00, 0x00, 0x22, 0x00, + 0x00, 0x22, 0x00, 0x00, 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x01, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x26, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x26, 0x00, + 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x26, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x26, 0x00, 0x00, 0x2a, 0x00, + 0x00, 0x2a, 0x00, 0x00, 0x26, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x26, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, + 0x00, 0x26, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x26, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x26, 0x00, + 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x26, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x26, 0x00, 0x00, 0x31, 0x00, + 0x00, 0x31, 0x00, 0x00, 0x26, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x26, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, + 0x00, 0x26, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x26, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x26, 0x00, + 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x26, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x26, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x26, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, + 0x00, 0x26, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x26, 0x00, + 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3f, 0x00, + 0x00, 0x3f, 0x00, 0x00, 0x26, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x26, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x26, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x26, 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x26, 0x00, + 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x26, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x26, 0x00, 0x00, 0x46, 0x00, + 0x00, 0x46, 0x00, 0x00, 0x26, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x26, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, + 0x00, 0x26, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x26, 0x00, 0x00, 0x25, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x4b, 0x00, + 0x00, 0x4c, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4e, 0x00, + 0x00, 0x4d, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x50, 0x00, 0x00, 0x4f, 0x00, + 0x00, 0x50, 0x00, 0x00, 0x51, 0x00, 0x00, 0x51, 0x00, 0x00, 0x50, 0x00, 0x00, 0x52, 0x00, 0x00, 0x51, 0x00, 0x00, 0x52, 0x00, + 0x00, 0x53, 0x00, 0x00, 0x53, 0x00, 0x00, 0x52, 0x00, 0x00, 0x54, 0x00, 0x00, 0x53, 0x00, 0x00, 0x54, 0x00, 0x00, 0x55, 0x00, + 0x00, 0x55, 0x00, 0x00, 0x54, 0x00, 0x00, 0x56, 0x00, 0x00, 0x55, 0x00, 0x00, 0x56, 0x00, 0x00, 0x57, 0x00, 0x00, 0x57, 0x00, + 0x00, 0x56, 0x00, 0x00, 0x58, 0x00, 0x00, 0x57, 0x00, 0x00, 0x58, 0x00, 0x00, 0x59, 0x00, 0x00, 0x59, 0x00, 0x00, 0x58, 0x00, + 0x00, 0x5a, 0x00, 0x00, 0x59, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5c, 0x00, + 0x00, 0x5b, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x5d, 0x00, + 0x00, 0x5e, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x60, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x61, 0x00, 0x00, 0x61, 0x00, 0x00, 0x60, 0x00, 0x00, 0x62, 0x00, 0x00, 0x61, 0x00, 0x00, 0x62, 0x00, 0x00, 0x63, 0x00, + 0x00, 0x63, 0x00, 0x00, 0x62, 0x00, 0x00, 0x64, 0x00, 0x00, 0x63, 0x00, 0x00, 0x64, 0x00, 0x00, 0x65, 0x00, 0x00, 0x65, 0x00, + 0x00, 0x64, 0x00, 0x00, 0x66, 0x00, 0x00, 0x65, 0x00, 0x00, 0x66, 0x00, 0x00, 0x67, 0x00, 0x00, 0x67, 0x00, 0x00, 0x66, 0x00, + 0x00, 0x68, 0x00, 0x00, 0x67, 0x00, 0x00, 0x68, 0x00, 0x00, 0x69, 0x00, 0x00, 0x69, 0x00, 0x00, 0x68, 0x00, 0x00, 0x6a, 0x00, + 0x00, 0x69, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6b, 0x00, + 0x00, 0x6c, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x6e, 0x00, + 0x00, 0x6f, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x70, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x70, 0x00, 0x00, 0x71, 0x00, + 0x00, 0x71, 0x00, 0x00, 0x70, 0x00, 0x00, 0x72, 0x00, 0x00, 0x71, 0x00, 0x00, 0x72, 0x00, 0x00, 0x73, 0x00, 0x00, 0x73, 0x00, + 0x00, 0x72, 0x00, 0x00, 0x74, 0x00, 0x00, 0x73, 0x00, 0x00, 0x74, 0x00, 0x00, 0x75, 0x00, 0x00, 0x75, 0x00, 0x00, 0x74, 0x00, + 0x00, 0x76, 0x00, 0x00, 0x75, 0x00, 0x00, 0x76, 0x00, 0x00, 0x77, 0x00, 0x00, 0x77, 0x00, 0x00, 0x76, 0x00, 0x00, 0x78, 0x00, + 0x00, 0x77, 0x00, 0x00, 0x78, 0x00, 0x00, 0x79, 0x00, 0x00, 0x79, 0x00, 0x00, 0x78, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x79, 0x00, + 0x00, 0x7a, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7c, 0x00, + 0x00, 0x7d, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7f, 0x00, + 0x00, 0x7f, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x80, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x82, 0x00, 0x00, 0x81, 0x00, 0x00, 0x82, 0x00, 0x00, 0x83, 0x00, 0x00, 0x83, 0x00, 0x00, 0x82, 0x00, + 0x00, 0x84, 0x00, 0x00, 0x83, 0x00, 0x00, 0x84, 0x00, 0x00, 0x85, 0x00, 0x00, 0x85, 0x00, 0x00, 0x84, 0x00, 0x00, 0x86, 0x00, + 0x00, 0x85, 0x00, 0x00, 0x86, 0x00, 0x00, 0x87, 0x00, 0x00, 0x87, 0x00, 0x00, 0x86, 0x00, 0x00, 0x88, 0x00, 0x00, 0x87, 0x00, + 0x00, 0x88, 0x00, 0x00, 0x89, 0x00, 0x00, 0x89, 0x00, 0x00, 0x88, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x89, 0x00, 0x00, 0x8a, 0x00, + 0x00, 0x8b, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8d, 0x00, + 0x00, 0x8d, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x8f, 0x00, + 0x00, 0x8e, 0x00, 0x00, 0x90, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x90, 0x00, 0x00, 0x91, 0x00, 0x00, 0x91, 0x00, 0x00, 0x90, 0x00, + 0x00, 0x4b, 0x00, 0x00, 0x91, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, + 0x00, 0x24, 0x00, 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, + 0x00, 0x21, 0x00, 0x00, 0x22, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x21, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1e, 0x00, + 0x00, 0x1e, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, + 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1d, 0x00, + 0x00, 0x1d, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, + 0x00, 0x1a, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x19, 0x00, + 0x00, 0x1a, 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x18, 0x00, 0x00, 0x19, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x17, 0x00, 0x00, 0x18, 0x00, 0x00, 0x17, 0x00, + 0x00, 0x17, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x16, 0x00, 0x00, 0x17, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, + 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x15, 0x00, 0x00, 0x16, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x16, 0x00, + 0x00, 0x16, 0x00, 0x00, 0x14, 0x00, 0x00, 0x15, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, + 0x00, 0x13, 0x00, 0x00, 0x14, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x12, 0x00, + 0x00, 0x13, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x11, 0x00, 0x00, 0x12, 0x00, + 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x10, 0x00, 0x00, 0x11, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0f, 0x00, + 0x00, 0x0f, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, + 0x00, 0x0c, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0b, 0x00, + 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x25, 0x00, 0x00, 0x27, 0x00, 0x00, 0x25, 0x00, 0x00, 0x25, 0x00, + 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x25, 0x00, 0x00, 0x49, 0x00, 0x00, 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x49, 0x00, + 0x00, 0x49, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x28, 0x00, 0x00, 0x29, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, + 0x00, 0x2a, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2b, 0x00, + 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2b, 0x00, + 0x00, 0x2b, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, + 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2e, 0x00, + 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, + 0x00, 0x2f, 0x00, 0x00, 0x30, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x31, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x32, 0x00, + 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x33, 0x00, 0x00, 0x32, 0x00, + 0x00, 0x32, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x34, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, + 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, + 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x36, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, + 0x00, 0x36, 0x00, 0x00, 0x37, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x39, 0x00, + 0x00, 0x39, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, + 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3c, 0x00, + 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, + 0x00, 0x3d, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, + 0x00, 0x3f, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x41, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x42, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x43, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x43, 0x00, + 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x44, 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, + 0x00, 0x44, 0x00, 0x00, 0x45, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, + 0x00, 0x46, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x47, 0x00, + 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x48, 0x00, 0x00, 0x47, 0x00, + 0x00, 0x47, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x49, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, + 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x01, 0x24, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x24, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x02, + 0x24, 0x00, 0x00, 0x01, 0x24, 0x00, 0x00, 0x01, 0x23, 0x00, 0x00, 0x02, 0x23, 0x00, 0x00, 0x03, 0x23, 0x00, 0x00, 0x00, 0x22, + 0x00, 0x00, 0x02, 0x23, 0x00, 0x00, 0x01, 0x23, 0x00, 0x00, 0x01, 0x22, 0x00, 0x00, 0x02, 0x22, 0x00, 0x00, 0x03, 0x22, 0x00, + 0x00, 0x00, 0x21, 0x00, 0x00, 0x02, 0x22, 0x00, 0x00, 0x01, 0x22, 0x00, 0x00, 0x01, 0x21, 0x00, 0x00, 0x02, 0x21, 0x00, 0x00, + 0x03, 0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x02, 0x21, 0x00, 0x00, 0x01, 0x21, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x02, + 0x20, 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x01, 0x1f, + 0x00, 0x00, 0x02, 0x1f, 0x00, 0x00, 0x03, 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x02, 0x1f, 0x00, 0x00, 0x01, 0x1f, 0x00, + 0x00, 0x01, 0x1e, 0x00, 0x00, 0x02, 0x1e, 0x00, 0x00, 0x03, 0x1e, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x02, 0x1e, 0x00, 0x00, + 0x01, 0x1e, 0x00, 0x00, 0x01, 0x1d, 0x00, 0x00, 0x02, 0x1d, 0x00, 0x00, 0x03, 0x1d, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x02, + 0x1d, 0x00, 0x00, 0x01, 0x1d, 0x00, 0x00, 0x01, 0x1c, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x03, 0x1c, 0x00, 0x00, 0x00, 0x1b, + 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x01, 0x1c, 0x00, 0x00, 0x01, 0x1b, 0x00, 0x00, 0x02, 0x1b, 0x00, 0x00, 0x03, 0x1b, 0x00, + 0x00, 0x00, 0x1a, 0x00, 0x00, 0x02, 0x1b, 0x00, 0x00, 0x01, 0x1b, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x00, 0x02, 0x1a, 0x00, 0x00, + 0x03, 0x1a, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x02, 0x1a, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x00, 0x01, 0x19, 0x00, 0x00, 0x02, + 0x19, 0x00, 0x00, 0x03, 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x02, 0x19, 0x00, 0x00, 0x01, 0x19, 0x00, 0x00, 0x01, 0x18, + 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x03, 0x18, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x01, 0x18, 0x00, + 0x00, 0x01, 0x17, 0x00, 0x00, 0x02, 0x17, 0x00, 0x00, 0x03, 0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x02, 0x17, 0x00, 0x00, + 0x01, 0x17, 0x00, 0x00, 0x01, 0x16, 0x00, 0x00, 0x02, 0x16, 0x00, 0x00, 0x03, 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x02, + 0x16, 0x00, 0x00, 0x01, 0x16, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00, 0x02, 0x15, 0x00, 0x00, 0x03, 0x15, 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x02, 0x15, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x03, 0x14, 0x00, + 0x00, 0x00, 0x13, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x01, 0x13, 0x00, 0x00, 0x02, 0x13, 0x00, 0x00, + 0x03, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x02, 0x13, 0x00, 0x00, 0x01, 0x13, 0x00, 0x00, 0x01, 0x12, 0x00, 0x00, 0x02, + 0x12, 0x00, 0x00, 0x03, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x02, 0x12, 0x00, 0x00, 0x01, 0x12, 0x00, 0x00, 0x01, 0x11, + 0x00, 0x00, 0x02, 0x11, 0x00, 0x00, 0x03, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x02, 0x11, 0x00, 0x00, 0x01, 0x11, 0x00, + 0x00, 0x01, 0x10, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, + 0x01, 0x10, 0x00, 0x00, 0x01, 0x0f, 0x00, 0x00, 0x02, 0x0f, 0x00, 0x00, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x02, + 0x0f, 0x00, 0x00, 0x01, 0x0f, 0x00, 0x00, 0x01, 0x0e, 0x00, 0x00, 0x02, 0x0e, 0x00, 0x00, 0x03, 0x0e, 0x00, 0x00, 0x00, 0x0d, + 0x00, 0x00, 0x02, 0x0e, 0x00, 0x00, 0x01, 0x0e, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x00, 0x02, 0x0d, 0x00, 0x00, 0x03, 0x0d, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x00, 0x02, 0x0d, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, + 0x03, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x02, + 0x0b, 0x00, 0x00, 0x03, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x01, 0x0a, + 0x00, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x03, 0x0a, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x01, 0x0a, 0x00, + 0x00, 0x01, 0x09, 0x00, 0x00, 0x02, 0x09, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x02, 0x09, 0x00, 0x00, + 0x01, 0x09, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x02, + 0x08, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x03, 0x06, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, + 0x03, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x02, + 0x04, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x01, 0x03, + 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x01, 0x03, 0x00, + 0x00, 0x01, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x03, 0x27, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x02, 0x27, 0x00, 0x00, + 0x01, 0x27, 0x00, 0x00, 0x01, 0x25, 0x00, 0x00, 0x02, 0x25, 0x00, 0x00, 0x03, 0x49, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x02, + 0x49, 0x00, 0x00, 0x01, 0x49, 0x00, 0x00, 0x01, 0x25, 0x00, 0x00, 0x02, 0x25, 0x00, 0x00, 0x03, 0x28, 0x00, 0x00, 0x00, 0x27, + 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x01, 0x28, 0x00, 0x00, 0x01, 0x27, 0x00, 0x00, 0x02, 0x27, 0x00, 0x00, 0x03, 0x29, 0x00, + 0x00, 0x00, 0x28, 0x00, 0x00, 0x02, 0x29, 0x00, 0x00, 0x01, 0x29, 0x00, 0x00, 0x01, 0x28, 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, + 0x03, 0x2a, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x02, 0x2a, 0x00, 0x00, 0x01, 0x2a, 0x00, 0x00, 0x01, 0x29, 0x00, 0x00, 0x02, + 0x29, 0x00, 0x00, 0x03, 0x2b, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x02, 0x2b, 0x00, 0x00, 0x01, 0x2b, 0x00, 0x00, 0x01, 0x2a, + 0x00, 0x00, 0x02, 0x2a, 0x00, 0x00, 0x03, 0x2c, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0x2c, 0x00, + 0x00, 0x01, 0x2b, 0x00, 0x00, 0x02, 0x2b, 0x00, 0x00, 0x03, 0x2d, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x02, 0x2d, 0x00, 0x00, + 0x01, 0x2d, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x03, 0x2e, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x02, + 0x2e, 0x00, 0x00, 0x01, 0x2e, 0x00, 0x00, 0x01, 0x2d, 0x00, 0x00, 0x02, 0x2d, 0x00, 0x00, 0x03, 0x2f, 0x00, 0x00, 0x00, 0x2e, + 0x00, 0x00, 0x02, 0x2f, 0x00, 0x00, 0x01, 0x2f, 0x00, 0x00, 0x01, 0x2e, 0x00, 0x00, 0x02, 0x2e, 0x00, 0x00, 0x03, 0x30, 0x00, + 0x00, 0x00, 0x2f, 0x00, 0x00, 0x02, 0x30, 0x00, 0x00, 0x01, 0x30, 0x00, 0x00, 0x01, 0x2f, 0x00, 0x00, 0x02, 0x2f, 0x00, 0x00, + 0x03, 0x31, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x02, 0x31, 0x00, 0x00, 0x01, 0x31, 0x00, 0x00, 0x01, 0x30, 0x00, 0x00, 0x02, + 0x30, 0x00, 0x00, 0x03, 0x32, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x02, 0x32, 0x00, 0x00, 0x01, 0x32, 0x00, 0x00, 0x01, 0x31, + 0x00, 0x00, 0x02, 0x31, 0x00, 0x00, 0x03, 0x33, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x02, 0x33, 0x00, 0x00, 0x01, 0x33, 0x00, + 0x00, 0x01, 0x32, 0x00, 0x00, 0x02, 0x32, 0x00, 0x00, 0x03, 0x34, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x02, 0x34, 0x00, 0x00, + 0x01, 0x34, 0x00, 0x00, 0x01, 0x33, 0x00, 0x00, 0x02, 0x33, 0x00, 0x00, 0x03, 0x35, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x02, + 0x35, 0x00, 0x00, 0x01, 0x35, 0x00, 0x00, 0x01, 0x34, 0x00, 0x00, 0x02, 0x34, 0x00, 0x00, 0x03, 0x36, 0x00, 0x00, 0x00, 0x35, + 0x00, 0x00, 0x02, 0x36, 0x00, 0x00, 0x01, 0x36, 0x00, 0x00, 0x01, 0x35, 0x00, 0x00, 0x02, 0x35, 0x00, 0x00, 0x03, 0x37, 0x00, + 0x00, 0x00, 0x36, 0x00, 0x00, 0x02, 0x37, 0x00, 0x00, 0x01, 0x37, 0x00, 0x00, 0x01, 0x36, 0x00, 0x00, 0x02, 0x36, 0x00, 0x00, + 0x03, 0x38, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x02, 0x38, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x01, 0x37, 0x00, 0x00, 0x02, + 0x37, 0x00, 0x00, 0x03, 0x39, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x02, 0x39, 0x00, 0x00, 0x01, 0x39, 0x00, 0x00, 0x01, 0x38, + 0x00, 0x00, 0x02, 0x38, 0x00, 0x00, 0x03, 0x3a, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x02, 0x3a, 0x00, 0x00, 0x01, 0x3a, 0x00, + 0x00, 0x01, 0x39, 0x00, 0x00, 0x02, 0x39, 0x00, 0x00, 0x03, 0x3b, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x02, 0x3b, 0x00, 0x00, + 0x01, 0x3b, 0x00, 0x00, 0x01, 0x3a, 0x00, 0x00, 0x02, 0x3a, 0x00, 0x00, 0x03, 0x3c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x02, + 0x3c, 0x00, 0x00, 0x01, 0x3c, 0x00, 0x00, 0x01, 0x3b, 0x00, 0x00, 0x02, 0x3b, 0x00, 0x00, 0x03, 0x3d, 0x00, 0x00, 0x00, 0x3c, + 0x00, 0x00, 0x02, 0x3d, 0x00, 0x00, 0x01, 0x3d, 0x00, 0x00, 0x01, 0x3c, 0x00, 0x00, 0x02, 0x3c, 0x00, 0x00, 0x03, 0x3e, 0x00, + 0x00, 0x00, 0x3d, 0x00, 0x00, 0x02, 0x3e, 0x00, 0x00, 0x01, 0x3e, 0x00, 0x00, 0x01, 0x3d, 0x00, 0x00, 0x02, 0x3d, 0x00, 0x00, + 0x03, 0x3f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x02, 0x3f, 0x00, 0x00, 0x01, 0x3f, 0x00, 0x00, 0x01, 0x3e, 0x00, 0x00, 0x02, + 0x3e, 0x00, 0x00, 0x03, 0x40, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x01, 0x3f, + 0x00, 0x00, 0x02, 0x3f, 0x00, 0x00, 0x03, 0x41, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0x41, 0x00, 0x00, 0x01, 0x41, 0x00, + 0x00, 0x01, 0x40, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x03, 0x42, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x02, 0x42, 0x00, 0x00, + 0x01, 0x42, 0x00, 0x00, 0x01, 0x41, 0x00, 0x00, 0x02, 0x41, 0x00, 0x00, 0x03, 0x43, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x02, + 0x43, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x01, 0x42, 0x00, 0x00, 0x02, 0x42, 0x00, 0x00, 0x03, 0x44, 0x00, 0x00, 0x00, 0x43, + 0x00, 0x00, 0x02, 0x44, 0x00, 0x00, 0x01, 0x44, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x02, 0x43, 0x00, 0x00, 0x03, 0x45, 0x00, + 0x00, 0x00, 0x44, 0x00, 0x00, 0x02, 0x45, 0x00, 0x00, 0x01, 0x45, 0x00, 0x00, 0x01, 0x44, 0x00, 0x00, 0x02, 0x44, 0x00, 0x00, + 0x03, 0x46, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x02, 0x46, 0x00, 0x00, 0x01, 0x46, 0x00, 0x00, 0x01, 0x45, 0x00, 0x00, 0x02, + 0x45, 0x00, 0x00, 0x03, 0x47, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x02, 0x47, 0x00, 0x00, 0x01, 0x47, 0x00, 0x00, 0x01, 0x46, + 0x00, 0x00, 0x02, 0x46, 0x00, 0x00, 0x03, 0x48, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x01, 0x48, 0x00, + 0x00, 0x01, 0x47, 0x00, 0x00, 0x02, 0x47, 0x00, 0x00, 0x03, 0x49, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x02, 0x49, 0x00, 0x00, + 0x01, 0x49, 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x03, 0x4a, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4a, 0x00, + 0x00, 0x4a, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4c, 0x00, + 0x00, 0x4d, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x4f, 0x00, + 0x00, 0x4f, 0x00, 0x00, 0x50, 0x00, 0x00, 0x51, 0x00, 0x00, 0x50, 0x00, 0x00, 0x50, 0x00, 0x00, 0x51, 0x00, 0x00, 0x51, 0x00, + 0x00, 0x52, 0x00, 0x00, 0x53, 0x00, 0x00, 0x52, 0x00, 0x00, 0x52, 0x00, 0x00, 0x53, 0x00, 0x00, 0x53, 0x00, 0x00, 0x54, 0x00, + 0x00, 0x55, 0x00, 0x00, 0x54, 0x00, 0x00, 0x54, 0x00, 0x00, 0x55, 0x00, 0x00, 0x55, 0x00, 0x00, 0x56, 0x00, 0x00, 0x57, 0x00, + 0x00, 0x56, 0x00, 0x00, 0x56, 0x00, 0x00, 0x57, 0x00, 0x00, 0x57, 0x00, 0x00, 0x58, 0x00, 0x00, 0x59, 0x00, 0x00, 0x58, 0x00, + 0x00, 0x58, 0x00, 0x00, 0x59, 0x00, 0x00, 0x59, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5a, 0x00, + 0x00, 0x5b, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5d, 0x00, + 0x00, 0x5d, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x5f, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x61, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x61, 0x00, 0x00, 0x61, 0x00, 0x00, 0x62, 0x00, + 0x00, 0x63, 0x00, 0x00, 0x62, 0x00, 0x00, 0x62, 0x00, 0x00, 0x63, 0x00, 0x00, 0x63, 0x00, 0x00, 0x64, 0x00, 0x00, 0x65, 0x00, + 0x00, 0x64, 0x00, 0x00, 0x64, 0x00, 0x00, 0x65, 0x00, 0x00, 0x65, 0x00, 0x00, 0x66, 0x00, 0x00, 0x67, 0x00, 0x00, 0x66, 0x00, + 0x00, 0x66, 0x00, 0x00, 0x67, 0x00, 0x00, 0x67, 0x00, 0x00, 0x68, 0x00, 0x00, 0x69, 0x00, 0x00, 0x68, 0x00, 0x00, 0x68, 0x00, + 0x00, 0x69, 0x00, 0x00, 0x69, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x6b, 0x00, + 0x00, 0x6b, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x6d, 0x00, + 0x00, 0x6e, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x70, 0x00, + 0x00, 0x71, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x71, 0x00, 0x00, 0x71, 0x00, 0x00, 0x72, 0x00, 0x00, 0x73, 0x00, + 0x00, 0x72, 0x00, 0x00, 0x72, 0x00, 0x00, 0x73, 0x00, 0x00, 0x73, 0x00, 0x00, 0x74, 0x00, 0x00, 0x75, 0x00, 0x00, 0x74, 0x00, + 0x00, 0x74, 0x00, 0x00, 0x75, 0x00, 0x00, 0x75, 0x00, 0x00, 0x76, 0x00, 0x00, 0x77, 0x00, 0x00, 0x76, 0x00, 0x00, 0x76, 0x00, + 0x00, 0x77, 0x00, 0x00, 0x77, 0x00, 0x00, 0x78, 0x00, 0x00, 0x79, 0x00, 0x00, 0x78, 0x00, 0x00, 0x78, 0x00, 0x00, 0x79, 0x00, + 0x00, 0x79, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7b, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7e, 0x00, + 0x00, 0x7f, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x81, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, 0x00, 0x82, 0x00, 0x00, 0x83, 0x00, 0x00, 0x82, 0x00, + 0x00, 0x82, 0x00, 0x00, 0x83, 0x00, 0x00, 0x83, 0x00, 0x00, 0x84, 0x00, 0x00, 0x85, 0x00, 0x00, 0x84, 0x00, 0x00, 0x84, 0x00, + 0x00, 0x85, 0x00, 0x00, 0x85, 0x00, 0x00, 0x86, 0x00, 0x00, 0x87, 0x00, 0x00, 0x86, 0x00, 0x00, 0x86, 0x00, 0x00, 0x87, 0x00, + 0x00, 0x87, 0x00, 0x00, 0x88, 0x00, 0x00, 0x89, 0x00, 0x00, 0x88, 0x00, 0x00, 0x88, 0x00, 0x00, 0x89, 0x00, 0x00, 0x89, 0x00, + 0x00, 0x8a, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x8c, 0x00, + 0x00, 0x8d, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x8f, 0x00, + 0x00, 0x8e, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x90, 0x00, 0x00, 0x91, 0x00, 0x00, 0x90, 0x00, + 0x00, 0x90, 0x00, 0x00, 0x91, 0x00, 0x00, 0x91, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x02, 0x4b, 0x00, 0x00, + 0x01, 0x4b, 0x00, 0x00, 0x01, 0x4a, 0x00, 0x00, 0x02, 0x4a, 0x00, 0x00, 0x03, 0x4d, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x02, + 0x4d, 0x00, 0x00, 0x01, 0x4d, 0x00, 0x00, 0x01, 0x4c, 0x00, 0x00, 0x02, 0x4c, 0x00, 0x00, 0x03, 0x4f, 0x00, 0x00, 0x00, 0x4e, + 0x00, 0x00, 0x02, 0x4f, 0x00, 0x00, 0x01, 0x4f, 0x00, 0x00, 0x01, 0x4e, 0x00, 0x00, 0x02, 0x4e, 0x00, 0x00, 0x03, 0x51, 0x00, + 0x00, 0x00, 0x50, 0x00, 0x00, 0x02, 0x51, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x02, 0x50, 0x00, 0x00, + 0x03, 0x53, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x02, 0x53, 0x00, 0x00, 0x01, 0x53, 0x00, 0x00, 0x01, 0x52, 0x00, 0x00, 0x02, + 0x52, 0x00, 0x00, 0x03, 0x55, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x02, 0x55, 0x00, 0x00, 0x01, 0x55, 0x00, 0x00, 0x01, 0x54, + 0x00, 0x00, 0x02, 0x54, 0x00, 0x00, 0x03, 0x57, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x02, 0x57, 0x00, 0x00, 0x01, 0x57, 0x00, + 0x00, 0x01, 0x56, 0x00, 0x00, 0x02, 0x56, 0x00, 0x00, 0x03, 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x02, 0x59, 0x00, 0x00, + 0x01, 0x59, 0x00, 0x00, 0x01, 0x58, 0x00, 0x00, 0x02, 0x58, 0x00, 0x00, 0x03, 0x5b, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x02, + 0x5b, 0x00, 0x00, 0x01, 0x5b, 0x00, 0x00, 0x01, 0x5a, 0x00, 0x00, 0x02, 0x5a, 0x00, 0x00, 0x03, 0x5d, 0x00, 0x00, 0x00, 0x5c, + 0x00, 0x00, 0x02, 0x5d, 0x00, 0x00, 0x01, 0x5d, 0x00, 0x00, 0x01, 0x5c, 0x00, 0x00, 0x02, 0x5c, 0x00, 0x00, 0x03, 0x5f, 0x00, + 0x00, 0x00, 0x5e, 0x00, 0x00, 0x02, 0x5f, 0x00, 0x00, 0x01, 0x5f, 0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x02, 0x5e, 0x00, 0x00, + 0x03, 0x61, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x02, 0x61, 0x00, 0x00, 0x01, 0x61, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, 0x02, + 0x60, 0x00, 0x00, 0x03, 0x63, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x02, 0x63, 0x00, 0x00, 0x01, 0x63, 0x00, 0x00, 0x01, 0x62, + 0x00, 0x00, 0x02, 0x62, 0x00, 0x00, 0x03, 0x65, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x02, 0x65, 0x00, 0x00, 0x01, 0x65, 0x00, + 0x00, 0x01, 0x64, 0x00, 0x00, 0x02, 0x64, 0x00, 0x00, 0x03, 0x67, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x02, 0x67, 0x00, 0x00, + 0x01, 0x67, 0x00, 0x00, 0x01, 0x66, 0x00, 0x00, 0x02, 0x66, 0x00, 0x00, 0x03, 0x69, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, + 0x69, 0x00, 0x00, 0x01, 0x69, 0x00, 0x00, 0x01, 0x68, 0x00, 0x00, 0x02, 0x68, 0x00, 0x00, 0x03, 0x6b, 0x00, 0x00, 0x00, 0x6a, + 0x00, 0x00, 0x02, 0x6b, 0x00, 0x00, 0x01, 0x6b, 0x00, 0x00, 0x01, 0x6a, 0x00, 0x00, 0x02, 0x6a, 0x00, 0x00, 0x03, 0x6d, 0x00, + 0x00, 0x00, 0x6c, 0x00, 0x00, 0x02, 0x6d, 0x00, 0x00, 0x01, 0x6d, 0x00, 0x00, 0x01, 0x6c, 0x00, 0x00, 0x02, 0x6c, 0x00, 0x00, + 0x03, 0x6f, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x02, 0x6f, 0x00, 0x00, 0x01, 0x6f, 0x00, 0x00, 0x01, 0x6e, 0x00, 0x00, 0x02, + 0x6e, 0x00, 0x00, 0x03, 0x71, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x02, 0x71, 0x00, 0x00, 0x01, 0x71, 0x00, 0x00, 0x01, 0x70, + 0x00, 0x00, 0x02, 0x70, 0x00, 0x00, 0x03, 0x73, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x02, 0x73, 0x00, 0x00, 0x01, 0x73, 0x00, + 0x00, 0x01, 0x72, 0x00, 0x00, 0x02, 0x72, 0x00, 0x00, 0x03, 0x75, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x02, 0x75, 0x00, 0x00, + 0x01, 0x75, 0x00, 0x00, 0x01, 0x74, 0x00, 0x00, 0x02, 0x74, 0x00, 0x00, 0x03, 0x77, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x02, + 0x77, 0x00, 0x00, 0x01, 0x77, 0x00, 0x00, 0x01, 0x76, 0x00, 0x00, 0x02, 0x76, 0x00, 0x00, 0x03, 0x79, 0x00, 0x00, 0x00, 0x78, + 0x00, 0x00, 0x02, 0x79, 0x00, 0x00, 0x01, 0x79, 0x00, 0x00, 0x01, 0x78, 0x00, 0x00, 0x02, 0x78, 0x00, 0x00, 0x03, 0x7b, 0x00, + 0x00, 0x00, 0x7a, 0x00, 0x00, 0x02, 0x7b, 0x00, 0x00, 0x01, 0x7b, 0x00, 0x00, 0x01, 0x7a, 0x00, 0x00, 0x02, 0x7a, 0x00, 0x00, + 0x03, 0x7d, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x02, 0x7d, 0x00, 0x00, 0x01, 0x7d, 0x00, 0x00, 0x01, 0x7c, 0x00, 0x00, 0x02, + 0x7c, 0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x7e, + 0x00, 0x00, 0x02, 0x7e, 0x00, 0x00, 0x03, 0x81, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x02, 0x81, 0x00, 0x00, 0x01, 0x81, 0x00, + 0x00, 0x01, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x03, 0x83, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x02, 0x83, 0x00, 0x00, + 0x01, 0x83, 0x00, 0x00, 0x01, 0x82, 0x00, 0x00, 0x02, 0x82, 0x00, 0x00, 0x03, 0x85, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x02, + 0x85, 0x00, 0x00, 0x01, 0x85, 0x00, 0x00, 0x01, 0x84, 0x00, 0x00, 0x02, 0x84, 0x00, 0x00, 0x03, 0x87, 0x00, 0x00, 0x00, 0x86, + 0x00, 0x00, 0x02, 0x87, 0x00, 0x00, 0x01, 0x87, 0x00, 0x00, 0x01, 0x86, 0x00, 0x00, 0x02, 0x86, 0x00, 0x00, 0x03, 0x89, 0x00, + 0x00, 0x00, 0x88, 0x00, 0x00, 0x02, 0x89, 0x00, 0x00, 0x01, 0x89, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x02, 0x88, 0x00, 0x00, + 0x03, 0x8b, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x02, 0x8b, 0x00, 0x00, 0x01, 0x8b, 0x00, 0x00, 0x01, 0x8a, 0x00, 0x00, 0x02, + 0x8a, 0x00, 0x00, 0x03, 0x8d, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x02, 0x8d, 0x00, 0x00, 0x01, 0x8d, 0x00, 0x00, 0x01, 0x8c, + 0x00, 0x00, 0x02, 0x8c, 0x00, 0x00, 0x03, 0x8f, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x02, 0x8f, 0x00, 0x00, 0x01, 0x8f, 0x00, + 0x00, 0x01, 0x8e, 0x00, 0x00, 0x02, 0x8e, 0x00, 0x00, 0x03, 0x91, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x02, 0x91, 0x00, 0x00, + 0x01, 0x91, 0x00, 0x00, 0x01, 0x90, 0x00, 0x00, 0x02, 0x90, 0x00, 0x00, 0x03, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, + 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xe4, 0x9a, 0xf5, + 0x8a, 0xe4, 0x9a, 0xf5, 0x8a, 0xe4, 0x9a, 0xf5, 0x8a, 0xe4, 0x9a, 0xf5, 0x8a, 0xe4, 0x9a, 0xf5, 0x8a, 0xe4, 0x9a, 0xe4, 0x9a, + 0xd6, 0xa8, 0xe4, 0x9a, 0xd6, 0xa8, 0xe4, 0x9a, 0xd6, 0xa8, 0xe4, 0x9a, 0xd6, 0xa8, 0xe4, 0x9a, 0xd6, 0xa8, 0xe4, 0x9a, 0xd6, + 0xa8, 0xd6, 0xa8, 0xca, 0xb4, 0xd6, 0xa8, 0xca, 0xb4, 0xd6, 0xa8, 0xca, 0xb4, 0xd6, 0xa8, 0xca, 0xb4, 0xd6, 0xa8, 0xca, 0xb4, + 0xd6, 0xa8, 0xca, 0xb4, 0xca, 0xb4, 0xbf, 0xbf, 0xca, 0xb4, 0xbf, 0xbf, 0xca, 0xb4, 0xbf, 0xbf, 0xca, 0xb4, 0xbf, 0xbf, 0xca, + 0xb4, 0xbf, 0xbf, 0xca, 0xb4, 0xbf, 0xbf, 0xbf, 0xbf, 0xb4, 0xca, 0xbf, 0xbf, 0xb4, 0xca, 0xbf, 0xbf, 0xb4, 0xca, 0xbf, 0xbf, + 0xb4, 0xca, 0xbf, 0xbf, 0xb4, 0xca, 0xbf, 0xbf, 0xb4, 0xca, 0xb4, 0xca, 0xa8, 0xd6, 0xb4, 0xca, 0xa8, 0xd6, 0xb4, 0xca, 0xa8, + 0xd6, 0xb4, 0xca, 0xa8, 0xd6, 0xb4, 0xca, 0xa8, 0xd6, 0xb4, 0xca, 0xa8, 0xd6, 0xa8, 0xd6, 0x9a, 0xe4, 0xa8, 0xd6, 0x9a, 0xe4, + 0xa8, 0xd6, 0x9a, 0xe4, 0xa8, 0xd6, 0x9a, 0xe4, 0xa8, 0xd6, 0x9a, 0xe4, 0xa8, 0xd6, 0x9a, 0xe4, 0x9a, 0xe4, 0x8a, 0xf5, 0x9a, + 0xe4, 0x8a, 0xf5, 0x9a, 0xe4, 0x8a, 0xf5, 0x9a, 0xe4, 0x8a, 0xf5, 0x9a, 0xe4, 0x8a, 0xf5, 0x9a, 0xe4, 0x8a, 0xf5, 0x8a, 0xf5, + 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, + 0xf5, 0x75, 0xf5, 0x65, 0xe4, 0x75, 0xf5, 0x65, 0xe4, 0x75, 0xf5, 0x65, 0xe4, 0x75, 0xf5, 0x65, 0xe4, 0x75, 0xf5, 0x65, 0xe4, + 0x75, 0xf5, 0x65, 0xe4, 0x65, 0xe4, 0x57, 0xd6, 0x65, 0xe4, 0x57, 0xd6, 0x65, 0xe4, 0x57, 0xd6, 0x65, 0xe4, 0x57, 0xd6, 0x65, + 0xe4, 0x57, 0xd6, 0x65, 0xe4, 0x57, 0xd6, 0x57, 0xd6, 0x4b, 0xca, 0x57, 0xd6, 0x4b, 0xca, 0x57, 0xd6, 0x4b, 0xca, 0x57, 0xd6, + 0x4b, 0xca, 0x57, 0xd6, 0x4b, 0xca, 0x57, 0xd6, 0x4b, 0xca, 0x4b, 0xca, 0x40, 0xbf, 0x4b, 0xca, 0x40, 0xbf, 0x4b, 0xca, 0x40, + 0xbf, 0x4b, 0xca, 0x40, 0xbf, 0x4b, 0xca, 0x40, 0xbf, 0x4b, 0xca, 0x40, 0xbf, 0x40, 0xbf, 0x35, 0xb4, 0x40, 0xbf, 0x35, 0xb4, + 0x40, 0xbf, 0x35, 0xb4, 0x40, 0xbf, 0x35, 0xb4, 0x40, 0xbf, 0x35, 0xb4, 0x40, 0xbf, 0x35, 0xb4, 0x35, 0xb4, 0x29, 0xa8, 0x35, + 0xb4, 0x29, 0xa8, 0x35, 0xb4, 0x29, 0xa8, 0x35, 0xb4, 0x29, 0xa8, 0x35, 0xb4, 0x29, 0xa8, 0x35, 0xb4, 0x29, 0xa8, 0x29, 0xa8, + 0x1b, 0x9a, 0x29, 0xa8, 0x1b, 0x9a, 0x29, 0xa8, 0x1b, 0x9a, 0x29, 0xa8, 0x1b, 0x9a, 0x29, 0xa8, 0x1b, 0x9a, 0x29, 0xa8, 0x1b, + 0x9a, 0x1b, 0x9a, 0x0a, 0x8a, 0x1b, 0x9a, 0x0a, 0x8a, 0x1b, 0x9a, 0x0a, 0x8a, 0x1b, 0x9a, 0x0a, 0x8a, 0x1b, 0x9a, 0x0a, 0x8a, + 0x1b, 0x9a, 0x0a, 0x8a, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, + 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x75, 0x1b, 0x65, 0x0a, 0x75, 0x1b, 0x65, 0x0a, 0x75, 0x1b, 0x65, 0x0a, 0x75, + 0x1b, 0x65, 0x0a, 0x75, 0x1b, 0x65, 0x0a, 0x75, 0x1b, 0x65, 0x1b, 0x65, 0x29, 0x57, 0x1b, 0x65, 0x29, 0x57, 0x1b, 0x65, 0x29, + 0x57, 0x1b, 0x65, 0x29, 0x57, 0x1b, 0x65, 0x29, 0x57, 0x1b, 0x65, 0x29, 0x57, 0x29, 0x57, 0x35, 0x4b, 0x29, 0x57, 0x35, 0x4b, + 0x29, 0x57, 0x35, 0x4b, 0x29, 0x57, 0x35, 0x4b, 0x29, 0x57, 0x35, 0x4b, 0x29, 0x57, 0x35, 0x4b, 0x35, 0x4b, 0x40, 0x40, 0x35, + 0x4b, 0x40, 0x40, 0x35, 0x4b, 0x40, 0x40, 0x35, 0x4b, 0x40, 0x40, 0x35, 0x4b, 0x40, 0x40, 0x35, 0x4b, 0x40, 0x40, 0x40, 0x40, + 0x4b, 0x35, 0x40, 0x40, 0x4b, 0x35, 0x40, 0x40, 0x4b, 0x35, 0x40, 0x40, 0x4b, 0x35, 0x40, 0x40, 0x4b, 0x35, 0x40, 0x40, 0x4b, + 0x35, 0x4b, 0x35, 0x57, 0x29, 0x4b, 0x35, 0x57, 0x29, 0x4b, 0x35, 0x57, 0x29, 0x4b, 0x35, 0x57, 0x29, 0x4b, 0x35, 0x57, 0x29, + 0x4b, 0x35, 0x57, 0x29, 0x57, 0x29, 0x65, 0x1b, 0x57, 0x29, 0x65, 0x1b, 0x57, 0x29, 0x65, 0x1b, 0x57, 0x29, 0x65, 0x1b, 0x57, + 0x29, 0x65, 0x1b, 0x57, 0x29, 0x65, 0x1b, 0x65, 0x1b, 0x75, 0x0a, 0x65, 0x1b, 0x75, 0x0a, 0x65, 0x1b, 0x75, 0x0a, 0x65, 0x1b, + 0x75, 0x0a, 0x65, 0x1b, 0x75, 0x0a, 0x65, 0x1b, 0x75, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, + 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x8a, 0x0a, 0x9a, 0x1b, 0x8a, 0x0a, 0x9a, 0x1b, + 0x8a, 0x0a, 0x9a, 0x1b, 0x8a, 0x0a, 0x9a, 0x1b, 0x8a, 0x0a, 0x9a, 0x1b, 0x8a, 0x0a, 0x9a, 0x1b, 0x9a, 0x1b, 0xa8, 0x29, 0x9a, + 0x1b, 0xa8, 0x29, 0x9a, 0x1b, 0xa8, 0x29, 0x9a, 0x1b, 0xa8, 0x29, 0x9a, 0x1b, 0xa8, 0x29, 0x9a, 0x1b, 0xa8, 0x29, 0xa8, 0x29, + 0xb4, 0x35, 0xa8, 0x29, 0xb4, 0x35, 0xa8, 0x29, 0xb4, 0x35, 0xa8, 0x29, 0xb4, 0x35, 0xa8, 0x29, 0xb4, 0x35, 0xa8, 0x29, 0xb4, + 0x35, 0xb4, 0x35, 0xbf, 0x40, 0xb4, 0x35, 0xbf, 0x40, 0xb4, 0x35, 0xbf, 0x40, 0xb4, 0x35, 0xbf, 0x40, 0xb4, 0x35, 0xbf, 0x40, + 0xb4, 0x35, 0xbf, 0x40, 0xbf, 0x40, 0xca, 0x4b, 0xbf, 0x40, 0xca, 0x4b, 0xbf, 0x40, 0xca, 0x4b, 0xbf, 0x40, 0xca, 0x4b, 0xbf, + 0x40, 0xca, 0x4b, 0xbf, 0x40, 0xca, 0x4b, 0xca, 0x4b, 0xd6, 0x57, 0xca, 0x4b, 0xd6, 0x57, 0xca, 0x4b, 0xd6, 0x57, 0xca, 0x4b, + 0xd6, 0x57, 0xca, 0x4b, 0xd6, 0x57, 0xca, 0x4b, 0xd6, 0x57, 0xd6, 0x57, 0xe4, 0x65, 0xd6, 0x57, 0xe4, 0x65, 0xd6, 0x57, 0xe4, + 0x65, 0xd6, 0x57, 0xe4, 0x65, 0xd6, 0x57, 0xe4, 0x65, 0xd6, 0x57, 0xe4, 0x65, 0xe4, 0x65, 0xf5, 0x75, 0xe4, 0x65, 0xf5, 0x75, + 0xe4, 0x65, 0xf5, 0x75, 0xe4, 0x65, 0xf5, 0x75, 0xe4, 0x65, 0xf5, 0x75, 0xe4, 0x65, 0xf5, 0x75, 0x00, 0x00, 0x00, 0x24, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, + 0x00, 0x24, 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, + 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x21, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x21, 0x00, + 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x20, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, + 0x00, 0x1e, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1d, 0x00, + 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1d, 0x00, + 0x00, 0x1d, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, + 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1a, 0x00, + 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x19, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, + 0x00, 0x19, 0x00, 0x00, 0x18, 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x17, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x16, 0x00, + 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x15, 0x00, 0x00, 0x16, 0x00, + 0x00, 0x16, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x14, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, + 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x13, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x13, 0x00, + 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x12, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, + 0x00, 0x12, 0x00, 0x00, 0x11, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0f, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0f, 0x00, + 0x00, 0x0f, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, + 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0c, 0x00, + 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, + 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x27, 0x00, 0x00, 0x25, 0x00, + 0x00, 0x25, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, + 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x29, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x29, 0x00, + 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, + 0x00, 0x2a, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, + 0x00, 0x2c, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2d, 0x00, + 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2d, 0x00, + 0x00, 0x2d, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, + 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x30, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x31, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, + 0x00, 0x31, 0x00, 0x00, 0x32, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, + 0x00, 0x33, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x34, 0x00, + 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00, 0x34, 0x00, + 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x36, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, + 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x37, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x37, 0x00, + 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, + 0x00, 0x3a, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3b, 0x00, + 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3b, 0x00, + 0x00, 0x3b, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, + 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3e, 0x00, + 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, + 0x00, 0x3f, 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x41, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x42, 0x00, + 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x43, 0x00, 0x00, 0x42, 0x00, + 0x00, 0x42, 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x44, 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, + 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x45, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x45, 0x00, + 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x46, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, + 0x00, 0x46, 0x00, 0x00, 0x47, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, + 0x00, 0x48, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x49, 0x00, + 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x25, 0x00, 0x00, 0x49, 0x00, + 0x00, 0x49, 0x00, 0x00, 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x02, 0x00, 0x00, 0x23, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, + 0x00, 0x22, 0x00, 0x00, 0x24, 0x00, 0x00, 0x21, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, + 0x00, 0x23, 0x00, 0x00, 0x20, 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x22, 0x00, + 0x00, 0x1f, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x21, 0x00, 0x00, 0x1e, 0x00, + 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, + 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1b, 0x00, + 0x00, 0x1b, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, + 0x00, 0x1c, 0x00, 0x00, 0x19, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x1b, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x17, 0x00, + 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00, 0x16, 0x00, 0x00, 0x19, 0x00, + 0x00, 0x19, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x18, 0x00, 0x00, 0x15, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x17, 0x00, 0x00, 0x14, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x14, 0x00, + 0x00, 0x14, 0x00, 0x00, 0x16, 0x00, 0x00, 0x13, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, + 0x00, 0x15, 0x00, 0x00, 0x12, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, + 0x00, 0x11, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x13, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x12, 0x00, + 0x00, 0x12, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x11, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0d, 0x00, + 0x00, 0x0d, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0d, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, + 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x09, 0x00, 0x00, 0x06, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x05, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x24, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x49, 0x00, 0x00, 0x28, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x28, 0x00, 0x00, 0x25, 0x00, 0x00, 0x29, 0x00, 0x00, 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, + 0x00, 0x27, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x2b, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x29, 0x00, 0x00, 0x2c, 0x00, + 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2a, 0x00, + 0x00, 0x2a, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, + 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2f, 0x00, + 0x00, 0x2f, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x30, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x2e, 0x00, 0x00, 0x31, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x2f, 0x00, + 0x00, 0x32, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x30, 0x00, 0x00, 0x33, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x31, 0x00, 0x00, 0x34, 0x00, 0x00, 0x31, 0x00, + 0x00, 0x31, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x32, 0x00, 0x00, 0x35, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, + 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x33, 0x00, 0x00, 0x36, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x36, 0x00, + 0x00, 0x36, 0x00, 0x00, 0x34, 0x00, 0x00, 0x37, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, + 0x00, 0x35, 0x00, 0x00, 0x38, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x36, 0x00, + 0x00, 0x39, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x37, 0x00, 0x00, 0x3a, 0x00, + 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, + 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3d, 0x00, + 0x00, 0x3d, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, + 0x00, 0x3c, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3d, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x42, 0x00, 0x00, 0x3f, 0x00, + 0x00, 0x3f, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x40, 0x00, 0x00, 0x43, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x41, 0x00, 0x00, 0x44, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x44, 0x00, + 0x00, 0x44, 0x00, 0x00, 0x42, 0x00, 0x00, 0x45, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, + 0x00, 0x43, 0x00, 0x00, 0x46, 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x44, 0x00, + 0x00, 0x47, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x45, 0x00, 0x00, 0x48, 0x00, + 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x46, 0x00, 0x00, 0x49, 0x00, 0x00, 0x46, 0x00, + 0x00, 0x46, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x47, 0x00, 0x00, 0x25, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, + 0x00, 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x48, 0x00, 0x00, 0x27, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x27, 0x00, + 0x00, 0x27, 0x00, 0x00, 0x24, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x36, 0x24, 0x00, 0x00, 0x06, 0x24, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x4e, 0x23, 0x00, 0x00, 0x1e, 0x24, 0x00, 0x00, 0x36, 0x23, 0x00, 0x00, 0x06, 0x23, 0x00, + 0x00, 0x06, 0x24, 0x00, 0x00, 0x36, 0x24, 0x00, 0x00, 0x4e, 0x22, 0x00, 0x00, 0x1e, 0x23, 0x00, 0x00, 0x36, 0x22, 0x00, 0x00, + 0x06, 0x22, 0x00, 0x00, 0x06, 0x23, 0x00, 0x00, 0x36, 0x23, 0x00, 0x00, 0x4e, 0x21, 0x00, 0x00, 0x1e, 0x22, 0x00, 0x00, 0x36, + 0x21, 0x00, 0x00, 0x06, 0x21, 0x00, 0x00, 0x06, 0x22, 0x00, 0x00, 0x36, 0x22, 0x00, 0x00, 0x4e, 0x20, 0x00, 0x00, 0x1e, 0x21, + 0x00, 0x00, 0x36, 0x20, 0x00, 0x00, 0x06, 0x20, 0x00, 0x00, 0x06, 0x21, 0x00, 0x00, 0x36, 0x21, 0x00, 0x00, 0x4e, 0x1f, 0x00, + 0x00, 0x1e, 0x20, 0x00, 0x00, 0x36, 0x1f, 0x00, 0x00, 0x06, 0x1f, 0x00, 0x00, 0x06, 0x20, 0x00, 0x00, 0x36, 0x20, 0x00, 0x00, + 0x4e, 0x1e, 0x00, 0x00, 0x1e, 0x1f, 0x00, 0x00, 0x36, 0x1e, 0x00, 0x00, 0x06, 0x1e, 0x00, 0x00, 0x06, 0x1f, 0x00, 0x00, 0x36, + 0x1f, 0x00, 0x00, 0x4e, 0x1d, 0x00, 0x00, 0x1e, 0x1e, 0x00, 0x00, 0x36, 0x1d, 0x00, 0x00, 0x06, 0x1d, 0x00, 0x00, 0x06, 0x1e, + 0x00, 0x00, 0x36, 0x1e, 0x00, 0x00, 0x4e, 0x1c, 0x00, 0x00, 0x1e, 0x1d, 0x00, 0x00, 0x36, 0x1c, 0x00, 0x00, 0x06, 0x1c, 0x00, + 0x00, 0x06, 0x1d, 0x00, 0x00, 0x36, 0x1d, 0x00, 0x00, 0x4e, 0x1b, 0x00, 0x00, 0x1e, 0x1c, 0x00, 0x00, 0x36, 0x1b, 0x00, 0x00, + 0x06, 0x1b, 0x00, 0x00, 0x06, 0x1c, 0x00, 0x00, 0x36, 0x1c, 0x00, 0x00, 0x4e, 0x1a, 0x00, 0x00, 0x1e, 0x1b, 0x00, 0x00, 0x36, + 0x1a, 0x00, 0x00, 0x06, 0x1a, 0x00, 0x00, 0x06, 0x1b, 0x00, 0x00, 0x36, 0x1b, 0x00, 0x00, 0x4e, 0x19, 0x00, 0x00, 0x1e, 0x1a, + 0x00, 0x00, 0x36, 0x19, 0x00, 0x00, 0x06, 0x19, 0x00, 0x00, 0x06, 0x1a, 0x00, 0x00, 0x36, 0x1a, 0x00, 0x00, 0x4e, 0x18, 0x00, + 0x00, 0x1e, 0x19, 0x00, 0x00, 0x36, 0x18, 0x00, 0x00, 0x06, 0x18, 0x00, 0x00, 0x06, 0x19, 0x00, 0x00, 0x36, 0x19, 0x00, 0x00, + 0x4e, 0x17, 0x00, 0x00, 0x1e, 0x18, 0x00, 0x00, 0x36, 0x17, 0x00, 0x00, 0x06, 0x17, 0x00, 0x00, 0x06, 0x18, 0x00, 0x00, 0x36, + 0x18, 0x00, 0x00, 0x4e, 0x16, 0x00, 0x00, 0x1e, 0x17, 0x00, 0x00, 0x36, 0x16, 0x00, 0x00, 0x06, 0x16, 0x00, 0x00, 0x06, 0x17, + 0x00, 0x00, 0x36, 0x17, 0x00, 0x00, 0x4e, 0x15, 0x00, 0x00, 0x1e, 0x16, 0x00, 0x00, 0x36, 0x15, 0x00, 0x00, 0x06, 0x15, 0x00, + 0x00, 0x06, 0x16, 0x00, 0x00, 0x36, 0x16, 0x00, 0x00, 0x4e, 0x14, 0x00, 0x00, 0x1e, 0x15, 0x00, 0x00, 0x36, 0x14, 0x00, 0x00, + 0x06, 0x14, 0x00, 0x00, 0x06, 0x15, 0x00, 0x00, 0x36, 0x15, 0x00, 0x00, 0x4e, 0x13, 0x00, 0x00, 0x1e, 0x14, 0x00, 0x00, 0x36, + 0x13, 0x00, 0x00, 0x06, 0x13, 0x00, 0x00, 0x06, 0x14, 0x00, 0x00, 0x36, 0x14, 0x00, 0x00, 0x4e, 0x12, 0x00, 0x00, 0x1e, 0x13, + 0x00, 0x00, 0x36, 0x12, 0x00, 0x00, 0x06, 0x12, 0x00, 0x00, 0x06, 0x13, 0x00, 0x00, 0x36, 0x13, 0x00, 0x00, 0x4e, 0x11, 0x00, + 0x00, 0x1e, 0x12, 0x00, 0x00, 0x36, 0x11, 0x00, 0x00, 0x06, 0x11, 0x00, 0x00, 0x06, 0x12, 0x00, 0x00, 0x36, 0x12, 0x00, 0x00, + 0x4e, 0x10, 0x00, 0x00, 0x1e, 0x11, 0x00, 0x00, 0x36, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x00, 0x06, 0x11, 0x00, 0x00, 0x36, + 0x11, 0x00, 0x00, 0x4e, 0x0f, 0x00, 0x00, 0x1e, 0x10, 0x00, 0x00, 0x36, 0x0f, 0x00, 0x00, 0x06, 0x0f, 0x00, 0x00, 0x06, 0x10, + 0x00, 0x00, 0x36, 0x10, 0x00, 0x00, 0x4e, 0x0e, 0x00, 0x00, 0x1e, 0x0f, 0x00, 0x00, 0x36, 0x0e, 0x00, 0x00, 0x06, 0x0e, 0x00, + 0x00, 0x06, 0x0f, 0x00, 0x00, 0x36, 0x0f, 0x00, 0x00, 0x4e, 0x0d, 0x00, 0x00, 0x1e, 0x0e, 0x00, 0x00, 0x36, 0x0d, 0x00, 0x00, + 0x06, 0x0d, 0x00, 0x00, 0x06, 0x0e, 0x00, 0x00, 0x36, 0x0e, 0x00, 0x00, 0x4e, 0x0c, 0x00, 0x00, 0x1e, 0x0d, 0x00, 0x00, 0x36, + 0x0c, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x06, 0x0d, 0x00, 0x00, 0x36, 0x0d, 0x00, 0x00, 0x4e, 0x0b, 0x00, 0x00, 0x1e, 0x0c, + 0x00, 0x00, 0x36, 0x0b, 0x00, 0x00, 0x06, 0x0b, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x4e, 0x0a, 0x00, + 0x00, 0x1e, 0x0b, 0x00, 0x00, 0x36, 0x0a, 0x00, 0x00, 0x06, 0x0a, 0x00, 0x00, 0x06, 0x0b, 0x00, 0x00, 0x36, 0x0b, 0x00, 0x00, + 0x4e, 0x09, 0x00, 0x00, 0x1e, 0x0a, 0x00, 0x00, 0x36, 0x09, 0x00, 0x00, 0x06, 0x09, 0x00, 0x00, 0x06, 0x0a, 0x00, 0x00, 0x36, + 0x0a, 0x00, 0x00, 0x4e, 0x08, 0x00, 0x00, 0x1e, 0x09, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x06, 0x08, 0x00, 0x00, 0x06, 0x09, + 0x00, 0x00, 0x36, 0x09, 0x00, 0x00, 0x4e, 0x07, 0x00, 0x00, 0x1e, 0x08, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x06, 0x07, 0x00, + 0x00, 0x06, 0x08, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x4e, 0x06, 0x00, 0x00, 0x1e, 0x07, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, + 0x06, 0x06, 0x00, 0x00, 0x06, 0x07, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x4e, 0x05, 0x00, 0x00, 0x1e, 0x06, 0x00, 0x00, 0x36, + 0x05, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x4e, 0x04, 0x00, 0x00, 0x1e, 0x05, + 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x36, 0x05, 0x00, 0x00, 0x4e, 0x03, 0x00, + 0x00, 0x1e, 0x04, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, + 0x4e, 0x02, 0x00, 0x00, 0x1e, 0x03, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x36, + 0x03, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x02, + 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x4e, 0x27, 0x00, 0x00, 0x1e, 0x25, 0x00, 0x00, 0x36, 0x27, 0x00, 0x00, 0x06, 0x27, 0x00, + 0x00, 0x06, 0x25, 0x00, 0x00, 0x36, 0x25, 0x00, 0x00, 0x4e, 0x28, 0x00, 0x00, 0x1e, 0x27, 0x00, 0x00, 0x36, 0x28, 0x00, 0x00, + 0x06, 0x28, 0x00, 0x00, 0x06, 0x27, 0x00, 0x00, 0x36, 0x27, 0x00, 0x00, 0x4e, 0x29, 0x00, 0x00, 0x1e, 0x28, 0x00, 0x00, 0x36, + 0x29, 0x00, 0x00, 0x06, 0x29, 0x00, 0x00, 0x06, 0x28, 0x00, 0x00, 0x36, 0x28, 0x00, 0x00, 0x4e, 0x2a, 0x00, 0x00, 0x1e, 0x29, + 0x00, 0x00, 0x36, 0x2a, 0x00, 0x00, 0x06, 0x2a, 0x00, 0x00, 0x06, 0x29, 0x00, 0x00, 0x36, 0x29, 0x00, 0x00, 0x4e, 0x2b, 0x00, + 0x00, 0x1e, 0x2a, 0x00, 0x00, 0x36, 0x2b, 0x00, 0x00, 0x06, 0x2b, 0x00, 0x00, 0x06, 0x2a, 0x00, 0x00, 0x36, 0x2a, 0x00, 0x00, + 0x4e, 0x2c, 0x00, 0x00, 0x1e, 0x2b, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x06, 0x2c, 0x00, 0x00, 0x06, 0x2b, 0x00, 0x00, 0x36, + 0x2b, 0x00, 0x00, 0x4e, 0x2d, 0x00, 0x00, 0x1e, 0x2c, 0x00, 0x00, 0x36, 0x2d, 0x00, 0x00, 0x06, 0x2d, 0x00, 0x00, 0x06, 0x2c, + 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x4e, 0x2e, 0x00, 0x00, 0x1e, 0x2d, 0x00, 0x00, 0x36, 0x2e, 0x00, 0x00, 0x06, 0x2e, 0x00, + 0x00, 0x06, 0x2d, 0x00, 0x00, 0x36, 0x2d, 0x00, 0x00, 0x4e, 0x2f, 0x00, 0x00, 0x1e, 0x2e, 0x00, 0x00, 0x36, 0x2f, 0x00, 0x00, + 0x06, 0x2f, 0x00, 0x00, 0x06, 0x2e, 0x00, 0x00, 0x36, 0x2e, 0x00, 0x00, 0x4e, 0x30, 0x00, 0x00, 0x1e, 0x2f, 0x00, 0x00, 0x36, + 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x2f, 0x00, 0x00, 0x36, 0x2f, 0x00, 0x00, 0x4e, 0x31, 0x00, 0x00, 0x1e, 0x30, + 0x00, 0x00, 0x36, 0x31, 0x00, 0x00, 0x06, 0x31, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x36, 0x30, 0x00, 0x00, 0x4e, 0x32, 0x00, + 0x00, 0x1e, 0x31, 0x00, 0x00, 0x36, 0x32, 0x00, 0x00, 0x06, 0x32, 0x00, 0x00, 0x06, 0x31, 0x00, 0x00, 0x36, 0x31, 0x00, 0x00, + 0x4e, 0x33, 0x00, 0x00, 0x1e, 0x32, 0x00, 0x00, 0x36, 0x33, 0x00, 0x00, 0x06, 0x33, 0x00, 0x00, 0x06, 0x32, 0x00, 0x00, 0x36, + 0x32, 0x00, 0x00, 0x4e, 0x34, 0x00, 0x00, 0x1e, 0x33, 0x00, 0x00, 0x36, 0x34, 0x00, 0x00, 0x06, 0x34, 0x00, 0x00, 0x06, 0x33, + 0x00, 0x00, 0x36, 0x33, 0x00, 0x00, 0x4e, 0x35, 0x00, 0x00, 0x1e, 0x34, 0x00, 0x00, 0x36, 0x35, 0x00, 0x00, 0x06, 0x35, 0x00, + 0x00, 0x06, 0x34, 0x00, 0x00, 0x36, 0x34, 0x00, 0x00, 0x4e, 0x36, 0x00, 0x00, 0x1e, 0x35, 0x00, 0x00, 0x36, 0x36, 0x00, 0x00, + 0x06, 0x36, 0x00, 0x00, 0x06, 0x35, 0x00, 0x00, 0x36, 0x35, 0x00, 0x00, 0x4e, 0x37, 0x00, 0x00, 0x1e, 0x36, 0x00, 0x00, 0x36, + 0x37, 0x00, 0x00, 0x06, 0x37, 0x00, 0x00, 0x06, 0x36, 0x00, 0x00, 0x36, 0x36, 0x00, 0x00, 0x4e, 0x38, 0x00, 0x00, 0x1e, 0x37, + 0x00, 0x00, 0x36, 0x38, 0x00, 0x00, 0x06, 0x38, 0x00, 0x00, 0x06, 0x37, 0x00, 0x00, 0x36, 0x37, 0x00, 0x00, 0x4e, 0x39, 0x00, + 0x00, 0x1e, 0x38, 0x00, 0x00, 0x36, 0x39, 0x00, 0x00, 0x06, 0x39, 0x00, 0x00, 0x06, 0x38, 0x00, 0x00, 0x36, 0x38, 0x00, 0x00, + 0x4e, 0x3a, 0x00, 0x00, 0x1e, 0x39, 0x00, 0x00, 0x36, 0x3a, 0x00, 0x00, 0x06, 0x3a, 0x00, 0x00, 0x06, 0x39, 0x00, 0x00, 0x36, + 0x39, 0x00, 0x00, 0x4e, 0x3b, 0x00, 0x00, 0x1e, 0x3a, 0x00, 0x00, 0x36, 0x3b, 0x00, 0x00, 0x06, 0x3b, 0x00, 0x00, 0x06, 0x3a, + 0x00, 0x00, 0x36, 0x3a, 0x00, 0x00, 0x4e, 0x3c, 0x00, 0x00, 0x1e, 0x3b, 0x00, 0x00, 0x36, 0x3c, 0x00, 0x00, 0x06, 0x3c, 0x00, + 0x00, 0x06, 0x3b, 0x00, 0x00, 0x36, 0x3b, 0x00, 0x00, 0x4e, 0x3d, 0x00, 0x00, 0x1e, 0x3c, 0x00, 0x00, 0x36, 0x3d, 0x00, 0x00, + 0x06, 0x3d, 0x00, 0x00, 0x06, 0x3c, 0x00, 0x00, 0x36, 0x3c, 0x00, 0x00, 0x4e, 0x3e, 0x00, 0x00, 0x1e, 0x3d, 0x00, 0x00, 0x36, + 0x3e, 0x00, 0x00, 0x06, 0x3e, 0x00, 0x00, 0x06, 0x3d, 0x00, 0x00, 0x36, 0x3d, 0x00, 0x00, 0x4e, 0x3f, 0x00, 0x00, 0x1e, 0x3e, + 0x00, 0x00, 0x36, 0x3f, 0x00, 0x00, 0x06, 0x3f, 0x00, 0x00, 0x06, 0x3e, 0x00, 0x00, 0x36, 0x3e, 0x00, 0x00, 0x4e, 0x40, 0x00, + 0x00, 0x1e, 0x3f, 0x00, 0x00, 0x36, 0x40, 0x00, 0x00, 0x06, 0x40, 0x00, 0x00, 0x06, 0x3f, 0x00, 0x00, 0x36, 0x3f, 0x00, 0x00, + 0x4e, 0x41, 0x00, 0x00, 0x1e, 0x40, 0x00, 0x00, 0x36, 0x41, 0x00, 0x00, 0x06, 0x41, 0x00, 0x00, 0x06, 0x40, 0x00, 0x00, 0x36, + 0x40, 0x00, 0x00, 0x4e, 0x42, 0x00, 0x00, 0x1e, 0x41, 0x00, 0x00, 0x36, 0x42, 0x00, 0x00, 0x06, 0x42, 0x00, 0x00, 0x06, 0x41, + 0x00, 0x00, 0x36, 0x41, 0x00, 0x00, 0x4e, 0x43, 0x00, 0x00, 0x1e, 0x42, 0x00, 0x00, 0x36, 0x43, 0x00, 0x00, 0x06, 0x43, 0x00, + 0x00, 0x06, 0x42, 0x00, 0x00, 0x36, 0x42, 0x00, 0x00, 0x4e, 0x44, 0x00, 0x00, 0x1e, 0x43, 0x00, 0x00, 0x36, 0x44, 0x00, 0x00, + 0x06, 0x44, 0x00, 0x00, 0x06, 0x43, 0x00, 0x00, 0x36, 0x43, 0x00, 0x00, 0x4e, 0x45, 0x00, 0x00, 0x1e, 0x44, 0x00, 0x00, 0x36, + 0x45, 0x00, 0x00, 0x06, 0x45, 0x00, 0x00, 0x06, 0x44, 0x00, 0x00, 0x36, 0x44, 0x00, 0x00, 0x4e, 0x46, 0x00, 0x00, 0x1e, 0x45, + 0x00, 0x00, 0x36, 0x46, 0x00, 0x00, 0x06, 0x46, 0x00, 0x00, 0x06, 0x45, 0x00, 0x00, 0x36, 0x45, 0x00, 0x00, 0x4e, 0x47, 0x00, + 0x00, 0x1e, 0x46, 0x00, 0x00, 0x36, 0x47, 0x00, 0x00, 0x06, 0x47, 0x00, 0x00, 0x06, 0x46, 0x00, 0x00, 0x36, 0x46, 0x00, 0x00, + 0x4e, 0x48, 0x00, 0x00, 0x1e, 0x47, 0x00, 0x00, 0x36, 0x48, 0x00, 0x00, 0x06, 0x48, 0x00, 0x00, 0x06, 0x47, 0x00, 0x00, 0x36, + 0x47, 0x00, 0x00, 0x4e, 0x49, 0x00, 0x00, 0x1e, 0x48, 0x00, 0x00, 0x36, 0x49, 0x00, 0x00, 0x06, 0x49, 0x00, 0x00, 0x06, 0x48, + 0x00, 0x00, 0x36, 0x48, 0x00, 0x00, 0x4e, 0x25, 0x00, 0x00, 0x1e, 0x49, 0x00, 0x00, 0x36, 0x25, 0x00, 0x00, 0x06, 0x25, 0x00, + 0x00, 0x06, 0x49, 0x00, 0x00, 0x36, 0x49, 0x00, 0x00, 0x4e, 0x8c, 0xce, 0x48, 0x41, 0x74, 0x62, 0x4e, 0x41, 0x8c, 0xce, 0x48, + 0x41, 0x8c, 0xce, 0x48, 0x41, 0x74, 0x62, 0x4e, 0x41, 0x74, 0x62, 0x4e, 0x41, 0x74, 0x62, 0x4e, 0x41, 0x55, 0xf6, 0x53, 0x41, + 0x74, 0x62, 0x4e, 0x41, 0x74, 0x62, 0x4e, 0x41, 0x55, 0xf6, 0x53, 0x41, 0x55, 0xf6, 0x53, 0x41, 0x55, 0xf6, 0x53, 0x41, 0x62, + 0x8a, 0x59, 0x41, 0x55, 0xf6, 0x53, 0x41, 0x55, 0xf6, 0x53, 0x41, 0x62, 0x8a, 0x59, 0x41, 0x62, 0x8a, 0x59, 0x41, 0x62, 0x8a, + 0x59, 0x41, 0x60, 0x1e, 0x5f, 0x41, 0x62, 0x8a, 0x59, 0x41, 0x62, 0x8a, 0x59, 0x41, 0x60, 0x1e, 0x5f, 0x41, 0x60, 0x1e, 0x5f, + 0x41, 0x60, 0x1e, 0x5f, 0x41, 0x42, 0xb2, 0x64, 0x41, 0x60, 0x1e, 0x5f, 0x41, 0x60, 0x1e, 0x5f, 0x41, 0x42, 0xb2, 0x64, 0x41, + 0x42, 0xb2, 0x64, 0x41, 0x42, 0xb2, 0x64, 0x41, 0x41, 0x46, 0x6a, 0x41, 0x42, 0xb2, 0x64, 0x41, 0x42, 0xb2, 0x64, 0x41, 0x41, + 0x46, 0x6a, 0x41, 0x41, 0x46, 0x6a, 0x41, 0x41, 0x46, 0x6a, 0x41, 0x4d, 0xda, 0x6f, 0x41, 0x41, 0x46, 0x6a, 0x41, 0x41, 0x46, + 0x6a, 0x41, 0x4d, 0xda, 0x6f, 0x41, 0x4d, 0xda, 0x6f, 0x41, 0x4d, 0xda, 0x6f, 0x41, 0x2f, 0x6e, 0x75, 0x41, 0x4d, 0xda, 0x6f, + 0x41, 0x4d, 0xda, 0x6f, 0x41, 0x2f, 0x6e, 0x75, 0x41, 0x2f, 0x6e, 0x75, 0x41, 0x2f, 0x6e, 0x75, 0x41, 0x16, 0x02, 0x7b, 0x41, + 0x2f, 0x6e, 0x75, 0x41, 0x2f, 0x6e, 0x75, 0x41, 0x16, 0x02, 0x7b, 0x41, 0x16, 0x02, 0x7b, 0x41, 0x16, 0x02, 0x7b, 0x41, 0xfe, + 0x4a, 0x80, 0x41, 0x16, 0x02, 0x7b, 0x41, 0x16, 0x02, 0x7b, 0x41, 0xfe, 0x4a, 0x80, 0x41, 0xfe, 0x4a, 0x80, 0x41, 0xfe, 0x4a, + 0x80, 0x41, 0x0e, 0x15, 0x83, 0x41, 0xfe, 0x4a, 0x80, 0x41, 0xfe, 0x4a, 0x80, 0x41, 0x0e, 0x15, 0x83, 0x41, 0x0e, 0x15, 0x83, + 0x41, 0x0e, 0x15, 0x83, 0x41, 0xf7, 0xde, 0x85, 0x41, 0x0e, 0x15, 0x83, 0x41, 0x0e, 0x15, 0x83, 0x41, 0xf7, 0xde, 0x85, 0x41, + 0xf7, 0xde, 0x85, 0x41, 0xf7, 0xde, 0x85, 0x41, 0x11, 0xa9, 0x88, 0x41, 0xf7, 0xde, 0x85, 0x41, 0xf7, 0xde, 0x85, 0x41, 0x11, + 0xa9, 0x88, 0x41, 0x11, 0xa9, 0x88, 0x41, 0x11, 0xa9, 0x88, 0x41, 0x02, 0x73, 0x8b, 0x41, 0x11, 0xa9, 0x88, 0x41, 0x11, 0xa9, + 0x88, 0x41, 0x02, 0x73, 0x8b, 0x41, 0x02, 0x73, 0x8b, 0x41, 0x02, 0x73, 0x8b, 0x41, 0xef, 0x3c, 0x8e, 0x41, 0x02, 0x73, 0x8b, + 0x41, 0x02, 0x73, 0x8b, 0x41, 0xef, 0x3c, 0x8e, 0x41, 0xef, 0x3c, 0x8e, 0x41, 0xef, 0x3c, 0x8e, 0x41, 0xf5, 0x06, 0x91, 0x41, + 0xef, 0x3c, 0x8e, 0x41, 0xef, 0x3c, 0x8e, 0x41, 0xf5, 0x06, 0x91, 0x41, 0xf5, 0x06, 0x91, 0x41, 0xf5, 0x06, 0x91, 0x41, 0xe6, + 0xd0, 0x93, 0x41, 0xf5, 0x06, 0x91, 0x41, 0xf5, 0x06, 0x91, 0x41, 0xe6, 0xd0, 0x93, 0x41, 0xe6, 0xd0, 0x93, 0x41, 0xe6, 0xd0, + 0x93, 0x41, 0xdc, 0x9a, 0x96, 0x41, 0xe6, 0xd0, 0x93, 0x41, 0xe6, 0xd0, 0x93, 0x41, 0xdc, 0x9a, 0x96, 0x41, 0xdc, 0x9a, 0x96, + 0x41, 0xdc, 0x9a, 0x96, 0x41, 0xd3, 0x64, 0x99, 0x41, 0xdc, 0x9a, 0x96, 0x41, 0xdc, 0x9a, 0x96, 0x41, 0xd3, 0x64, 0x99, 0x41, + 0xd3, 0x64, 0x99, 0x41, 0xd3, 0x64, 0x99, 0x41, 0xe2, 0x2e, 0x9c, 0x41, 0xd3, 0x64, 0x99, 0x41, 0xd3, 0x64, 0x99, 0x41, 0xe2, + 0x2e, 0x9c, 0x41, 0xe2, 0x2e, 0x9c, 0x41, 0xe2, 0x2e, 0x9c, 0x41, 0xcb, 0xf8, 0x9e, 0x41, 0xe2, 0x2e, 0x9c, 0x41, 0xe2, 0x2e, + 0x9c, 0x41, 0xcb, 0xf8, 0x9e, 0x41, 0xcb, 0xf8, 0x9e, 0x41, 0xcb, 0xf8, 0x9e, 0x41, 0xd2, 0xc2, 0xa1, 0x41, 0xcb, 0xf8, 0x9e, + 0x41, 0xcb, 0xf8, 0x9e, 0x41, 0xd2, 0xc2, 0xa1, 0x41, 0xd2, 0xc2, 0xa1, 0x41, 0xd2, 0xc2, 0xa1, 0x41, 0xc4, 0x8c, 0xa4, 0x41, + 0xd2, 0xc2, 0xa1, 0x41, 0xd2, 0xc2, 0xa1, 0x41, 0xc4, 0x8c, 0xa4, 0x41, 0xc4, 0x8c, 0xa4, 0x41, 0xc4, 0x8c, 0xa4, 0x41, 0xcb, + 0x56, 0xa7, 0x41, 0xc4, 0x8c, 0xa4, 0x41, 0xc4, 0x8c, 0xa4, 0x41, 0xcb, 0x56, 0xa7, 0x41, 0xcb, 0x56, 0xa7, 0x41, 0xcb, 0x56, + 0xa7, 0x41, 0xb4, 0x20, 0xaa, 0x41, 0xcb, 0x56, 0xa7, 0x41, 0xcb, 0x56, 0xa7, 0x41, 0xb4, 0x20, 0xaa, 0x41, 0xb4, 0x20, 0xaa, + 0x41, 0xb4, 0x20, 0xaa, 0x41, 0xc4, 0xea, 0xac, 0x41, 0xb4, 0x20, 0xaa, 0x41, 0xb4, 0x20, 0xaa, 0x41, 0xc4, 0xea, 0xac, 0x41, + 0xc4, 0xea, 0xac, 0x41, 0xc4, 0xea, 0xac, 0x41, 0xba, 0xb4, 0xaf, 0x41, 0xc4, 0xea, 0xac, 0x41, 0xc4, 0xea, 0xac, 0x41, 0xba, + 0xb4, 0xaf, 0x41, 0xba, 0xb4, 0xaf, 0x41, 0xba, 0xb4, 0xaf, 0x41, 0xb0, 0x7e, 0xb2, 0x41, 0xba, 0xb4, 0xaf, 0x41, 0xba, 0xb4, + 0xaf, 0x41, 0xb0, 0x7e, 0xb2, 0x41, 0xb0, 0x7e, 0xb2, 0x41, 0xb0, 0x7e, 0xb2, 0x41, 0xa1, 0x48, 0xb5, 0x41, 0xb0, 0x7e, 0xb2, + 0x41, 0xb0, 0x7e, 0xb2, 0x41, 0xa1, 0x48, 0xb5, 0x41, 0xa1, 0x48, 0xb5, 0x41, 0xa1, 0x48, 0xb5, 0x41, 0xa7, 0x12, 0xb8, 0x41, + 0xa1, 0x48, 0xb5, 0x41, 0xa1, 0x48, 0xb5, 0x41, 0xa7, 0x12, 0xb8, 0x41, 0xa7, 0x12, 0xb8, 0x41, 0xa7, 0x12, 0xb8, 0x41, 0x94, + 0xdc, 0xba, 0x41, 0xa7, 0x12, 0xb8, 0x41, 0xa7, 0x12, 0xb8, 0x41, 0x94, 0xdc, 0xba, 0x41, 0x94, 0xdc, 0xba, 0x41, 0x94, 0xdc, + 0xba, 0x41, 0x85, 0xa6, 0xbd, 0x41, 0x94, 0xdc, 0xba, 0x41, 0x94, 0xdc, 0xba, 0x41, 0x85, 0xa6, 0xbd, 0x41, 0x85, 0xa6, 0xbd, + 0x41, 0x85, 0xa6, 0xbd, 0x41, 0x9f, 0x70, 0xc0, 0x41, 0x85, 0xa6, 0xbd, 0x41, 0x85, 0xa6, 0xbd, 0x41, 0x9f, 0x70, 0xc0, 0x41, + 0x9f, 0x70, 0xc0, 0x41, 0x9f, 0x70, 0xc0, 0x41, 0x88, 0x3a, 0xc3, 0x41, 0x9f, 0x70, 0xc0, 0x41, 0x9f, 0x70, 0xc0, 0x41, 0x88, + 0x3a, 0xc3, 0x41, 0x88, 0x3a, 0xc3, 0x41, 0x88, 0x3a, 0xc3, 0x41, 0x98, 0x04, 0xc6, 0x41, 0x88, 0x3a, 0xc3, 0x41, 0x88, 0x3a, + 0xc3, 0x41, 0x98, 0x04, 0xc6, 0x41, 0x98, 0x04, 0xc6, 0x41, 0x98, 0x04, 0xc6, 0x41, 0x8b, 0xce, 0xc8, 0x41, 0x98, 0x04, 0xc6, + 0x41, 0x98, 0x04, 0xc6, 0x41, 0x8b, 0xce, 0xc8, 0x41, 0x8b, 0xce, 0xc8, 0x41, 0x8c, 0xce, 0x48, 0x41, 0x74, 0x62, 0x4e, 0x41, + 0x8c, 0xce, 0x48, 0x41, 0x8c, 0xce, 0x48, 0x41, 0x74, 0x62, 0x4e, 0x41, 0x74, 0x62, 0x4e, 0x41, 0x74, 0x62, 0x4e, 0x41, 0x55, + 0xf6, 0x53, 0x41, 0x74, 0x62, 0x4e, 0x41, 0x74, 0x62, 0x4e, 0x41, 0x55, 0xf6, 0x53, 0x41, 0x55, 0xf6, 0x53, 0x41, 0x55, 0xf6, + 0x53, 0x41, 0x62, 0x8a, 0x59, 0x41, 0x55, 0xf6, 0x53, 0x41, 0x55, 0xf6, 0x53, 0x41, 0x62, 0x8a, 0x59, 0x41, 0x62, 0x8a, 0x59, + 0x41, 0x62, 0x8a, 0x59, 0x41, 0x60, 0x1e, 0x5f, 0x41, 0x62, 0x8a, 0x59, 0x41, 0x62, 0x8a, 0x59, 0x41, 0x60, 0x1e, 0x5f, 0x41, + 0x60, 0x1e, 0x5f, 0x41, 0x60, 0x1e, 0x5f, 0x41, 0x42, 0xb2, 0x64, 0x41, 0x60, 0x1e, 0x5f, 0x41, 0x60, 0x1e, 0x5f, 0x41, 0x42, + 0xb2, 0x64, 0x41, 0x42, 0xb2, 0x64, 0x41, 0x42, 0xb2, 0x64, 0x41, 0x41, 0x46, 0x6a, 0x41, 0x42, 0xb2, 0x64, 0x41, 0x42, 0xb2, + 0x64, 0x41, 0x41, 0x46, 0x6a, 0x41, 0x41, 0x46, 0x6a, 0x41, 0x41, 0x46, 0x6a, 0x41, 0x4d, 0xda, 0x6f, 0x41, 0x41, 0x46, 0x6a, + 0x41, 0x41, 0x46, 0x6a, 0x41, 0x4d, 0xda, 0x6f, 0x41, 0x4d, 0xda, 0x6f, 0x41, 0x4d, 0xda, 0x6f, 0x41, 0x2f, 0x6e, 0x75, 0x41, + 0x4d, 0xda, 0x6f, 0x41, 0x4d, 0xda, 0x6f, 0x41, 0x2f, 0x6e, 0x75, 0x41, 0x2f, 0x6e, 0x75, 0x41, 0x2f, 0x6e, 0x75, 0x41, 0x16, + 0x02, 0x7b, 0x41, 0x2f, 0x6e, 0x75, 0x41, 0x2f, 0x6e, 0x75, 0x41, 0x16, 0x02, 0x7b, 0x41, 0x16, 0x02, 0x7b, 0x41, 0x16, 0x02, + 0x7b, 0x41, 0xfe, 0x4a, 0x80, 0x41, 0x16, 0x02, 0x7b, 0x41, 0x16, 0x02, 0x7b, 0x41, 0xfe, 0x4a, 0x80, 0x41, 0xfe, 0x4a, 0x80, + 0x41, 0xfe, 0x4a, 0x80, 0x41, 0x0e, 0x15, 0x83, 0x41, 0xfe, 0x4a, 0x80, 0x41, 0xfe, 0x4a, 0x80, 0x41, 0x0e, 0x15, 0x83, 0x41, + 0x0e, 0x15, 0x83, 0x41, 0x0e, 0x15, 0x83, 0x41, 0xf7, 0xde, 0x85, 0x41, 0x0e, 0x15, 0x83, 0x41, 0x0e, 0x15, 0x83, 0x41, 0xf7, + 0xde, 0x85, 0x41, 0xf7, 0xde, 0x85, 0x41, 0xf7, 0xde, 0x85, 0x41, 0x11, 0xa9, 0x88, 0x41, 0xf7, 0xde, 0x85, 0x41, 0xf7, 0xde, + 0x85, 0x41, 0x11, 0xa9, 0x88, 0x41, 0x11, 0xa9, 0x88, 0x41, 0x11, 0xa9, 0x88, 0x41, 0x02, 0x73, 0x8b, 0x41, 0x11, 0xa9, 0x88, + 0x41, 0x11, 0xa9, 0x88, 0x41, 0x02, 0x73, 0x8b, 0x41, 0x02, 0x73, 0x8b, 0x41, 0x02, 0x73, 0x8b, 0x41, 0xef, 0x3c, 0x8e, 0x41, + 0x02, 0x73, 0x8b, 0x41, 0x02, 0x73, 0x8b, 0x41, 0xef, 0x3c, 0x8e, 0x41, 0xef, 0x3c, 0x8e, 0x41, 0xef, 0x3c, 0x8e, 0x41, 0xf5, + 0x06, 0x91, 0x41, 0xef, 0x3c, 0x8e, 0x41, 0xef, 0x3c, 0x8e, 0x41, 0xf5, 0x06, 0x91, 0x41, 0xf5, 0x06, 0x91, 0x41, 0xf5, 0x06, + 0x91, 0x41, 0xe6, 0xd0, 0x93, 0x41, 0xf5, 0x06, 0x91, 0x41, 0xf5, 0x06, 0x91, 0x41, 0xe6, 0xd0, 0x93, 0x41, 0xe6, 0xd0, 0x93, + 0x41, 0xe6, 0xd0, 0x93, 0x41, 0xdc, 0x9a, 0x96, 0x41, 0xe6, 0xd0, 0x93, 0x41, 0xe6, 0xd0, 0x93, 0x41, 0xdc, 0x9a, 0x96, 0x41, + 0xdc, 0x9a, 0x96, 0x41, 0xdc, 0x9a, 0x96, 0x41, 0xd3, 0x64, 0x99, 0x41, 0xdc, 0x9a, 0x96, 0x41, 0xdc, 0x9a, 0x96, 0x41, 0xd3, + 0x64, 0x99, 0x41, 0xd3, 0x64, 0x99, 0x41, 0xd3, 0x64, 0x99, 0x41, 0xe2, 0x2e, 0x9c, 0x41, 0xd3, 0x64, 0x99, 0x41, 0xd3, 0x64, + 0x99, 0x41, 0xe2, 0x2e, 0x9c, 0x41, 0xe2, 0x2e, 0x9c, 0x41, 0xe2, 0x2e, 0x9c, 0x41, 0xcb, 0xf8, 0x9e, 0x41, 0xe2, 0x2e, 0x9c, + 0x41, 0xe2, 0x2e, 0x9c, 0x41, 0xcb, 0xf8, 0x9e, 0x41, 0xcb, 0xf8, 0x9e, 0x41, 0xcb, 0xf8, 0x9e, 0x41, 0xd2, 0xc2, 0xa1, 0x41, + 0xcb, 0xf8, 0x9e, 0x41, 0xcb, 0xf8, 0x9e, 0x41, 0xd2, 0xc2, 0xa1, 0x41, 0xd2, 0xc2, 0xa1, 0x41, 0xd2, 0xc2, 0xa1, 0x41, 0xc4, + 0x8c, 0xa4, 0x41, 0xd2, 0xc2, 0xa1, 0x41, 0xd2, 0xc2, 0xa1, 0x41, 0xc4, 0x8c, 0xa4, 0x41, 0xc4, 0x8c, 0xa4, 0x41, 0xc4, 0x8c, + 0xa4, 0x41, 0xcb, 0x56, 0xa7, 0x41, 0xc4, 0x8c, 0xa4, 0x41, 0xc4, 0x8c, 0xa4, 0x41, 0xcb, 0x56, 0xa7, 0x41, 0xcb, 0x56, 0xa7, + 0x41, 0xcb, 0x56, 0xa7, 0x41, 0xb4, 0x20, 0xaa, 0x41, 0xcb, 0x56, 0xa7, 0x41, 0xcb, 0x56, 0xa7, 0x41, 0xb4, 0x20, 0xaa, 0x41, + 0xb4, 0x20, 0xaa, 0x41, 0xb4, 0x20, 0xaa, 0x41, 0xc4, 0xea, 0xac, 0x41, 0xb4, 0x20, 0xaa, 0x41, 0xb4, 0x20, 0xaa, 0x41, 0xc4, + 0xea, 0xac, 0x41, 0xc4, 0xea, 0xac, 0x41, 0xc4, 0xea, 0xac, 0x41, 0xba, 0xb4, 0xaf, 0x41, 0xc4, 0xea, 0xac, 0x41, 0xc4, 0xea, + 0xac, 0x41, 0xba, 0xb4, 0xaf, 0x41, 0xba, 0xb4, 0xaf, 0x41, 0xba, 0xb4, 0xaf, 0x41, 0xb0, 0x7e, 0xb2, 0x41, 0xba, 0xb4, 0xaf, + 0x41, 0xba, 0xb4, 0xaf, 0x41, 0xb0, 0x7e, 0xb2, 0x41, 0xb0, 0x7e, 0xb2, 0x41, 0xb0, 0x7e, 0xb2, 0x41, 0xa1, 0x48, 0xb5, 0x41, + 0xb0, 0x7e, 0xb2, 0x41, 0xb0, 0x7e, 0xb2, 0x41, 0xa1, 0x48, 0xb5, 0x41, 0xa1, 0x48, 0xb5, 0x41, 0xa1, 0x48, 0xb5, 0x41, 0xa7, + 0x12, 0xb8, 0x41, 0xa1, 0x48, 0xb5, 0x41, 0xa1, 0x48, 0xb5, 0x41, 0xa7, 0x12, 0xb8, 0x41, 0xa7, 0x12, 0xb8, 0x41, 0xa7, 0x12, + 0xb8, 0x41, 0x94, 0xdc, 0xba, 0x41, 0xa7, 0x12, 0xb8, 0x41, 0xa7, 0x12, 0xb8, 0x41, 0x94, 0xdc, 0xba, 0x41, 0x94, 0xdc, 0xba, + 0x41, 0x94, 0xdc, 0xba, 0x41, 0x85, 0xa6, 0xbd, 0x41, 0x94, 0xdc, 0xba, 0x41, 0x94, 0xdc, 0xba, 0x41, 0x85, 0xa6, 0xbd, 0x41, + 0x85, 0xa6, 0xbd, 0x41, 0x85, 0xa6, 0xbd, 0x41, 0x9f, 0x70, 0xc0, 0x41, 0x85, 0xa6, 0xbd, 0x41, 0x85, 0xa6, 0xbd, 0x41, 0x9f, + 0x70, 0xc0, 0x41, 0x9f, 0x70, 0xc0, 0x41, 0x9f, 0x70, 0xc0, 0x41, 0x88, 0x3a, 0xc3, 0x41, 0x9f, 0x70, 0xc0, 0x41, 0x9f, 0x70, + 0xc0, 0x41, 0x88, 0x3a, 0xc3, 0x41, 0x88, 0x3a, 0xc3, 0x41, 0x88, 0x3a, 0xc3, 0x41, 0x98, 0x04, 0xc6, 0x41, 0x88, 0x3a, 0xc3, + 0x41, 0x88, 0x3a, 0xc3, 0x41, 0x98, 0x04, 0xc6, 0x41, 0x98, 0x04, 0xc6, 0x41, 0x98, 0x04, 0xc6, 0x41, 0x8b, 0xce, 0xc8, 0x41, + 0x98, 0x04, 0xc6, 0x41, 0x98, 0x04, 0xc6, 0x41, 0x8b, 0xce, 0xc8, 0x41, 0x8b, 0xce, 0xc8, 0x41, + ]), + }, +}; diff --git a/test-apps/testbed/frontend/TileIO.data.1.2.ts b/test-apps/testbed/frontend/TileIO.data.1.2.ts new file mode 100644 index 0000000..583b8d5 --- /dev/null +++ b/test-apps/testbed/frontend/TileIO.data.1.2.ts @@ -0,0 +1,1696 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { IModelTileIO } from "@bentley/imodeljs-frontend/lib/tile"; + +// Binary tile data produced using version 1.2 of the tile format. +export const TILE_DATA_1_2 = { + versionMajor: 1, + versionMinor: 2, + headerLength: 84, + rectangle: { + flags: IModelTileIO.Flags.None, + bytes: new Uint8Array([ + 0x69, 0x4d, 0x64, 0x6c, 0x02, 0x00, 0x01, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x4b, 0x37, 0x89, + 0x41, 0x00, 0x04, 0xc0, 0xc7, 0x4b, 0x37, 0x89, 0x41, 0x00, 0x14, 0xc0, 0x00, 0x00, 0xbd, 0x9a, 0xf2, 0xd7, 0x5a, 0xbe, + 0x39, 0xb4, 0xc8, 0x76, 0xbe, 0xff, 0x03, 0x40, 0x39, 0xb4, 0xc8, 0x76, 0xbe, 0xff, 0x13, 0x40, 0x00, 0x00, 0xbd, 0x9a, + 0xf2, 0xd7, 0x5a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc4, 0x09, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, + 0x02, 0x00, 0x00, 0x00, 0x50, 0x09, 0x00, 0x00, 0x50, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, + 0x78, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x36, 0x34, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x30, 0x7d, 0x2c, + 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, + 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, + 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x35, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, + 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x37, 0x32, 0x2c, + 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x32, 0x35, 0x32, 0x7d, 0x2c, 0x22, 0x62, + 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, + 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, + 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x37, 0x32, 0x2c, 0x22, + 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x38, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, + 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x38, 0x2c, 0x22, 0x62, 0x79, 0x74, + 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x36, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, + 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, + 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, + 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, + 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x22, 0x3a, 0x33, 0x39, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, + 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x37, 0x32, 0x2c, + 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x33, 0x32, 0x34, 0x7d, 0x7d, 0x2c, 0x22, + 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4b, 0x48, + 0x52, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x57, 0x45, 0x42, 0x33, + 0x44, 0x5f, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, + 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x66, 0x69, + 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, + 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, + 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, + 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, + 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x2c, 0x22, + 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x7d, 0x7d, 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x7b, + 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, + 0x69, 0x76, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x65, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x6f, + 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, + 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, + 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, + 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, + 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, + 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x22, 0x3a, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, + 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, + 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, + 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x7d, 0x7d, 0x2c, 0x22, + 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x2c, 0x22, 0x73, + 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, + 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x2c, 0x22, + 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x7d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x76, + 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, + 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x44, 0x22, 0x3a, 0x30, 0x2c, 0x22, + 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, + 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, + 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, + 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x70, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, + 0x3a, 0x5b, 0x37, 0x2e, 0x36, 0x33, 0x30, 0x36, 0x35, 0x35, 0x33, 0x37, 0x34, 0x39, 0x39, 0x30, 0x34, 0x36, 0x31, 0x31, + 0x65, 0x2d, 0x30, 0x35, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, + 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x31, 0x35, 0x32, 0x36, 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, + 0x39, 0x32, 0x32, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, + 0x31, 0x2e, 0x35, 0x32, 0x36, 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, 0x2d, + 0x30, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x2d, 0x32, 0x2e, 0x35, 0x30, 0x30, 0x33, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x36, 0x2c, 0x2d, 0x35, 0x2e, 0x30, 0x30, 0x30, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x31, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, + 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, + 0x32, 0x2e, 0x35, 0x30, 0x30, 0x33, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x36, 0x2c, 0x35, + 0x2e, 0x30, 0x30, 0x30, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x31, 0x2c, 0x30, 0x2e, + 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, + 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x32, 0x2e, 0x35, 0x30, 0x30, 0x33, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x36, 0x2c, 0x2d, 0x35, 0x2e, 0x30, 0x30, 0x30, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x31, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, + 0x7d, 0x2c, 0x22, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, + 0x38, 0x30, 0x2c, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, + 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4e, 0x6f, 0x64, 0x65, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, + 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, + 0x3a, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, + 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, + 0x3a, 0x7b, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, + 0x22, 0x5d, 0x7d, 0x7d, 0x7d, 0x0a, 0x20, 0x20, 0x03, 0x00, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0xf8, 0xff, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0xf8, 0xff, 0xf8, 0xff, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x03, 0x00, 0xf8, 0xff, + 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x01, + 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x1e, 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x06, + 0x02, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x4e, 0x03, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x36, + 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x1e, + 0x03, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x4e, + ]), + }, + lineString: { + flags: IModelTileIO.Flags.None, + bytes: new Uint8Array([ + 0x69, 0x4d, 0x64, 0x6c, 0x02, 0x00, 0x01, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xf1, 0xd2, 0x4d, + 0x62, 0x00, 0x1e, 0xc0, 0xc7, 0x4b, 0x37, 0x89, 0x41, 0x00, 0x24, 0xc0, 0x00, 0x00, 0xbd, 0x9a, 0xf2, 0xd7, 0x5a, 0xbe, + 0x55, 0x0e, 0x2d, 0xb2, 0x9d, 0xff, 0x1d, 0x40, 0x39, 0xb4, 0xc8, 0x76, 0xbe, 0xff, 0x23, 0x40, 0x00, 0x00, 0xbd, 0x9a, + 0xf2, 0xd7, 0x5a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0x0a, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, + 0x02, 0x00, 0x00, 0x00, 0x2c, 0x0a, 0x00, 0x00, 0x58, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, + 0x78, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x37, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x30, 0x7d, 0x2c, + 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, + 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x34, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x37, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, + 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, + 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x34, 0x35, 0x36, 0x2c, 0x22, 0x62, + 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x37, 0x36, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, + 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, + 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x34, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x34, 0x31, 0x36, 0x7d, 0x7d, 0x2c, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4b, 0x48, 0x52, 0x5f, 0x62, 0x69, 0x6e, 0x61, + 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x57, 0x45, 0x42, 0x33, 0x44, 0x5f, 0x71, 0x75, 0x61, 0x6e, + 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, + 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, + 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, 0x69, + 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, + 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, + 0x72, 0x22, 0x3a, 0x36, 0x35, 0x35, 0x33, 0x35, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, + 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, + 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, + 0x35, 0x33, 0x35, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, + 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, + 0x31, 0x7d, 0x7d, 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, + 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, 0x3a, + 0x5b, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x30, 0x22, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, + 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, + 0x61, 0x6c, 0x30, 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x30, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, + 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, + 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x36, + 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x44, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x68, 0x61, 0x73, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, + 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, + 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x33, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, + 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x30, 0x2e, + 0x30, 0x30, 0x30, 0x32, 0x32, 0x38, 0x39, 0x31, 0x39, 0x36, 0x36, 0x31, 0x32, 0x34, 0x39, 0x37, 0x31, 0x33, 0x38, 0x37, + 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, + 0x30, 0x30, 0x33, 0x30, 0x35, 0x32, 0x32, 0x36, 0x32, 0x31, 0x34, 0x39, 0x39, 0x39, 0x36, 0x31, 0x38, 0x34, 0x34, 0x2c, + 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, + 0x36, 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, + 0x2e, 0x30, 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x2c, 0x31, 0x2e, 0x30, + 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x37, 0x2e, 0x35, 0x30, + 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x31, 0x30, 0x2e, 0x30, 0x30, + 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, + 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, + 0x3a, 0x5b, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x7d, 0x2c, 0x22, 0x75, + 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x35, 0x33, 0x35, 0x2c, 0x22, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x38, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x6e, 0x6f, 0x64, 0x65, + 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4e, 0x6f, 0x64, 0x65, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x22, 0x4d, 0x65, 0x73, + 0x68, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x22, 0x64, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x73, 0x22, + 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x6e, + 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x5d, 0x7d, 0x7d, + 0x7d, 0x0a, 0x20, 0x20, 0x03, 0x00, 0xf8, 0xff, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x55, 0x55, 0xf8, 0xff, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x03, + 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0d, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x90, + 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x60, + 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x90, + 0x02, 0x00, 0x00, 0x09, 0x02, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x60, + 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, + 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x39, + 0x03, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x39, 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x60, + 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x51, + 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0e, + 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x21, + 0x03, 0x00, 0x00, 0x39, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x90, + 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x90, + 0x03, 0x00, 0x00, 0x51, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x0c, 0x04, 0x00, 0x00, 0x60, + 0x04, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0f, 0x04, 0x00, 0x00, 0x0e, + 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0e, + 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x21, + 0x04, 0x00, 0x00, 0x33, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x33, 0x04, 0x00, 0x00, 0x90, + 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x09, 0x05, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x90, + 0x04, 0x00, 0x00, 0x4b, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x0c, 0x05, 0x00, 0x00, 0x60, + 0x05, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0f, 0x05, 0x00, 0x00, 0x0e, + ]), + }, + lineStrings: { + flags: IModelTileIO.Flags.None, + bytes: new Uint8Array([ + 0x69, 0x4d, 0x64, 0x6c, 0x02, 0x00, 0x01, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xf1, 0xd2, 0x4d, + 0x62, 0x00, 0x1e, 0xc0, 0xab, 0xf1, 0xd2, 0x4d, 0x62, 0x00, 0x3e, 0xc0, 0x00, 0x00, 0xbd, 0x9a, 0xf2, 0xd7, 0x5a, 0xbe, + 0x55, 0x0e, 0x2d, 0xb2, 0x9d, 0xff, 0x1d, 0x40, 0x55, 0x0e, 0x2d, 0xb2, 0x9d, 0xff, 0x3d, 0x40, 0x00, 0x00, 0xbd, 0x9a, + 0xf2, 0xd7, 0x5a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x68, 0x18, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x67, 0x6c, 0x54, 0x46, 0x02, 0x00, 0x00, 0x00, 0xdc, 0x17, 0x00, 0x00, 0x88, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x56, + 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, + 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x22, 0x3a, 0x37, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, + 0x3a, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, + 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x35, 0x32, 0x2c, 0x22, 0x62, 0x79, + 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x32, 0x31, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, + 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x34, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x22, 0x3a, 0x37, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x22, 0x3a, + 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, + 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x36, 0x38, 0x34, + 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x33, 0x36, 0x38, 0x7d, 0x2c, + 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, + 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x22, 0x3a, 0x34, 0x35, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, + 0x37, 0x36, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, + 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, + 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, + 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x31, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x22, 0x3a, 0x32, 0x37, 0x33, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, + 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x34, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x22, 0x3a, 0x34, 0x31, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x36, 0x38, 0x34, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x32, 0x30, + 0x35, 0x32, 0x7d, 0x7d, 0x2c, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, + 0x22, 0x3a, 0x5b, 0x22, 0x4b, 0x48, 0x52, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, + 0x2c, 0x22, 0x57, 0x45, 0x42, 0x33, 0x44, 0x5f, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, 0x6d, 0x61, + 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, + 0x22, 0x3a, 0x7b, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x31, 0x36, 0x37, 0x31, 0x31, + 0x39, 0x33, 0x35, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, + 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, + 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x31, 0x36, 0x37, 0x31, 0x31, 0x39, 0x33, + 0x35, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6c, 0x69, + 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x7d, + 0x2c, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, + 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x31, 0x35, 0x37, 0x39, 0x30, 0x30, 0x38, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, + 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, + 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, + 0x6f, 0x72, 0x22, 0x3a, 0x31, 0x35, 0x37, 0x39, 0x30, 0x30, 0x38, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, + 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x34, 0x31, 0x37, 0x37, 0x30, 0x36, 0x36, 0x32, 0x33, 0x32, 0x2c, 0x22, 0x6c, 0x69, + 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x7d, + 0x7d, 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, 0x6f, + 0x6f, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x7b, + 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x30, 0x22, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, + 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x30, 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x30, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, + 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, + 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x36, 0x2c, 0x22, + 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x44, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, + 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x33, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, + 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x30, 0x2e, 0x30, 0x30, + 0x30, 0x32, 0x32, 0x38, 0x39, 0x31, 0x39, 0x36, 0x36, 0x31, 0x32, 0x34, 0x39, 0x37, 0x31, 0x33, 0x38, 0x37, 0x2c, 0x30, + 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, + 0x39, 0x31, 0x35, 0x36, 0x37, 0x38, 0x36, 0x34, 0x34, 0x39, 0x39, 0x38, 0x38, 0x35, 0x35, 0x35, 0x30, 0x2c, 0x30, 0x2e, + 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, 0x31, + 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, 0x30, + 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, + 0x2c, 0x2d, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, + 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, + 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, + 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x34, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, + 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, + 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, + 0x2d, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, + 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x7d, 0x2c, 0x22, 0x75, 0x6e, 0x69, + 0x66, 0x6f, 0x72, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x31, 0x36, 0x37, 0x31, 0x31, 0x39, 0x33, 0x35, 0x2c, + 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x38, 0x7d, 0x7d, 0x2c, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x22, 0x2c, 0x22, 0x69, 0x73, + 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, + 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x31, 0x22, 0x2c, 0x22, 0x6e, 0x65, + 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, + 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x31, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, + 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x22, 0x2c, 0x22, 0x74, 0x79, + 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, + 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, + 0x31, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x31, 0x32, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x32, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, + 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x33, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x30, 0x2e, 0x30, + 0x30, 0x30, 0x32, 0x32, 0x38, 0x39, 0x31, 0x39, 0x36, 0x36, 0x31, 0x32, 0x34, 0x39, 0x37, 0x31, 0x33, 0x38, 0x37, 0x2c, + 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, + 0x30, 0x39, 0x31, 0x35, 0x36, 0x37, 0x38, 0x36, 0x34, 0x34, 0x39, 0x39, 0x38, 0x38, 0x35, 0x35, 0x35, 0x30, 0x2c, 0x30, + 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, + 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, + 0x30, 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x32, 0x2c, 0x2d, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x37, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, + 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x37, 0x2e, 0x35, 0x30, 0x31, + 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, + 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, + 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, + 0x5b, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, + 0x2c, 0x2d, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, + 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x7d, 0x2c, 0x22, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x38, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, + 0x3a, 0x7b, 0x22, 0x4e, 0x6f, 0x64, 0x65, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, + 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x22, 0x64, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, + 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x6e, 0x6f, 0x64, + 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x5d, 0x7d, 0x7d, 0x7d, 0x0a, + 0x03, 0x00, 0x55, 0x55, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0xac, 0x2a, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55, + 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf8, 0xff, 0xac, 0x2a, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x39, + 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, + 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0f, + 0x00, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x60, + 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x09, + 0x02, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0d, + 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x60, + 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x60, + 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x39, 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x90, + 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x51, 0x02, 0x00, 0x00, 0x90, + 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, + 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x21, 0x03, 0x00, 0x00, 0x39, + 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x60, + 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x51, + 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x0c, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0e, + 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0f, 0x04, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x90, + 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x0d, + 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x21, 0x04, 0x00, 0x00, 0x33, + 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x33, 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x60, + 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x09, 0x05, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x4b, + 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x0c, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0e, + 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0f, 0x05, 0x00, 0x00, 0x0e, 0x03, 0x00, 0xa7, 0xaa, + 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x55, 0x55, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x55, 0x55, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x55, 0x55, 0xa7, 0xaa, 0xfe, 0x7f, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x55, 0x55, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xfe, 0x7f, + 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0xf8, 0xff, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x03, 0x00, 0xa7, 0xaa, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x55, 0x55, 0x50, 0xd5, 0xfe, 0x7f, 0x01, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x55, 0x55, 0xf8, 0xff, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xa7, 0xaa, + 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x50, 0xd5, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, + 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x06, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, + 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x09, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1b, + 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x90, + 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x90, + 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x90, + 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, + 0x02, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x36, + 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x09, 0x02, 0x00, 0x00, 0x09, + 0x01, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, + 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0f, + 0x02, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x60, + 0x02, 0x00, 0x00, 0x39, 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x06, + 0x03, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x51, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0d, + 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x90, + 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x21, 0x03, 0x00, 0x00, 0x39, 0x04, 0x00, 0x00, 0x60, + 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x90, + 0x04, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x51, 0x04, 0x00, 0x00, 0x60, + 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x0c, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x0d, + 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0f, 0x04, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0d, + 0x03, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x90, + 0x03, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x21, 0x04, 0x00, 0x00, 0x33, 0x05, 0x00, 0x00, 0x60, + 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x33, 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x90, + 0x05, 0x00, 0x00, 0x09, 0x05, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x4b, 0x05, 0x00, 0x00, 0x60, + 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x0c, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x0d, + 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0f, 0x05, 0x00, 0x00, 0x0e, 0x07, 0x00, 0x00, 0x1b, 0x06, 0x00, 0x00, 0x39, + 0x07, 0x00, 0x00, 0x60, 0x07, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x39, 0x06, 0x00, 0x00, 0x90, 0x07, 0x00, 0x00, 0x60, + 0x06, 0x00, 0x00, 0x90, 0x07, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x51, + 0x06, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x0d, 0x06, 0x00, 0x00, 0x0c, 0x06, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x0e, + 0x06, 0x00, 0x00, 0x0d, 0x06, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x0f, 0x06, 0x00, 0x00, 0x0e, 0x08, 0x00, 0x00, 0x21, + 0x07, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x60, 0x07, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x90, + 0x08, 0x00, 0x00, 0x60, 0x07, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x09, 0x08, 0x00, 0x00, 0x09, 0x07, 0x00, 0x00, 0x90, + 0x07, 0x00, 0x00, 0x4e, 0x08, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x0d, 0x08, 0x00, 0x00, 0x0c, 0x08, 0x00, 0x00, 0x60, + 0x08, 0x00, 0x00, 0x0e, 0x08, 0x00, 0x00, 0x0d, 0x08, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x0f, 0x08, 0x00, 0x00, 0x0e, + 0x09, 0x00, 0x00, 0x1e, 0x08, 0x00, 0x00, 0x39, 0x09, 0x00, 0x00, 0x60, 0x09, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x39, + 0x08, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x06, 0x09, 0x00, 0x00, 0x06, + 0x08, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x51, 0x08, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x0d, 0x08, 0x00, 0x00, 0x0c, + 0x08, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x0e, 0x08, 0x00, 0x00, 0x0d, 0x08, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x0f, + 0x08, 0x00, 0x00, 0x0e, 0x0a, 0x00, 0x00, 0x21, 0x09, 0x00, 0x00, 0x39, 0x0a, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x60, + 0x09, 0x00, 0x00, 0x39, 0x09, 0x00, 0x00, 0x90, 0x0a, 0x00, 0x00, 0x60, 0x09, 0x00, 0x00, 0x90, 0x0a, 0x00, 0x00, 0x09, + 0x0a, 0x00, 0x00, 0x09, 0x09, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x51, 0x0a, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x0d, + 0x0a, 0x00, 0x00, 0x0c, 0x0a, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x0e, 0x0a, 0x00, 0x00, 0x0d, 0x0a, 0x00, 0x00, 0x60, + 0x0a, 0x00, 0x00, 0x0f, 0x0a, 0x00, 0x00, 0x0e, 0x09, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x0d, 0x09, 0x00, 0x00, 0x0c, + 0x09, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x0e, 0x09, 0x00, 0x00, 0x0d, 0x09, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x0f, + 0x09, 0x00, 0x00, 0x0e, 0x0b, 0x00, 0x00, 0x21, 0x0a, 0x00, 0x00, 0x33, 0x0b, 0x00, 0x00, 0x60, 0x0b, 0x00, 0x00, 0x60, + 0x0a, 0x00, 0x00, 0x33, 0x0a, 0x00, 0x00, 0x90, 0x0b, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x90, 0x0b, 0x00, 0x00, 0x09, + 0x0b, 0x00, 0x00, 0x09, 0x0a, 0x00, 0x00, 0x90, 0x0a, 0x00, 0x00, 0x4b, 0x0b, 0x00, 0x00, 0x60, 0x0b, 0x00, 0x00, 0x0d, + 0x0b, 0x00, 0x00, 0x0c, 0x0b, 0x00, 0x00, 0x60, 0x0b, 0x00, 0x00, 0x0e, 0x0b, 0x00, 0x00, 0x0d, 0x0b, 0x00, 0x00, 0x60, + 0x0b, 0x00, 0x00, 0x0f, 0x0b, 0x00, 0x00, 0x0e, + ]), + }, + triangles: { + flags: IModelTileIO.Flags.None, + bytes: new Uint8Array([ + 0x69, 0x4d, 0x64, 0x6c, 0x02, 0x00, 0x01, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xf1, 0xd2, 0x4d, + 0x62, 0x00, 0x1e, 0xc0, 0xc7, 0x4b, 0x37, 0x89, 0x41, 0x00, 0x24, 0xc0, 0x00, 0x00, 0xbd, 0x9a, 0xf2, 0xd7, 0x5a, 0xbe, + 0x55, 0x0e, 0x2d, 0xb2, 0x9d, 0xff, 0x1d, 0x40, 0x39, 0xb4, 0xc8, 0x76, 0xbe, 0xff, 0x23, 0x40, 0x00, 0x00, 0xbd, 0x9a, + 0xf2, 0xd7, 0x5a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x16, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x06, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x16, 0x00, 0x00, 0x54, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, + 0x78, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x31, 0x35, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x30, 0x7d, + 0x2c, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, + 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x35, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x31, 0x30, 0x38, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, + 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, + 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, + 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x31, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x22, 0x3a, 0x33, 0x34, 0x38, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, + 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x32, 0x31, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x34, + 0x35, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, + 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, + 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x22, 0x3a, 0x35, 0x36, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, + 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, + 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x22, 0x3a, 0x31, 0x38, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, + 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, + 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x37, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x22, 0x3a, 0x31, 0x35, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x50, 0x6f, + 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, + 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x36, 0x37, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, + 0x63, 0x65, 0x73, 0x31, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, + 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, + 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, + 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x32, 0x39, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, + 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, + 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x37, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x32, 0x36, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, + 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, + 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, + 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, + 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x31, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x38, 0x39, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x31, 0x50, 0x6f, 0x6c, 0x79, 0x6c, + 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, + 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x31, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x22, 0x3a, 0x32, 0x30, 0x30, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, + 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x32, + 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x37, 0x32, 0x38, 0x7d, 0x2c, 0x22, + 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, + 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, + 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x22, 0x3a, 0x31, 0x38, 0x33, 0x36, 0x7d, 0x7d, 0x2c, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, + 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4b, 0x48, 0x52, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, + 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x57, 0x45, 0x42, 0x33, 0x44, 0x5f, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, + 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, 0x67, 0x6c, 0x45, + 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4f, 0x45, 0x53, + 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x22, + 0x5d, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x61, 0x74, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, + 0x32, 0x35, 0x35, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, + 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, + 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x32, 0x35, 0x35, 0x2c, 0x22, 0x6c, 0x69, + 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, + 0x74, 0x68, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x7d, 0x2c, 0x22, 0x4d, 0x61, 0x74, + 0x65, 0x72, 0x69, 0x61, 0x6c, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, + 0x3a, 0x32, 0x31, 0x33, 0x30, 0x37, 0x30, 0x36, 0x36, 0x38, 0x37, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, + 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, + 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, + 0x3a, 0x32, 0x31, 0x33, 0x30, 0x37, 0x30, 0x36, 0x36, 0x38, 0x37, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, + 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, + 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x7d, 0x7d, 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, + 0x3a, 0x7b, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, + 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x65, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, + 0x70, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, + 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, + 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, + 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x65, 0x67, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, + 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, + 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, + 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, + 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x7d, 0x7d, + 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x22, 0x6d, 0x61, + 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x2c, + 0x22, 0x73, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, + 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, + 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x7d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x2c, + 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, + 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x39, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x32, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, + 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, + 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, + 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, + 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x32, 0x38, 0x39, 0x31, + 0x39, 0x36, 0x36, 0x31, 0x32, 0x34, 0x39, 0x37, 0x31, 0x33, 0x38, 0x37, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, + 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x33, 0x30, 0x35, 0x32, 0x32, 0x36, + 0x32, 0x31, 0x34, 0x39, 0x39, 0x39, 0x36, 0x31, 0x38, 0x34, 0x34, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, + 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, + 0x39, 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, + 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, + 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, + 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, + 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, + 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, + 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, + 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, + 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x7d, 0x2c, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x39, + 0x7d, 0x7d, 0x2c, 0x7b, 0x22, 0x65, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x6f, 0x6c, 0x79, 0x6c, 0x69, + 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, + 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, + 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x31, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, + 0x65, 0x73, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, + 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, + 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, + 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x31, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x7d, 0x7d, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, + 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x31, 0x22, 0x2c, 0x22, 0x73, 0x75, 0x72, 0x66, 0x61, + 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, + 0x22, 0x3a, 0x31, 0x7d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, + 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, + 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x31, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x39, + 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, + 0x32, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, + 0x74, 0x72, 0x75, 0x65, 0x2c, 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, + 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x70, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, + 0x22, 0x3a, 0x5b, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x32, 0x38, 0x39, 0x31, 0x39, 0x36, 0x36, 0x31, 0x32, 0x34, 0x39, + 0x37, 0x31, 0x33, 0x38, 0x37, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, + 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x33, 0x30, 0x35, 0x32, 0x32, 0x36, 0x32, 0x31, 0x34, 0x39, 0x39, 0x39, 0x36, + 0x31, 0x38, 0x34, 0x34, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, + 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, + 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, + 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, + 0x5b, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, + 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x30, + 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, + 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, + 0x5d, 0x7d, 0x2c, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x39, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, + 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4e, 0x6f, 0x64, 0x65, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, + 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, + 0x3a, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, + 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, + 0x3a, 0x7b, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, + 0x22, 0x5d, 0x7d, 0x7d, 0x7d, 0x0a, 0x20, 0x20, 0xa7, 0xaa, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xa7, 0xaa, 0xf8, 0xff, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0xf8, 0xff, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0xfe, 0x7f, + 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0xf8, 0xff, 0xfe, 0x7f, 0x01, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xa7, 0xaa, 0xfe, 0x7f, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, 0xfe, 0x7f, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0x03, 0x00, 0xf8, 0xff, 0xfe, 0x7f, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0xfe, 0x7f, + 0xfe, 0x7f, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x01, + 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, + 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x03, + 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x02, + 0x06, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x01, + 0x06, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, + 0x08, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x36, + 0x00, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x1e, 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x06, + 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x4e, 0x04, 0x00, 0x00, 0x1e, 0x03, 0x00, 0x00, 0x36, + 0x04, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x4e, 0x05, 0x00, 0x00, 0x1e, + 0x04, 0x00, 0x00, 0x36, 0x05, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x4e, + 0x03, 0x00, 0x00, 0x1e, 0x05, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x36, + 0x05, 0x00, 0x00, 0x4e, 0x07, 0x00, 0x00, 0x1e, 0x06, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x06, 0x07, 0x00, 0x00, 0x06, + 0x06, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x4e, 0x08, 0x00, 0x00, 0x1e, 0x07, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x06, + 0x08, 0x00, 0x00, 0x06, 0x07, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x4e, 0x06, 0x00, 0x00, 0x1e, 0x08, 0x00, 0x00, 0x36, + 0x06, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x06, 0x08, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x4e, 0xa7, 0xaa, 0x03, 0x00, + 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xa7, 0xaa, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xf8, 0xff, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0x03, 0x00, 0xfe, 0x7f, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0x55, 0x55, 0xfe, 0x7f, 0xfe, 0x7f, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xa7, 0xaa, 0x03, 0x00, + 0xfe, 0x7f, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x03, 0x00, 0x03, 0x00, 0xfe, 0x7f, 0x02, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, 0xfe, 0x7f, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0x03, 0x00, 0xfe, 0x7f, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0x00, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, + 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x02, + 0x03, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, + 0x04, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x01, + 0x07, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, + 0x08, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x06, + 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x1e, 0x01, 0x00, 0x00, 0x36, + 0x02, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x1e, + 0x02, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x4e, + 0x04, 0x00, 0x00, 0x1e, 0x03, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x36, + 0x03, 0x00, 0x00, 0x4e, 0x05, 0x00, 0x00, 0x1e, 0x04, 0x00, 0x00, 0x36, 0x05, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x06, + 0x04, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x4e, 0x03, 0x00, 0x00, 0x1e, 0x05, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x06, + 0x03, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x36, 0x05, 0x00, 0x00, 0x4e, 0x07, 0x00, 0x00, 0x1e, 0x06, 0x00, 0x00, 0x36, + 0x07, 0x00, 0x00, 0x06, 0x07, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x4e, 0x08, 0x00, 0x00, 0x1e, + 0x07, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x06, 0x08, 0x00, 0x00, 0x06, 0x07, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x4e, + 0x06, 0x00, 0x00, 0x1e, 0x08, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x06, 0x08, 0x00, 0x00, 0x36, + 0x08, 0x00, 0x00, 0x4e, + ]), + }, + cylinder: { + flags: IModelTileIO.Flags.ContainsCurves, + bytes: new Uint8Array([ + 0x69, 0x4d, 0x64, 0x6c, 0x02, 0x00, 0x01, 0x00, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x39, 0xd6, 0xc5, 0x6d, + 0x34, 0x00, 0x00, 0xc0, 0x39, 0xd6, 0xc5, 0x6d, 0x34, 0x00, 0x00, 0xc0, 0x56, 0xc1, 0xa8, 0xa4, 0x4e, 0x00, 0x08, 0xc0, + 0x8e, 0x53, 0x74, 0x24, 0x97, 0xff, 0xff, 0x3f, 0x8e, 0x53, 0x74, 0x24, 0x97, 0xff, 0xff, 0x3f, 0xaa, 0x3e, 0x57, 0x5b, + 0xb1, 0xff, 0x07, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xd8, 0x3d, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, + 0x02, 0x00, 0x00, 0x00, 0x64, 0x3d, 0x00, 0x00, 0x28, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, + 0x78, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x32, 0x33, 0x33, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x30, + 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, + 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, + 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, + 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x37, 0x32, 0x38, 0x2c, + 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x34, 0x39, 0x32, 0x38, 0x7d, 0x2c, 0x22, + 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, + 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, + 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x38, 0x36, 0x34, 0x2c, 0x22, + 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x37, 0x33, 0x30, 0x34, 0x7d, 0x2c, 0x22, 0x62, + 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, + 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x31, 0x32, 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x39, + 0x30, 0x33, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, + 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x22, 0x3a, 0x31, 0x32, 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, + 0x3a, 0x33, 0x36, 0x33, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x69, + 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, + 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, + 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x36, 0x34, 0x38, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x22, 0x3a, 0x36, 0x36, 0x35, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, + 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, + 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x32, 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x32, 0x33, 0x33, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, + 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, + 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, + 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x37, 0x32, 0x38, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x31, 0x36, 0x32, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x6f, 0x72, 0x6d, + 0x61, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, + 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, + 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x38, 0x36, + 0x34, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x38, 0x31, 0x36, 0x38, 0x7d, + 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, + 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, + 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, + 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x32, 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x30, 0x33, 0x32, 0x38, 0x7d, 0x7d, 0x2c, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4b, 0x48, 0x52, 0x5f, 0x62, 0x69, 0x6e, + 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x57, 0x45, 0x42, 0x33, 0x44, 0x5f, 0x71, 0x75, 0x61, + 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x5d, 0x2c, + 0x22, 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, + 0x22, 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, + 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x3a, 0x7b, 0x22, + 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, + 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, + 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, + 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, + 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, + 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, + 0x3a, 0x30, 0x7d, 0x7d, 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x65, 0x73, 0x68, + 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, + 0x3a, 0x5b, 0x7b, 0x22, 0x65, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, + 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, + 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, + 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, + 0x73, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, + 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, + 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, + 0x22, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, + 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, + 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, + 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x69, 0x6c, 0x68, 0x6f, 0x75, + 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, + 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, + 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, + 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, + 0x74, 0x74, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x22, 0x3a, + 0x22, 0x62, 0x76, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, + 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x7d, 0x7d, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, + 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, + 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x2c, 0x22, 0x73, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, + 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x7d, + 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x22, + 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x56, 0x65, + 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x31, 0x34, 0x36, 0x2c, 0x22, + 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x44, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, + 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, + 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x36, 0x2e, 0x31, 0x30, + 0x34, 0x35, 0x32, 0x34, 0x32, 0x39, 0x39, 0x39, 0x39, 0x32, 0x33, 0x37, 0x30, 0x30, 0x65, 0x2d, 0x30, 0x35, 0x2c, 0x30, + 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x36, 0x2e, 0x31, 0x30, 0x34, + 0x35, 0x32, 0x34, 0x32, 0x39, 0x39, 0x39, 0x39, 0x32, 0x33, 0x37, 0x30, 0x30, 0x65, 0x2d, 0x30, 0x35, 0x2c, 0x30, 0x2e, + 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x39, 0x2e, 0x31, 0x35, 0x36, 0x37, + 0x38, 0x36, 0x34, 0x34, 0x39, 0x39, 0x38, 0x38, 0x35, 0x35, 0x35, 0x30, 0x65, 0x2d, 0x30, 0x35, 0x2c, 0x30, 0x2e, 0x30, + 0x2c, 0x2d, 0x32, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, + 0x2c, 0x2d, 0x32, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, + 0x2c, 0x2d, 0x33, 0x2e, 0x30, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, + 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, + 0x32, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x32, + 0x2e, 0x30, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x33, 0x2e, + 0x30, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x5d, 0x2c, 0x22, 0x64, + 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x32, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x2d, 0x32, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x2d, 0x33, 0x2e, 0x30, 0x30, 0x30, 0x34, 0x34, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x5d, 0x7d, 0x2c, 0x22, 0x75, 0x6e, 0x69, 0x66, 0x6f, + 0x72, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x77, 0x69, 0x64, 0x74, + 0x68, 0x22, 0x3a, 0x35, 0x38, 0x34, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x4e, 0x6f, 0x64, 0x65, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, + 0x6f, 0x6f, 0x74, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, + 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x6e, 0x6f, 0x64, 0x65, + 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x5d, 0x7d, 0x7d, 0x7d, 0x0a, 0x20, + 0xf8, 0xff, 0xfe, 0x7f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xfe, 0x7f, 0xfe, 0x7f, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x07, 0xfe, 0xc5, 0x69, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x41, 0xf8, 0x38, 0x54, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0xd3, 0xee, 0x01, 0x40, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0x07, 0xe2, 0xba, 0x2d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x41, 0xd2, 0xf4, 0x1d, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xfb, 0xbf, 0x29, 0x11, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xc3, 0xab, 0xbb, 0x07, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0x37, 0x96, 0xf5, 0x01, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0xfe, 0x7f, 0x03, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xc5, 0x69, 0xf5, 0x01, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x38, 0x54, 0xbb, 0x07, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x01, 0x40, 0x29, 0x11, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0xba, 0x2d, 0xf4, 0x1d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0xf4, 0x1d, 0xba, 0x2d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x29, 0x11, 0x01, 0x40, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xbb, 0x07, 0x38, 0x54, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xf5, 0x01, 0xc5, 0x69, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0xf5, 0x01, 0x37, 0x96, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xbb, 0x07, 0xc3, 0xab, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x29, 0x11, 0xfb, 0xbf, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xf4, 0x1d, 0x41, 0xd2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0xba, 0x2d, 0x07, 0xe2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0x01, 0x40, 0xd3, 0xee, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x38, 0x54, 0x41, 0xf8, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xc5, 0x69, 0x07, 0xfe, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xfe, 0x7f, 0xf8, 0xff, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0x37, 0x96, 0x07, 0xfe, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0xc3, 0xab, 0x41, 0xf8, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xfb, 0xbf, 0xd3, 0xee, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x41, 0xd2, 0x07, 0xe2, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x07, 0xe2, 0x41, 0xd2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0xd3, 0xee, 0xfb, 0xbf, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0x41, 0xf8, 0xc3, 0xab, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x07, 0xfe, 0x37, 0x96, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xf8, 0xff, 0xfe, 0x7f, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xfe, 0x7f, 0xfe, 0x7f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0x07, 0xfe, 0x37, 0x96, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0x41, 0xf8, 0xc3, 0xab, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xd3, 0xee, 0xfb, 0xbf, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x07, 0xe2, 0x41, 0xd2, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x41, 0xd2, 0x07, 0xe2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xfb, 0xbf, 0xd3, 0xee, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0xc3, 0xab, 0x41, 0xf8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x37, 0x96, 0x07, 0xfe, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xfe, 0x7f, 0xf8, 0xff, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xc5, 0x69, 0x07, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0x38, 0x54, 0x41, 0xf8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0x01, 0x40, 0xd3, 0xee, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xba, 0x2d, 0x07, 0xe2, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xf4, 0x1d, 0x41, 0xd2, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x29, 0x11, 0xfb, 0xbf, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xbb, 0x07, 0xc3, 0xab, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0xf5, 0x01, 0x37, 0x96, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xf5, 0x01, 0xc5, 0x69, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xbb, 0x07, 0x38, 0x54, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0x29, 0x11, 0x01, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0xf4, 0x1d, 0xba, 0x2d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xba, 0x2d, 0xf4, 0x1d, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x01, 0x40, 0x29, 0x11, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x38, 0x54, 0xbb, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xc5, 0x69, 0xf5, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0xfe, 0x7f, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x37, 0x96, 0xf5, 0x01, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xc3, 0xab, 0xbb, 0x07, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xfb, 0xbf, 0x29, 0x11, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0x41, 0xd2, 0xf4, 0x1d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0x07, 0xe2, 0xba, 0x2d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xd3, 0xee, 0x01, 0x40, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x41, 0xf8, 0x38, 0x54, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x07, 0xfe, 0xc5, 0x69, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xf8, 0xff, 0xfe, 0x7f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x80, 0xcc, 0xcc, + 0xf8, 0xff, 0xfe, 0x7f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x80, 0xcc, 0xcc, 0x07, 0xfe, 0x37, 0x96, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x93, 0xcc, 0xcc, 0x07, 0xfe, 0x37, 0x96, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xec, 0x93, 0xcc, 0xcc, 0x41, 0xf8, 0xc3, 0xab, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0xa2, 0xcc, 0xcc, 0x41, 0xf8, 0xc3, 0xab, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0xa2, 0xcc, 0xcc, + 0xd3, 0xee, 0xfb, 0xbf, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xae, 0xcc, 0xcc, 0xd3, 0xee, 0xfb, 0xbf, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xae, 0xcc, 0xcc, 0x07, 0xe2, 0x41, 0xd2, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc5, 0xba, 0xcc, 0xcc, 0x07, 0xe2, 0x41, 0xd2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc5, 0xba, 0xcc, 0xcc, 0x41, 0xd2, 0x07, 0xe2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xc5, 0xcc, 0xcc, + 0x41, 0xd2, 0x07, 0xe2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xc5, 0xcc, 0xcc, 0xfb, 0xbf, 0xd3, 0xee, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0xd0, 0xcc, 0xcc, 0xfb, 0xbf, 0xd3, 0xee, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xae, 0xd0, 0xcc, 0xcc, 0xc3, 0xab, 0x41, 0xf8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa2, 0xdd, 0xcc, 0xcc, 0xc3, 0xab, 0x41, 0xf8, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0xdd, 0xcc, 0xcc, + 0x37, 0x96, 0x07, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0xec, 0xcc, 0xcc, 0x37, 0x96, 0x07, 0xfe, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0xec, 0xcc, 0xcc, 0xfe, 0x7f, 0xf8, 0xff, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xcc, 0xcc, 0xfe, 0x7f, 0xf8, 0xff, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xff, 0xcc, 0xcc, 0xc5, 0x69, 0x07, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xec, 0xcc, 0xcc, + 0xc5, 0x69, 0x07, 0xfe, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xec, 0xcc, 0xcc, 0x38, 0x54, 0x41, 0xf8, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xdd, 0xcc, 0xcc, 0x38, 0x54, 0x41, 0xf8, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5d, 0xdd, 0xcc, 0xcc, 0x01, 0x40, 0xd3, 0xee, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0xd0, 0xcc, 0xcc, 0x01, 0x40, 0xd3, 0xee, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0xd0, 0xcc, 0xcc, + 0xba, 0x2d, 0x07, 0xe2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xc5, 0xcc, 0xcc, 0xba, 0x2d, 0x07, 0xe2, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xc5, 0xcc, 0xcc, 0xf4, 0x1d, 0x41, 0xd2, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3a, 0xba, 0xcc, 0xcc, 0xf4, 0x1d, 0x41, 0xd2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0xba, 0xcc, 0xcc, 0x29, 0x11, 0xfb, 0xbf, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0xae, 0xcc, 0xcc, + 0x29, 0x11, 0xfb, 0xbf, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0xae, 0xcc, 0xcc, 0xbb, 0x07, 0xc3, 0xab, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xa2, 0xcc, 0xcc, 0xbb, 0x07, 0xc3, 0xab, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0xa2, 0xcc, 0xcc, 0xf5, 0x01, 0x37, 0x96, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x13, 0x93, 0xcc, 0xcc, 0xf5, 0x01, 0x37, 0x96, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x93, 0xcc, 0xcc, + 0x03, 0x00, 0xfe, 0x7f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcc, 0xcc, 0xf5, 0x01, 0xc5, 0x69, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x6c, 0xcc, 0xcc, 0xf5, 0x01, 0xc5, 0x69, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x13, 0x6c, 0xcc, 0xcc, 0xbb, 0x07, 0x38, 0x54, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x5d, 0xcc, 0xcc, + 0xbb, 0x07, 0x38, 0x54, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x5d, 0xcc, 0xcc, 0x29, 0x11, 0x01, 0x40, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x51, 0xcc, 0xcc, 0x29, 0x11, 0x01, 0x40, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2f, 0x51, 0xcc, 0xcc, 0xf4, 0x1d, 0xba, 0x2d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x45, 0xcc, 0xcc, 0xf4, 0x1d, 0xba, 0x2d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x45, 0xcc, 0xcc, + 0xba, 0x2d, 0xf4, 0x1d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x3a, 0xcc, 0xcc, 0xba, 0x2d, 0xf4, 0x1d, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x3a, 0xcc, 0xcc, 0x01, 0x40, 0x29, 0x11, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x2f, 0xcc, 0xcc, 0x01, 0x40, 0x29, 0x11, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x2f, 0xcc, 0xcc, 0x38, 0x54, 0xbb, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x22, 0xcc, 0xcc, + 0x38, 0x54, 0xbb, 0x07, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x22, 0xcc, 0xcc, 0xc5, 0x69, 0xf5, 0x01, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x13, 0xcc, 0xcc, 0xc5, 0x69, 0xf5, 0x01, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0x13, 0xcc, 0xcc, 0xfe, 0x7f, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0xcc, 0xcc, 0xfe, 0x7f, 0x03, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0xcc, 0xcc, + 0x37, 0x96, 0xf5, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x13, 0xcc, 0xcc, 0x37, 0x96, 0xf5, 0x01, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x13, 0xcc, 0xcc, 0xc3, 0xab, 0xbb, 0x07, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa2, 0x22, 0xcc, 0xcc, 0xc3, 0xab, 0xbb, 0x07, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa2, 0x22, 0xcc, 0xcc, 0xfb, 0xbf, 0x29, 0x11, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0x2f, 0xcc, 0xcc, + 0xfb, 0xbf, 0x29, 0x11, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0x2f, 0xcc, 0xcc, 0x41, 0xd2, 0xf4, 0x1d, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x3a, 0xcc, 0xcc, 0x41, 0xd2, 0xf4, 0x1d, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xba, 0x3a, 0xcc, 0xcc, 0x07, 0xe2, 0xba, 0x2d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc5, 0x45, 0xcc, 0xcc, 0x07, 0xe2, 0xba, 0x2d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0x45, 0xcc, 0xcc, + 0xd3, 0xee, 0x01, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x51, 0xcc, 0xcc, 0xd3, 0xee, 0x01, 0x40, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x51, 0xcc, 0xcc, 0x41, 0xf8, 0x38, 0x54, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdd, 0x5d, 0xcc, 0xcc, 0x41, 0xf8, 0x38, 0x54, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x5d, 0xcc, 0xcc, 0x07, 0xfe, 0xc5, 0x69, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x6c, 0xcc, 0xcc, + 0x07, 0xfe, 0xc5, 0x69, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x6c, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, + 0x00, 0x00, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x11, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x01, 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1a, 0x00, + 0x00, 0x1a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x01, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x01, 0x00, 0x00, 0x23, + 0x00, 0x00, 0x23, 0x00, 0x00, 0x01, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x26, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x26, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x26, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x26, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x26, + 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x26, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x26, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x26, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x26, 0x00, 0x00, 0x2f, 0x00, + 0x00, 0x2f, 0x00, 0x00, 0x26, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x26, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, + 0x00, 0x00, 0x26, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x26, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x26, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x26, 0x00, + 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x26, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x26, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x26, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3a, 0x00, 0x00, + 0x3a, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, + 0x00, 0x26, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x26, + 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x26, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x26, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x26, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x26, 0x00, 0x00, 0x43, 0x00, + 0x00, 0x43, 0x00, 0x00, 0x26, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x26, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, + 0x00, 0x00, 0x26, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x26, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x26, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x26, 0x00, + 0x00, 0x25, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4d, + 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x4f, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x50, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x50, 0x00, 0x00, 0x51, 0x00, 0x00, 0x51, 0x00, + 0x00, 0x50, 0x00, 0x00, 0x52, 0x00, 0x00, 0x51, 0x00, 0x00, 0x52, 0x00, 0x00, 0x53, 0x00, 0x00, 0x53, 0x00, 0x00, 0x52, + 0x00, 0x00, 0x54, 0x00, 0x00, 0x53, 0x00, 0x00, 0x54, 0x00, 0x00, 0x55, 0x00, 0x00, 0x55, 0x00, 0x00, 0x54, 0x00, 0x00, + 0x56, 0x00, 0x00, 0x55, 0x00, 0x00, 0x56, 0x00, 0x00, 0x57, 0x00, 0x00, 0x57, 0x00, 0x00, 0x56, 0x00, 0x00, 0x58, 0x00, + 0x00, 0x57, 0x00, 0x00, 0x58, 0x00, 0x00, 0x59, 0x00, 0x00, 0x59, 0x00, 0x00, 0x58, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x59, + 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5b, 0x00, 0x00, + 0x5c, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x5e, 0x00, + 0x00, 0x5f, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x60, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x60, 0x00, 0x00, 0x61, + 0x00, 0x00, 0x61, 0x00, 0x00, 0x60, 0x00, 0x00, 0x62, 0x00, 0x00, 0x61, 0x00, 0x00, 0x62, 0x00, 0x00, 0x63, 0x00, 0x00, + 0x63, 0x00, 0x00, 0x62, 0x00, 0x00, 0x64, 0x00, 0x00, 0x63, 0x00, 0x00, 0x64, 0x00, 0x00, 0x65, 0x00, 0x00, 0x65, 0x00, + 0x00, 0x64, 0x00, 0x00, 0x66, 0x00, 0x00, 0x65, 0x00, 0x00, 0x66, 0x00, 0x00, 0x67, 0x00, 0x00, 0x67, 0x00, 0x00, 0x66, + 0x00, 0x00, 0x68, 0x00, 0x00, 0x67, 0x00, 0x00, 0x68, 0x00, 0x00, 0x69, 0x00, 0x00, 0x69, 0x00, 0x00, 0x68, 0x00, 0x00, + 0x6a, 0x00, 0x00, 0x69, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x6c, 0x00, + 0x00, 0x6b, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x6d, + 0x00, 0x00, 0x6e, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x70, 0x00, 0x00, 0x6f, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x71, 0x00, 0x00, 0x71, 0x00, 0x00, 0x70, 0x00, 0x00, 0x72, 0x00, 0x00, 0x71, 0x00, 0x00, 0x72, 0x00, + 0x00, 0x73, 0x00, 0x00, 0x73, 0x00, 0x00, 0x72, 0x00, 0x00, 0x74, 0x00, 0x00, 0x73, 0x00, 0x00, 0x74, 0x00, 0x00, 0x75, + 0x00, 0x00, 0x75, 0x00, 0x00, 0x74, 0x00, 0x00, 0x76, 0x00, 0x00, 0x75, 0x00, 0x00, 0x76, 0x00, 0x00, 0x77, 0x00, 0x00, + 0x77, 0x00, 0x00, 0x76, 0x00, 0x00, 0x78, 0x00, 0x00, 0x77, 0x00, 0x00, 0x78, 0x00, 0x00, 0x79, 0x00, 0x00, 0x79, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x79, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7a, + 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7c, 0x00, 0x00, + 0x7e, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x7f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, 0x00, 0x80, 0x00, 0x00, 0x82, 0x00, 0x00, 0x81, + 0x00, 0x00, 0x82, 0x00, 0x00, 0x83, 0x00, 0x00, 0x83, 0x00, 0x00, 0x82, 0x00, 0x00, 0x84, 0x00, 0x00, 0x83, 0x00, 0x00, + 0x84, 0x00, 0x00, 0x85, 0x00, 0x00, 0x85, 0x00, 0x00, 0x84, 0x00, 0x00, 0x86, 0x00, 0x00, 0x85, 0x00, 0x00, 0x86, 0x00, + 0x00, 0x87, 0x00, 0x00, 0x87, 0x00, 0x00, 0x86, 0x00, 0x00, 0x88, 0x00, 0x00, 0x87, 0x00, 0x00, 0x88, 0x00, 0x00, 0x89, + 0x00, 0x00, 0x89, 0x00, 0x00, 0x88, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x89, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x8b, 0x00, 0x00, + 0x8b, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x8d, 0x00, + 0x00, 0x8c, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x8e, + 0x00, 0x00, 0x90, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x90, 0x00, 0x00, 0x91, 0x00, 0x00, 0x91, 0x00, 0x00, 0x90, 0x00, 0x00, + 0x4b, 0x00, 0x00, 0x91, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, + 0x00, 0x23, 0x00, 0x00, 0x21, 0x00, 0x00, 0x22, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x21, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1e, 0x00, + 0x00, 0x1f, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1d, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1b, 0x00, + 0x00, 0x1b, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, + 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x19, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, + 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x18, 0x00, 0x00, 0x19, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x19, 0x00, + 0x00, 0x19, 0x00, 0x00, 0x17, 0x00, 0x00, 0x18, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x16, 0x00, 0x00, 0x17, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x16, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x14, 0x00, + 0x00, 0x15, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x13, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x12, 0x00, 0x00, 0x13, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x11, 0x00, 0x00, 0x12, 0x00, 0x00, 0x11, 0x00, + 0x00, 0x11, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x10, 0x00, 0x00, 0x11, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0f, 0x00, + 0x00, 0x0f, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, + 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x27, 0x00, 0x00, 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x25, 0x00, + 0x00, 0x49, 0x00, 0x00, 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28, + 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x29, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x29, 0x00, + 0x00, 0x29, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, + 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2d, 0x00, + 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, + 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, + 0x2f, 0x00, 0x00, 0x30, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x31, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x32, + 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x33, 0x00, 0x00, + 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x34, 0x00, 0x00, 0x33, 0x00, + 0x00, 0x33, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, + 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x36, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x37, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x37, 0x00, + 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, + 0x39, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, + 0x00, 0x3b, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3c, + 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3d, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3d, 0x00, + 0x00, 0x3d, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, + 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x41, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x42, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, + 0x00, 0x00, 0x42, 0x00, 0x00, 0x43, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, + 0x43, 0x00, 0x00, 0x44, 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, + 0x00, 0x45, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x46, + 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x47, 0x00, 0x00, + 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x48, 0x00, 0x00, 0x47, 0x00, + 0x00, 0x47, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x49, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, + 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x01, + 0x24, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x24, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x01, 0x24, 0x00, 0x00, 0x01, 0x23, 0x00, 0x00, 0x02, 0x23, 0x00, 0x00, 0x03, + 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x02, 0x23, 0x00, 0x00, 0x01, 0x23, 0x00, 0x00, 0x01, 0x22, 0x00, 0x00, 0x02, + 0x22, 0x00, 0x00, 0x03, 0x22, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x02, 0x22, 0x00, 0x00, 0x01, 0x22, 0x00, 0x00, 0x01, + 0x21, 0x00, 0x00, 0x02, 0x21, 0x00, 0x00, 0x03, 0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x02, 0x21, 0x00, 0x00, 0x01, + 0x21, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x02, + 0x20, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x01, 0x1f, 0x00, 0x00, 0x02, 0x1f, 0x00, 0x00, 0x03, 0x1f, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x02, 0x1f, 0x00, 0x00, 0x01, 0x1f, 0x00, 0x00, 0x01, 0x1e, 0x00, 0x00, 0x02, 0x1e, 0x00, 0x00, 0x03, + 0x1e, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x02, 0x1e, 0x00, 0x00, 0x01, 0x1e, 0x00, 0x00, 0x01, 0x1d, 0x00, 0x00, 0x02, + 0x1d, 0x00, 0x00, 0x03, 0x1d, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x02, 0x1d, 0x00, 0x00, 0x01, 0x1d, 0x00, 0x00, 0x01, + 0x1c, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x03, 0x1c, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x01, + 0x1c, 0x00, 0x00, 0x01, 0x1b, 0x00, 0x00, 0x02, 0x1b, 0x00, 0x00, 0x03, 0x1b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x02, + 0x1b, 0x00, 0x00, 0x01, 0x1b, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x00, 0x02, 0x1a, 0x00, 0x00, 0x03, 0x1a, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x02, 0x1a, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x00, 0x01, 0x19, 0x00, 0x00, 0x02, 0x19, 0x00, 0x00, 0x03, + 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x02, 0x19, 0x00, 0x00, 0x01, 0x19, 0x00, 0x00, 0x01, 0x18, 0x00, 0x00, 0x02, + 0x18, 0x00, 0x00, 0x03, 0x18, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x01, 0x18, 0x00, 0x00, 0x01, + 0x17, 0x00, 0x00, 0x02, 0x17, 0x00, 0x00, 0x03, 0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x02, 0x17, 0x00, 0x00, 0x01, + 0x17, 0x00, 0x00, 0x01, 0x16, 0x00, 0x00, 0x02, 0x16, 0x00, 0x00, 0x03, 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x02, + 0x16, 0x00, 0x00, 0x01, 0x16, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00, 0x02, 0x15, 0x00, 0x00, 0x03, 0x15, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x02, 0x15, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x03, + 0x14, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x01, 0x13, 0x00, 0x00, 0x02, + 0x13, 0x00, 0x00, 0x03, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x02, 0x13, 0x00, 0x00, 0x01, 0x13, 0x00, 0x00, 0x01, + 0x12, 0x00, 0x00, 0x02, 0x12, 0x00, 0x00, 0x03, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x02, 0x12, 0x00, 0x00, 0x01, + 0x12, 0x00, 0x00, 0x01, 0x11, 0x00, 0x00, 0x02, 0x11, 0x00, 0x00, 0x03, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x02, + 0x11, 0x00, 0x00, 0x01, 0x11, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x01, 0x0f, 0x00, 0x00, 0x02, 0x0f, 0x00, 0x00, 0x03, + 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x02, 0x0f, 0x00, 0x00, 0x01, 0x0f, 0x00, 0x00, 0x01, 0x0e, 0x00, 0x00, 0x02, + 0x0e, 0x00, 0x00, 0x03, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x02, 0x0e, 0x00, 0x00, 0x01, 0x0e, 0x00, 0x00, 0x01, + 0x0d, 0x00, 0x00, 0x02, 0x0d, 0x00, 0x00, 0x03, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x02, 0x0d, 0x00, 0x00, 0x01, + 0x0d, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x03, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x02, + 0x0c, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x00, 0x03, 0x0b, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x03, + 0x0a, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x00, 0x01, 0x09, 0x00, 0x00, 0x02, + 0x09, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x02, 0x09, 0x00, 0x00, 0x01, 0x09, 0x00, 0x00, 0x01, + 0x08, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, + 0x08, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, + 0x07, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x03, + 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x02, + 0x04, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x01, + 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x01, + 0x03, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x03, 0x27, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x02, + 0x27, 0x00, 0x00, 0x01, 0x27, 0x00, 0x00, 0x01, 0x25, 0x00, 0x00, 0x02, 0x25, 0x00, 0x00, 0x03, 0x49, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x02, 0x49, 0x00, 0x00, 0x01, 0x49, 0x00, 0x00, 0x01, 0x25, 0x00, 0x00, 0x02, 0x25, 0x00, 0x00, 0x03, + 0x28, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x01, 0x28, 0x00, 0x00, 0x01, 0x27, 0x00, 0x00, 0x02, + 0x27, 0x00, 0x00, 0x03, 0x29, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x02, 0x29, 0x00, 0x00, 0x01, 0x29, 0x00, 0x00, 0x01, + 0x28, 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x03, 0x2a, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x02, 0x2a, 0x00, 0x00, 0x01, + 0x2a, 0x00, 0x00, 0x01, 0x29, 0x00, 0x00, 0x02, 0x29, 0x00, 0x00, 0x03, 0x2b, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x02, + 0x2b, 0x00, 0x00, 0x01, 0x2b, 0x00, 0x00, 0x01, 0x2a, 0x00, 0x00, 0x02, 0x2a, 0x00, 0x00, 0x03, 0x2c, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x00, 0x01, 0x2b, 0x00, 0x00, 0x02, 0x2b, 0x00, 0x00, 0x03, + 0x2d, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x02, 0x2d, 0x00, 0x00, 0x01, 0x2d, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x00, 0x02, + 0x2c, 0x00, 0x00, 0x03, 0x2e, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x02, 0x2e, 0x00, 0x00, 0x01, 0x2e, 0x00, 0x00, 0x01, + 0x2d, 0x00, 0x00, 0x02, 0x2d, 0x00, 0x00, 0x03, 0x2f, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x02, 0x2f, 0x00, 0x00, 0x01, + 0x2f, 0x00, 0x00, 0x01, 0x2e, 0x00, 0x00, 0x02, 0x2e, 0x00, 0x00, 0x03, 0x30, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x02, + 0x30, 0x00, 0x00, 0x01, 0x30, 0x00, 0x00, 0x01, 0x2f, 0x00, 0x00, 0x02, 0x2f, 0x00, 0x00, 0x03, 0x31, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x02, 0x31, 0x00, 0x00, 0x01, 0x31, 0x00, 0x00, 0x01, 0x30, 0x00, 0x00, 0x02, 0x30, 0x00, 0x00, 0x03, + 0x32, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x02, 0x32, 0x00, 0x00, 0x01, 0x32, 0x00, 0x00, 0x01, 0x31, 0x00, 0x00, 0x02, + 0x31, 0x00, 0x00, 0x03, 0x33, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x02, 0x33, 0x00, 0x00, 0x01, 0x33, 0x00, 0x00, 0x01, + 0x32, 0x00, 0x00, 0x02, 0x32, 0x00, 0x00, 0x03, 0x34, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x02, 0x34, 0x00, 0x00, 0x01, + 0x34, 0x00, 0x00, 0x01, 0x33, 0x00, 0x00, 0x02, 0x33, 0x00, 0x00, 0x03, 0x35, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x02, + 0x35, 0x00, 0x00, 0x01, 0x35, 0x00, 0x00, 0x01, 0x34, 0x00, 0x00, 0x02, 0x34, 0x00, 0x00, 0x03, 0x36, 0x00, 0x00, 0x00, + 0x35, 0x00, 0x00, 0x02, 0x36, 0x00, 0x00, 0x01, 0x36, 0x00, 0x00, 0x01, 0x35, 0x00, 0x00, 0x02, 0x35, 0x00, 0x00, 0x03, + 0x37, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x02, 0x37, 0x00, 0x00, 0x01, 0x37, 0x00, 0x00, 0x01, 0x36, 0x00, 0x00, 0x02, + 0x36, 0x00, 0x00, 0x03, 0x38, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x02, 0x38, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x01, + 0x37, 0x00, 0x00, 0x02, 0x37, 0x00, 0x00, 0x03, 0x39, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x02, 0x39, 0x00, 0x00, 0x01, + 0x39, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x02, 0x38, 0x00, 0x00, 0x03, 0x3a, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x02, + 0x3a, 0x00, 0x00, 0x01, 0x3a, 0x00, 0x00, 0x01, 0x39, 0x00, 0x00, 0x02, 0x39, 0x00, 0x00, 0x03, 0x3b, 0x00, 0x00, 0x00, + 0x3a, 0x00, 0x00, 0x02, 0x3b, 0x00, 0x00, 0x01, 0x3b, 0x00, 0x00, 0x01, 0x3a, 0x00, 0x00, 0x02, 0x3a, 0x00, 0x00, 0x03, + 0x3c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x02, 0x3c, 0x00, 0x00, 0x01, 0x3c, 0x00, 0x00, 0x01, 0x3b, 0x00, 0x00, 0x02, + 0x3b, 0x00, 0x00, 0x03, 0x3d, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x02, 0x3d, 0x00, 0x00, 0x01, 0x3d, 0x00, 0x00, 0x01, + 0x3c, 0x00, 0x00, 0x02, 0x3c, 0x00, 0x00, 0x03, 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x02, 0x3e, 0x00, 0x00, 0x01, + 0x3e, 0x00, 0x00, 0x01, 0x3d, 0x00, 0x00, 0x02, 0x3d, 0x00, 0x00, 0x03, 0x3f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x02, + 0x3f, 0x00, 0x00, 0x01, 0x3f, 0x00, 0x00, 0x01, 0x3e, 0x00, 0x00, 0x02, 0x3e, 0x00, 0x00, 0x03, 0x40, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x01, 0x3f, 0x00, 0x00, 0x02, 0x3f, 0x00, 0x00, 0x03, + 0x41, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0x41, 0x00, 0x00, 0x01, 0x41, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x02, + 0x40, 0x00, 0x00, 0x03, 0x42, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x02, 0x42, 0x00, 0x00, 0x01, 0x42, 0x00, 0x00, 0x01, + 0x41, 0x00, 0x00, 0x02, 0x41, 0x00, 0x00, 0x03, 0x43, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x02, 0x43, 0x00, 0x00, 0x01, + 0x43, 0x00, 0x00, 0x01, 0x42, 0x00, 0x00, 0x02, 0x42, 0x00, 0x00, 0x03, 0x44, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x02, + 0x44, 0x00, 0x00, 0x01, 0x44, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x02, 0x43, 0x00, 0x00, 0x03, 0x45, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x02, 0x45, 0x00, 0x00, 0x01, 0x45, 0x00, 0x00, 0x01, 0x44, 0x00, 0x00, 0x02, 0x44, 0x00, 0x00, 0x03, + 0x46, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x02, 0x46, 0x00, 0x00, 0x01, 0x46, 0x00, 0x00, 0x01, 0x45, 0x00, 0x00, 0x02, + 0x45, 0x00, 0x00, 0x03, 0x47, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x02, 0x47, 0x00, 0x00, 0x01, 0x47, 0x00, 0x00, 0x01, + 0x46, 0x00, 0x00, 0x02, 0x46, 0x00, 0x00, 0x03, 0x48, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x01, + 0x48, 0x00, 0x00, 0x01, 0x47, 0x00, 0x00, 0x02, 0x47, 0x00, 0x00, 0x03, 0x49, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x02, + 0x49, 0x00, 0x00, 0x01, 0x49, 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x03, 0x4a, 0x00, 0x00, 0x4b, + 0x00, 0x00, 0x4a, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4d, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x4e, 0x00, + 0x00, 0x4e, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x50, 0x00, 0x00, 0x51, 0x00, 0x00, 0x50, 0x00, 0x00, 0x50, + 0x00, 0x00, 0x51, 0x00, 0x00, 0x51, 0x00, 0x00, 0x52, 0x00, 0x00, 0x53, 0x00, 0x00, 0x52, 0x00, 0x00, 0x52, 0x00, 0x00, + 0x53, 0x00, 0x00, 0x53, 0x00, 0x00, 0x54, 0x00, 0x00, 0x55, 0x00, 0x00, 0x54, 0x00, 0x00, 0x54, 0x00, 0x00, 0x55, 0x00, + 0x00, 0x55, 0x00, 0x00, 0x56, 0x00, 0x00, 0x57, 0x00, 0x00, 0x56, 0x00, 0x00, 0x56, 0x00, 0x00, 0x57, 0x00, 0x00, 0x57, + 0x00, 0x00, 0x58, 0x00, 0x00, 0x59, 0x00, 0x00, 0x58, 0x00, 0x00, 0x58, 0x00, 0x00, 0x59, 0x00, 0x00, 0x59, 0x00, 0x00, + 0x5a, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5c, 0x00, + 0x00, 0x5d, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x5f, + 0x00, 0x00, 0x5e, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x60, 0x00, 0x00, 0x61, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x61, 0x00, 0x00, 0x61, 0x00, 0x00, 0x62, 0x00, 0x00, 0x63, 0x00, 0x00, 0x62, 0x00, + 0x00, 0x62, 0x00, 0x00, 0x63, 0x00, 0x00, 0x63, 0x00, 0x00, 0x64, 0x00, 0x00, 0x65, 0x00, 0x00, 0x64, 0x00, 0x00, 0x64, + 0x00, 0x00, 0x65, 0x00, 0x00, 0x65, 0x00, 0x00, 0x66, 0x00, 0x00, 0x67, 0x00, 0x00, 0x66, 0x00, 0x00, 0x66, 0x00, 0x00, + 0x67, 0x00, 0x00, 0x67, 0x00, 0x00, 0x68, 0x00, 0x00, 0x69, 0x00, 0x00, 0x68, 0x00, 0x00, 0x68, 0x00, 0x00, 0x69, 0x00, + 0x00, 0x69, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6b, + 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x6d, 0x00, 0x00, + 0x6e, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x70, 0x00, + 0x00, 0x71, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x71, 0x00, 0x00, 0x71, 0x00, 0x00, 0x72, 0x00, 0x00, 0x73, + 0x00, 0x00, 0x72, 0x00, 0x00, 0x72, 0x00, 0x00, 0x73, 0x00, 0x00, 0x73, 0x00, 0x00, 0x74, 0x00, 0x00, 0x75, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x74, 0x00, 0x00, 0x75, 0x00, 0x00, 0x75, 0x00, 0x00, 0x76, 0x00, 0x00, 0x77, 0x00, 0x00, 0x76, 0x00, + 0x00, 0x76, 0x00, 0x00, 0x77, 0x00, 0x00, 0x77, 0x00, 0x00, 0x78, 0x00, 0x00, 0x79, 0x00, 0x00, 0x78, 0x00, 0x00, 0x78, + 0x00, 0x00, 0x79, 0x00, 0x00, 0x79, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x7a, 0x00, 0x00, + 0x7b, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7d, 0x00, + 0x00, 0x7d, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x81, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, 0x00, + 0x82, 0x00, 0x00, 0x83, 0x00, 0x00, 0x82, 0x00, 0x00, 0x82, 0x00, 0x00, 0x83, 0x00, 0x00, 0x83, 0x00, 0x00, 0x84, 0x00, + 0x00, 0x85, 0x00, 0x00, 0x84, 0x00, 0x00, 0x84, 0x00, 0x00, 0x85, 0x00, 0x00, 0x85, 0x00, 0x00, 0x86, 0x00, 0x00, 0x87, + 0x00, 0x00, 0x86, 0x00, 0x00, 0x86, 0x00, 0x00, 0x87, 0x00, 0x00, 0x87, 0x00, 0x00, 0x88, 0x00, 0x00, 0x89, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x88, 0x00, 0x00, 0x89, 0x00, 0x00, 0x89, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x8a, 0x00, + 0x00, 0x8a, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8c, + 0x00, 0x00, 0x8d, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x8e, 0x00, 0x00, + 0x8f, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x90, 0x00, 0x00, 0x91, 0x00, 0x00, 0x90, 0x00, 0x00, 0x90, 0x00, 0x00, 0x91, 0x00, + 0x00, 0x91, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x02, 0x4b, 0x00, 0x00, 0x01, 0x4b, 0x00, 0x00, 0x01, + 0x4a, 0x00, 0x00, 0x02, 0x4a, 0x00, 0x00, 0x03, 0x4d, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x02, 0x4d, 0x00, 0x00, 0x01, + 0x4d, 0x00, 0x00, 0x01, 0x4c, 0x00, 0x00, 0x02, 0x4c, 0x00, 0x00, 0x03, 0x4f, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x02, + 0x4f, 0x00, 0x00, 0x01, 0x4f, 0x00, 0x00, 0x01, 0x4e, 0x00, 0x00, 0x02, 0x4e, 0x00, 0x00, 0x03, 0x51, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x00, 0x02, 0x51, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x02, 0x50, 0x00, 0x00, 0x03, + 0x53, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x02, 0x53, 0x00, 0x00, 0x01, 0x53, 0x00, 0x00, 0x01, 0x52, 0x00, 0x00, 0x02, + 0x52, 0x00, 0x00, 0x03, 0x55, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x02, 0x55, 0x00, 0x00, 0x01, 0x55, 0x00, 0x00, 0x01, + 0x54, 0x00, 0x00, 0x02, 0x54, 0x00, 0x00, 0x03, 0x57, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x02, 0x57, 0x00, 0x00, 0x01, + 0x57, 0x00, 0x00, 0x01, 0x56, 0x00, 0x00, 0x02, 0x56, 0x00, 0x00, 0x03, 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x02, + 0x59, 0x00, 0x00, 0x01, 0x59, 0x00, 0x00, 0x01, 0x58, 0x00, 0x00, 0x02, 0x58, 0x00, 0x00, 0x03, 0x5b, 0x00, 0x00, 0x00, + 0x5a, 0x00, 0x00, 0x02, 0x5b, 0x00, 0x00, 0x01, 0x5b, 0x00, 0x00, 0x01, 0x5a, 0x00, 0x00, 0x02, 0x5a, 0x00, 0x00, 0x03, + 0x5d, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x02, 0x5d, 0x00, 0x00, 0x01, 0x5d, 0x00, 0x00, 0x01, 0x5c, 0x00, 0x00, 0x02, + 0x5c, 0x00, 0x00, 0x03, 0x5f, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x02, 0x5f, 0x00, 0x00, 0x01, 0x5f, 0x00, 0x00, 0x01, + 0x5e, 0x00, 0x00, 0x02, 0x5e, 0x00, 0x00, 0x03, 0x61, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x02, 0x61, 0x00, 0x00, 0x01, + 0x61, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, 0x02, 0x60, 0x00, 0x00, 0x03, 0x63, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x02, + 0x63, 0x00, 0x00, 0x01, 0x63, 0x00, 0x00, 0x01, 0x62, 0x00, 0x00, 0x02, 0x62, 0x00, 0x00, 0x03, 0x65, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x02, 0x65, 0x00, 0x00, 0x01, 0x65, 0x00, 0x00, 0x01, 0x64, 0x00, 0x00, 0x02, 0x64, 0x00, 0x00, 0x03, + 0x67, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x02, 0x67, 0x00, 0x00, 0x01, 0x67, 0x00, 0x00, 0x01, 0x66, 0x00, 0x00, 0x02, + 0x66, 0x00, 0x00, 0x03, 0x69, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x69, 0x00, 0x00, 0x01, 0x69, 0x00, 0x00, 0x01, + 0x68, 0x00, 0x00, 0x02, 0x68, 0x00, 0x00, 0x03, 0x6b, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x02, 0x6b, 0x00, 0x00, 0x01, + 0x6b, 0x00, 0x00, 0x01, 0x6a, 0x00, 0x00, 0x02, 0x6a, 0x00, 0x00, 0x03, 0x6d, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x02, + 0x6d, 0x00, 0x00, 0x01, 0x6d, 0x00, 0x00, 0x01, 0x6c, 0x00, 0x00, 0x02, 0x6c, 0x00, 0x00, 0x03, 0x6f, 0x00, 0x00, 0x00, + 0x6e, 0x00, 0x00, 0x02, 0x6f, 0x00, 0x00, 0x01, 0x6f, 0x00, 0x00, 0x01, 0x6e, 0x00, 0x00, 0x02, 0x6e, 0x00, 0x00, 0x03, + 0x71, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x02, 0x71, 0x00, 0x00, 0x01, 0x71, 0x00, 0x00, 0x01, 0x70, 0x00, 0x00, 0x02, + 0x70, 0x00, 0x00, 0x03, 0x73, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x02, 0x73, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00, 0x01, + 0x72, 0x00, 0x00, 0x02, 0x72, 0x00, 0x00, 0x03, 0x75, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x02, 0x75, 0x00, 0x00, 0x01, + 0x75, 0x00, 0x00, 0x01, 0x74, 0x00, 0x00, 0x02, 0x74, 0x00, 0x00, 0x03, 0x77, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x02, + 0x77, 0x00, 0x00, 0x01, 0x77, 0x00, 0x00, 0x01, 0x76, 0x00, 0x00, 0x02, 0x76, 0x00, 0x00, 0x03, 0x79, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x02, 0x79, 0x00, 0x00, 0x01, 0x79, 0x00, 0x00, 0x01, 0x78, 0x00, 0x00, 0x02, 0x78, 0x00, 0x00, 0x03, + 0x7b, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x02, 0x7b, 0x00, 0x00, 0x01, 0x7b, 0x00, 0x00, 0x01, 0x7a, 0x00, 0x00, 0x02, + 0x7a, 0x00, 0x00, 0x03, 0x7d, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x02, 0x7d, 0x00, 0x00, 0x01, 0x7d, 0x00, 0x00, 0x01, + 0x7c, 0x00, 0x00, 0x02, 0x7c, 0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x01, + 0x7f, 0x00, 0x00, 0x01, 0x7e, 0x00, 0x00, 0x02, 0x7e, 0x00, 0x00, 0x03, 0x81, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x02, + 0x81, 0x00, 0x00, 0x01, 0x81, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x03, 0x83, 0x00, 0x00, 0x00, + 0x82, 0x00, 0x00, 0x02, 0x83, 0x00, 0x00, 0x01, 0x83, 0x00, 0x00, 0x01, 0x82, 0x00, 0x00, 0x02, 0x82, 0x00, 0x00, 0x03, + 0x85, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x02, 0x85, 0x00, 0x00, 0x01, 0x85, 0x00, 0x00, 0x01, 0x84, 0x00, 0x00, 0x02, + 0x84, 0x00, 0x00, 0x03, 0x87, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x02, 0x87, 0x00, 0x00, 0x01, 0x87, 0x00, 0x00, 0x01, + 0x86, 0x00, 0x00, 0x02, 0x86, 0x00, 0x00, 0x03, 0x89, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x02, 0x89, 0x00, 0x00, 0x01, + 0x89, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x02, 0x88, 0x00, 0x00, 0x03, 0x8b, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x02, + 0x8b, 0x00, 0x00, 0x01, 0x8b, 0x00, 0x00, 0x01, 0x8a, 0x00, 0x00, 0x02, 0x8a, 0x00, 0x00, 0x03, 0x8d, 0x00, 0x00, 0x00, + 0x8c, 0x00, 0x00, 0x02, 0x8d, 0x00, 0x00, 0x01, 0x8d, 0x00, 0x00, 0x01, 0x8c, 0x00, 0x00, 0x02, 0x8c, 0x00, 0x00, 0x03, + 0x8f, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x02, 0x8f, 0x00, 0x00, 0x01, 0x8f, 0x00, 0x00, 0x01, 0x8e, 0x00, 0x00, 0x02, + 0x8e, 0x00, 0x00, 0x03, 0x91, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x02, 0x91, 0x00, 0x00, 0x01, 0x91, 0x00, 0x00, 0x01, + 0x90, 0x00, 0x00, 0x02, 0x90, 0x00, 0x00, 0x03, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, + 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xe4, 0x9a, 0xf5, 0x8a, 0xe4, 0x9a, + 0xf5, 0x8a, 0xe4, 0x9a, 0xf5, 0x8a, 0xe4, 0x9a, 0xf5, 0x8a, 0xe4, 0x9a, 0xf5, 0x8a, 0xe4, 0x9a, 0xe4, 0x9a, 0xd6, 0xa8, + 0xe4, 0x9a, 0xd6, 0xa8, 0xe4, 0x9a, 0xd6, 0xa8, 0xe4, 0x9a, 0xd6, 0xa8, 0xe4, 0x9a, 0xd6, 0xa8, 0xe4, 0x9a, 0xd6, 0xa8, + 0xd6, 0xa8, 0xca, 0xb4, 0xd6, 0xa8, 0xca, 0xb4, 0xd6, 0xa8, 0xca, 0xb4, 0xd6, 0xa8, 0xca, 0xb4, 0xd6, 0xa8, 0xca, 0xb4, + 0xd6, 0xa8, 0xca, 0xb4, 0xca, 0xb4, 0xbf, 0xbf, 0xca, 0xb4, 0xbf, 0xbf, 0xca, 0xb4, 0xbf, 0xbf, 0xca, 0xb4, 0xbf, 0xbf, + 0xca, 0xb4, 0xbf, 0xbf, 0xca, 0xb4, 0xbf, 0xbf, 0xbf, 0xbf, 0xb4, 0xca, 0xbf, 0xbf, 0xb4, 0xca, 0xbf, 0xbf, 0xb4, 0xca, + 0xbf, 0xbf, 0xb4, 0xca, 0xbf, 0xbf, 0xb4, 0xca, 0xbf, 0xbf, 0xb4, 0xca, 0xb4, 0xca, 0xa8, 0xd6, 0xb4, 0xca, 0xa8, 0xd6, + 0xb4, 0xca, 0xa8, 0xd6, 0xb4, 0xca, 0xa8, 0xd6, 0xb4, 0xca, 0xa8, 0xd6, 0xb4, 0xca, 0xa8, 0xd6, 0xa8, 0xd6, 0x9a, 0xe4, + 0xa8, 0xd6, 0x9a, 0xe4, 0xa8, 0xd6, 0x9a, 0xe4, 0xa8, 0xd6, 0x9a, 0xe4, 0xa8, 0xd6, 0x9a, 0xe4, 0xa8, 0xd6, 0x9a, 0xe4, + 0x9a, 0xe4, 0x8a, 0xf5, 0x9a, 0xe4, 0x8a, 0xf5, 0x9a, 0xe4, 0x8a, 0xf5, 0x9a, 0xe4, 0x8a, 0xf5, 0x9a, 0xe4, 0x8a, 0xf5, + 0x9a, 0xe4, 0x8a, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, + 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x75, 0xf5, 0x65, 0xe4, 0x75, 0xf5, 0x65, 0xe4, 0x75, 0xf5, 0x65, 0xe4, + 0x75, 0xf5, 0x65, 0xe4, 0x75, 0xf5, 0x65, 0xe4, 0x75, 0xf5, 0x65, 0xe4, 0x65, 0xe4, 0x57, 0xd6, 0x65, 0xe4, 0x57, 0xd6, + 0x65, 0xe4, 0x57, 0xd6, 0x65, 0xe4, 0x57, 0xd6, 0x65, 0xe4, 0x57, 0xd6, 0x65, 0xe4, 0x57, 0xd6, 0x57, 0xd6, 0x4b, 0xca, + 0x57, 0xd6, 0x4b, 0xca, 0x57, 0xd6, 0x4b, 0xca, 0x57, 0xd6, 0x4b, 0xca, 0x57, 0xd6, 0x4b, 0xca, 0x57, 0xd6, 0x4b, 0xca, + 0x4b, 0xca, 0x40, 0xbf, 0x4b, 0xca, 0x40, 0xbf, 0x4b, 0xca, 0x40, 0xbf, 0x4b, 0xca, 0x40, 0xbf, 0x4b, 0xca, 0x40, 0xbf, + 0x4b, 0xca, 0x40, 0xbf, 0x40, 0xbf, 0x35, 0xb4, 0x40, 0xbf, 0x35, 0xb4, 0x40, 0xbf, 0x35, 0xb4, 0x40, 0xbf, 0x35, 0xb4, + 0x40, 0xbf, 0x35, 0xb4, 0x40, 0xbf, 0x35, 0xb4, 0x35, 0xb4, 0x29, 0xa8, 0x35, 0xb4, 0x29, 0xa8, 0x35, 0xb4, 0x29, 0xa8, + 0x35, 0xb4, 0x29, 0xa8, 0x35, 0xb4, 0x29, 0xa8, 0x35, 0xb4, 0x29, 0xa8, 0x29, 0xa8, 0x1b, 0x9a, 0x29, 0xa8, 0x1b, 0x9a, + 0x29, 0xa8, 0x1b, 0x9a, 0x29, 0xa8, 0x1b, 0x9a, 0x29, 0xa8, 0x1b, 0x9a, 0x29, 0xa8, 0x1b, 0x9a, 0x1b, 0x9a, 0x0a, 0x8a, + 0x1b, 0x9a, 0x0a, 0x8a, 0x1b, 0x9a, 0x0a, 0x8a, 0x1b, 0x9a, 0x0a, 0x8a, 0x1b, 0x9a, 0x0a, 0x8a, 0x1b, 0x9a, 0x0a, 0x8a, + 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, + 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x75, 0x1b, 0x65, 0x0a, 0x75, 0x1b, 0x65, 0x0a, 0x75, 0x1b, 0x65, 0x0a, 0x75, 0x1b, 0x65, + 0x0a, 0x75, 0x1b, 0x65, 0x0a, 0x75, 0x1b, 0x65, 0x1b, 0x65, 0x29, 0x57, 0x1b, 0x65, 0x29, 0x57, 0x1b, 0x65, 0x29, 0x57, + 0x1b, 0x65, 0x29, 0x57, 0x1b, 0x65, 0x29, 0x57, 0x1b, 0x65, 0x29, 0x57, 0x29, 0x57, 0x35, 0x4b, 0x29, 0x57, 0x35, 0x4b, + 0x29, 0x57, 0x35, 0x4b, 0x29, 0x57, 0x35, 0x4b, 0x29, 0x57, 0x35, 0x4b, 0x29, 0x57, 0x35, 0x4b, 0x35, 0x4b, 0x40, 0x40, + 0x35, 0x4b, 0x40, 0x40, 0x35, 0x4b, 0x40, 0x40, 0x35, 0x4b, 0x40, 0x40, 0x35, 0x4b, 0x40, 0x40, 0x35, 0x4b, 0x40, 0x40, + 0x40, 0x40, 0x4b, 0x35, 0x40, 0x40, 0x4b, 0x35, 0x40, 0x40, 0x4b, 0x35, 0x40, 0x40, 0x4b, 0x35, 0x40, 0x40, 0x4b, 0x35, + 0x40, 0x40, 0x4b, 0x35, 0x4b, 0x35, 0x57, 0x29, 0x4b, 0x35, 0x57, 0x29, 0x4b, 0x35, 0x57, 0x29, 0x4b, 0x35, 0x57, 0x29, + 0x4b, 0x35, 0x57, 0x29, 0x4b, 0x35, 0x57, 0x29, 0x57, 0x29, 0x65, 0x1b, 0x57, 0x29, 0x65, 0x1b, 0x57, 0x29, 0x65, 0x1b, + 0x57, 0x29, 0x65, 0x1b, 0x57, 0x29, 0x65, 0x1b, 0x57, 0x29, 0x65, 0x1b, 0x65, 0x1b, 0x75, 0x0a, 0x65, 0x1b, 0x75, 0x0a, + 0x65, 0x1b, 0x75, 0x0a, 0x65, 0x1b, 0x75, 0x0a, 0x65, 0x1b, 0x75, 0x0a, 0x65, 0x1b, 0x75, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, + 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, + 0x8a, 0x0a, 0x9a, 0x1b, 0x8a, 0x0a, 0x9a, 0x1b, 0x8a, 0x0a, 0x9a, 0x1b, 0x8a, 0x0a, 0x9a, 0x1b, 0x8a, 0x0a, 0x9a, 0x1b, + 0x8a, 0x0a, 0x9a, 0x1b, 0x9a, 0x1b, 0xa8, 0x29, 0x9a, 0x1b, 0xa8, 0x29, 0x9a, 0x1b, 0xa8, 0x29, 0x9a, 0x1b, 0xa8, 0x29, + 0x9a, 0x1b, 0xa8, 0x29, 0x9a, 0x1b, 0xa8, 0x29, 0xa8, 0x29, 0xb4, 0x35, 0xa8, 0x29, 0xb4, 0x35, 0xa8, 0x29, 0xb4, 0x35, + 0xa8, 0x29, 0xb4, 0x35, 0xa8, 0x29, 0xb4, 0x35, 0xa8, 0x29, 0xb4, 0x35, 0xb4, 0x35, 0xbf, 0x40, 0xb4, 0x35, 0xbf, 0x40, + 0xb4, 0x35, 0xbf, 0x40, 0xb4, 0x35, 0xbf, 0x40, 0xb4, 0x35, 0xbf, 0x40, 0xb4, 0x35, 0xbf, 0x40, 0xbf, 0x40, 0xca, 0x4b, + 0xbf, 0x40, 0xca, 0x4b, 0xbf, 0x40, 0xca, 0x4b, 0xbf, 0x40, 0xca, 0x4b, 0xbf, 0x40, 0xca, 0x4b, 0xbf, 0x40, 0xca, 0x4b, + 0xca, 0x4b, 0xd6, 0x57, 0xca, 0x4b, 0xd6, 0x57, 0xca, 0x4b, 0xd6, 0x57, 0xca, 0x4b, 0xd6, 0x57, 0xca, 0x4b, 0xd6, 0x57, + 0xca, 0x4b, 0xd6, 0x57, 0xd6, 0x57, 0xe4, 0x65, 0xd6, 0x57, 0xe4, 0x65, 0xd6, 0x57, 0xe4, 0x65, 0xd6, 0x57, 0xe4, 0x65, + 0xd6, 0x57, 0xe4, 0x65, 0xd6, 0x57, 0xe4, 0x65, 0xe4, 0x65, 0xf5, 0x75, 0xe4, 0x65, 0xf5, 0x75, 0xe4, 0x65, 0xf5, 0x75, + 0xe4, 0x65, 0xf5, 0x75, 0xe4, 0x65, 0xf5, 0x75, 0xe4, 0x65, 0xf5, 0x75, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, + 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x21, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x21, 0x00, + 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x20, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, + 0x00, 0x1d, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1c, + 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1b, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1b, 0x00, + 0x00, 0x1b, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x19, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, + 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x18, 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x17, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x17, 0x00, + 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x16, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, + 0x00, 0x00, 0x16, 0x00, 0x00, 0x15, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x14, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, + 0x00, 0x13, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x12, + 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x11, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x10, 0x00, 0x00, 0x11, 0x00, + 0x00, 0x11, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, + 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, + 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x27, 0x00, 0x00, 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, + 0x00, 0x28, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x29, + 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x2a, 0x00, 0x00, + 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2a, 0x00, + 0x00, 0x2a, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, + 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2e, 0x00, + 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, + 0x00, 0x00, 0x2f, 0x00, 0x00, 0x30, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x31, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, + 0x00, 0x32, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x33, + 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x34, 0x00, 0x00, + 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00, 0x34, 0x00, + 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x36, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, + 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x37, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, + 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, + 0x00, 0x00, 0x39, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, + 0x3a, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, + 0x00, 0x3c, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3d, + 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3e, 0x00, 0x00, + 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3e, 0x00, + 0x00, 0x3e, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x41, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x42, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x42, 0x00, + 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x43, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, + 0x00, 0x00, 0x43, 0x00, 0x00, 0x44, 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x45, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, + 0x00, 0x46, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x47, + 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x48, 0x00, 0x00, + 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x49, 0x00, 0x00, 0x48, 0x00, + 0x00, 0x48, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x25, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, + 0x00, 0x00, 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x02, 0x00, 0x00, 0x23, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, + 0x00, 0x22, 0x00, 0x00, 0x24, 0x00, 0x00, 0x21, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, + 0x00, 0x00, 0x23, 0x00, 0x00, 0x20, 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x21, 0x00, + 0x00, 0x1e, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1d, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1c, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1e, 0x00, + 0x00, 0x1e, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, + 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x19, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x17, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, + 0x00, 0x00, 0x19, 0x00, 0x00, 0x16, 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x15, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x17, 0x00, + 0x00, 0x14, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x16, 0x00, 0x00, 0x13, + 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x15, 0x00, 0x00, 0x12, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x00, 0x11, 0x00, 0x00, 0x14, 0x00, + 0x00, 0x14, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x13, 0x00, 0x00, 0x10, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x11, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x0e, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, + 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0d, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x09, 0x00, 0x00, 0x06, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x05, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x24, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x49, 0x00, 0x00, 0x28, + 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x25, 0x00, 0x00, 0x29, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x27, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x27, 0x00, + 0x00, 0x27, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x28, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, + 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x29, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2d, 0x00, + 0x00, 0x2d, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, + 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x30, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x2e, 0x00, + 0x00, 0x31, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x32, + 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x30, 0x00, 0x00, 0x33, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x31, 0x00, 0x00, 0x34, 0x00, 0x00, 0x31, 0x00, + 0x00, 0x31, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x32, 0x00, 0x00, 0x35, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, + 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x33, 0x00, 0x00, 0x36, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x34, 0x00, 0x00, 0x37, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x37, 0x00, + 0x00, 0x37, 0x00, 0x00, 0x35, 0x00, 0x00, 0x38, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x36, 0x00, 0x00, 0x39, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, + 0x37, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x3b, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3c, + 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3d, 0x00, 0x00, + 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3b, 0x00, + 0x00, 0x3b, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, + 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x40, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x41, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x41, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x42, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x43, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x44, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x42, 0x00, + 0x00, 0x45, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x43, 0x00, 0x00, 0x46, + 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x44, 0x00, 0x00, 0x47, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x45, 0x00, 0x00, 0x48, 0x00, 0x00, 0x45, 0x00, + 0x00, 0x45, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x46, 0x00, 0x00, 0x49, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, + 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x47, 0x00, 0x00, 0x25, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x48, 0x00, 0x00, 0x27, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x27, 0x00, + 0x00, 0x27, 0x00, 0x00, 0x24, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x36, 0x24, 0x00, 0x00, 0x06, 0x24, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x4e, 0x23, 0x00, 0x00, 0x1e, 0x24, 0x00, 0x00, 0x36, 0x23, 0x00, 0x00, 0x06, + 0x23, 0x00, 0x00, 0x06, 0x24, 0x00, 0x00, 0x36, 0x24, 0x00, 0x00, 0x4e, 0x22, 0x00, 0x00, 0x1e, 0x23, 0x00, 0x00, 0x36, + 0x22, 0x00, 0x00, 0x06, 0x22, 0x00, 0x00, 0x06, 0x23, 0x00, 0x00, 0x36, 0x23, 0x00, 0x00, 0x4e, 0x21, 0x00, 0x00, 0x1e, + 0x22, 0x00, 0x00, 0x36, 0x21, 0x00, 0x00, 0x06, 0x21, 0x00, 0x00, 0x06, 0x22, 0x00, 0x00, 0x36, 0x22, 0x00, 0x00, 0x4e, + 0x20, 0x00, 0x00, 0x1e, 0x21, 0x00, 0x00, 0x36, 0x20, 0x00, 0x00, 0x06, 0x20, 0x00, 0x00, 0x06, 0x21, 0x00, 0x00, 0x36, + 0x21, 0x00, 0x00, 0x4e, 0x1f, 0x00, 0x00, 0x1e, 0x20, 0x00, 0x00, 0x36, 0x1f, 0x00, 0x00, 0x06, 0x1f, 0x00, 0x00, 0x06, + 0x20, 0x00, 0x00, 0x36, 0x20, 0x00, 0x00, 0x4e, 0x1e, 0x00, 0x00, 0x1e, 0x1f, 0x00, 0x00, 0x36, 0x1e, 0x00, 0x00, 0x06, + 0x1e, 0x00, 0x00, 0x06, 0x1f, 0x00, 0x00, 0x36, 0x1f, 0x00, 0x00, 0x4e, 0x1d, 0x00, 0x00, 0x1e, 0x1e, 0x00, 0x00, 0x36, + 0x1d, 0x00, 0x00, 0x06, 0x1d, 0x00, 0x00, 0x06, 0x1e, 0x00, 0x00, 0x36, 0x1e, 0x00, 0x00, 0x4e, 0x1c, 0x00, 0x00, 0x1e, + 0x1d, 0x00, 0x00, 0x36, 0x1c, 0x00, 0x00, 0x06, 0x1c, 0x00, 0x00, 0x06, 0x1d, 0x00, 0x00, 0x36, 0x1d, 0x00, 0x00, 0x4e, + 0x1b, 0x00, 0x00, 0x1e, 0x1c, 0x00, 0x00, 0x36, 0x1b, 0x00, 0x00, 0x06, 0x1b, 0x00, 0x00, 0x06, 0x1c, 0x00, 0x00, 0x36, + 0x1c, 0x00, 0x00, 0x4e, 0x1a, 0x00, 0x00, 0x1e, 0x1b, 0x00, 0x00, 0x36, 0x1a, 0x00, 0x00, 0x06, 0x1a, 0x00, 0x00, 0x06, + 0x1b, 0x00, 0x00, 0x36, 0x1b, 0x00, 0x00, 0x4e, 0x19, 0x00, 0x00, 0x1e, 0x1a, 0x00, 0x00, 0x36, 0x19, 0x00, 0x00, 0x06, + 0x19, 0x00, 0x00, 0x06, 0x1a, 0x00, 0x00, 0x36, 0x1a, 0x00, 0x00, 0x4e, 0x18, 0x00, 0x00, 0x1e, 0x19, 0x00, 0x00, 0x36, + 0x18, 0x00, 0x00, 0x06, 0x18, 0x00, 0x00, 0x06, 0x19, 0x00, 0x00, 0x36, 0x19, 0x00, 0x00, 0x4e, 0x17, 0x00, 0x00, 0x1e, + 0x18, 0x00, 0x00, 0x36, 0x17, 0x00, 0x00, 0x06, 0x17, 0x00, 0x00, 0x06, 0x18, 0x00, 0x00, 0x36, 0x18, 0x00, 0x00, 0x4e, + 0x16, 0x00, 0x00, 0x1e, 0x17, 0x00, 0x00, 0x36, 0x16, 0x00, 0x00, 0x06, 0x16, 0x00, 0x00, 0x06, 0x17, 0x00, 0x00, 0x36, + 0x17, 0x00, 0x00, 0x4e, 0x15, 0x00, 0x00, 0x1e, 0x16, 0x00, 0x00, 0x36, 0x15, 0x00, 0x00, 0x06, 0x15, 0x00, 0x00, 0x06, + 0x16, 0x00, 0x00, 0x36, 0x16, 0x00, 0x00, 0x4e, 0x14, 0x00, 0x00, 0x1e, 0x15, 0x00, 0x00, 0x36, 0x14, 0x00, 0x00, 0x06, + 0x14, 0x00, 0x00, 0x06, 0x15, 0x00, 0x00, 0x36, 0x15, 0x00, 0x00, 0x4e, 0x13, 0x00, 0x00, 0x1e, 0x14, 0x00, 0x00, 0x36, + 0x13, 0x00, 0x00, 0x06, 0x13, 0x00, 0x00, 0x06, 0x14, 0x00, 0x00, 0x36, 0x14, 0x00, 0x00, 0x4e, 0x12, 0x00, 0x00, 0x1e, + 0x13, 0x00, 0x00, 0x36, 0x12, 0x00, 0x00, 0x06, 0x12, 0x00, 0x00, 0x06, 0x13, 0x00, 0x00, 0x36, 0x13, 0x00, 0x00, 0x4e, + 0x11, 0x00, 0x00, 0x1e, 0x12, 0x00, 0x00, 0x36, 0x11, 0x00, 0x00, 0x06, 0x11, 0x00, 0x00, 0x06, 0x12, 0x00, 0x00, 0x36, + 0x12, 0x00, 0x00, 0x4e, 0x10, 0x00, 0x00, 0x1e, 0x11, 0x00, 0x00, 0x36, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x00, 0x06, + 0x11, 0x00, 0x00, 0x36, 0x11, 0x00, 0x00, 0x4e, 0x0f, 0x00, 0x00, 0x1e, 0x10, 0x00, 0x00, 0x36, 0x0f, 0x00, 0x00, 0x06, + 0x0f, 0x00, 0x00, 0x06, 0x10, 0x00, 0x00, 0x36, 0x10, 0x00, 0x00, 0x4e, 0x0e, 0x00, 0x00, 0x1e, 0x0f, 0x00, 0x00, 0x36, + 0x0e, 0x00, 0x00, 0x06, 0x0e, 0x00, 0x00, 0x06, 0x0f, 0x00, 0x00, 0x36, 0x0f, 0x00, 0x00, 0x4e, 0x0d, 0x00, 0x00, 0x1e, + 0x0e, 0x00, 0x00, 0x36, 0x0d, 0x00, 0x00, 0x06, 0x0d, 0x00, 0x00, 0x06, 0x0e, 0x00, 0x00, 0x36, 0x0e, 0x00, 0x00, 0x4e, + 0x0c, 0x00, 0x00, 0x1e, 0x0d, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x06, 0x0d, 0x00, 0x00, 0x36, + 0x0d, 0x00, 0x00, 0x4e, 0x0b, 0x00, 0x00, 0x1e, 0x0c, 0x00, 0x00, 0x36, 0x0b, 0x00, 0x00, 0x06, 0x0b, 0x00, 0x00, 0x06, + 0x0c, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x4e, 0x0a, 0x00, 0x00, 0x1e, 0x0b, 0x00, 0x00, 0x36, 0x0a, 0x00, 0x00, 0x06, + 0x0a, 0x00, 0x00, 0x06, 0x0b, 0x00, 0x00, 0x36, 0x0b, 0x00, 0x00, 0x4e, 0x09, 0x00, 0x00, 0x1e, 0x0a, 0x00, 0x00, 0x36, + 0x09, 0x00, 0x00, 0x06, 0x09, 0x00, 0x00, 0x06, 0x0a, 0x00, 0x00, 0x36, 0x0a, 0x00, 0x00, 0x4e, 0x08, 0x00, 0x00, 0x1e, + 0x09, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x06, 0x08, 0x00, 0x00, 0x06, 0x09, 0x00, 0x00, 0x36, 0x09, 0x00, 0x00, 0x4e, + 0x07, 0x00, 0x00, 0x1e, 0x08, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x06, 0x07, 0x00, 0x00, 0x06, 0x08, 0x00, 0x00, 0x36, + 0x08, 0x00, 0x00, 0x4e, 0x06, 0x00, 0x00, 0x1e, 0x07, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x06, + 0x07, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x4e, 0x05, 0x00, 0x00, 0x1e, 0x06, 0x00, 0x00, 0x36, 0x05, 0x00, 0x00, 0x06, + 0x05, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x4e, 0x04, 0x00, 0x00, 0x1e, 0x05, 0x00, 0x00, 0x36, + 0x04, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x36, 0x05, 0x00, 0x00, 0x4e, 0x03, 0x00, 0x00, 0x1e, + 0x04, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x4e, + 0x02, 0x00, 0x00, 0x1e, 0x03, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x36, + 0x03, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, + 0x02, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x4e, 0x27, 0x00, 0x00, 0x1e, 0x25, 0x00, 0x00, 0x36, 0x27, 0x00, 0x00, 0x06, + 0x27, 0x00, 0x00, 0x06, 0x25, 0x00, 0x00, 0x36, 0x25, 0x00, 0x00, 0x4e, 0x28, 0x00, 0x00, 0x1e, 0x27, 0x00, 0x00, 0x36, + 0x28, 0x00, 0x00, 0x06, 0x28, 0x00, 0x00, 0x06, 0x27, 0x00, 0x00, 0x36, 0x27, 0x00, 0x00, 0x4e, 0x29, 0x00, 0x00, 0x1e, + 0x28, 0x00, 0x00, 0x36, 0x29, 0x00, 0x00, 0x06, 0x29, 0x00, 0x00, 0x06, 0x28, 0x00, 0x00, 0x36, 0x28, 0x00, 0x00, 0x4e, + 0x2a, 0x00, 0x00, 0x1e, 0x29, 0x00, 0x00, 0x36, 0x2a, 0x00, 0x00, 0x06, 0x2a, 0x00, 0x00, 0x06, 0x29, 0x00, 0x00, 0x36, + 0x29, 0x00, 0x00, 0x4e, 0x2b, 0x00, 0x00, 0x1e, 0x2a, 0x00, 0x00, 0x36, 0x2b, 0x00, 0x00, 0x06, 0x2b, 0x00, 0x00, 0x06, + 0x2a, 0x00, 0x00, 0x36, 0x2a, 0x00, 0x00, 0x4e, 0x2c, 0x00, 0x00, 0x1e, 0x2b, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x06, + 0x2c, 0x00, 0x00, 0x06, 0x2b, 0x00, 0x00, 0x36, 0x2b, 0x00, 0x00, 0x4e, 0x2d, 0x00, 0x00, 0x1e, 0x2c, 0x00, 0x00, 0x36, + 0x2d, 0x00, 0x00, 0x06, 0x2d, 0x00, 0x00, 0x06, 0x2c, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x4e, 0x2e, 0x00, 0x00, 0x1e, + 0x2d, 0x00, 0x00, 0x36, 0x2e, 0x00, 0x00, 0x06, 0x2e, 0x00, 0x00, 0x06, 0x2d, 0x00, 0x00, 0x36, 0x2d, 0x00, 0x00, 0x4e, + 0x2f, 0x00, 0x00, 0x1e, 0x2e, 0x00, 0x00, 0x36, 0x2f, 0x00, 0x00, 0x06, 0x2f, 0x00, 0x00, 0x06, 0x2e, 0x00, 0x00, 0x36, + 0x2e, 0x00, 0x00, 0x4e, 0x30, 0x00, 0x00, 0x1e, 0x2f, 0x00, 0x00, 0x36, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, + 0x2f, 0x00, 0x00, 0x36, 0x2f, 0x00, 0x00, 0x4e, 0x31, 0x00, 0x00, 0x1e, 0x30, 0x00, 0x00, 0x36, 0x31, 0x00, 0x00, 0x06, + 0x31, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x36, 0x30, 0x00, 0x00, 0x4e, 0x32, 0x00, 0x00, 0x1e, 0x31, 0x00, 0x00, 0x36, + 0x32, 0x00, 0x00, 0x06, 0x32, 0x00, 0x00, 0x06, 0x31, 0x00, 0x00, 0x36, 0x31, 0x00, 0x00, 0x4e, 0x33, 0x00, 0x00, 0x1e, + 0x32, 0x00, 0x00, 0x36, 0x33, 0x00, 0x00, 0x06, 0x33, 0x00, 0x00, 0x06, 0x32, 0x00, 0x00, 0x36, 0x32, 0x00, 0x00, 0x4e, + 0x34, 0x00, 0x00, 0x1e, 0x33, 0x00, 0x00, 0x36, 0x34, 0x00, 0x00, 0x06, 0x34, 0x00, 0x00, 0x06, 0x33, 0x00, 0x00, 0x36, + 0x33, 0x00, 0x00, 0x4e, 0x35, 0x00, 0x00, 0x1e, 0x34, 0x00, 0x00, 0x36, 0x35, 0x00, 0x00, 0x06, 0x35, 0x00, 0x00, 0x06, + 0x34, 0x00, 0x00, 0x36, 0x34, 0x00, 0x00, 0x4e, 0x36, 0x00, 0x00, 0x1e, 0x35, 0x00, 0x00, 0x36, 0x36, 0x00, 0x00, 0x06, + 0x36, 0x00, 0x00, 0x06, 0x35, 0x00, 0x00, 0x36, 0x35, 0x00, 0x00, 0x4e, 0x37, 0x00, 0x00, 0x1e, 0x36, 0x00, 0x00, 0x36, + 0x37, 0x00, 0x00, 0x06, 0x37, 0x00, 0x00, 0x06, 0x36, 0x00, 0x00, 0x36, 0x36, 0x00, 0x00, 0x4e, 0x38, 0x00, 0x00, 0x1e, + 0x37, 0x00, 0x00, 0x36, 0x38, 0x00, 0x00, 0x06, 0x38, 0x00, 0x00, 0x06, 0x37, 0x00, 0x00, 0x36, 0x37, 0x00, 0x00, 0x4e, + 0x39, 0x00, 0x00, 0x1e, 0x38, 0x00, 0x00, 0x36, 0x39, 0x00, 0x00, 0x06, 0x39, 0x00, 0x00, 0x06, 0x38, 0x00, 0x00, 0x36, + 0x38, 0x00, 0x00, 0x4e, 0x3a, 0x00, 0x00, 0x1e, 0x39, 0x00, 0x00, 0x36, 0x3a, 0x00, 0x00, 0x06, 0x3a, 0x00, 0x00, 0x06, + 0x39, 0x00, 0x00, 0x36, 0x39, 0x00, 0x00, 0x4e, 0x3b, 0x00, 0x00, 0x1e, 0x3a, 0x00, 0x00, 0x36, 0x3b, 0x00, 0x00, 0x06, + 0x3b, 0x00, 0x00, 0x06, 0x3a, 0x00, 0x00, 0x36, 0x3a, 0x00, 0x00, 0x4e, 0x3c, 0x00, 0x00, 0x1e, 0x3b, 0x00, 0x00, 0x36, + 0x3c, 0x00, 0x00, 0x06, 0x3c, 0x00, 0x00, 0x06, 0x3b, 0x00, 0x00, 0x36, 0x3b, 0x00, 0x00, 0x4e, 0x3d, 0x00, 0x00, 0x1e, + 0x3c, 0x00, 0x00, 0x36, 0x3d, 0x00, 0x00, 0x06, 0x3d, 0x00, 0x00, 0x06, 0x3c, 0x00, 0x00, 0x36, 0x3c, 0x00, 0x00, 0x4e, + 0x3e, 0x00, 0x00, 0x1e, 0x3d, 0x00, 0x00, 0x36, 0x3e, 0x00, 0x00, 0x06, 0x3e, 0x00, 0x00, 0x06, 0x3d, 0x00, 0x00, 0x36, + 0x3d, 0x00, 0x00, 0x4e, 0x3f, 0x00, 0x00, 0x1e, 0x3e, 0x00, 0x00, 0x36, 0x3f, 0x00, 0x00, 0x06, 0x3f, 0x00, 0x00, 0x06, + 0x3e, 0x00, 0x00, 0x36, 0x3e, 0x00, 0x00, 0x4e, 0x40, 0x00, 0x00, 0x1e, 0x3f, 0x00, 0x00, 0x36, 0x40, 0x00, 0x00, 0x06, + 0x40, 0x00, 0x00, 0x06, 0x3f, 0x00, 0x00, 0x36, 0x3f, 0x00, 0x00, 0x4e, 0x41, 0x00, 0x00, 0x1e, 0x40, 0x00, 0x00, 0x36, + 0x41, 0x00, 0x00, 0x06, 0x41, 0x00, 0x00, 0x06, 0x40, 0x00, 0x00, 0x36, 0x40, 0x00, 0x00, 0x4e, 0x42, 0x00, 0x00, 0x1e, + 0x41, 0x00, 0x00, 0x36, 0x42, 0x00, 0x00, 0x06, 0x42, 0x00, 0x00, 0x06, 0x41, 0x00, 0x00, 0x36, 0x41, 0x00, 0x00, 0x4e, + 0x43, 0x00, 0x00, 0x1e, 0x42, 0x00, 0x00, 0x36, 0x43, 0x00, 0x00, 0x06, 0x43, 0x00, 0x00, 0x06, 0x42, 0x00, 0x00, 0x36, + 0x42, 0x00, 0x00, 0x4e, 0x44, 0x00, 0x00, 0x1e, 0x43, 0x00, 0x00, 0x36, 0x44, 0x00, 0x00, 0x06, 0x44, 0x00, 0x00, 0x06, + 0x43, 0x00, 0x00, 0x36, 0x43, 0x00, 0x00, 0x4e, 0x45, 0x00, 0x00, 0x1e, 0x44, 0x00, 0x00, 0x36, 0x45, 0x00, 0x00, 0x06, + 0x45, 0x00, 0x00, 0x06, 0x44, 0x00, 0x00, 0x36, 0x44, 0x00, 0x00, 0x4e, 0x46, 0x00, 0x00, 0x1e, 0x45, 0x00, 0x00, 0x36, + 0x46, 0x00, 0x00, 0x06, 0x46, 0x00, 0x00, 0x06, 0x45, 0x00, 0x00, 0x36, 0x45, 0x00, 0x00, 0x4e, 0x47, 0x00, 0x00, 0x1e, + 0x46, 0x00, 0x00, 0x36, 0x47, 0x00, 0x00, 0x06, 0x47, 0x00, 0x00, 0x06, 0x46, 0x00, 0x00, 0x36, 0x46, 0x00, 0x00, 0x4e, + 0x48, 0x00, 0x00, 0x1e, 0x47, 0x00, 0x00, 0x36, 0x48, 0x00, 0x00, 0x06, 0x48, 0x00, 0x00, 0x06, 0x47, 0x00, 0x00, 0x36, + 0x47, 0x00, 0x00, 0x4e, 0x49, 0x00, 0x00, 0x1e, 0x48, 0x00, 0x00, 0x36, 0x49, 0x00, 0x00, 0x06, 0x49, 0x00, 0x00, 0x06, + 0x48, 0x00, 0x00, 0x36, 0x48, 0x00, 0x00, 0x4e, 0x25, 0x00, 0x00, 0x1e, 0x49, 0x00, 0x00, 0x36, 0x25, 0x00, 0x00, 0x06, + 0x25, 0x00, 0x00, 0x06, 0x49, 0x00, 0x00, 0x36, 0x49, 0x00, 0x00, 0x4e, + ]), + }, +}; diff --git a/test-apps/testbed/frontend/TileIO.data.1.3.ts b/test-apps/testbed/frontend/TileIO.data.1.3.ts new file mode 100644 index 0000000..c183b7a --- /dev/null +++ b/test-apps/testbed/frontend/TileIO.data.1.3.ts @@ -0,0 +1,1696 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { IModelTileIO } from "@bentley/imodeljs-frontend/lib/tile"; + +// Binary tile data produced using version 1.2 of the tile format. +export const TILE_DATA_1_3 = { + versionMajor: 1, + versionMinor: 3, + headerLength: 84, + rectangle: { + flags: IModelTileIO.Flags.None, + bytes: new Uint8Array([ + 0x69, 0x4d, 0x64, 0x6c, 0x03, 0x00, 0x01, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x4b, 0x37, 0x89, + 0x41, 0x00, 0x04, 0xc0, 0xc7, 0x4b, 0x37, 0x89, 0x41, 0x00, 0x14, 0xc0, 0x00, 0x00, 0xbd, 0x9a, 0xf2, 0xd7, 0x5a, 0xbe, + 0x39, 0xb4, 0xc8, 0x76, 0xbe, 0xff, 0x03, 0x40, 0x39, 0xb4, 0xc8, 0x76, 0xbe, 0xff, 0x13, 0x40, 0x00, 0x00, 0xbd, 0x9a, + 0xf2, 0xd7, 0x5a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc4, 0x09, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, + 0x02, 0x00, 0x00, 0x00, 0x50, 0x09, 0x00, 0x00, 0x50, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, + 0x78, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x36, 0x34, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x30, 0x7d, 0x2c, + 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, + 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, + 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x35, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, + 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x37, 0x32, 0x2c, + 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x32, 0x35, 0x32, 0x7d, 0x2c, 0x22, 0x62, + 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, + 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, + 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x37, 0x32, 0x2c, 0x22, + 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x38, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, + 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x38, 0x2c, 0x22, 0x62, 0x79, 0x74, + 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x36, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, + 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, + 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, + 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, + 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x22, 0x3a, 0x33, 0x39, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, + 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x37, 0x32, 0x2c, + 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x33, 0x32, 0x34, 0x7d, 0x7d, 0x2c, 0x22, + 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4b, 0x48, + 0x52, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x57, 0x45, 0x42, 0x33, + 0x44, 0x5f, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, + 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x66, 0x69, + 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, + 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, + 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, + 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, + 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x2c, 0x22, + 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x7d, 0x7d, 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x7b, + 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, + 0x69, 0x76, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x65, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x6f, + 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, + 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, + 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, + 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, + 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, + 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x22, 0x3a, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, + 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, + 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, + 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x7d, 0x7d, 0x2c, 0x22, + 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x2c, 0x22, 0x73, + 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, + 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x2c, 0x22, + 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x7d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x76, + 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, + 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x44, 0x22, 0x3a, 0x30, 0x2c, 0x22, + 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, + 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, + 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, + 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x70, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, + 0x3a, 0x5b, 0x37, 0x2e, 0x36, 0x33, 0x30, 0x36, 0x35, 0x35, 0x33, 0x37, 0x34, 0x39, 0x39, 0x30, 0x34, 0x36, 0x31, 0x31, + 0x65, 0x2d, 0x30, 0x35, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, + 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x31, 0x35, 0x32, 0x36, 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, + 0x39, 0x32, 0x32, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, + 0x31, 0x2e, 0x35, 0x32, 0x36, 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, 0x2d, + 0x30, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x2d, 0x32, 0x2e, 0x35, 0x30, 0x30, 0x33, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x36, 0x2c, 0x2d, 0x35, 0x2e, 0x30, 0x30, 0x30, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x31, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, + 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, + 0x32, 0x2e, 0x35, 0x30, 0x30, 0x33, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x36, 0x2c, 0x35, + 0x2e, 0x30, 0x30, 0x30, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x31, 0x2c, 0x30, 0x2e, + 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, + 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x32, 0x2e, 0x35, 0x30, 0x30, 0x33, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x36, 0x2c, 0x2d, 0x35, 0x2e, 0x30, 0x30, 0x30, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x31, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, + 0x7d, 0x2c, 0x22, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, + 0x38, 0x30, 0x2c, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, + 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4e, 0x6f, 0x64, 0x65, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, + 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, + 0x3a, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, + 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, + 0x3a, 0x7b, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, + 0x22, 0x5d, 0x7d, 0x7d, 0x7d, 0x0a, 0x20, 0x20, 0x03, 0x00, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0xf8, 0xff, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0xf8, 0xff, 0xf8, 0xff, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x03, 0x00, 0xf8, 0xff, + 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x01, + 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x1e, 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x06, + 0x02, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x4e, 0x03, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x36, + 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x1e, + 0x03, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x4e, + ]), + }, + lineString: { + flags: IModelTileIO.Flags.None, + bytes: new Uint8Array([ + 0x69, 0x4d, 0x64, 0x6c, 0x03, 0x00, 0x01, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xf1, 0xd2, 0x4d, + 0x62, 0x00, 0x1e, 0xc0, 0xc7, 0x4b, 0x37, 0x89, 0x41, 0x00, 0x24, 0xc0, 0x00, 0x00, 0xbd, 0x9a, 0xf2, 0xd7, 0x5a, 0xbe, + 0x55, 0x0e, 0x2d, 0xb2, 0x9d, 0xff, 0x1d, 0x40, 0x39, 0xb4, 0xc8, 0x76, 0xbe, 0xff, 0x23, 0x40, 0x00, 0x00, 0xbd, 0x9a, + 0xf2, 0xd7, 0x5a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0x0a, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, + 0x02, 0x00, 0x00, 0x00, 0x2c, 0x0a, 0x00, 0x00, 0x58, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, + 0x78, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x37, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x30, 0x7d, 0x2c, + 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, + 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x34, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x37, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, + 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, + 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x34, 0x35, 0x36, 0x2c, 0x22, 0x62, + 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x37, 0x36, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, + 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, + 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x34, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x34, 0x31, 0x36, 0x7d, 0x7d, 0x2c, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4b, 0x48, 0x52, 0x5f, 0x62, 0x69, 0x6e, 0x61, + 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x57, 0x45, 0x42, 0x33, 0x44, 0x5f, 0x71, 0x75, 0x61, 0x6e, + 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, + 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, + 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, 0x69, + 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, + 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, + 0x72, 0x22, 0x3a, 0x36, 0x35, 0x35, 0x33, 0x35, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, + 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, + 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, + 0x35, 0x33, 0x35, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, + 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, + 0x31, 0x7d, 0x7d, 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, + 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, 0x3a, + 0x5b, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x30, 0x22, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, + 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, + 0x61, 0x6c, 0x30, 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x30, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, + 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, + 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x36, + 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x44, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x68, 0x61, 0x73, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, + 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, + 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x33, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, + 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x30, 0x2e, + 0x30, 0x30, 0x30, 0x32, 0x32, 0x38, 0x39, 0x31, 0x39, 0x36, 0x36, 0x31, 0x32, 0x34, 0x39, 0x37, 0x31, 0x33, 0x38, 0x37, + 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, + 0x30, 0x30, 0x33, 0x30, 0x35, 0x32, 0x32, 0x36, 0x32, 0x31, 0x34, 0x39, 0x39, 0x39, 0x36, 0x31, 0x38, 0x34, 0x34, 0x2c, + 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, + 0x36, 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, + 0x2e, 0x30, 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x2c, 0x31, 0x2e, 0x30, + 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x37, 0x2e, 0x35, 0x30, + 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x31, 0x30, 0x2e, 0x30, 0x30, + 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, + 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, + 0x3a, 0x5b, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x7d, 0x2c, 0x22, 0x75, + 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x35, 0x33, 0x35, 0x2c, 0x22, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x38, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x6e, 0x6f, 0x64, 0x65, + 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4e, 0x6f, 0x64, 0x65, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x22, 0x4d, 0x65, 0x73, + 0x68, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x22, 0x64, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x73, 0x22, + 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x6e, + 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x5d, 0x7d, 0x7d, + 0x7d, 0x0a, 0x20, 0x20, 0x03, 0x00, 0xf8, 0xff, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x55, 0x55, 0xf8, 0xff, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x03, + 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0d, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x90, + 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x60, + 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x90, + 0x02, 0x00, 0x00, 0x09, 0x02, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x60, + 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, + 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x39, + 0x03, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x39, 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x60, + 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x51, + 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0e, + 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x21, + 0x03, 0x00, 0x00, 0x39, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x90, + 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x90, + 0x03, 0x00, 0x00, 0x51, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x0c, 0x04, 0x00, 0x00, 0x60, + 0x04, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0f, 0x04, 0x00, 0x00, 0x0e, + 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0e, + 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x21, + 0x04, 0x00, 0x00, 0x33, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x33, 0x04, 0x00, 0x00, 0x90, + 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x09, 0x05, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x90, + 0x04, 0x00, 0x00, 0x4b, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x0c, 0x05, 0x00, 0x00, 0x60, + 0x05, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0f, 0x05, 0x00, 0x00, 0x0e, + ]), + }, + lineStrings: { + flags: IModelTileIO.Flags.None, + bytes: new Uint8Array([ + 0x69, 0x4d, 0x64, 0x6c, 0x03, 0x00, 0x01, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xf1, 0xd2, 0x4d, + 0x62, 0x00, 0x1e, 0xc0, 0xab, 0xf1, 0xd2, 0x4d, 0x62, 0x00, 0x3e, 0xc0, 0x00, 0x00, 0xbd, 0x9a, 0xf2, 0xd7, 0x5a, 0xbe, + 0x55, 0x0e, 0x2d, 0xb2, 0x9d, 0xff, 0x1d, 0x40, 0x55, 0x0e, 0x2d, 0xb2, 0x9d, 0xff, 0x3d, 0x40, 0x00, 0x00, 0xbd, 0x9a, + 0xf2, 0xd7, 0x5a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x68, 0x18, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x67, 0x6c, 0x54, 0x46, 0x02, 0x00, 0x00, 0x00, 0xdc, 0x17, 0x00, 0x00, 0x88, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x56, + 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, + 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x22, 0x3a, 0x37, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, + 0x3a, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, + 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x35, 0x32, 0x2c, 0x22, 0x62, 0x79, + 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x32, 0x31, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, + 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x34, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x22, 0x3a, 0x37, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x22, 0x3a, + 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, + 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x36, 0x38, 0x34, + 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x33, 0x36, 0x38, 0x7d, 0x2c, + 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, + 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x22, 0x3a, 0x34, 0x35, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, + 0x37, 0x36, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, + 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, + 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, + 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x31, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x22, 0x3a, 0x32, 0x37, 0x33, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, + 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x34, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x22, 0x3a, 0x34, 0x31, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x36, 0x38, 0x34, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x32, 0x30, + 0x35, 0x32, 0x7d, 0x7d, 0x2c, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, + 0x22, 0x3a, 0x5b, 0x22, 0x4b, 0x48, 0x52, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, + 0x2c, 0x22, 0x57, 0x45, 0x42, 0x33, 0x44, 0x5f, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, 0x6d, 0x61, + 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, + 0x22, 0x3a, 0x7b, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x31, 0x36, 0x37, 0x31, 0x31, + 0x39, 0x33, 0x35, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, + 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, + 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x31, 0x36, 0x37, 0x31, 0x31, 0x39, 0x33, + 0x35, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6c, 0x69, + 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x7d, + 0x2c, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, + 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x31, 0x35, 0x37, 0x39, 0x30, 0x30, 0x38, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, + 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, + 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, + 0x6f, 0x72, 0x22, 0x3a, 0x31, 0x35, 0x37, 0x39, 0x30, 0x30, 0x38, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, + 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x34, 0x31, 0x37, 0x37, 0x30, 0x36, 0x36, 0x32, 0x33, 0x32, 0x2c, 0x22, 0x6c, 0x69, + 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x7d, + 0x7d, 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, 0x6f, + 0x6f, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x7b, + 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x30, 0x22, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, + 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x30, 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x30, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, + 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, + 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x36, 0x2c, 0x22, + 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x44, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, + 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x33, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, + 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x30, 0x2e, 0x30, 0x30, + 0x30, 0x32, 0x32, 0x38, 0x39, 0x31, 0x39, 0x36, 0x36, 0x31, 0x32, 0x34, 0x39, 0x37, 0x31, 0x33, 0x38, 0x37, 0x2c, 0x30, + 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, + 0x39, 0x31, 0x35, 0x36, 0x37, 0x38, 0x36, 0x34, 0x34, 0x39, 0x39, 0x38, 0x38, 0x35, 0x35, 0x35, 0x30, 0x2c, 0x30, 0x2e, + 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, 0x31, + 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, 0x30, + 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, + 0x2c, 0x2d, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, + 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, + 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, + 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x34, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, + 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, + 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, + 0x2d, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, + 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x7d, 0x2c, 0x22, 0x75, 0x6e, 0x69, + 0x66, 0x6f, 0x72, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x31, 0x36, 0x37, 0x31, 0x31, 0x39, 0x33, 0x35, 0x2c, + 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x38, 0x7d, 0x7d, 0x2c, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x22, 0x2c, 0x22, 0x69, 0x73, + 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, + 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x31, 0x22, 0x2c, 0x22, 0x6e, 0x65, + 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, + 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x31, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, + 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x22, 0x2c, 0x22, 0x74, 0x79, + 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, + 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, + 0x31, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x31, 0x32, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x32, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, + 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x33, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x30, 0x2e, 0x30, + 0x30, 0x30, 0x32, 0x32, 0x38, 0x39, 0x31, 0x39, 0x36, 0x36, 0x31, 0x32, 0x34, 0x39, 0x37, 0x31, 0x33, 0x38, 0x37, 0x2c, + 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, + 0x30, 0x39, 0x31, 0x35, 0x36, 0x37, 0x38, 0x36, 0x34, 0x34, 0x39, 0x39, 0x38, 0x38, 0x35, 0x35, 0x35, 0x30, 0x2c, 0x30, + 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, + 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, + 0x30, 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x32, 0x2c, 0x2d, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x37, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, + 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x37, 0x2e, 0x35, 0x30, 0x31, + 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, + 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, + 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, + 0x5b, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, + 0x2c, 0x2d, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, + 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x7d, 0x2c, 0x22, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x38, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, + 0x3a, 0x7b, 0x22, 0x4e, 0x6f, 0x64, 0x65, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, + 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x22, 0x64, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, + 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x6e, 0x6f, 0x64, + 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x5d, 0x7d, 0x7d, 0x7d, 0x0a, + 0x03, 0x00, 0x55, 0x55, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0xac, 0x2a, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55, + 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf8, 0xff, 0xac, 0x2a, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x39, + 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, + 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0f, + 0x00, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x60, + 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x09, + 0x02, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0d, + 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x60, + 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x60, + 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x39, 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x90, + 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x51, 0x02, 0x00, 0x00, 0x90, + 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, + 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x21, 0x03, 0x00, 0x00, 0x39, + 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x60, + 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x51, + 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x0c, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0e, + 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0f, 0x04, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x90, + 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x0d, + 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x21, 0x04, 0x00, 0x00, 0x33, + 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x33, 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x60, + 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x09, 0x05, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x4b, + 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x0c, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0e, + 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0f, 0x05, 0x00, 0x00, 0x0e, 0x03, 0x00, 0xa7, 0xaa, + 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x55, 0x55, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x55, 0x55, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x55, 0x55, 0xa7, 0xaa, 0xfe, 0x7f, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x55, 0x55, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xfe, 0x7f, + 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0xf8, 0xff, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x03, 0x00, 0xa7, 0xaa, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x55, 0x55, 0x50, 0xd5, 0xfe, 0x7f, 0x01, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x55, 0x55, 0xf8, 0xff, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xa7, 0xaa, + 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x50, 0xd5, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, + 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x06, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, + 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x09, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1b, + 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x90, + 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x90, + 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x90, + 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, + 0x02, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x36, + 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x09, 0x02, 0x00, 0x00, 0x09, + 0x01, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, + 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0f, + 0x02, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x60, + 0x02, 0x00, 0x00, 0x39, 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x06, + 0x03, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x51, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0d, + 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x90, + 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x21, 0x03, 0x00, 0x00, 0x39, 0x04, 0x00, 0x00, 0x60, + 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x90, + 0x04, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x51, 0x04, 0x00, 0x00, 0x60, + 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x0c, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x0d, + 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0f, 0x04, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0d, + 0x03, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x90, + 0x03, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x21, 0x04, 0x00, 0x00, 0x33, 0x05, 0x00, 0x00, 0x60, + 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x33, 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x90, + 0x05, 0x00, 0x00, 0x09, 0x05, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x4b, 0x05, 0x00, 0x00, 0x60, + 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x0c, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x0d, + 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0f, 0x05, 0x00, 0x00, 0x0e, 0x07, 0x00, 0x00, 0x1b, 0x06, 0x00, 0x00, 0x39, + 0x07, 0x00, 0x00, 0x60, 0x07, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x39, 0x06, 0x00, 0x00, 0x90, 0x07, 0x00, 0x00, 0x60, + 0x06, 0x00, 0x00, 0x90, 0x07, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x51, + 0x06, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x0d, 0x06, 0x00, 0x00, 0x0c, 0x06, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x0e, + 0x06, 0x00, 0x00, 0x0d, 0x06, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x0f, 0x06, 0x00, 0x00, 0x0e, 0x08, 0x00, 0x00, 0x21, + 0x07, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x60, 0x07, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x90, + 0x08, 0x00, 0x00, 0x60, 0x07, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x09, 0x08, 0x00, 0x00, 0x09, 0x07, 0x00, 0x00, 0x90, + 0x07, 0x00, 0x00, 0x4e, 0x08, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x0d, 0x08, 0x00, 0x00, 0x0c, 0x08, 0x00, 0x00, 0x60, + 0x08, 0x00, 0x00, 0x0e, 0x08, 0x00, 0x00, 0x0d, 0x08, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x0f, 0x08, 0x00, 0x00, 0x0e, + 0x09, 0x00, 0x00, 0x1e, 0x08, 0x00, 0x00, 0x39, 0x09, 0x00, 0x00, 0x60, 0x09, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x39, + 0x08, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x06, 0x09, 0x00, 0x00, 0x06, + 0x08, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x51, 0x08, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x0d, 0x08, 0x00, 0x00, 0x0c, + 0x08, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x0e, 0x08, 0x00, 0x00, 0x0d, 0x08, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x0f, + 0x08, 0x00, 0x00, 0x0e, 0x0a, 0x00, 0x00, 0x21, 0x09, 0x00, 0x00, 0x39, 0x0a, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x60, + 0x09, 0x00, 0x00, 0x39, 0x09, 0x00, 0x00, 0x90, 0x0a, 0x00, 0x00, 0x60, 0x09, 0x00, 0x00, 0x90, 0x0a, 0x00, 0x00, 0x09, + 0x0a, 0x00, 0x00, 0x09, 0x09, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x51, 0x0a, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x0d, + 0x0a, 0x00, 0x00, 0x0c, 0x0a, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x0e, 0x0a, 0x00, 0x00, 0x0d, 0x0a, 0x00, 0x00, 0x60, + 0x0a, 0x00, 0x00, 0x0f, 0x0a, 0x00, 0x00, 0x0e, 0x09, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x0d, 0x09, 0x00, 0x00, 0x0c, + 0x09, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x0e, 0x09, 0x00, 0x00, 0x0d, 0x09, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x0f, + 0x09, 0x00, 0x00, 0x0e, 0x0b, 0x00, 0x00, 0x21, 0x0a, 0x00, 0x00, 0x33, 0x0b, 0x00, 0x00, 0x60, 0x0b, 0x00, 0x00, 0x60, + 0x0a, 0x00, 0x00, 0x33, 0x0a, 0x00, 0x00, 0x90, 0x0b, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x90, 0x0b, 0x00, 0x00, 0x09, + 0x0b, 0x00, 0x00, 0x09, 0x0a, 0x00, 0x00, 0x90, 0x0a, 0x00, 0x00, 0x4b, 0x0b, 0x00, 0x00, 0x60, 0x0b, 0x00, 0x00, 0x0d, + 0x0b, 0x00, 0x00, 0x0c, 0x0b, 0x00, 0x00, 0x60, 0x0b, 0x00, 0x00, 0x0e, 0x0b, 0x00, 0x00, 0x0d, 0x0b, 0x00, 0x00, 0x60, + 0x0b, 0x00, 0x00, 0x0f, 0x0b, 0x00, 0x00, 0x0e, + ]), + }, + triangles: { + flags: IModelTileIO.Flags.None, + bytes: new Uint8Array([ + 0x69, 0x4d, 0x64, 0x6c, 0x03, 0x00, 0x01, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xf1, 0xd2, 0x4d, + 0x62, 0x00, 0x1e, 0xc0, 0xc7, 0x4b, 0x37, 0x89, 0x41, 0x00, 0x24, 0xc0, 0x00, 0x00, 0xbd, 0x9a, 0xf2, 0xd7, 0x5a, 0xbe, + 0x55, 0x0e, 0x2d, 0xb2, 0x9d, 0xff, 0x1d, 0x40, 0x39, 0xb4, 0xc8, 0x76, 0xbe, 0xff, 0x23, 0x40, 0x00, 0x00, 0xbd, 0x9a, + 0xf2, 0xd7, 0x5a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x16, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x06, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x16, 0x00, 0x00, 0x54, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, + 0x78, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x31, 0x35, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x30, 0x7d, + 0x2c, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, + 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x35, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x31, 0x30, 0x38, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, + 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, + 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, + 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x31, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x22, 0x3a, 0x33, 0x34, 0x38, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, + 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x32, 0x31, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x34, + 0x35, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, + 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, + 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x22, 0x3a, 0x35, 0x36, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, + 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, + 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x22, 0x3a, 0x31, 0x38, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, + 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, + 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x37, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x22, 0x3a, 0x31, 0x35, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x50, 0x6f, + 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, + 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x36, 0x37, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, + 0x63, 0x65, 0x73, 0x31, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, + 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, + 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, + 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x32, 0x39, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, + 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, + 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x37, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x32, 0x36, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, + 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, + 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, + 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, + 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x31, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x38, 0x39, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x31, 0x50, 0x6f, 0x6c, 0x79, 0x6c, + 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, + 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x31, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x22, 0x3a, 0x32, 0x30, 0x30, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, + 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x32, + 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x37, 0x32, 0x38, 0x7d, 0x2c, 0x22, + 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, + 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, + 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x22, 0x3a, 0x31, 0x38, 0x33, 0x36, 0x7d, 0x7d, 0x2c, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, + 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4b, 0x48, 0x52, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, + 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x57, 0x45, 0x42, 0x33, 0x44, 0x5f, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, + 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, 0x67, 0x6c, 0x45, + 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4f, 0x45, 0x53, + 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x22, + 0x5d, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x61, 0x74, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, + 0x32, 0x35, 0x35, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, + 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, + 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x32, 0x35, 0x35, 0x2c, 0x22, 0x6c, 0x69, + 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, + 0x74, 0x68, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x7d, 0x2c, 0x22, 0x4d, 0x61, 0x74, + 0x65, 0x72, 0x69, 0x61, 0x6c, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, + 0x3a, 0x32, 0x31, 0x33, 0x30, 0x37, 0x30, 0x36, 0x36, 0x38, 0x37, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, + 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, + 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, + 0x3a, 0x32, 0x31, 0x33, 0x30, 0x37, 0x30, 0x36, 0x36, 0x38, 0x37, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, + 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, + 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x7d, 0x7d, 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, + 0x3a, 0x7b, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, + 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x65, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, + 0x70, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, + 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, + 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, + 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x65, 0x67, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, + 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, + 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, + 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, + 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x7d, 0x7d, + 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x22, 0x6d, 0x61, + 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x2c, + 0x22, 0x73, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, + 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, + 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x7d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x2c, + 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, + 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x39, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x32, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, + 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, + 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, + 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, + 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x32, 0x38, 0x39, 0x31, + 0x39, 0x36, 0x36, 0x31, 0x32, 0x34, 0x39, 0x37, 0x31, 0x33, 0x38, 0x37, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, + 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x33, 0x30, 0x35, 0x32, 0x32, 0x36, + 0x32, 0x31, 0x34, 0x39, 0x39, 0x39, 0x36, 0x31, 0x38, 0x34, 0x34, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, + 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, + 0x39, 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, + 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, + 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, + 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, + 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, + 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, + 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, + 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, + 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x7d, 0x2c, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x39, + 0x7d, 0x7d, 0x2c, 0x7b, 0x22, 0x65, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x6f, 0x6c, 0x79, 0x6c, 0x69, + 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, + 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, + 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x31, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, + 0x65, 0x73, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, + 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, + 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, + 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x31, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x7d, 0x7d, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, + 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x31, 0x22, 0x2c, 0x22, 0x73, 0x75, 0x72, 0x66, 0x61, + 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, + 0x22, 0x3a, 0x31, 0x7d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, + 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, + 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x31, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x39, + 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, + 0x32, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, + 0x74, 0x72, 0x75, 0x65, 0x2c, 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, + 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x70, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, + 0x22, 0x3a, 0x5b, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x32, 0x38, 0x39, 0x31, 0x39, 0x36, 0x36, 0x31, 0x32, 0x34, 0x39, + 0x37, 0x31, 0x33, 0x38, 0x37, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, + 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x33, 0x30, 0x35, 0x32, 0x32, 0x36, 0x32, 0x31, 0x34, 0x39, 0x39, 0x39, 0x36, + 0x31, 0x38, 0x34, 0x34, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, + 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, + 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, + 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, + 0x5b, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, + 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x30, + 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, + 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, + 0x5d, 0x7d, 0x2c, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x39, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, + 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4e, 0x6f, 0x64, 0x65, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, + 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, + 0x3a, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, + 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, + 0x3a, 0x7b, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, + 0x22, 0x5d, 0x7d, 0x7d, 0x7d, 0x0a, 0x20, 0x20, 0xa7, 0xaa, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xa7, 0xaa, 0xf8, 0xff, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0xf8, 0xff, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0xfe, 0x7f, + 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0xf8, 0xff, 0xfe, 0x7f, 0x01, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xa7, 0xaa, 0xfe, 0x7f, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, 0xfe, 0x7f, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0x03, 0x00, 0xf8, 0xff, 0xfe, 0x7f, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0xfe, 0x7f, + 0xfe, 0x7f, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x01, + 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, + 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x03, + 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x02, + 0x06, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x01, + 0x06, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, + 0x08, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x36, + 0x00, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x1e, 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x06, + 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x4e, 0x04, 0x00, 0x00, 0x1e, 0x03, 0x00, 0x00, 0x36, + 0x04, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x4e, 0x05, 0x00, 0x00, 0x1e, + 0x04, 0x00, 0x00, 0x36, 0x05, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x4e, + 0x03, 0x00, 0x00, 0x1e, 0x05, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x36, + 0x05, 0x00, 0x00, 0x4e, 0x07, 0x00, 0x00, 0x1e, 0x06, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x06, 0x07, 0x00, 0x00, 0x06, + 0x06, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x4e, 0x08, 0x00, 0x00, 0x1e, 0x07, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x06, + 0x08, 0x00, 0x00, 0x06, 0x07, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x4e, 0x06, 0x00, 0x00, 0x1e, 0x08, 0x00, 0x00, 0x36, + 0x06, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x06, 0x08, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x4e, 0xa7, 0xaa, 0x03, 0x00, + 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xa7, 0xaa, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xf8, 0xff, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0x03, 0x00, 0xfe, 0x7f, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0x55, 0x55, 0xfe, 0x7f, 0xfe, 0x7f, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xa7, 0xaa, 0x03, 0x00, + 0xfe, 0x7f, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x03, 0x00, 0x03, 0x00, 0xfe, 0x7f, 0x02, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, 0xfe, 0x7f, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0x03, 0x00, 0xfe, 0x7f, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0x00, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, + 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x02, + 0x03, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, + 0x04, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x01, + 0x07, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, + 0x08, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x06, + 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x1e, 0x01, 0x00, 0x00, 0x36, + 0x02, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x1e, + 0x02, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x4e, + 0x04, 0x00, 0x00, 0x1e, 0x03, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x36, + 0x03, 0x00, 0x00, 0x4e, 0x05, 0x00, 0x00, 0x1e, 0x04, 0x00, 0x00, 0x36, 0x05, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x06, + 0x04, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x4e, 0x03, 0x00, 0x00, 0x1e, 0x05, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x06, + 0x03, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x36, 0x05, 0x00, 0x00, 0x4e, 0x07, 0x00, 0x00, 0x1e, 0x06, 0x00, 0x00, 0x36, + 0x07, 0x00, 0x00, 0x06, 0x07, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x4e, 0x08, 0x00, 0x00, 0x1e, + 0x07, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x06, 0x08, 0x00, 0x00, 0x06, 0x07, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x4e, + 0x06, 0x00, 0x00, 0x1e, 0x08, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x06, 0x08, 0x00, 0x00, 0x36, + 0x08, 0x00, 0x00, 0x4e, + ]), + }, + cylinder: { + flags: IModelTileIO.Flags.ContainsCurves, + bytes: new Uint8Array([ + 0x69, 0x4d, 0x64, 0x6c, 0x03, 0x00, 0x01, 0x00, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x39, 0xd6, 0xc5, 0x6d, + 0x34, 0x00, 0x00, 0xc0, 0x39, 0xd6, 0xc5, 0x6d, 0x34, 0x00, 0x00, 0xc0, 0x56, 0xc1, 0xa8, 0xa4, 0x4e, 0x00, 0x08, 0xc0, + 0x8e, 0x53, 0x74, 0x24, 0x97, 0xff, 0xff, 0x3f, 0x8e, 0x53, 0x74, 0x24, 0x97, 0xff, 0xff, 0x3f, 0xaa, 0x3e, 0x57, 0x5b, + 0xb1, 0xff, 0x07, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xd8, 0x3d, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, + 0x02, 0x00, 0x00, 0x00, 0x64, 0x3d, 0x00, 0x00, 0x28, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, + 0x78, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x32, 0x33, 0x33, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x30, + 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, + 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, + 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, + 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x37, 0x32, 0x38, 0x2c, + 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x34, 0x39, 0x32, 0x38, 0x7d, 0x2c, 0x22, + 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, + 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, + 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x38, 0x36, 0x34, 0x2c, 0x22, + 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x37, 0x33, 0x30, 0x34, 0x7d, 0x2c, 0x22, 0x62, + 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, + 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x31, 0x32, 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x39, + 0x30, 0x33, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, + 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x22, 0x3a, 0x31, 0x32, 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, + 0x3a, 0x33, 0x36, 0x33, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x69, + 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, + 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, + 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x36, 0x34, 0x38, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x22, 0x3a, 0x36, 0x36, 0x35, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, + 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, + 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x32, 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x32, 0x33, 0x33, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, + 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, + 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, + 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x37, 0x32, 0x38, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x31, 0x36, 0x32, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x6f, 0x72, 0x6d, + 0x61, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, + 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, + 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x38, 0x36, + 0x34, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x38, 0x31, 0x36, 0x38, 0x7d, + 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, + 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, + 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, + 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x32, 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x30, 0x33, 0x32, 0x38, 0x7d, 0x7d, 0x2c, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4b, 0x48, 0x52, 0x5f, 0x62, 0x69, 0x6e, + 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x57, 0x45, 0x42, 0x33, 0x44, 0x5f, 0x71, 0x75, 0x61, + 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x5d, 0x2c, + 0x22, 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, + 0x22, 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, + 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x3a, 0x7b, 0x22, + 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, + 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, + 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, + 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, + 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, + 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, + 0x3a, 0x30, 0x7d, 0x7d, 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x65, 0x73, 0x68, + 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, + 0x3a, 0x5b, 0x7b, 0x22, 0x65, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, + 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, + 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, + 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, + 0x73, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, + 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, + 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, + 0x22, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, + 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, + 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, + 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x69, 0x6c, 0x68, 0x6f, 0x75, + 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, + 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, + 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, + 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, + 0x74, 0x74, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x22, 0x3a, + 0x22, 0x62, 0x76, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, + 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x7d, 0x7d, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, + 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, + 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x2c, 0x22, 0x73, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, + 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x7d, + 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x22, + 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x56, 0x65, + 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x31, 0x34, 0x36, 0x2c, 0x22, + 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x44, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, + 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, + 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x36, 0x2e, 0x31, 0x30, + 0x34, 0x35, 0x32, 0x34, 0x32, 0x39, 0x39, 0x39, 0x39, 0x32, 0x33, 0x37, 0x30, 0x30, 0x65, 0x2d, 0x30, 0x35, 0x2c, 0x30, + 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x36, 0x2e, 0x31, 0x30, 0x34, + 0x35, 0x32, 0x34, 0x32, 0x39, 0x39, 0x39, 0x39, 0x32, 0x33, 0x37, 0x30, 0x30, 0x65, 0x2d, 0x30, 0x35, 0x2c, 0x30, 0x2e, + 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x39, 0x2e, 0x31, 0x35, 0x36, 0x37, + 0x38, 0x36, 0x34, 0x34, 0x39, 0x39, 0x38, 0x38, 0x35, 0x35, 0x35, 0x30, 0x65, 0x2d, 0x30, 0x35, 0x2c, 0x30, 0x2e, 0x30, + 0x2c, 0x2d, 0x32, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, + 0x2c, 0x2d, 0x32, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, + 0x2c, 0x2d, 0x33, 0x2e, 0x30, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, + 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, + 0x32, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x32, + 0x2e, 0x30, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x33, 0x2e, + 0x30, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x5d, 0x2c, 0x22, 0x64, + 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x32, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x2d, 0x32, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x2d, 0x33, 0x2e, 0x30, 0x30, 0x30, 0x34, 0x34, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x5d, 0x7d, 0x2c, 0x22, 0x75, 0x6e, 0x69, 0x66, 0x6f, + 0x72, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x77, 0x69, 0x64, 0x74, + 0x68, 0x22, 0x3a, 0x35, 0x38, 0x34, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x4e, 0x6f, 0x64, 0x65, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, + 0x6f, 0x6f, 0x74, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, + 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x6e, 0x6f, 0x64, 0x65, + 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x5d, 0x7d, 0x7d, 0x7d, 0x0a, 0x20, + 0xf8, 0xff, 0xfe, 0x7f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xfe, 0x7f, 0xfe, 0x7f, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x07, 0xfe, 0xc5, 0x69, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x41, 0xf8, 0x38, 0x54, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0xd3, 0xee, 0x01, 0x40, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0x07, 0xe2, 0xba, 0x2d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x41, 0xd2, 0xf4, 0x1d, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xfb, 0xbf, 0x29, 0x11, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xc3, 0xab, 0xbb, 0x07, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0x37, 0x96, 0xf5, 0x01, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0xfe, 0x7f, 0x03, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xc5, 0x69, 0xf5, 0x01, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x38, 0x54, 0xbb, 0x07, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x01, 0x40, 0x29, 0x11, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0xba, 0x2d, 0xf4, 0x1d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0xf4, 0x1d, 0xba, 0x2d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x29, 0x11, 0x01, 0x40, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xbb, 0x07, 0x38, 0x54, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xf5, 0x01, 0xc5, 0x69, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0xf5, 0x01, 0x37, 0x96, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xbb, 0x07, 0xc3, 0xab, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x29, 0x11, 0xfb, 0xbf, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xf4, 0x1d, 0x41, 0xd2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0xba, 0x2d, 0x07, 0xe2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0x01, 0x40, 0xd3, 0xee, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x38, 0x54, 0x41, 0xf8, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xc5, 0x69, 0x07, 0xfe, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xfe, 0x7f, 0xf8, 0xff, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0x37, 0x96, 0x07, 0xfe, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0xc3, 0xab, 0x41, 0xf8, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xfb, 0xbf, 0xd3, 0xee, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x41, 0xd2, 0x07, 0xe2, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x07, 0xe2, 0x41, 0xd2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0xd3, 0xee, 0xfb, 0xbf, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0x41, 0xf8, 0xc3, 0xab, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x07, 0xfe, 0x37, 0x96, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xf8, 0xff, 0xfe, 0x7f, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xfe, 0x7f, 0xfe, 0x7f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0x07, 0xfe, 0x37, 0x96, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0x41, 0xf8, 0xc3, 0xab, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xd3, 0xee, 0xfb, 0xbf, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x07, 0xe2, 0x41, 0xd2, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x41, 0xd2, 0x07, 0xe2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xfb, 0xbf, 0xd3, 0xee, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0xc3, 0xab, 0x41, 0xf8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x37, 0x96, 0x07, 0xfe, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xfe, 0x7f, 0xf8, 0xff, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xc5, 0x69, 0x07, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0x38, 0x54, 0x41, 0xf8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0x01, 0x40, 0xd3, 0xee, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xba, 0x2d, 0x07, 0xe2, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xf4, 0x1d, 0x41, 0xd2, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x29, 0x11, 0xfb, 0xbf, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xbb, 0x07, 0xc3, 0xab, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0xf5, 0x01, 0x37, 0x96, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xf5, 0x01, 0xc5, 0x69, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xbb, 0x07, 0x38, 0x54, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0x29, 0x11, 0x01, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0xf4, 0x1d, 0xba, 0x2d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xba, 0x2d, 0xf4, 0x1d, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x01, 0x40, 0x29, 0x11, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x38, 0x54, 0xbb, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xc5, 0x69, 0xf5, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0xfe, 0x7f, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x37, 0x96, 0xf5, 0x01, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xc3, 0xab, 0xbb, 0x07, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xfb, 0xbf, 0x29, 0x11, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0x41, 0xd2, 0xf4, 0x1d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0x07, 0xe2, 0xba, 0x2d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xd3, 0xee, 0x01, 0x40, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x41, 0xf8, 0x38, 0x54, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x07, 0xfe, 0xc5, 0x69, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xf8, 0xff, 0xfe, 0x7f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x80, 0xcc, 0xcc, + 0xf8, 0xff, 0xfe, 0x7f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x80, 0xcc, 0xcc, 0x07, 0xfe, 0x37, 0x96, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x93, 0xcc, 0xcc, 0x07, 0xfe, 0x37, 0x96, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xec, 0x93, 0xcc, 0xcc, 0x41, 0xf8, 0xc3, 0xab, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0xa2, 0xcc, 0xcc, 0x41, 0xf8, 0xc3, 0xab, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0xa2, 0xcc, 0xcc, + 0xd3, 0xee, 0xfb, 0xbf, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xae, 0xcc, 0xcc, 0xd3, 0xee, 0xfb, 0xbf, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xae, 0xcc, 0xcc, 0x07, 0xe2, 0x41, 0xd2, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc5, 0xba, 0xcc, 0xcc, 0x07, 0xe2, 0x41, 0xd2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc5, 0xba, 0xcc, 0xcc, 0x41, 0xd2, 0x07, 0xe2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xc5, 0xcc, 0xcc, + 0x41, 0xd2, 0x07, 0xe2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xc5, 0xcc, 0xcc, 0xfb, 0xbf, 0xd3, 0xee, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0xd0, 0xcc, 0xcc, 0xfb, 0xbf, 0xd3, 0xee, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xae, 0xd0, 0xcc, 0xcc, 0xc3, 0xab, 0x41, 0xf8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa2, 0xdd, 0xcc, 0xcc, 0xc3, 0xab, 0x41, 0xf8, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0xdd, 0xcc, 0xcc, + 0x37, 0x96, 0x07, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0xec, 0xcc, 0xcc, 0x37, 0x96, 0x07, 0xfe, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0xec, 0xcc, 0xcc, 0xfe, 0x7f, 0xf8, 0xff, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xcc, 0xcc, 0xfe, 0x7f, 0xf8, 0xff, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xff, 0xcc, 0xcc, 0xc5, 0x69, 0x07, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xec, 0xcc, 0xcc, + 0xc5, 0x69, 0x07, 0xfe, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xec, 0xcc, 0xcc, 0x38, 0x54, 0x41, 0xf8, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xdd, 0xcc, 0xcc, 0x38, 0x54, 0x41, 0xf8, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5d, 0xdd, 0xcc, 0xcc, 0x01, 0x40, 0xd3, 0xee, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0xd0, 0xcc, 0xcc, 0x01, 0x40, 0xd3, 0xee, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0xd0, 0xcc, 0xcc, + 0xba, 0x2d, 0x07, 0xe2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xc5, 0xcc, 0xcc, 0xba, 0x2d, 0x07, 0xe2, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xc5, 0xcc, 0xcc, 0xf4, 0x1d, 0x41, 0xd2, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3a, 0xba, 0xcc, 0xcc, 0xf4, 0x1d, 0x41, 0xd2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0xba, 0xcc, 0xcc, 0x29, 0x11, 0xfb, 0xbf, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0xae, 0xcc, 0xcc, + 0x29, 0x11, 0xfb, 0xbf, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0xae, 0xcc, 0xcc, 0xbb, 0x07, 0xc3, 0xab, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xa2, 0xcc, 0xcc, 0xbb, 0x07, 0xc3, 0xab, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0xa2, 0xcc, 0xcc, 0xf5, 0x01, 0x37, 0x96, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x13, 0x93, 0xcc, 0xcc, 0xf5, 0x01, 0x37, 0x96, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x93, 0xcc, 0xcc, + 0x03, 0x00, 0xfe, 0x7f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcc, 0xcc, 0xf5, 0x01, 0xc5, 0x69, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x6c, 0xcc, 0xcc, 0xf5, 0x01, 0xc5, 0x69, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x13, 0x6c, 0xcc, 0xcc, 0xbb, 0x07, 0x38, 0x54, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x5d, 0xcc, 0xcc, + 0xbb, 0x07, 0x38, 0x54, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x5d, 0xcc, 0xcc, 0x29, 0x11, 0x01, 0x40, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x51, 0xcc, 0xcc, 0x29, 0x11, 0x01, 0x40, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2f, 0x51, 0xcc, 0xcc, 0xf4, 0x1d, 0xba, 0x2d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x45, 0xcc, 0xcc, 0xf4, 0x1d, 0xba, 0x2d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x45, 0xcc, 0xcc, + 0xba, 0x2d, 0xf4, 0x1d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x3a, 0xcc, 0xcc, 0xba, 0x2d, 0xf4, 0x1d, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x3a, 0xcc, 0xcc, 0x01, 0x40, 0x29, 0x11, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x2f, 0xcc, 0xcc, 0x01, 0x40, 0x29, 0x11, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x2f, 0xcc, 0xcc, 0x38, 0x54, 0xbb, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x22, 0xcc, 0xcc, + 0x38, 0x54, 0xbb, 0x07, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x22, 0xcc, 0xcc, 0xc5, 0x69, 0xf5, 0x01, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x13, 0xcc, 0xcc, 0xc5, 0x69, 0xf5, 0x01, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0x13, 0xcc, 0xcc, 0xfe, 0x7f, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0xcc, 0xcc, 0xfe, 0x7f, 0x03, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0xcc, 0xcc, + 0x37, 0x96, 0xf5, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x13, 0xcc, 0xcc, 0x37, 0x96, 0xf5, 0x01, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x13, 0xcc, 0xcc, 0xc3, 0xab, 0xbb, 0x07, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa2, 0x22, 0xcc, 0xcc, 0xc3, 0xab, 0xbb, 0x07, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa2, 0x22, 0xcc, 0xcc, 0xfb, 0xbf, 0x29, 0x11, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0x2f, 0xcc, 0xcc, + 0xfb, 0xbf, 0x29, 0x11, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0x2f, 0xcc, 0xcc, 0x41, 0xd2, 0xf4, 0x1d, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x3a, 0xcc, 0xcc, 0x41, 0xd2, 0xf4, 0x1d, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xba, 0x3a, 0xcc, 0xcc, 0x07, 0xe2, 0xba, 0x2d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc5, 0x45, 0xcc, 0xcc, 0x07, 0xe2, 0xba, 0x2d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0x45, 0xcc, 0xcc, + 0xd3, 0xee, 0x01, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x51, 0xcc, 0xcc, 0xd3, 0xee, 0x01, 0x40, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x51, 0xcc, 0xcc, 0x41, 0xf8, 0x38, 0x54, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdd, 0x5d, 0xcc, 0xcc, 0x41, 0xf8, 0x38, 0x54, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x5d, 0xcc, 0xcc, 0x07, 0xfe, 0xc5, 0x69, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x6c, 0xcc, 0xcc, + 0x07, 0xfe, 0xc5, 0x69, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x6c, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, + 0x00, 0x00, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x11, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x01, 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1a, 0x00, + 0x00, 0x1a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x01, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x01, 0x00, 0x00, 0x23, + 0x00, 0x00, 0x23, 0x00, 0x00, 0x01, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x26, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x26, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x26, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x26, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x26, + 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x26, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x26, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x26, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x26, 0x00, 0x00, 0x2f, 0x00, + 0x00, 0x2f, 0x00, 0x00, 0x26, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x26, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, + 0x00, 0x00, 0x26, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x26, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x26, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x26, 0x00, + 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x26, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x26, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x26, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3a, 0x00, 0x00, + 0x3a, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, + 0x00, 0x26, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x26, + 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x26, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x26, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x26, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x26, 0x00, 0x00, 0x43, 0x00, + 0x00, 0x43, 0x00, 0x00, 0x26, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x26, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, + 0x00, 0x00, 0x26, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x26, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x26, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x26, 0x00, + 0x00, 0x25, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4d, + 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x4f, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x50, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x50, 0x00, 0x00, 0x51, 0x00, 0x00, 0x51, 0x00, + 0x00, 0x50, 0x00, 0x00, 0x52, 0x00, 0x00, 0x51, 0x00, 0x00, 0x52, 0x00, 0x00, 0x53, 0x00, 0x00, 0x53, 0x00, 0x00, 0x52, + 0x00, 0x00, 0x54, 0x00, 0x00, 0x53, 0x00, 0x00, 0x54, 0x00, 0x00, 0x55, 0x00, 0x00, 0x55, 0x00, 0x00, 0x54, 0x00, 0x00, + 0x56, 0x00, 0x00, 0x55, 0x00, 0x00, 0x56, 0x00, 0x00, 0x57, 0x00, 0x00, 0x57, 0x00, 0x00, 0x56, 0x00, 0x00, 0x58, 0x00, + 0x00, 0x57, 0x00, 0x00, 0x58, 0x00, 0x00, 0x59, 0x00, 0x00, 0x59, 0x00, 0x00, 0x58, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x59, + 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5b, 0x00, 0x00, + 0x5c, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x5e, 0x00, + 0x00, 0x5f, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x60, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x60, 0x00, 0x00, 0x61, + 0x00, 0x00, 0x61, 0x00, 0x00, 0x60, 0x00, 0x00, 0x62, 0x00, 0x00, 0x61, 0x00, 0x00, 0x62, 0x00, 0x00, 0x63, 0x00, 0x00, + 0x63, 0x00, 0x00, 0x62, 0x00, 0x00, 0x64, 0x00, 0x00, 0x63, 0x00, 0x00, 0x64, 0x00, 0x00, 0x65, 0x00, 0x00, 0x65, 0x00, + 0x00, 0x64, 0x00, 0x00, 0x66, 0x00, 0x00, 0x65, 0x00, 0x00, 0x66, 0x00, 0x00, 0x67, 0x00, 0x00, 0x67, 0x00, 0x00, 0x66, + 0x00, 0x00, 0x68, 0x00, 0x00, 0x67, 0x00, 0x00, 0x68, 0x00, 0x00, 0x69, 0x00, 0x00, 0x69, 0x00, 0x00, 0x68, 0x00, 0x00, + 0x6a, 0x00, 0x00, 0x69, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x6c, 0x00, + 0x00, 0x6b, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x6d, + 0x00, 0x00, 0x6e, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x70, 0x00, 0x00, 0x6f, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x71, 0x00, 0x00, 0x71, 0x00, 0x00, 0x70, 0x00, 0x00, 0x72, 0x00, 0x00, 0x71, 0x00, 0x00, 0x72, 0x00, + 0x00, 0x73, 0x00, 0x00, 0x73, 0x00, 0x00, 0x72, 0x00, 0x00, 0x74, 0x00, 0x00, 0x73, 0x00, 0x00, 0x74, 0x00, 0x00, 0x75, + 0x00, 0x00, 0x75, 0x00, 0x00, 0x74, 0x00, 0x00, 0x76, 0x00, 0x00, 0x75, 0x00, 0x00, 0x76, 0x00, 0x00, 0x77, 0x00, 0x00, + 0x77, 0x00, 0x00, 0x76, 0x00, 0x00, 0x78, 0x00, 0x00, 0x77, 0x00, 0x00, 0x78, 0x00, 0x00, 0x79, 0x00, 0x00, 0x79, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x79, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7a, + 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7c, 0x00, 0x00, + 0x7e, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x7f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, 0x00, 0x80, 0x00, 0x00, 0x82, 0x00, 0x00, 0x81, + 0x00, 0x00, 0x82, 0x00, 0x00, 0x83, 0x00, 0x00, 0x83, 0x00, 0x00, 0x82, 0x00, 0x00, 0x84, 0x00, 0x00, 0x83, 0x00, 0x00, + 0x84, 0x00, 0x00, 0x85, 0x00, 0x00, 0x85, 0x00, 0x00, 0x84, 0x00, 0x00, 0x86, 0x00, 0x00, 0x85, 0x00, 0x00, 0x86, 0x00, + 0x00, 0x87, 0x00, 0x00, 0x87, 0x00, 0x00, 0x86, 0x00, 0x00, 0x88, 0x00, 0x00, 0x87, 0x00, 0x00, 0x88, 0x00, 0x00, 0x89, + 0x00, 0x00, 0x89, 0x00, 0x00, 0x88, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x89, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x8b, 0x00, 0x00, + 0x8b, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x8d, 0x00, + 0x00, 0x8c, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x8e, + 0x00, 0x00, 0x90, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x90, 0x00, 0x00, 0x91, 0x00, 0x00, 0x91, 0x00, 0x00, 0x90, 0x00, 0x00, + 0x4b, 0x00, 0x00, 0x91, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, + 0x00, 0x23, 0x00, 0x00, 0x21, 0x00, 0x00, 0x22, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x21, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1e, 0x00, + 0x00, 0x1f, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1d, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1b, 0x00, + 0x00, 0x1b, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, + 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x19, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, + 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x18, 0x00, 0x00, 0x19, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x19, 0x00, + 0x00, 0x19, 0x00, 0x00, 0x17, 0x00, 0x00, 0x18, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x16, 0x00, 0x00, 0x17, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x16, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x14, 0x00, + 0x00, 0x15, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x13, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x12, 0x00, 0x00, 0x13, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x11, 0x00, 0x00, 0x12, 0x00, 0x00, 0x11, 0x00, + 0x00, 0x11, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x10, 0x00, 0x00, 0x11, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0f, 0x00, + 0x00, 0x0f, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, + 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x27, 0x00, 0x00, 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x25, 0x00, + 0x00, 0x49, 0x00, 0x00, 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28, + 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x29, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x29, 0x00, + 0x00, 0x29, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, + 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2d, 0x00, + 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, + 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, + 0x2f, 0x00, 0x00, 0x30, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x31, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x32, + 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x33, 0x00, 0x00, + 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x34, 0x00, 0x00, 0x33, 0x00, + 0x00, 0x33, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, + 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x36, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x37, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x37, 0x00, + 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, + 0x39, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, + 0x00, 0x3b, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3c, + 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3d, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3d, 0x00, + 0x00, 0x3d, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, + 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x41, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x42, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, + 0x00, 0x00, 0x42, 0x00, 0x00, 0x43, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, + 0x43, 0x00, 0x00, 0x44, 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, + 0x00, 0x45, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x46, + 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x47, 0x00, 0x00, + 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x48, 0x00, 0x00, 0x47, 0x00, + 0x00, 0x47, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x49, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, + 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x01, + 0x24, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x24, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x01, 0x24, 0x00, 0x00, 0x01, 0x23, 0x00, 0x00, 0x02, 0x23, 0x00, 0x00, 0x03, + 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x02, 0x23, 0x00, 0x00, 0x01, 0x23, 0x00, 0x00, 0x01, 0x22, 0x00, 0x00, 0x02, + 0x22, 0x00, 0x00, 0x03, 0x22, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x02, 0x22, 0x00, 0x00, 0x01, 0x22, 0x00, 0x00, 0x01, + 0x21, 0x00, 0x00, 0x02, 0x21, 0x00, 0x00, 0x03, 0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x02, 0x21, 0x00, 0x00, 0x01, + 0x21, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x02, + 0x20, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x01, 0x1f, 0x00, 0x00, 0x02, 0x1f, 0x00, 0x00, 0x03, 0x1f, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x02, 0x1f, 0x00, 0x00, 0x01, 0x1f, 0x00, 0x00, 0x01, 0x1e, 0x00, 0x00, 0x02, 0x1e, 0x00, 0x00, 0x03, + 0x1e, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x02, 0x1e, 0x00, 0x00, 0x01, 0x1e, 0x00, 0x00, 0x01, 0x1d, 0x00, 0x00, 0x02, + 0x1d, 0x00, 0x00, 0x03, 0x1d, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x02, 0x1d, 0x00, 0x00, 0x01, 0x1d, 0x00, 0x00, 0x01, + 0x1c, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x03, 0x1c, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x01, + 0x1c, 0x00, 0x00, 0x01, 0x1b, 0x00, 0x00, 0x02, 0x1b, 0x00, 0x00, 0x03, 0x1b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x02, + 0x1b, 0x00, 0x00, 0x01, 0x1b, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x00, 0x02, 0x1a, 0x00, 0x00, 0x03, 0x1a, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x02, 0x1a, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x00, 0x01, 0x19, 0x00, 0x00, 0x02, 0x19, 0x00, 0x00, 0x03, + 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x02, 0x19, 0x00, 0x00, 0x01, 0x19, 0x00, 0x00, 0x01, 0x18, 0x00, 0x00, 0x02, + 0x18, 0x00, 0x00, 0x03, 0x18, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x01, 0x18, 0x00, 0x00, 0x01, + 0x17, 0x00, 0x00, 0x02, 0x17, 0x00, 0x00, 0x03, 0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x02, 0x17, 0x00, 0x00, 0x01, + 0x17, 0x00, 0x00, 0x01, 0x16, 0x00, 0x00, 0x02, 0x16, 0x00, 0x00, 0x03, 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x02, + 0x16, 0x00, 0x00, 0x01, 0x16, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00, 0x02, 0x15, 0x00, 0x00, 0x03, 0x15, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x02, 0x15, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x03, + 0x14, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x01, 0x13, 0x00, 0x00, 0x02, + 0x13, 0x00, 0x00, 0x03, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x02, 0x13, 0x00, 0x00, 0x01, 0x13, 0x00, 0x00, 0x01, + 0x12, 0x00, 0x00, 0x02, 0x12, 0x00, 0x00, 0x03, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x02, 0x12, 0x00, 0x00, 0x01, + 0x12, 0x00, 0x00, 0x01, 0x11, 0x00, 0x00, 0x02, 0x11, 0x00, 0x00, 0x03, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x02, + 0x11, 0x00, 0x00, 0x01, 0x11, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x01, 0x0f, 0x00, 0x00, 0x02, 0x0f, 0x00, 0x00, 0x03, + 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x02, 0x0f, 0x00, 0x00, 0x01, 0x0f, 0x00, 0x00, 0x01, 0x0e, 0x00, 0x00, 0x02, + 0x0e, 0x00, 0x00, 0x03, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x02, 0x0e, 0x00, 0x00, 0x01, 0x0e, 0x00, 0x00, 0x01, + 0x0d, 0x00, 0x00, 0x02, 0x0d, 0x00, 0x00, 0x03, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x02, 0x0d, 0x00, 0x00, 0x01, + 0x0d, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x03, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x02, + 0x0c, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x00, 0x03, 0x0b, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x03, + 0x0a, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x00, 0x01, 0x09, 0x00, 0x00, 0x02, + 0x09, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x02, 0x09, 0x00, 0x00, 0x01, 0x09, 0x00, 0x00, 0x01, + 0x08, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, + 0x08, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, + 0x07, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x03, + 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x02, + 0x04, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x01, + 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x01, + 0x03, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x03, 0x27, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x02, + 0x27, 0x00, 0x00, 0x01, 0x27, 0x00, 0x00, 0x01, 0x25, 0x00, 0x00, 0x02, 0x25, 0x00, 0x00, 0x03, 0x49, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x02, 0x49, 0x00, 0x00, 0x01, 0x49, 0x00, 0x00, 0x01, 0x25, 0x00, 0x00, 0x02, 0x25, 0x00, 0x00, 0x03, + 0x28, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x01, 0x28, 0x00, 0x00, 0x01, 0x27, 0x00, 0x00, 0x02, + 0x27, 0x00, 0x00, 0x03, 0x29, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x02, 0x29, 0x00, 0x00, 0x01, 0x29, 0x00, 0x00, 0x01, + 0x28, 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x03, 0x2a, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x02, 0x2a, 0x00, 0x00, 0x01, + 0x2a, 0x00, 0x00, 0x01, 0x29, 0x00, 0x00, 0x02, 0x29, 0x00, 0x00, 0x03, 0x2b, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x02, + 0x2b, 0x00, 0x00, 0x01, 0x2b, 0x00, 0x00, 0x01, 0x2a, 0x00, 0x00, 0x02, 0x2a, 0x00, 0x00, 0x03, 0x2c, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x00, 0x01, 0x2b, 0x00, 0x00, 0x02, 0x2b, 0x00, 0x00, 0x03, + 0x2d, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x02, 0x2d, 0x00, 0x00, 0x01, 0x2d, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x00, 0x02, + 0x2c, 0x00, 0x00, 0x03, 0x2e, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x02, 0x2e, 0x00, 0x00, 0x01, 0x2e, 0x00, 0x00, 0x01, + 0x2d, 0x00, 0x00, 0x02, 0x2d, 0x00, 0x00, 0x03, 0x2f, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x02, 0x2f, 0x00, 0x00, 0x01, + 0x2f, 0x00, 0x00, 0x01, 0x2e, 0x00, 0x00, 0x02, 0x2e, 0x00, 0x00, 0x03, 0x30, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x02, + 0x30, 0x00, 0x00, 0x01, 0x30, 0x00, 0x00, 0x01, 0x2f, 0x00, 0x00, 0x02, 0x2f, 0x00, 0x00, 0x03, 0x31, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x02, 0x31, 0x00, 0x00, 0x01, 0x31, 0x00, 0x00, 0x01, 0x30, 0x00, 0x00, 0x02, 0x30, 0x00, 0x00, 0x03, + 0x32, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x02, 0x32, 0x00, 0x00, 0x01, 0x32, 0x00, 0x00, 0x01, 0x31, 0x00, 0x00, 0x02, + 0x31, 0x00, 0x00, 0x03, 0x33, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x02, 0x33, 0x00, 0x00, 0x01, 0x33, 0x00, 0x00, 0x01, + 0x32, 0x00, 0x00, 0x02, 0x32, 0x00, 0x00, 0x03, 0x34, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x02, 0x34, 0x00, 0x00, 0x01, + 0x34, 0x00, 0x00, 0x01, 0x33, 0x00, 0x00, 0x02, 0x33, 0x00, 0x00, 0x03, 0x35, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x02, + 0x35, 0x00, 0x00, 0x01, 0x35, 0x00, 0x00, 0x01, 0x34, 0x00, 0x00, 0x02, 0x34, 0x00, 0x00, 0x03, 0x36, 0x00, 0x00, 0x00, + 0x35, 0x00, 0x00, 0x02, 0x36, 0x00, 0x00, 0x01, 0x36, 0x00, 0x00, 0x01, 0x35, 0x00, 0x00, 0x02, 0x35, 0x00, 0x00, 0x03, + 0x37, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x02, 0x37, 0x00, 0x00, 0x01, 0x37, 0x00, 0x00, 0x01, 0x36, 0x00, 0x00, 0x02, + 0x36, 0x00, 0x00, 0x03, 0x38, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x02, 0x38, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x01, + 0x37, 0x00, 0x00, 0x02, 0x37, 0x00, 0x00, 0x03, 0x39, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x02, 0x39, 0x00, 0x00, 0x01, + 0x39, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x02, 0x38, 0x00, 0x00, 0x03, 0x3a, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x02, + 0x3a, 0x00, 0x00, 0x01, 0x3a, 0x00, 0x00, 0x01, 0x39, 0x00, 0x00, 0x02, 0x39, 0x00, 0x00, 0x03, 0x3b, 0x00, 0x00, 0x00, + 0x3a, 0x00, 0x00, 0x02, 0x3b, 0x00, 0x00, 0x01, 0x3b, 0x00, 0x00, 0x01, 0x3a, 0x00, 0x00, 0x02, 0x3a, 0x00, 0x00, 0x03, + 0x3c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x02, 0x3c, 0x00, 0x00, 0x01, 0x3c, 0x00, 0x00, 0x01, 0x3b, 0x00, 0x00, 0x02, + 0x3b, 0x00, 0x00, 0x03, 0x3d, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x02, 0x3d, 0x00, 0x00, 0x01, 0x3d, 0x00, 0x00, 0x01, + 0x3c, 0x00, 0x00, 0x02, 0x3c, 0x00, 0x00, 0x03, 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x02, 0x3e, 0x00, 0x00, 0x01, + 0x3e, 0x00, 0x00, 0x01, 0x3d, 0x00, 0x00, 0x02, 0x3d, 0x00, 0x00, 0x03, 0x3f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x02, + 0x3f, 0x00, 0x00, 0x01, 0x3f, 0x00, 0x00, 0x01, 0x3e, 0x00, 0x00, 0x02, 0x3e, 0x00, 0x00, 0x03, 0x40, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x01, 0x3f, 0x00, 0x00, 0x02, 0x3f, 0x00, 0x00, 0x03, + 0x41, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0x41, 0x00, 0x00, 0x01, 0x41, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x02, + 0x40, 0x00, 0x00, 0x03, 0x42, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x02, 0x42, 0x00, 0x00, 0x01, 0x42, 0x00, 0x00, 0x01, + 0x41, 0x00, 0x00, 0x02, 0x41, 0x00, 0x00, 0x03, 0x43, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x02, 0x43, 0x00, 0x00, 0x01, + 0x43, 0x00, 0x00, 0x01, 0x42, 0x00, 0x00, 0x02, 0x42, 0x00, 0x00, 0x03, 0x44, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x02, + 0x44, 0x00, 0x00, 0x01, 0x44, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x02, 0x43, 0x00, 0x00, 0x03, 0x45, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x02, 0x45, 0x00, 0x00, 0x01, 0x45, 0x00, 0x00, 0x01, 0x44, 0x00, 0x00, 0x02, 0x44, 0x00, 0x00, 0x03, + 0x46, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x02, 0x46, 0x00, 0x00, 0x01, 0x46, 0x00, 0x00, 0x01, 0x45, 0x00, 0x00, 0x02, + 0x45, 0x00, 0x00, 0x03, 0x47, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x02, 0x47, 0x00, 0x00, 0x01, 0x47, 0x00, 0x00, 0x01, + 0x46, 0x00, 0x00, 0x02, 0x46, 0x00, 0x00, 0x03, 0x48, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x01, + 0x48, 0x00, 0x00, 0x01, 0x47, 0x00, 0x00, 0x02, 0x47, 0x00, 0x00, 0x03, 0x49, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x02, + 0x49, 0x00, 0x00, 0x01, 0x49, 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x03, 0x4a, 0x00, 0x00, 0x4b, + 0x00, 0x00, 0x4a, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4d, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x4e, 0x00, + 0x00, 0x4e, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x50, 0x00, 0x00, 0x51, 0x00, 0x00, 0x50, 0x00, 0x00, 0x50, + 0x00, 0x00, 0x51, 0x00, 0x00, 0x51, 0x00, 0x00, 0x52, 0x00, 0x00, 0x53, 0x00, 0x00, 0x52, 0x00, 0x00, 0x52, 0x00, 0x00, + 0x53, 0x00, 0x00, 0x53, 0x00, 0x00, 0x54, 0x00, 0x00, 0x55, 0x00, 0x00, 0x54, 0x00, 0x00, 0x54, 0x00, 0x00, 0x55, 0x00, + 0x00, 0x55, 0x00, 0x00, 0x56, 0x00, 0x00, 0x57, 0x00, 0x00, 0x56, 0x00, 0x00, 0x56, 0x00, 0x00, 0x57, 0x00, 0x00, 0x57, + 0x00, 0x00, 0x58, 0x00, 0x00, 0x59, 0x00, 0x00, 0x58, 0x00, 0x00, 0x58, 0x00, 0x00, 0x59, 0x00, 0x00, 0x59, 0x00, 0x00, + 0x5a, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5c, 0x00, + 0x00, 0x5d, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x5f, + 0x00, 0x00, 0x5e, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x60, 0x00, 0x00, 0x61, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x61, 0x00, 0x00, 0x61, 0x00, 0x00, 0x62, 0x00, 0x00, 0x63, 0x00, 0x00, 0x62, 0x00, + 0x00, 0x62, 0x00, 0x00, 0x63, 0x00, 0x00, 0x63, 0x00, 0x00, 0x64, 0x00, 0x00, 0x65, 0x00, 0x00, 0x64, 0x00, 0x00, 0x64, + 0x00, 0x00, 0x65, 0x00, 0x00, 0x65, 0x00, 0x00, 0x66, 0x00, 0x00, 0x67, 0x00, 0x00, 0x66, 0x00, 0x00, 0x66, 0x00, 0x00, + 0x67, 0x00, 0x00, 0x67, 0x00, 0x00, 0x68, 0x00, 0x00, 0x69, 0x00, 0x00, 0x68, 0x00, 0x00, 0x68, 0x00, 0x00, 0x69, 0x00, + 0x00, 0x69, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6b, + 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x6d, 0x00, 0x00, + 0x6e, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x70, 0x00, + 0x00, 0x71, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x71, 0x00, 0x00, 0x71, 0x00, 0x00, 0x72, 0x00, 0x00, 0x73, + 0x00, 0x00, 0x72, 0x00, 0x00, 0x72, 0x00, 0x00, 0x73, 0x00, 0x00, 0x73, 0x00, 0x00, 0x74, 0x00, 0x00, 0x75, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x74, 0x00, 0x00, 0x75, 0x00, 0x00, 0x75, 0x00, 0x00, 0x76, 0x00, 0x00, 0x77, 0x00, 0x00, 0x76, 0x00, + 0x00, 0x76, 0x00, 0x00, 0x77, 0x00, 0x00, 0x77, 0x00, 0x00, 0x78, 0x00, 0x00, 0x79, 0x00, 0x00, 0x78, 0x00, 0x00, 0x78, + 0x00, 0x00, 0x79, 0x00, 0x00, 0x79, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x7a, 0x00, 0x00, + 0x7b, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7d, 0x00, + 0x00, 0x7d, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x81, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, 0x00, + 0x82, 0x00, 0x00, 0x83, 0x00, 0x00, 0x82, 0x00, 0x00, 0x82, 0x00, 0x00, 0x83, 0x00, 0x00, 0x83, 0x00, 0x00, 0x84, 0x00, + 0x00, 0x85, 0x00, 0x00, 0x84, 0x00, 0x00, 0x84, 0x00, 0x00, 0x85, 0x00, 0x00, 0x85, 0x00, 0x00, 0x86, 0x00, 0x00, 0x87, + 0x00, 0x00, 0x86, 0x00, 0x00, 0x86, 0x00, 0x00, 0x87, 0x00, 0x00, 0x87, 0x00, 0x00, 0x88, 0x00, 0x00, 0x89, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x88, 0x00, 0x00, 0x89, 0x00, 0x00, 0x89, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x8a, 0x00, + 0x00, 0x8a, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8c, + 0x00, 0x00, 0x8d, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x8e, 0x00, 0x00, + 0x8f, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x90, 0x00, 0x00, 0x91, 0x00, 0x00, 0x90, 0x00, 0x00, 0x90, 0x00, 0x00, 0x91, 0x00, + 0x00, 0x91, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x02, 0x4b, 0x00, 0x00, 0x01, 0x4b, 0x00, 0x00, 0x01, + 0x4a, 0x00, 0x00, 0x02, 0x4a, 0x00, 0x00, 0x03, 0x4d, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x02, 0x4d, 0x00, 0x00, 0x01, + 0x4d, 0x00, 0x00, 0x01, 0x4c, 0x00, 0x00, 0x02, 0x4c, 0x00, 0x00, 0x03, 0x4f, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x02, + 0x4f, 0x00, 0x00, 0x01, 0x4f, 0x00, 0x00, 0x01, 0x4e, 0x00, 0x00, 0x02, 0x4e, 0x00, 0x00, 0x03, 0x51, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x00, 0x02, 0x51, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x02, 0x50, 0x00, 0x00, 0x03, + 0x53, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x02, 0x53, 0x00, 0x00, 0x01, 0x53, 0x00, 0x00, 0x01, 0x52, 0x00, 0x00, 0x02, + 0x52, 0x00, 0x00, 0x03, 0x55, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x02, 0x55, 0x00, 0x00, 0x01, 0x55, 0x00, 0x00, 0x01, + 0x54, 0x00, 0x00, 0x02, 0x54, 0x00, 0x00, 0x03, 0x57, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x02, 0x57, 0x00, 0x00, 0x01, + 0x57, 0x00, 0x00, 0x01, 0x56, 0x00, 0x00, 0x02, 0x56, 0x00, 0x00, 0x03, 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x02, + 0x59, 0x00, 0x00, 0x01, 0x59, 0x00, 0x00, 0x01, 0x58, 0x00, 0x00, 0x02, 0x58, 0x00, 0x00, 0x03, 0x5b, 0x00, 0x00, 0x00, + 0x5a, 0x00, 0x00, 0x02, 0x5b, 0x00, 0x00, 0x01, 0x5b, 0x00, 0x00, 0x01, 0x5a, 0x00, 0x00, 0x02, 0x5a, 0x00, 0x00, 0x03, + 0x5d, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x02, 0x5d, 0x00, 0x00, 0x01, 0x5d, 0x00, 0x00, 0x01, 0x5c, 0x00, 0x00, 0x02, + 0x5c, 0x00, 0x00, 0x03, 0x5f, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x02, 0x5f, 0x00, 0x00, 0x01, 0x5f, 0x00, 0x00, 0x01, + 0x5e, 0x00, 0x00, 0x02, 0x5e, 0x00, 0x00, 0x03, 0x61, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x02, 0x61, 0x00, 0x00, 0x01, + 0x61, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, 0x02, 0x60, 0x00, 0x00, 0x03, 0x63, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x02, + 0x63, 0x00, 0x00, 0x01, 0x63, 0x00, 0x00, 0x01, 0x62, 0x00, 0x00, 0x02, 0x62, 0x00, 0x00, 0x03, 0x65, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x02, 0x65, 0x00, 0x00, 0x01, 0x65, 0x00, 0x00, 0x01, 0x64, 0x00, 0x00, 0x02, 0x64, 0x00, 0x00, 0x03, + 0x67, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x02, 0x67, 0x00, 0x00, 0x01, 0x67, 0x00, 0x00, 0x01, 0x66, 0x00, 0x00, 0x02, + 0x66, 0x00, 0x00, 0x03, 0x69, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x69, 0x00, 0x00, 0x01, 0x69, 0x00, 0x00, 0x01, + 0x68, 0x00, 0x00, 0x02, 0x68, 0x00, 0x00, 0x03, 0x6b, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x02, 0x6b, 0x00, 0x00, 0x01, + 0x6b, 0x00, 0x00, 0x01, 0x6a, 0x00, 0x00, 0x02, 0x6a, 0x00, 0x00, 0x03, 0x6d, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x02, + 0x6d, 0x00, 0x00, 0x01, 0x6d, 0x00, 0x00, 0x01, 0x6c, 0x00, 0x00, 0x02, 0x6c, 0x00, 0x00, 0x03, 0x6f, 0x00, 0x00, 0x00, + 0x6e, 0x00, 0x00, 0x02, 0x6f, 0x00, 0x00, 0x01, 0x6f, 0x00, 0x00, 0x01, 0x6e, 0x00, 0x00, 0x02, 0x6e, 0x00, 0x00, 0x03, + 0x71, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x02, 0x71, 0x00, 0x00, 0x01, 0x71, 0x00, 0x00, 0x01, 0x70, 0x00, 0x00, 0x02, + 0x70, 0x00, 0x00, 0x03, 0x73, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x02, 0x73, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00, 0x01, + 0x72, 0x00, 0x00, 0x02, 0x72, 0x00, 0x00, 0x03, 0x75, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x02, 0x75, 0x00, 0x00, 0x01, + 0x75, 0x00, 0x00, 0x01, 0x74, 0x00, 0x00, 0x02, 0x74, 0x00, 0x00, 0x03, 0x77, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x02, + 0x77, 0x00, 0x00, 0x01, 0x77, 0x00, 0x00, 0x01, 0x76, 0x00, 0x00, 0x02, 0x76, 0x00, 0x00, 0x03, 0x79, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x02, 0x79, 0x00, 0x00, 0x01, 0x79, 0x00, 0x00, 0x01, 0x78, 0x00, 0x00, 0x02, 0x78, 0x00, 0x00, 0x03, + 0x7b, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x02, 0x7b, 0x00, 0x00, 0x01, 0x7b, 0x00, 0x00, 0x01, 0x7a, 0x00, 0x00, 0x02, + 0x7a, 0x00, 0x00, 0x03, 0x7d, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x02, 0x7d, 0x00, 0x00, 0x01, 0x7d, 0x00, 0x00, 0x01, + 0x7c, 0x00, 0x00, 0x02, 0x7c, 0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x01, + 0x7f, 0x00, 0x00, 0x01, 0x7e, 0x00, 0x00, 0x02, 0x7e, 0x00, 0x00, 0x03, 0x81, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x02, + 0x81, 0x00, 0x00, 0x01, 0x81, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x03, 0x83, 0x00, 0x00, 0x00, + 0x82, 0x00, 0x00, 0x02, 0x83, 0x00, 0x00, 0x01, 0x83, 0x00, 0x00, 0x01, 0x82, 0x00, 0x00, 0x02, 0x82, 0x00, 0x00, 0x03, + 0x85, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x02, 0x85, 0x00, 0x00, 0x01, 0x85, 0x00, 0x00, 0x01, 0x84, 0x00, 0x00, 0x02, + 0x84, 0x00, 0x00, 0x03, 0x87, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x02, 0x87, 0x00, 0x00, 0x01, 0x87, 0x00, 0x00, 0x01, + 0x86, 0x00, 0x00, 0x02, 0x86, 0x00, 0x00, 0x03, 0x89, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x02, 0x89, 0x00, 0x00, 0x01, + 0x89, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x02, 0x88, 0x00, 0x00, 0x03, 0x8b, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x02, + 0x8b, 0x00, 0x00, 0x01, 0x8b, 0x00, 0x00, 0x01, 0x8a, 0x00, 0x00, 0x02, 0x8a, 0x00, 0x00, 0x03, 0x8d, 0x00, 0x00, 0x00, + 0x8c, 0x00, 0x00, 0x02, 0x8d, 0x00, 0x00, 0x01, 0x8d, 0x00, 0x00, 0x01, 0x8c, 0x00, 0x00, 0x02, 0x8c, 0x00, 0x00, 0x03, + 0x8f, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x02, 0x8f, 0x00, 0x00, 0x01, 0x8f, 0x00, 0x00, 0x01, 0x8e, 0x00, 0x00, 0x02, + 0x8e, 0x00, 0x00, 0x03, 0x91, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x02, 0x91, 0x00, 0x00, 0x01, 0x91, 0x00, 0x00, 0x01, + 0x90, 0x00, 0x00, 0x02, 0x90, 0x00, 0x00, 0x03, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, + 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xe4, 0x9a, 0xf5, 0x8a, 0xe4, 0x9a, + 0xf5, 0x8a, 0xe4, 0x9a, 0xf5, 0x8a, 0xe4, 0x9a, 0xf5, 0x8a, 0xe4, 0x9a, 0xf5, 0x8a, 0xe4, 0x9a, 0xe4, 0x9a, 0xd6, 0xa8, + 0xe4, 0x9a, 0xd6, 0xa8, 0xe4, 0x9a, 0xd6, 0xa8, 0xe4, 0x9a, 0xd6, 0xa8, 0xe4, 0x9a, 0xd6, 0xa8, 0xe4, 0x9a, 0xd6, 0xa8, + 0xd6, 0xa8, 0xca, 0xb4, 0xd6, 0xa8, 0xca, 0xb4, 0xd6, 0xa8, 0xca, 0xb4, 0xd6, 0xa8, 0xca, 0xb4, 0xd6, 0xa8, 0xca, 0xb4, + 0xd6, 0xa8, 0xca, 0xb4, 0xca, 0xb4, 0xbf, 0xbf, 0xca, 0xb4, 0xbf, 0xbf, 0xca, 0xb4, 0xbf, 0xbf, 0xca, 0xb4, 0xbf, 0xbf, + 0xca, 0xb4, 0xbf, 0xbf, 0xca, 0xb4, 0xbf, 0xbf, 0xbf, 0xbf, 0xb4, 0xca, 0xbf, 0xbf, 0xb4, 0xca, 0xbf, 0xbf, 0xb4, 0xca, + 0xbf, 0xbf, 0xb4, 0xca, 0xbf, 0xbf, 0xb4, 0xca, 0xbf, 0xbf, 0xb4, 0xca, 0xb4, 0xca, 0xa8, 0xd6, 0xb4, 0xca, 0xa8, 0xd6, + 0xb4, 0xca, 0xa8, 0xd6, 0xb4, 0xca, 0xa8, 0xd6, 0xb4, 0xca, 0xa8, 0xd6, 0xb4, 0xca, 0xa8, 0xd6, 0xa8, 0xd6, 0x9a, 0xe4, + 0xa8, 0xd6, 0x9a, 0xe4, 0xa8, 0xd6, 0x9a, 0xe4, 0xa8, 0xd6, 0x9a, 0xe4, 0xa8, 0xd6, 0x9a, 0xe4, 0xa8, 0xd6, 0x9a, 0xe4, + 0x9a, 0xe4, 0x8a, 0xf5, 0x9a, 0xe4, 0x8a, 0xf5, 0x9a, 0xe4, 0x8a, 0xf5, 0x9a, 0xe4, 0x8a, 0xf5, 0x9a, 0xe4, 0x8a, 0xf5, + 0x9a, 0xe4, 0x8a, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, + 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x75, 0xf5, 0x65, 0xe4, 0x75, 0xf5, 0x65, 0xe4, 0x75, 0xf5, 0x65, 0xe4, + 0x75, 0xf5, 0x65, 0xe4, 0x75, 0xf5, 0x65, 0xe4, 0x75, 0xf5, 0x65, 0xe4, 0x65, 0xe4, 0x57, 0xd6, 0x65, 0xe4, 0x57, 0xd6, + 0x65, 0xe4, 0x57, 0xd6, 0x65, 0xe4, 0x57, 0xd6, 0x65, 0xe4, 0x57, 0xd6, 0x65, 0xe4, 0x57, 0xd6, 0x57, 0xd6, 0x4b, 0xca, + 0x57, 0xd6, 0x4b, 0xca, 0x57, 0xd6, 0x4b, 0xca, 0x57, 0xd6, 0x4b, 0xca, 0x57, 0xd6, 0x4b, 0xca, 0x57, 0xd6, 0x4b, 0xca, + 0x4b, 0xca, 0x40, 0xbf, 0x4b, 0xca, 0x40, 0xbf, 0x4b, 0xca, 0x40, 0xbf, 0x4b, 0xca, 0x40, 0xbf, 0x4b, 0xca, 0x40, 0xbf, + 0x4b, 0xca, 0x40, 0xbf, 0x40, 0xbf, 0x35, 0xb4, 0x40, 0xbf, 0x35, 0xb4, 0x40, 0xbf, 0x35, 0xb4, 0x40, 0xbf, 0x35, 0xb4, + 0x40, 0xbf, 0x35, 0xb4, 0x40, 0xbf, 0x35, 0xb4, 0x35, 0xb4, 0x29, 0xa8, 0x35, 0xb4, 0x29, 0xa8, 0x35, 0xb4, 0x29, 0xa8, + 0x35, 0xb4, 0x29, 0xa8, 0x35, 0xb4, 0x29, 0xa8, 0x35, 0xb4, 0x29, 0xa8, 0x29, 0xa8, 0x1b, 0x9a, 0x29, 0xa8, 0x1b, 0x9a, + 0x29, 0xa8, 0x1b, 0x9a, 0x29, 0xa8, 0x1b, 0x9a, 0x29, 0xa8, 0x1b, 0x9a, 0x29, 0xa8, 0x1b, 0x9a, 0x1b, 0x9a, 0x0a, 0x8a, + 0x1b, 0x9a, 0x0a, 0x8a, 0x1b, 0x9a, 0x0a, 0x8a, 0x1b, 0x9a, 0x0a, 0x8a, 0x1b, 0x9a, 0x0a, 0x8a, 0x1b, 0x9a, 0x0a, 0x8a, + 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, + 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x75, 0x1b, 0x65, 0x0a, 0x75, 0x1b, 0x65, 0x0a, 0x75, 0x1b, 0x65, 0x0a, 0x75, 0x1b, 0x65, + 0x0a, 0x75, 0x1b, 0x65, 0x0a, 0x75, 0x1b, 0x65, 0x1b, 0x65, 0x29, 0x57, 0x1b, 0x65, 0x29, 0x57, 0x1b, 0x65, 0x29, 0x57, + 0x1b, 0x65, 0x29, 0x57, 0x1b, 0x65, 0x29, 0x57, 0x1b, 0x65, 0x29, 0x57, 0x29, 0x57, 0x35, 0x4b, 0x29, 0x57, 0x35, 0x4b, + 0x29, 0x57, 0x35, 0x4b, 0x29, 0x57, 0x35, 0x4b, 0x29, 0x57, 0x35, 0x4b, 0x29, 0x57, 0x35, 0x4b, 0x35, 0x4b, 0x40, 0x40, + 0x35, 0x4b, 0x40, 0x40, 0x35, 0x4b, 0x40, 0x40, 0x35, 0x4b, 0x40, 0x40, 0x35, 0x4b, 0x40, 0x40, 0x35, 0x4b, 0x40, 0x40, + 0x40, 0x40, 0x4b, 0x35, 0x40, 0x40, 0x4b, 0x35, 0x40, 0x40, 0x4b, 0x35, 0x40, 0x40, 0x4b, 0x35, 0x40, 0x40, 0x4b, 0x35, + 0x40, 0x40, 0x4b, 0x35, 0x4b, 0x35, 0x57, 0x29, 0x4b, 0x35, 0x57, 0x29, 0x4b, 0x35, 0x57, 0x29, 0x4b, 0x35, 0x57, 0x29, + 0x4b, 0x35, 0x57, 0x29, 0x4b, 0x35, 0x57, 0x29, 0x57, 0x29, 0x65, 0x1b, 0x57, 0x29, 0x65, 0x1b, 0x57, 0x29, 0x65, 0x1b, + 0x57, 0x29, 0x65, 0x1b, 0x57, 0x29, 0x65, 0x1b, 0x57, 0x29, 0x65, 0x1b, 0x65, 0x1b, 0x75, 0x0a, 0x65, 0x1b, 0x75, 0x0a, + 0x65, 0x1b, 0x75, 0x0a, 0x65, 0x1b, 0x75, 0x0a, 0x65, 0x1b, 0x75, 0x0a, 0x65, 0x1b, 0x75, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, + 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, + 0x8a, 0x0a, 0x9a, 0x1b, 0x8a, 0x0a, 0x9a, 0x1b, 0x8a, 0x0a, 0x9a, 0x1b, 0x8a, 0x0a, 0x9a, 0x1b, 0x8a, 0x0a, 0x9a, 0x1b, + 0x8a, 0x0a, 0x9a, 0x1b, 0x9a, 0x1b, 0xa8, 0x29, 0x9a, 0x1b, 0xa8, 0x29, 0x9a, 0x1b, 0xa8, 0x29, 0x9a, 0x1b, 0xa8, 0x29, + 0x9a, 0x1b, 0xa8, 0x29, 0x9a, 0x1b, 0xa8, 0x29, 0xa8, 0x29, 0xb4, 0x35, 0xa8, 0x29, 0xb4, 0x35, 0xa8, 0x29, 0xb4, 0x35, + 0xa8, 0x29, 0xb4, 0x35, 0xa8, 0x29, 0xb4, 0x35, 0xa8, 0x29, 0xb4, 0x35, 0xb4, 0x35, 0xbf, 0x40, 0xb4, 0x35, 0xbf, 0x40, + 0xb4, 0x35, 0xbf, 0x40, 0xb4, 0x35, 0xbf, 0x40, 0xb4, 0x35, 0xbf, 0x40, 0xb4, 0x35, 0xbf, 0x40, 0xbf, 0x40, 0xca, 0x4b, + 0xbf, 0x40, 0xca, 0x4b, 0xbf, 0x40, 0xca, 0x4b, 0xbf, 0x40, 0xca, 0x4b, 0xbf, 0x40, 0xca, 0x4b, 0xbf, 0x40, 0xca, 0x4b, + 0xca, 0x4b, 0xd6, 0x57, 0xca, 0x4b, 0xd6, 0x57, 0xca, 0x4b, 0xd6, 0x57, 0xca, 0x4b, 0xd6, 0x57, 0xca, 0x4b, 0xd6, 0x57, + 0xca, 0x4b, 0xd6, 0x57, 0xd6, 0x57, 0xe4, 0x65, 0xd6, 0x57, 0xe4, 0x65, 0xd6, 0x57, 0xe4, 0x65, 0xd6, 0x57, 0xe4, 0x65, + 0xd6, 0x57, 0xe4, 0x65, 0xd6, 0x57, 0xe4, 0x65, 0xe4, 0x65, 0xf5, 0x75, 0xe4, 0x65, 0xf5, 0x75, 0xe4, 0x65, 0xf5, 0x75, + 0xe4, 0x65, 0xf5, 0x75, 0xe4, 0x65, 0xf5, 0x75, 0xe4, 0x65, 0xf5, 0x75, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, + 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x21, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x21, 0x00, + 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x20, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, + 0x00, 0x1d, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1c, + 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1b, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1b, 0x00, + 0x00, 0x1b, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x19, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, + 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x18, 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x17, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x17, 0x00, + 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x16, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, + 0x00, 0x00, 0x16, 0x00, 0x00, 0x15, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x14, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, + 0x00, 0x13, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x12, + 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x11, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x10, 0x00, 0x00, 0x11, 0x00, + 0x00, 0x11, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, + 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, + 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x27, 0x00, 0x00, 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, + 0x00, 0x28, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x29, + 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x2a, 0x00, 0x00, + 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2a, 0x00, + 0x00, 0x2a, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, + 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2e, 0x00, + 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, + 0x00, 0x00, 0x2f, 0x00, 0x00, 0x30, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x31, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, + 0x00, 0x32, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x33, + 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x34, 0x00, 0x00, + 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00, 0x34, 0x00, + 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x36, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, + 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x37, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, + 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, + 0x00, 0x00, 0x39, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, + 0x3a, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, + 0x00, 0x3c, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3d, + 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3e, 0x00, 0x00, + 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3e, 0x00, + 0x00, 0x3e, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x41, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x42, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x42, 0x00, + 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x43, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, + 0x00, 0x00, 0x43, 0x00, 0x00, 0x44, 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x45, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, + 0x00, 0x46, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x47, + 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x48, 0x00, 0x00, + 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x49, 0x00, 0x00, 0x48, 0x00, + 0x00, 0x48, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x25, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, + 0x00, 0x00, 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x02, 0x00, 0x00, 0x23, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, + 0x00, 0x22, 0x00, 0x00, 0x24, 0x00, 0x00, 0x21, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, + 0x00, 0x00, 0x23, 0x00, 0x00, 0x20, 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x21, 0x00, + 0x00, 0x1e, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1d, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1c, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1e, 0x00, + 0x00, 0x1e, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, + 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x19, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x17, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, + 0x00, 0x00, 0x19, 0x00, 0x00, 0x16, 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x15, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x17, 0x00, + 0x00, 0x14, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x16, 0x00, 0x00, 0x13, + 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x15, 0x00, 0x00, 0x12, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x00, 0x11, 0x00, 0x00, 0x14, 0x00, + 0x00, 0x14, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x13, 0x00, 0x00, 0x10, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x11, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x0e, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, + 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0d, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x09, 0x00, 0x00, 0x06, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x05, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x24, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x49, 0x00, 0x00, 0x28, + 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x25, 0x00, 0x00, 0x29, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x27, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x27, 0x00, + 0x00, 0x27, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x28, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, + 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x29, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2d, 0x00, + 0x00, 0x2d, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, + 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x30, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x2e, 0x00, + 0x00, 0x31, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x32, + 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x30, 0x00, 0x00, 0x33, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x31, 0x00, 0x00, 0x34, 0x00, 0x00, 0x31, 0x00, + 0x00, 0x31, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x32, 0x00, 0x00, 0x35, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, + 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x33, 0x00, 0x00, 0x36, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x34, 0x00, 0x00, 0x37, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x37, 0x00, + 0x00, 0x37, 0x00, 0x00, 0x35, 0x00, 0x00, 0x38, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x36, 0x00, 0x00, 0x39, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, + 0x37, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x3b, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3c, + 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3d, 0x00, 0x00, + 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3b, 0x00, + 0x00, 0x3b, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, + 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x40, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x41, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x41, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x42, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x43, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x44, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x42, 0x00, + 0x00, 0x45, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x43, 0x00, 0x00, 0x46, + 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x44, 0x00, 0x00, 0x47, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x45, 0x00, 0x00, 0x48, 0x00, 0x00, 0x45, 0x00, + 0x00, 0x45, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x46, 0x00, 0x00, 0x49, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, + 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x47, 0x00, 0x00, 0x25, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x48, 0x00, 0x00, 0x27, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x27, 0x00, + 0x00, 0x27, 0x00, 0x00, 0x24, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x36, 0x24, 0x00, 0x00, 0x06, 0x24, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x4e, 0x23, 0x00, 0x00, 0x1e, 0x24, 0x00, 0x00, 0x36, 0x23, 0x00, 0x00, 0x06, + 0x23, 0x00, 0x00, 0x06, 0x24, 0x00, 0x00, 0x36, 0x24, 0x00, 0x00, 0x4e, 0x22, 0x00, 0x00, 0x1e, 0x23, 0x00, 0x00, 0x36, + 0x22, 0x00, 0x00, 0x06, 0x22, 0x00, 0x00, 0x06, 0x23, 0x00, 0x00, 0x36, 0x23, 0x00, 0x00, 0x4e, 0x21, 0x00, 0x00, 0x1e, + 0x22, 0x00, 0x00, 0x36, 0x21, 0x00, 0x00, 0x06, 0x21, 0x00, 0x00, 0x06, 0x22, 0x00, 0x00, 0x36, 0x22, 0x00, 0x00, 0x4e, + 0x20, 0x00, 0x00, 0x1e, 0x21, 0x00, 0x00, 0x36, 0x20, 0x00, 0x00, 0x06, 0x20, 0x00, 0x00, 0x06, 0x21, 0x00, 0x00, 0x36, + 0x21, 0x00, 0x00, 0x4e, 0x1f, 0x00, 0x00, 0x1e, 0x20, 0x00, 0x00, 0x36, 0x1f, 0x00, 0x00, 0x06, 0x1f, 0x00, 0x00, 0x06, + 0x20, 0x00, 0x00, 0x36, 0x20, 0x00, 0x00, 0x4e, 0x1e, 0x00, 0x00, 0x1e, 0x1f, 0x00, 0x00, 0x36, 0x1e, 0x00, 0x00, 0x06, + 0x1e, 0x00, 0x00, 0x06, 0x1f, 0x00, 0x00, 0x36, 0x1f, 0x00, 0x00, 0x4e, 0x1d, 0x00, 0x00, 0x1e, 0x1e, 0x00, 0x00, 0x36, + 0x1d, 0x00, 0x00, 0x06, 0x1d, 0x00, 0x00, 0x06, 0x1e, 0x00, 0x00, 0x36, 0x1e, 0x00, 0x00, 0x4e, 0x1c, 0x00, 0x00, 0x1e, + 0x1d, 0x00, 0x00, 0x36, 0x1c, 0x00, 0x00, 0x06, 0x1c, 0x00, 0x00, 0x06, 0x1d, 0x00, 0x00, 0x36, 0x1d, 0x00, 0x00, 0x4e, + 0x1b, 0x00, 0x00, 0x1e, 0x1c, 0x00, 0x00, 0x36, 0x1b, 0x00, 0x00, 0x06, 0x1b, 0x00, 0x00, 0x06, 0x1c, 0x00, 0x00, 0x36, + 0x1c, 0x00, 0x00, 0x4e, 0x1a, 0x00, 0x00, 0x1e, 0x1b, 0x00, 0x00, 0x36, 0x1a, 0x00, 0x00, 0x06, 0x1a, 0x00, 0x00, 0x06, + 0x1b, 0x00, 0x00, 0x36, 0x1b, 0x00, 0x00, 0x4e, 0x19, 0x00, 0x00, 0x1e, 0x1a, 0x00, 0x00, 0x36, 0x19, 0x00, 0x00, 0x06, + 0x19, 0x00, 0x00, 0x06, 0x1a, 0x00, 0x00, 0x36, 0x1a, 0x00, 0x00, 0x4e, 0x18, 0x00, 0x00, 0x1e, 0x19, 0x00, 0x00, 0x36, + 0x18, 0x00, 0x00, 0x06, 0x18, 0x00, 0x00, 0x06, 0x19, 0x00, 0x00, 0x36, 0x19, 0x00, 0x00, 0x4e, 0x17, 0x00, 0x00, 0x1e, + 0x18, 0x00, 0x00, 0x36, 0x17, 0x00, 0x00, 0x06, 0x17, 0x00, 0x00, 0x06, 0x18, 0x00, 0x00, 0x36, 0x18, 0x00, 0x00, 0x4e, + 0x16, 0x00, 0x00, 0x1e, 0x17, 0x00, 0x00, 0x36, 0x16, 0x00, 0x00, 0x06, 0x16, 0x00, 0x00, 0x06, 0x17, 0x00, 0x00, 0x36, + 0x17, 0x00, 0x00, 0x4e, 0x15, 0x00, 0x00, 0x1e, 0x16, 0x00, 0x00, 0x36, 0x15, 0x00, 0x00, 0x06, 0x15, 0x00, 0x00, 0x06, + 0x16, 0x00, 0x00, 0x36, 0x16, 0x00, 0x00, 0x4e, 0x14, 0x00, 0x00, 0x1e, 0x15, 0x00, 0x00, 0x36, 0x14, 0x00, 0x00, 0x06, + 0x14, 0x00, 0x00, 0x06, 0x15, 0x00, 0x00, 0x36, 0x15, 0x00, 0x00, 0x4e, 0x13, 0x00, 0x00, 0x1e, 0x14, 0x00, 0x00, 0x36, + 0x13, 0x00, 0x00, 0x06, 0x13, 0x00, 0x00, 0x06, 0x14, 0x00, 0x00, 0x36, 0x14, 0x00, 0x00, 0x4e, 0x12, 0x00, 0x00, 0x1e, + 0x13, 0x00, 0x00, 0x36, 0x12, 0x00, 0x00, 0x06, 0x12, 0x00, 0x00, 0x06, 0x13, 0x00, 0x00, 0x36, 0x13, 0x00, 0x00, 0x4e, + 0x11, 0x00, 0x00, 0x1e, 0x12, 0x00, 0x00, 0x36, 0x11, 0x00, 0x00, 0x06, 0x11, 0x00, 0x00, 0x06, 0x12, 0x00, 0x00, 0x36, + 0x12, 0x00, 0x00, 0x4e, 0x10, 0x00, 0x00, 0x1e, 0x11, 0x00, 0x00, 0x36, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x00, 0x06, + 0x11, 0x00, 0x00, 0x36, 0x11, 0x00, 0x00, 0x4e, 0x0f, 0x00, 0x00, 0x1e, 0x10, 0x00, 0x00, 0x36, 0x0f, 0x00, 0x00, 0x06, + 0x0f, 0x00, 0x00, 0x06, 0x10, 0x00, 0x00, 0x36, 0x10, 0x00, 0x00, 0x4e, 0x0e, 0x00, 0x00, 0x1e, 0x0f, 0x00, 0x00, 0x36, + 0x0e, 0x00, 0x00, 0x06, 0x0e, 0x00, 0x00, 0x06, 0x0f, 0x00, 0x00, 0x36, 0x0f, 0x00, 0x00, 0x4e, 0x0d, 0x00, 0x00, 0x1e, + 0x0e, 0x00, 0x00, 0x36, 0x0d, 0x00, 0x00, 0x06, 0x0d, 0x00, 0x00, 0x06, 0x0e, 0x00, 0x00, 0x36, 0x0e, 0x00, 0x00, 0x4e, + 0x0c, 0x00, 0x00, 0x1e, 0x0d, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x06, 0x0d, 0x00, 0x00, 0x36, + 0x0d, 0x00, 0x00, 0x4e, 0x0b, 0x00, 0x00, 0x1e, 0x0c, 0x00, 0x00, 0x36, 0x0b, 0x00, 0x00, 0x06, 0x0b, 0x00, 0x00, 0x06, + 0x0c, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x4e, 0x0a, 0x00, 0x00, 0x1e, 0x0b, 0x00, 0x00, 0x36, 0x0a, 0x00, 0x00, 0x06, + 0x0a, 0x00, 0x00, 0x06, 0x0b, 0x00, 0x00, 0x36, 0x0b, 0x00, 0x00, 0x4e, 0x09, 0x00, 0x00, 0x1e, 0x0a, 0x00, 0x00, 0x36, + 0x09, 0x00, 0x00, 0x06, 0x09, 0x00, 0x00, 0x06, 0x0a, 0x00, 0x00, 0x36, 0x0a, 0x00, 0x00, 0x4e, 0x08, 0x00, 0x00, 0x1e, + 0x09, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x06, 0x08, 0x00, 0x00, 0x06, 0x09, 0x00, 0x00, 0x36, 0x09, 0x00, 0x00, 0x4e, + 0x07, 0x00, 0x00, 0x1e, 0x08, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x06, 0x07, 0x00, 0x00, 0x06, 0x08, 0x00, 0x00, 0x36, + 0x08, 0x00, 0x00, 0x4e, 0x06, 0x00, 0x00, 0x1e, 0x07, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x06, + 0x07, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x4e, 0x05, 0x00, 0x00, 0x1e, 0x06, 0x00, 0x00, 0x36, 0x05, 0x00, 0x00, 0x06, + 0x05, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x4e, 0x04, 0x00, 0x00, 0x1e, 0x05, 0x00, 0x00, 0x36, + 0x04, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x36, 0x05, 0x00, 0x00, 0x4e, 0x03, 0x00, 0x00, 0x1e, + 0x04, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x4e, + 0x02, 0x00, 0x00, 0x1e, 0x03, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x36, + 0x03, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, + 0x02, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x4e, 0x27, 0x00, 0x00, 0x1e, 0x25, 0x00, 0x00, 0x36, 0x27, 0x00, 0x00, 0x06, + 0x27, 0x00, 0x00, 0x06, 0x25, 0x00, 0x00, 0x36, 0x25, 0x00, 0x00, 0x4e, 0x28, 0x00, 0x00, 0x1e, 0x27, 0x00, 0x00, 0x36, + 0x28, 0x00, 0x00, 0x06, 0x28, 0x00, 0x00, 0x06, 0x27, 0x00, 0x00, 0x36, 0x27, 0x00, 0x00, 0x4e, 0x29, 0x00, 0x00, 0x1e, + 0x28, 0x00, 0x00, 0x36, 0x29, 0x00, 0x00, 0x06, 0x29, 0x00, 0x00, 0x06, 0x28, 0x00, 0x00, 0x36, 0x28, 0x00, 0x00, 0x4e, + 0x2a, 0x00, 0x00, 0x1e, 0x29, 0x00, 0x00, 0x36, 0x2a, 0x00, 0x00, 0x06, 0x2a, 0x00, 0x00, 0x06, 0x29, 0x00, 0x00, 0x36, + 0x29, 0x00, 0x00, 0x4e, 0x2b, 0x00, 0x00, 0x1e, 0x2a, 0x00, 0x00, 0x36, 0x2b, 0x00, 0x00, 0x06, 0x2b, 0x00, 0x00, 0x06, + 0x2a, 0x00, 0x00, 0x36, 0x2a, 0x00, 0x00, 0x4e, 0x2c, 0x00, 0x00, 0x1e, 0x2b, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x06, + 0x2c, 0x00, 0x00, 0x06, 0x2b, 0x00, 0x00, 0x36, 0x2b, 0x00, 0x00, 0x4e, 0x2d, 0x00, 0x00, 0x1e, 0x2c, 0x00, 0x00, 0x36, + 0x2d, 0x00, 0x00, 0x06, 0x2d, 0x00, 0x00, 0x06, 0x2c, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x4e, 0x2e, 0x00, 0x00, 0x1e, + 0x2d, 0x00, 0x00, 0x36, 0x2e, 0x00, 0x00, 0x06, 0x2e, 0x00, 0x00, 0x06, 0x2d, 0x00, 0x00, 0x36, 0x2d, 0x00, 0x00, 0x4e, + 0x2f, 0x00, 0x00, 0x1e, 0x2e, 0x00, 0x00, 0x36, 0x2f, 0x00, 0x00, 0x06, 0x2f, 0x00, 0x00, 0x06, 0x2e, 0x00, 0x00, 0x36, + 0x2e, 0x00, 0x00, 0x4e, 0x30, 0x00, 0x00, 0x1e, 0x2f, 0x00, 0x00, 0x36, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, + 0x2f, 0x00, 0x00, 0x36, 0x2f, 0x00, 0x00, 0x4e, 0x31, 0x00, 0x00, 0x1e, 0x30, 0x00, 0x00, 0x36, 0x31, 0x00, 0x00, 0x06, + 0x31, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x36, 0x30, 0x00, 0x00, 0x4e, 0x32, 0x00, 0x00, 0x1e, 0x31, 0x00, 0x00, 0x36, + 0x32, 0x00, 0x00, 0x06, 0x32, 0x00, 0x00, 0x06, 0x31, 0x00, 0x00, 0x36, 0x31, 0x00, 0x00, 0x4e, 0x33, 0x00, 0x00, 0x1e, + 0x32, 0x00, 0x00, 0x36, 0x33, 0x00, 0x00, 0x06, 0x33, 0x00, 0x00, 0x06, 0x32, 0x00, 0x00, 0x36, 0x32, 0x00, 0x00, 0x4e, + 0x34, 0x00, 0x00, 0x1e, 0x33, 0x00, 0x00, 0x36, 0x34, 0x00, 0x00, 0x06, 0x34, 0x00, 0x00, 0x06, 0x33, 0x00, 0x00, 0x36, + 0x33, 0x00, 0x00, 0x4e, 0x35, 0x00, 0x00, 0x1e, 0x34, 0x00, 0x00, 0x36, 0x35, 0x00, 0x00, 0x06, 0x35, 0x00, 0x00, 0x06, + 0x34, 0x00, 0x00, 0x36, 0x34, 0x00, 0x00, 0x4e, 0x36, 0x00, 0x00, 0x1e, 0x35, 0x00, 0x00, 0x36, 0x36, 0x00, 0x00, 0x06, + 0x36, 0x00, 0x00, 0x06, 0x35, 0x00, 0x00, 0x36, 0x35, 0x00, 0x00, 0x4e, 0x37, 0x00, 0x00, 0x1e, 0x36, 0x00, 0x00, 0x36, + 0x37, 0x00, 0x00, 0x06, 0x37, 0x00, 0x00, 0x06, 0x36, 0x00, 0x00, 0x36, 0x36, 0x00, 0x00, 0x4e, 0x38, 0x00, 0x00, 0x1e, + 0x37, 0x00, 0x00, 0x36, 0x38, 0x00, 0x00, 0x06, 0x38, 0x00, 0x00, 0x06, 0x37, 0x00, 0x00, 0x36, 0x37, 0x00, 0x00, 0x4e, + 0x39, 0x00, 0x00, 0x1e, 0x38, 0x00, 0x00, 0x36, 0x39, 0x00, 0x00, 0x06, 0x39, 0x00, 0x00, 0x06, 0x38, 0x00, 0x00, 0x36, + 0x38, 0x00, 0x00, 0x4e, 0x3a, 0x00, 0x00, 0x1e, 0x39, 0x00, 0x00, 0x36, 0x3a, 0x00, 0x00, 0x06, 0x3a, 0x00, 0x00, 0x06, + 0x39, 0x00, 0x00, 0x36, 0x39, 0x00, 0x00, 0x4e, 0x3b, 0x00, 0x00, 0x1e, 0x3a, 0x00, 0x00, 0x36, 0x3b, 0x00, 0x00, 0x06, + 0x3b, 0x00, 0x00, 0x06, 0x3a, 0x00, 0x00, 0x36, 0x3a, 0x00, 0x00, 0x4e, 0x3c, 0x00, 0x00, 0x1e, 0x3b, 0x00, 0x00, 0x36, + 0x3c, 0x00, 0x00, 0x06, 0x3c, 0x00, 0x00, 0x06, 0x3b, 0x00, 0x00, 0x36, 0x3b, 0x00, 0x00, 0x4e, 0x3d, 0x00, 0x00, 0x1e, + 0x3c, 0x00, 0x00, 0x36, 0x3d, 0x00, 0x00, 0x06, 0x3d, 0x00, 0x00, 0x06, 0x3c, 0x00, 0x00, 0x36, 0x3c, 0x00, 0x00, 0x4e, + 0x3e, 0x00, 0x00, 0x1e, 0x3d, 0x00, 0x00, 0x36, 0x3e, 0x00, 0x00, 0x06, 0x3e, 0x00, 0x00, 0x06, 0x3d, 0x00, 0x00, 0x36, + 0x3d, 0x00, 0x00, 0x4e, 0x3f, 0x00, 0x00, 0x1e, 0x3e, 0x00, 0x00, 0x36, 0x3f, 0x00, 0x00, 0x06, 0x3f, 0x00, 0x00, 0x06, + 0x3e, 0x00, 0x00, 0x36, 0x3e, 0x00, 0x00, 0x4e, 0x40, 0x00, 0x00, 0x1e, 0x3f, 0x00, 0x00, 0x36, 0x40, 0x00, 0x00, 0x06, + 0x40, 0x00, 0x00, 0x06, 0x3f, 0x00, 0x00, 0x36, 0x3f, 0x00, 0x00, 0x4e, 0x41, 0x00, 0x00, 0x1e, 0x40, 0x00, 0x00, 0x36, + 0x41, 0x00, 0x00, 0x06, 0x41, 0x00, 0x00, 0x06, 0x40, 0x00, 0x00, 0x36, 0x40, 0x00, 0x00, 0x4e, 0x42, 0x00, 0x00, 0x1e, + 0x41, 0x00, 0x00, 0x36, 0x42, 0x00, 0x00, 0x06, 0x42, 0x00, 0x00, 0x06, 0x41, 0x00, 0x00, 0x36, 0x41, 0x00, 0x00, 0x4e, + 0x43, 0x00, 0x00, 0x1e, 0x42, 0x00, 0x00, 0x36, 0x43, 0x00, 0x00, 0x06, 0x43, 0x00, 0x00, 0x06, 0x42, 0x00, 0x00, 0x36, + 0x42, 0x00, 0x00, 0x4e, 0x44, 0x00, 0x00, 0x1e, 0x43, 0x00, 0x00, 0x36, 0x44, 0x00, 0x00, 0x06, 0x44, 0x00, 0x00, 0x06, + 0x43, 0x00, 0x00, 0x36, 0x43, 0x00, 0x00, 0x4e, 0x45, 0x00, 0x00, 0x1e, 0x44, 0x00, 0x00, 0x36, 0x45, 0x00, 0x00, 0x06, + 0x45, 0x00, 0x00, 0x06, 0x44, 0x00, 0x00, 0x36, 0x44, 0x00, 0x00, 0x4e, 0x46, 0x00, 0x00, 0x1e, 0x45, 0x00, 0x00, 0x36, + 0x46, 0x00, 0x00, 0x06, 0x46, 0x00, 0x00, 0x06, 0x45, 0x00, 0x00, 0x36, 0x45, 0x00, 0x00, 0x4e, 0x47, 0x00, 0x00, 0x1e, + 0x46, 0x00, 0x00, 0x36, 0x47, 0x00, 0x00, 0x06, 0x47, 0x00, 0x00, 0x06, 0x46, 0x00, 0x00, 0x36, 0x46, 0x00, 0x00, 0x4e, + 0x48, 0x00, 0x00, 0x1e, 0x47, 0x00, 0x00, 0x36, 0x48, 0x00, 0x00, 0x06, 0x48, 0x00, 0x00, 0x06, 0x47, 0x00, 0x00, 0x36, + 0x47, 0x00, 0x00, 0x4e, 0x49, 0x00, 0x00, 0x1e, 0x48, 0x00, 0x00, 0x36, 0x49, 0x00, 0x00, 0x06, 0x49, 0x00, 0x00, 0x06, + 0x48, 0x00, 0x00, 0x36, 0x48, 0x00, 0x00, 0x4e, 0x25, 0x00, 0x00, 0x1e, 0x49, 0x00, 0x00, 0x36, 0x25, 0x00, 0x00, 0x06, + 0x25, 0x00, 0x00, 0x06, 0x49, 0x00, 0x00, 0x36, 0x49, 0x00, 0x00, 0x4e, + ]), + }, +}; diff --git a/test-apps/testbed/frontend/TileIO.data.1.4.ts b/test-apps/testbed/frontend/TileIO.data.1.4.ts new file mode 100644 index 0000000..6d0ead9 --- /dev/null +++ b/test-apps/testbed/frontend/TileIO.data.1.4.ts @@ -0,0 +1,1327 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { IModelTileIO } from "@bentley/imodeljs-frontend/lib/tile"; + +// Binary tile data produced using version 1.2 of the tile format. +export const TILE_DATA_1_4 = { + versionMajor: 1, + versionMinor: 4, + headerLength: 84, + rectangle: { + flags: IModelTileIO.Flags.None, + bytes: new Uint8Array([ + 0x69, 0x4d, 0x64, 0x6c, 0x04, 0x00, 0x01, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x4b, 0x37, 0x89, + 0x41, 0x00, 0x04, 0xc0, 0xc7, 0x4b, 0x37, 0x89, 0x41, 0x00, 0x14, 0xc0, 0x00, 0x00, 0xbd, 0x9a, 0xf2, 0xd7, 0x5a, 0xbe, + 0x39, 0xb4, 0xc8, 0x76, 0xbe, 0xff, 0x03, 0x40, 0x39, 0xb4, 0xc8, 0x76, 0xbe, 0xff, 0x13, 0x40, 0x00, 0x00, 0xbd, 0x9a, + 0xf2, 0xd7, 0x5a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2c, 0x07, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, + 0x02, 0x00, 0x00, 0x00, 0xb8, 0x06, 0x00, 0x00, 0xa8, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, + 0x78, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x36, 0x34, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x30, 0x7d, 0x2c, + 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, + 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, + 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x35, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, + 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, + 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x37, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, + 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x38, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, + 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, + 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x38, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x22, 0x3a, 0x36, 0x34, 0x7d, 0x7d, 0x2c, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, + 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4b, 0x48, 0x52, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, + 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x57, 0x45, 0x42, 0x33, 0x44, 0x5f, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, + 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, 0x67, 0x6c, 0x45, + 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4f, 0x45, 0x53, + 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x22, + 0x5d, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x61, 0x74, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, + 0x36, 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, + 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, + 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, 0x38, 0x30, + 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, + 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x7d, 0x7d, + 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, 0x6f, 0x6f, + 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, + 0x65, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, + 0x22, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, + 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, + 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, + 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x7d, 0x7d, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, + 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, + 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x2c, 0x22, 0x73, 0x75, 0x72, 0x66, 0x61, 0x63, + 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, + 0x3a, 0x31, 0x7d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, + 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, + 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x34, 0x2c, + 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x44, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, + 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x37, 0x2e, 0x36, + 0x33, 0x30, 0x36, 0x35, 0x35, 0x33, 0x37, 0x34, 0x39, 0x39, 0x30, 0x34, 0x36, 0x31, 0x31, 0x65, 0x2d, 0x30, 0x35, 0x2c, + 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, + 0x30, 0x31, 0x35, 0x32, 0x36, 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, 0x32, 0x2c, 0x30, + 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, + 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, + 0x30, 0x2c, 0x2d, 0x32, 0x2e, 0x35, 0x30, 0x30, 0x33, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x36, 0x2c, 0x2d, 0x35, 0x2e, 0x30, 0x30, 0x30, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x31, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, + 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x32, 0x2e, 0x35, 0x30, 0x30, + 0x33, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x36, 0x2c, 0x35, 0x2e, 0x30, 0x30, 0x30, 0x37, + 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x31, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, + 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, + 0x5b, 0x2d, 0x32, 0x2e, 0x35, 0x30, 0x30, 0x33, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x36, + 0x2c, 0x2d, 0x35, 0x2e, 0x30, 0x30, 0x30, 0x37, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x31, + 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x7d, 0x2c, 0x22, 0x75, 0x6e, + 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, + 0x22, 0x3a, 0x7b, 0x22, 0x4e, 0x6f, 0x64, 0x65, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x22, 0x4d, 0x65, 0x73, 0x68, + 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x22, 0x64, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x6e, 0x6f, + 0x64, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x5d, 0x7d, 0x7d, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x03, 0x00, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0xf8, 0xff, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xf8, 0xff, 0xf8, 0xff, + 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x03, 0x00, 0xf8, 0xff, 0xfe, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x03, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x03, + ]), + }, + lineString: { + flags: IModelTileIO.Flags.None, + bytes: new Uint8Array([ + 0x69, 0x4d, 0x64, 0x6c, 0x04, 0x00, 0x01, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xf1, 0xd2, 0x4d, + 0x62, 0x00, 0x1e, 0xc0, 0xc7, 0x4b, 0x37, 0x89, 0x41, 0x00, 0x24, 0xc0, 0x00, 0x00, 0xbd, 0x9a, 0xf2, 0xd7, 0x5a, 0xbe, + 0x55, 0x0e, 0x2d, 0xb2, 0x9d, 0xff, 0x1d, 0x40, 0x39, 0xb4, 0xc8, 0x76, 0xbe, 0xff, 0x23, 0x40, 0x00, 0x00, 0xbd, 0x9a, + 0xf2, 0xd7, 0x5a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0x0a, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, + 0x02, 0x00, 0x00, 0x00, 0x2c, 0x0a, 0x00, 0x00, 0x58, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, + 0x78, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x37, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x30, 0x7d, 0x2c, + 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, + 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x34, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x37, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, + 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, + 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x34, 0x35, 0x36, 0x2c, 0x22, 0x62, + 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x37, 0x36, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, + 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, + 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x34, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x34, 0x31, 0x36, 0x7d, 0x7d, 0x2c, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4b, 0x48, 0x52, 0x5f, 0x62, 0x69, 0x6e, 0x61, + 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x57, 0x45, 0x42, 0x33, 0x44, 0x5f, 0x71, 0x75, 0x61, 0x6e, + 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, + 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, + 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, 0x69, + 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, + 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, + 0x72, 0x22, 0x3a, 0x36, 0x35, 0x35, 0x33, 0x35, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, + 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, + 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, + 0x35, 0x33, 0x35, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, + 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, + 0x31, 0x7d, 0x7d, 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, + 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, 0x3a, + 0x5b, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x30, 0x22, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, + 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, + 0x61, 0x6c, 0x30, 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x30, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, + 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, + 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x36, + 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x44, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x68, 0x61, 0x73, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, + 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, + 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x33, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, + 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x30, 0x2e, + 0x30, 0x30, 0x30, 0x32, 0x32, 0x38, 0x39, 0x31, 0x39, 0x36, 0x36, 0x31, 0x32, 0x34, 0x39, 0x37, 0x31, 0x33, 0x38, 0x37, + 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, + 0x30, 0x30, 0x33, 0x30, 0x35, 0x32, 0x32, 0x36, 0x32, 0x31, 0x34, 0x39, 0x39, 0x39, 0x36, 0x31, 0x38, 0x34, 0x34, 0x2c, + 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, + 0x36, 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, + 0x2e, 0x30, 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x2c, 0x31, 0x2e, 0x30, + 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x37, 0x2e, 0x35, 0x30, + 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x31, 0x30, 0x2e, 0x30, 0x30, + 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, + 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, + 0x3a, 0x5b, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x7d, 0x2c, 0x22, 0x75, + 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x35, 0x33, 0x35, 0x2c, 0x22, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x38, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x6e, 0x6f, 0x64, 0x65, + 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4e, 0x6f, 0x64, 0x65, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x22, 0x4d, 0x65, 0x73, + 0x68, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x22, 0x64, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x73, 0x22, + 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x6e, + 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x5d, 0x7d, 0x7d, + 0x7d, 0x0a, 0x20, 0x20, 0x03, 0x00, 0xf8, 0xff, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x55, 0x55, 0xf8, 0xff, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x03, + 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0d, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x90, + 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x60, + 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x90, + 0x02, 0x00, 0x00, 0x09, 0x02, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x60, + 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, + 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x39, + 0x03, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x39, 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x60, + 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x51, + 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0e, + 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x21, + 0x03, 0x00, 0x00, 0x39, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x90, + 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x90, + 0x03, 0x00, 0x00, 0x51, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x0c, 0x04, 0x00, 0x00, 0x60, + 0x04, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0f, 0x04, 0x00, 0x00, 0x0e, + 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0e, + 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x21, + 0x04, 0x00, 0x00, 0x33, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x33, 0x04, 0x00, 0x00, 0x90, + 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x09, 0x05, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x90, + 0x04, 0x00, 0x00, 0x4b, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x0c, 0x05, 0x00, 0x00, 0x60, + 0x05, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0f, 0x05, 0x00, 0x00, 0x0e, + ]), + }, + lineStrings: { + flags: IModelTileIO.Flags.None, + bytes: new Uint8Array([ + 0x69, 0x4d, 0x64, 0x6c, 0x04, 0x00, 0x01, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xf1, 0xd2, 0x4d, + 0x62, 0x00, 0x1e, 0xc0, 0xab, 0xf1, 0xd2, 0x4d, 0x62, 0x00, 0x3e, 0xc0, 0x00, 0x00, 0xbd, 0x9a, 0xf2, 0xd7, 0x5a, 0xbe, + 0x55, 0x0e, 0x2d, 0xb2, 0x9d, 0xff, 0x1d, 0x40, 0x55, 0x0e, 0x2d, 0xb2, 0x9d, 0xff, 0x3d, 0x40, 0x00, 0x00, 0xbd, 0x9a, + 0xf2, 0xd7, 0x5a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x68, 0x18, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x67, 0x6c, 0x54, 0x46, 0x02, 0x00, 0x00, 0x00, 0xdc, 0x17, 0x00, 0x00, 0x88, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x56, + 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, + 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x22, 0x3a, 0x37, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, + 0x3a, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, + 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x35, 0x32, 0x2c, 0x22, 0x62, 0x79, + 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x32, 0x31, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, + 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x34, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x22, 0x3a, 0x37, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x22, 0x3a, + 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, + 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x36, 0x38, 0x34, + 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x33, 0x36, 0x38, 0x7d, 0x2c, + 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, + 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x22, 0x3a, 0x34, 0x35, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, + 0x37, 0x36, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, + 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, + 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, + 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x31, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x22, 0x3a, 0x32, 0x37, 0x33, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, + 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x34, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x22, 0x3a, 0x34, 0x31, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x36, 0x38, 0x34, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x32, 0x30, + 0x35, 0x32, 0x7d, 0x7d, 0x2c, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, + 0x22, 0x3a, 0x5b, 0x22, 0x4b, 0x48, 0x52, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, + 0x2c, 0x22, 0x57, 0x45, 0x42, 0x33, 0x44, 0x5f, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, 0x6d, 0x61, + 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, + 0x22, 0x3a, 0x7b, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x31, 0x36, 0x37, 0x31, 0x31, + 0x39, 0x33, 0x35, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, + 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, + 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x31, 0x36, 0x37, 0x31, 0x31, 0x39, 0x33, + 0x35, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6c, 0x69, + 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x7d, + 0x2c, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, + 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x31, 0x35, 0x37, 0x39, 0x30, 0x30, 0x38, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, + 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, + 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, + 0x6f, 0x72, 0x22, 0x3a, 0x31, 0x35, 0x37, 0x39, 0x30, 0x30, 0x38, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, + 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x34, 0x31, 0x37, 0x37, 0x30, 0x36, 0x36, 0x32, 0x33, 0x32, 0x2c, 0x22, 0x6c, 0x69, + 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x7d, + 0x7d, 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, 0x6f, + 0x6f, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x7b, + 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x30, 0x22, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, + 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x30, 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, + 0x30, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, + 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, + 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x36, 0x2c, 0x22, + 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x44, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, + 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x33, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, + 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x30, 0x2e, 0x30, 0x30, + 0x30, 0x32, 0x32, 0x38, 0x39, 0x31, 0x39, 0x36, 0x36, 0x31, 0x32, 0x34, 0x39, 0x37, 0x31, 0x33, 0x38, 0x37, 0x2c, 0x30, + 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, + 0x39, 0x31, 0x35, 0x36, 0x37, 0x38, 0x36, 0x34, 0x34, 0x39, 0x39, 0x38, 0x38, 0x35, 0x35, 0x35, 0x30, 0x2c, 0x30, 0x2e, + 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, 0x31, + 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, 0x30, + 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, + 0x2c, 0x2d, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, + 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, + 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, + 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x34, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, + 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, + 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, + 0x2d, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, + 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x7d, 0x2c, 0x22, 0x75, 0x6e, 0x69, + 0x66, 0x6f, 0x72, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x31, 0x36, 0x37, 0x31, 0x31, 0x39, 0x33, 0x35, 0x2c, + 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x38, 0x7d, 0x7d, 0x2c, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x22, 0x2c, 0x22, 0x69, 0x73, + 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, + 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x31, 0x22, 0x2c, 0x22, 0x6e, 0x65, + 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, + 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x31, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, + 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x22, 0x2c, 0x22, 0x74, 0x79, + 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, + 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, + 0x31, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x31, 0x32, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x32, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, + 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x33, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, + 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x30, 0x2e, 0x30, + 0x30, 0x30, 0x32, 0x32, 0x38, 0x39, 0x31, 0x39, 0x36, 0x36, 0x31, 0x32, 0x34, 0x39, 0x37, 0x31, 0x33, 0x38, 0x37, 0x2c, + 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, + 0x30, 0x39, 0x31, 0x35, 0x36, 0x37, 0x38, 0x36, 0x34, 0x34, 0x39, 0x39, 0x38, 0x38, 0x35, 0x35, 0x35, 0x30, 0x2c, 0x30, + 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, + 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, + 0x30, 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x32, 0x2c, 0x2d, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x37, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, + 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x37, 0x2e, 0x35, 0x30, 0x31, + 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, + 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, + 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, + 0x5b, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, + 0x2c, 0x2d, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, + 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x7d, 0x2c, 0x22, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x38, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, + 0x3a, 0x7b, 0x22, 0x4e, 0x6f, 0x64, 0x65, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, + 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x22, 0x64, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, + 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x6e, 0x6f, 0x64, + 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x5d, 0x7d, 0x7d, 0x7d, 0x0a, + 0x03, 0x00, 0x55, 0x55, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0xac, 0x2a, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55, + 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf8, 0xff, 0xac, 0x2a, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x39, + 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, + 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0f, + 0x00, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x60, + 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x09, + 0x02, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0d, + 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x60, + 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x60, + 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x39, 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x90, + 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x51, 0x02, 0x00, 0x00, 0x90, + 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, + 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x21, 0x03, 0x00, 0x00, 0x39, + 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x60, + 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x51, + 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x0c, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0e, + 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0f, 0x04, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x90, + 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x0d, + 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x21, 0x04, 0x00, 0x00, 0x33, + 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x33, 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x60, + 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x09, 0x05, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x4b, + 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x0c, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0e, + 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0f, 0x05, 0x00, 0x00, 0x0e, 0x03, 0x00, 0xa7, 0xaa, + 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x55, 0x55, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x55, 0x55, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x55, 0x55, 0xa7, 0xaa, 0xfe, 0x7f, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x55, 0x55, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xfe, 0x7f, + 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0xf8, 0xff, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x03, 0x00, 0xa7, 0xaa, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x55, 0x55, 0x50, 0xd5, 0xfe, 0x7f, 0x01, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x55, 0x55, 0xf8, 0xff, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xa7, 0xaa, + 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x50, 0xd5, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, + 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x06, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, + 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x09, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1b, + 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x90, + 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x90, + 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x90, + 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, + 0x02, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x36, + 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x09, 0x02, 0x00, 0x00, 0x09, + 0x01, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, + 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0f, + 0x02, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x60, + 0x02, 0x00, 0x00, 0x39, 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x06, + 0x03, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x51, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0d, + 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x90, + 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x21, 0x03, 0x00, 0x00, 0x39, 0x04, 0x00, 0x00, 0x60, + 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x90, + 0x04, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x51, 0x04, 0x00, 0x00, 0x60, + 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x0c, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x0d, + 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0f, 0x04, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0d, + 0x03, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x90, + 0x03, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x21, 0x04, 0x00, 0x00, 0x33, 0x05, 0x00, 0x00, 0x60, + 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x33, 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x90, + 0x05, 0x00, 0x00, 0x09, 0x05, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x4b, 0x05, 0x00, 0x00, 0x60, + 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x0c, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x0d, + 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0f, 0x05, 0x00, 0x00, 0x0e, 0x07, 0x00, 0x00, 0x1b, 0x06, 0x00, 0x00, 0x39, + 0x07, 0x00, 0x00, 0x60, 0x07, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x39, 0x06, 0x00, 0x00, 0x90, 0x07, 0x00, 0x00, 0x60, + 0x06, 0x00, 0x00, 0x90, 0x07, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x51, + 0x06, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x0d, 0x06, 0x00, 0x00, 0x0c, 0x06, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x0e, + 0x06, 0x00, 0x00, 0x0d, 0x06, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x0f, 0x06, 0x00, 0x00, 0x0e, 0x08, 0x00, 0x00, 0x21, + 0x07, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x60, 0x07, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x90, + 0x08, 0x00, 0x00, 0x60, 0x07, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x09, 0x08, 0x00, 0x00, 0x09, 0x07, 0x00, 0x00, 0x90, + 0x07, 0x00, 0x00, 0x4e, 0x08, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x0d, 0x08, 0x00, 0x00, 0x0c, 0x08, 0x00, 0x00, 0x60, + 0x08, 0x00, 0x00, 0x0e, 0x08, 0x00, 0x00, 0x0d, 0x08, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x0f, 0x08, 0x00, 0x00, 0x0e, + 0x09, 0x00, 0x00, 0x1e, 0x08, 0x00, 0x00, 0x39, 0x09, 0x00, 0x00, 0x60, 0x09, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x39, + 0x08, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x06, 0x09, 0x00, 0x00, 0x06, + 0x08, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x51, 0x08, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x0d, 0x08, 0x00, 0x00, 0x0c, + 0x08, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x0e, 0x08, 0x00, 0x00, 0x0d, 0x08, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x0f, + 0x08, 0x00, 0x00, 0x0e, 0x0a, 0x00, 0x00, 0x21, 0x09, 0x00, 0x00, 0x39, 0x0a, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x60, + 0x09, 0x00, 0x00, 0x39, 0x09, 0x00, 0x00, 0x90, 0x0a, 0x00, 0x00, 0x60, 0x09, 0x00, 0x00, 0x90, 0x0a, 0x00, 0x00, 0x09, + 0x0a, 0x00, 0x00, 0x09, 0x09, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x51, 0x0a, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x0d, + 0x0a, 0x00, 0x00, 0x0c, 0x0a, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x0e, 0x0a, 0x00, 0x00, 0x0d, 0x0a, 0x00, 0x00, 0x60, + 0x0a, 0x00, 0x00, 0x0f, 0x0a, 0x00, 0x00, 0x0e, 0x09, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x0d, 0x09, 0x00, 0x00, 0x0c, + 0x09, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x0e, 0x09, 0x00, 0x00, 0x0d, 0x09, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x0f, + 0x09, 0x00, 0x00, 0x0e, 0x0b, 0x00, 0x00, 0x21, 0x0a, 0x00, 0x00, 0x33, 0x0b, 0x00, 0x00, 0x60, 0x0b, 0x00, 0x00, 0x60, + 0x0a, 0x00, 0x00, 0x33, 0x0a, 0x00, 0x00, 0x90, 0x0b, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x90, 0x0b, 0x00, 0x00, 0x09, + 0x0b, 0x00, 0x00, 0x09, 0x0a, 0x00, 0x00, 0x90, 0x0a, 0x00, 0x00, 0x4b, 0x0b, 0x00, 0x00, 0x60, 0x0b, 0x00, 0x00, 0x0d, + 0x0b, 0x00, 0x00, 0x0c, 0x0b, 0x00, 0x00, 0x60, 0x0b, 0x00, 0x00, 0x0e, 0x0b, 0x00, 0x00, 0x0d, 0x0b, 0x00, 0x00, 0x60, + 0x0b, 0x00, 0x00, 0x0f, 0x0b, 0x00, 0x00, 0x0e, + ]), + }, + triangles: { + flags: IModelTileIO.Flags.None, + bytes: new Uint8Array([ + 0x69, 0x4d, 0x64, 0x6c, 0x04, 0x00, 0x01, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xf1, 0xd2, 0x4d, + 0x62, 0x00, 0x1e, 0xc0, 0xc7, 0x4b, 0x37, 0x89, 0x41, 0x00, 0x24, 0xc0, 0x00, 0x00, 0xbd, 0x9a, 0xf2, 0xd7, 0x5a, 0xbe, + 0x55, 0x0e, 0x2d, 0xb2, 0x9d, 0xff, 0x1d, 0x40, 0x39, 0xb4, 0xc8, 0x76, 0xbe, 0xff, 0x23, 0x40, 0x00, 0x00, 0xbd, 0x9a, + 0xf2, 0xd7, 0x5a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x0f, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x06, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, + 0x02, 0x00, 0x00, 0x00, 0x70, 0x0e, 0x00, 0x00, 0xf4, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, + 0x78, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x31, 0x35, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x30, 0x7d, + 0x2c, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, + 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x35, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x35, 0x36, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, + 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, + 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, + 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x31, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x22, 0x3a, 0x33, 0x34, 0x38, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, + 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, + 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, + 0x32, 0x31, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x39, 0x31, 0x32, + 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, + 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, + 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x38, 0x34, + 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, + 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, + 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, + 0x37, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x35, 0x36, 0x7d, 0x2c, + 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, + 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, + 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, + 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x37, 0x34, 0x38, 0x7d, 0x2c, + 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, + 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, + 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x37, 0x2c, + 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x37, 0x32, 0x30, 0x7d, 0x7d, 0x2c, 0x22, + 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4b, 0x48, + 0x52, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x57, 0x45, 0x42, 0x33, + 0x44, 0x5f, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, + 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x66, 0x69, + 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x32, 0x35, 0x35, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, + 0x61, 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, + 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, + 0x22, 0x3a, 0x32, 0x35, 0x35, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, + 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, + 0x22, 0x3a, 0x30, 0x7d, 0x2c, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x66, + 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x32, 0x31, 0x33, 0x30, 0x37, 0x30, 0x36, 0x36, 0x38, 0x37, + 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, + 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, + 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x32, 0x31, 0x33, 0x30, 0x37, 0x30, 0x36, 0x36, 0x38, 0x37, + 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, + 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x7d, 0x7d, + 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x5f, 0x52, 0x6f, 0x6f, + 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, + 0x65, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, + 0x22, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, + 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, + 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, + 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x7d, 0x7d, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, + 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, + 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x2c, 0x22, 0x73, 0x75, 0x72, 0x66, 0x61, 0x63, + 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, + 0x3a, 0x31, 0x7d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, + 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, + 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x39, 0x2c, + 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x32, + 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, + 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, + 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x70, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, + 0x22, 0x3a, 0x5b, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x32, 0x38, 0x39, 0x31, 0x39, 0x36, 0x36, 0x31, 0x32, 0x34, 0x39, + 0x37, 0x31, 0x33, 0x38, 0x37, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, + 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x33, 0x30, 0x35, 0x32, 0x32, 0x36, 0x32, 0x31, 0x34, 0x39, 0x39, 0x39, 0x36, + 0x31, 0x38, 0x34, 0x34, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, + 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, + 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, + 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, + 0x5b, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, + 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x30, + 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, + 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, + 0x5d, 0x7d, 0x2c, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x39, 0x7d, 0x7d, 0x2c, 0x7b, 0x22, 0x65, 0x64, + 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x65, + 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, + 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x2c, 0x22, + 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, + 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x7d, 0x7d, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, + 0x72, 0x22, 0x3a, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, + 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x31, 0x22, 0x2c, 0x22, 0x73, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, + 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x31, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, + 0x7d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, + 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x56, + 0x65, 0x72, 0x74, 0x65, 0x78, 0x31, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x39, 0x2c, 0x22, 0x66, + 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x32, 0x2c, 0x22, + 0x68, 0x61, 0x73, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x74, 0x72, 0x75, + 0x65, 0x2c, 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, + 0x61, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, + 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, + 0x30, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x32, 0x38, 0x39, 0x31, 0x39, 0x36, 0x36, 0x31, 0x32, 0x34, 0x39, 0x37, 0x31, 0x33, + 0x38, 0x37, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, + 0x2e, 0x30, 0x30, 0x30, 0x33, 0x30, 0x35, 0x32, 0x32, 0x36, 0x32, 0x31, 0x34, 0x39, 0x39, 0x39, 0x36, 0x31, 0x38, 0x34, + 0x34, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, + 0x35, 0x32, 0x36, 0x31, 0x33, 0x31, 0x30, 0x37, 0x34, 0x39, 0x39, 0x38, 0x30, 0x39, 0x32, 0x38, 0x65, 0x2d, 0x30, 0x38, + 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x2c, 0x31, + 0x2e, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x37, 0x2e, + 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x32, 0x2c, 0x31, 0x30, 0x2e, + 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x30, + 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, + 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x32, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x38, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x5d, 0x7d, 0x2c, + 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x39, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x6e, 0x6f, 0x64, + 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4e, 0x6f, 0x64, 0x65, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x22, 0x4d, 0x65, + 0x73, 0x68, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x22, 0x64, + 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x73, + 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x7b, 0x22, + 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x5d, 0x7d, + 0x7d, 0x7d, 0x0a, 0x20, 0xa7, 0xaa, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0xa7, 0xaa, 0xf8, 0xff, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xf8, 0xff, 0xfe, 0x7f, + 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0xfe, 0x7f, 0xfe, 0x7f, 0x01, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0xf8, 0xff, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xa7, 0xaa, 0xfe, 0x7f, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0x03, 0x00, 0xfe, 0x7f, 0xfe, 0x7f, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x03, 0x00, 0xf8, 0xff, + 0xfe, 0x7f, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0xfe, 0x7f, 0xfe, 0x7f, 0x02, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, + 0x01, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x01, + 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x01, + 0x05, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, + 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x03, + 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x02, + 0x06, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x01, + 0x07, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x03, 0xa7, 0xaa, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xa7, 0xaa, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0xf8, 0xff, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0x03, 0x00, + 0xfe, 0x7f, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0xfe, 0x7f, 0xfe, 0x7f, 0x01, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xa7, 0xaa, 0x03, 0x00, 0xfe, 0x7f, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0x03, 0x00, 0x03, 0x00, 0xfe, 0x7f, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0x03, 0x00, 0xfe, 0x7f, 0xfe, 0x7f, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0x03, 0x00, + 0xfe, 0x7f, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x00, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x80, + 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x01, + 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, + 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x03, + 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x02, + 0x06, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x01, + 0x06, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, + 0x08, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x03, + ]), + }, + cylinder: { + flags: IModelTileIO.Flags.ContainsCurves, + bytes: new Uint8Array([ + 0x69, 0x4d, 0x64, 0x6c, 0x04, 0x00, 0x01, 0x00, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x39, 0xd6, 0xc5, 0x6d, + 0x34, 0x00, 0x00, 0xc0, 0x39, 0xd6, 0xc5, 0x6d, 0x34, 0x00, 0x00, 0xc0, 0x56, 0xc1, 0xa8, 0xa4, 0x4e, 0x00, 0x08, 0xc0, + 0x8e, 0x53, 0x74, 0x24, 0x97, 0xff, 0xff, 0x3f, 0x8e, 0x53, 0x74, 0x24, 0x97, 0xff, 0xff, 0x3f, 0xaa, 0x3e, 0x57, 0x5b, + 0xb1, 0xff, 0x07, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x2b, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, + 0x02, 0x00, 0x00, 0x00, 0xd0, 0x2a, 0x00, 0x00, 0x74, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, + 0x78, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x32, 0x33, 0x33, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x30, + 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, + 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, + 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, + 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x37, 0x32, 0x38, 0x2c, + 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x34, 0x39, 0x32, 0x38, 0x7d, 0x2c, 0x22, + 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, + 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, + 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x38, 0x36, 0x34, 0x2c, 0x22, + 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x37, 0x33, 0x30, 0x34, 0x7d, 0x2c, 0x22, 0x62, + 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, + 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, + 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x32, 0x39, 0x36, + 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x33, 0x36, 0x33, 0x32, 0x7d, 0x2c, + 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, + 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x36, 0x34, 0x38, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x36, 0x36, + 0x35, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, + 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, + 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x3a, 0x31, 0x32, 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x32, + 0x33, 0x33, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x30, + 0x53, 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, + 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x38, 0x36, 0x34, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x38, 0x31, 0x36, 0x38, 0x7d, 0x7d, 0x2c, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4b, 0x48, 0x52, 0x5f, 0x62, 0x69, 0x6e, + 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x57, 0x45, 0x42, 0x33, 0x44, 0x5f, 0x71, 0x75, 0x61, + 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x5d, 0x2c, + 0x22, 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, + 0x22, 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, + 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x3a, 0x7b, 0x22, + 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, + 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, + 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, + 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, + 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, + 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, + 0x3a, 0x30, 0x7d, 0x7d, 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x65, 0x73, 0x68, + 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, + 0x3a, 0x5b, 0x7b, 0x22, 0x65, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, + 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, + 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x69, + 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, + 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, + 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x69, 0x6c, + 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x50, 0x61, 0x69, + 0x72, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x30, 0x53, + 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x7d, 0x7d, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, + 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x2c, 0x22, 0x73, 0x75, 0x72, 0x66, 0x61, + 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, + 0x22, 0x3a, 0x31, 0x7d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, + 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, + 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x31, + 0x34, 0x36, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x44, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x65, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x68, + 0x61, 0x73, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, + 0x65, 0x2c, 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, + 0x61, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, + 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, + 0x36, 0x2e, 0x31, 0x30, 0x34, 0x35, 0x32, 0x34, 0x32, 0x39, 0x39, 0x39, 0x39, 0x32, 0x33, 0x37, 0x30, 0x30, 0x65, 0x2d, + 0x30, 0x35, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x36, + 0x2e, 0x31, 0x30, 0x34, 0x35, 0x32, 0x34, 0x32, 0x39, 0x39, 0x39, 0x39, 0x32, 0x33, 0x37, 0x30, 0x30, 0x65, 0x2d, 0x30, + 0x35, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x39, 0x2e, + 0x31, 0x35, 0x36, 0x37, 0x38, 0x36, 0x34, 0x34, 0x39, 0x39, 0x38, 0x38, 0x35, 0x35, 0x35, 0x30, 0x65, 0x2d, 0x30, 0x35, + 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x2d, 0x32, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x37, 0x2c, 0x2d, 0x32, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x37, 0x2c, 0x2d, 0x33, 0x2e, 0x30, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x38, 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, + 0x78, 0x22, 0x3a, 0x5b, 0x32, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x37, 0x2c, 0x32, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x37, 0x2c, 0x33, 0x2e, 0x30, 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, + 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x32, 0x2e, 0x30, + 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x2d, 0x32, 0x2e, 0x30, + 0x30, 0x30, 0x32, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x37, 0x2c, 0x2d, 0x33, 0x2e, 0x30, + 0x30, 0x30, 0x34, 0x34, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x5d, 0x7d, 0x2c, 0x22, 0x75, + 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x35, 0x38, 0x34, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x6e, 0x6f, 0x64, + 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4e, 0x6f, 0x64, 0x65, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x3a, 0x22, 0x4d, 0x65, + 0x73, 0x68, 0x5f, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x22, 0x64, + 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x73, + 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x7b, 0x22, + 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x5d, 0x7d, + 0x7d, 0x7d, 0x0a, 0x20, 0xf8, 0xff, 0xfe, 0x7f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0xfe, 0x7f, 0xfe, 0x7f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x07, 0xfe, 0xc5, 0x69, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x41, 0xf8, 0x38, 0x54, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xd3, 0xee, 0x01, 0x40, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0x07, 0xe2, 0xba, 0x2d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0x41, 0xd2, 0xf4, 0x1d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xfb, 0xbf, 0x29, 0x11, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xc3, 0xab, 0xbb, 0x07, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x37, 0x96, 0xf5, 0x01, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0xfe, 0x7f, 0x03, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0xc5, 0x69, 0xf5, 0x01, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x38, 0x54, 0xbb, 0x07, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x01, 0x40, 0x29, 0x11, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xba, 0x2d, 0xf4, 0x1d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0xf4, 0x1d, 0xba, 0x2d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0x29, 0x11, 0x01, 0x40, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xbb, 0x07, 0x38, 0x54, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xf5, 0x01, 0xc5, 0x69, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0xf5, 0x01, 0x37, 0x96, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0xbb, 0x07, 0xc3, 0xab, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x29, 0x11, 0xfb, 0xbf, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xf4, 0x1d, 0x41, 0xd2, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xba, 0x2d, 0x07, 0xe2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0x01, 0x40, 0xd3, 0xee, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0x38, 0x54, 0x41, 0xf8, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xc5, 0x69, 0x07, 0xfe, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xfe, 0x7f, 0xf8, 0xff, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x37, 0x96, 0x07, 0xfe, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0xc3, 0xab, 0x41, 0xf8, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0xfb, 0xbf, 0xd3, 0xee, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x41, 0xd2, 0x07, 0xe2, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x07, 0xe2, 0x41, 0xd2, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xd3, 0xee, 0xfb, 0xbf, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0xcc, 0xcc, 0x41, 0xf8, 0xc3, 0xab, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, + 0x07, 0xfe, 0x37, 0x96, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xf8, 0xff, 0xfe, 0x7f, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xfe, 0x7f, 0xfe, 0x7f, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x07, 0xfe, 0x37, 0x96, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0x41, 0xf8, 0xc3, 0xab, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0xd3, 0xee, 0xfb, 0xbf, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x07, 0xe2, 0x41, 0xd2, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x41, 0xd2, 0x07, 0xe2, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xfb, 0xbf, 0xd3, 0xee, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xc3, 0xab, 0x41, 0xf8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0x37, 0x96, 0x07, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xfe, 0x7f, 0xf8, 0xff, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xc5, 0x69, 0x07, 0xfe, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x38, 0x54, 0x41, 0xf8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0x01, 0x40, 0xd3, 0xee, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0xba, 0x2d, 0x07, 0xe2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xf4, 0x1d, 0x41, 0xd2, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x29, 0x11, 0xfb, 0xbf, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xbb, 0x07, 0xc3, 0xab, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xf5, 0x01, 0x37, 0x96, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0x03, 0x00, 0xfe, 0x7f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xf5, 0x01, 0xc5, 0x69, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xbb, 0x07, 0x38, 0x54, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x29, 0x11, 0x01, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xf4, 0x1d, 0xba, 0x2d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0xba, 0x2d, 0xf4, 0x1d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x01, 0x40, 0x29, 0x11, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x38, 0x54, 0xbb, 0x07, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xc5, 0x69, 0xf5, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xfe, 0x7f, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0x37, 0x96, 0xf5, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xc3, 0xab, 0xbb, 0x07, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xfb, 0xbf, 0x29, 0x11, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x41, 0xd2, 0xf4, 0x1d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0x07, 0xe2, 0xba, 0x2d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, + 0xd3, 0xee, 0x01, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x41, 0xf8, 0x38, 0x54, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x07, 0xfe, 0xc5, 0x69, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xf8, 0xff, 0xfe, 0x7f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x80, 0xcc, 0xcc, 0xf8, 0xff, 0xfe, 0x7f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x80, 0xcc, 0xcc, + 0x07, 0xfe, 0x37, 0x96, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x93, 0xcc, 0xcc, 0x07, 0xfe, 0x37, 0x96, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x93, 0xcc, 0xcc, 0x41, 0xf8, 0xc3, 0xab, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdd, 0xa2, 0xcc, 0xcc, 0x41, 0xf8, 0xc3, 0xab, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0xa2, 0xcc, 0xcc, 0xd3, 0xee, 0xfb, 0xbf, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xae, 0xcc, 0xcc, + 0xd3, 0xee, 0xfb, 0xbf, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xae, 0xcc, 0xcc, 0x07, 0xe2, 0x41, 0xd2, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0xba, 0xcc, 0xcc, 0x07, 0xe2, 0x41, 0xd2, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc5, 0xba, 0xcc, 0xcc, 0x41, 0xd2, 0x07, 0xe2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xba, 0xc5, 0xcc, 0xcc, 0x41, 0xd2, 0x07, 0xe2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xc5, 0xcc, 0xcc, + 0xfb, 0xbf, 0xd3, 0xee, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0xd0, 0xcc, 0xcc, 0xfb, 0xbf, 0xd3, 0xee, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0xd0, 0xcc, 0xcc, 0xc3, 0xab, 0x41, 0xf8, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa2, 0xdd, 0xcc, 0xcc, 0xc3, 0xab, 0x41, 0xf8, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa2, 0xdd, 0xcc, 0xcc, 0x37, 0x96, 0x07, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0xec, 0xcc, 0xcc, + 0x37, 0x96, 0x07, 0xfe, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0xec, 0xcc, 0xcc, 0xfe, 0x7f, 0xf8, 0xff, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xcc, 0xcc, 0xfe, 0x7f, 0xf8, 0xff, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xcc, 0xcc, 0xc5, 0x69, 0x07, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0xec, 0xcc, 0xcc, 0xc5, 0x69, 0x07, 0xfe, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xec, 0xcc, 0xcc, + 0x38, 0x54, 0x41, 0xf8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xdd, 0xcc, 0xcc, 0x38, 0x54, 0x41, 0xf8, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xdd, 0xcc, 0xcc, 0x01, 0x40, 0xd3, 0xee, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0xd0, 0xcc, 0xcc, 0x01, 0x40, 0xd3, 0xee, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0xd0, 0xcc, 0xcc, 0xba, 0x2d, 0x07, 0xe2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xc5, 0xcc, 0xcc, + 0xba, 0x2d, 0x07, 0xe2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xc5, 0xcc, 0xcc, 0xf4, 0x1d, 0x41, 0xd2, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0xba, 0xcc, 0xcc, 0xf4, 0x1d, 0x41, 0xd2, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3a, 0xba, 0xcc, 0xcc, 0x29, 0x11, 0xfb, 0xbf, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2f, 0xae, 0xcc, 0xcc, 0x29, 0x11, 0xfb, 0xbf, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0xae, 0xcc, 0xcc, + 0xbb, 0x07, 0xc3, 0xab, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xa2, 0xcc, 0xcc, 0xbb, 0x07, 0xc3, 0xab, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xa2, 0xcc, 0xcc, 0xf5, 0x01, 0x37, 0x96, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x93, 0xcc, 0xcc, 0xf5, 0x01, 0x37, 0x96, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x13, 0x93, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcc, 0xcc, + 0x03, 0x00, 0xfe, 0x7f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcc, 0xcc, 0xf5, 0x01, 0xc5, 0x69, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x6c, 0xcc, 0xcc, 0xf5, 0x01, 0xc5, 0x69, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x6c, 0xcc, 0xcc, 0xbb, 0x07, 0x38, 0x54, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x5d, 0xcc, 0xcc, 0xbb, 0x07, 0x38, 0x54, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x5d, 0xcc, 0xcc, + 0x29, 0x11, 0x01, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x51, 0xcc, 0xcc, 0x29, 0x11, 0x01, 0x40, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x51, 0xcc, 0xcc, 0xf4, 0x1d, 0xba, 0x2d, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3a, 0x45, 0xcc, 0xcc, 0xf4, 0x1d, 0xba, 0x2d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x45, 0xcc, 0xcc, 0xba, 0x2d, 0xf4, 0x1d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x3a, 0xcc, 0xcc, + 0xba, 0x2d, 0xf4, 0x1d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x3a, 0xcc, 0xcc, 0x01, 0x40, 0x29, 0x11, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x2f, 0xcc, 0xcc, 0x01, 0x40, 0x29, 0x11, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x2f, 0xcc, 0xcc, 0x38, 0x54, 0xbb, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5d, 0x22, 0xcc, 0xcc, 0x38, 0x54, 0xbb, 0x07, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x22, 0xcc, 0xcc, + 0xc5, 0x69, 0xf5, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x13, 0xcc, 0xcc, 0xc5, 0x69, 0xf5, 0x01, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x13, 0xcc, 0xcc, 0xfe, 0x7f, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0xcc, 0xcc, 0xfe, 0x7f, 0x03, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0xcc, 0xcc, 0x37, 0x96, 0xf5, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x13, 0xcc, 0xcc, + 0x37, 0x96, 0xf5, 0x01, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x13, 0xcc, 0xcc, 0xc3, 0xab, 0xbb, 0x07, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0x22, 0xcc, 0xcc, 0xc3, 0xab, 0xbb, 0x07, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa2, 0x22, 0xcc, 0xcc, 0xfb, 0xbf, 0x29, 0x11, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xae, 0x2f, 0xcc, 0xcc, 0xfb, 0xbf, 0x29, 0x11, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0x2f, 0xcc, 0xcc, + 0x41, 0xd2, 0xf4, 0x1d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x3a, 0xcc, 0xcc, 0x41, 0xd2, 0xf4, 0x1d, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x3a, 0xcc, 0xcc, 0x07, 0xe2, 0xba, 0x2d, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc5, 0x45, 0xcc, 0xcc, 0x07, 0xe2, 0xba, 0x2d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc5, 0x45, 0xcc, 0xcc, 0xd3, 0xee, 0x01, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x51, 0xcc, 0xcc, + 0xd3, 0xee, 0x01, 0x40, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x51, 0xcc, 0xcc, 0x41, 0xf8, 0x38, 0x54, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x5d, 0xcc, 0xcc, 0x41, 0xf8, 0x38, 0x54, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdd, 0x5d, 0xcc, 0xcc, 0x07, 0xfe, 0xc5, 0x69, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xec, 0x6c, 0xcc, 0xcc, 0x07, 0xfe, 0xc5, 0x69, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x6c, 0xcc, 0xcc, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x13, + 0x00, 0x00, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x15, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x01, 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1e, 0x00, + 0x00, 0x1e, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x01, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x01, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x26, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x26, 0x00, 0x00, 0x28, + 0x00, 0x00, 0x28, 0x00, 0x00, 0x26, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x26, 0x00, 0x00, 0x2a, 0x00, 0x00, + 0x2a, 0x00, 0x00, 0x26, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x26, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, + 0x00, 0x26, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x26, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x26, + 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x26, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x26, 0x00, 0x00, + 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x26, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x26, 0x00, 0x00, 0x33, 0x00, + 0x00, 0x33, 0x00, 0x00, 0x26, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x26, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, + 0x00, 0x00, 0x26, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x26, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x26, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x26, 0x00, + 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3c, + 0x00, 0x00, 0x3c, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3e, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x26, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x26, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x26, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x26, + 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x26, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x26, 0x00, 0x00, + 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x26, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x26, 0x00, 0x00, 0x47, 0x00, + 0x00, 0x47, 0x00, 0x00, 0x26, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x26, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, + 0x00, 0x00, 0x26, 0x00, 0x00, 0x25, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4a, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4e, 0x00, + 0x00, 0x4f, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x50, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x50, 0x00, 0x00, 0x51, + 0x00, 0x00, 0x51, 0x00, 0x00, 0x50, 0x00, 0x00, 0x52, 0x00, 0x00, 0x51, 0x00, 0x00, 0x52, 0x00, 0x00, 0x53, 0x00, 0x00, + 0x53, 0x00, 0x00, 0x52, 0x00, 0x00, 0x54, 0x00, 0x00, 0x53, 0x00, 0x00, 0x54, 0x00, 0x00, 0x55, 0x00, 0x00, 0x55, 0x00, + 0x00, 0x54, 0x00, 0x00, 0x56, 0x00, 0x00, 0x55, 0x00, 0x00, 0x56, 0x00, 0x00, 0x57, 0x00, 0x00, 0x57, 0x00, 0x00, 0x56, + 0x00, 0x00, 0x58, 0x00, 0x00, 0x57, 0x00, 0x00, 0x58, 0x00, 0x00, 0x59, 0x00, 0x00, 0x59, 0x00, 0x00, 0x58, 0x00, 0x00, + 0x5a, 0x00, 0x00, 0x59, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5c, 0x00, + 0x00, 0x5b, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x5d, + 0x00, 0x00, 0x5e, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x60, 0x00, 0x00, 0x5f, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x61, 0x00, 0x00, 0x61, 0x00, 0x00, 0x60, 0x00, 0x00, 0x62, 0x00, 0x00, 0x61, 0x00, 0x00, 0x62, 0x00, + 0x00, 0x63, 0x00, 0x00, 0x63, 0x00, 0x00, 0x62, 0x00, 0x00, 0x64, 0x00, 0x00, 0x63, 0x00, 0x00, 0x64, 0x00, 0x00, 0x65, + 0x00, 0x00, 0x65, 0x00, 0x00, 0x64, 0x00, 0x00, 0x66, 0x00, 0x00, 0x65, 0x00, 0x00, 0x66, 0x00, 0x00, 0x67, 0x00, 0x00, + 0x67, 0x00, 0x00, 0x66, 0x00, 0x00, 0x68, 0x00, 0x00, 0x67, 0x00, 0x00, 0x68, 0x00, 0x00, 0x69, 0x00, 0x00, 0x69, 0x00, + 0x00, 0x68, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x69, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6a, + 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x6c, 0x00, 0x00, + 0x6e, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x70, 0x00, + 0x00, 0x6f, 0x00, 0x00, 0x70, 0x00, 0x00, 0x71, 0x00, 0x00, 0x71, 0x00, 0x00, 0x70, 0x00, 0x00, 0x72, 0x00, 0x00, 0x71, + 0x00, 0x00, 0x72, 0x00, 0x00, 0x73, 0x00, 0x00, 0x73, 0x00, 0x00, 0x72, 0x00, 0x00, 0x74, 0x00, 0x00, 0x73, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x75, 0x00, 0x00, 0x75, 0x00, 0x00, 0x74, 0x00, 0x00, 0x76, 0x00, 0x00, 0x75, 0x00, 0x00, 0x76, 0x00, + 0x00, 0x77, 0x00, 0x00, 0x77, 0x00, 0x00, 0x76, 0x00, 0x00, 0x78, 0x00, 0x00, 0x77, 0x00, 0x00, 0x78, 0x00, 0x00, 0x79, + 0x00, 0x00, 0x79, 0x00, 0x00, 0x78, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x79, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x7b, 0x00, 0x00, + 0x7b, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7d, 0x00, + 0x00, 0x7c, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7e, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x82, 0x00, 0x00, 0x81, 0x00, 0x00, 0x82, 0x00, 0x00, 0x83, 0x00, 0x00, 0x83, 0x00, 0x00, 0x82, 0x00, 0x00, 0x84, 0x00, + 0x00, 0x83, 0x00, 0x00, 0x84, 0x00, 0x00, 0x85, 0x00, 0x00, 0x85, 0x00, 0x00, 0x84, 0x00, 0x00, 0x86, 0x00, 0x00, 0x85, + 0x00, 0x00, 0x86, 0x00, 0x00, 0x87, 0x00, 0x00, 0x87, 0x00, 0x00, 0x86, 0x00, 0x00, 0x88, 0x00, 0x00, 0x87, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x89, 0x00, 0x00, 0x89, 0x00, 0x00, 0x88, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x89, 0x00, 0x00, 0x8a, 0x00, + 0x00, 0x8b, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8d, + 0x00, 0x00, 0x8d, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x8f, 0x00, 0x00, + 0x8f, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x90, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x90, 0x00, 0x00, 0x91, 0x00, 0x00, 0x91, 0x00, + 0x00, 0x90, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x91, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x23, 0x00, + 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, + 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x21, 0x00, 0x00, 0x22, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x20, 0x00, 0x00, 0x21, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x21, 0x00, + 0x00, 0x21, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, + 0x1d, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1c, 0x00, + 0x00, 0x1d, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1c, + 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1b, 0x00, 0x00, + 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x19, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x19, 0x00, + 0x00, 0x19, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x18, 0x00, 0x00, 0x19, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x17, 0x00, 0x00, 0x18, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x16, 0x00, 0x00, 0x17, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x17, 0x00, + 0x00, 0x17, 0x00, 0x00, 0x15, 0x00, 0x00, 0x16, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, + 0x00, 0x00, 0x14, 0x00, 0x00, 0x15, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x14, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x12, 0x00, + 0x00, 0x13, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x11, 0x00, 0x00, 0x12, + 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x10, 0x00, 0x00, 0x11, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0f, 0x00, + 0x00, 0x0f, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, + 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0d, 0x00, + 0x00, 0x0d, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, + 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x25, 0x00, 0x00, 0x27, 0x00, 0x00, 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, + 0x00, 0x00, 0x25, 0x00, 0x00, 0x49, 0x00, 0x00, 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, + 0x27, 0x00, 0x00, 0x28, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x29, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x2a, + 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2b, 0x00, 0x00, + 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2b, 0x00, + 0x00, 0x2b, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, + 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2f, 0x00, + 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x30, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x31, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, + 0x31, 0x00, 0x00, 0x32, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, + 0x00, 0x33, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x34, + 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x36, 0x00, 0x00, 0x35, 0x00, + 0x00, 0x35, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x37, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, + 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, + 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, + 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, + 0x3b, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, + 0x00, 0x3d, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3e, + 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3f, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f, 0x00, + 0x00, 0x3f, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x41, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x42, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, + 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x43, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x43, 0x00, + 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x44, 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, + 0x00, 0x00, 0x44, 0x00, 0x00, 0x45, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, + 0x45, 0x00, 0x00, 0x46, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, + 0x00, 0x47, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x48, + 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x49, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x24, 0x00, 0x00, 0x01, 0x24, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, + 0x24, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x01, 0x24, 0x00, 0x00, 0x01, 0x23, 0x00, 0x00, 0x02, + 0x23, 0x00, 0x00, 0x03, 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x02, 0x23, 0x00, 0x00, 0x01, 0x23, 0x00, 0x00, 0x01, + 0x22, 0x00, 0x00, 0x02, 0x22, 0x00, 0x00, 0x03, 0x22, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x02, 0x22, 0x00, 0x00, 0x01, + 0x22, 0x00, 0x00, 0x01, 0x21, 0x00, 0x00, 0x02, 0x21, 0x00, 0x00, 0x03, 0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x02, + 0x21, 0x00, 0x00, 0x01, 0x21, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x01, 0x1f, 0x00, 0x00, 0x02, 0x1f, 0x00, 0x00, 0x03, + 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x02, 0x1f, 0x00, 0x00, 0x01, 0x1f, 0x00, 0x00, 0x01, 0x1e, 0x00, 0x00, 0x02, + 0x1e, 0x00, 0x00, 0x03, 0x1e, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x02, 0x1e, 0x00, 0x00, 0x01, 0x1e, 0x00, 0x00, 0x01, + 0x1d, 0x00, 0x00, 0x02, 0x1d, 0x00, 0x00, 0x03, 0x1d, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x02, 0x1d, 0x00, 0x00, 0x01, + 0x1d, 0x00, 0x00, 0x01, 0x1c, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x03, 0x1c, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x02, + 0x1c, 0x00, 0x00, 0x01, 0x1c, 0x00, 0x00, 0x01, 0x1b, 0x00, 0x00, 0x02, 0x1b, 0x00, 0x00, 0x03, 0x1b, 0x00, 0x00, 0x00, + 0x1a, 0x00, 0x00, 0x02, 0x1b, 0x00, 0x00, 0x01, 0x1b, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x00, 0x02, 0x1a, 0x00, 0x00, 0x03, + 0x1a, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x02, 0x1a, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x00, 0x01, 0x19, 0x00, 0x00, 0x02, + 0x19, 0x00, 0x00, 0x03, 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x02, 0x19, 0x00, 0x00, 0x01, 0x19, 0x00, 0x00, 0x01, + 0x18, 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x03, 0x18, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x01, + 0x18, 0x00, 0x00, 0x01, 0x17, 0x00, 0x00, 0x02, 0x17, 0x00, 0x00, 0x03, 0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x02, + 0x17, 0x00, 0x00, 0x01, 0x17, 0x00, 0x00, 0x01, 0x16, 0x00, 0x00, 0x02, 0x16, 0x00, 0x00, 0x03, 0x16, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x02, 0x16, 0x00, 0x00, 0x01, 0x16, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00, 0x02, 0x15, 0x00, 0x00, 0x03, + 0x15, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x02, 0x15, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x02, + 0x14, 0x00, 0x00, 0x03, 0x14, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x01, + 0x13, 0x00, 0x00, 0x02, 0x13, 0x00, 0x00, 0x03, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x02, 0x13, 0x00, 0x00, 0x01, + 0x13, 0x00, 0x00, 0x01, 0x12, 0x00, 0x00, 0x02, 0x12, 0x00, 0x00, 0x03, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x02, + 0x12, 0x00, 0x00, 0x01, 0x12, 0x00, 0x00, 0x01, 0x11, 0x00, 0x00, 0x02, 0x11, 0x00, 0x00, 0x03, 0x11, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x02, 0x11, 0x00, 0x00, 0x01, 0x11, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x03, + 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x01, 0x0f, 0x00, 0x00, 0x02, + 0x0f, 0x00, 0x00, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x02, 0x0f, 0x00, 0x00, 0x01, 0x0f, 0x00, 0x00, 0x01, + 0x0e, 0x00, 0x00, 0x02, 0x0e, 0x00, 0x00, 0x03, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x02, 0x0e, 0x00, 0x00, 0x01, + 0x0e, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x00, 0x02, 0x0d, 0x00, 0x00, 0x03, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x02, + 0x0d, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x03, 0x0c, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x00, 0x03, + 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x00, 0x02, + 0x0a, 0x00, 0x00, 0x03, 0x0a, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x00, 0x01, + 0x09, 0x00, 0x00, 0x02, 0x09, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x02, 0x09, 0x00, 0x00, 0x01, + 0x09, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x02, + 0x08, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x03, + 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x02, + 0x05, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, + 0x04, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x01, + 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x03, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x03, 0x27, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x02, 0x27, 0x00, 0x00, 0x01, 0x27, 0x00, 0x00, 0x01, 0x25, 0x00, 0x00, 0x02, 0x25, 0x00, 0x00, 0x03, + 0x49, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x02, 0x49, 0x00, 0x00, 0x01, 0x49, 0x00, 0x00, 0x01, 0x25, 0x00, 0x00, 0x02, + 0x25, 0x00, 0x00, 0x03, 0x28, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x01, 0x28, 0x00, 0x00, 0x01, + 0x27, 0x00, 0x00, 0x02, 0x27, 0x00, 0x00, 0x03, 0x29, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x02, 0x29, 0x00, 0x00, 0x01, + 0x29, 0x00, 0x00, 0x01, 0x28, 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x03, 0x2a, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x02, + 0x2a, 0x00, 0x00, 0x01, 0x2a, 0x00, 0x00, 0x01, 0x29, 0x00, 0x00, 0x02, 0x29, 0x00, 0x00, 0x03, 0x2b, 0x00, 0x00, 0x00, + 0x2a, 0x00, 0x00, 0x02, 0x2b, 0x00, 0x00, 0x01, 0x2b, 0x00, 0x00, 0x01, 0x2a, 0x00, 0x00, 0x02, 0x2a, 0x00, 0x00, 0x03, + 0x2c, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x00, 0x01, 0x2b, 0x00, 0x00, 0x02, + 0x2b, 0x00, 0x00, 0x03, 0x2d, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x02, 0x2d, 0x00, 0x00, 0x01, 0x2d, 0x00, 0x00, 0x01, + 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x03, 0x2e, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x02, 0x2e, 0x00, 0x00, 0x01, + 0x2e, 0x00, 0x00, 0x01, 0x2d, 0x00, 0x00, 0x02, 0x2d, 0x00, 0x00, 0x03, 0x2f, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x02, + 0x2f, 0x00, 0x00, 0x01, 0x2f, 0x00, 0x00, 0x01, 0x2e, 0x00, 0x00, 0x02, 0x2e, 0x00, 0x00, 0x03, 0x30, 0x00, 0x00, 0x00, + 0x2f, 0x00, 0x00, 0x02, 0x30, 0x00, 0x00, 0x01, 0x30, 0x00, 0x00, 0x01, 0x2f, 0x00, 0x00, 0x02, 0x2f, 0x00, 0x00, 0x03, + 0x31, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x02, 0x31, 0x00, 0x00, 0x01, 0x31, 0x00, 0x00, 0x01, 0x30, 0x00, 0x00, 0x02, + 0x30, 0x00, 0x00, 0x03, 0x32, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x02, 0x32, 0x00, 0x00, 0x01, 0x32, 0x00, 0x00, 0x01, + 0x31, 0x00, 0x00, 0x02, 0x31, 0x00, 0x00, 0x03, 0x33, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x02, 0x33, 0x00, 0x00, 0x01, + 0x33, 0x00, 0x00, 0x01, 0x32, 0x00, 0x00, 0x02, 0x32, 0x00, 0x00, 0x03, 0x34, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x02, + 0x34, 0x00, 0x00, 0x01, 0x34, 0x00, 0x00, 0x01, 0x33, 0x00, 0x00, 0x02, 0x33, 0x00, 0x00, 0x03, 0x35, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x02, 0x35, 0x00, 0x00, 0x01, 0x35, 0x00, 0x00, 0x01, 0x34, 0x00, 0x00, 0x02, 0x34, 0x00, 0x00, 0x03, + 0x36, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x02, 0x36, 0x00, 0x00, 0x01, 0x36, 0x00, 0x00, 0x01, 0x35, 0x00, 0x00, 0x02, + 0x35, 0x00, 0x00, 0x03, 0x37, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x02, 0x37, 0x00, 0x00, 0x01, 0x37, 0x00, 0x00, 0x01, + 0x36, 0x00, 0x00, 0x02, 0x36, 0x00, 0x00, 0x03, 0x38, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x02, 0x38, 0x00, 0x00, 0x01, + 0x38, 0x00, 0x00, 0x01, 0x37, 0x00, 0x00, 0x02, 0x37, 0x00, 0x00, 0x03, 0x39, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x02, + 0x39, 0x00, 0x00, 0x01, 0x39, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x02, 0x38, 0x00, 0x00, 0x03, 0x3a, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x00, 0x02, 0x3a, 0x00, 0x00, 0x01, 0x3a, 0x00, 0x00, 0x01, 0x39, 0x00, 0x00, 0x02, 0x39, 0x00, 0x00, 0x03, + 0x3b, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x02, 0x3b, 0x00, 0x00, 0x01, 0x3b, 0x00, 0x00, 0x01, 0x3a, 0x00, 0x00, 0x02, + 0x3a, 0x00, 0x00, 0x03, 0x3c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x02, 0x3c, 0x00, 0x00, 0x01, 0x3c, 0x00, 0x00, 0x01, + 0x3b, 0x00, 0x00, 0x02, 0x3b, 0x00, 0x00, 0x03, 0x3d, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x02, 0x3d, 0x00, 0x00, 0x01, + 0x3d, 0x00, 0x00, 0x01, 0x3c, 0x00, 0x00, 0x02, 0x3c, 0x00, 0x00, 0x03, 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x02, + 0x3e, 0x00, 0x00, 0x01, 0x3e, 0x00, 0x00, 0x01, 0x3d, 0x00, 0x00, 0x02, 0x3d, 0x00, 0x00, 0x03, 0x3f, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x02, 0x3f, 0x00, 0x00, 0x01, 0x3f, 0x00, 0x00, 0x01, 0x3e, 0x00, 0x00, 0x02, 0x3e, 0x00, 0x00, 0x03, + 0x40, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x01, 0x3f, 0x00, 0x00, 0x02, + 0x3f, 0x00, 0x00, 0x03, 0x41, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0x41, 0x00, 0x00, 0x01, 0x41, 0x00, 0x00, 0x01, + 0x40, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x03, 0x42, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x02, 0x42, 0x00, 0x00, 0x01, + 0x42, 0x00, 0x00, 0x01, 0x41, 0x00, 0x00, 0x02, 0x41, 0x00, 0x00, 0x03, 0x43, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x02, + 0x43, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x01, 0x42, 0x00, 0x00, 0x02, 0x42, 0x00, 0x00, 0x03, 0x44, 0x00, 0x00, 0x00, + 0x43, 0x00, 0x00, 0x02, 0x44, 0x00, 0x00, 0x01, 0x44, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x02, 0x43, 0x00, 0x00, 0x03, + 0x45, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x02, 0x45, 0x00, 0x00, 0x01, 0x45, 0x00, 0x00, 0x01, 0x44, 0x00, 0x00, 0x02, + 0x44, 0x00, 0x00, 0x03, 0x46, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x02, 0x46, 0x00, 0x00, 0x01, 0x46, 0x00, 0x00, 0x01, + 0x45, 0x00, 0x00, 0x02, 0x45, 0x00, 0x00, 0x03, 0x47, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x02, 0x47, 0x00, 0x00, 0x01, + 0x47, 0x00, 0x00, 0x01, 0x46, 0x00, 0x00, 0x02, 0x46, 0x00, 0x00, 0x03, 0x48, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x02, + 0x48, 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, 0x01, 0x47, 0x00, 0x00, 0x02, 0x47, 0x00, 0x00, 0x03, 0x49, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x02, 0x49, 0x00, 0x00, 0x01, 0x49, 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x03, + 0x4a, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4c, 0x00, + 0x00, 0x4d, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x4f, + 0x00, 0x00, 0x4e, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x50, 0x00, 0x00, 0x51, 0x00, 0x00, + 0x50, 0x00, 0x00, 0x50, 0x00, 0x00, 0x51, 0x00, 0x00, 0x51, 0x00, 0x00, 0x52, 0x00, 0x00, 0x53, 0x00, 0x00, 0x52, 0x00, + 0x00, 0x52, 0x00, 0x00, 0x53, 0x00, 0x00, 0x53, 0x00, 0x00, 0x54, 0x00, 0x00, 0x55, 0x00, 0x00, 0x54, 0x00, 0x00, 0x54, + 0x00, 0x00, 0x55, 0x00, 0x00, 0x55, 0x00, 0x00, 0x56, 0x00, 0x00, 0x57, 0x00, 0x00, 0x56, 0x00, 0x00, 0x56, 0x00, 0x00, + 0x57, 0x00, 0x00, 0x57, 0x00, 0x00, 0x58, 0x00, 0x00, 0x59, 0x00, 0x00, 0x58, 0x00, 0x00, 0x58, 0x00, 0x00, 0x59, 0x00, + 0x00, 0x59, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5b, + 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x5d, 0x00, 0x00, + 0x5e, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x61, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x61, 0x00, 0x00, 0x61, 0x00, 0x00, 0x62, 0x00, 0x00, 0x63, + 0x00, 0x00, 0x62, 0x00, 0x00, 0x62, 0x00, 0x00, 0x63, 0x00, 0x00, 0x63, 0x00, 0x00, 0x64, 0x00, 0x00, 0x65, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x64, 0x00, 0x00, 0x65, 0x00, 0x00, 0x65, 0x00, 0x00, 0x66, 0x00, 0x00, 0x67, 0x00, 0x00, 0x66, 0x00, + 0x00, 0x66, 0x00, 0x00, 0x67, 0x00, 0x00, 0x67, 0x00, 0x00, 0x68, 0x00, 0x00, 0x69, 0x00, 0x00, 0x68, 0x00, 0x00, 0x68, + 0x00, 0x00, 0x69, 0x00, 0x00, 0x69, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x6a, 0x00, 0x00, + 0x6b, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6d, 0x00, + 0x00, 0x6d, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x6f, + 0x00, 0x00, 0x70, 0x00, 0x00, 0x71, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x71, 0x00, 0x00, 0x71, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x73, 0x00, 0x00, 0x72, 0x00, 0x00, 0x72, 0x00, 0x00, 0x73, 0x00, 0x00, 0x73, 0x00, 0x00, 0x74, 0x00, + 0x00, 0x75, 0x00, 0x00, 0x74, 0x00, 0x00, 0x74, 0x00, 0x00, 0x75, 0x00, 0x00, 0x75, 0x00, 0x00, 0x76, 0x00, 0x00, 0x77, + 0x00, 0x00, 0x76, 0x00, 0x00, 0x76, 0x00, 0x00, 0x77, 0x00, 0x00, 0x77, 0x00, 0x00, 0x78, 0x00, 0x00, 0x79, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x78, 0x00, 0x00, 0x79, 0x00, 0x00, 0x79, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7a, 0x00, + 0x00, 0x7a, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7c, + 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x81, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x81, 0x00, + 0x00, 0x81, 0x00, 0x00, 0x82, 0x00, 0x00, 0x83, 0x00, 0x00, 0x82, 0x00, 0x00, 0x82, 0x00, 0x00, 0x83, 0x00, 0x00, 0x83, + 0x00, 0x00, 0x84, 0x00, 0x00, 0x85, 0x00, 0x00, 0x84, 0x00, 0x00, 0x84, 0x00, 0x00, 0x85, 0x00, 0x00, 0x85, 0x00, 0x00, + 0x86, 0x00, 0x00, 0x87, 0x00, 0x00, 0x86, 0x00, 0x00, 0x86, 0x00, 0x00, 0x87, 0x00, 0x00, 0x87, 0x00, 0x00, 0x88, 0x00, + 0x00, 0x89, 0x00, 0x00, 0x88, 0x00, 0x00, 0x88, 0x00, 0x00, 0x89, 0x00, 0x00, 0x89, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x8b, + 0x00, 0x00, 0x8a, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8d, 0x00, 0x00, + 0x8c, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x8e, 0x00, + 0x00, 0x8e, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x90, 0x00, 0x00, 0x91, 0x00, 0x00, 0x90, 0x00, 0x00, 0x90, + 0x00, 0x00, 0x91, 0x00, 0x00, 0x91, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x02, 0x4b, 0x00, 0x00, 0x01, + 0x4b, 0x00, 0x00, 0x01, 0x4a, 0x00, 0x00, 0x02, 0x4a, 0x00, 0x00, 0x03, 0x4d, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x02, + 0x4d, 0x00, 0x00, 0x01, 0x4d, 0x00, 0x00, 0x01, 0x4c, 0x00, 0x00, 0x02, 0x4c, 0x00, 0x00, 0x03, 0x4f, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x00, 0x02, 0x4f, 0x00, 0x00, 0x01, 0x4f, 0x00, 0x00, 0x01, 0x4e, 0x00, 0x00, 0x02, 0x4e, 0x00, 0x00, 0x03, + 0x51, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x02, 0x51, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x02, + 0x50, 0x00, 0x00, 0x03, 0x53, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x02, 0x53, 0x00, 0x00, 0x01, 0x53, 0x00, 0x00, 0x01, + 0x52, 0x00, 0x00, 0x02, 0x52, 0x00, 0x00, 0x03, 0x55, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x02, 0x55, 0x00, 0x00, 0x01, + 0x55, 0x00, 0x00, 0x01, 0x54, 0x00, 0x00, 0x02, 0x54, 0x00, 0x00, 0x03, 0x57, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x02, + 0x57, 0x00, 0x00, 0x01, 0x57, 0x00, 0x00, 0x01, 0x56, 0x00, 0x00, 0x02, 0x56, 0x00, 0x00, 0x03, 0x59, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x02, 0x59, 0x00, 0x00, 0x01, 0x59, 0x00, 0x00, 0x01, 0x58, 0x00, 0x00, 0x02, 0x58, 0x00, 0x00, 0x03, + 0x5b, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x02, 0x5b, 0x00, 0x00, 0x01, 0x5b, 0x00, 0x00, 0x01, 0x5a, 0x00, 0x00, 0x02, + 0x5a, 0x00, 0x00, 0x03, 0x5d, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x02, 0x5d, 0x00, 0x00, 0x01, 0x5d, 0x00, 0x00, 0x01, + 0x5c, 0x00, 0x00, 0x02, 0x5c, 0x00, 0x00, 0x03, 0x5f, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x02, 0x5f, 0x00, 0x00, 0x01, + 0x5f, 0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x02, 0x5e, 0x00, 0x00, 0x03, 0x61, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x02, + 0x61, 0x00, 0x00, 0x01, 0x61, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, 0x02, 0x60, 0x00, 0x00, 0x03, 0x63, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x00, 0x02, 0x63, 0x00, 0x00, 0x01, 0x63, 0x00, 0x00, 0x01, 0x62, 0x00, 0x00, 0x02, 0x62, 0x00, 0x00, 0x03, + 0x65, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x02, 0x65, 0x00, 0x00, 0x01, 0x65, 0x00, 0x00, 0x01, 0x64, 0x00, 0x00, 0x02, + 0x64, 0x00, 0x00, 0x03, 0x67, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x02, 0x67, 0x00, 0x00, 0x01, 0x67, 0x00, 0x00, 0x01, + 0x66, 0x00, 0x00, 0x02, 0x66, 0x00, 0x00, 0x03, 0x69, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x69, 0x00, 0x00, 0x01, + 0x69, 0x00, 0x00, 0x01, 0x68, 0x00, 0x00, 0x02, 0x68, 0x00, 0x00, 0x03, 0x6b, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x02, + 0x6b, 0x00, 0x00, 0x01, 0x6b, 0x00, 0x00, 0x01, 0x6a, 0x00, 0x00, 0x02, 0x6a, 0x00, 0x00, 0x03, 0x6d, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x02, 0x6d, 0x00, 0x00, 0x01, 0x6d, 0x00, 0x00, 0x01, 0x6c, 0x00, 0x00, 0x02, 0x6c, 0x00, 0x00, 0x03, + 0x6f, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x02, 0x6f, 0x00, 0x00, 0x01, 0x6f, 0x00, 0x00, 0x01, 0x6e, 0x00, 0x00, 0x02, + 0x6e, 0x00, 0x00, 0x03, 0x71, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x02, 0x71, 0x00, 0x00, 0x01, 0x71, 0x00, 0x00, 0x01, + 0x70, 0x00, 0x00, 0x02, 0x70, 0x00, 0x00, 0x03, 0x73, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x02, 0x73, 0x00, 0x00, 0x01, + 0x73, 0x00, 0x00, 0x01, 0x72, 0x00, 0x00, 0x02, 0x72, 0x00, 0x00, 0x03, 0x75, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x02, + 0x75, 0x00, 0x00, 0x01, 0x75, 0x00, 0x00, 0x01, 0x74, 0x00, 0x00, 0x02, 0x74, 0x00, 0x00, 0x03, 0x77, 0x00, 0x00, 0x00, + 0x76, 0x00, 0x00, 0x02, 0x77, 0x00, 0x00, 0x01, 0x77, 0x00, 0x00, 0x01, 0x76, 0x00, 0x00, 0x02, 0x76, 0x00, 0x00, 0x03, + 0x79, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x02, 0x79, 0x00, 0x00, 0x01, 0x79, 0x00, 0x00, 0x01, 0x78, 0x00, 0x00, 0x02, + 0x78, 0x00, 0x00, 0x03, 0x7b, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x02, 0x7b, 0x00, 0x00, 0x01, 0x7b, 0x00, 0x00, 0x01, + 0x7a, 0x00, 0x00, 0x02, 0x7a, 0x00, 0x00, 0x03, 0x7d, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x02, 0x7d, 0x00, 0x00, 0x01, + 0x7d, 0x00, 0x00, 0x01, 0x7c, 0x00, 0x00, 0x02, 0x7c, 0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x02, + 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x7e, 0x00, 0x00, 0x02, 0x7e, 0x00, 0x00, 0x03, 0x81, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x02, 0x81, 0x00, 0x00, 0x01, 0x81, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x03, + 0x83, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x02, 0x83, 0x00, 0x00, 0x01, 0x83, 0x00, 0x00, 0x01, 0x82, 0x00, 0x00, 0x02, + 0x82, 0x00, 0x00, 0x03, 0x85, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x02, 0x85, 0x00, 0x00, 0x01, 0x85, 0x00, 0x00, 0x01, + 0x84, 0x00, 0x00, 0x02, 0x84, 0x00, 0x00, 0x03, 0x87, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x02, 0x87, 0x00, 0x00, 0x01, + 0x87, 0x00, 0x00, 0x01, 0x86, 0x00, 0x00, 0x02, 0x86, 0x00, 0x00, 0x03, 0x89, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x02, + 0x89, 0x00, 0x00, 0x01, 0x89, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x02, 0x88, 0x00, 0x00, 0x03, 0x8b, 0x00, 0x00, 0x00, + 0x8a, 0x00, 0x00, 0x02, 0x8b, 0x00, 0x00, 0x01, 0x8b, 0x00, 0x00, 0x01, 0x8a, 0x00, 0x00, 0x02, 0x8a, 0x00, 0x00, 0x03, + 0x8d, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x02, 0x8d, 0x00, 0x00, 0x01, 0x8d, 0x00, 0x00, 0x01, 0x8c, 0x00, 0x00, 0x02, + 0x8c, 0x00, 0x00, 0x03, 0x8f, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x02, 0x8f, 0x00, 0x00, 0x01, 0x8f, 0x00, 0x00, 0x01, + 0x8e, 0x00, 0x00, 0x02, 0x8e, 0x00, 0x00, 0x03, 0x91, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x02, 0x91, 0x00, 0x00, 0x01, + 0x91, 0x00, 0x00, 0x01, 0x90, 0x00, 0x00, 0x02, 0x90, 0x00, 0x00, 0x03, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, + 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xe4, 0x9a, + 0xf5, 0x8a, 0xe4, 0x9a, 0xf5, 0x8a, 0xe4, 0x9a, 0xf5, 0x8a, 0xe4, 0x9a, 0xf5, 0x8a, 0xe4, 0x9a, 0xf5, 0x8a, 0xe4, 0x9a, + 0xe4, 0x9a, 0xd6, 0xa8, 0xe4, 0x9a, 0xd6, 0xa8, 0xe4, 0x9a, 0xd6, 0xa8, 0xe4, 0x9a, 0xd6, 0xa8, 0xe4, 0x9a, 0xd6, 0xa8, + 0xe4, 0x9a, 0xd6, 0xa8, 0xd6, 0xa8, 0xca, 0xb4, 0xd6, 0xa8, 0xca, 0xb4, 0xd6, 0xa8, 0xca, 0xb4, 0xd6, 0xa8, 0xca, 0xb4, + 0xd6, 0xa8, 0xca, 0xb4, 0xd6, 0xa8, 0xca, 0xb4, 0xca, 0xb4, 0xbf, 0xbf, 0xca, 0xb4, 0xbf, 0xbf, 0xca, 0xb4, 0xbf, 0xbf, + 0xca, 0xb4, 0xbf, 0xbf, 0xca, 0xb4, 0xbf, 0xbf, 0xca, 0xb4, 0xbf, 0xbf, 0xbf, 0xbf, 0xb4, 0xca, 0xbf, 0xbf, 0xb4, 0xca, + 0xbf, 0xbf, 0xb4, 0xca, 0xbf, 0xbf, 0xb4, 0xca, 0xbf, 0xbf, 0xb4, 0xca, 0xbf, 0xbf, 0xb4, 0xca, 0xb4, 0xca, 0xa8, 0xd6, + 0xb4, 0xca, 0xa8, 0xd6, 0xb4, 0xca, 0xa8, 0xd6, 0xb4, 0xca, 0xa8, 0xd6, 0xb4, 0xca, 0xa8, 0xd6, 0xb4, 0xca, 0xa8, 0xd6, + 0xa8, 0xd6, 0x9a, 0xe4, 0xa8, 0xd6, 0x9a, 0xe4, 0xa8, 0xd6, 0x9a, 0xe4, 0xa8, 0xd6, 0x9a, 0xe4, 0xa8, 0xd6, 0x9a, 0xe4, + 0xa8, 0xd6, 0x9a, 0xe4, 0x9a, 0xe4, 0x8a, 0xf5, 0x9a, 0xe4, 0x8a, 0xf5, 0x9a, 0xe4, 0x8a, 0xf5, 0x9a, 0xe4, 0x8a, 0xf5, + 0x9a, 0xe4, 0x8a, 0xf5, 0x9a, 0xe4, 0x8a, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, + 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x75, 0xf5, 0x65, 0xe4, 0x75, 0xf5, 0x65, 0xe4, + 0x75, 0xf5, 0x65, 0xe4, 0x75, 0xf5, 0x65, 0xe4, 0x75, 0xf5, 0x65, 0xe4, 0x75, 0xf5, 0x65, 0xe4, 0x65, 0xe4, 0x57, 0xd6, + 0x65, 0xe4, 0x57, 0xd6, 0x65, 0xe4, 0x57, 0xd6, 0x65, 0xe4, 0x57, 0xd6, 0x65, 0xe4, 0x57, 0xd6, 0x65, 0xe4, 0x57, 0xd6, + 0x57, 0xd6, 0x4b, 0xca, 0x57, 0xd6, 0x4b, 0xca, 0x57, 0xd6, 0x4b, 0xca, 0x57, 0xd6, 0x4b, 0xca, 0x57, 0xd6, 0x4b, 0xca, + 0x57, 0xd6, 0x4b, 0xca, 0x4b, 0xca, 0x40, 0xbf, 0x4b, 0xca, 0x40, 0xbf, 0x4b, 0xca, 0x40, 0xbf, 0x4b, 0xca, 0x40, 0xbf, + 0x4b, 0xca, 0x40, 0xbf, 0x4b, 0xca, 0x40, 0xbf, 0x40, 0xbf, 0x35, 0xb4, 0x40, 0xbf, 0x35, 0xb4, 0x40, 0xbf, 0x35, 0xb4, + 0x40, 0xbf, 0x35, 0xb4, 0x40, 0xbf, 0x35, 0xb4, 0x40, 0xbf, 0x35, 0xb4, 0x35, 0xb4, 0x29, 0xa8, 0x35, 0xb4, 0x29, 0xa8, + 0x35, 0xb4, 0x29, 0xa8, 0x35, 0xb4, 0x29, 0xa8, 0x35, 0xb4, 0x29, 0xa8, 0x35, 0xb4, 0x29, 0xa8, 0x29, 0xa8, 0x1b, 0x9a, + 0x29, 0xa8, 0x1b, 0x9a, 0x29, 0xa8, 0x1b, 0x9a, 0x29, 0xa8, 0x1b, 0x9a, 0x29, 0xa8, 0x1b, 0x9a, 0x29, 0xa8, 0x1b, 0x9a, + 0x1b, 0x9a, 0x0a, 0x8a, 0x1b, 0x9a, 0x0a, 0x8a, 0x1b, 0x9a, 0x0a, 0x8a, 0x1b, 0x9a, 0x0a, 0x8a, 0x1b, 0x9a, 0x0a, 0x8a, + 0x1b, 0x9a, 0x0a, 0x8a, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, + 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x75, 0x1b, 0x65, 0x0a, 0x75, 0x1b, 0x65, 0x0a, 0x75, 0x1b, 0x65, + 0x0a, 0x75, 0x1b, 0x65, 0x0a, 0x75, 0x1b, 0x65, 0x0a, 0x75, 0x1b, 0x65, 0x1b, 0x65, 0x29, 0x57, 0x1b, 0x65, 0x29, 0x57, + 0x1b, 0x65, 0x29, 0x57, 0x1b, 0x65, 0x29, 0x57, 0x1b, 0x65, 0x29, 0x57, 0x1b, 0x65, 0x29, 0x57, 0x29, 0x57, 0x35, 0x4b, + 0x29, 0x57, 0x35, 0x4b, 0x29, 0x57, 0x35, 0x4b, 0x29, 0x57, 0x35, 0x4b, 0x29, 0x57, 0x35, 0x4b, 0x29, 0x57, 0x35, 0x4b, + 0x35, 0x4b, 0x40, 0x40, 0x35, 0x4b, 0x40, 0x40, 0x35, 0x4b, 0x40, 0x40, 0x35, 0x4b, 0x40, 0x40, 0x35, 0x4b, 0x40, 0x40, + 0x35, 0x4b, 0x40, 0x40, 0x40, 0x40, 0x4b, 0x35, 0x40, 0x40, 0x4b, 0x35, 0x40, 0x40, 0x4b, 0x35, 0x40, 0x40, 0x4b, 0x35, + 0x40, 0x40, 0x4b, 0x35, 0x40, 0x40, 0x4b, 0x35, 0x4b, 0x35, 0x57, 0x29, 0x4b, 0x35, 0x57, 0x29, 0x4b, 0x35, 0x57, 0x29, + 0x4b, 0x35, 0x57, 0x29, 0x4b, 0x35, 0x57, 0x29, 0x4b, 0x35, 0x57, 0x29, 0x57, 0x29, 0x65, 0x1b, 0x57, 0x29, 0x65, 0x1b, + 0x57, 0x29, 0x65, 0x1b, 0x57, 0x29, 0x65, 0x1b, 0x57, 0x29, 0x65, 0x1b, 0x57, 0x29, 0x65, 0x1b, 0x65, 0x1b, 0x75, 0x0a, + 0x65, 0x1b, 0x75, 0x0a, 0x65, 0x1b, 0x75, 0x0a, 0x65, 0x1b, 0x75, 0x0a, 0x65, 0x1b, 0x75, 0x0a, 0x65, 0x1b, 0x75, 0x0a, + 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, + 0x75, 0x0a, 0x8a, 0x0a, 0x8a, 0x0a, 0x9a, 0x1b, 0x8a, 0x0a, 0x9a, 0x1b, 0x8a, 0x0a, 0x9a, 0x1b, 0x8a, 0x0a, 0x9a, 0x1b, + 0x8a, 0x0a, 0x9a, 0x1b, 0x8a, 0x0a, 0x9a, 0x1b, 0x9a, 0x1b, 0xa8, 0x29, 0x9a, 0x1b, 0xa8, 0x29, 0x9a, 0x1b, 0xa8, 0x29, + 0x9a, 0x1b, 0xa8, 0x29, 0x9a, 0x1b, 0xa8, 0x29, 0x9a, 0x1b, 0xa8, 0x29, 0xa8, 0x29, 0xb4, 0x35, 0xa8, 0x29, 0xb4, 0x35, + 0xa8, 0x29, 0xb4, 0x35, 0xa8, 0x29, 0xb4, 0x35, 0xa8, 0x29, 0xb4, 0x35, 0xa8, 0x29, 0xb4, 0x35, 0xb4, 0x35, 0xbf, 0x40, + 0xb4, 0x35, 0xbf, 0x40, 0xb4, 0x35, 0xbf, 0x40, 0xb4, 0x35, 0xbf, 0x40, 0xb4, 0x35, 0xbf, 0x40, 0xb4, 0x35, 0xbf, 0x40, + 0xbf, 0x40, 0xca, 0x4b, 0xbf, 0x40, 0xca, 0x4b, 0xbf, 0x40, 0xca, 0x4b, 0xbf, 0x40, 0xca, 0x4b, 0xbf, 0x40, 0xca, 0x4b, + 0xbf, 0x40, 0xca, 0x4b, 0xca, 0x4b, 0xd6, 0x57, 0xca, 0x4b, 0xd6, 0x57, 0xca, 0x4b, 0xd6, 0x57, 0xca, 0x4b, 0xd6, 0x57, + 0xca, 0x4b, 0xd6, 0x57, 0xca, 0x4b, 0xd6, 0x57, 0xd6, 0x57, 0xe4, 0x65, 0xd6, 0x57, 0xe4, 0x65, 0xd6, 0x57, 0xe4, 0x65, + 0xd6, 0x57, 0xe4, 0x65, 0xd6, 0x57, 0xe4, 0x65, 0xd6, 0x57, 0xe4, 0x65, 0xe4, 0x65, 0xf5, 0x75, 0xe4, 0x65, 0xf5, 0x75, + 0xe4, 0x65, 0xf5, 0x75, 0xe4, 0x65, 0xf5, 0x75, 0xe4, 0x65, 0xf5, 0x75, 0xe4, 0x65, 0xf5, 0x75, + ]), + }, +}; diff --git a/test-apps/testbed/frontend/TileIO.data.fake.ts b/test-apps/testbed/frontend/TileIO.data.fake.ts new file mode 100644 index 0000000..32469f7 --- /dev/null +++ b/test-apps/testbed/frontend/TileIO.data.fake.ts @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { TileTestData, TileTestCase } from "./TileIO.data"; +import { IModelTileIO } from "@bentley/imodeljs-frontend/lib/tile"; + +type TestCaseName = "rectangle" | "triangles" | "lineString" | "lineStrings" | "cylinder"; + +function changeVersionInPlace(bytes: Uint8Array, versionMajor?: number, versionMinor?: number): void { + if (undefined !== versionMinor) { + bytes[4] = (versionMinor & 0x00ff); + bytes[5] = (versionMinor & 0xff00) >> 8; + } + + if (undefined !== versionMajor) { + bytes[6] = (versionMajor & 0x00ff); + bytes[7] = (versionMajor & 0xff00) >> 8; + } +} + +function changeTestCaseVersion(src: TileTestCase, majorVersion?: number, minorVersion?: number): TileTestCase { + const bytes = new Uint8Array(src.bytes); + changeVersionInPlace(bytes, majorVersion, minorVersion); + return { + flags: src.flags, + bytes, + }; +} + +function changeVersion(src: TileTestData, versionMajor?: number, versionMinor?: number): TileTestData { + const dst: TileTestData = { + versionMajor: undefined !== versionMajor ? versionMajor : src.versionMajor, + versionMinor: undefined !== versionMinor ? versionMinor : src.versionMinor, + headerLength: src.headerLength, + rectangle: changeTestCaseVersion(src.rectangle, versionMajor, versionMinor), + lineString: changeTestCaseVersion(src.lineString, versionMajor, versionMinor), + lineStrings: changeTestCaseVersion(src.lineStrings, versionMajor, versionMinor), + triangles: changeTestCaseVersion(src.triangles, versionMajor, versionMinor), + cylinder: changeTestCaseVersion(src.cylinder, versionMajor, versionMinor), + }; + + dst.unreadable = (dst.versionMajor > IModelTileIO.CurrentVersion.Major) ? true : undefined; + return dst; +} + +// Make a copy of the input test data that differs only in the minor version number. +export function changeMinorVersion(src: TileTestData, versionMinor: number): TileTestData { + return changeVersion(src, undefined, versionMinor); +} + +// Make a copy of the input test data that differs only in the major version number. +export function changeMajorVersion(src: TileTestData, versionMajor: number): TileTestData { + return changeVersion(src, versionMajor); +} + +function changeHeaderLengthInPlace(bytes: Uint8Array, data: TileTestData, numPaddingBytes: number): void { + // header length is 32-bit little-endian integer beginning at index 8. + const headerLength = data.headerLength + numPaddingBytes; + bytes[8] = (headerLength & 0xff); + bytes[9] = (headerLength & 0xff00) >>> 8; + bytes[10] = (headerLength & 0xff0000) >>> 0x10; + bytes[11] = (headerLength & 0xff000000) >>> 0x18; + + // tile length is 32-bit little-endian integer beginning at index 80 + const tileLength = ((bytes[80] | (bytes[81] << 8) | (bytes[82] << 0x10) | (bytes[83] << 0x18)) >>> 0) + numPaddingBytes; + bytes[80] = (tileLength & 0xff); + bytes[81] = (tileLength & 0xff00) >>> 8; + bytes[82] = (tileLength & 0xff0000) >>> 0x10; + bytes[83] = (tileLength & 0xff000000) >>> 0x18; +} + +function padTestCaseHeader(data: TileTestData, testCase: TestCaseName, minorVersion: number, numPaddingBytes: number): TileTestCase { + const src = data[testCase]; + const bytes = new Uint8Array(src.bytes.length + numPaddingBytes); + for (let i = 0; i < data.headerLength; i++) + bytes[i] = src.bytes[i]; + + for (let i = 0; i < numPaddingBytes; i++) + bytes[data.headerLength + i] = Math.floor(Math.random() * 0xff); + + for (let i = data.headerLength; i < src.bytes.length; i++) + bytes[i + numPaddingBytes] = src.bytes[i]; + + changeVersionInPlace(bytes, undefined, minorVersion); + changeHeaderLengthInPlace(bytes, data, numPaddingBytes); + + return { + flags: src.flags, + bytes, + }; +} + +// Make a copy of the input test data that differs only in the minor version number and in the header length (appends extra bytes to header) +export function changeHeaderLength(src: TileTestData, versionMinor: number, numPaddingBytes: number): TileTestData { + return { + versionMajor: src.versionMajor, + versionMinor, + headerLength: src.headerLength + numPaddingBytes, + rectangle: padTestCaseHeader(src, "rectangle", versionMinor, numPaddingBytes), + triangles: padTestCaseHeader(src, "triangles", versionMinor, numPaddingBytes), + lineString: padTestCaseHeader(src, "lineString", versionMinor, numPaddingBytes), + lineStrings: padTestCaseHeader(src, "lineStrings", versionMinor, numPaddingBytes), + cylinder: padTestCaseHeader(src, "cylinder", versionMinor, numPaddingBytes), + }; +} diff --git a/test-apps/testbed/frontend/TileIO.data.ts b/test-apps/testbed/frontend/TileIO.data.ts index 26b0717..b2cad9a 100644 --- a/test-apps/testbed/frontend/TileIO.data.ts +++ b/test-apps/testbed/frontend/TileIO.data.ts @@ -3,1972 +3,47 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -// This file contains binary data for element tiles, to be used in TileIO tests. -// All of these tiles are 'leaf' tiles (have no children). +import { IModelTileIO } from "@bentley/imodeljs-frontend/lib/tile"; -export namespace TileData { - // Binary data for a tile created for a model containing a single element: a green rectangle in the range [0, 0] to [5, 10] - // A single element: a green rectangle in the range [0, 0] to [5, 10] - export const rectangle = new Uint8Array([ - 0x69, 0x4d, 0x64, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa3, 0xb4, 0x37, 0xa9, 0x41, 0x00, 0x04, 0xc0, - 0xa3, 0xb4, 0x37, 0xa9, 0x41, 0x00, 0x14, 0xc0, 0x00, 0x00, 0x21, 0xb0, 0xf2, 0xd7, 0x5a, 0xbe, 0x5d, 0x4b, 0xc8, 0x56, - 0xbe, 0xff, 0x03, 0x40, 0x5d, 0x4b, 0xc8, 0x56, 0xbe, 0xff, 0x13, 0x40, 0x00, 0x00, 0x21, 0xb0, 0xf2, 0xd7, 0x5a, 0xbe, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x0a, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, - 0x02, 0x00, 0x00, 0x00, 0x80, 0x0a, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, - 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x44, 0x69, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, - 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, - 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x36, 0x2c, 0x22, - 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x34, 0x39, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, - 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, - 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, - 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x36, 0x34, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, - 0x22, 0x3a, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, - 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, - 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, - 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x36, - 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x35, 0x36, 0x7d, 0x2c, 0x22, - 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, - 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, - 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, - 0x22, 0x3a, 0x37, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x32, 0x35, - 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, - 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, - 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, - 0x3a, 0x37, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x38, 0x34, 0x7d, - 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, - 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, - 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x38, - 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x36, 0x34, 0x7d, 0x2c, 0x22, 0x62, - 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, - 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, - 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, - 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x33, 0x39, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, - 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, - 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, - 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, - 0x22, 0x3a, 0x37, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x33, 0x32, - 0x34, 0x7d, 0x7d, 0x2c, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, - 0x3a, 0x5b, 0x22, 0x4b, 0x48, 0x52, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, - 0x22, 0x57, 0x45, 0x42, 0x33, 0x44, 0x5f, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, 0x6d, 0x61, 0x74, - 0x65, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, - 0x3a, 0x7b, 0x22, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x37, - 0x22, 0x2c, 0x22, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, - 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, - 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, - 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, - 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, - 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x73, 0x75, 0x62, 0x43, 0x61, - 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x38, 0x22, 0x2c, 0x22, 0x74, 0x79, - 0x70, 0x65, 0x22, 0x3a, 0x30, 0x7d, 0x7d, 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, - 0x65, 0x73, 0x68, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, 0x3a, 0x5b, - 0x7b, 0x22, 0x65, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x73, - 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x44, 0x69, - 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, - 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, - 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x6e, - 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, - 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, - 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, - 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, - 0x65, 0x73, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x65, 0x6e, - 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, - 0x22, 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, - 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x2c, 0x22, 0x69, - 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, - 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x7d, 0x7d, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, - 0x22, 0x3a, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, - 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x2c, 0x22, 0x73, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, - 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, - 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x7d, - 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x22, - 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x56, 0x65, - 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x66, 0x65, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x44, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x68, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, 0x56, 0x65, - 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, - 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x37, 0x2e, 0x36, 0x33, 0x30, 0x36, - 0x35, 0x36, 0x31, 0x30, 0x32, 0x37, 0x30, 0x36, 0x34, 0x36, 0x37, 0x33, 0x65, 0x2d, 0x30, 0x35, 0x2c, 0x30, 0x2e, 0x30, - 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x31, 0x35, - 0x32, 0x36, 0x31, 0x33, 0x31, 0x32, 0x32, 0x30, 0x35, 0x34, 0x31, 0x32, 0x39, 0x33, 0x35, 0x2c, 0x30, 0x2e, 0x30, 0x2c, - 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, 0x31, 0x33, 0x31, - 0x31, 0x34, 0x37, 0x34, 0x38, 0x35, 0x34, 0x32, 0x38, 0x37, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x2d, - 0x32, 0x2e, 0x35, 0x30, 0x30, 0x33, 0x37, 0x35, 0x32, 0x33, 0x38, 0x34, 0x35, 0x34, 0x33, 0x34, 0x31, 0x36, 0x2c, 0x2d, - 0x35, 0x2e, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x34, 0x37, 0x36, 0x39, 0x30, 0x38, 0x36, 0x38, 0x33, 0x32, 0x2c, 0x2d, - 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x32, 0x33, 0x37, 0x35, 0x32, 0x32, 0x38, 0x37, - 0x39, 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, - 0x3a, 0x5b, 0x32, 0x2e, 0x35, 0x30, 0x30, 0x33, 0x37, 0x35, 0x32, 0x33, 0x38, 0x34, 0x35, 0x34, 0x33, 0x34, 0x31, 0x36, - 0x2c, 0x35, 0x2e, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x34, 0x37, 0x36, 0x39, 0x30, 0x38, 0x36, 0x38, 0x33, 0x32, 0x2c, - 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x32, 0x33, 0x37, 0x35, 0x32, 0x32, 0x38, 0x37, - 0x39, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x32, - 0x2e, 0x35, 0x30, 0x30, 0x33, 0x37, 0x35, 0x32, 0x33, 0x38, 0x34, 0x35, 0x34, 0x33, 0x34, 0x31, 0x36, 0x2c, 0x2d, 0x35, - 0x2e, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x34, 0x37, 0x36, 0x39, 0x30, 0x38, 0x36, 0x38, 0x33, 0x32, 0x2c, 0x2d, 0x30, - 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x32, 0x33, 0x37, 0x35, 0x32, 0x32, 0x38, 0x37, 0x39, - 0x30, 0x5d, 0x7d, 0x2c, 0x22, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, - 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, - 0x2c, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, - 0x3a, 0x7b, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x22, 0x5d, 0x7d, - 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, - 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x66, 0x61, - 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x5b, - 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x5d, 0x7d, 0x7d, 0x7d, 0x0a, 0x20, 0x03, 0x00, 0x03, 0x00, - 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xf8, 0xff, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xf8, 0xff, 0xf8, 0xff, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x80, 0xcc, 0xcc, 0x03, 0x00, 0xf8, 0xff, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, - 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, - 0x01, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x01, - 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x36, - 0x01, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x1e, - 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x4e, - 0x03, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x36, - 0x02, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x1e, 0x03, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, - 0x03, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x4e, 0x00, 0x00, 0xf0, 0x41, 0xfd, 0xff, 0x0b, 0x42, 0x00, 0x00, 0xf0, 0x41, - 0x00, 0x00, 0xf0, 0x41, 0xfd, 0xff, 0x0b, 0x42, 0xfd, 0xff, 0x0b, 0x42, 0xfd, 0xff, 0x0b, 0x42, 0xf6, 0xff, 0x33, 0x42, - 0xfd, 0xff, 0x0b, 0x42, 0xfd, 0xff, 0x0b, 0x42, 0xf6, 0xff, 0x33, 0x42, 0xf6, 0xff, 0x33, 0x42, 0xf6, 0xff, 0x33, 0x42, - 0xf3, 0xff, 0x47, 0x42, 0xf6, 0xff, 0x33, 0x42, 0xf6, 0xff, 0x33, 0x42, 0xf3, 0xff, 0x47, 0x42, 0xf3, 0xff, 0x47, 0x42, - 0xf3, 0xff, 0x47, 0x42, 0xec, 0xff, 0x6f, 0x42, 0xf3, 0xff, 0x47, 0x42, 0xf3, 0xff, 0x47, 0x42, 0xec, 0xff, 0x6f, 0x42, - 0xec, 0xff, 0x6f, 0x42, - ]); +// Describes a single tile. +export interface TileTestCase { + readonly bytes: Uint8Array; + readonly flags: IModelTileIO.Flags; +} + +// Describes a set of tiles generated for a particular version of the tile format from a set of simple iModels. +export interface TileTestData { + readonly versionMajor: number; + readonly versionMinor: number; + readonly headerLength: number; + unreadable?: true; + + // Binary data for a tile created for a model containing a single element: a green rectangle in the range [0,0] to [5,10] + // A single element: a green rectangle in the range [0,0] to [5,10] + readonly rectangle: TileTestCase; // A single open yellow line string with following points (z==0 for all): - // (0, 10) (0, -10) (5, 0) (5, 10) (15, -10), (15, 0) - export const lineString = new Uint8Array([ - 0x69, 0x4d, 0x64, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0xc3, 0xd3, 0x8d, 0x62, 0x00, 0x1e, 0xc0, - 0xa3, 0xb4, 0x37, 0x89, 0x41, 0x00, 0x24, 0xc0, 0x00, 0x00, 0x21, 0xb0, 0xf2, 0xd7, 0x5a, 0xbe, 0x9e, 0x3c, 0x2c, 0x72, - 0x9d, 0xff, 0x1d, 0x40, 0x5d, 0x4b, 0xc8, 0x76, 0xbe, 0xff, 0x23, 0x40, 0x00, 0x00, 0x21, 0xb0, 0xf2, 0xd7, 0x5a, 0xbe, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0d, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, - 0x02, 0x00, 0x00, 0x00, 0xac, 0x0c, 0x00, 0x00, 0x10, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, - 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x44, 0x69, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, - 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, - 0x68, 0x22, 0x3a, 0x34, 0x35, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, - 0x31, 0x32, 0x31, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x3a, 0x7b, 0x22, - 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, - 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x37, 0x32, 0x2c, 0x22, 0x62, - 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, - 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, - 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, - 0x74, 0x68, 0x22, 0x3a, 0x33, 0x34, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, - 0x3a, 0x37, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, - 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, - 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, - 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x34, 0x35, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, - 0x73, 0x65, 0x74, 0x22, 0x3a, 0x37, 0x36, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, - 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, - 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, - 0x74, 0x68, 0x22, 0x3a, 0x33, 0x34, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, - 0x3a, 0x34, 0x31, 0x36, 0x7d, 0x7d, 0x2c, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, - 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4b, 0x48, 0x52, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, - 0x46, 0x22, 0x2c, 0x22, 0x57, 0x45, 0x42, 0x33, 0x44, 0x5f, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, - 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, - 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, - 0x6c, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, - 0x78, 0x31, 0x37, 0x22, 0x2c, 0x22, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, - 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x35, 0x33, 0x35, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, - 0x61, 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, - 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, - 0x22, 0x3a, 0x36, 0x35, 0x35, 0x33, 0x35, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, - 0x3a, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x2c, 0x22, 0x73, 0x75, - 0x62, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x38, 0x22, 0x2c, - 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x7d, 0x7d, 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, - 0x7b, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, - 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x44, - 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, - 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, - 0x61, 0x72, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, - 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, - 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, - 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, - 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x70, - 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, - 0x31, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, - 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x36, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x44, 0x22, - 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, - 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, - 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, - 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x33, 0x2c, - 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, - 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x32, 0x38, 0x39, 0x31, 0x39, 0x36, 0x39, 0x30, - 0x33, 0x35, 0x38, 0x33, 0x35, 0x34, 0x30, 0x34, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, - 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x33, 0x30, 0x35, 0x32, 0x32, 0x36, 0x32, 0x34, 0x34, 0x31, - 0x30, 0x38, 0x32, 0x35, 0x38, 0x36, 0x39, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, - 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, 0x31, 0x33, 0x31, 0x31, 0x34, 0x37, 0x34, 0x38, 0x35, 0x34, 0x32, - 0x38, 0x37, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x35, - 0x39, 0x35, 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x35, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x35, 0x30, - 0x30, 0x39, 0x35, 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, - 0x30, 0x37, 0x35, 0x30, 0x32, 0x33, 0x37, 0x35, 0x32, 0x32, 0x38, 0x37, 0x39, 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, - 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, - 0x32, 0x35, 0x39, 0x35, 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x35, 0x2c, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x35, - 0x30, 0x30, 0x39, 0x35, 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, - 0x30, 0x37, 0x35, 0x30, 0x32, 0x33, 0x37, 0x35, 0x32, 0x32, 0x38, 0x37, 0x39, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, - 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x35, 0x39, - 0x35, 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x35, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x35, 0x30, 0x30, - 0x39, 0x35, 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, - 0x37, 0x35, 0x30, 0x32, 0x33, 0x37, 0x35, 0x32, 0x32, 0x38, 0x37, 0x39, 0x30, 0x5d, 0x7d, 0x2c, 0x22, 0x75, 0x6e, 0x69, - 0x66, 0x6f, 0x72, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x35, 0x33, 0x35, 0x2c, 0x22, 0x77, 0x69, - 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x38, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, - 0x3a, 0x7b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, - 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x22, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, - 0x22, 0x3a, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, - 0x65, 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, - 0x22, 0x3a, 0x7b, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, - 0x65, 0x22, 0x5d, 0x7d, 0x7d, 0x7d, 0x0a, 0x20, 0x03, 0x00, 0xf8, 0xff, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0xf8, 0xff, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x03, 0x00, - 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, - 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, - 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, - 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, - 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, - 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, - 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, - 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x60, - 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x90, - 0x01, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x90, - 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, - 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0x36, - 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x60, - 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x09, 0x02, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x4e, - 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0e, - 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x1e, - 0x02, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x39, 0x02, 0x00, 0x00, 0x90, - 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x90, - 0x02, 0x00, 0x00, 0x51, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x90, - 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, - 0x04, 0x00, 0x00, 0x21, 0x03, 0x00, 0x00, 0x39, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x39, - 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x09, - 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x51, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x0c, - 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0f, - 0x04, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x90, - 0x03, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x00, 0x0e, - 0x05, 0x00, 0x00, 0x21, 0x04, 0x00, 0x00, 0x33, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x33, - 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x09, 0x05, 0x00, 0x00, 0x09, - 0x04, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x4b, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x0c, - 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0f, - 0x05, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0x9f, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xe6, 0xff, 0x9f, 0x41, 0xe6, 0xff, 0x9f, 0x41, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0x9f, 0x41, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0x9f, 0x41, 0xe6, 0xff, 0x9f, 0x41, 0xe6, 0xff, 0x9f, 0x41, 0xe6, 0xff, 0x9f, 0x41, - 0xe6, 0xff, 0x9f, 0x41, 0xe6, 0xff, 0x9f, 0x41, 0xe6, 0xff, 0x9f, 0x41, 0xe6, 0xff, 0x9f, 0x41, 0xe6, 0xff, 0x9f, 0x41, - 0xe6, 0xff, 0x9f, 0x41, 0xe6, 0xff, 0x9f, 0x41, 0xe6, 0xff, 0x9f, 0x41, 0x86, 0x71, 0xf9, 0x41, 0xe6, 0xff, 0x9f, 0x41, - 0xe6, 0xff, 0x9f, 0x41, 0x86, 0x71, 0xf9, 0x41, 0x86, 0x71, 0xf9, 0x41, 0xe6, 0xff, 0x9f, 0x41, 0x86, 0x71, 0xf9, 0x41, - 0xe6, 0xff, 0x9f, 0x41, 0xe6, 0xff, 0x9f, 0x41, 0x86, 0x71, 0xf9, 0x41, 0x86, 0x71, 0xf9, 0x41, 0xe6, 0xff, 0x9f, 0x41, - 0xe6, 0xff, 0x9f, 0x41, 0xe6, 0xff, 0x9f, 0x41, 0xe6, 0xff, 0x9f, 0x41, 0xe6, 0xff, 0x9f, 0x41, 0xe6, 0xff, 0x9f, 0x41, - 0xe6, 0xff, 0x9f, 0x41, 0xe6, 0xff, 0x9f, 0x41, 0xe6, 0xff, 0x9f, 0x41, 0x86, 0x71, 0xf9, 0x41, 0x95, 0xb8, 0x24, 0x42, - 0x86, 0x71, 0xf9, 0x41, 0x86, 0x71, 0xf9, 0x41, 0x95, 0xb8, 0x24, 0x42, 0x95, 0xb8, 0x24, 0x42, 0x86, 0x71, 0xf9, 0x41, - 0x95, 0xb8, 0x24, 0x42, 0x86, 0x71, 0xf9, 0x41, 0x86, 0x71, 0xf9, 0x41, 0x95, 0xb8, 0x24, 0x42, 0x95, 0xb8, 0x24, 0x42, - 0x95, 0xb8, 0x24, 0x42, 0x95, 0xb8, 0x24, 0x42, 0x95, 0xb8, 0x24, 0x42, 0x95, 0xb8, 0x24, 0x42, 0x95, 0xb8, 0x24, 0x42, - 0x95, 0xb8, 0x24, 0x42, 0x95, 0xb8, 0x24, 0x42, 0x95, 0xb8, 0x24, 0x42, 0x95, 0xb8, 0x24, 0x42, 0x95, 0xb8, 0x24, 0x42, - 0xd3, 0x29, 0x7e, 0x42, 0x95, 0xb8, 0x24, 0x42, 0x95, 0xb8, 0x24, 0x42, 0xd3, 0x29, 0x7e, 0x42, 0xd3, 0x29, 0x7e, 0x42, - 0x95, 0xb8, 0x24, 0x42, 0xd3, 0x29, 0x7e, 0x42, 0x95, 0xb8, 0x24, 0x42, 0x95, 0xb8, 0x24, 0x42, 0xd3, 0x29, 0x7e, 0x42, - 0xd3, 0x29, 0x7e, 0x42, 0x95, 0xb8, 0x24, 0x42, 0x95, 0xb8, 0x24, 0x42, 0x95, 0xb8, 0x24, 0x42, 0x95, 0xb8, 0x24, 0x42, - 0x95, 0xb8, 0x24, 0x42, 0x95, 0xb8, 0x24, 0x42, 0x95, 0xb8, 0x24, 0x42, 0x95, 0xb8, 0x24, 0x42, 0x95, 0xb8, 0x24, 0x42, - 0xd3, 0x29, 0x7e, 0x42, 0xd3, 0x29, 0x7e, 0x42, 0xd3, 0x29, 0x7e, 0x42, 0xd3, 0x29, 0x7e, 0x42, 0xd3, 0x29, 0x7e, 0x42, - 0xd3, 0x29, 0x7e, 0x42, 0xd3, 0x29, 0x7e, 0x42, 0xd3, 0x29, 0x7e, 0x42, 0xd3, 0x29, 0x7e, 0x42, 0xd3, 0x29, 0x7e, 0x42, - 0xfa, 0x14, 0x93, 0x42, 0xd3, 0x29, 0x7e, 0x42, 0xd3, 0x29, 0x7e, 0x42, 0xfa, 0x14, 0x93, 0x42, 0xfa, 0x14, 0x93, 0x42, - 0xd3, 0x29, 0x7e, 0x42, 0xfa, 0x14, 0x93, 0x42, 0xd3, 0x29, 0x7e, 0x42, 0xd3, 0x29, 0x7e, 0x42, 0xfa, 0x14, 0x93, 0x42, - 0xfa, 0x14, 0x93, 0x42, 0xd3, 0x29, 0x7e, 0x42, 0xd3, 0x29, 0x7e, 0x42, 0xd3, 0x29, 0x7e, 0x42, 0xd3, 0x29, 0x7e, 0x42, - 0xd3, 0x29, 0x7e, 0x42, 0xd3, 0x29, 0x7e, 0x42, 0xd3, 0x29, 0x7e, 0x42, 0xd3, 0x29, 0x7e, 0x42, 0xd3, 0x29, 0x7e, 0x42, - ]); + // (0,10) (0,-10) (5,0) (5,10) (15,-10),(15,0) + readonly lineString: TileTestCase; // 3 line strings. The first has same coordinates as lineString above. The second has same coordinates with -10 added to each y. The third same as first with -20 added to y. // First: line code = 2; color = yellow; width = 8 // Second: line code = 2; color = cyan; width = 8 // Third: line code = 0 (solid); color = purple; width = 8 - export const lineStrings = new Uint8Array([ - 0x69, 0x4d, 0x64, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0xc3, 0xd3, 0x8d, 0x62, 0x00, 0x1e, 0xc0, - 0x99, 0xdd, 0xd3, 0x15, 0x62, 0x00, 0x3e, 0xc0, 0x00, 0x00, 0x21, 0xb0, 0xf2, 0xd7, 0x5a, 0xbe, 0x9e, 0x3c, 0x2c, 0x72, - 0x9d, 0xff, 0x1d, 0x40, 0x67, 0x22, 0x2c, 0xea, 0x9d, 0xff, 0x3d, 0x40, 0x00, 0x00, 0x21, 0xb0, 0xf2, 0xd7, 0x5a, 0xbe, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x1f, 0x00, 0x00, - 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, 0x02, 0x00, 0x00, 0x00, 0xa4, 0x1e, 0x00, 0x00, 0xf8, 0x0a, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x3a, 0x7b, - 0x22, 0x62, 0x76, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, - 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, - 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x34, 0x35, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, - 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x32, 0x31, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x44, 0x69, 0x73, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, - 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, - 0x67, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x31, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, - 0x22, 0x3a, 0x34, 0x31, 0x30, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x3a, - 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, - 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x37, 0x32, 0x2c, - 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x56, - 0x65, 0x72, 0x74, 0x65, 0x78, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, - 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, - 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x35, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, - 0x22, 0x3a, 0x31, 0x36, 0x37, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, - 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, - 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x34, - 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x37, 0x32, 0x7d, 0x2c, 0x22, - 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, - 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, - 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x36, 0x38, 0x34, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, - 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x38, 0x32, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, - 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x22, 0x3a, 0x7b, 0x22, - 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, - 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x34, 0x35, 0x36, 0x2c, 0x22, - 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x37, 0x36, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, - 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, - 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, - 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, - 0x39, 0x31, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x33, 0x31, 0x39, - 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x22, 0x3a, - 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, - 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x34, 0x32, - 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x34, 0x31, 0x36, 0x7d, 0x2c, 0x22, - 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, - 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, - 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x36, 0x38, 0x34, 0x2c, 0x22, 0x62, 0x79, - 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x32, 0x35, 0x30, 0x38, 0x7d, 0x7d, 0x2c, 0x22, 0x65, 0x78, - 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4b, 0x48, 0x52, 0x5f, - 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x57, 0x45, 0x42, 0x33, 0x44, 0x5f, - 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, - 0x22, 0x5d, 0x2c, 0x22, 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, - 0x22, 0x3a, 0x5b, 0x22, 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, - 0x78, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, - 0x3a, 0x7b, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x63, 0x61, 0x74, 0x65, - 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x37, 0x22, 0x2c, 0x22, 0x63, 0x6c, 0x61, 0x73, - 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x31, 0x36, 0x37, - 0x31, 0x31, 0x39, 0x33, 0x35, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3a, 0x30, 0x2c, - 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x66, 0x61, 0x6c, - 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x31, 0x36, 0x37, 0x31, 0x31, - 0x39, 0x33, 0x35, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, - 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x39, 0x2c, 0x22, 0x73, 0x75, 0x62, 0x43, 0x61, 0x74, - 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x38, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, - 0x65, 0x22, 0x3a, 0x31, 0x7d, 0x2c, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x31, 0x22, 0x3a, 0x7b, 0x22, - 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x37, 0x22, 0x2c, 0x22, - 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, - 0x3a, 0x31, 0x35, 0x37, 0x39, 0x30, 0x30, 0x38, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, - 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, - 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x31, - 0x35, 0x37, 0x39, 0x30, 0x30, 0x38, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, - 0x3a, 0x34, 0x31, 0x37, 0x37, 0x30, 0x36, 0x36, 0x32, 0x33, 0x32, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, - 0x74, 0x68, 0x22, 0x3a, 0x39, 0x2c, 0x22, 0x73, 0x75, 0x62, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, - 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x38, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x7d, 0x7d, 0x2c, - 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x22, 0x3a, 0x7b, 0x22, 0x70, - 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x22, 0x2c, 0x22, - 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, - 0x22, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, - 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, - 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, - 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, - 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, - 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, - 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x56, - 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x36, 0x2c, 0x22, 0x66, - 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x44, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x68, 0x65, - 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, 0x56, - 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x33, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, 0x22, - 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x30, 0x2e, 0x30, 0x30, 0x30, - 0x32, 0x32, 0x38, 0x39, 0x31, 0x39, 0x36, 0x39, 0x30, 0x33, 0x35, 0x38, 0x33, 0x35, 0x34, 0x30, 0x34, 0x2c, 0x30, 0x2e, - 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x39, - 0x31, 0x35, 0x36, 0x37, 0x38, 0x37, 0x37, 0x35, 0x39, 0x38, 0x37, 0x37, 0x33, 0x36, 0x33, 0x37, 0x2c, 0x30, 0x2e, 0x30, - 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, 0x31, 0x33, - 0x31, 0x31, 0x34, 0x37, 0x34, 0x38, 0x35, 0x34, 0x32, 0x38, 0x37, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x2c, - 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x35, 0x39, 0x35, 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x35, 0x2c, - 0x2d, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x35, 0x30, 0x34, 0x32, 0x39, 0x32, 0x31, 0x37, 0x38, 0x31, 0x35, 0x31, 0x2c, - 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x32, 0x33, 0x37, 0x35, 0x32, 0x32, 0x38, - 0x37, 0x39, 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, - 0x22, 0x3a, 0x5b, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x35, 0x39, 0x35, 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, - 0x35, 0x2c, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x35, 0x30, 0x34, 0x32, 0x39, 0x32, 0x31, 0x37, 0x38, 0x31, 0x35, 0x31, - 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x32, 0x33, 0x37, 0x35, 0x32, 0x32, 0x38, - 0x37, 0x39, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, - 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x35, 0x39, 0x35, 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x35, 0x2c, 0x2d, - 0x33, 0x30, 0x2e, 0x30, 0x30, 0x34, 0x35, 0x30, 0x34, 0x32, 0x39, 0x32, 0x31, 0x37, 0x38, 0x31, 0x35, 0x31, 0x2c, 0x2d, - 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x32, 0x33, 0x37, 0x35, 0x32, 0x32, 0x38, 0x37, - 0x39, 0x30, 0x5d, 0x7d, 0x2c, 0x22, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, - 0x31, 0x36, 0x37, 0x31, 0x31, 0x39, 0x33, 0x35, 0x2c, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x38, 0x7d, - 0x7d, 0x2c, 0x7b, 0x22, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x44, 0x69, - 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x31, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, - 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x22, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, - 0x72, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x3a, - 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x31, 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, - 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, - 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x31, 0x22, - 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x70, 0x72, - 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, - 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, - 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x31, 0x22, 0x2c, 0x22, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x31, 0x32, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x32, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, - 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, - 0x65, 0x78, 0x22, 0x3a, 0x33, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x63, - 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x32, 0x38, - 0x39, 0x31, 0x39, 0x36, 0x39, 0x30, 0x33, 0x35, 0x38, 0x33, 0x35, 0x34, 0x30, 0x34, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, - 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x39, 0x31, 0x35, 0x36, - 0x37, 0x38, 0x37, 0x37, 0x35, 0x39, 0x38, 0x37, 0x37, 0x33, 0x36, 0x33, 0x37, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, - 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, 0x31, 0x33, 0x31, 0x31, 0x34, - 0x37, 0x34, 0x38, 0x35, 0x34, 0x32, 0x38, 0x37, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x2d, 0x37, 0x2e, - 0x35, 0x30, 0x31, 0x31, 0x32, 0x35, 0x39, 0x35, 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x35, 0x2c, 0x2d, 0x33, 0x30, - 0x2e, 0x30, 0x30, 0x34, 0x35, 0x30, 0x34, 0x32, 0x39, 0x32, 0x31, 0x37, 0x38, 0x31, 0x35, 0x31, 0x2c, 0x2d, 0x30, 0x2e, - 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x32, 0x33, 0x37, 0x35, 0x32, 0x32, 0x38, 0x37, 0x39, 0x30, - 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, - 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x35, 0x39, 0x35, 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x35, 0x2c, 0x33, - 0x30, 0x2e, 0x30, 0x30, 0x34, 0x35, 0x30, 0x34, 0x32, 0x39, 0x32, 0x31, 0x37, 0x38, 0x31, 0x35, 0x31, 0x2c, 0x30, 0x2e, - 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x32, 0x33, 0x37, 0x35, 0x32, 0x32, 0x38, 0x37, 0x39, 0x30, - 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x37, 0x2e, 0x35, - 0x30, 0x31, 0x31, 0x32, 0x35, 0x39, 0x35, 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x35, 0x2c, 0x2d, 0x33, 0x30, 0x2e, - 0x30, 0x30, 0x34, 0x35, 0x30, 0x34, 0x32, 0x39, 0x32, 0x31, 0x37, 0x38, 0x31, 0x35, 0x31, 0x2c, 0x2d, 0x30, 0x2e, 0x30, - 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, 0x30, 0x32, 0x33, 0x37, 0x35, 0x32, 0x32, 0x38, 0x37, 0x39, 0x30, 0x5d, - 0x7d, 0x2c, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x33, 0x38, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x6e, - 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x3a, 0x7b, 0x22, - 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x22, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, - 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, - 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, - 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x72, 0x6f, - 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x5d, 0x7d, 0x7d, 0x7d, 0x0a, 0x03, 0x00, 0x55, 0x55, 0xfe, 0x7f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0xac, 0x2a, - 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xf8, 0xff, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xac, 0x2a, 0xfe, 0x7f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, - 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, - 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, - 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, - 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, - 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, - 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x39, - 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x60, - 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x51, - 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0e, - 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x21, - 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x90, - 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x09, 0x02, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x90, - 0x01, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x60, - 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, - 0x03, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x39, - 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, - 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x51, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, - 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0f, - 0x02, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x21, 0x03, 0x00, 0x00, 0x39, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x60, - 0x03, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x09, - 0x04, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x51, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0d, - 0x04, 0x00, 0x00, 0x0c, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x60, - 0x04, 0x00, 0x00, 0x0f, 0x04, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x0c, - 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0f, - 0x03, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x21, 0x04, 0x00, 0x00, 0x33, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x60, - 0x04, 0x00, 0x00, 0x33, 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x09, - 0x05, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x4b, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0d, - 0x05, 0x00, 0x00, 0x0c, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x60, - 0x05, 0x00, 0x00, 0x0f, 0x05, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0xa0, 0x41, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0xa0, 0x41, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, - 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, - 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x27, 0x72, 0xf9, 0x41, - 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x27, 0x72, 0xf9, 0x41, 0x27, 0x72, 0xf9, 0x41, 0x86, 0x00, 0xa0, 0x41, - 0x27, 0x72, 0xf9, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x27, 0x72, 0xf9, 0x41, 0x27, 0x72, 0xf9, 0x41, - 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, - 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x27, 0x72, 0xf9, 0x41, - 0x35, 0xb9, 0x24, 0x42, 0x27, 0x72, 0xf9, 0x41, 0x27, 0x72, 0xf9, 0x41, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, - 0x27, 0x72, 0xf9, 0x41, 0x35, 0xb9, 0x24, 0x42, 0x27, 0x72, 0xf9, 0x41, 0x27, 0x72, 0xf9, 0x41, 0x35, 0xb9, 0x24, 0x42, - 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, - 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, - 0x35, 0xb9, 0x24, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0xbb, 0x2a, 0x7e, 0x42, - 0xbb, 0x2a, 0x7e, 0x42, 0x35, 0xb9, 0x24, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, - 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, - 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, - 0x35, 0xb9, 0x24, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, - 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, - 0xbb, 0x2a, 0x7e, 0x42, 0x6e, 0x15, 0x93, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0x6e, 0x15, 0x93, 0x42, - 0x6e, 0x15, 0x93, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0x6e, 0x15, 0x93, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, - 0x6e, 0x15, 0x93, 0x42, 0x6e, 0x15, 0x93, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, - 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, - 0xbb, 0x2a, 0x7e, 0x42, 0x03, 0x00, 0xa7, 0xaa, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x55, 0x55, - 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x55, 0x55, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x55, 0x55, 0xa7, 0xaa, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x55, 0x55, 0xfe, 0x7f, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0xf8, 0xff, - 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0xa7, 0xaa, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x55, 0x55, 0x50, 0xd5, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x55, 0x55, 0xf8, 0xff, 0xfe, 0x7f, 0x01, 0x00, - 0x02, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xa7, 0xaa, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x50, 0xd5, - 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, - 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, - 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, - 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, - 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, - 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, - 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, - 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, - 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, - 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, - 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, - 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, - 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, - 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, - 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, - 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, - 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, - 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, - 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, - 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, - 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, - 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x09, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x09, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x06, 0x00, 0x00, 0x09, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, - 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, - 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, - 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0a, 0x00, - 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, - 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, - 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, - 0x00, 0x09, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, - 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x09, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, - 0x00, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x60, - 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x03, - 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0d, - 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x90, - 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x60, - 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x90, - 0x02, 0x00, 0x00, 0x09, 0x02, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x60, - 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x0d, - 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x39, - 0x03, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x39, 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x60, - 0x02, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x51, - 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0e, - 0x02, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x21, - 0x03, 0x00, 0x00, 0x39, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x90, - 0x04, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x90, - 0x03, 0x00, 0x00, 0x51, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x0c, 0x04, 0x00, 0x00, 0x60, - 0x04, 0x00, 0x00, 0x0e, 0x04, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x0f, 0x04, 0x00, 0x00, 0x0e, - 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0e, - 0x03, 0x00, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x21, - 0x04, 0x00, 0x00, 0x33, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x33, 0x04, 0x00, 0x00, 0x90, - 0x05, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x09, 0x05, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x90, - 0x04, 0x00, 0x00, 0x4b, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x0c, 0x05, 0x00, 0x00, 0x60, - 0x05, 0x00, 0x00, 0x0e, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x0f, 0x05, 0x00, 0x00, 0x0e, - 0x07, 0x00, 0x00, 0x1b, 0x06, 0x00, 0x00, 0x39, 0x07, 0x00, 0x00, 0x60, 0x07, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x39, - 0x06, 0x00, 0x00, 0x90, 0x07, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x90, 0x07, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x03, - 0x06, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x51, 0x06, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x0d, 0x06, 0x00, 0x00, 0x0c, - 0x06, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x0e, 0x06, 0x00, 0x00, 0x0d, 0x06, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00, 0x0f, - 0x06, 0x00, 0x00, 0x0e, 0x08, 0x00, 0x00, 0x21, 0x07, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x60, - 0x07, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x60, 0x07, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x09, - 0x08, 0x00, 0x00, 0x09, 0x07, 0x00, 0x00, 0x90, 0x07, 0x00, 0x00, 0x4e, 0x08, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x0d, - 0x08, 0x00, 0x00, 0x0c, 0x08, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x0e, 0x08, 0x00, 0x00, 0x0d, 0x08, 0x00, 0x00, 0x60, - 0x08, 0x00, 0x00, 0x0f, 0x08, 0x00, 0x00, 0x0e, 0x09, 0x00, 0x00, 0x1e, 0x08, 0x00, 0x00, 0x39, 0x09, 0x00, 0x00, 0x60, - 0x09, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x39, 0x08, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00, 0x90, - 0x09, 0x00, 0x00, 0x06, 0x09, 0x00, 0x00, 0x06, 0x08, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x51, 0x08, 0x00, 0x00, 0x90, - 0x08, 0x00, 0x00, 0x0d, 0x08, 0x00, 0x00, 0x0c, 0x08, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x0e, 0x08, 0x00, 0x00, 0x0d, - 0x08, 0x00, 0x00, 0x90, 0x08, 0x00, 0x00, 0x0f, 0x08, 0x00, 0x00, 0x0e, 0x0a, 0x00, 0x00, 0x21, 0x09, 0x00, 0x00, 0x39, - 0x0a, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x60, 0x09, 0x00, 0x00, 0x39, 0x09, 0x00, 0x00, 0x90, 0x0a, 0x00, 0x00, 0x60, - 0x09, 0x00, 0x00, 0x90, 0x0a, 0x00, 0x00, 0x09, 0x0a, 0x00, 0x00, 0x09, 0x09, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x51, - 0x0a, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x0d, 0x0a, 0x00, 0x00, 0x0c, 0x0a, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x0e, - 0x0a, 0x00, 0x00, 0x0d, 0x0a, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x0f, 0x0a, 0x00, 0x00, 0x0e, 0x09, 0x00, 0x00, 0x90, - 0x09, 0x00, 0x00, 0x0d, 0x09, 0x00, 0x00, 0x0c, 0x09, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x0e, 0x09, 0x00, 0x00, 0x0d, - 0x09, 0x00, 0x00, 0x90, 0x09, 0x00, 0x00, 0x0f, 0x09, 0x00, 0x00, 0x0e, 0x0b, 0x00, 0x00, 0x21, 0x0a, 0x00, 0x00, 0x33, - 0x0b, 0x00, 0x00, 0x60, 0x0b, 0x00, 0x00, 0x60, 0x0a, 0x00, 0x00, 0x33, 0x0a, 0x00, 0x00, 0x90, 0x0b, 0x00, 0x00, 0x60, - 0x0a, 0x00, 0x00, 0x90, 0x0b, 0x00, 0x00, 0x09, 0x0b, 0x00, 0x00, 0x09, 0x0a, 0x00, 0x00, 0x90, 0x0a, 0x00, 0x00, 0x4b, - 0x0b, 0x00, 0x00, 0x60, 0x0b, 0x00, 0x00, 0x0d, 0x0b, 0x00, 0x00, 0x0c, 0x0b, 0x00, 0x00, 0x60, 0x0b, 0x00, 0x00, 0x0e, - 0x0b, 0x00, 0x00, 0x0d, 0x0b, 0x00, 0x00, 0x60, 0x0b, 0x00, 0x00, 0x0f, 0x0b, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, - 0x86, 0x00, 0xa0, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, - 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0xa0, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0xa0, 0x41, - 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, - 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, - 0x86, 0x00, 0xa0, 0x41, 0x27, 0x72, 0xf9, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x27, 0x72, 0xf9, 0x41, - 0x27, 0x72, 0xf9, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x27, 0x72, 0xf9, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, - 0x27, 0x72, 0xf9, 0x41, 0x27, 0x72, 0xf9, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, - 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, 0x86, 0x00, 0xa0, 0x41, - 0x86, 0x00, 0xa0, 0x41, 0x27, 0x72, 0xf9, 0x41, 0x35, 0xb9, 0x24, 0x42, 0x27, 0x72, 0xf9, 0x41, 0x27, 0x72, 0xf9, 0x41, - 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x27, 0x72, 0xf9, 0x41, 0x35, 0xb9, 0x24, 0x42, 0x27, 0x72, 0xf9, 0x41, - 0x27, 0x72, 0xf9, 0x41, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, - 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, - 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0x35, 0xb9, 0x24, 0x42, - 0x35, 0xb9, 0x24, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0x35, 0xb9, 0x24, 0x42, 0xbb, 0x2a, 0x7e, 0x42, - 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0x35, 0xb9, 0x24, 0x42, - 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, - 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0x35, 0xb9, 0x24, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, - 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, - 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0x6e, 0x15, 0x93, 0x42, 0xbb, 0x2a, 0x7e, 0x42, - 0xbb, 0x2a, 0x7e, 0x42, 0x6e, 0x15, 0x93, 0x42, 0x6e, 0x15, 0x93, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0x6e, 0x15, 0x93, 0x42, - 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0x6e, 0x15, 0x93, 0x42, 0x6e, 0x15, 0x93, 0x42, 0xbb, 0x2a, 0x7e, 0x42, - 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, - 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0xbb, 0x2a, 0x7e, 0x42, 0x00, 0x00, 0x00, 0x00, 0xa6, 0xfe, 0x9f, 0x41, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0xfe, 0x9f, 0x41, 0xa6, 0xfe, 0x9f, 0x41, 0x00, 0x00, 0x00, 0x00, - 0xa6, 0xfe, 0x9f, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0xfe, 0x9f, 0x41, 0xa6, 0xfe, 0x9f, 0x41, - 0xa6, 0xfe, 0x9f, 0x41, 0xa6, 0xfe, 0x9f, 0x41, 0xa6, 0xfe, 0x9f, 0x41, 0xa6, 0xfe, 0x9f, 0x41, 0xa6, 0xfe, 0x9f, 0x41, - 0xa6, 0xfe, 0x9f, 0x41, 0xa6, 0xfe, 0x9f, 0x41, 0xa6, 0xfe, 0x9f, 0x41, 0xa6, 0xfe, 0x9f, 0x41, 0xa6, 0xfe, 0x9f, 0x41, - 0x47, 0x70, 0xf9, 0x41, 0xa6, 0xfe, 0x9f, 0x41, 0xa6, 0xfe, 0x9f, 0x41, 0x47, 0x70, 0xf9, 0x41, 0x47, 0x70, 0xf9, 0x41, - 0xa6, 0xfe, 0x9f, 0x41, 0x47, 0x70, 0xf9, 0x41, 0xa6, 0xfe, 0x9f, 0x41, 0xa6, 0xfe, 0x9f, 0x41, 0x47, 0x70, 0xf9, 0x41, - 0x47, 0x70, 0xf9, 0x41, 0xa6, 0xfe, 0x9f, 0x41, 0xa6, 0xfe, 0x9f, 0x41, 0xa6, 0xfe, 0x9f, 0x41, 0xa6, 0xfe, 0x9f, 0x41, - 0xa6, 0xfe, 0x9f, 0x41, 0xa6, 0xfe, 0x9f, 0x41, 0xa6, 0xfe, 0x9f, 0x41, 0xa6, 0xfe, 0x9f, 0x41, 0xa6, 0xfe, 0x9f, 0x41, - 0x47, 0x70, 0xf9, 0x41, 0x55, 0xb7, 0x24, 0x42, 0x47, 0x70, 0xf9, 0x41, 0x47, 0x70, 0xf9, 0x41, 0x55, 0xb7, 0x24, 0x42, - 0x55, 0xb7, 0x24, 0x42, 0x47, 0x70, 0xf9, 0x41, 0x55, 0xb7, 0x24, 0x42, 0x47, 0x70, 0xf9, 0x41, 0x47, 0x70, 0xf9, 0x41, - 0x55, 0xb7, 0x24, 0x42, 0x55, 0xb7, 0x24, 0x42, 0x55, 0xb7, 0x24, 0x42, 0x55, 0xb7, 0x24, 0x42, 0x55, 0xb7, 0x24, 0x42, - 0x55, 0xb7, 0x24, 0x42, 0x55, 0xb7, 0x24, 0x42, 0x55, 0xb7, 0x24, 0x42, 0x55, 0xb7, 0x24, 0x42, 0x55, 0xb7, 0x24, 0x42, - 0x55, 0xb7, 0x24, 0x42, 0x55, 0xb7, 0x24, 0x42, 0x04, 0x28, 0x7e, 0x42, 0x55, 0xb7, 0x24, 0x42, 0x55, 0xb7, 0x24, 0x42, - 0x04, 0x28, 0x7e, 0x42, 0x04, 0x28, 0x7e, 0x42, 0x55, 0xb7, 0x24, 0x42, 0x04, 0x28, 0x7e, 0x42, 0x55, 0xb7, 0x24, 0x42, - 0x55, 0xb7, 0x24, 0x42, 0x04, 0x28, 0x7e, 0x42, 0x04, 0x28, 0x7e, 0x42, 0x55, 0xb7, 0x24, 0x42, 0x55, 0xb7, 0x24, 0x42, - 0x55, 0xb7, 0x24, 0x42, 0x55, 0xb7, 0x24, 0x42, 0x55, 0xb7, 0x24, 0x42, 0x55, 0xb7, 0x24, 0x42, 0x55, 0xb7, 0x24, 0x42, - 0x55, 0xb7, 0x24, 0x42, 0x55, 0xb7, 0x24, 0x42, 0x04, 0x28, 0x7e, 0x42, 0x04, 0x28, 0x7e, 0x42, 0x04, 0x28, 0x7e, 0x42, - 0x04, 0x28, 0x7e, 0x42, 0x04, 0x28, 0x7e, 0x42, 0x04, 0x28, 0x7e, 0x42, 0x04, 0x28, 0x7e, 0x42, 0x04, 0x28, 0x7e, 0x42, - 0x04, 0x28, 0x7e, 0x42, 0x04, 0x28, 0x7e, 0x42, 0x13, 0x14, 0x93, 0x42, 0x04, 0x28, 0x7e, 0x42, 0x04, 0x28, 0x7e, 0x42, - 0x13, 0x14, 0x93, 0x42, 0x13, 0x14, 0x93, 0x42, 0x04, 0x28, 0x7e, 0x42, 0x13, 0x14, 0x93, 0x42, 0x04, 0x28, 0x7e, 0x42, - 0x04, 0x28, 0x7e, 0x42, 0x13, 0x14, 0x93, 0x42, 0x13, 0x14, 0x93, 0x42, 0x04, 0x28, 0x7e, 0x42, 0x04, 0x28, 0x7e, 0x42, - 0x04, 0x28, 0x7e, 0x42, 0x04, 0x28, 0x7e, 0x42, 0x04, 0x28, 0x7e, 0x42, 0x04, 0x28, 0x7e, 0x42, 0x04, 0x28, 0x7e, 0x42, - 0x04, 0x28, 0x7e, 0x42, 0x04, 0x28, 0x7e, 0x42, - ]); + readonly lineStrings: TileTestCase; + // 3 line strings. The first has same coordinates as lineString above. The second has same coordinates with -10 added to each y. The third same as first with -20 added to y. + // First: line code = 2; color = yellow; width = 8 + // Second: line code = 2; color = cyan; width = 8 + // Third: line code = 0 (solid); color = purple; width = 8 // 6 triangles arranged in two rows of 3. // top-left triangle has coordinates (0,0,0) (0,10,0) (5,0,0). Add 5 to x to get coords of 2nd and 3rd in row. // Bottom left same coords as top-left except add -10 to y. Add 5 to x to get coords of 2nd and 3rd in row. // colors: left=red middle=green right=blue // Bottom row has 50% transparency. - export const triangles = new Uint8Array([ - 0x69, 0x4d, 0x64, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0xc3, 0xd3, 0x8d, 0x62, 0x00, 0x1e, 0xc0, - 0xa3, 0xb4, 0x37, 0x89, 0x41, 0x00, 0x24, 0xc0, 0x00, 0x00, 0x21, 0xb0, 0xf2, 0xd7, 0x5a, 0xbe, 0x9e, 0x3c, 0x2c, 0x72, - 0x9d, 0xff, 0x1d, 0x40, 0x5d, 0x4b, 0xc8, 0x76, 0xbe, 0xff, 0x23, 0x40, 0x00, 0x00, 0x21, 0xb0, 0xf2, 0xd7, 0x5a, 0xbe, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x1a, 0x00, 0x00, - 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, - 0x02, 0x00, 0x00, 0x00, 0x64, 0x19, 0x00, 0x00, 0xf8, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, - 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x44, 0x69, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, - 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, - 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x31, 0x36, 0x2c, - 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x31, 0x30, 0x38, 0x7d, 0x2c, 0x22, - 0x62, 0x76, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x31, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, - 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, - 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, - 0x68, 0x22, 0x3a, 0x32, 0x31, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, - 0x32, 0x34, 0x33, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x3a, 0x7b, 0x22, - 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, - 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x35, 0x36, 0x2c, 0x22, - 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x56, 0x65, - 0x72, 0x74, 0x65, 0x78, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, - 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, - 0x74, 0x68, 0x22, 0x3a, 0x31, 0x35, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, - 0x3a, 0x31, 0x33, 0x32, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, - 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, - 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, - 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, - 0x32, 0x31, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x33, 0x34, 0x38, - 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, - 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, - 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, - 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x31, 0x36, 0x2c, 0x22, - 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x36, 0x37, 0x32, 0x7d, 0x2c, 0x22, 0x62, - 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, - 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, - 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, - 0x3a, 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x35, 0x36, - 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, - 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, - 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, - 0x3a, 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x38, - 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, - 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, - 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, - 0x32, 0x37, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x35, 0x36, 0x7d, - 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, - 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, - 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, - 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, - 0x3a, 0x31, 0x38, 0x38, 0x38, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x65, - 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, - 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, - 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, - 0x22, 0x3a, 0x31, 0x35, 0x30, 0x38, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, - 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, - 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, - 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x37, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, - 0x3a, 0x31, 0x34, 0x38, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, - 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, - 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, - 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, - 0x68, 0x22, 0x3a, 0x32, 0x31, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, - 0x38, 0x39, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, - 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x31, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, - 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, - 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, - 0x3a, 0x32, 0x31, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x32, 0x32, - 0x31, 0x36, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, - 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, - 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, - 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, - 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x37, 0x32, 0x38, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, - 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, - 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, - 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, - 0x31, 0x36, 0x32, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x32, 0x30, 0x35, - 0x32, 0x7d, 0x7d, 0x2c, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, - 0x3a, 0x5b, 0x22, 0x4b, 0x48, 0x52, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, - 0x22, 0x57, 0x45, 0x42, 0x33, 0x44, 0x5f, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, 0x6d, 0x61, 0x74, - 0x65, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, - 0x3a, 0x7b, 0x22, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x37, - 0x22, 0x2c, 0x22, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, - 0x6f, 0x72, 0x22, 0x3a, 0x32, 0x35, 0x35, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3a, - 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x66, - 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x32, 0x35, 0x35, - 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, - 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x73, 0x75, 0x62, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, - 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x38, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, - 0x30, 0x7d, 0x2c, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x63, 0x61, 0x74, - 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x37, 0x22, 0x2c, 0x22, 0x63, 0x6c, 0x61, - 0x73, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x32, 0x31, - 0x33, 0x30, 0x37, 0x30, 0x36, 0x36, 0x38, 0x37, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, - 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, - 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x32, 0x31, - 0x33, 0x30, 0x37, 0x30, 0x36, 0x36, 0x38, 0x37, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, - 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x73, - 0x75, 0x62, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x38, 0x22, - 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x7d, 0x7d, 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, - 0x3a, 0x7b, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, - 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x65, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x6f, 0x6c, 0x79, 0x6c, - 0x69, 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, - 0x62, 0x76, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, - 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, - 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, - 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, - 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, - 0x65, 0x73, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, - 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, - 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, - 0x7b, 0x22, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, - 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, - 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, - 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x7d, 0x7d, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, - 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, - 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x2c, 0x22, 0x73, 0x75, 0x72, 0x66, 0x61, - 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, - 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, - 0x22, 0x3a, 0x31, 0x7d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, - 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, - 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x39, - 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, - 0x32, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, - 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, - 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x70, - 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, - 0x78, 0x22, 0x3a, 0x5b, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x32, 0x38, 0x39, 0x31, 0x39, 0x36, 0x39, 0x30, 0x33, 0x35, - 0x38, 0x33, 0x35, 0x34, 0x30, 0x34, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, - 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x33, 0x30, 0x35, 0x32, 0x32, 0x36, 0x32, 0x34, 0x34, 0x31, 0x30, 0x38, - 0x32, 0x35, 0x38, 0x36, 0x39, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, - 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, 0x31, 0x33, 0x31, 0x31, 0x34, 0x37, 0x34, 0x38, 0x35, 0x34, 0x32, 0x38, 0x37, - 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x35, 0x39, 0x35, - 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x35, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x35, 0x30, 0x30, 0x39, - 0x35, 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, - 0x35, 0x30, 0x32, 0x33, 0x37, 0x35, 0x32, 0x32, 0x38, 0x37, 0x39, 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, 0x22, 0x64, - 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x35, - 0x39, 0x35, 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x35, 0x2c, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x35, 0x30, 0x30, - 0x39, 0x35, 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, - 0x35, 0x30, 0x32, 0x33, 0x37, 0x35, 0x32, 0x32, 0x38, 0x37, 0x39, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, - 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x35, 0x39, 0x35, 0x33, - 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x35, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x35, 0x30, 0x30, 0x39, 0x35, - 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, 0x37, 0x35, - 0x30, 0x32, 0x33, 0x37, 0x35, 0x32, 0x32, 0x38, 0x37, 0x39, 0x30, 0x5d, 0x7d, 0x2c, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, - 0x22, 0x3a, 0x33, 0x39, 0x7d, 0x7d, 0x2c, 0x7b, 0x22, 0x65, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x6f, - 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, - 0x22, 0x3a, 0x22, 0x62, 0x76, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x31, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, - 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, - 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, - 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, - 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, - 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x31, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, - 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, - 0x3a, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x50, 0x6f, 0x6c, 0x79, - 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, - 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, - 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, - 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x65, 0x67, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, - 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x7d, 0x7d, 0x2c, 0x22, 0x69, - 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, - 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x31, 0x22, 0x2c, 0x22, 0x73, 0x75, - 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, - 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x31, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x2c, 0x22, 0x74, - 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x7d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x76, 0x65, - 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, - 0x22, 0x3a, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x31, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x22, 0x3a, 0x39, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x54, 0x79, 0x70, - 0x65, 0x22, 0x3a, 0x32, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x75, 0x63, 0x65, 0x6e, 0x63, - 0x79, 0x22, 0x3a, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, - 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x22, 0x3a, 0x34, 0x2c, - 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, - 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x32, 0x32, 0x38, 0x39, 0x31, 0x39, 0x36, 0x39, 0x30, - 0x33, 0x35, 0x38, 0x33, 0x35, 0x34, 0x30, 0x34, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, - 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x33, 0x30, 0x35, 0x32, 0x32, 0x36, 0x32, 0x34, 0x34, 0x31, - 0x30, 0x38, 0x32, 0x35, 0x38, 0x36, 0x39, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, - 0x30, 0x2e, 0x30, 0x2c, 0x31, 0x2e, 0x35, 0x32, 0x36, 0x31, 0x33, 0x31, 0x31, 0x34, 0x37, 0x34, 0x38, 0x35, 0x34, 0x32, - 0x38, 0x37, 0x65, 0x2d, 0x30, 0x38, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x35, - 0x39, 0x35, 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x35, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x35, 0x30, - 0x30, 0x39, 0x35, 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, - 0x30, 0x37, 0x35, 0x30, 0x32, 0x33, 0x37, 0x35, 0x32, 0x32, 0x38, 0x37, 0x39, 0x30, 0x2c, 0x31, 0x2e, 0x30, 0x5d, 0x2c, - 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, - 0x32, 0x35, 0x39, 0x35, 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x35, 0x2c, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x35, - 0x30, 0x30, 0x39, 0x35, 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x2c, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, - 0x30, 0x37, 0x35, 0x30, 0x32, 0x33, 0x37, 0x35, 0x32, 0x32, 0x38, 0x37, 0x39, 0x30, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, - 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x37, 0x2e, 0x35, 0x30, 0x31, 0x31, 0x32, 0x35, 0x39, - 0x35, 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x35, 0x2c, 0x2d, 0x31, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x35, 0x30, 0x30, - 0x39, 0x35, 0x33, 0x38, 0x31, 0x37, 0x33, 0x36, 0x36, 0x2c, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x35, 0x30, 0x30, 0x30, - 0x37, 0x35, 0x30, 0x32, 0x33, 0x37, 0x35, 0x32, 0x32, 0x38, 0x37, 0x39, 0x30, 0x5d, 0x7d, 0x2c, 0x22, 0x77, 0x69, 0x64, - 0x74, 0x68, 0x22, 0x3a, 0x33, 0x39, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, - 0x7b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, - 0x22, 0x3a, 0x5b, 0x22, 0x4d, 0x65, 0x73, 0x68, 0x22, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, - 0x3a, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, - 0x6e, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, - 0x3a, 0x7b, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, - 0x22, 0x5d, 0x7d, 0x7d, 0x7d, 0x0a, 0x20, 0x20, 0xa7, 0xaa, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xcc, 0xcc, 0xa7, 0xaa, 0xf8, 0xff, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, - 0xf8, 0xff, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0xfe, 0x7f, - 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0xf8, 0xff, 0xfe, 0x7f, 0x01, 0x00, - 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xa7, 0xaa, 0xfe, 0x7f, 0xfe, 0x7f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, 0xfe, 0x7f, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, - 0x03, 0x00, 0xf8, 0xff, 0xfe, 0x7f, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0xfe, 0x7f, - 0xfe, 0x7f, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, - 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, - 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, - 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, - 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, - 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, - 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x01, - 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, - 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x03, - 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x02, - 0x06, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x01, - 0x06, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, - 0x08, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, - 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, - 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, - 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, - 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, - 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, - 0x00, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x36, - 0x00, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x1e, 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x06, - 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x06, - 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x4e, 0x04, 0x00, 0x00, 0x1e, 0x03, 0x00, 0x00, 0x36, - 0x04, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x4e, 0x05, 0x00, 0x00, 0x1e, - 0x04, 0x00, 0x00, 0x36, 0x05, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x4e, - 0x03, 0x00, 0x00, 0x1e, 0x05, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x36, - 0x05, 0x00, 0x00, 0x4e, 0x07, 0x00, 0x00, 0x1e, 0x06, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x06, 0x07, 0x00, 0x00, 0x06, - 0x06, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x4e, 0x08, 0x00, 0x00, 0x1e, 0x07, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x06, - 0x08, 0x00, 0x00, 0x06, 0x07, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x4e, 0x06, 0x00, 0x00, 0x1e, 0x08, 0x00, 0x00, 0x36, - 0x06, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x06, 0x08, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x4e, 0x56, 0x71, 0xd1, 0x41, - 0x7c, 0xb8, 0x10, 0x42, 0x56, 0x71, 0xd1, 0x41, 0x56, 0x71, 0xd1, 0x41, 0x7c, 0xb8, 0x10, 0x42, 0x7c, 0xb8, 0x10, 0x42, - 0x7c, 0xb8, 0x10, 0x42, 0xea, 0x70, 0x3d, 0x42, 0x7c, 0xb8, 0x10, 0x42, 0x7c, 0xb8, 0x10, 0x42, 0xea, 0x70, 0x3d, 0x42, - 0xea, 0x70, 0x3d, 0x42, 0xea, 0x70, 0x3d, 0x42, 0xbf, 0x70, 0x51, 0x42, 0xea, 0x70, 0x3d, 0x42, 0xea, 0x70, 0x3d, 0x42, - 0xbf, 0x70, 0x51, 0x42, 0xbf, 0x70, 0x51, 0x42, 0x56, 0x71, 0xd1, 0x41, 0x7c, 0xb8, 0x10, 0x42, 0x56, 0x71, 0xd1, 0x41, - 0x56, 0x71, 0xd1, 0x41, 0x7c, 0xb8, 0x10, 0x42, 0x7c, 0xb8, 0x10, 0x42, 0x7c, 0xb8, 0x10, 0x42, 0x05, 0x71, 0x3d, 0x42, - 0x7c, 0xb8, 0x10, 0x42, 0x7c, 0xb8, 0x10, 0x42, 0x05, 0x71, 0x3d, 0x42, 0x05, 0x71, 0x3d, 0x42, 0x05, 0x71, 0x3d, 0x42, - 0x16, 0x71, 0x51, 0x42, 0x05, 0x71, 0x3d, 0x42, 0x05, 0x71, 0x3d, 0x42, 0x16, 0x71, 0x51, 0x42, 0x16, 0x71, 0x51, 0x42, - 0x56, 0x71, 0xd1, 0x41, 0x7c, 0xb8, 0x10, 0x42, 0x56, 0x71, 0xd1, 0x41, 0x56, 0x71, 0xd1, 0x41, 0x7c, 0xb8, 0x10, 0x42, - 0x7c, 0xb8, 0x10, 0x42, 0x7c, 0xb8, 0x10, 0x42, 0x05, 0x71, 0x3d, 0x42, 0x7c, 0xb8, 0x10, 0x42, 0x7c, 0xb8, 0x10, 0x42, - 0x05, 0x71, 0x3d, 0x42, 0x05, 0x71, 0x3d, 0x42, 0x05, 0x71, 0x3d, 0x42, 0x16, 0x71, 0x51, 0x42, 0x05, 0x71, 0x3d, 0x42, - 0x05, 0x71, 0x3d, 0x42, 0x16, 0x71, 0x51, 0x42, 0x16, 0x71, 0x51, 0x42, 0xa7, 0xaa, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xa7, 0xaa, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xcc, 0xcc, 0xf8, 0xff, 0x03, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, - 0x55, 0x55, 0x03, 0x00, 0xfe, 0x7f, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x55, 0x55, 0xfe, 0x7f, - 0xfe, 0x7f, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xa7, 0xaa, 0x03, 0x00, 0xfe, 0x7f, 0x01, 0x00, - 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x03, 0x00, 0x03, 0x00, 0xfe, 0x7f, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, 0xfe, 0x7f, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, - 0x55, 0x55, 0x03, 0x00, 0xfe, 0x7f, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x00, 0x00, 0x80, 0x80, - 0x00, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, - 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, - 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, - 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, - 0x04, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, - 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x02, - 0x04, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x01, - 0x06, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, - 0x08, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x02, - 0x08, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, - 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, - 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, - 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, - 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x06, - 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x1e, 0x01, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x06, - 0x02, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x36, - 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x4e, 0x04, 0x00, 0x00, 0x1e, - 0x03, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x4e, - 0x05, 0x00, 0x00, 0x1e, 0x04, 0x00, 0x00, 0x36, 0x05, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x36, - 0x04, 0x00, 0x00, 0x4e, 0x03, 0x00, 0x00, 0x1e, 0x05, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, - 0x05, 0x00, 0x00, 0x36, 0x05, 0x00, 0x00, 0x4e, 0x07, 0x00, 0x00, 0x1e, 0x06, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x06, - 0x07, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x4e, 0x08, 0x00, 0x00, 0x1e, 0x07, 0x00, 0x00, 0x36, - 0x08, 0x00, 0x00, 0x06, 0x08, 0x00, 0x00, 0x06, 0x07, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x4e, 0x06, 0x00, 0x00, 0x1e, - 0x08, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x06, 0x08, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x4e, - 0x56, 0x71, 0xd1, 0x41, 0xcc, 0xb8, 0x10, 0x42, 0x56, 0x71, 0xd1, 0x41, 0x56, 0x71, 0xd1, 0x41, 0xcc, 0xb8, 0x10, 0x42, - 0xcc, 0xb8, 0x10, 0x42, 0xcc, 0xb8, 0x10, 0x42, 0x82, 0x71, 0x3d, 0x42, 0xcc, 0xb8, 0x10, 0x42, 0xcc, 0xb8, 0x10, 0x42, - 0x82, 0x71, 0x3d, 0x42, 0x82, 0x71, 0x3d, 0x42, 0x82, 0x71, 0x3d, 0x42, 0x57, 0x71, 0x51, 0x42, 0x82, 0x71, 0x3d, 0x42, - 0x82, 0x71, 0x3d, 0x42, 0x57, 0x71, 0x51, 0x42, 0x57, 0x71, 0x51, 0x42, 0x56, 0x71, 0xd1, 0x41, 0xcc, 0xb8, 0x10, 0x42, - 0x56, 0x71, 0xd1, 0x41, 0x56, 0x71, 0xd1, 0x41, 0xcc, 0xb8, 0x10, 0x42, 0xcc, 0xb8, 0x10, 0x42, 0xcc, 0xb8, 0x10, 0x42, - 0x9d, 0x71, 0x3d, 0x42, 0xcc, 0xb8, 0x10, 0x42, 0xcc, 0xb8, 0x10, 0x42, 0x9d, 0x71, 0x3d, 0x42, 0x9d, 0x71, 0x3d, 0x42, - 0x9d, 0x71, 0x3d, 0x42, 0xae, 0x71, 0x51, 0x42, 0x9d, 0x71, 0x3d, 0x42, 0x9d, 0x71, 0x3d, 0x42, 0xae, 0x71, 0x51, 0x42, - 0xae, 0x71, 0x51, 0x42, 0x56, 0x71, 0xd1, 0x41, 0xcc, 0xb8, 0x10, 0x42, 0x56, 0x71, 0xd1, 0x41, 0x56, 0x71, 0xd1, 0x41, - 0xcc, 0xb8, 0x10, 0x42, 0xcc, 0xb8, 0x10, 0x42, 0xcc, 0xb8, 0x10, 0x42, 0x9d, 0x71, 0x3d, 0x42, 0xcc, 0xb8, 0x10, 0x42, - 0xcc, 0xb8, 0x10, 0x42, 0x9d, 0x71, 0x3d, 0x42, 0x9d, 0x71, 0x3d, 0x42, 0x9d, 0x71, 0x3d, 0x42, 0xae, 0x71, 0x51, 0x42, - 0x9d, 0x71, 0x3d, 0x42, 0x9d, 0x71, 0x3d, 0x42, 0xae, 0x71, 0x51, 0x42, 0xae, 0x71, 0x51, 0x42, - ]); + readonly triangles: TileTestCase; // Binary data for a tile created for a model containing a single element: a green cylinder. - // Center1 = (0, 0, 0) Center2 = (0, 0, 6) Radius = 2 - export const cylinder = new Uint8Array([ - 0x69, 0x4d, 0x64, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x3f, 0xc6, 0x6d, 0x34, 0x00, 0x00, 0xc0, - 0x15, 0x3f, 0xc6, 0x6d, 0x34, 0x00, 0x00, 0xc0, 0x0d, 0x93, 0xa9, 0xe4, 0x4e, 0x00, 0x08, 0xc0, 0xd6, 0x81, 0x73, 0x24, - 0x97, 0xff, 0xff, 0x3f, 0xd6, 0x81, 0x73, 0x24, 0x97, 0xff, 0xff, 0x3f, 0xf3, 0x6c, 0x56, 0x1b, 0xb1, 0xff, 0x07, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x54, 0x46, - 0x02, 0x00, 0x00, 0x00, 0xdc, 0x44, 0x00, 0x00, 0xe0, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x62, 0x75, - 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x76, 0x44, 0x69, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, - 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, - 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x37, 0x32, 0x38, - 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x33, 0x33, 0x35, 0x32, 0x7d, - 0x2c, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x30, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, - 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, - 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x32, 0x33, 0x33, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, - 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x30, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, - 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, - 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, - 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, - 0x74, 0x68, 0x22, 0x3a, 0x31, 0x37, 0x32, 0x38, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, - 0x22, 0x3a, 0x34, 0x39, 0x32, 0x38, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, - 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, 0x75, - 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, - 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, - 0x74, 0x68, 0x22, 0x3a, 0x38, 0x36, 0x34, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, - 0x3a, 0x37, 0x33, 0x30, 0x34, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, - 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, - 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, - 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x32, 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, - 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x39, 0x30, 0x33, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, - 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, - 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, - 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x32, 0x39, 0x36, 0x2c, 0x22, 0x62, 0x79, - 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x33, 0x36, 0x33, 0x32, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x69, - 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x3a, - 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, - 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x36, 0x34, 0x38, - 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x36, 0x36, 0x35, 0x36, 0x7d, 0x2c, - 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, - 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, - 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x32, 0x39, - 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x32, 0x33, 0x33, 0x36, 0x7d, - 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, - 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, - 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x37, 0x32, - 0x38, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x31, 0x36, 0x32, 0x34, - 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x30, 0x53, 0x69, 0x6c, - 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, - 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, - 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x38, 0x36, 0x34, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, - 0x65, 0x74, 0x22, 0x3a, 0x38, 0x31, 0x36, 0x38, 0x7d, 0x2c, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, - 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, - 0x7b, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, - 0x54, 0x46, 0x22, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x32, 0x39, - 0x36, 0x2c, 0x22, 0x62, 0x79, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x31, 0x30, 0x33, 0x32, 0x38, - 0x7d, 0x7d, 0x2c, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, - 0x5b, 0x22, 0x4b, 0x48, 0x52, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x67, 0x6c, 0x54, 0x46, 0x22, 0x2c, 0x22, - 0x57, 0x45, 0x42, 0x33, 0x44, 0x5f, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x22, 0x67, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x5b, 0x22, 0x4f, 0x45, 0x53, 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, - 0x72, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x22, 0x3a, - 0x7b, 0x22, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x37, 0x22, - 0x2c, 0x22, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6c, 0x6f, - 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x66, 0x69, 0x6c, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, - 0x3a, 0x30, 0x2c, 0x22, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x3a, - 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, - 0x32, 0x38, 0x30, 0x2c, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x22, 0x3a, 0x30, 0x2c, 0x22, - 0x6c, 0x69, 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x73, 0x75, 0x62, 0x43, 0x61, 0x74, - 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x31, 0x38, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, - 0x65, 0x22, 0x3a, 0x30, 0x7d, 0x7d, 0x2c, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x4d, 0x65, - 0x73, 0x68, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x7b, - 0x22, 0x65, 0x64, 0x67, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x70, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x22, - 0x3a, 0x7b, 0x22, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x44, 0x69, 0x73, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, - 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, - 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x6e, 0x65, - 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, - 0x22, 0x62, 0x76, 0x6e, 0x65, 0x78, 0x74, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, 0x73, 0x22, 0x2c, 0x22, - 0x70, 0x72, 0x65, 0x76, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x70, 0x72, 0x65, 0x76, - 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x64, 0x67, 0x65, - 0x73, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x65, 0x6e, 0x64, - 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, - 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, - 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x2c, 0x22, 0x69, 0x6e, - 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x65, - 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x7d, 0x2c, 0x22, 0x73, 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, - 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, - 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, - 0x41, 0x6e, 0x64, 0x51, 0x75, 0x61, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, - 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, 0x22, 0x2c, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, - 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, 0x65, 0x73, - 0x22, 0x2c, 0x22, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x6e, - 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x30, 0x53, 0x69, 0x6c, 0x68, 0x6f, 0x75, 0x65, 0x74, 0x74, - 0x65, 0x73, 0x22, 0x7d, 0x7d, 0x2c, 0x22, 0x69, 0x73, 0x50, 0x6c, 0x61, 0x6e, 0x61, 0x72, 0x22, 0x3a, 0x66, 0x61, 0x6c, - 0x73, 0x65, 0x2c, 0x22, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x3a, 0x22, 0x4d, 0x61, 0x74, 0x65, 0x72, - 0x69, 0x61, 0x6c, 0x30, 0x22, 0x2c, 0x22, 0x73, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x6e, - 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x30, 0x53, 0x75, - 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x7d, 0x2c, 0x22, 0x74, 0x79, - 0x70, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x62, - 0x75, 0x66, 0x66, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x22, 0x3a, 0x22, 0x62, 0x76, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, - 0x30, 0x22, 0x2c, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x31, 0x34, 0x36, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x49, 0x44, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x68, 0x61, 0x73, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, - 0x75, 0x63, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x6e, 0x75, 0x6d, 0x52, 0x67, 0x62, 0x61, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, - 0x65, 0x78, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x63, - 0x6f, 0x64, 0x65, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x22, 0x3a, 0x5b, 0x36, 0x2e, 0x31, 0x30, 0x34, 0x35, 0x32, 0x35, - 0x30, 0x32, 0x37, 0x37, 0x30, 0x38, 0x33, 0x37, 0x34, 0x38, 0x65, 0x2d, 0x30, 0x35, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, - 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x36, 0x2e, 0x31, 0x30, 0x34, 0x35, 0x32, 0x35, 0x30, - 0x32, 0x37, 0x37, 0x30, 0x38, 0x33, 0x37, 0x34, 0x38, 0x65, 0x2d, 0x30, 0x35, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, - 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x39, 0x2e, 0x31, 0x35, 0x36, 0x37, 0x38, 0x37, 0x39, 0x30, - 0x35, 0x34, 0x32, 0x30, 0x35, 0x36, 0x36, 0x30, 0x65, 0x2d, 0x30, 0x35, 0x2c, 0x30, 0x2e, 0x30, 0x2c, 0x2d, 0x32, 0x2e, - 0x30, 0x30, 0x30, 0x33, 0x30, 0x30, 0x32, 0x33, 0x38, 0x34, 0x35, 0x34, 0x33, 0x34, 0x31, 0x38, 0x2c, 0x2d, 0x32, 0x2e, - 0x30, 0x30, 0x30, 0x33, 0x30, 0x30, 0x32, 0x33, 0x38, 0x34, 0x35, 0x34, 0x33, 0x34, 0x31, 0x38, 0x2c, 0x2d, 0x33, 0x2e, - 0x30, 0x30, 0x30, 0x34, 0x35, 0x30, 0x34, 0x37, 0x36, 0x39, 0x30, 0x38, 0x36, 0x38, 0x33, 0x35, 0x2c, 0x31, 0x2e, 0x30, - 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x22, 0x3a, 0x5b, 0x32, 0x2e, 0x30, 0x30, - 0x30, 0x33, 0x30, 0x30, 0x32, 0x33, 0x38, 0x34, 0x35, 0x34, 0x33, 0x34, 0x31, 0x38, 0x2c, 0x32, 0x2e, 0x30, 0x30, 0x30, - 0x33, 0x30, 0x30, 0x32, 0x33, 0x38, 0x34, 0x35, 0x34, 0x33, 0x34, 0x31, 0x38, 0x2c, 0x33, 0x2e, 0x30, 0x30, 0x30, 0x34, - 0x35, 0x30, 0x34, 0x37, 0x36, 0x39, 0x30, 0x38, 0x36, 0x38, 0x34, 0x34, 0x5d, 0x2c, 0x22, 0x64, 0x65, 0x63, 0x6f, 0x64, - 0x65, 0x64, 0x4d, 0x69, 0x6e, 0x22, 0x3a, 0x5b, 0x2d, 0x32, 0x2e, 0x30, 0x30, 0x30, 0x33, 0x30, 0x30, 0x32, 0x33, 0x38, - 0x34, 0x35, 0x34, 0x33, 0x34, 0x31, 0x38, 0x2c, 0x2d, 0x32, 0x2e, 0x30, 0x30, 0x30, 0x33, 0x30, 0x30, 0x32, 0x33, 0x38, - 0x34, 0x35, 0x34, 0x33, 0x34, 0x31, 0x38, 0x2c, 0x2d, 0x33, 0x2e, 0x30, 0x30, 0x30, 0x34, 0x35, 0x30, 0x34, 0x37, 0x36, - 0x39, 0x30, 0x38, 0x36, 0x38, 0x33, 0x35, 0x5d, 0x7d, 0x2c, 0x22, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x43, 0x6f, - 0x6c, 0x6f, 0x72, 0x22, 0x3a, 0x36, 0x35, 0x32, 0x38, 0x30, 0x2c, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x35, - 0x38, 0x34, 0x7d, 0x7d, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x72, 0x6f, - 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, - 0x4d, 0x65, 0x73, 0x68, 0x22, 0x5d, 0x7d, 0x7d, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x22, 0x64, 0x65, - 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x2c, 0x22, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x73, 0x22, - 0x3a, 0x7b, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x6e, - 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x5d, 0x7d, 0x7d, - 0x7d, 0x0a, 0x20, 0x20, 0xf8, 0xff, 0xfe, 0x7f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, - 0xfe, 0x7f, 0xfe, 0x7f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x07, 0xfe, 0xc5, 0x69, - 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x41, 0xf8, 0x38, 0x54, 0xf8, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xd3, 0xee, 0x01, 0x40, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x80, 0xcc, 0xcc, 0x07, 0xe2, 0xba, 0x2d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, - 0x41, 0xd2, 0xf4, 0x1d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xfb, 0xbf, 0x29, 0x11, - 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xc3, 0xab, 0xbb, 0x07, 0xf8, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x37, 0x96, 0xf5, 0x01, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x80, 0xcc, 0xcc, 0xfe, 0x7f, 0x03, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, - 0xc5, 0x69, 0xf5, 0x01, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x38, 0x54, 0xbb, 0x07, - 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x01, 0x40, 0x29, 0x11, 0xf8, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xba, 0x2d, 0xf4, 0x1d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x80, 0xcc, 0xcc, 0xf4, 0x1d, 0xba, 0x2d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, - 0x29, 0x11, 0x01, 0x40, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xbb, 0x07, 0x38, 0x54, - 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xf5, 0x01, 0xc5, 0x69, 0xf8, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x80, 0xcc, 0xcc, 0xf5, 0x01, 0x37, 0x96, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, - 0xbb, 0x07, 0xc3, 0xab, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x29, 0x11, 0xfb, 0xbf, - 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xf4, 0x1d, 0x41, 0xd2, 0xf8, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xba, 0x2d, 0x07, 0xe2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x80, 0xcc, 0xcc, 0x01, 0x40, 0xd3, 0xee, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, - 0x38, 0x54, 0x41, 0xf8, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xc5, 0x69, 0x07, 0xfe, - 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xfe, 0x7f, 0xf8, 0xff, 0xf8, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x37, 0x96, 0x07, 0xfe, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x80, 0xcc, 0xcc, 0xc3, 0xab, 0x41, 0xf8, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, - 0xfb, 0xbf, 0xd3, 0xee, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x41, 0xd2, 0x07, 0xe2, - 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0x07, 0xe2, 0x41, 0xd2, 0xf8, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xd3, 0xee, 0xfb, 0xbf, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x80, 0xcc, 0xcc, 0x41, 0xf8, 0xc3, 0xab, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, - 0x07, 0xfe, 0x37, 0x96, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xcc, 0xcc, 0xf8, 0xff, 0xfe, 0x7f, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xfe, 0x7f, 0xfe, 0x7f, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x07, 0xfe, 0x37, 0x96, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xcc, 0xcc, 0x41, 0xf8, 0xc3, 0xab, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, - 0xd3, 0xee, 0xfb, 0xbf, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x07, 0xe2, 0x41, 0xd2, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x41, 0xd2, 0x07, 0xe2, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xfb, 0xbf, 0xd3, 0xee, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xcc, 0xcc, 0xc3, 0xab, 0x41, 0xf8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, - 0x37, 0x96, 0x07, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xfe, 0x7f, 0xf8, 0xff, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xc5, 0x69, 0x07, 0xfe, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x38, 0x54, 0x41, 0xf8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xcc, 0xcc, 0x01, 0x40, 0xd3, 0xee, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, - 0xba, 0x2d, 0x07, 0xe2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xf4, 0x1d, 0x41, 0xd2, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x29, 0x11, 0xfb, 0xbf, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xbb, 0x07, 0xc3, 0xab, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xcc, 0xcc, 0xf5, 0x01, 0x37, 0x96, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, - 0x03, 0x00, 0xfe, 0x7f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xf5, 0x01, 0xc5, 0x69, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xbb, 0x07, 0x38, 0x54, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x29, 0x11, 0x01, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xcc, 0xcc, 0xf4, 0x1d, 0xba, 0x2d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, - 0xba, 0x2d, 0xf4, 0x1d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x01, 0x40, 0x29, 0x11, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x38, 0x54, 0xbb, 0x07, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xc5, 0x69, 0xf5, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xcc, 0xcc, 0xfe, 0x7f, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, - 0x37, 0x96, 0xf5, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xc3, 0xab, 0xbb, 0x07, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xfb, 0xbf, 0x29, 0x11, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x41, 0xd2, 0xf4, 0x1d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xcc, 0xcc, 0x07, 0xe2, 0xba, 0x2d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, - 0xd3, 0xee, 0x01, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x41, 0xf8, 0x38, 0x54, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0x07, 0xfe, 0xc5, 0x69, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xf8, 0xff, 0xfe, 0x7f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0x80, 0xcc, 0xcc, 0xf8, 0xff, 0xfe, 0x7f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x80, 0xcc, 0xcc, - 0x07, 0xfe, 0x37, 0x96, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x93, 0xcc, 0xcc, 0x07, 0xfe, 0x37, 0x96, - 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x93, 0xcc, 0xcc, 0x41, 0xf8, 0xc3, 0xab, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xdd, 0xa2, 0xcc, 0xcc, 0x41, 0xf8, 0xc3, 0xab, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdd, 0xa2, 0xcc, 0xcc, 0xd3, 0xee, 0xfb, 0xbf, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xae, 0xcc, 0xcc, - 0xd3, 0xee, 0xfb, 0xbf, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xae, 0xcc, 0xcc, 0x07, 0xe2, 0x41, 0xd2, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0xba, 0xcc, 0xcc, 0x07, 0xe2, 0x41, 0xd2, 0xf8, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xc5, 0xba, 0xcc, 0xcc, 0x41, 0xd2, 0x07, 0xe2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xba, 0xc5, 0xcc, 0xcc, 0x41, 0xd2, 0x07, 0xe2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xc5, 0xcc, 0xcc, - 0xfb, 0xbf, 0xd3, 0xee, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0xd0, 0xcc, 0xcc, 0xfb, 0xbf, 0xd3, 0xee, - 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0xd0, 0xcc, 0xcc, 0xc3, 0xab, 0x41, 0xf8, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xa2, 0xdd, 0xcc, 0xcc, 0xc3, 0xab, 0x41, 0xf8, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xa2, 0xdd, 0xcc, 0xcc, 0x37, 0x96, 0x07, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0xec, 0xcc, 0xcc, - 0x37, 0x96, 0x07, 0xfe, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0xec, 0xcc, 0xcc, 0xfe, 0x7f, 0xf8, 0xff, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xcc, 0xcc, 0xfe, 0x7f, 0xf8, 0xff, 0xf8, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xcc, 0xcc, 0xc5, 0x69, 0x07, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x6c, 0xec, 0xcc, 0xcc, 0xc5, 0x69, 0x07, 0xfe, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xec, 0xcc, 0xcc, - 0x38, 0x54, 0x41, 0xf8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xdd, 0xcc, 0xcc, 0x38, 0x54, 0x41, 0xf8, - 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xdd, 0xcc, 0xcc, 0x01, 0x40, 0xd3, 0xee, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x51, 0xd0, 0xcc, 0xcc, 0x01, 0x40, 0xd3, 0xee, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x51, 0xd0, 0xcc, 0xcc, 0xba, 0x2d, 0x07, 0xe2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xc5, 0xcc, 0xcc, - 0xba, 0x2d, 0x07, 0xe2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xc5, 0xcc, 0xcc, 0xf4, 0x1d, 0x41, 0xd2, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0xba, 0xcc, 0xcc, 0xf4, 0x1d, 0x41, 0xd2, 0xf8, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3a, 0xba, 0xcc, 0xcc, 0x29, 0x11, 0xfb, 0xbf, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2f, 0xae, 0xcc, 0xcc, 0x29, 0x11, 0xfb, 0xbf, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0xae, 0xcc, 0xcc, - 0xbb, 0x07, 0xc3, 0xab, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xa2, 0xcc, 0xcc, 0xbb, 0x07, 0xc3, 0xab, - 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xa2, 0xcc, 0xcc, 0xf5, 0x01, 0x37, 0x96, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x13, 0x93, 0xcc, 0xcc, 0xf5, 0x01, 0x37, 0x96, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x13, 0x93, 0xcc, 0xcc, 0x03, 0x00, 0xfe, 0x7f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcc, 0xcc, - 0x03, 0x00, 0xfe, 0x7f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcc, 0xcc, 0xf5, 0x01, 0xc5, 0x69, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x6c, 0xcc, 0xcc, 0xf5, 0x01, 0xc5, 0x69, 0xf8, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x13, 0x6c, 0xcc, 0xcc, 0xbb, 0x07, 0x38, 0x54, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x22, 0x5d, 0xcc, 0xcc, 0xbb, 0x07, 0x38, 0x54, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x5d, 0xcc, 0xcc, - 0x29, 0x11, 0x01, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x51, 0xcc, 0xcc, 0x29, 0x11, 0x01, 0x40, - 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x51, 0xcc, 0xcc, 0xf4, 0x1d, 0xba, 0x2d, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3a, 0x45, 0xcc, 0xcc, 0xf4, 0x1d, 0xba, 0x2d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3a, 0x45, 0xcc, 0xcc, 0xba, 0x2d, 0xf4, 0x1d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x3a, 0xcc, 0xcc, - 0xba, 0x2d, 0xf4, 0x1d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x3a, 0xcc, 0xcc, 0x01, 0x40, 0x29, 0x11, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x2f, 0xcc, 0xcc, 0x01, 0x40, 0x29, 0x11, 0xf8, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x51, 0x2f, 0xcc, 0xcc, 0x38, 0x54, 0xbb, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5d, 0x22, 0xcc, 0xcc, 0x38, 0x54, 0xbb, 0x07, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x22, 0xcc, 0xcc, - 0xc5, 0x69, 0xf5, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x13, 0xcc, 0xcc, 0xc5, 0x69, 0xf5, 0x01, - 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x13, 0xcc, 0xcc, 0xfe, 0x7f, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0xcc, 0xcc, 0xfe, 0x7f, 0x03, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7f, 0x00, 0xcc, 0xcc, 0x37, 0x96, 0xf5, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x13, 0xcc, 0xcc, - 0x37, 0x96, 0xf5, 0x01, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x13, 0xcc, 0xcc, 0xc3, 0xab, 0xbb, 0x07, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0x22, 0xcc, 0xcc, 0xc3, 0xab, 0xbb, 0x07, 0xf8, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xa2, 0x22, 0xcc, 0xcc, 0xfb, 0xbf, 0x29, 0x11, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xae, 0x2f, 0xcc, 0xcc, 0xfb, 0xbf, 0x29, 0x11, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0x2f, 0xcc, 0xcc, - 0x41, 0xd2, 0xf4, 0x1d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x3a, 0xcc, 0xcc, 0x41, 0xd2, 0xf4, 0x1d, - 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x3a, 0xcc, 0xcc, 0x07, 0xe2, 0xba, 0x2d, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xc5, 0x45, 0xcc, 0xcc, 0x07, 0xe2, 0xba, 0x2d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xc5, 0x45, 0xcc, 0xcc, 0xd3, 0xee, 0x01, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x51, 0xcc, 0xcc, - 0xd3, 0xee, 0x01, 0x40, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x51, 0xcc, 0xcc, 0x41, 0xf8, 0x38, 0x54, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x5d, 0xcc, 0xcc, 0x41, 0xf8, 0x38, 0x54, 0xf8, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xdd, 0x5d, 0xcc, 0xcc, 0x07, 0xfe, 0xc5, 0x69, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xec, 0x6c, 0xcc, 0xcc, 0x07, 0xfe, 0xc5, 0x69, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x6c, 0xcc, 0xcc, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0a, 0x00, - 0x00, 0x0a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x13, - 0x00, 0x00, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x15, 0x00, 0x00, - 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x01, 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1e, 0x00, - 0x00, 0x1e, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x01, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x01, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x26, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x26, 0x00, 0x00, 0x28, - 0x00, 0x00, 0x28, 0x00, 0x00, 0x26, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x26, 0x00, 0x00, 0x2a, 0x00, 0x00, - 0x2a, 0x00, 0x00, 0x26, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x26, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, - 0x00, 0x26, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x26, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x26, - 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x26, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x26, 0x00, 0x00, - 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x26, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x26, 0x00, 0x00, 0x33, 0x00, - 0x00, 0x33, 0x00, 0x00, 0x26, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x26, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, - 0x00, 0x00, 0x26, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x26, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, - 0x26, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x26, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x26, 0x00, - 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3c, - 0x00, 0x00, 0x3c, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3e, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x26, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x26, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, - 0x00, 0x26, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x26, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x26, - 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x26, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x26, 0x00, 0x00, - 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x26, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x26, 0x00, 0x00, 0x47, 0x00, - 0x00, 0x47, 0x00, 0x00, 0x26, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x26, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, - 0x00, 0x00, 0x26, 0x00, 0x00, 0x25, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4a, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4e, 0x00, - 0x00, 0x4f, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x50, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x50, 0x00, 0x00, 0x51, - 0x00, 0x00, 0x51, 0x00, 0x00, 0x50, 0x00, 0x00, 0x52, 0x00, 0x00, 0x51, 0x00, 0x00, 0x52, 0x00, 0x00, 0x53, 0x00, 0x00, - 0x53, 0x00, 0x00, 0x52, 0x00, 0x00, 0x54, 0x00, 0x00, 0x53, 0x00, 0x00, 0x54, 0x00, 0x00, 0x55, 0x00, 0x00, 0x55, 0x00, - 0x00, 0x54, 0x00, 0x00, 0x56, 0x00, 0x00, 0x55, 0x00, 0x00, 0x56, 0x00, 0x00, 0x57, 0x00, 0x00, 0x57, 0x00, 0x00, 0x56, - 0x00, 0x00, 0x58, 0x00, 0x00, 0x57, 0x00, 0x00, 0x58, 0x00, 0x00, 0x59, 0x00, 0x00, 0x59, 0x00, 0x00, 0x58, 0x00, 0x00, - 0x5a, 0x00, 0x00, 0x59, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5c, 0x00, - 0x00, 0x5b, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x5d, - 0x00, 0x00, 0x5e, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x60, 0x00, 0x00, 0x5f, 0x00, 0x00, - 0x60, 0x00, 0x00, 0x61, 0x00, 0x00, 0x61, 0x00, 0x00, 0x60, 0x00, 0x00, 0x62, 0x00, 0x00, 0x61, 0x00, 0x00, 0x62, 0x00, - 0x00, 0x63, 0x00, 0x00, 0x63, 0x00, 0x00, 0x62, 0x00, 0x00, 0x64, 0x00, 0x00, 0x63, 0x00, 0x00, 0x64, 0x00, 0x00, 0x65, - 0x00, 0x00, 0x65, 0x00, 0x00, 0x64, 0x00, 0x00, 0x66, 0x00, 0x00, 0x65, 0x00, 0x00, 0x66, 0x00, 0x00, 0x67, 0x00, 0x00, - 0x67, 0x00, 0x00, 0x66, 0x00, 0x00, 0x68, 0x00, 0x00, 0x67, 0x00, 0x00, 0x68, 0x00, 0x00, 0x69, 0x00, 0x00, 0x69, 0x00, - 0x00, 0x68, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x69, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6a, - 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x6c, 0x00, 0x00, - 0x6e, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x70, 0x00, - 0x00, 0x6f, 0x00, 0x00, 0x70, 0x00, 0x00, 0x71, 0x00, 0x00, 0x71, 0x00, 0x00, 0x70, 0x00, 0x00, 0x72, 0x00, 0x00, 0x71, - 0x00, 0x00, 0x72, 0x00, 0x00, 0x73, 0x00, 0x00, 0x73, 0x00, 0x00, 0x72, 0x00, 0x00, 0x74, 0x00, 0x00, 0x73, 0x00, 0x00, - 0x74, 0x00, 0x00, 0x75, 0x00, 0x00, 0x75, 0x00, 0x00, 0x74, 0x00, 0x00, 0x76, 0x00, 0x00, 0x75, 0x00, 0x00, 0x76, 0x00, - 0x00, 0x77, 0x00, 0x00, 0x77, 0x00, 0x00, 0x76, 0x00, 0x00, 0x78, 0x00, 0x00, 0x77, 0x00, 0x00, 0x78, 0x00, 0x00, 0x79, - 0x00, 0x00, 0x79, 0x00, 0x00, 0x78, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x79, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x7b, 0x00, 0x00, - 0x7b, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7d, 0x00, - 0x00, 0x7c, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7e, - 0x00, 0x00, 0x80, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, 0x00, 0x80, 0x00, 0x00, - 0x82, 0x00, 0x00, 0x81, 0x00, 0x00, 0x82, 0x00, 0x00, 0x83, 0x00, 0x00, 0x83, 0x00, 0x00, 0x82, 0x00, 0x00, 0x84, 0x00, - 0x00, 0x83, 0x00, 0x00, 0x84, 0x00, 0x00, 0x85, 0x00, 0x00, 0x85, 0x00, 0x00, 0x84, 0x00, 0x00, 0x86, 0x00, 0x00, 0x85, - 0x00, 0x00, 0x86, 0x00, 0x00, 0x87, 0x00, 0x00, 0x87, 0x00, 0x00, 0x86, 0x00, 0x00, 0x88, 0x00, 0x00, 0x87, 0x00, 0x00, - 0x88, 0x00, 0x00, 0x89, 0x00, 0x00, 0x89, 0x00, 0x00, 0x88, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x89, 0x00, 0x00, 0x8a, 0x00, - 0x00, 0x8b, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8d, - 0x00, 0x00, 0x8d, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x8f, 0x00, 0x00, - 0x8f, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x90, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x90, 0x00, 0x00, 0x91, 0x00, 0x00, 0x91, 0x00, - 0x00, 0x90, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x91, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x23, 0x00, - 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, - 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x21, 0x00, 0x00, 0x22, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x20, 0x00, 0x00, 0x21, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x21, 0x00, - 0x00, 0x21, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, - 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, - 0x1d, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1c, 0x00, - 0x00, 0x1d, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1c, - 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1b, 0x00, 0x00, - 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x19, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x19, 0x00, - 0x00, 0x19, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x18, 0x00, 0x00, 0x19, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, - 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x17, 0x00, 0x00, 0x18, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x16, 0x00, 0x00, 0x17, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x17, 0x00, - 0x00, 0x17, 0x00, 0x00, 0x15, 0x00, 0x00, 0x16, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, - 0x00, 0x00, 0x14, 0x00, 0x00, 0x15, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, - 0x13, 0x00, 0x00, 0x14, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x12, 0x00, - 0x00, 0x13, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x11, 0x00, 0x00, 0x12, - 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x10, 0x00, 0x00, 0x11, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0f, 0x00, - 0x00, 0x0f, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, - 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, - 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0d, 0x00, - 0x00, 0x0d, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, - 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x08, 0x00, - 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x05, 0x00, - 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, - 0x00, 0x03, 0x00, 0x00, 0x25, 0x00, 0x00, 0x27, 0x00, 0x00, 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, - 0x00, 0x00, 0x25, 0x00, 0x00, 0x49, 0x00, 0x00, 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, - 0x27, 0x00, 0x00, 0x28, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, - 0x00, 0x29, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x2a, - 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2b, 0x00, 0x00, - 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2b, 0x00, - 0x00, 0x2b, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, - 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, - 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2f, 0x00, - 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x30, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, - 0x00, 0x00, 0x30, 0x00, 0x00, 0x31, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, - 0x31, 0x00, 0x00, 0x32, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, - 0x00, 0x33, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x34, - 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x36, 0x00, 0x00, 0x35, 0x00, - 0x00, 0x35, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x37, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, - 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, - 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, - 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, - 0x3b, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, - 0x00, 0x3d, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3e, - 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3f, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f, 0x00, - 0x00, 0x3f, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x41, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, - 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x42, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, - 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x43, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x43, 0x00, - 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x44, 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, - 0x00, 0x00, 0x44, 0x00, 0x00, 0x45, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, - 0x45, 0x00, 0x00, 0x46, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, - 0x00, 0x47, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x48, - 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x49, 0x00, 0x00, - 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x24, 0x00, 0x00, 0x01, 0x24, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, - 0x24, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x01, 0x24, 0x00, 0x00, 0x01, 0x23, 0x00, 0x00, 0x02, - 0x23, 0x00, 0x00, 0x03, 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x02, 0x23, 0x00, 0x00, 0x01, 0x23, 0x00, 0x00, 0x01, - 0x22, 0x00, 0x00, 0x02, 0x22, 0x00, 0x00, 0x03, 0x22, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x02, 0x22, 0x00, 0x00, 0x01, - 0x22, 0x00, 0x00, 0x01, 0x21, 0x00, 0x00, 0x02, 0x21, 0x00, 0x00, 0x03, 0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x02, - 0x21, 0x00, 0x00, 0x01, 0x21, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x01, 0x1f, 0x00, 0x00, 0x02, 0x1f, 0x00, 0x00, 0x03, - 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x02, 0x1f, 0x00, 0x00, 0x01, 0x1f, 0x00, 0x00, 0x01, 0x1e, 0x00, 0x00, 0x02, - 0x1e, 0x00, 0x00, 0x03, 0x1e, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x02, 0x1e, 0x00, 0x00, 0x01, 0x1e, 0x00, 0x00, 0x01, - 0x1d, 0x00, 0x00, 0x02, 0x1d, 0x00, 0x00, 0x03, 0x1d, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x02, 0x1d, 0x00, 0x00, 0x01, - 0x1d, 0x00, 0x00, 0x01, 0x1c, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x03, 0x1c, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x02, - 0x1c, 0x00, 0x00, 0x01, 0x1c, 0x00, 0x00, 0x01, 0x1b, 0x00, 0x00, 0x02, 0x1b, 0x00, 0x00, 0x03, 0x1b, 0x00, 0x00, 0x00, - 0x1a, 0x00, 0x00, 0x02, 0x1b, 0x00, 0x00, 0x01, 0x1b, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x00, 0x02, 0x1a, 0x00, 0x00, 0x03, - 0x1a, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x02, 0x1a, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x00, 0x01, 0x19, 0x00, 0x00, 0x02, - 0x19, 0x00, 0x00, 0x03, 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x02, 0x19, 0x00, 0x00, 0x01, 0x19, 0x00, 0x00, 0x01, - 0x18, 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x03, 0x18, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x01, - 0x18, 0x00, 0x00, 0x01, 0x17, 0x00, 0x00, 0x02, 0x17, 0x00, 0x00, 0x03, 0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x02, - 0x17, 0x00, 0x00, 0x01, 0x17, 0x00, 0x00, 0x01, 0x16, 0x00, 0x00, 0x02, 0x16, 0x00, 0x00, 0x03, 0x16, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x00, 0x02, 0x16, 0x00, 0x00, 0x01, 0x16, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00, 0x02, 0x15, 0x00, 0x00, 0x03, - 0x15, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x02, 0x15, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x02, - 0x14, 0x00, 0x00, 0x03, 0x14, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x01, - 0x13, 0x00, 0x00, 0x02, 0x13, 0x00, 0x00, 0x03, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x02, 0x13, 0x00, 0x00, 0x01, - 0x13, 0x00, 0x00, 0x01, 0x12, 0x00, 0x00, 0x02, 0x12, 0x00, 0x00, 0x03, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x02, - 0x12, 0x00, 0x00, 0x01, 0x12, 0x00, 0x00, 0x01, 0x11, 0x00, 0x00, 0x02, 0x11, 0x00, 0x00, 0x03, 0x11, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x02, 0x11, 0x00, 0x00, 0x01, 0x11, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x03, - 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x01, 0x0f, 0x00, 0x00, 0x02, - 0x0f, 0x00, 0x00, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x02, 0x0f, 0x00, 0x00, 0x01, 0x0f, 0x00, 0x00, 0x01, - 0x0e, 0x00, 0x00, 0x02, 0x0e, 0x00, 0x00, 0x03, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x02, 0x0e, 0x00, 0x00, 0x01, - 0x0e, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x00, 0x02, 0x0d, 0x00, 0x00, 0x03, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x02, - 0x0d, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x03, 0x0c, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x00, 0x03, - 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x00, 0x02, - 0x0a, 0x00, 0x00, 0x03, 0x0a, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x00, 0x01, - 0x09, 0x00, 0x00, 0x02, 0x09, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x02, 0x09, 0x00, 0x00, 0x01, - 0x09, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x02, - 0x08, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x03, - 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x02, - 0x05, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, - 0x04, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x01, - 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, - 0x03, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x03, 0x27, 0x00, 0x00, 0x00, - 0x25, 0x00, 0x00, 0x02, 0x27, 0x00, 0x00, 0x01, 0x27, 0x00, 0x00, 0x01, 0x25, 0x00, 0x00, 0x02, 0x25, 0x00, 0x00, 0x03, - 0x49, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x02, 0x49, 0x00, 0x00, 0x01, 0x49, 0x00, 0x00, 0x01, 0x25, 0x00, 0x00, 0x02, - 0x25, 0x00, 0x00, 0x03, 0x28, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x01, 0x28, 0x00, 0x00, 0x01, - 0x27, 0x00, 0x00, 0x02, 0x27, 0x00, 0x00, 0x03, 0x29, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x02, 0x29, 0x00, 0x00, 0x01, - 0x29, 0x00, 0x00, 0x01, 0x28, 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x03, 0x2a, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x02, - 0x2a, 0x00, 0x00, 0x01, 0x2a, 0x00, 0x00, 0x01, 0x29, 0x00, 0x00, 0x02, 0x29, 0x00, 0x00, 0x03, 0x2b, 0x00, 0x00, 0x00, - 0x2a, 0x00, 0x00, 0x02, 0x2b, 0x00, 0x00, 0x01, 0x2b, 0x00, 0x00, 0x01, 0x2a, 0x00, 0x00, 0x02, 0x2a, 0x00, 0x00, 0x03, - 0x2c, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x00, 0x01, 0x2b, 0x00, 0x00, 0x02, - 0x2b, 0x00, 0x00, 0x03, 0x2d, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x02, 0x2d, 0x00, 0x00, 0x01, 0x2d, 0x00, 0x00, 0x01, - 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x03, 0x2e, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x02, 0x2e, 0x00, 0x00, 0x01, - 0x2e, 0x00, 0x00, 0x01, 0x2d, 0x00, 0x00, 0x02, 0x2d, 0x00, 0x00, 0x03, 0x2f, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x02, - 0x2f, 0x00, 0x00, 0x01, 0x2f, 0x00, 0x00, 0x01, 0x2e, 0x00, 0x00, 0x02, 0x2e, 0x00, 0x00, 0x03, 0x30, 0x00, 0x00, 0x00, - 0x2f, 0x00, 0x00, 0x02, 0x30, 0x00, 0x00, 0x01, 0x30, 0x00, 0x00, 0x01, 0x2f, 0x00, 0x00, 0x02, 0x2f, 0x00, 0x00, 0x03, - 0x31, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x02, 0x31, 0x00, 0x00, 0x01, 0x31, 0x00, 0x00, 0x01, 0x30, 0x00, 0x00, 0x02, - 0x30, 0x00, 0x00, 0x03, 0x32, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x02, 0x32, 0x00, 0x00, 0x01, 0x32, 0x00, 0x00, 0x01, - 0x31, 0x00, 0x00, 0x02, 0x31, 0x00, 0x00, 0x03, 0x33, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x02, 0x33, 0x00, 0x00, 0x01, - 0x33, 0x00, 0x00, 0x01, 0x32, 0x00, 0x00, 0x02, 0x32, 0x00, 0x00, 0x03, 0x34, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x02, - 0x34, 0x00, 0x00, 0x01, 0x34, 0x00, 0x00, 0x01, 0x33, 0x00, 0x00, 0x02, 0x33, 0x00, 0x00, 0x03, 0x35, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x02, 0x35, 0x00, 0x00, 0x01, 0x35, 0x00, 0x00, 0x01, 0x34, 0x00, 0x00, 0x02, 0x34, 0x00, 0x00, 0x03, - 0x36, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x02, 0x36, 0x00, 0x00, 0x01, 0x36, 0x00, 0x00, 0x01, 0x35, 0x00, 0x00, 0x02, - 0x35, 0x00, 0x00, 0x03, 0x37, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x02, 0x37, 0x00, 0x00, 0x01, 0x37, 0x00, 0x00, 0x01, - 0x36, 0x00, 0x00, 0x02, 0x36, 0x00, 0x00, 0x03, 0x38, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x02, 0x38, 0x00, 0x00, 0x01, - 0x38, 0x00, 0x00, 0x01, 0x37, 0x00, 0x00, 0x02, 0x37, 0x00, 0x00, 0x03, 0x39, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x02, - 0x39, 0x00, 0x00, 0x01, 0x39, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x02, 0x38, 0x00, 0x00, 0x03, 0x3a, 0x00, 0x00, 0x00, - 0x39, 0x00, 0x00, 0x02, 0x3a, 0x00, 0x00, 0x01, 0x3a, 0x00, 0x00, 0x01, 0x39, 0x00, 0x00, 0x02, 0x39, 0x00, 0x00, 0x03, - 0x3b, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x02, 0x3b, 0x00, 0x00, 0x01, 0x3b, 0x00, 0x00, 0x01, 0x3a, 0x00, 0x00, 0x02, - 0x3a, 0x00, 0x00, 0x03, 0x3c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x02, 0x3c, 0x00, 0x00, 0x01, 0x3c, 0x00, 0x00, 0x01, - 0x3b, 0x00, 0x00, 0x02, 0x3b, 0x00, 0x00, 0x03, 0x3d, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x02, 0x3d, 0x00, 0x00, 0x01, - 0x3d, 0x00, 0x00, 0x01, 0x3c, 0x00, 0x00, 0x02, 0x3c, 0x00, 0x00, 0x03, 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x02, - 0x3e, 0x00, 0x00, 0x01, 0x3e, 0x00, 0x00, 0x01, 0x3d, 0x00, 0x00, 0x02, 0x3d, 0x00, 0x00, 0x03, 0x3f, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x02, 0x3f, 0x00, 0x00, 0x01, 0x3f, 0x00, 0x00, 0x01, 0x3e, 0x00, 0x00, 0x02, 0x3e, 0x00, 0x00, 0x03, - 0x40, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x01, 0x3f, 0x00, 0x00, 0x02, - 0x3f, 0x00, 0x00, 0x03, 0x41, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0x41, 0x00, 0x00, 0x01, 0x41, 0x00, 0x00, 0x01, - 0x40, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x03, 0x42, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x02, 0x42, 0x00, 0x00, 0x01, - 0x42, 0x00, 0x00, 0x01, 0x41, 0x00, 0x00, 0x02, 0x41, 0x00, 0x00, 0x03, 0x43, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x02, - 0x43, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x01, 0x42, 0x00, 0x00, 0x02, 0x42, 0x00, 0x00, 0x03, 0x44, 0x00, 0x00, 0x00, - 0x43, 0x00, 0x00, 0x02, 0x44, 0x00, 0x00, 0x01, 0x44, 0x00, 0x00, 0x01, 0x43, 0x00, 0x00, 0x02, 0x43, 0x00, 0x00, 0x03, - 0x45, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x02, 0x45, 0x00, 0x00, 0x01, 0x45, 0x00, 0x00, 0x01, 0x44, 0x00, 0x00, 0x02, - 0x44, 0x00, 0x00, 0x03, 0x46, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x02, 0x46, 0x00, 0x00, 0x01, 0x46, 0x00, 0x00, 0x01, - 0x45, 0x00, 0x00, 0x02, 0x45, 0x00, 0x00, 0x03, 0x47, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x02, 0x47, 0x00, 0x00, 0x01, - 0x47, 0x00, 0x00, 0x01, 0x46, 0x00, 0x00, 0x02, 0x46, 0x00, 0x00, 0x03, 0x48, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x02, - 0x48, 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, 0x01, 0x47, 0x00, 0x00, 0x02, 0x47, 0x00, 0x00, 0x03, 0x49, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x00, 0x02, 0x49, 0x00, 0x00, 0x01, 0x49, 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x03, - 0x4a, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x4c, 0x00, - 0x00, 0x4d, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x4f, - 0x00, 0x00, 0x4e, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x50, 0x00, 0x00, 0x51, 0x00, 0x00, - 0x50, 0x00, 0x00, 0x50, 0x00, 0x00, 0x51, 0x00, 0x00, 0x51, 0x00, 0x00, 0x52, 0x00, 0x00, 0x53, 0x00, 0x00, 0x52, 0x00, - 0x00, 0x52, 0x00, 0x00, 0x53, 0x00, 0x00, 0x53, 0x00, 0x00, 0x54, 0x00, 0x00, 0x55, 0x00, 0x00, 0x54, 0x00, 0x00, 0x54, - 0x00, 0x00, 0x55, 0x00, 0x00, 0x55, 0x00, 0x00, 0x56, 0x00, 0x00, 0x57, 0x00, 0x00, 0x56, 0x00, 0x00, 0x56, 0x00, 0x00, - 0x57, 0x00, 0x00, 0x57, 0x00, 0x00, 0x58, 0x00, 0x00, 0x59, 0x00, 0x00, 0x58, 0x00, 0x00, 0x58, 0x00, 0x00, 0x59, 0x00, - 0x00, 0x59, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x5b, - 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x5d, 0x00, 0x00, - 0x5e, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x60, 0x00, - 0x00, 0x61, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x61, 0x00, 0x00, 0x61, 0x00, 0x00, 0x62, 0x00, 0x00, 0x63, - 0x00, 0x00, 0x62, 0x00, 0x00, 0x62, 0x00, 0x00, 0x63, 0x00, 0x00, 0x63, 0x00, 0x00, 0x64, 0x00, 0x00, 0x65, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x64, 0x00, 0x00, 0x65, 0x00, 0x00, 0x65, 0x00, 0x00, 0x66, 0x00, 0x00, 0x67, 0x00, 0x00, 0x66, 0x00, - 0x00, 0x66, 0x00, 0x00, 0x67, 0x00, 0x00, 0x67, 0x00, 0x00, 0x68, 0x00, 0x00, 0x69, 0x00, 0x00, 0x68, 0x00, 0x00, 0x68, - 0x00, 0x00, 0x69, 0x00, 0x00, 0x69, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x6a, 0x00, 0x00, - 0x6b, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x6d, 0x00, - 0x00, 0x6d, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x6f, - 0x00, 0x00, 0x70, 0x00, 0x00, 0x71, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x71, 0x00, 0x00, 0x71, 0x00, 0x00, - 0x72, 0x00, 0x00, 0x73, 0x00, 0x00, 0x72, 0x00, 0x00, 0x72, 0x00, 0x00, 0x73, 0x00, 0x00, 0x73, 0x00, 0x00, 0x74, 0x00, - 0x00, 0x75, 0x00, 0x00, 0x74, 0x00, 0x00, 0x74, 0x00, 0x00, 0x75, 0x00, 0x00, 0x75, 0x00, 0x00, 0x76, 0x00, 0x00, 0x77, - 0x00, 0x00, 0x76, 0x00, 0x00, 0x76, 0x00, 0x00, 0x77, 0x00, 0x00, 0x77, 0x00, 0x00, 0x78, 0x00, 0x00, 0x79, 0x00, 0x00, - 0x78, 0x00, 0x00, 0x78, 0x00, 0x00, 0x79, 0x00, 0x00, 0x79, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7a, 0x00, - 0x00, 0x7a, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7c, - 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, - 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x81, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x81, 0x00, - 0x00, 0x81, 0x00, 0x00, 0x82, 0x00, 0x00, 0x83, 0x00, 0x00, 0x82, 0x00, 0x00, 0x82, 0x00, 0x00, 0x83, 0x00, 0x00, 0x83, - 0x00, 0x00, 0x84, 0x00, 0x00, 0x85, 0x00, 0x00, 0x84, 0x00, 0x00, 0x84, 0x00, 0x00, 0x85, 0x00, 0x00, 0x85, 0x00, 0x00, - 0x86, 0x00, 0x00, 0x87, 0x00, 0x00, 0x86, 0x00, 0x00, 0x86, 0x00, 0x00, 0x87, 0x00, 0x00, 0x87, 0x00, 0x00, 0x88, 0x00, - 0x00, 0x89, 0x00, 0x00, 0x88, 0x00, 0x00, 0x88, 0x00, 0x00, 0x89, 0x00, 0x00, 0x89, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x8b, - 0x00, 0x00, 0x8a, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8d, 0x00, 0x00, - 0x8c, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x8e, 0x00, - 0x00, 0x8e, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x90, 0x00, 0x00, 0x91, 0x00, 0x00, 0x90, 0x00, 0x00, 0x90, - 0x00, 0x00, 0x91, 0x00, 0x00, 0x91, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x02, 0x4b, 0x00, 0x00, 0x01, - 0x4b, 0x00, 0x00, 0x01, 0x4a, 0x00, 0x00, 0x02, 0x4a, 0x00, 0x00, 0x03, 0x4d, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x02, - 0x4d, 0x00, 0x00, 0x01, 0x4d, 0x00, 0x00, 0x01, 0x4c, 0x00, 0x00, 0x02, 0x4c, 0x00, 0x00, 0x03, 0x4f, 0x00, 0x00, 0x00, - 0x4e, 0x00, 0x00, 0x02, 0x4f, 0x00, 0x00, 0x01, 0x4f, 0x00, 0x00, 0x01, 0x4e, 0x00, 0x00, 0x02, 0x4e, 0x00, 0x00, 0x03, - 0x51, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x02, 0x51, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x02, - 0x50, 0x00, 0x00, 0x03, 0x53, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x02, 0x53, 0x00, 0x00, 0x01, 0x53, 0x00, 0x00, 0x01, - 0x52, 0x00, 0x00, 0x02, 0x52, 0x00, 0x00, 0x03, 0x55, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x02, 0x55, 0x00, 0x00, 0x01, - 0x55, 0x00, 0x00, 0x01, 0x54, 0x00, 0x00, 0x02, 0x54, 0x00, 0x00, 0x03, 0x57, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x02, - 0x57, 0x00, 0x00, 0x01, 0x57, 0x00, 0x00, 0x01, 0x56, 0x00, 0x00, 0x02, 0x56, 0x00, 0x00, 0x03, 0x59, 0x00, 0x00, 0x00, - 0x58, 0x00, 0x00, 0x02, 0x59, 0x00, 0x00, 0x01, 0x59, 0x00, 0x00, 0x01, 0x58, 0x00, 0x00, 0x02, 0x58, 0x00, 0x00, 0x03, - 0x5b, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x02, 0x5b, 0x00, 0x00, 0x01, 0x5b, 0x00, 0x00, 0x01, 0x5a, 0x00, 0x00, 0x02, - 0x5a, 0x00, 0x00, 0x03, 0x5d, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x02, 0x5d, 0x00, 0x00, 0x01, 0x5d, 0x00, 0x00, 0x01, - 0x5c, 0x00, 0x00, 0x02, 0x5c, 0x00, 0x00, 0x03, 0x5f, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x02, 0x5f, 0x00, 0x00, 0x01, - 0x5f, 0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x02, 0x5e, 0x00, 0x00, 0x03, 0x61, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x02, - 0x61, 0x00, 0x00, 0x01, 0x61, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, 0x02, 0x60, 0x00, 0x00, 0x03, 0x63, 0x00, 0x00, 0x00, - 0x62, 0x00, 0x00, 0x02, 0x63, 0x00, 0x00, 0x01, 0x63, 0x00, 0x00, 0x01, 0x62, 0x00, 0x00, 0x02, 0x62, 0x00, 0x00, 0x03, - 0x65, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x02, 0x65, 0x00, 0x00, 0x01, 0x65, 0x00, 0x00, 0x01, 0x64, 0x00, 0x00, 0x02, - 0x64, 0x00, 0x00, 0x03, 0x67, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x02, 0x67, 0x00, 0x00, 0x01, 0x67, 0x00, 0x00, 0x01, - 0x66, 0x00, 0x00, 0x02, 0x66, 0x00, 0x00, 0x03, 0x69, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x69, 0x00, 0x00, 0x01, - 0x69, 0x00, 0x00, 0x01, 0x68, 0x00, 0x00, 0x02, 0x68, 0x00, 0x00, 0x03, 0x6b, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x02, - 0x6b, 0x00, 0x00, 0x01, 0x6b, 0x00, 0x00, 0x01, 0x6a, 0x00, 0x00, 0x02, 0x6a, 0x00, 0x00, 0x03, 0x6d, 0x00, 0x00, 0x00, - 0x6c, 0x00, 0x00, 0x02, 0x6d, 0x00, 0x00, 0x01, 0x6d, 0x00, 0x00, 0x01, 0x6c, 0x00, 0x00, 0x02, 0x6c, 0x00, 0x00, 0x03, - 0x6f, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x02, 0x6f, 0x00, 0x00, 0x01, 0x6f, 0x00, 0x00, 0x01, 0x6e, 0x00, 0x00, 0x02, - 0x6e, 0x00, 0x00, 0x03, 0x71, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x02, 0x71, 0x00, 0x00, 0x01, 0x71, 0x00, 0x00, 0x01, - 0x70, 0x00, 0x00, 0x02, 0x70, 0x00, 0x00, 0x03, 0x73, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x02, 0x73, 0x00, 0x00, 0x01, - 0x73, 0x00, 0x00, 0x01, 0x72, 0x00, 0x00, 0x02, 0x72, 0x00, 0x00, 0x03, 0x75, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x02, - 0x75, 0x00, 0x00, 0x01, 0x75, 0x00, 0x00, 0x01, 0x74, 0x00, 0x00, 0x02, 0x74, 0x00, 0x00, 0x03, 0x77, 0x00, 0x00, 0x00, - 0x76, 0x00, 0x00, 0x02, 0x77, 0x00, 0x00, 0x01, 0x77, 0x00, 0x00, 0x01, 0x76, 0x00, 0x00, 0x02, 0x76, 0x00, 0x00, 0x03, - 0x79, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x02, 0x79, 0x00, 0x00, 0x01, 0x79, 0x00, 0x00, 0x01, 0x78, 0x00, 0x00, 0x02, - 0x78, 0x00, 0x00, 0x03, 0x7b, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x02, 0x7b, 0x00, 0x00, 0x01, 0x7b, 0x00, 0x00, 0x01, - 0x7a, 0x00, 0x00, 0x02, 0x7a, 0x00, 0x00, 0x03, 0x7d, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x02, 0x7d, 0x00, 0x00, 0x01, - 0x7d, 0x00, 0x00, 0x01, 0x7c, 0x00, 0x00, 0x02, 0x7c, 0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x02, - 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x7e, 0x00, 0x00, 0x02, 0x7e, 0x00, 0x00, 0x03, 0x81, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x02, 0x81, 0x00, 0x00, 0x01, 0x81, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x03, - 0x83, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x02, 0x83, 0x00, 0x00, 0x01, 0x83, 0x00, 0x00, 0x01, 0x82, 0x00, 0x00, 0x02, - 0x82, 0x00, 0x00, 0x03, 0x85, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x02, 0x85, 0x00, 0x00, 0x01, 0x85, 0x00, 0x00, 0x01, - 0x84, 0x00, 0x00, 0x02, 0x84, 0x00, 0x00, 0x03, 0x87, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x02, 0x87, 0x00, 0x00, 0x01, - 0x87, 0x00, 0x00, 0x01, 0x86, 0x00, 0x00, 0x02, 0x86, 0x00, 0x00, 0x03, 0x89, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x02, - 0x89, 0x00, 0x00, 0x01, 0x89, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x02, 0x88, 0x00, 0x00, 0x03, 0x8b, 0x00, 0x00, 0x00, - 0x8a, 0x00, 0x00, 0x02, 0x8b, 0x00, 0x00, 0x01, 0x8b, 0x00, 0x00, 0x01, 0x8a, 0x00, 0x00, 0x02, 0x8a, 0x00, 0x00, 0x03, - 0x8d, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x02, 0x8d, 0x00, 0x00, 0x01, 0x8d, 0x00, 0x00, 0x01, 0x8c, 0x00, 0x00, 0x02, - 0x8c, 0x00, 0x00, 0x03, 0x8f, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x02, 0x8f, 0x00, 0x00, 0x01, 0x8f, 0x00, 0x00, 0x01, - 0x8e, 0x00, 0x00, 0x02, 0x8e, 0x00, 0x00, 0x03, 0x91, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x02, 0x91, 0x00, 0x00, 0x01, - 0x91, 0x00, 0x00, 0x01, 0x90, 0x00, 0x00, 0x02, 0x90, 0x00, 0x00, 0x03, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, - 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xe4, 0x9a, - 0xf5, 0x8a, 0xe4, 0x9a, 0xf5, 0x8a, 0xe4, 0x9a, 0xf5, 0x8a, 0xe4, 0x9a, 0xf5, 0x8a, 0xe4, 0x9a, 0xf5, 0x8a, 0xe4, 0x9a, - 0xe4, 0x9a, 0xd6, 0xa8, 0xe4, 0x9a, 0xd6, 0xa8, 0xe4, 0x9a, 0xd6, 0xa8, 0xe4, 0x9a, 0xd6, 0xa8, 0xe4, 0x9a, 0xd6, 0xa8, - 0xe4, 0x9a, 0xd6, 0xa8, 0xd6, 0xa8, 0xca, 0xb4, 0xd6, 0xa8, 0xca, 0xb4, 0xd6, 0xa8, 0xca, 0xb4, 0xd6, 0xa8, 0xca, 0xb4, - 0xd6, 0xa8, 0xca, 0xb4, 0xd6, 0xa8, 0xca, 0xb4, 0xca, 0xb4, 0xbf, 0xbf, 0xca, 0xb4, 0xbf, 0xbf, 0xca, 0xb4, 0xbf, 0xbf, - 0xca, 0xb4, 0xbf, 0xbf, 0xca, 0xb4, 0xbf, 0xbf, 0xca, 0xb4, 0xbf, 0xbf, 0xbf, 0xbf, 0xb4, 0xca, 0xbf, 0xbf, 0xb4, 0xca, - 0xbf, 0xbf, 0xb4, 0xca, 0xbf, 0xbf, 0xb4, 0xca, 0xbf, 0xbf, 0xb4, 0xca, 0xbf, 0xbf, 0xb4, 0xca, 0xb4, 0xca, 0xa8, 0xd6, - 0xb4, 0xca, 0xa8, 0xd6, 0xb4, 0xca, 0xa8, 0xd6, 0xb4, 0xca, 0xa8, 0xd6, 0xb4, 0xca, 0xa8, 0xd6, 0xb4, 0xca, 0xa8, 0xd6, - 0xa8, 0xd6, 0x9a, 0xe4, 0xa8, 0xd6, 0x9a, 0xe4, 0xa8, 0xd6, 0x9a, 0xe4, 0xa8, 0xd6, 0x9a, 0xe4, 0xa8, 0xd6, 0x9a, 0xe4, - 0xa8, 0xd6, 0x9a, 0xe4, 0x9a, 0xe4, 0x8a, 0xf5, 0x9a, 0xe4, 0x8a, 0xf5, 0x9a, 0xe4, 0x8a, 0xf5, 0x9a, 0xe4, 0x8a, 0xf5, - 0x9a, 0xe4, 0x8a, 0xf5, 0x9a, 0xe4, 0x8a, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, - 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x8a, 0xf5, 0x75, 0xf5, 0x75, 0xf5, 0x65, 0xe4, 0x75, 0xf5, 0x65, 0xe4, - 0x75, 0xf5, 0x65, 0xe4, 0x75, 0xf5, 0x65, 0xe4, 0x75, 0xf5, 0x65, 0xe4, 0x75, 0xf5, 0x65, 0xe4, 0x65, 0xe4, 0x57, 0xd6, - 0x65, 0xe4, 0x57, 0xd6, 0x65, 0xe4, 0x57, 0xd6, 0x65, 0xe4, 0x57, 0xd6, 0x65, 0xe4, 0x57, 0xd6, 0x65, 0xe4, 0x57, 0xd6, - 0x57, 0xd6, 0x4b, 0xca, 0x57, 0xd6, 0x4b, 0xca, 0x57, 0xd6, 0x4b, 0xca, 0x57, 0xd6, 0x4b, 0xca, 0x57, 0xd6, 0x4b, 0xca, - 0x57, 0xd6, 0x4b, 0xca, 0x4b, 0xca, 0x40, 0xbf, 0x4b, 0xca, 0x40, 0xbf, 0x4b, 0xca, 0x40, 0xbf, 0x4b, 0xca, 0x40, 0xbf, - 0x4b, 0xca, 0x40, 0xbf, 0x4b, 0xca, 0x40, 0xbf, 0x40, 0xbf, 0x35, 0xb4, 0x40, 0xbf, 0x35, 0xb4, 0x40, 0xbf, 0x35, 0xb4, - 0x40, 0xbf, 0x35, 0xb4, 0x40, 0xbf, 0x35, 0xb4, 0x40, 0xbf, 0x35, 0xb4, 0x35, 0xb4, 0x29, 0xa8, 0x35, 0xb4, 0x29, 0xa8, - 0x35, 0xb4, 0x29, 0xa8, 0x35, 0xb4, 0x29, 0xa8, 0x35, 0xb4, 0x29, 0xa8, 0x35, 0xb4, 0x29, 0xa8, 0x29, 0xa8, 0x1b, 0x9a, - 0x29, 0xa8, 0x1b, 0x9a, 0x29, 0xa8, 0x1b, 0x9a, 0x29, 0xa8, 0x1b, 0x9a, 0x29, 0xa8, 0x1b, 0x9a, 0x29, 0xa8, 0x1b, 0x9a, - 0x1b, 0x9a, 0x0a, 0x8a, 0x1b, 0x9a, 0x0a, 0x8a, 0x1b, 0x9a, 0x0a, 0x8a, 0x1b, 0x9a, 0x0a, 0x8a, 0x1b, 0x9a, 0x0a, 0x8a, - 0x1b, 0x9a, 0x0a, 0x8a, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, - 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x75, 0x1b, 0x65, 0x0a, 0x75, 0x1b, 0x65, 0x0a, 0x75, 0x1b, 0x65, - 0x0a, 0x75, 0x1b, 0x65, 0x0a, 0x75, 0x1b, 0x65, 0x0a, 0x75, 0x1b, 0x65, 0x1b, 0x65, 0x29, 0x57, 0x1b, 0x65, 0x29, 0x57, - 0x1b, 0x65, 0x29, 0x57, 0x1b, 0x65, 0x29, 0x57, 0x1b, 0x65, 0x29, 0x57, 0x1b, 0x65, 0x29, 0x57, 0x29, 0x57, 0x35, 0x4b, - 0x29, 0x57, 0x35, 0x4b, 0x29, 0x57, 0x35, 0x4b, 0x29, 0x57, 0x35, 0x4b, 0x29, 0x57, 0x35, 0x4b, 0x29, 0x57, 0x35, 0x4b, - 0x35, 0x4b, 0x40, 0x40, 0x35, 0x4b, 0x40, 0x40, 0x35, 0x4b, 0x40, 0x40, 0x35, 0x4b, 0x40, 0x40, 0x35, 0x4b, 0x40, 0x40, - 0x35, 0x4b, 0x40, 0x40, 0x40, 0x40, 0x4b, 0x35, 0x40, 0x40, 0x4b, 0x35, 0x40, 0x40, 0x4b, 0x35, 0x40, 0x40, 0x4b, 0x35, - 0x40, 0x40, 0x4b, 0x35, 0x40, 0x40, 0x4b, 0x35, 0x4b, 0x35, 0x57, 0x29, 0x4b, 0x35, 0x57, 0x29, 0x4b, 0x35, 0x57, 0x29, - 0x4b, 0x35, 0x57, 0x29, 0x4b, 0x35, 0x57, 0x29, 0x4b, 0x35, 0x57, 0x29, 0x57, 0x29, 0x65, 0x1b, 0x57, 0x29, 0x65, 0x1b, - 0x57, 0x29, 0x65, 0x1b, 0x57, 0x29, 0x65, 0x1b, 0x57, 0x29, 0x65, 0x1b, 0x57, 0x29, 0x65, 0x1b, 0x65, 0x1b, 0x75, 0x0a, - 0x65, 0x1b, 0x75, 0x0a, 0x65, 0x1b, 0x75, 0x0a, 0x65, 0x1b, 0x75, 0x0a, 0x65, 0x1b, 0x75, 0x0a, 0x65, 0x1b, 0x75, 0x0a, - 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, 0x75, 0x0a, 0x8a, 0x0a, - 0x75, 0x0a, 0x8a, 0x0a, 0x8a, 0x0a, 0x9a, 0x1b, 0x8a, 0x0a, 0x9a, 0x1b, 0x8a, 0x0a, 0x9a, 0x1b, 0x8a, 0x0a, 0x9a, 0x1b, - 0x8a, 0x0a, 0x9a, 0x1b, 0x8a, 0x0a, 0x9a, 0x1b, 0x9a, 0x1b, 0xa8, 0x29, 0x9a, 0x1b, 0xa8, 0x29, 0x9a, 0x1b, 0xa8, 0x29, - 0x9a, 0x1b, 0xa8, 0x29, 0x9a, 0x1b, 0xa8, 0x29, 0x9a, 0x1b, 0xa8, 0x29, 0xa8, 0x29, 0xb4, 0x35, 0xa8, 0x29, 0xb4, 0x35, - 0xa8, 0x29, 0xb4, 0x35, 0xa8, 0x29, 0xb4, 0x35, 0xa8, 0x29, 0xb4, 0x35, 0xa8, 0x29, 0xb4, 0x35, 0xb4, 0x35, 0xbf, 0x40, - 0xb4, 0x35, 0xbf, 0x40, 0xb4, 0x35, 0xbf, 0x40, 0xb4, 0x35, 0xbf, 0x40, 0xb4, 0x35, 0xbf, 0x40, 0xb4, 0x35, 0xbf, 0x40, - 0xbf, 0x40, 0xca, 0x4b, 0xbf, 0x40, 0xca, 0x4b, 0xbf, 0x40, 0xca, 0x4b, 0xbf, 0x40, 0xca, 0x4b, 0xbf, 0x40, 0xca, 0x4b, - 0xbf, 0x40, 0xca, 0x4b, 0xca, 0x4b, 0xd6, 0x57, 0xca, 0x4b, 0xd6, 0x57, 0xca, 0x4b, 0xd6, 0x57, 0xca, 0x4b, 0xd6, 0x57, - 0xca, 0x4b, 0xd6, 0x57, 0xca, 0x4b, 0xd6, 0x57, 0xd6, 0x57, 0xe4, 0x65, 0xd6, 0x57, 0xe4, 0x65, 0xd6, 0x57, 0xe4, 0x65, - 0xd6, 0x57, 0xe4, 0x65, 0xd6, 0x57, 0xe4, 0x65, 0xd6, 0x57, 0xe4, 0x65, 0xe4, 0x65, 0xf5, 0x75, 0xe4, 0x65, 0xf5, 0x75, - 0xe4, 0x65, 0xf5, 0x75, 0xe4, 0x65, 0xf5, 0x75, 0xe4, 0x65, 0xf5, 0x75, 0xe4, 0x65, 0xf5, 0x75, 0x00, 0x00, 0x00, 0x24, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x23, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, - 0x00, 0x23, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x21, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, - 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x20, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, - 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, - 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, - 0x1d, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, - 0x00, 0x1b, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1a, - 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x19, 0x00, 0x00, - 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x18, 0x00, 0x00, 0x19, 0x00, - 0x00, 0x19, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x17, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, - 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x16, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x15, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x15, 0x00, - 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x14, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, - 0x00, 0x00, 0x14, 0x00, 0x00, 0x13, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, - 0x13, 0x00, 0x00, 0x12, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, - 0x00, 0x11, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x10, - 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0f, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0f, 0x00, - 0x00, 0x0f, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, - 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, - 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, - 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, - 0x00, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, - 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x05, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, - 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x27, 0x00, 0x00, 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, - 0x00, 0x00, 0x27, 0x00, 0x00, 0x28, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x29, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, - 0x00, 0x2a, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2b, - 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2c, 0x00, 0x00, - 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2c, 0x00, - 0x00, 0x2c, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, - 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, - 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x30, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x30, 0x00, - 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x31, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, - 0x00, 0x00, 0x31, 0x00, 0x00, 0x32, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, - 0x32, 0x00, 0x00, 0x33, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, - 0x00, 0x34, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, - 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x36, 0x00, 0x00, - 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x37, 0x00, 0x00, 0x36, 0x00, - 0x00, 0x36, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, - 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, - 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3a, 0x00, - 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, - 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, - 0x3c, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, - 0x00, 0x3e, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3f, - 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x40, 0x00, 0x00, - 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x41, 0x00, 0x00, 0x40, 0x00, - 0x00, 0x40, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x42, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, - 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x43, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, - 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x44, 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x44, 0x00, - 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x45, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, - 0x00, 0x00, 0x45, 0x00, 0x00, 0x46, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, - 0x46, 0x00, 0x00, 0x47, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, - 0x00, 0x48, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x49, - 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x25, 0x00, 0x00, - 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x02, 0x00, 0x00, 0x23, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x24, 0x00, 0x00, 0x21, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x23, 0x00, 0x00, 0x20, 0x00, 0x00, 0x23, 0x00, 0x00, 0x23, 0x00, 0x00, 0x20, 0x00, - 0x00, 0x20, 0x00, 0x00, 0x22, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, - 0x00, 0x00, 0x21, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x21, 0x00, 0x00, 0x21, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1f, 0x00, - 0x00, 0x1c, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1b, - 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1a, 0x00, 0x00, - 0x1d, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x19, 0x00, 0x00, 0x1c, 0x00, - 0x00, 0x1c, 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x1b, - 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x17, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1a, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00, 0x16, 0x00, 0x00, 0x19, 0x00, 0x00, 0x19, 0x00, 0x00, 0x16, 0x00, - 0x00, 0x16, 0x00, 0x00, 0x18, 0x00, 0x00, 0x15, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, - 0x00, 0x00, 0x17, 0x00, 0x00, 0x14, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x13, 0x00, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x00, 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x15, 0x00, - 0x00, 0x12, 0x00, 0x00, 0x15, 0x00, 0x00, 0x15, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x00, 0x11, - 0x00, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x13, 0x00, 0x00, 0x10, 0x00, 0x00, - 0x13, 0x00, 0x00, 0x13, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x12, 0x00, - 0x00, 0x12, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x11, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, - 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0c, 0x00, - 0x00, 0x0c, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, - 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0b, 0x00, - 0x00, 0x08, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x07, - 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x09, 0x00, 0x00, 0x06, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x05, 0x00, 0x00, 0x08, 0x00, - 0x00, 0x08, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, - 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x03, 0x00, 0x00, 0x24, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x00, 0x00, - 0x49, 0x00, 0x00, 0x28, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x25, 0x00, - 0x00, 0x29, 0x00, 0x00, 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x29, 0x00, 0x00, 0x29, 0x00, 0x00, 0x27, 0x00, 0x00, 0x2a, - 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x28, 0x00, 0x00, 0x2b, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x28, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x29, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x29, 0x00, - 0x00, 0x29, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x2a, - 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x2b, 0x00, 0x00, - 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x2f, 0x00, - 0x00, 0x2f, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x30, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, - 0x00, 0x00, 0x2e, 0x00, 0x00, 0x31, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, - 0x2f, 0x00, 0x00, 0x32, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x30, 0x00, - 0x00, 0x33, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x00, 0x00, 0x31, 0x00, 0x00, 0x34, - 0x00, 0x00, 0x31, 0x00, 0x00, 0x31, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, 0x00, 0x00, 0x32, 0x00, 0x00, 0x35, 0x00, 0x00, - 0x32, 0x00, 0x00, 0x32, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, 0x33, 0x00, 0x00, 0x36, 0x00, 0x00, 0x33, 0x00, - 0x00, 0x33, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x34, 0x00, 0x00, 0x37, 0x00, 0x00, 0x34, 0x00, 0x00, 0x34, - 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x35, 0x00, 0x00, 0x38, 0x00, 0x00, 0x35, 0x00, 0x00, 0x35, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x36, 0x00, 0x00, 0x39, 0x00, 0x00, 0x36, 0x00, 0x00, 0x36, 0x00, 0x00, 0x39, 0x00, - 0x00, 0x39, 0x00, 0x00, 0x37, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x37, 0x00, 0x00, 0x37, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, - 0x00, 0x00, 0x38, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, - 0x39, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x39, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3a, 0x00, - 0x00, 0x3d, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3e, - 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3f, 0x00, 0x00, - 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x40, 0x00, 0x00, 0x3d, 0x00, - 0x00, 0x3d, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x41, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3e, - 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x42, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, - 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x40, 0x00, 0x00, 0x43, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x43, 0x00, - 0x00, 0x43, 0x00, 0x00, 0x41, 0x00, 0x00, 0x44, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, - 0x00, 0x00, 0x42, 0x00, 0x00, 0x45, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, - 0x43, 0x00, 0x00, 0x46, 0x00, 0x00, 0x43, 0x00, 0x00, 0x43, 0x00, 0x00, 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x44, 0x00, - 0x00, 0x47, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x47, 0x00, 0x00, 0x47, 0x00, 0x00, 0x45, 0x00, 0x00, 0x48, - 0x00, 0x00, 0x45, 0x00, 0x00, 0x45, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x46, 0x00, 0x00, 0x49, 0x00, 0x00, - 0x46, 0x00, 0x00, 0x46, 0x00, 0x00, 0x49, 0x00, 0x00, 0x49, 0x00, 0x00, 0x47, 0x00, 0x00, 0x25, 0x00, 0x00, 0x47, 0x00, - 0x00, 0x47, 0x00, 0x00, 0x25, 0x00, 0x00, 0x25, 0x00, 0x00, 0x48, 0x00, 0x00, 0x27, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, - 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, 0x00, 0x24, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x36, 0x24, 0x00, 0x00, 0x06, - 0x24, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x4e, 0x23, 0x00, 0x00, 0x1e, 0x24, 0x00, 0x00, 0x36, - 0x23, 0x00, 0x00, 0x06, 0x23, 0x00, 0x00, 0x06, 0x24, 0x00, 0x00, 0x36, 0x24, 0x00, 0x00, 0x4e, 0x22, 0x00, 0x00, 0x1e, - 0x23, 0x00, 0x00, 0x36, 0x22, 0x00, 0x00, 0x06, 0x22, 0x00, 0x00, 0x06, 0x23, 0x00, 0x00, 0x36, 0x23, 0x00, 0x00, 0x4e, - 0x21, 0x00, 0x00, 0x1e, 0x22, 0x00, 0x00, 0x36, 0x21, 0x00, 0x00, 0x06, 0x21, 0x00, 0x00, 0x06, 0x22, 0x00, 0x00, 0x36, - 0x22, 0x00, 0x00, 0x4e, 0x20, 0x00, 0x00, 0x1e, 0x21, 0x00, 0x00, 0x36, 0x20, 0x00, 0x00, 0x06, 0x20, 0x00, 0x00, 0x06, - 0x21, 0x00, 0x00, 0x36, 0x21, 0x00, 0x00, 0x4e, 0x1f, 0x00, 0x00, 0x1e, 0x20, 0x00, 0x00, 0x36, 0x1f, 0x00, 0x00, 0x06, - 0x1f, 0x00, 0x00, 0x06, 0x20, 0x00, 0x00, 0x36, 0x20, 0x00, 0x00, 0x4e, 0x1e, 0x00, 0x00, 0x1e, 0x1f, 0x00, 0x00, 0x36, - 0x1e, 0x00, 0x00, 0x06, 0x1e, 0x00, 0x00, 0x06, 0x1f, 0x00, 0x00, 0x36, 0x1f, 0x00, 0x00, 0x4e, 0x1d, 0x00, 0x00, 0x1e, - 0x1e, 0x00, 0x00, 0x36, 0x1d, 0x00, 0x00, 0x06, 0x1d, 0x00, 0x00, 0x06, 0x1e, 0x00, 0x00, 0x36, 0x1e, 0x00, 0x00, 0x4e, - 0x1c, 0x00, 0x00, 0x1e, 0x1d, 0x00, 0x00, 0x36, 0x1c, 0x00, 0x00, 0x06, 0x1c, 0x00, 0x00, 0x06, 0x1d, 0x00, 0x00, 0x36, - 0x1d, 0x00, 0x00, 0x4e, 0x1b, 0x00, 0x00, 0x1e, 0x1c, 0x00, 0x00, 0x36, 0x1b, 0x00, 0x00, 0x06, 0x1b, 0x00, 0x00, 0x06, - 0x1c, 0x00, 0x00, 0x36, 0x1c, 0x00, 0x00, 0x4e, 0x1a, 0x00, 0x00, 0x1e, 0x1b, 0x00, 0x00, 0x36, 0x1a, 0x00, 0x00, 0x06, - 0x1a, 0x00, 0x00, 0x06, 0x1b, 0x00, 0x00, 0x36, 0x1b, 0x00, 0x00, 0x4e, 0x19, 0x00, 0x00, 0x1e, 0x1a, 0x00, 0x00, 0x36, - 0x19, 0x00, 0x00, 0x06, 0x19, 0x00, 0x00, 0x06, 0x1a, 0x00, 0x00, 0x36, 0x1a, 0x00, 0x00, 0x4e, 0x18, 0x00, 0x00, 0x1e, - 0x19, 0x00, 0x00, 0x36, 0x18, 0x00, 0x00, 0x06, 0x18, 0x00, 0x00, 0x06, 0x19, 0x00, 0x00, 0x36, 0x19, 0x00, 0x00, 0x4e, - 0x17, 0x00, 0x00, 0x1e, 0x18, 0x00, 0x00, 0x36, 0x17, 0x00, 0x00, 0x06, 0x17, 0x00, 0x00, 0x06, 0x18, 0x00, 0x00, 0x36, - 0x18, 0x00, 0x00, 0x4e, 0x16, 0x00, 0x00, 0x1e, 0x17, 0x00, 0x00, 0x36, 0x16, 0x00, 0x00, 0x06, 0x16, 0x00, 0x00, 0x06, - 0x17, 0x00, 0x00, 0x36, 0x17, 0x00, 0x00, 0x4e, 0x15, 0x00, 0x00, 0x1e, 0x16, 0x00, 0x00, 0x36, 0x15, 0x00, 0x00, 0x06, - 0x15, 0x00, 0x00, 0x06, 0x16, 0x00, 0x00, 0x36, 0x16, 0x00, 0x00, 0x4e, 0x14, 0x00, 0x00, 0x1e, 0x15, 0x00, 0x00, 0x36, - 0x14, 0x00, 0x00, 0x06, 0x14, 0x00, 0x00, 0x06, 0x15, 0x00, 0x00, 0x36, 0x15, 0x00, 0x00, 0x4e, 0x13, 0x00, 0x00, 0x1e, - 0x14, 0x00, 0x00, 0x36, 0x13, 0x00, 0x00, 0x06, 0x13, 0x00, 0x00, 0x06, 0x14, 0x00, 0x00, 0x36, 0x14, 0x00, 0x00, 0x4e, - 0x12, 0x00, 0x00, 0x1e, 0x13, 0x00, 0x00, 0x36, 0x12, 0x00, 0x00, 0x06, 0x12, 0x00, 0x00, 0x06, 0x13, 0x00, 0x00, 0x36, - 0x13, 0x00, 0x00, 0x4e, 0x11, 0x00, 0x00, 0x1e, 0x12, 0x00, 0x00, 0x36, 0x11, 0x00, 0x00, 0x06, 0x11, 0x00, 0x00, 0x06, - 0x12, 0x00, 0x00, 0x36, 0x12, 0x00, 0x00, 0x4e, 0x10, 0x00, 0x00, 0x1e, 0x11, 0x00, 0x00, 0x36, 0x10, 0x00, 0x00, 0x06, - 0x10, 0x00, 0x00, 0x06, 0x11, 0x00, 0x00, 0x36, 0x11, 0x00, 0x00, 0x4e, 0x0f, 0x00, 0x00, 0x1e, 0x10, 0x00, 0x00, 0x36, - 0x0f, 0x00, 0x00, 0x06, 0x0f, 0x00, 0x00, 0x06, 0x10, 0x00, 0x00, 0x36, 0x10, 0x00, 0x00, 0x4e, 0x0e, 0x00, 0x00, 0x1e, - 0x0f, 0x00, 0x00, 0x36, 0x0e, 0x00, 0x00, 0x06, 0x0e, 0x00, 0x00, 0x06, 0x0f, 0x00, 0x00, 0x36, 0x0f, 0x00, 0x00, 0x4e, - 0x0d, 0x00, 0x00, 0x1e, 0x0e, 0x00, 0x00, 0x36, 0x0d, 0x00, 0x00, 0x06, 0x0d, 0x00, 0x00, 0x06, 0x0e, 0x00, 0x00, 0x36, - 0x0e, 0x00, 0x00, 0x4e, 0x0c, 0x00, 0x00, 0x1e, 0x0d, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x06, - 0x0d, 0x00, 0x00, 0x36, 0x0d, 0x00, 0x00, 0x4e, 0x0b, 0x00, 0x00, 0x1e, 0x0c, 0x00, 0x00, 0x36, 0x0b, 0x00, 0x00, 0x06, - 0x0b, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x4e, 0x0a, 0x00, 0x00, 0x1e, 0x0b, 0x00, 0x00, 0x36, - 0x0a, 0x00, 0x00, 0x06, 0x0a, 0x00, 0x00, 0x06, 0x0b, 0x00, 0x00, 0x36, 0x0b, 0x00, 0x00, 0x4e, 0x09, 0x00, 0x00, 0x1e, - 0x0a, 0x00, 0x00, 0x36, 0x09, 0x00, 0x00, 0x06, 0x09, 0x00, 0x00, 0x06, 0x0a, 0x00, 0x00, 0x36, 0x0a, 0x00, 0x00, 0x4e, - 0x08, 0x00, 0x00, 0x1e, 0x09, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x06, 0x08, 0x00, 0x00, 0x06, 0x09, 0x00, 0x00, 0x36, - 0x09, 0x00, 0x00, 0x4e, 0x07, 0x00, 0x00, 0x1e, 0x08, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x06, 0x07, 0x00, 0x00, 0x06, - 0x08, 0x00, 0x00, 0x36, 0x08, 0x00, 0x00, 0x4e, 0x06, 0x00, 0x00, 0x1e, 0x07, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x06, - 0x06, 0x00, 0x00, 0x06, 0x07, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x4e, 0x05, 0x00, 0x00, 0x1e, 0x06, 0x00, 0x00, 0x36, - 0x05, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x4e, 0x04, 0x00, 0x00, 0x1e, - 0x05, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x36, 0x05, 0x00, 0x00, 0x4e, - 0x03, 0x00, 0x00, 0x1e, 0x04, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x36, - 0x04, 0x00, 0x00, 0x4e, 0x02, 0x00, 0x00, 0x1e, 0x03, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x06, - 0x03, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x1e, 0x02, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x06, - 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x4e, 0x27, 0x00, 0x00, 0x1e, 0x25, 0x00, 0x00, 0x36, - 0x27, 0x00, 0x00, 0x06, 0x27, 0x00, 0x00, 0x06, 0x25, 0x00, 0x00, 0x36, 0x25, 0x00, 0x00, 0x4e, 0x28, 0x00, 0x00, 0x1e, - 0x27, 0x00, 0x00, 0x36, 0x28, 0x00, 0x00, 0x06, 0x28, 0x00, 0x00, 0x06, 0x27, 0x00, 0x00, 0x36, 0x27, 0x00, 0x00, 0x4e, - 0x29, 0x00, 0x00, 0x1e, 0x28, 0x00, 0x00, 0x36, 0x29, 0x00, 0x00, 0x06, 0x29, 0x00, 0x00, 0x06, 0x28, 0x00, 0x00, 0x36, - 0x28, 0x00, 0x00, 0x4e, 0x2a, 0x00, 0x00, 0x1e, 0x29, 0x00, 0x00, 0x36, 0x2a, 0x00, 0x00, 0x06, 0x2a, 0x00, 0x00, 0x06, - 0x29, 0x00, 0x00, 0x36, 0x29, 0x00, 0x00, 0x4e, 0x2b, 0x00, 0x00, 0x1e, 0x2a, 0x00, 0x00, 0x36, 0x2b, 0x00, 0x00, 0x06, - 0x2b, 0x00, 0x00, 0x06, 0x2a, 0x00, 0x00, 0x36, 0x2a, 0x00, 0x00, 0x4e, 0x2c, 0x00, 0x00, 0x1e, 0x2b, 0x00, 0x00, 0x36, - 0x2c, 0x00, 0x00, 0x06, 0x2c, 0x00, 0x00, 0x06, 0x2b, 0x00, 0x00, 0x36, 0x2b, 0x00, 0x00, 0x4e, 0x2d, 0x00, 0x00, 0x1e, - 0x2c, 0x00, 0x00, 0x36, 0x2d, 0x00, 0x00, 0x06, 0x2d, 0x00, 0x00, 0x06, 0x2c, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x4e, - 0x2e, 0x00, 0x00, 0x1e, 0x2d, 0x00, 0x00, 0x36, 0x2e, 0x00, 0x00, 0x06, 0x2e, 0x00, 0x00, 0x06, 0x2d, 0x00, 0x00, 0x36, - 0x2d, 0x00, 0x00, 0x4e, 0x2f, 0x00, 0x00, 0x1e, 0x2e, 0x00, 0x00, 0x36, 0x2f, 0x00, 0x00, 0x06, 0x2f, 0x00, 0x00, 0x06, - 0x2e, 0x00, 0x00, 0x36, 0x2e, 0x00, 0x00, 0x4e, 0x30, 0x00, 0x00, 0x1e, 0x2f, 0x00, 0x00, 0x36, 0x30, 0x00, 0x00, 0x06, - 0x30, 0x00, 0x00, 0x06, 0x2f, 0x00, 0x00, 0x36, 0x2f, 0x00, 0x00, 0x4e, 0x31, 0x00, 0x00, 0x1e, 0x30, 0x00, 0x00, 0x36, - 0x31, 0x00, 0x00, 0x06, 0x31, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x36, 0x30, 0x00, 0x00, 0x4e, 0x32, 0x00, 0x00, 0x1e, - 0x31, 0x00, 0x00, 0x36, 0x32, 0x00, 0x00, 0x06, 0x32, 0x00, 0x00, 0x06, 0x31, 0x00, 0x00, 0x36, 0x31, 0x00, 0x00, 0x4e, - 0x33, 0x00, 0x00, 0x1e, 0x32, 0x00, 0x00, 0x36, 0x33, 0x00, 0x00, 0x06, 0x33, 0x00, 0x00, 0x06, 0x32, 0x00, 0x00, 0x36, - 0x32, 0x00, 0x00, 0x4e, 0x34, 0x00, 0x00, 0x1e, 0x33, 0x00, 0x00, 0x36, 0x34, 0x00, 0x00, 0x06, 0x34, 0x00, 0x00, 0x06, - 0x33, 0x00, 0x00, 0x36, 0x33, 0x00, 0x00, 0x4e, 0x35, 0x00, 0x00, 0x1e, 0x34, 0x00, 0x00, 0x36, 0x35, 0x00, 0x00, 0x06, - 0x35, 0x00, 0x00, 0x06, 0x34, 0x00, 0x00, 0x36, 0x34, 0x00, 0x00, 0x4e, 0x36, 0x00, 0x00, 0x1e, 0x35, 0x00, 0x00, 0x36, - 0x36, 0x00, 0x00, 0x06, 0x36, 0x00, 0x00, 0x06, 0x35, 0x00, 0x00, 0x36, 0x35, 0x00, 0x00, 0x4e, 0x37, 0x00, 0x00, 0x1e, - 0x36, 0x00, 0x00, 0x36, 0x37, 0x00, 0x00, 0x06, 0x37, 0x00, 0x00, 0x06, 0x36, 0x00, 0x00, 0x36, 0x36, 0x00, 0x00, 0x4e, - 0x38, 0x00, 0x00, 0x1e, 0x37, 0x00, 0x00, 0x36, 0x38, 0x00, 0x00, 0x06, 0x38, 0x00, 0x00, 0x06, 0x37, 0x00, 0x00, 0x36, - 0x37, 0x00, 0x00, 0x4e, 0x39, 0x00, 0x00, 0x1e, 0x38, 0x00, 0x00, 0x36, 0x39, 0x00, 0x00, 0x06, 0x39, 0x00, 0x00, 0x06, - 0x38, 0x00, 0x00, 0x36, 0x38, 0x00, 0x00, 0x4e, 0x3a, 0x00, 0x00, 0x1e, 0x39, 0x00, 0x00, 0x36, 0x3a, 0x00, 0x00, 0x06, - 0x3a, 0x00, 0x00, 0x06, 0x39, 0x00, 0x00, 0x36, 0x39, 0x00, 0x00, 0x4e, 0x3b, 0x00, 0x00, 0x1e, 0x3a, 0x00, 0x00, 0x36, - 0x3b, 0x00, 0x00, 0x06, 0x3b, 0x00, 0x00, 0x06, 0x3a, 0x00, 0x00, 0x36, 0x3a, 0x00, 0x00, 0x4e, 0x3c, 0x00, 0x00, 0x1e, - 0x3b, 0x00, 0x00, 0x36, 0x3c, 0x00, 0x00, 0x06, 0x3c, 0x00, 0x00, 0x06, 0x3b, 0x00, 0x00, 0x36, 0x3b, 0x00, 0x00, 0x4e, - 0x3d, 0x00, 0x00, 0x1e, 0x3c, 0x00, 0x00, 0x36, 0x3d, 0x00, 0x00, 0x06, 0x3d, 0x00, 0x00, 0x06, 0x3c, 0x00, 0x00, 0x36, - 0x3c, 0x00, 0x00, 0x4e, 0x3e, 0x00, 0x00, 0x1e, 0x3d, 0x00, 0x00, 0x36, 0x3e, 0x00, 0x00, 0x06, 0x3e, 0x00, 0x00, 0x06, - 0x3d, 0x00, 0x00, 0x36, 0x3d, 0x00, 0x00, 0x4e, 0x3f, 0x00, 0x00, 0x1e, 0x3e, 0x00, 0x00, 0x36, 0x3f, 0x00, 0x00, 0x06, - 0x3f, 0x00, 0x00, 0x06, 0x3e, 0x00, 0x00, 0x36, 0x3e, 0x00, 0x00, 0x4e, 0x40, 0x00, 0x00, 0x1e, 0x3f, 0x00, 0x00, 0x36, - 0x40, 0x00, 0x00, 0x06, 0x40, 0x00, 0x00, 0x06, 0x3f, 0x00, 0x00, 0x36, 0x3f, 0x00, 0x00, 0x4e, 0x41, 0x00, 0x00, 0x1e, - 0x40, 0x00, 0x00, 0x36, 0x41, 0x00, 0x00, 0x06, 0x41, 0x00, 0x00, 0x06, 0x40, 0x00, 0x00, 0x36, 0x40, 0x00, 0x00, 0x4e, - 0x42, 0x00, 0x00, 0x1e, 0x41, 0x00, 0x00, 0x36, 0x42, 0x00, 0x00, 0x06, 0x42, 0x00, 0x00, 0x06, 0x41, 0x00, 0x00, 0x36, - 0x41, 0x00, 0x00, 0x4e, 0x43, 0x00, 0x00, 0x1e, 0x42, 0x00, 0x00, 0x36, 0x43, 0x00, 0x00, 0x06, 0x43, 0x00, 0x00, 0x06, - 0x42, 0x00, 0x00, 0x36, 0x42, 0x00, 0x00, 0x4e, 0x44, 0x00, 0x00, 0x1e, 0x43, 0x00, 0x00, 0x36, 0x44, 0x00, 0x00, 0x06, - 0x44, 0x00, 0x00, 0x06, 0x43, 0x00, 0x00, 0x36, 0x43, 0x00, 0x00, 0x4e, 0x45, 0x00, 0x00, 0x1e, 0x44, 0x00, 0x00, 0x36, - 0x45, 0x00, 0x00, 0x06, 0x45, 0x00, 0x00, 0x06, 0x44, 0x00, 0x00, 0x36, 0x44, 0x00, 0x00, 0x4e, 0x46, 0x00, 0x00, 0x1e, - 0x45, 0x00, 0x00, 0x36, 0x46, 0x00, 0x00, 0x06, 0x46, 0x00, 0x00, 0x06, 0x45, 0x00, 0x00, 0x36, 0x45, 0x00, 0x00, 0x4e, - 0x47, 0x00, 0x00, 0x1e, 0x46, 0x00, 0x00, 0x36, 0x47, 0x00, 0x00, 0x06, 0x47, 0x00, 0x00, 0x06, 0x46, 0x00, 0x00, 0x36, - 0x46, 0x00, 0x00, 0x4e, 0x48, 0x00, 0x00, 0x1e, 0x47, 0x00, 0x00, 0x36, 0x48, 0x00, 0x00, 0x06, 0x48, 0x00, 0x00, 0x06, - 0x47, 0x00, 0x00, 0x36, 0x47, 0x00, 0x00, 0x4e, 0x49, 0x00, 0x00, 0x1e, 0x48, 0x00, 0x00, 0x36, 0x49, 0x00, 0x00, 0x06, - 0x49, 0x00, 0x00, 0x06, 0x48, 0x00, 0x00, 0x36, 0x48, 0x00, 0x00, 0x4e, 0x25, 0x00, 0x00, 0x1e, 0x49, 0x00, 0x00, 0x36, - 0x25, 0x00, 0x00, 0x06, 0x25, 0x00, 0x00, 0x06, 0x49, 0x00, 0x00, 0x36, 0x49, 0x00, 0x00, 0x4e, 0x8c, 0xce, 0x48, 0x41, - 0x74, 0x62, 0x4e, 0x41, 0x8c, 0xce, 0x48, 0x41, 0x8c, 0xce, 0x48, 0x41, 0x74, 0x62, 0x4e, 0x41, 0x74, 0x62, 0x4e, 0x41, - 0x74, 0x62, 0x4e, 0x41, 0x55, 0xf6, 0x53, 0x41, 0x74, 0x62, 0x4e, 0x41, 0x74, 0x62, 0x4e, 0x41, 0x55, 0xf6, 0x53, 0x41, - 0x55, 0xf6, 0x53, 0x41, 0x55, 0xf6, 0x53, 0x41, 0x62, 0x8a, 0x59, 0x41, 0x55, 0xf6, 0x53, 0x41, 0x55, 0xf6, 0x53, 0x41, - 0x62, 0x8a, 0x59, 0x41, 0x62, 0x8a, 0x59, 0x41, 0x62, 0x8a, 0x59, 0x41, 0x60, 0x1e, 0x5f, 0x41, 0x62, 0x8a, 0x59, 0x41, - 0x62, 0x8a, 0x59, 0x41, 0x60, 0x1e, 0x5f, 0x41, 0x60, 0x1e, 0x5f, 0x41, 0x60, 0x1e, 0x5f, 0x41, 0x43, 0xb2, 0x64, 0x41, - 0x60, 0x1e, 0x5f, 0x41, 0x60, 0x1e, 0x5f, 0x41, 0x43, 0xb2, 0x64, 0x41, 0x43, 0xb2, 0x64, 0x41, 0x43, 0xb2, 0x64, 0x41, - 0x41, 0x46, 0x6a, 0x41, 0x43, 0xb2, 0x64, 0x41, 0x43, 0xb2, 0x64, 0x41, 0x41, 0x46, 0x6a, 0x41, 0x41, 0x46, 0x6a, 0x41, - 0x41, 0x46, 0x6a, 0x41, 0x4d, 0xda, 0x6f, 0x41, 0x41, 0x46, 0x6a, 0x41, 0x41, 0x46, 0x6a, 0x41, 0x4d, 0xda, 0x6f, 0x41, - 0x4d, 0xda, 0x6f, 0x41, 0x4d, 0xda, 0x6f, 0x41, 0x2f, 0x6e, 0x75, 0x41, 0x4d, 0xda, 0x6f, 0x41, 0x4d, 0xda, 0x6f, 0x41, - 0x2f, 0x6e, 0x75, 0x41, 0x2f, 0x6e, 0x75, 0x41, 0x2f, 0x6e, 0x75, 0x41, 0x16, 0x02, 0x7b, 0x41, 0x2f, 0x6e, 0x75, 0x41, - 0x2f, 0x6e, 0x75, 0x41, 0x16, 0x02, 0x7b, 0x41, 0x16, 0x02, 0x7b, 0x41, 0x16, 0x02, 0x7b, 0x41, 0xff, 0x4a, 0x80, 0x41, - 0x16, 0x02, 0x7b, 0x41, 0x16, 0x02, 0x7b, 0x41, 0xff, 0x4a, 0x80, 0x41, 0xff, 0x4a, 0x80, 0x41, 0xff, 0x4a, 0x80, 0x41, - 0x0e, 0x15, 0x83, 0x41, 0xff, 0x4a, 0x80, 0x41, 0xff, 0x4a, 0x80, 0x41, 0x0e, 0x15, 0x83, 0x41, 0x0e, 0x15, 0x83, 0x41, - 0x0e, 0x15, 0x83, 0x41, 0xf8, 0xde, 0x85, 0x41, 0x0e, 0x15, 0x83, 0x41, 0x0e, 0x15, 0x83, 0x41, 0xf8, 0xde, 0x85, 0x41, - 0xf8, 0xde, 0x85, 0x41, 0xf8, 0xde, 0x85, 0x41, 0x11, 0xa9, 0x88, 0x41, 0xf8, 0xde, 0x85, 0x41, 0xf8, 0xde, 0x85, 0x41, - 0x11, 0xa9, 0x88, 0x41, 0x11, 0xa9, 0x88, 0x41, 0x11, 0xa9, 0x88, 0x41, 0x02, 0x73, 0x8b, 0x41, 0x11, 0xa9, 0x88, 0x41, - 0x11, 0xa9, 0x88, 0x41, 0x02, 0x73, 0x8b, 0x41, 0x02, 0x73, 0x8b, 0x41, 0x02, 0x73, 0x8b, 0x41, 0xef, 0x3c, 0x8e, 0x41, - 0x02, 0x73, 0x8b, 0x41, 0x02, 0x73, 0x8b, 0x41, 0xef, 0x3c, 0x8e, 0x41, 0xef, 0x3c, 0x8e, 0x41, 0xef, 0x3c, 0x8e, 0x41, - 0xf5, 0x06, 0x91, 0x41, 0xef, 0x3c, 0x8e, 0x41, 0xef, 0x3c, 0x8e, 0x41, 0xf5, 0x06, 0x91, 0x41, 0xf5, 0x06, 0x91, 0x41, - 0xf5, 0x06, 0x91, 0x41, 0xe6, 0xd0, 0x93, 0x41, 0xf5, 0x06, 0x91, 0x41, 0xf5, 0x06, 0x91, 0x41, 0xe6, 0xd0, 0x93, 0x41, - 0xe6, 0xd0, 0x93, 0x41, 0xe6, 0xd0, 0x93, 0x41, 0xdd, 0x9a, 0x96, 0x41, 0xe6, 0xd0, 0x93, 0x41, 0xe6, 0xd0, 0x93, 0x41, - 0xdd, 0x9a, 0x96, 0x41, 0xdd, 0x9a, 0x96, 0x41, 0xdd, 0x9a, 0x96, 0x41, 0xd3, 0x64, 0x99, 0x41, 0xdd, 0x9a, 0x96, 0x41, - 0xdd, 0x9a, 0x96, 0x41, 0xd3, 0x64, 0x99, 0x41, 0xd3, 0x64, 0x99, 0x41, 0xd3, 0x64, 0x99, 0x41, 0xe3, 0x2e, 0x9c, 0x41, - 0xd3, 0x64, 0x99, 0x41, 0xd3, 0x64, 0x99, 0x41, 0xe3, 0x2e, 0x9c, 0x41, 0xe3, 0x2e, 0x9c, 0x41, 0xe3, 0x2e, 0x9c, 0x41, - 0xcc, 0xf8, 0x9e, 0x41, 0xe3, 0x2e, 0x9c, 0x41, 0xe3, 0x2e, 0x9c, 0x41, 0xcc, 0xf8, 0x9e, 0x41, 0xcc, 0xf8, 0x9e, 0x41, - 0xcc, 0xf8, 0x9e, 0x41, 0xd3, 0xc2, 0xa1, 0x41, 0xcc, 0xf8, 0x9e, 0x41, 0xcc, 0xf8, 0x9e, 0x41, 0xd3, 0xc2, 0xa1, 0x41, - 0xd3, 0xc2, 0xa1, 0x41, 0xd3, 0xc2, 0xa1, 0x41, 0xc4, 0x8c, 0xa4, 0x41, 0xd3, 0xc2, 0xa1, 0x41, 0xd3, 0xc2, 0xa1, 0x41, - 0xc4, 0x8c, 0xa4, 0x41, 0xc4, 0x8c, 0xa4, 0x41, 0xc4, 0x8c, 0xa4, 0x41, 0xcb, 0x56, 0xa7, 0x41, 0xc4, 0x8c, 0xa4, 0x41, - 0xc4, 0x8c, 0xa4, 0x41, 0xcb, 0x56, 0xa7, 0x41, 0xcb, 0x56, 0xa7, 0x41, 0xcb, 0x56, 0xa7, 0x41, 0xb4, 0x20, 0xaa, 0x41, - 0xcb, 0x56, 0xa7, 0x41, 0xcb, 0x56, 0xa7, 0x41, 0xb4, 0x20, 0xaa, 0x41, 0xb4, 0x20, 0xaa, 0x41, 0xb4, 0x20, 0xaa, 0x41, - 0xc4, 0xea, 0xac, 0x41, 0xb4, 0x20, 0xaa, 0x41, 0xb4, 0x20, 0xaa, 0x41, 0xc4, 0xea, 0xac, 0x41, 0xc4, 0xea, 0xac, 0x41, - 0xc4, 0xea, 0xac, 0x41, 0xbb, 0xb4, 0xaf, 0x41, 0xc4, 0xea, 0xac, 0x41, 0xc4, 0xea, 0xac, 0x41, 0xbb, 0xb4, 0xaf, 0x41, - 0xbb, 0xb4, 0xaf, 0x41, 0xbb, 0xb4, 0xaf, 0x41, 0xb1, 0x7e, 0xb2, 0x41, 0xbb, 0xb4, 0xaf, 0x41, 0xbb, 0xb4, 0xaf, 0x41, - 0xb1, 0x7e, 0xb2, 0x41, 0xb1, 0x7e, 0xb2, 0x41, 0xb1, 0x7e, 0xb2, 0x41, 0xa2, 0x48, 0xb5, 0x41, 0xb1, 0x7e, 0xb2, 0x41, - 0xb1, 0x7e, 0xb2, 0x41, 0xa2, 0x48, 0xb5, 0x41, 0xa2, 0x48, 0xb5, 0x41, 0xa2, 0x48, 0xb5, 0x41, 0xa8, 0x12, 0xb8, 0x41, - 0xa2, 0x48, 0xb5, 0x41, 0xa2, 0x48, 0xb5, 0x41, 0xa8, 0x12, 0xb8, 0x41, 0xa8, 0x12, 0xb8, 0x41, 0xa8, 0x12, 0xb8, 0x41, - 0x95, 0xdc, 0xba, 0x41, 0xa8, 0x12, 0xb8, 0x41, 0xa8, 0x12, 0xb8, 0x41, 0x95, 0xdc, 0xba, 0x41, 0x95, 0xdc, 0xba, 0x41, - 0x95, 0xdc, 0xba, 0x41, 0x86, 0xa6, 0xbd, 0x41, 0x95, 0xdc, 0xba, 0x41, 0x95, 0xdc, 0xba, 0x41, 0x86, 0xa6, 0xbd, 0x41, - 0x86, 0xa6, 0xbd, 0x41, 0x86, 0xa6, 0xbd, 0x41, 0x9f, 0x70, 0xc0, 0x41, 0x86, 0xa6, 0xbd, 0x41, 0x86, 0xa6, 0xbd, 0x41, - 0x9f, 0x70, 0xc0, 0x41, 0x9f, 0x70, 0xc0, 0x41, 0x9f, 0x70, 0xc0, 0x41, 0x89, 0x3a, 0xc3, 0x41, 0x9f, 0x70, 0xc0, 0x41, - 0x9f, 0x70, 0xc0, 0x41, 0x89, 0x3a, 0xc3, 0x41, 0x89, 0x3a, 0xc3, 0x41, 0x89, 0x3a, 0xc3, 0x41, 0x98, 0x04, 0xc6, 0x41, - 0x89, 0x3a, 0xc3, 0x41, 0x89, 0x3a, 0xc3, 0x41, 0x98, 0x04, 0xc6, 0x41, 0x98, 0x04, 0xc6, 0x41, 0x98, 0x04, 0xc6, 0x41, - 0x8c, 0xce, 0xc8, 0x41, 0x98, 0x04, 0xc6, 0x41, 0x98, 0x04, 0xc6, 0x41, 0x8c, 0xce, 0xc8, 0x41, 0x8c, 0xce, 0xc8, 0x41, - 0x8c, 0xce, 0x48, 0x41, 0x74, 0x62, 0x4e, 0x41, 0x8c, 0xce, 0x48, 0x41, 0x8c, 0xce, 0x48, 0x41, 0x74, 0x62, 0x4e, 0x41, - 0x74, 0x62, 0x4e, 0x41, 0x74, 0x62, 0x4e, 0x41, 0x55, 0xf6, 0x53, 0x41, 0x74, 0x62, 0x4e, 0x41, 0x74, 0x62, 0x4e, 0x41, - 0x55, 0xf6, 0x53, 0x41, 0x55, 0xf6, 0x53, 0x41, 0x55, 0xf6, 0x53, 0x41, 0x62, 0x8a, 0x59, 0x41, 0x55, 0xf6, 0x53, 0x41, - 0x55, 0xf6, 0x53, 0x41, 0x62, 0x8a, 0x59, 0x41, 0x62, 0x8a, 0x59, 0x41, 0x62, 0x8a, 0x59, 0x41, 0x60, 0x1e, 0x5f, 0x41, - 0x62, 0x8a, 0x59, 0x41, 0x62, 0x8a, 0x59, 0x41, 0x60, 0x1e, 0x5f, 0x41, 0x60, 0x1e, 0x5f, 0x41, 0x60, 0x1e, 0x5f, 0x41, - 0x43, 0xb2, 0x64, 0x41, 0x60, 0x1e, 0x5f, 0x41, 0x60, 0x1e, 0x5f, 0x41, 0x43, 0xb2, 0x64, 0x41, 0x43, 0xb2, 0x64, 0x41, - 0x43, 0xb2, 0x64, 0x41, 0x41, 0x46, 0x6a, 0x41, 0x43, 0xb2, 0x64, 0x41, 0x43, 0xb2, 0x64, 0x41, 0x41, 0x46, 0x6a, 0x41, - 0x41, 0x46, 0x6a, 0x41, 0x41, 0x46, 0x6a, 0x41, 0x4d, 0xda, 0x6f, 0x41, 0x41, 0x46, 0x6a, 0x41, 0x41, 0x46, 0x6a, 0x41, - 0x4d, 0xda, 0x6f, 0x41, 0x4d, 0xda, 0x6f, 0x41, 0x4d, 0xda, 0x6f, 0x41, 0x2f, 0x6e, 0x75, 0x41, 0x4d, 0xda, 0x6f, 0x41, - 0x4d, 0xda, 0x6f, 0x41, 0x2f, 0x6e, 0x75, 0x41, 0x2f, 0x6e, 0x75, 0x41, 0x2f, 0x6e, 0x75, 0x41, 0x16, 0x02, 0x7b, 0x41, - 0x2f, 0x6e, 0x75, 0x41, 0x2f, 0x6e, 0x75, 0x41, 0x16, 0x02, 0x7b, 0x41, 0x16, 0x02, 0x7b, 0x41, 0x16, 0x02, 0x7b, 0x41, - 0xff, 0x4a, 0x80, 0x41, 0x16, 0x02, 0x7b, 0x41, 0x16, 0x02, 0x7b, 0x41, 0xff, 0x4a, 0x80, 0x41, 0xff, 0x4a, 0x80, 0x41, - 0xff, 0x4a, 0x80, 0x41, 0x0e, 0x15, 0x83, 0x41, 0xff, 0x4a, 0x80, 0x41, 0xff, 0x4a, 0x80, 0x41, 0x0e, 0x15, 0x83, 0x41, - 0x0e, 0x15, 0x83, 0x41, 0x0e, 0x15, 0x83, 0x41, 0xf8, 0xde, 0x85, 0x41, 0x0e, 0x15, 0x83, 0x41, 0x0e, 0x15, 0x83, 0x41, - 0xf8, 0xde, 0x85, 0x41, 0xf8, 0xde, 0x85, 0x41, 0xf8, 0xde, 0x85, 0x41, 0x11, 0xa9, 0x88, 0x41, 0xf8, 0xde, 0x85, 0x41, - 0xf8, 0xde, 0x85, 0x41, 0x11, 0xa9, 0x88, 0x41, 0x11, 0xa9, 0x88, 0x41, 0x11, 0xa9, 0x88, 0x41, 0x02, 0x73, 0x8b, 0x41, - 0x11, 0xa9, 0x88, 0x41, 0x11, 0xa9, 0x88, 0x41, 0x02, 0x73, 0x8b, 0x41, 0x02, 0x73, 0x8b, 0x41, 0x02, 0x73, 0x8b, 0x41, - 0xef, 0x3c, 0x8e, 0x41, 0x02, 0x73, 0x8b, 0x41, 0x02, 0x73, 0x8b, 0x41, 0xef, 0x3c, 0x8e, 0x41, 0xef, 0x3c, 0x8e, 0x41, - 0xef, 0x3c, 0x8e, 0x41, 0xf5, 0x06, 0x91, 0x41, 0xef, 0x3c, 0x8e, 0x41, 0xef, 0x3c, 0x8e, 0x41, 0xf5, 0x06, 0x91, 0x41, - 0xf5, 0x06, 0x91, 0x41, 0xf5, 0x06, 0x91, 0x41, 0xe6, 0xd0, 0x93, 0x41, 0xf5, 0x06, 0x91, 0x41, 0xf5, 0x06, 0x91, 0x41, - 0xe6, 0xd0, 0x93, 0x41, 0xe6, 0xd0, 0x93, 0x41, 0xe6, 0xd0, 0x93, 0x41, 0xdd, 0x9a, 0x96, 0x41, 0xe6, 0xd0, 0x93, 0x41, - 0xe6, 0xd0, 0x93, 0x41, 0xdd, 0x9a, 0x96, 0x41, 0xdd, 0x9a, 0x96, 0x41, 0xdd, 0x9a, 0x96, 0x41, 0xd3, 0x64, 0x99, 0x41, - 0xdd, 0x9a, 0x96, 0x41, 0xdd, 0x9a, 0x96, 0x41, 0xd3, 0x64, 0x99, 0x41, 0xd3, 0x64, 0x99, 0x41, 0xd3, 0x64, 0x99, 0x41, - 0xe3, 0x2e, 0x9c, 0x41, 0xd3, 0x64, 0x99, 0x41, 0xd3, 0x64, 0x99, 0x41, 0xe3, 0x2e, 0x9c, 0x41, 0xe3, 0x2e, 0x9c, 0x41, - 0xe3, 0x2e, 0x9c, 0x41, 0xcc, 0xf8, 0x9e, 0x41, 0xe3, 0x2e, 0x9c, 0x41, 0xe3, 0x2e, 0x9c, 0x41, 0xcc, 0xf8, 0x9e, 0x41, - 0xcc, 0xf8, 0x9e, 0x41, 0xcc, 0xf8, 0x9e, 0x41, 0xd3, 0xc2, 0xa1, 0x41, 0xcc, 0xf8, 0x9e, 0x41, 0xcc, 0xf8, 0x9e, 0x41, - 0xd3, 0xc2, 0xa1, 0x41, 0xd3, 0xc2, 0xa1, 0x41, 0xd3, 0xc2, 0xa1, 0x41, 0xc4, 0x8c, 0xa4, 0x41, 0xd3, 0xc2, 0xa1, 0x41, - 0xd3, 0xc2, 0xa1, 0x41, 0xc4, 0x8c, 0xa4, 0x41, 0xc4, 0x8c, 0xa4, 0x41, 0xc4, 0x8c, 0xa4, 0x41, 0xcb, 0x56, 0xa7, 0x41, - 0xc4, 0x8c, 0xa4, 0x41, 0xc4, 0x8c, 0xa4, 0x41, 0xcb, 0x56, 0xa7, 0x41, 0xcb, 0x56, 0xa7, 0x41, 0xcb, 0x56, 0xa7, 0x41, - 0xb4, 0x20, 0xaa, 0x41, 0xcb, 0x56, 0xa7, 0x41, 0xcb, 0x56, 0xa7, 0x41, 0xb4, 0x20, 0xaa, 0x41, 0xb4, 0x20, 0xaa, 0x41, - 0xb4, 0x20, 0xaa, 0x41, 0xc4, 0xea, 0xac, 0x41, 0xb4, 0x20, 0xaa, 0x41, 0xb4, 0x20, 0xaa, 0x41, 0xc4, 0xea, 0xac, 0x41, - 0xc4, 0xea, 0xac, 0x41, 0xc4, 0xea, 0xac, 0x41, 0xbb, 0xb4, 0xaf, 0x41, 0xc4, 0xea, 0xac, 0x41, 0xc4, 0xea, 0xac, 0x41, - 0xbb, 0xb4, 0xaf, 0x41, 0xbb, 0xb4, 0xaf, 0x41, 0xbb, 0xb4, 0xaf, 0x41, 0xb1, 0x7e, 0xb2, 0x41, 0xbb, 0xb4, 0xaf, 0x41, - 0xbb, 0xb4, 0xaf, 0x41, 0xb1, 0x7e, 0xb2, 0x41, 0xb1, 0x7e, 0xb2, 0x41, 0xb1, 0x7e, 0xb2, 0x41, 0xa2, 0x48, 0xb5, 0x41, - 0xb1, 0x7e, 0xb2, 0x41, 0xb1, 0x7e, 0xb2, 0x41, 0xa2, 0x48, 0xb5, 0x41, 0xa2, 0x48, 0xb5, 0x41, 0xa2, 0x48, 0xb5, 0x41, - 0xa8, 0x12, 0xb8, 0x41, 0xa2, 0x48, 0xb5, 0x41, 0xa2, 0x48, 0xb5, 0x41, 0xa8, 0x12, 0xb8, 0x41, 0xa8, 0x12, 0xb8, 0x41, - 0xa8, 0x12, 0xb8, 0x41, 0x95, 0xdc, 0xba, 0x41, 0xa8, 0x12, 0xb8, 0x41, 0xa8, 0x12, 0xb8, 0x41, 0x95, 0xdc, 0xba, 0x41, - 0x95, 0xdc, 0xba, 0x41, 0x95, 0xdc, 0xba, 0x41, 0x86, 0xa6, 0xbd, 0x41, 0x95, 0xdc, 0xba, 0x41, 0x95, 0xdc, 0xba, 0x41, - 0x86, 0xa6, 0xbd, 0x41, 0x86, 0xa6, 0xbd, 0x41, 0x86, 0xa6, 0xbd, 0x41, 0x9f, 0x70, 0xc0, 0x41, 0x86, 0xa6, 0xbd, 0x41, - 0x86, 0xa6, 0xbd, 0x41, 0x9f, 0x70, 0xc0, 0x41, 0x9f, 0x70, 0xc0, 0x41, 0x9f, 0x70, 0xc0, 0x41, 0x89, 0x3a, 0xc3, 0x41, - 0x9f, 0x70, 0xc0, 0x41, 0x9f, 0x70, 0xc0, 0x41, 0x89, 0x3a, 0xc3, 0x41, 0x89, 0x3a, 0xc3, 0x41, 0x89, 0x3a, 0xc3, 0x41, - 0x98, 0x04, 0xc6, 0x41, 0x89, 0x3a, 0xc3, 0x41, 0x89, 0x3a, 0xc3, 0x41, 0x98, 0x04, 0xc6, 0x41, 0x98, 0x04, 0xc6, 0x41, - 0x98, 0x04, 0xc6, 0x41, 0x8c, 0xce, 0xc8, 0x41, 0x98, 0x04, 0xc6, 0x41, 0x98, 0x04, 0xc6, 0x41, 0x8c, 0xce, 0xc8, 0x41, - 0x8c, 0xce, 0xc8, 0x41, - ]); + // Center1 = (0,0,0) Center2 = (0,0,6) Radius = 2 + readonly cylinder: TileTestCase; } diff --git a/test-apps/testbed/frontend/TileIO.test.ts b/test-apps/testbed/frontend/TileIO.test.ts index 1aaa7f5..7660eb0 100644 --- a/test-apps/testbed/frontend/TileIO.test.ts +++ b/test-apps/testbed/frontend/TileIO.test.ts @@ -5,18 +5,41 @@ import { expect } from "chai"; import { TileIO, IModelTileIO, IModelTileLoader, TileTree, TileRequest } from "@bentley/imodeljs-frontend/lib/tile"; import { SurfaceType } from "@bentley/imodeljs-frontend/lib/rendering"; -import { Batch, MeshGraphic, GraphicsArray, PolylinePrimitive, PolylineGeometry } from "@bentley/imodeljs-frontend/lib/webgl"; -import { ModelProps, RelatedElementProps, FeatureIndexType, BatchType } from "@bentley/imodeljs-common"; +import { Batch, MeshGraphic, GraphicsArray, Primitive, PolylineGeometry } from "@bentley/imodeljs-frontend/lib/webgl"; +import { ModelProps, RelatedElementProps, FeatureIndexType, BatchType, ServerTimeoutError } from "@bentley/imodeljs-common"; import { Id64, Id64String } from "@bentley/bentleyjs-core"; -import { TileData } from "./TileIO.data"; import * as path from "path"; import { CONSTANTS } from "../common/Testbed"; -import { RenderGraphic, IModelApp, IModelConnection, GeometricModelState } from "@bentley/imodeljs-frontend"; +import { MockRender, RenderGraphic, IModelApp, IModelConnection, GeometricModelState } from "@bentley/imodeljs-frontend"; import { WebGLTestContext } from "./WebGLTestContext"; -import { MockRender } from "./MockRender"; +import { TileTestCase, TileTestData } from "./TileIO.data"; +import { TILE_DATA_1_1 } from "./TileIO.data.1.1"; +import { TILE_DATA_1_2 } from "./TileIO.data.1.2"; +import { TILE_DATA_1_3 } from "./TileIO.data.1.3"; +import { TILE_DATA_1_4 } from "./TileIO.data.1.4"; +import { changeMinorVersion, changeMajorVersion, changeHeaderLength } from "./TileIO.data.fake"; +import { testOnScreenViewport } from "./TestViewport"; const iModelLocation = path.join(CONSTANTS.IMODELJS_CORE_DIRNAME, "core/backend/lib/test/assets/test.bim"); +const testCases = [ + TILE_DATA_1_1, + TILE_DATA_1_2, + TILE_DATA_1_3, + TILE_DATA_1_4, +]; + +const currentTestCase = testCases[testCases.length - 1]; + +// Make fake versions of each real version +const numBaseTestCases = testCases.length; +for (let i = 0; i < numBaseTestCases; i++) { + const testCase = testCases[i]; + testCases.push(changeMinorVersion(testCase, 5000 + i)); + testCases.push(changeMajorVersion(testCase, 6000 + i)); + testCases.push(changeHeaderLength(testCase, 7000 + i, 8)); +} + export class FakeGMState extends GeometricModelState { public get is3d(): boolean { return true; } public get is2d(): boolean { return !this.is3d; } @@ -37,14 +60,33 @@ export class FakeREProps implements RelatedElementProps { function delta(a: number, b: number): number { return Math.abs(a - b); } type ProcessGraphic = (graphic: RenderGraphic) => void; -const rectangle = TileData.rectangle.buffer; +function processHeader(data: TileTestData, test: TileTestCase, numElements: number) { + const stream = new TileIO.StreamBuffer(test.bytes.buffer); + stream.reset(); + const header = new IModelTileIO.Header(stream); + expect(header.isValid).to.be.true; + expect(header.format).to.equal(TileIO.Format.IModel); + expect(header.versionMajor).to.equal(data.versionMajor); + expect(header.versionMinor).to.equal(data.versionMinor); + expect(header.headerLength).to.equal(data.headerLength); + expect(header.tileLength).to.equal(test.bytes.byteLength); + expect(header.flags).to.equal(test.flags); + expect(header.numElementsIncluded).to.equal(numElements); + expect(header.numElementsExcluded).to.equal(0); + expect(header.isReadableVersion).to.equal(!data.unreadable); +} -async function processRectangle(imodel: IModelConnection, processGraphic: ProcessGraphic) { +function createReader(imodel: IModelConnection, data: TileTestData, test: TileTestCase): IModelTileIO.Reader | undefined { const model = new FakeGMState(new FakeModelProps(new FakeREProps()), imodel); - const stream = new TileIO.StreamBuffer(rectangle); - const reader = IModelTileIO.Reader.create(stream, model.iModel, model.id, model.is3d, IModelApp.renderSystem); - expect(reader).not.to.be.undefined; + const stream = new TileIO.StreamBuffer(test.bytes.buffer); + const reader = IModelTileIO.Reader.create(stream, imodel, model.id, model.is3d, IModelApp.renderSystem); + expect(undefined === reader).to.equal(!!data.unreadable); + return reader; +} +async function processRectangle(data: TileTestData, imodel: IModelConnection, processGraphic: ProcessGraphic) { + processHeader(data, data.rectangle, 1); + const reader = createReader(imodel, data, data.rectangle); if (undefined !== reader) { const result = await reader.read(); expect(result.readStatus).to.equal(TileIO.ReadStatus.Success); @@ -67,13 +109,14 @@ async function processRectangle(imodel: IModelConnection, processGraphic: Proces } } -async function processTriangles(imodel: IModelConnection, processGraphic: ProcessGraphic) { - const triangles = TileData.triangles.buffer; - const model = new FakeGMState(new FakeModelProps(new FakeREProps()), imodel); - const stream = new TileIO.StreamBuffer(triangles); - const reader = IModelTileIO.Reader.create(stream, model.iModel, model.id, model.is3d, IModelApp.renderSystem); - expect(reader).not.to.be.undefined; +async function processEachRectangle(imodel: IModelConnection, processGraphic: ProcessGraphic) { + for (const data of testCases) + await processRectangle(data, imodel, processGraphic); +} +async function processTriangles(data: TileTestData, imodel: IModelConnection, processGraphic: ProcessGraphic) { + processHeader(data, data.triangles, 6); + const reader = createReader(imodel, data, data.triangles); if (undefined !== reader) { const result = await reader.read(); expect(result.readStatus).to.equal(TileIO.ReadStatus.Success); @@ -96,13 +139,14 @@ async function processTriangles(imodel: IModelConnection, processGraphic: Proces } } -async function processLineString(imodel: IModelConnection, processGraphic: ProcessGraphic) { - const lineString = TileData.lineString.buffer; - const model = new FakeGMState(new FakeModelProps(new FakeREProps()), imodel); - const stream = new TileIO.StreamBuffer(lineString); - const reader = IModelTileIO.Reader.create(stream, model.iModel, model.id, model.is3d, IModelApp.renderSystem); - expect(reader).not.to.be.undefined; +async function processEachTriangles(imodel: IModelConnection, processGraphic: ProcessGraphic) { + for (const data of testCases) + await processTriangles(data, imodel, processGraphic); +} +async function processLineString(data: TileTestData, imodel: IModelConnection, processGraphic: ProcessGraphic) { + processHeader(data, data.lineString, 1); + const reader = createReader(imodel, data, data.lineString); if (undefined !== reader) { const result = await reader.read(); expect(result.readStatus).to.equal(TileIO.ReadStatus.Success); @@ -125,13 +169,14 @@ async function processLineString(imodel: IModelConnection, processGraphic: Proce } } -async function processLineStrings(imodel: IModelConnection, processGraphic: ProcessGraphic) { - const lineStrings = TileData.lineStrings.buffer; - const model = new FakeGMState(new FakeModelProps(new FakeREProps()), imodel); - const stream = new TileIO.StreamBuffer(lineStrings); - const reader = IModelTileIO.Reader.create(stream, model.iModel, model.id, model.is3d, IModelApp.renderSystem); - expect(reader).not.to.be.undefined; +async function processEachLineString(imodel: IModelConnection, processGraphic: ProcessGraphic) { + for (const data of testCases) + await processLineString(data, imodel, processGraphic); +} +async function processLineStrings(data: TileTestData, imodel: IModelConnection, processGraphic: ProcessGraphic) { + processHeader(data, data.lineStrings, 3); + const reader = createReader(imodel, data, data.lineStrings); if (undefined !== reader) { const result = await reader.read(); expect(result.readStatus).to.equal(TileIO.ReadStatus.Success); @@ -154,13 +199,14 @@ async function processLineStrings(imodel: IModelConnection, processGraphic: Proc } } -async function processCylinder(imodel: IModelConnection, processGraphic: ProcessGraphic) { - const cylinder = TileData.cylinder.buffer; - const model = new FakeGMState(new FakeModelProps(new FakeREProps()), imodel); - const stream = new TileIO.StreamBuffer(cylinder); - const reader = IModelTileIO.Reader.create(stream, model.iModel, model.id, model.is3d, IModelApp.renderSystem); - expect(reader).not.to.be.undefined; +async function processEachLineStrings(imodel: IModelConnection, processGraphic: ProcessGraphic) { + for (const data of testCases) + await processLineStrings(data, imodel, processGraphic); +} +async function processCylinder(data: TileTestData, imodel: IModelConnection, processGraphic: ProcessGraphic) { + processHeader(data, data.cylinder, 1); + const reader = createReader(imodel, data, data.cylinder); if (undefined !== reader) { const result = await reader.read(); expect(result.readStatus).to.equal(TileIO.ReadStatus.Success); @@ -183,8 +229,12 @@ async function processCylinder(imodel: IModelConnection, processGraphic: Process } } -// ###TODO: TileIO.data.ts contains tiles in old format. Update it. (The tests below continue to pass, but could exercise more of the tile contents). -// These tests require the real (webgl-based) RenderSystem. Won't execute in Windows CI job due to electron bug. +async function processEachCylinder(imodel: IModelConnection, processGraphic: ProcessGraphic) { + for (const data of testCases) + await processCylinder(data, imodel, processGraphic); +} + +// These tests require the real (webgl-based) RenderSystem. describe("TileIO (WebGL)", () => { let imodel: IModelConnection; @@ -200,7 +250,7 @@ describe("TileIO (WebGL)", () => { it("should read an iModel tile containing a single rectangle", async () => { if (WebGLTestContext.isInitialized) { - await processRectangle(imodel, (graphic) => { + await processEachRectangle(imodel, (graphic) => { expect(graphic).to.be.instanceOf(Batch); const batch = graphic as Batch; expect(batch.featureTable.isUniform).to.be.true; @@ -223,7 +273,7 @@ describe("TileIO (WebGL)", () => { it("should read an iModel tile containing multiple meshes and non-uniform feature/color tables", async () => { if (WebGLTestContext.isInitialized) { - await processTriangles(imodel, (graphic) => { + await processEachTriangles(imodel, (graphic) => { expect(graphic).to.be.instanceOf(Batch); const batch = graphic as Batch; expect(batch.featureTable.isUniform).to.be.false; @@ -264,18 +314,17 @@ describe("TileIO (WebGL)", () => { it("should read an iModel tile containing single open yellow line string", async () => { if (WebGLTestContext.isInitialized) { - await processLineString(imodel, (graphic) => { + await processEachLineString(imodel, (graphic) => { expect(graphic).to.be.instanceOf(Batch); const batch = graphic as Batch; expect(batch.featureTable.isUniform).to.be.true; expect(batch.featureTable.numFeatures).to.equal(1); expect(batch.graphic).not.to.be.undefined; - expect(batch.graphic).to.be.instanceOf(PolylinePrimitive); - const plinePrim = batch.graphic as PolylinePrimitive; + expect(batch.graphic).to.be.instanceOf(Primitive); + const plinePrim = batch.graphic as Primitive; expect(plinePrim.featureIndexType).to.equal(FeatureIndexType.Uniform); expect(plinePrim.isEdge).to.be.false; expect(plinePrim.isLit).to.be.false; - expect(plinePrim.isPlanar).to.be.false; expect(plinePrim.renderOrder).to.equal(3); expect(plinePrim.cachedGeometry).to.not.be.undefined; const plGeom = plinePrim.cachedGeometry as PolylineGeometry; @@ -283,13 +332,14 @@ describe("TileIO (WebGL)", () => { expect(plGeom.lut.numVertices).to.equal(6); expect(plGeom.lineCode).to.equal(0); expect(plGeom.lineWeight).to.equal(9); + expect(plGeom.isPlanar).to.be.false; }); } }); it("should read an iModel tile containing multiple line strings", async () => { if (WebGLTestContext.isInitialized) { - await processLineStrings(imodel, (graphic) => { + await processEachLineStrings(imodel, (graphic) => { expect(graphic).to.be.instanceOf(Batch); const batch = graphic as Batch; expect(batch.featureTable.isUniform).to.be.false; @@ -299,12 +349,11 @@ describe("TileIO (WebGL)", () => { const list = batch.graphic as GraphicsArray; expect(list.graphics.length).to.equal(2); - expect(list.graphics[0]).to.be.instanceOf(PolylinePrimitive); - let plinePrim = list.graphics[0] as PolylinePrimitive; + expect(list.graphics[0]).to.be.instanceOf(Primitive); + let plinePrim = list.graphics[0] as Primitive; expect(plinePrim.featureIndexType).to.equal(FeatureIndexType.Uniform); expect(plinePrim.isEdge).to.be.false; expect(plinePrim.isLit).to.be.false; - expect(plinePrim.isPlanar).to.be.false; expect(plinePrim.renderOrder).to.equal(3); expect(plinePrim.cachedGeometry).to.not.be.undefined; let plGeom = plinePrim.cachedGeometry as PolylineGeometry; @@ -312,13 +361,13 @@ describe("TileIO (WebGL)", () => { expect(plGeom.lut.numVertices).to.equal(6); expect(plGeom.lineCode).to.equal(0); expect(plGeom.lineWeight).to.equal(9); + expect(plGeom.isPlanar).to.be.false; - expect(list.graphics[1]).to.be.instanceOf(PolylinePrimitive); - plinePrim = list.graphics[1] as PolylinePrimitive; + expect(list.graphics[1]).to.be.instanceOf(Primitive); + plinePrim = list.graphics[1] as Primitive; expect(plinePrim.featureIndexType).to.equal(FeatureIndexType.NonUniform); expect(plinePrim.isEdge).to.be.false; expect(plinePrim.isLit).to.be.false; - expect(plinePrim.isPlanar).to.be.false; expect(plinePrim.renderOrder).to.equal(3); expect(plinePrim.cachedGeometry).to.not.be.undefined; plGeom = plinePrim.cachedGeometry as PolylineGeometry; @@ -326,13 +375,14 @@ describe("TileIO (WebGL)", () => { expect(plGeom.lut.numVertices).to.equal(12); expect(plGeom.lineCode).to.equal(2); expect(plGeom.lineWeight).to.equal(9); + expect(plGeom.isPlanar).to.be.false; }); } }); it("should read an iModel tile containing edges and silhouettes", async () => { if (WebGLTestContext.isInitialized) { - await processCylinder(imodel, (graphic) => { + await processEachCylinder(imodel, (graphic) => { expect(graphic).to.be.instanceOf(Batch); const batch = graphic as Batch; expect(batch.featureTable.isUniform).to.be.true; @@ -368,33 +418,11 @@ describe("TileIO (mock render)", () => { if (imodel) await imodel.closeStandalone(); }); - it("should read tile headers", () => { - const stream = new TileIO.StreamBuffer(rectangle); - stream.reset(); - const header = new IModelTileIO.Header(stream); - expect(header.isValid).to.be.true; - expect(header.format).to.equal(TileIO.Format.IModel); - expect(header.version).to.equal(0); - expect(header.flags).to.equal(IModelTileIO.Flags.None); - expect(header.length).to.equal(TileData.rectangle.length); - - // content range is relative to tileset origin at (0, 0, 0) - const low = header.contentRange.low; - expect(delta(low.x, -2.5)).to.be.lessThan(0.0005); - expect(delta(low.y, -5.0)).to.be.lessThan(0.0005); - expect(delta(low.z, 0.0)).to.be.lessThan(0.0005); - - const high = header.contentRange.high; - expect(delta(high.x, 2.5)).to.be.lessThan(0.0005); - expect(delta(high.y, 5.0)).to.be.lessThan(0.0005); - expect(delta(high.z, 0.0)).to.be.lessThan(0.0005); - }); - it("should support canceling operation", async () => { if (WebGLTestContext.isInitialized) { const model = new FakeGMState(new FakeModelProps(new FakeREProps()), imodel); - const stream = new TileIO.StreamBuffer(rectangle); - const reader = IModelTileIO.Reader.create(stream, model.iModel, model.id, model.is3d, IModelApp.renderSystem, BatchType.Primary, (_) => true); + const stream = new TileIO.StreamBuffer(currentTestCase.rectangle.bytes.buffer); + const reader = IModelTileIO.Reader.create(stream, model.iModel, model.id, model.is3d, IModelApp.renderSystem, BatchType.Primary, true, (_) => true); expect(reader).not.to.be.undefined; const result = await reader!.read(); @@ -419,7 +447,7 @@ describe("TileIO (mock render)", () => { }); it("should read an iModel tile containing a single rectangle", async () => { - await processRectangle(imodel, (graphic) => { + await processEachRectangle(imodel, (graphic) => { expect(graphic).instanceof(MockRender.Batch); const batch = graphic as MockRender.Batch; expect(batch.featureTable.isUniform).to.be.true; @@ -429,7 +457,7 @@ describe("TileIO (mock render)", () => { }); it("should read an iModel tile containing multiple meshes and non-uniform feature/color tables", async () => { - await processTriangles(imodel, (graphic) => { + await processEachTriangles(imodel, (graphic) => { expect(graphic).instanceof(MockRender.Batch); const batch = graphic as MockRender.Batch; expect(batch.featureTable.isUniform).to.be.false; @@ -442,7 +470,7 @@ describe("TileIO (mock render)", () => { }); it("should read an iModel tile containing single open yellow line string", async () => { - await processLineString(imodel, (graphic) => { + await processEachLineString(imodel, (graphic) => { expect(graphic).instanceof(MockRender.Batch); const batch = graphic as MockRender.Batch; expect(batch.featureTable.isUniform).to.be.true; @@ -452,7 +480,7 @@ describe("TileIO (mock render)", () => { }); it("should read an iModel tile containing multiple line strings", async () => { - await processLineStrings(imodel, (graphic) => { + await processEachLineStrings(imodel, (graphic) => { expect(graphic).instanceof(MockRender.Batch); const batch = graphic as MockRender.Batch; expect(batch.featureTable.isUniform).to.be.false; @@ -465,7 +493,7 @@ describe("TileIO (mock render)", () => { }); it("should read an iModel tile containing edges and silhouettes", async () => { - await processCylinder(imodel, (graphic) => { + await processEachCylinder(imodel, (graphic) => { expect(graphic).instanceof(MockRender.Batch); const batch = graphic as MockRender.Batch; expect(batch.featureTable.isUniform).to.be.true; @@ -490,7 +518,7 @@ async function getTileTree(imodel: IModelConnection, modelId: Id64String): Promi let tree: TileTree | undefined; await waitUntil(() => { - tree = model.getOrLoadTileTree(); + tree = model.getOrLoadTileTree(true); return undefined !== tree; }); @@ -559,7 +587,9 @@ describe("mirukuru TileTree", () => { const header = new IModelTileIO.Header(stream); expect(header.isValid).to.be.true; expect(header.format).to.equal(TileIO.Format.IModel); - expect(header.version).to.equal(0); + expect(header.version).to.equal(IModelTileIO.CurrentVersion.Combined); + expect(header.versionMajor).to.equal(IModelTileIO.CurrentVersion.Major); + expect(header.versionMinor).to.equal(IModelTileIO.CurrentVersion.Minor); expect(header.flags).to.equal(IModelTileIO.Flags.None); expect(header.numElementsIncluded).to.equal(1); expect(header.numElementsExcluded).to.equal(0); @@ -569,4 +599,36 @@ describe("mirukuru TileTree", () => { expect(projExt.yLength()).to.equal(header.contentRange.yLength()); expect(header.contentRange.zLength()).to.equal(0); // project extents are chubbed up; content range is tight. }); + + it("should retry tile requests on server timeout error", async () => { + let treeCounter = 0; + let tileCounter = 0; + const numRetries = 3; + + const getTileTreeProps = imodel.tiles.getTileTreeProps; + imodel.tiles.getTileTreeProps = async () => { + ++treeCounter; + if (treeCounter >= numRetries) + imodel.tiles.getTileTreeProps = getTileTreeProps; + + throw new ServerTimeoutError(504, "fake timeout"); + }; + + const getTileContent = imodel.tiles.getTileContent; + imodel.tiles.getTileContent = async () => { + ++tileCounter; + if (tileCounter >= numRetries) + imodel.tiles.getTileContent = getTileContent; + + throw new ServerTimeoutError(504, "fake timeout"); + }; + + await testOnScreenViewport("0x24", imodel, 100, 100, async (vp) => { + await vp.waitForAllTilesToRender(); + expect(tileCounter).to.equal(numRetries); + expect(vp.numRequestedTiles).to.equal(0); + expect(vp.numSelectedTiles).to.equal(1); + expect(treeCounter).to.equal(numRetries); + }); + }); }); diff --git a/test-apps/testbed/frontend/Tool.test.ts b/test-apps/testbed/frontend/Tool.test.ts index 4b43c28..d64f462 100644 --- a/test-apps/testbed/frontend/Tool.test.ts +++ b/test-apps/testbed/frontend/Tool.test.ts @@ -4,10 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as path from "path"; import { assert } from "chai"; -import { IModelConnection, ElementAgenda, ModifyElementSource, SelectEventType } from "@bentley/imodeljs-frontend"; +import { MockRender, IModelConnection, ElementAgenda, ModifyElementSource, SelectEventType } from "@bentley/imodeljs-frontend"; import { Id64 } from "@bentley/bentleyjs-core"; import { CONSTANTS } from "../common/Testbed"; -import { MockRender } from "./MockRender"; const iModelLocation = path.join(CONSTANTS.IMODELJS_CORE_DIRNAME, "core/backend/lib/test/assets/test.bim"); diff --git a/test-apps/testbed/frontend/ToolRegistry.test.ts b/test-apps/testbed/frontend/ToolRegistry.test.ts index 52798db..d320043 100644 --- a/test-apps/testbed/frontend/ToolRegistry.test.ts +++ b/test-apps/testbed/frontend/ToolRegistry.test.ts @@ -3,10 +3,9 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import { assert } from "chai"; -import { IModelApp, Tool, FuzzySearchResults, FuzzySearchResult } from "@bentley/imodeljs-frontend"; +import { MockRender, IModelApp, Tool, FuzzySearchResults, FuzzySearchResult } from "@bentley/imodeljs-frontend"; import { I18NNamespace } from "@bentley/imodeljs-i18n"; import { TestbedConfig } from "../common/TestbedConfig"; -import { MockRender } from "./MockRender"; // these are later set by executing the TestImmediate tool. let testVal1: string; diff --git a/test-apps/testbed/frontend/VertexTable.test.ts b/test-apps/testbed/frontend/VertexTable.test.ts index 46ff259..69cad8a 100644 --- a/test-apps/testbed/frontend/VertexTable.test.ts +++ b/test-apps/testbed/frontend/VertexTable.test.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; import { MeshArgs, MeshParams } from "@bentley/imodeljs-frontend/lib/rendering"; +import { MockRender } from "@bentley/imodeljs-frontend"; import { RenderTexture, QParams2d, QPoint3dList, QParams3d, QPoint3d, ColorIndex, FeatureIndexType } from "@bentley/imodeljs-common"; import { Point2d } from "@bentley/geometry-core"; -import { MockRender } from "./MockRender"; function expectMeshParams(args: MeshArgs, colorIndex: ColorIndex, vertexBytes: number[][], expectedColors?: number[], quvParams?: QParams2d) { const params = MeshParams.create(args); @@ -118,9 +118,7 @@ describe("VertexLUT", () => { colors[0] = 0xffeeddcc; colors[1] = 0x00010203; colors[2] = 0x7f00ff00; - const colorIndices = new Uint16Array(3); - colorIndices[1] = 0x0001; - colorIndices[2] = 0xffee; + const colorIndices = [0, 0x0001, 0xffee]; args.colors.initNonUniform(colors, colorIndices, true); expected[0][6] = 0x00; expected[0][7] = 0x00; diff --git a/test-apps/testbed/frontend/ViewState.test.ts b/test-apps/testbed/frontend/ViewState.test.ts index 576b58c..9f95ec2 100644 --- a/test-apps/testbed/frontend/ViewState.test.ts +++ b/test-apps/testbed/frontend/ViewState.test.ts @@ -8,10 +8,11 @@ import { AmbientOcclusion, BackgroundMapType, ColorDef, HiddenLine, RenderMode, import * as path from "path"; import { SpatialViewState, ViewStatus, ViewState3d, StandardView, StandardViewId, MarginPercent, AuxCoordSystemSpatialState, CategorySelectorState, - ModelSelectorState, IModelConnection, DisplayStyle3dState, SheetModelState, SpatialModelState, DrawingModelState, + ModelSelectorState, IModelConnection, DisplayStyle3dState, SheetModelState, SpatialModelState, DrawingModelState, MockRender, } from "@bentley/imodeljs-frontend"; import { CONSTANTS } from "../common/Testbed"; -import { MockRender } from "./MockRender"; +import { IModelUnitTestRpcInterface } from "../common/IModelUnitTestRpcInterface"; +import { IModelUnitTestRpcImpl } from "../backend/IModelUnitTestRpcImpl"; const iModelLocation = path.join(CONSTANTS.IMODELJS_CORE_DIRNAME, "core/backend/lib/test/assets/test.bim"); const iModelLocation2 = path.join(CONSTANTS.IMODELJS_CORE_DIRNAME, "core/backend/lib/test/assets/CompatibilityTestSeed.bim"); @@ -20,6 +21,7 @@ describe("ViewState", () => { let imodel: IModelConnection; let imodel2: IModelConnection; let viewState: SpatialViewState; + let unitTestRpcImp: IModelUnitTestRpcImpl; before(async () => { MockRender.App.startup(); @@ -29,6 +31,8 @@ describe("ViewState", () => { viewState = await imodel.views.load(viewRows[0].id!) as SpatialViewState; imodel2 = await IModelConnection.openStandalone(iModelLocation2); + + unitTestRpcImp = IModelUnitTestRpcInterface.getClient(); }); after(async () => { @@ -64,7 +68,7 @@ describe("ViewState", () => { assert.equal(viewState.modelSelector.models.size, 5); assert.isTrue(viewState.origin.isAlmostEqual(new Point3d(-87.73958171815832, -108.96514044887601, -0.0853709702222105)), "View origin as expected"); - const v2 = viewState.clone(); + const v2 = viewState.clone(); compareView(viewState, v2.toJSON(), "v2 clone"); assert.notEqual(v2.origin, viewState.origin); // make sure we're really looking at a copy @@ -86,7 +90,7 @@ describe("ViewState", () => { }); it("should be able to propagate viewFlags and displayStyle changes when cloning ViewState", async () => { - const vs0 = viewState.clone(); + const vs0 = viewState.clone(); assert.isTrue(vs0.is3d(), "viewState should be 3d"); @@ -168,7 +172,7 @@ describe("ViewState", () => { // clone the state and check if the changes persisted - const vs1 = vs0.clone(); + const vs1 = vs0.clone(); const vs1DisplayStyle3d = (vs1 as ViewState3d).getDisplayStyle3d(); const vs1AOSettings = vs1DisplayStyle3d.settings.ambientOcclusionSettings; @@ -238,7 +242,7 @@ describe("ViewState", () => { viewState.setFocusDistance(49); viewState.setEyePoint(Point3d.create(5, 5, 50)); - let cppView: SpatialViewDefinitionProps = await imodel.executeTest("lookAtVolume", testParams); + let cppView: SpatialViewDefinitionProps = await unitTestRpcImp.executeTest(imodel.iModelToken, "lookAtVolume", testParams); viewState.lookAtVolume(testParams.volume, testParams.aspectRatio, testParams.margin); compareView(viewState, cppView, "LookAtVolume 1"); @@ -252,7 +256,7 @@ describe("ViewState", () => { testParams.volume = Range3d.createXYZXYZ(1000, -10, 6, -5, 0, 0); testParams.margin = new MarginPercent(.01, .02, .03, .04); testParams.aspectRatio = 1.2; - cppView = await imodel.executeTest("lookAtVolume", testParams); + cppView = await unitTestRpcImp.executeTest(imodel.iModelToken, "lookAtVolume", testParams); viewState.lookAtVolume(testParams.volume, testParams.aspectRatio, testParams.margin); compareView(viewState, cppView, "LookAtVolume 3"); @@ -266,7 +270,7 @@ describe("ViewState", () => { testParams.volume = Range3d.createXYZXYZ(10, 20, 0.5, 35, 21, 2); testParams.aspectRatio = 1.0; testParams.margin = new MarginPercent(0, 0, 0, 0); - cppView = await imodel.executeTest("lookAtVolume", testParams); + cppView = await unitTestRpcImp.executeTest(imodel.iModelToken, "lookAtVolume", testParams); viewState.lookAtVolume(testParams.volume, testParams.aspectRatio, testParams.margin); compareView(viewState, cppView, "LookAtVolume 2"); }); @@ -285,7 +289,7 @@ describe("ViewState", () => { viewState.setLensAngle(Angle.createDegrees(50)); viewState.setFocusDistance(49); viewState.setEyePoint(Point3d.create(5, 5, 50)); - let cppView: SpatialViewDefinitionProps = await imodel.executeTest("rotateCameraLocal", testParams); + let cppView: SpatialViewDefinitionProps = await unitTestRpcImp.executeTest(imodel.iModelToken, "rotateCameraLocal", testParams); viewState.rotateCameraLocal(Angle.createRadians(testParams.angle), testParams.axis, testParams.about); compareView(viewState, cppView, "RotateCameraLocal 1"); @@ -298,7 +302,7 @@ describe("ViewState", () => { testParams.angle = 1.6788888; testParams.axis = Vector3d.create(-1, 6, 3); testParams.about = Point3d.create(1, 2, 3); - cppView = await imodel.executeTest("rotateCameraLocal", testParams); + cppView = await unitTestRpcImp.executeTest(imodel.iModelToken, "rotateCameraLocal", testParams); viewState.rotateCameraLocal(Angle.createRadians(testParams.angle), testParams.axis, testParams.about); compareView(viewState, cppView, "RotateCameraLocal 2"); }); @@ -320,7 +324,7 @@ describe("ViewState", () => { viewState.setLensAngle(Angle.createDegrees(11)); viewState.setFocusDistance(191); viewState.setEyePoint(Point3d.create(-64, 120, 500)); - const cppView: SpatialViewDefinitionProps = await imodel.executeTest("lookAtUsingLensAngle", testParams); + const cppView: SpatialViewDefinitionProps = await unitTestRpcImp.executeTest(imodel.iModelToken, "lookAtUsingLensAngle", testParams); viewState.lookAtUsingLensAngle(testParams.eye, testParams.target, testParams.up, testParams.lens, testParams.front, testParams.back); compareView(viewState, cppView, "lookAtUsingLensAngle"); }); diff --git a/test-apps/testbed/frontend/Viewport.test.ts b/test-apps/testbed/frontend/Viewport.test.ts index b41592d..9ac30a2 100644 --- a/test-apps/testbed/frontend/Viewport.test.ts +++ b/test-apps/testbed/frontend/Viewport.test.ts @@ -2,19 +2,19 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import { assert } from "chai"; +import { assert, expect } from "chai"; import { Point3d, Angle } from "@bentley/geometry-core"; import { Cartographic, FontType, FontMap, ColorDef } from "@bentley/imodeljs-common"; import * as path from "path"; -import { SpatialViewState, StandardViewId, IModelConnection, ScreenViewport, IModelApp, PanViewTool, CompassMode, TwoWayViewportSync } from "@bentley/imodeljs-frontend"; +import { MockRender, SpatialViewState, StandardViewId, IModelConnection, ScreenViewport, IModelApp, PanViewTool, CompassMode, TwoWayViewportSync } from "@bentley/imodeljs-frontend"; import { CONSTANTS } from "../common/Testbed"; import { RenderPlan } from "@bentley/imodeljs-frontend/lib/rendering"; -import { MockRender } from "./MockRender"; -const iModelLocation = path.join(CONSTANTS.IMODELJS_CORE_DIRNAME, "core/backend/lib/test/assets/test.bim"); +const iModelDir = path.join(CONSTANTS.IMODELJS_CORE_DIRNAME, "core/backend/lib/test/assets"); describe("Viewport", () => { let imodel: IModelConnection; + let imodel2: IModelConnection; let spatialView: SpatialViewState; const createViewDiv = () => { @@ -30,7 +30,8 @@ describe("Viewport", () => { before(async () => { // Create a ViewState to load into a Viewport MockRender.App.startup(); - imodel = await IModelConnection.openStandalone(iModelLocation); + imodel = await IModelConnection.openStandalone(path.join(iModelDir, "test.bim")); + imodel2 = await IModelConnection.openStandalone(path.join(iModelDir, "test2.bim")); spatialView = await imodel.views.load("0x34") as SpatialViewState; spatialView.setStandardRotation(StandardViewId.RightIso); }); @@ -41,19 +42,19 @@ describe("Viewport", () => { }); it("Viewport", async () => { - const vpView = spatialView.clone(); + const vpView = spatialView.clone(); const vp = ScreenViewport.create(viewDiv!, vpView); assert.isFalse(vp.isRedoPossible, "no redo"); assert.isFalse(vp.isUndoPossible, "no undo"); assert.isFalse(vp.isCameraOn, "camera is off"); - const saveView = vpView.clone(); + const saveView = vpView.clone(); assert.notEqual(saveView.modelSelector, vpView.modelSelector, "clone should copy modelSelector"); assert.notEqual(saveView.categorySelector, vpView.categorySelector, "clone should copy categorySelector"); assert.notEqual(saveView.displayStyle, vpView.displayStyle, "clone should copy displayStyle"); const frustSave = vp.getFrustum(); - const vpView2 = spatialView.clone(); + const vpView2 = spatialView.clone(imodel2); vpView2.setStandardRotation(StandardViewId.Top); const vp2 = ScreenViewport.create(viewDiv2!, vpView2); assert.isFalse(vp2.getFrustum().isSame(vp.getFrustum()), "frustums should start out different"); @@ -62,18 +63,12 @@ describe("Viewport", () => { const vpConnection = new TwoWayViewportSync(); vpConnection.connect(vp, vp2); // wire them together assert.isTrue(vp2.getFrustum().isSame(frustSave), "vp2 frustum should be same as vp1 after connect"); - - // const clientRect = vp.getClientRect(); - vpView.camera.validateLens(); - - // currently the range test for visible elements doesn't match native code, so we get a different result. - // re-enable this test when models hold their ranges. - // const testParams: any = { view: vpView, rect: { left: clientRect.left, bottom: clientRect.bottom, right: clientRect.right, top: clientRect.top } }; - // const cppView = await imodel.executeTest("turnCameraOn", testParams); vp.turnCameraOn(); - // compareView(vpView, cppView, "turnCameraOn 3"); vp.synchWithView(true); + assert.equal(vp.iModel, imodel); + assert.equal(vp2.iModel, imodel2); + assert.isTrue(vp.isCameraOn, "camera should be on"); assert.isTrue(vp2.isCameraOn, "camera should be synched"); assert.isTrue(vp2.getFrustum().isSame(vp.getFrustum()), "frustum should be synched"); @@ -99,10 +94,65 @@ describe("Viewport", () => { const pan = IModelApp.tools.create("View.Pan", vp) as PanViewTool; assert.instanceOf(pan, PanViewTool); assert.equal(pan.viewport, vp); + + let neverDrawnChanged = 0; + let alwaysDrawnChanged = 0; + const removeNeverDrawnListener = vp.onNeverDrawnChanged.addListener((_) => ++neverDrawnChanged); + const removeAlwaysDrawnListener = vp.onAlwaysDrawnChanged.addListener((_) => ++alwaysDrawnChanged); + + // No event if the set is already empty when we clear it. + vp.clearNeverDrawn(); + expect(neverDrawnChanged).to.equal(0); + vp.clearAlwaysDrawn(); + expect(alwaysDrawnChanged).to.equal(0); + + // Assigning the set always raises an event. + const idSet = new Set(); + idSet.add("0x123"); + vp.setNeverDrawn(idSet); + expect(neverDrawnChanged).to.equal(1); + vp.setAlwaysDrawn(idSet, false); + expect(alwaysDrawnChanged).to.equal(1); + vp.setAlwaysDrawn(idSet, true); + expect(alwaysDrawnChanged).to.equal(2); + + // Clearing raises event if set was assigned. + vp.clearNeverDrawn(); + expect(neverDrawnChanged).to.equal(2); + vp.clearAlwaysDrawn(); + expect(alwaysDrawnChanged).to.equal(3); + + // Clearing again will not re-raise because already cleared. + vp.clearNeverDrawn(); + expect(neverDrawnChanged).to.equal(2); + vp.clearAlwaysDrawn(); + expect(alwaysDrawnChanged).to.equal(3); + + // Setting repeatedly to same set raises each time, because we're not going to compare to previous set every time it changes. + vp.setAlwaysDrawn(idSet, true); + vp.setAlwaysDrawn(idSet, true); + vp.setAlwaysDrawn(idSet, true); + expect(alwaysDrawnChanged).to.equal(6); + + // Setting to an empty set, and also setting the 'exclusive' flags - effectively means no elements should draw. + idSet.clear(); + vp.setAlwaysDrawn(idSet, true); + expect(alwaysDrawnChanged).to.equal(7); + vp.clearAlwaysDrawn(); + + // Raises even though set was already empty, because this resets the 'exclusive' flag. + expect(alwaysDrawnChanged).to.equal(8); + + // Exclusive flag no longer set and set is empty, so no event. + vp.clearAlwaysDrawn(); + expect(alwaysDrawnChanged).to.equal(8); + + removeNeverDrawnListener(); + removeAlwaysDrawnListener(); }); it("AccuDraw", () => { - const vpView = spatialView.clone(); + const vpView = spatialView.clone(); const viewport = ScreenViewport.create(viewDiv!, vpView); const accudraw = IModelApp.accuDraw; assert.isTrue(accudraw.isEnabled, "Accudraw should be enabled"); @@ -137,7 +187,7 @@ describe("Viewport", () => { }); it("creates a RenderPlan from a viewport", () => { - const vpView = spatialView.clone(); + const vpView = spatialView.clone(); const vp = ScreenViewport.create(viewDiv!, vpView); let plan: RenderPlan | undefined; try { diff --git a/test-apps/testbed/frontend/WebGLTestContext.ts b/test-apps/testbed/frontend/WebGLTestContext.ts index fe35c8f..a4559ef 100644 --- a/test-apps/testbed/frontend/WebGLTestContext.ts +++ b/test-apps/testbed/frontend/WebGLTestContext.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import { IModelApp, NullRenderSystem } from "@bentley/imodeljs-frontend"; -import { RenderSystem } from "@bentley/imodeljs-frontend/lib/rendering"; +import { RenderSystem, RenderDiagnostics } from "@bentley/imodeljs-frontend/lib/rendering"; // Electron running inside a Windows VM fails to acquire a WebGLRenderingContext. // This prevents us from creating a "real" RenderSystem during CI jobs. @@ -27,9 +27,11 @@ export namespace WebGLTestContext { export function startup() { MaybeRenderApp.startup(); isInitialized = MaybeRenderApp.hasRenderSystem; + MaybeRenderApp.renderSystem.enableDiagnostics(RenderDiagnostics.All); } export function shutdown() { + MaybeRenderApp.renderSystem.enableDiagnostics(RenderDiagnostics.None); MaybeRenderApp.shutdown(); isInitialized = false; } diff --git a/test-apps/testbed/package.json b/test-apps/testbed/package.json index 4bfa979..037a2df 100644 --- a/test-apps/testbed/package.json +++ b/test-apps/testbed/package.json @@ -5,7 +5,7 @@ "license": "MIT", "scripts": { "prebuild": "rush build -t testbed", - "build": "tsc 1>&2 && npm run copy:frontendpublic && npm run copy:testpublic && npm run webpack:frontend", + "build": "tsc 1>&2 && npm run copy:frontendpublic && npm run copy:testpublic && npm run webpack:frontend && node ./node_modules/@bentley/build-tools/scripts/ignoreargs.js 1>&2", "clean": "rimraf lib backend/lib frontend/lib package-deps.json coverage", "copy:frontendpublic": "cpx \"../../core/frontend/src/public/**/*\" ./lib/backend/public", "copy:testpublic": "cpx \"./public/**/*\" ./lib/backend/public", @@ -19,13 +19,13 @@ }, "repository": {}, "dependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/geometry-core": "0.187.0", - "@bentley/imodeljs-backend": "0.187.0", - "@bentley/imodeljs-clients": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-i18n": "0.187.0", - "@bentley/imodeljs-frontend": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/imodeljs-backend": "0.189.0", + "@bentley/imodeljs-clients": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-i18n": "0.189.0", + "@bentley/imodeljs-frontend": "0.189.0", "body-parser": "^1.18.2", "chai": "^4.1.2", "commander": "^2.14.1", @@ -35,7 +35,7 @@ "fuse.js": "^3.3.0", "i18next": "^10.2.2", "i18next-browser-languagedetector": "^2.1.0", - "i18next-xhr-backend": "^1.5.1", + "i18next-xhr-backend": "^2.0.1", "js-base64": "^2.4.5", "object-assign": "^4.1.1", "resolve": "^1.1.7", @@ -44,18 +44,17 @@ "ws": "^6.0.0" }, "devDependencies": { - "@bentley/config-loader": "0.187.0", - "@bentley/build-tools": "0.187.0", + "@bentley/config-loader": "0.189.0", + "@bentley/build-tools": "0.189.0", "@types/body-parser": "^1.17.0", "@types/chai": "^4.1.4", - "@types/express": "^4.11.1", + "@types/express": "^4.16.1", "@types/fs-extra": "^4.0.7", "@types/i18next": "^8.4.2", "@types/i18next-browser-languagedetector": "^2.0.1", - "@types/i18next-xhr-backend": "^1.4.1", "@types/js-base64": "^2.3.1", "@types/mocha": "^5.2.5", - "@types/node": "10.10.3", + "@types/node": "10.12.18", "@types/webpack": "^3.8.5", "@types/semver": "^5.5.0", "@types/ws": "^6.0.1", @@ -68,7 +67,7 @@ "rimraf": "^2.6.2", "source-map-loader": "^0.2.3", "tslint": "^5.11.0", - "typescript": "~3.1.0", + "typescript": "~3.2.2", "webpack": "^4.20.2", "find-root": "1.1.0", "nyc": "^13.0.1", diff --git a/test-apps/testbed/webpack.config.js b/test-apps/testbed/webpack.config.js index b18bea9..af5ce1c 100644 --- a/test-apps/testbed/webpack.config.js +++ b/test-apps/testbed/webpack.config.js @@ -17,6 +17,13 @@ module.exports = { }, devtool: "nosources-source-map", module: { + noParse: [ + // Don't parse draco_*_nodejs.js modules for `require` calls. There are + // requires for fs that cause it to fail even though the fs dependency + // is not used. + /draco_decoder_nodejs.js$/, + /draco_encoder_nodejs.js$/ + ], rules: [ { test: /\.js$/, @@ -47,5 +54,5 @@ module.exports = { }, {}), }) ] - }; +}; diff --git a/test-apps/ui-test-app/package.json b/test-apps/ui-test-app/package.json index d75116c..052ec49 100644 --- a/test-apps/ui-test-app/package.json +++ b/test-apps/ui-test-app/package.json @@ -5,27 +5,63 @@ "license": "MIT", "version": "0.0.0", "scripts": { - "build": "tsc 1>&2 && npm run build:scss && npm run build:svg && npm run copy:public && npm run webpack:modules", - "build:scss": "cpx \"./src/**/*.scss\" ./lib", - "build:svg": "cpx \"./src/**/*.svg\" ./lib", + "build": "node ./node_modules/@bentley/webpack-tools/bin/buildIModelJsModule", "lint": "tslint -p . 1>&2", "electron": "electron ./lib/backend/main.js", "start:webserver": "node ./node_modules/@bentley/imodeljs-webserver/lib/WebServer.js --port=3000 --resources=./lib/webresources/", "start:backend": "node lib/backend/main.js", "start:servers": "run-p \"start:webserver\" \"start:backend\"", - "test": "node ./node_modules/@bentley/webpack-tools/bin/bentley-webpack-tools.js test --useConfigLoader", - "cover": "node ./node_modules/@bentley/webpack-tools/bin/bentley-webpack-tools.js cover --useConfigLoader", "clean": "rimraf lib dist package-deps.json", - "copy:public": "cpx \"public/**/*\" ./lib/webresources && cpx \"src/frontend/plugins/public/**/*\" ./lib/webresources", - "copy:modules": "node ./node_modules/@bentley/webpack-tools/modules/copyExternalModules.js --packageFile=./package.json --destDir=./lib/webresources --type=dev", - "copy:modulesprod": "node ./node_modules/@bentley/webpack-tools/modules/copyExternalModules.js --packageFile=./package.json --destDir=./lib/webresources --type=prod", - "makeconfig": "node ./node_modules/@bentley/config-loader/scripts/write.js ./lib/webresources/config.json ^imjs_", - "copy:plugin": "cpx \"./lib/module/dev/MeasurePoints.js*\" ./lib/webresources", - "webpack:plugin": "webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.outdir=./lib/webresources --env.entry=./lib/frontend/plugins/MeasurePoints.js --env.bundlename=MeasurePoints", - "webpack:main": "webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.outdir=./lib/webresources --env.entry=./lib/frontend/index.js --env.bundlename=main --env.stylesheets", - "webpack:modules": "npm run copy:modules && npm run webpack:main && npm run makeconfig && npm run webpack:plugin && npm run copy:plugin", + "test": "", + "cover": "", "docs": "" }, + "iModelJs": { + "buildModule": { + "type": "application", + "sourceResources": [ + { + "source": "./src/**/*.scss", + "dest": "./lib" + }, + { + "source": "./src/**/*.svg", + "dest": "./lib" + }, + { + "source": "./public/**/*", + "dest": "./lib/webresources" + }, + { + "source": "./src/frontend/plugins/public/**/*", + "dest": "./lib/webresources" + } + ], + "webpack": { + "dest": "./lib/webresources", + "entry": "./lib/frontend/index.js", + "bundleName": "main", + "styleSheets": true, + "htmlTemplate": "./src/frontend/index.html" + }, + "subModules": [ + { + "dest": "./lib/webresources", + "entry": "./lib/frontend/plugins/MeasurePoints.js", + "bundleName": "MeasurePoints", + "type": "plugin" + } + ], + "makeConfig": { + "dest": "./lib/webresources/config.json", + "filter": "^imjs_" + }, + "pseudoLocalize": { + "source": "./lib/webresources/locales/en", + "dest": "./lib/webresources/locales/en-pseudo" + } + } + }, "repository": {}, "keywords": [ "Bentley", @@ -41,28 +77,26 @@ "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/config-loader": "0.187.0", - "@bentley/webpack-tools": "0.187.0", - "@bentley/build-tools": "0.187.0", - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/imodeljs-i18n": "0.187.0", - "@bentley/imodeljs-backend": "0.187.0", - "@bentley/imodeljs-frontend": "0.187.0", - "@bentley/imodeljs-quantity": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-clients": "0.187.0", - "@bentley/imodeljs-webserver": "0.187.0", - "@bentley/bwc": "^7.1.0", - "@bentley/icons-generic-webfont": "^0.0.6", + "@bentley/config-loader": "0.189.0", + "@bentley/webpack-tools": "0.189.0", + "@bentley/build-tools": "0.189.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/imodeljs-i18n": "0.189.0", + "@bentley/imodeljs-backend": "0.189.0", + "@bentley/imodeljs-frontend": "0.189.0", + "@bentley/imodeljs-quantity": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-clients": "0.189.0", + "@bentley/imodeljs-webserver": "0.189.0", + "@bentley/icons-generic-webfont": "^0.0.9", "@types/body-parser": "^1.17.0", - "@types/express": "^4.11.1", - "@types/react": "^16.4.14", - "@types/react-dom": "16.0.7", - "@types/react-redux": "^5.0.15", + "@types/express": "^4.16.1", + "@types/react": "16.7.22", + "@types/react-dom": "16.0.11", + "@types/react-redux": "^7.0.1", "@types/semver": "^5.5.0", "autoprefixer": "^8.6.5", "css-loader": "^0.28.11", - "cpx": "^1.5.0", "electron": "^4.0.1", "electron-chromedriver": "^2.0.0", "file-loader": "^1.1.11", @@ -72,47 +106,40 @@ "react-test-renderer": "^16.4.2", "rimraf": "^2.6.2", "sass-loader": "^7.1.0", - "source-map-loader": "^0.2.3", "style-loader": "^0.21.0", "svg-sprite-loader": "^3.8.0", "tsconfig-paths": "^3.3.2", "tslint": "^5.11.0", - "typescript": "~3.1.0", + "typescript": "~3.2.2", "uglifyjs-webpack-plugin": "^1.2.5", "url-loader": "^1.0.1", "webpack": "^4.20.2" }, "dependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/electron-manager": "0.187.0", - "@bentley/geometry-core": "0.187.0", - "@bentley/imodeljs-clients": "0.187.0", - "@bentley/imodeljs-clients-backend": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-i18n": "0.187.0", - "@bentley/imodeljs-quantity": "0.187.0", - "@bentley/imodeljs-frontend": "0.187.0", - "@bentley/imodeljs-backend": "0.187.0", - "@bentley/bwc": "^7.1.0", - "@bentley/presentation-backend": "0.187.0", - "@bentley/presentation-common": "0.187.0", - "@bentley/presentation-components": "0.187.0", - "@bentley/presentation-frontend": "0.187.0", - "@bentley/ui-core": "0.187.0", - "@bentley/ui-components": "0.187.0", - "@bentley/ui-ninezone": "0.187.0", - "@bentley/ui-framework": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/electron-manager": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/imodeljs-clients": "0.189.0", + "@bentley/imodeljs-clients-backend": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-i18n": "0.189.0", + "@bentley/imodeljs-frontend": "0.189.0", + "@bentley/imodeljs-quantity": "0.189.0", + "@bentley/imodeljs-backend": "0.189.0", + "@bentley/presentation-backend": "0.189.0", + "@bentley/presentation-common": "0.189.0", + "@bentley/presentation-components": "0.189.0", + "@bentley/presentation-frontend": "0.189.0", + "@bentley/ui-core": "0.189.0", + "@bentley/ui-components": "0.189.0", + "@bentley/ui-ninezone": "0.189.0", + "@bentley/ui-framework": "0.189.0", "body-parser": "^1.18.2", "express": "^4.16.3", - "inspire-tree": "^5.0.1", - "lodash": "^4.17.10", "mobx": "^5.8.0", "mobx-react": "^5.4.3", "react": "^16.4.2", "react-dom": "^16.4.2", - "react-dnd": "^5.0.0", - "react-dnd-html5-backend": "^5.0.1", - "react-redux": "^5.0.7", "redux": "^4.0.0", "semver": "^5.5.0", "immutable": "^3.8.2", diff --git a/test-apps/ui-test-app/public/locales/en/SampleApp.json b/test-apps/ui-test-app/public/locales/en/SampleApp.json index 702f3df..05706a3 100644 --- a/test-apps/ui-test-app/public/locales/en/SampleApp.json +++ b/test-apps/ui-test-app/public/locales/en/SampleApp.json @@ -29,6 +29,7 @@ "testFrontstage3": "Test 3 Frontstage", "testFrontstage4": "Test 4 Frontstage", "testFrontstage5": "Test 5 Frontstage", + "testFrontstage6": "Settings", "iModelStage": "Review iModel", "task1": "Task 1", "task2": "Task 2", @@ -63,7 +64,8 @@ "number": "Number", "password": "Password", "radio": "Radio", - "range": "Range" + "range": "Range", + "color": "Color" }, "tool2": { "month": "Month2", @@ -109,6 +111,26 @@ "openNestedFrontstage1": "Open Nested Frontstage" }, "tools": { + "ToolWithSettings": { + "keyin": "ToolWithSettings", + "description": "Demo Tool with Settings", + "flyover": "Tool with Settings", + "Prompts": { + "GetPoint": "Enter point/Reset to Exit", + "Options": "Options", + "Lock": "Lock", + "City": "City", + "State": "State", + "Coordinate": "Coordinate", + "Length": "Length" + }, + "Options": { + "Red": "Red", + "White": "White", + "Blue": "Blue", + "Yellow": "Yellow" + } + }, "Tool1": { "keyin": "Tool1", "description": "Test1 tool", @@ -136,6 +158,11 @@ } } }, + "AppSelect": { + "keyin": "Select", + "description": "Element Selection", + "flyover": "Select" + }, "select": "Select", "fitView": "Fit View", "windowArea": "Window Area", diff --git a/test-apps/ui-test-app/src/frontend/appui/AppBackstage.tsx b/test-apps/ui-test-app/src/frontend/appui/AppBackstage.tsx index 4c67509..5184084 100644 --- a/test-apps/ui-test-app/src/frontend/appui/AppBackstage.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/AppBackstage.tsx @@ -4,15 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import * as React from "react"; import { connect } from "react-redux"; - import { SampleAppIModelApp, RootState, SampleAppUiActionId } from ".."; - import { Backstage, + FrontstageManager, FrontstageLaunchBackstageItem, + CommandLaunchBackstageItem, SeparatorBackstageItem, BackstageCloseEventArgs, BooleanSyncUiListener, + SettingsModalFrontstage, } from "@bentley/ui-framework"; import { Tool } from "@bentley/imodeljs-frontend"; @@ -72,7 +73,6 @@ class AppBackstage extends React.Component { } public render(): React.ReactNode { - return ( @@ -84,6 +84,9 @@ class AppBackstage extends React.Component { {(isEnabled: boolean) => } + FrontstageManager.openModalFrontstage(new SettingsModalFrontstage())}/> + ); diff --git a/test-apps/ui-test-app/src/frontend/appui/AppUi.tsx b/test-apps/ui-test-app/src/frontend/appui/AppUi.tsx index 16f71c4..046497b 100644 --- a/test-apps/ui-test-app/src/frontend/appui/AppUi.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/AppUi.tsx @@ -6,7 +6,8 @@ import "@bentley/icons-generic-webfont/dist/bentley-icons-generic-webfont.css"; import { ConfigurableUiManager, FrontstageManager, WidgetState, - ContentGroupProps, ViewClass, TaskPropsList, WorkflowPropsList, ContentLayoutProps, KeyboardShortcutProps, CoreTools, FunctionKey, CommandItemDef, KeyboardShortcutManager, + ContentGroupProps, ViewClass, TaskPropsList, WorkflowPropsList, ContentLayoutProps, + KeyboardShortcutProps, FunctionKey, CommandItemDef, KeyboardShortcutManager, } from "@bentley/ui-framework"; import { StandardViewId } from "@bentley/imodeljs-frontend"; @@ -331,12 +332,7 @@ export class AppUi { // Test Workflows const workflowPropsList: WorkflowPropsList = { - defaultWorkflowId: "default-workflow", - taskPicker: { - classId: "taskpicker-class", - iconSpec: "taskpicker-icon", - labelKey: "taskpicker-label", - }, + defaultWorkflowId: "ExampleWorkflow", workflows: [ { id: "ExampleWorkflow", @@ -377,7 +373,7 @@ export class AppUi { }, { key: "s", - item: CoreTools.selectElementCommand, + item: AppTools.appSelectElementCommand, }, ], }, diff --git a/test-apps/ui-test-app/src/frontend/appui/contentviews/CubeContent.scss b/test-apps/ui-test-app/src/frontend/appui/contentviews/CubeContent.scss index f47e4f4..a86a6a7 100644 --- a/test-apps/ui-test-app/src/frontend/appui/contentviews/CubeContent.scss +++ b/test-apps/ui-test-app/src/frontend/appui/contentviews/CubeContent.scss @@ -1,45 +1,45 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ -@import "@bentley/bwc/lib/index.scss"; - -$example-cube-width: 256px; -.example-cube-container { - display: flex; - justify-content: center; - align-items: center; - width: 100%; - height: 100%; - background: black; - .example-cube { - perspective: 600px; - width: $example-cube-width; - height: $example-cube-width; - .face { - display: flex; - width: 100%; - height: 100%; - color: $bwc-text-color; - box-shadow: 0px 0px .0px 1px #000; - } - .top { - background: blue; - } - .front{ - background: red; - } - .right { - background: limegreen; - } - .left { - background: darkviolet; - } - .back { - background: yellow; - } - .bottom { - background: orange; - } - } -} +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +@import "@bentley/ui-core/lib/ui-core/index"; + +$example-cube-width: 256px; +.example-cube-container { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + background: black; + .example-cube { + perspective: 600px; + width: $example-cube-width; + height: $example-cube-width; + .face { + display: flex; + width: 100%; + height: 100%; + color: $uicore-text-color; + box-shadow: 0px 0px .0px 1px #000; + } + .top { + background: blue; + } + .front{ + background: red; + } + .right { + background: limegreen; + } + .left { + background: darkviolet; + } + .back { + background: yellow; + } + .bottom { + background: orange; + } + } +} diff --git a/test-apps/ui-test-app/src/frontend/appui/contentviews/TableExampleContent.tsx b/test-apps/ui-test-app/src/frontend/appui/contentviews/TableExampleContent.tsx index b6c63f1..3aff683 100644 --- a/test-apps/ui-test-app/src/frontend/appui/contentviews/TableExampleContent.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/contentviews/TableExampleContent.tsx @@ -5,9 +5,10 @@ import * as React from "react"; import { ConfigurableUiManager, ConfigurableCreateInfo, ContentControl } from "@bentley/ui-framework"; +import { PropertyRecord, PropertyValueFormat, PropertyValue, PropertyDescription } from "@bentley/imodeljs-frontend"; import { Table, ColumnDescription, RowItem, TableDataProvider, - SimpleTableDataProvider, TableSelectionTarget, SelectionMode, PropertyRecord, PropertyValueFormat, PropertyValue, PropertyDescription, + SimpleTableDataProvider, TableSelectionTarget, SelectionMode, PropertyUpdatedArgs, TableCellUpdatedArgs, } from "@bentley/ui-components"; @@ -41,6 +42,30 @@ const createPropertyRecord = (value: any, column: ColumnDescription, typename: s return new PropertyRecord(v, pd); }; +const createEnumPropertyRecord = (rowIndex: number, column: ColumnDescription) => { + const value = rowIndex % 4; + const v: PropertyValue = { + valueFormat: PropertyValueFormat.Primitive, + value, + displayValue: value.toString(), + }; + const pd: PropertyDescription = { + typename: "enum", + name: column.key, + displayLabel: column.label, + }; + column.propertyDescription = pd; + const enumPropertyRecord = new PropertyRecord(v, pd); + enumPropertyRecord.property.enum = { choices: [], isStrict: false }; + enumPropertyRecord.property.enum.choices = [ + { label: "Yellow", value: 0 }, + { label: "Red", value: 1 }, + { label: "Green", value: 2 }, + { label: "Blue", value: 3 }, + ]; + return enumPropertyRecord; +}; + class TableExampleContent extends React.Component<{}, TableExampleState> { public readonly state: Readonly; @@ -59,10 +84,11 @@ class TableExampleContent extends React.Component<{}, TableExampleState> { label: "Title", sortable: true, resizable: true, + editable: true, }, { - key: "more", - label: "More Data", + key: "color", + label: "Color", sortable: true, resizable: false, editable: true, @@ -81,8 +107,8 @@ class TableExampleContent extends React.Component<{}, TableExampleState> { record: createPropertyRecord("Title " + i, columns[1], "text"), }); row.cells.push({ - key: "more", - record: createPropertyRecord("More Data - " + i, columns[2], "text"), + key: "color", + record: createEnumPropertyRecord(i, columns[2]), }); rows.push(row); } @@ -123,13 +149,8 @@ class TableExampleContent extends React.Component<{}, TableExampleState> { this.setState({ tableSelectionTarget: TableSelectionTarget.Cell }); } - private _updatePropertyRecord(record: PropertyRecord, newValue: string): PropertyRecord { - const propertyValue: PropertyValue = { - valueFormat: PropertyValueFormat.Primitive, - value: newValue, - displayValue: newValue, - }; - return record.copyWithNewValue(propertyValue); + private _updatePropertyRecord(record: PropertyRecord, newValue: PropertyValue): PropertyRecord { + return record.copyWithNewValue(newValue); } private _handlePropertyUpdated = async (propertyArgs: PropertyUpdatedArgs, cellArgs: TableCellUpdatedArgs): Promise => { diff --git a/test-apps/ui-test-app/src/frontend/appui/contentviews/TreeExampleContent.tsx b/test-apps/ui-test-app/src/frontend/appui/contentviews/TreeExampleContent.tsx index 8eca2f8..9e38fd0 100644 --- a/test-apps/ui-test-app/src/frontend/appui/contentviews/TreeExampleContent.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/contentviews/TreeExampleContent.tsx @@ -8,7 +8,7 @@ import { ConfigurableUiManager, ConfigurableCreateInfo, ContentControl } from "@ import { SelectionMode, DelayLoadedTreeNodeItem, SimpleTreeDataProvider, SimpleTreeDataProviderHierarchy, - Tree, TreeCellEditorState, TreeCellUpdatedArgs, TreeNodeItem, EditableTreeDataProvider, + Tree, TreeCellUpdatedArgs, TreeNodeItem, EditableTreeDataProvider, } from "@bentley/ui-components"; // import { demoMutableTreeDataProvider } from "../widgets/demodataproviders/demoTreeDataProvider"; @@ -87,12 +87,13 @@ class TreeExampleContent extends React.Component<{}, TreeExampleState> { this.setState({ selectionMode }); } - private _onCellEditing = (_cellEditorState: TreeCellEditorState): void => { + private _onCellEditing = (): void => { } private _onCellUpdated = async (args: TreeCellUpdatedArgs): Promise => { const nodeItem: TreeNodeItem = args.node.payload!; - this.state.dataProvider.updateLabel(nodeItem, args.newValue); + const newLabel = args.newValue; + this.state.dataProvider.updateLabel(nodeItem, newLabel); return true; } @@ -108,7 +109,14 @@ class TreeExampleContent extends React.Component<{}, TreeExampleState> {
- +
); diff --git a/test-apps/ui-test-app/src/frontend/appui/dialogs/TestMessageBox.tsx b/test-apps/ui-test-app/src/frontend/appui/dialogs/TestMessageBox.tsx index 18bc60a..ef5d189 100644 --- a/test-apps/ui-test-app/src/frontend/appui/dialogs/TestMessageBox.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/dialogs/TestMessageBox.tsx @@ -3,7 +3,7 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import * as React from "react"; -import { ButtonType, ButtonStyle, MessageBox, MessageSeverity } from "@bentley/ui-core"; +import { DialogButtonType, DialogButtonStyle, MessageBox, MessageSeverity, Button, ButtonType } from "@bentley/ui-core"; import { ModalDialogManager } from "@bentley/ui-framework"; import { IModelApp, MessageBoxType, MessageBoxIconType } from "@bentley/imodeljs-frontend"; @@ -11,7 +11,7 @@ export interface TestMessageBoxProps { opened: boolean; severity: MessageSeverity; title: string; - onResult?: (result: ButtonType) => void; + onResult?: (result: DialogButtonType) => void; } export interface TestMessageBoxState { @@ -37,16 +37,16 @@ export class TestMessageBox extends React.Component { this._toggleOpened(); } }, + { type: DialogButtonType.Close, buttonStyle: DialogButtonStyle.Primary, onClick: () => { this._toggleOpened(); } }, ]} onClose={this._toggleOpened} onEscape={this._toggleOpened} > Lorem ipsum dolor sit amet, posse imperdiet ius in, mundi cotidieque ei per. Vel scripta ornatus assentior cu. Duo nonumy equidem te, per ad malis deserunt consetetur. In per invidunt conceptam. Ea pri aeque corrumpit. Eum ea ipsum perfecto vulputate, an cum oblique ornatus.
- +
); @@ -69,7 +69,7 @@ export class TestMessageBox extends React.Component void; + onResult?: (result: DialogButtonType) => void; } export interface TestModalDialogState { @@ -39,8 +39,8 @@ export class TestModalDialog extends React.Component { this._handleOK(); } }, - { type: ButtonType.Cancel, onClick: () => { this._handleCancel(); } }, + { type: DialogButtonType.OK, onClick: () => { this._handleOK(); } }, + { type: DialogButtonType.Cancel, onClick: () => { this._handleCancel(); } }, ]} onClose={() => this._handleCancel()} onEscape={() => this._handleCancel()} @@ -61,14 +61,14 @@ export class TestModalDialog extends React.Component { this._closeDialog(() => { if (this.props.onResult) - this.props.onResult(ButtonType.OK); + this.props.onResult(DialogButtonType.OK); }); } private _handleCancel = () => { this._closeDialog(() => { if (this.props.onResult) - this.props.onResult(ButtonType.Cancel); + this.props.onResult(DialogButtonType.Cancel); }); } diff --git a/test-apps/ui-test-app/src/frontend/appui/frontstages/Frontstage1.tsx b/test-apps/ui-test-app/src/frontend/appui/frontstages/Frontstage1.tsx index 8fb24d4..9b0e86c 100644 --- a/test-apps/ui-test-app/src/frontend/appui/frontstages/Frontstage1.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/frontstages/Frontstage1.tsx @@ -20,6 +20,7 @@ import { ActionItemButton, CommandItemDef, FrontstageManager, + CoreTools, } from "@bentley/ui-framework"; import { AppStatusBarWidgetControl } from "../statusbars/AppStatusBar"; @@ -35,11 +36,11 @@ export class Frontstage1 extends FrontstageProvider { public get frontstage(): React.ReactElement { return ( + @@ -131,6 +133,7 @@ class FrontstageToolWidget extends React.Component { expandsTo={Direction.Right} items={ <> + } @@ -157,10 +157,12 @@ export class Frontstage3 extends FrontstageProvider { } @@ -188,7 +190,7 @@ export class Frontstage3 extends FrontstageProvider { { return ( , + , , ]} /> @@ -93,6 +94,7 @@ class FrontstageToolWidget extends React.Component { expandsTo={Direction.Bottom} items={ <> + + { - const details = new NotifyMessageDetails(OutputMessagePriority.Info, "Press an arrow", "Press an arrow and move mouse to dismiss", OutputMessageType.Pointer); + const details = new NotifyMessageDetails(OutputMessagePriority.Error, "Press an arrow", "Press an arrow and move mouse to dismiss", OutputMessageType.Pointer); details.setPointerTypeDetails(IModelApp.viewManager.selectedView!.parentDiv, { x: window.innerWidth / 2, @@ -232,7 +233,7 @@ class FrontstageToolWidget extends React.Component { } private _handleTool4Keypress = (event: any) => { - const details = new NotifyMessageDetails(OutputMessagePriority.Info, "", "", OutputMessageType.Pointer); + const details = new NotifyMessageDetails(OutputMessagePriority.Warning, "", "", OutputMessageType.Pointer); const viewport = IModelApp.viewManager.selectedView!.parentDiv; const midX = window.innerWidth / 2; const midY = window.innerHeight / 2; @@ -290,9 +291,6 @@ class FrontstageToolWidget extends React.Component { private executeMeasureByPoints() { // first load the plugin IModelApp.tools.run("Plugin", ["MeasurePoints.js"]); - // then wait one second and run the newly installed Plugin tool. - BeDuration.wait(1000).then(() => { IModelApp.tools.run("Measure.Points"); }) - .catch(); } private _horizontalToolbar = @@ -300,13 +298,16 @@ class FrontstageToolWidget extends React.Component { expandsTo={Direction.Bottom} items={ <> - + + SampleAppIModelApp.getTestProperty() !== "HIDE"}> {(enabled: boolean, otherProps: any) => } + + SampleAppIModelApp.getTestProperty() !== "HIDE"}> {(isVisible: boolean, otherProps: any) => { diff --git a/test-apps/ui-test-app/src/frontend/appui/tooluiproviders/MeasurePoints.tsx b/test-apps/ui-test-app/src/frontend/appui/tooluiproviders/MeasurePoints.tsx index c334f97..e33f6c8 100644 --- a/test-apps/ui-test-app/src/frontend/appui/tooluiproviders/MeasurePoints.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/tooluiproviders/MeasurePoints.tsx @@ -6,7 +6,6 @@ import * as React from "react"; import { IModelApp } from "@bentley/imodeljs-frontend"; import { ToolButton, FrontstageManager, ConfigurableUiControlType, ContentControlActivatedEventArgs } from "@bentley/ui-framework"; -import { BeDuration } from "@bentley/bentleyjs-core"; // import { constants } from "perf_hooks"; export interface ToolState { @@ -22,9 +21,6 @@ export class MeasureByPointsButton extends React.Component<{}, ToolState> { public static executeCommand = () => { // first load the plugin IModelApp.tools.run("Plugin", ["MeasurePoints.js"]); - // then wait one second and run the newly installed Plugin tool. - BeDuration.wait(1000).then(() => { IModelApp.tools.run("Measure.Points"); }) - .catch(); } private _handleContentControlActivatedEvent = (args: ContentControlActivatedEventArgs) => { diff --git a/test-apps/ui-test-app/src/frontend/appui/tooluiproviders/Tool1UiProvider.tsx b/test-apps/ui-test-app/src/frontend/appui/tooluiproviders/Tool1UiProvider.tsx index 236343d..a993be1 100644 --- a/test-apps/ui-test-app/src/frontend/appui/tooluiproviders/Tool1UiProvider.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/tooluiproviders/Tool1UiProvider.tsx @@ -5,7 +5,9 @@ import * as React from "react"; import { ConfigurableUiManager, ConfigurableCreateInfo, ToolUiProvider } from "@bentley/ui-framework"; +import { IModelApp, NotifyMessageDetails, OutputMessagePriority } from "@bentley/imodeljs-frontend"; +import { ColorSwatch } from "@bentley/ui-components"; import { ToolAssistanceItem, ToolAssistanceSeparator } from "@bentley/ui-ninezone"; import { SampleAppIModelApp } from "../.."; @@ -22,6 +24,11 @@ class Tool1UiProvider extends ToolUiProvider { } class Tool1Settings extends React.Component { + private _handleColorChange = (color: string) => { + const msg = `Color set to ${color}`; + IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Info, msg)); + } + public render(): React.ReactNode { return (
@@ -51,6 +58,26 @@ class Tool1Settings extends React.Component { {SampleAppIModelApp.i18n.translate("SampleApp:tool1.range")} + + Red + + + + Green + + + + Blue + + + + Purple + + + + Brown + +
diff --git a/test-apps/ui-test-app/src/frontend/appui/widgets/MobxDemoWidget/MobxDemoView.scss b/test-apps/ui-test-app/src/frontend/appui/widgets/MobxDemoWidget/MobxDemoView.scss index 8229cb3..e7e11cb 100644 --- a/test-apps/ui-test-app/src/frontend/appui/widgets/MobxDemoWidget/MobxDemoView.scss +++ b/test-apps/ui-test-app/src/frontend/appui/widgets/MobxDemoWidget/MobxDemoView.scss @@ -2,8 +2,7 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -@import "@bentley/bwc/lib/inputs/classes"; -@import "@bentley/bwc/lib/buttons/classes"; +@import "@bentley/ui-core/lib/ui-core/index"; .bird-name { width: 200px; diff --git a/test-apps/ui-test-app/src/frontend/appui/widgets/MobxDemoWidget/MobxDemoView.tsx b/test-apps/ui-test-app/src/frontend/appui/widgets/MobxDemoWidget/MobxDemoView.tsx index 030f211..5aeedf9 100644 --- a/test-apps/ui-test-app/src/frontend/appui/widgets/MobxDemoWidget/MobxDemoView.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/widgets/MobxDemoWidget/MobxDemoView.tsx @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as React from "react"; -import { Input, PrimaryButton } from "@bentley/bwc"; +import { Input, Button } from "@bentley/ui-core"; import "./MobxDemoView.scss"; interface MobxDemoViewProps { @@ -35,7 +35,7 @@ export class MobxDemoView extends React.Component {
  - Add bird +
    diff --git a/test-apps/ui-test-app/src/frontend/appui/widgets/PropertyGridDemoWidget.tsx b/test-apps/ui-test-app/src/frontend/appui/widgets/PropertyGridDemoWidget.tsx index b5af467..d582996 100644 --- a/test-apps/ui-test-app/src/frontend/appui/widgets/PropertyGridDemoWidget.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/widgets/PropertyGridDemoWidget.tsx @@ -8,8 +8,10 @@ import { ConfigurableUiManager, ConfigurableCreateInfo, ContentControl, WidgetCo import { Orientation } from "@bentley/ui-core"; import { - PropertyDescription, PropertyRecord, PropertyValueFormat, PrimitiveValue, - PropertyGrid, SimplePropertyDataProvider, PropertyValue, PropertyUpdatedArgs, PropertyCategory, + PropertyDescription, PropertyRecord, PropertyValueFormat, PrimitiveValue, PropertyValue, +} from "@bentley/imodeljs-frontend"; +import { + PropertyGrid, SimplePropertyDataProvider, PropertyUpdatedArgs, PropertyCategory, } from "@bentley/ui-components"; class SamplePropertyRecord extends PropertyRecord { @@ -65,12 +67,12 @@ class SamplePropertyDataProvider extends SimplePropertyDataProvider { new SamplePropertyRecord("ID_Attribute", iVolume, "34B72774-E885-4FB7-B031-64D040E37322"), new SamplePropertyRecord("Name", iVolume, "DT1002"), enumPropertyRecord, + booleanPropertyRecord, ], [ new SamplePropertyRecord("ID", iVolume, "34B72774-E885-4FB7-B031-64D040E37322", ""), new SamplePropertyRecord("Model", iVolume, "Default"), new SamplePropertyRecord("Level", iVolume, "Default"), - booleanPropertyRecord, ], [ new SamplePropertyRecord("Area", iVolume, "6.1875", "ft2"), @@ -107,13 +109,8 @@ class VerticalPropertyGridWidget extends React.Component { this._dataProvider = new SamplePropertyDataProvider(); } - private _updatePropertyRecord(record: PropertyRecord, newValue: string): PropertyRecord { - const propertyValue: PropertyValue = { - valueFormat: PropertyValueFormat.Primitive, - value: newValue, - displayValue: newValue.toString(), - }; - return record.copyWithNewValue(propertyValue); + private _updatePropertyRecord(record: PropertyRecord, newValue: PropertyValue): PropertyRecord { + return record.copyWithNewValue(newValue); } private _handlePropertyUpdated = async (args: PropertyUpdatedArgs, category: PropertyCategory): Promise => { @@ -157,13 +154,8 @@ class HorizontalPropertyGridWidget extends React.Component { this._dataProvider = new SamplePropertyDataProvider(); } - private _updatePropertyRecord(record: PropertyRecord, newValue: string): PropertyRecord { - const propertyValue: PropertyValue = { - valueFormat: PropertyValueFormat.Primitive, - value: newValue, - displayValue: newValue.toString(), - }; - return record.copyWithNewValue(propertyValue); + private _updatePropertyRecord(record: PropertyRecord, newValue: PropertyValue): PropertyRecord { + return record.copyWithNewValue(newValue); } private _handlePropertyUpdated = async (args: PropertyUpdatedArgs, category: PropertyCategory): Promise => { @@ -207,13 +199,8 @@ class HorizontalPropertyGridContent extends React.Component { this._dataProvider = new SamplePropertyDataProvider(); } - private _updatePropertyRecord(record: PropertyRecord, newValue: string): PropertyRecord { - const propertyValue: PropertyValue = { - valueFormat: PropertyValueFormat.Primitive, - value: newValue, - displayValue: newValue.toString(), - }; - return record.copyWithNewValue(propertyValue); + private _updatePropertyRecord(record: PropertyRecord, newValue: PropertyValue): PropertyRecord { + return record.copyWithNewValue(newValue); } private _handlePropertyUpdated = async (args: PropertyUpdatedArgs, category: PropertyCategory): Promise => { diff --git a/test-apps/ui-test-app/src/frontend/appui/widgets/UnifiedSelectionTableWidget.tsx b/test-apps/ui-test-app/src/frontend/appui/widgets/UnifiedSelectionTableWidget.tsx index e900678..e6bbe1e 100644 --- a/test-apps/ui-test-app/src/frontend/appui/widgets/UnifiedSelectionTableWidget.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/widgets/UnifiedSelectionTableWidget.tsx @@ -31,19 +31,41 @@ interface UnifiedSelectionTableWidgetProps { rulesetId?: string; } -class UnifiedSelectionTableWidget extends React.Component { +interface UnifiedSelectionTableWidgetState { + dataProvider: PresentationTableDataProvider; +} +class UnifiedSelectionTableWidget extends React.PureComponent { + constructor(props: UnifiedSelectionTableWidgetProps, context?: any) { + super(props, context); + this.state = { dataProvider: createDataProviderFromProps(props) }; + } + public static getDerivedStateFromProps(props: UnifiedSelectionTableWidgetProps, state: UnifiedSelectionTableWidgetState): UnifiedSelectionTableWidgetState | null { + const needsDataProviderRecreated = (props.iModelConnection !== state.dataProvider.imodel || props.rulesetId !== state.dataProvider.rulesetId); + if (needsDataProviderRecreated) + state.dataProvider = createDataProviderFromProps(props); + return state; + } + public componentWillUnmount() { + this.state.dataProvider.dispose(); + } + public componentDidUpdate(_prevProps: UnifiedSelectionTableWidgetProps, prevState: UnifiedSelectionTableWidgetState) { + if (this.state.dataProvider !== prevState.dataProvider) + prevState.dataProvider.dispose(); + } public render() { if (this.props.iModelConnection && this.props.rulesetId) { return (
    - +
    ); } - return null; } } +const createDataProviderFromProps = (props: UnifiedSelectionTableWidgetProps) => + new PresentationTableDataProvider({ imodel: props.iModelConnection!, ruleset: props.rulesetId! }); + ConfigurableUiManager.registerControl("UnifiedSelectionTableDemoWidget", UnifiedSelectionTableWidgetControl); diff --git a/test-apps/ui-test-app/src/frontend/appui/widgets/demodataproviders/demoTableDataProvider.ts b/test-apps/ui-test-app/src/frontend/appui/widgets/demodataproviders/demoTableDataProvider.ts index da123ec..f08c51e 100644 --- a/test-apps/ui-test-app/src/frontend/appui/widgets/demodataproviders/demoTableDataProvider.ts +++ b/test-apps/ui-test-app/src/frontend/appui/widgets/demodataproviders/demoTableDataProvider.ts @@ -3,10 +3,11 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import { SortDirection } from "@bentley/ui-core"; +import { PropertyValue, PropertyDescription, PropertyRecord, PropertyValueFormat } from "@bentley/imodeljs-frontend"; import { TableDataProvider, MutableTableDataProvider, RowItem, TableDataChangeEvent, ColumnDescription, - DropTargetArguments, DragSourceArguments, DropStatus, DropEffects, PropertyValue, PropertyDescription, PropertyRecord, PropertyValueFormat, DragSourceProps, DropTargetProps, + DropTargetArguments, DragSourceArguments, DropStatus, DropEffects, DragSourceProps, DropTargetProps, } from "@bentley/ui-components"; import { DemoDragDropObject } from "./demoTreeDataProvider"; diff --git a/test-apps/ui-test-app/src/frontend/appui/widgets/demodataproviders/demoTreeDataProvider.ts b/test-apps/ui-test-app/src/frontend/appui/widgets/demodataproviders/demoTreeDataProvider.ts index bca65b4..7bad94e 100644 --- a/test-apps/ui-test-app/src/frontend/appui/widgets/demodataproviders/demoTreeDataProvider.ts +++ b/test-apps/ui-test-app/src/frontend/appui/widgets/demodataproviders/demoTreeDataProvider.ts @@ -241,7 +241,7 @@ export const treeDragSourceBeginCallback = (args: DragSourceArguments - iModel.js UI Test App - + - + + + @@ -48,10 +42,6 @@ If you open it directly in the browser, you will see an empty page. You can add webfonts, meta tags, or analytics to this file. - The build step will place the bundled scripts into the tag. - - To begin the development, run `npm start` or `yarn start`. - To create a production bundle, use `npm run build` or `yarn build`. --> diff --git a/test-apps/ui-test-app/src/frontend/index.scss b/test-apps/ui-test-app/src/frontend/index.scss index bf2e579..3d98f08 100644 --- a/test-apps/ui-test-app/src/frontend/index.scss +++ b/test-apps/ui-test-app/src/frontend/index.scss @@ -1,12 +1,16 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ - -@import "@bentley/bwc/lib/mixins"; - -body { - margin: 0; - padding: 0; - font-family: $bwc-sans; -} +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +@import "@bentley/ui-core/lib/ui-core/index"; + +body { + margin: 0; + padding: 0; + font-family: $uicore-sans; +} + +.red-text { + color: red; +} diff --git a/test-apps/ui-test-app/src/frontend/index.tsx b/test-apps/ui-test-app/src/frontend/index.tsx index fb9643a..15e82de 100644 --- a/test-apps/ui-test-app/src/frontend/index.tsx +++ b/test-apps/ui-test-app/src/frontend/index.tsx @@ -24,9 +24,11 @@ import { OverallContent, AppNotificationManager, IModelInfo, + ProjectInfo, FrontstageManager, createAction, ActionsUnion, DeepReadonly, SyncUiEventDispatcher, } from "@bentley/ui-framework"; + import { Id64String } from "@bentley/bentleyjs-core"; import getSupportedRpcs from "../common/rpcs"; @@ -35,6 +37,8 @@ import AppBackstage, { BackstageShow, BackstageHide } from "./appui/AppBackstage import { ViewsFrontstage } from "./appui/frontstages/ViewsFrontstage"; import { Tool1 } from "./tools/Tool1"; import { Tool2 } from "./tools/Tool2"; +import { AppSelectTool } from "./tools/AppSelectTool"; +import { ToolWithSettings } from "./tools/ToolWithSettings"; // Mobx demo import { configure as mobxConfigure } from "mobx"; @@ -190,6 +194,9 @@ export class SampleAppIModelApp extends IModelApp { BackstageHide.register(this.sampleAppNamespace); Tool1.register(this.sampleAppNamespace); Tool2.register(this.sampleAppNamespace); + ToolWithSettings.register(this.sampleAppNamespace); + AppSelectTool.register(); + IModelApp.toolAdmin.defaultToolId = AppSelectTool.toolId; } public static async handleIModelViewsSelected(iModelInfo: IModelInfo, viewIdsSelected: Id64String[]) { @@ -245,17 +252,49 @@ SampleAppIModelApp.initialize().then(() => { // tslint:disable-line:no-floating- color: "red", marginLeft: "10px", }; + const applicationIcon = React.createElement(WebFontIcon, { iconName: "icon-construction-worker", style: applicationIconStyle }); + let defaultImodel: IModelInfo | undefined; + let viewId: string | undefined; + + if (Config.App.has("imjs_uitestapp_imodel_viewId")) + viewId = Config.App.get("imjs_uitestapp_imodel_viewId"); + + if (Config.App.has("imjs_uitestapp_imodel_name") && + Config.App.has("imjs_uitestapp_imodel_wsgId") && + Config.App.has("imjs_uitestapp_imodel_project_name") && + Config.App.has("imjs_uitestapp_imodel_project_projectNumber") && + Config.App.has("imjs_uitestapp_imodel_project_wsgId")) { + const defaultProject = { + name: Config.App.get("imjs_uitestapp_imodel_project_name"), + projectNumber: Config.App.get("imjs_uitestapp_imodel_project_projectNumber"), + wsgId: Config.App.get("imjs_uitestapp_imodel_project_wsgId"), + readStatus: 0, + } as ProjectInfo; + + defaultImodel = { + name: Config.App.get("imjs_uitestapp_imodel_name"), + description: Config.App.get("imjs_uitestapp_imodel_name"), + wsgId: Config.App.get("imjs_uitestapp_imodel_wsgId"), + projectInfo: defaultProject, + status: "", + } as IModelInfo; + } + const overallContentProps = { appHeaderIcon: applicationIcon, appHeaderMessage: SampleAppIModelApp.i18n.translate("SampleApp:Header.welcome"), appBackstage: , onIModelViewsSelected: SampleAppIModelApp.handleIModelViewsSelected, onWorkOffline: SampleAppIModelApp.handleWorkOffline, + initialIModels: defaultImodel ? [defaultImodel] : undefined, + initialViewIds: viewId ? [viewId] : undefined, }; - AppUi.initialize(); + // tslint:disable-next-line:no-console + console.log("Versions:", (window as any).iModelJsVersions); + ReactDOM.render( diff --git a/test-apps/ui-test-app/src/frontend/plugins/MeasurePoints.ts b/test-apps/ui-test-app/src/frontend/plugins/MeasurePoints.ts index 1e2c31b..c1c24d3 100644 --- a/test-apps/ui-test-app/src/frontend/plugins/MeasurePoints.ts +++ b/test-apps/ui-test-app/src/frontend/plugins/MeasurePoints.ts @@ -8,7 +8,7 @@ import { ColorDef } from "@bentley/imodeljs-common"; import { Point3d, XYAndZ, XAndY, Point2d } from "@bentley/geometry-core"; import { IModelApp, PrimitiveTool, ViewRect, Viewport, QuantityType, BeButtonEvent, - EventHandled, DynamicsContext, DecorateContext, CanvasDecoration, + EventHandled, DynamicsContext, DecorateContext, CanvasDecoration, Plugin, PluginAdmin, } from "@bentley/imodeljs-frontend"; class DistanceMarker implements CanvasDecoration { @@ -161,12 +161,33 @@ export class MeasurePointsTool extends PrimitiveTool { } } -// define and run the entry point // tslint:disable:no-console -function main() { - const measureNamespace: I18NNamespace = IModelApp.i18n.registerNamespace("MeasureTool"); - measureNamespace.readFinished.then(() => { MeasurePointsTool.register(measureNamespace); }) - .catch((err) => { console.log(err); }); +class MeasurePointsPlugin extends Plugin { + private _measureNamespace: I18NNamespace | undefined; + + public constructor(name: string, versionsRequired: string) { + super(name, versionsRequired); + this._measureNamespace = undefined; + } + + public onLoad(_args: string[]): void { + // don't register the namespace and the tool until the onLoad method. That's called after we know the versions of the modules required are good. + this._measureNamespace = IModelApp.i18n.registerNamespace("MeasureTool"); + this._measureNamespace.readFinished.then(() => { MeasurePointsTool.register(this._measureNamespace); }) + .catch((err) => { console.log(err); }); + } + + public onExecute(args: string[]): void { + this._measureNamespace!.readFinished.then(() => { + // restart the tool. + console.log("MeasurePoints onExecute called, args", args); + IModelApp.tools.run("Measure.Points"); + }) + .catch((err) => { console.log(err); }); + } } -main(); +declare var IMODELJS_VERSIONS_REQUIRED: string; +declare var PLUGIN_NAME: string; + +PluginAdmin.register(new MeasurePointsPlugin(PLUGIN_NAME, IMODELJS_VERSIONS_REQUIRED)); diff --git a/test-apps/ui-test-app/src/frontend/tools/AppSelectTool.ts b/test-apps/ui-test-app/src/frontend/tools/AppSelectTool.ts new file mode 100644 index 0000000..9e1afe2 --- /dev/null +++ b/test-apps/ui-test-app/src/frontend/tools/AppSelectTool.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module SelectionSet */ + +import { SelectionTool, IModelApp } from "@bentley/imodeljs-frontend"; + +/** Tool for picking a set of elements of interest, selected by the user. */ +export class AppSelectTool extends SelectionTool { + public static toolId = "AppSelect"; + public static get flyover(): string { return IModelApp.i18n.translate("SampleApp:tools.AppSelect.flyover"); } + public static get description(): string { return IModelApp.i18n.translate("SampleApp:tools.AppSelect.description"); } + protected wantToolSettings(): boolean { return true; } + protected wantSelectionScopeInToolSettings(): boolean { return true; } +} diff --git a/test-apps/ui-test-app/src/frontend/tools/ToolSpecifications.tsx b/test-apps/ui-test-app/src/frontend/tools/ToolSpecifications.tsx index 31528cc..cd6af16 100644 --- a/test-apps/ui-test-app/src/frontend/tools/ToolSpecifications.tsx +++ b/test-apps/ui-test-app/src/frontend/tools/ToolSpecifications.tsx @@ -15,17 +15,29 @@ import { import { SampleAppIModelApp, RootState, SampleAppUiActionId } from "../"; import { Tool1 } from "../tools/Tool1"; import { Tool2 } from "../tools/Tool2"; +import { ToolWithSettings } from "../tools/ToolWithSettings"; +import { AppSelectTool } from "../tools/AppSelectTool"; // cSpell:ignore appui import { TestMessageBox } from "../appui/dialogs/TestMessageBox"; import { AppUi } from "../appui/AppUi"; export class AppTools { + public static get appSelectElementCommand() { + return new ToolItemDef({ + toolId: AppSelectTool.toolId, + iconSpec: "icon-cursor", + labelKey: "SampleApp:tools.AppSelect.flyover", + tooltipKey: "SampleApp:tools.AppSelect.description", + execute: () => { IModelApp.tools.run(AppSelectTool.toolId); }, + }); + } + public static get tool1() { return new ToolItemDef({ toolId: Tool1.toolId, iconSpec: "icon-placeholder", - labelKey: "SampleApp:tools.Tool1.flyover", - tooltipKey: "SampleApp:tools.Tool1.description", + label: () => Tool1.flyover, + tooltip: () => Tool1.description, execute: () => { IModelApp.tools.run(Tool1.toolId); }, }); } @@ -40,6 +52,16 @@ export class AppTools { }); } + public static get toolWithSettings() { + return new ToolItemDef({ + toolId: ToolWithSettings.toolId, + iconSpec: "icon-placeholder", + labelKey: "SampleApp:tools.ToolWithSettings.flyover", + tooltipKey: "SampleApp:tools.ToolWithSettings.description", + execute: () => { IModelApp.tools.run(ToolWithSettings.toolId); }, + }); + } + /* ------------- NEEDSWORK - figure out how to move to plugin. public static get measurePoints() { return new ToolItemDef({ @@ -206,12 +228,14 @@ export class AppTools { }); } + private static _detailedMessage = "This is a detailed message with a line
    break and bold, italic and red text."; + public static get infoMessageCommand() { return new CommandItemDef({ commandId: "infoMessage", iconSpec: "icon-info", labelKey: "SampleApp:buttons.informationMessageBox", - execute: () => IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Info, "This is an info message")), + execute: () => IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Info, "This is an info message", this._detailedMessage)), }); } @@ -220,7 +244,7 @@ export class AppTools { commandId: "warningMessage", iconSpec: "icon-status-warning", labelKey: "SampleApp:buttons.warningMessageBox", - execute: () => IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Warning, "This is a warning message", undefined, OutputMessageType.Sticky)), + execute: () => IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Warning, "This is a warning message", this._detailedMessage, OutputMessageType.Sticky)), }); } @@ -229,7 +253,10 @@ export class AppTools { commandId: "errorMessage", iconSpec: "icon-status-rejected", labelKey: "SampleApp:buttons.errorMessageBox", - execute: () => IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, "This is an error message", undefined, OutputMessageType.Alert)), + execute: () => IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, "This is an error message", + "1. " + this._detailedMessage + "
    " + + "2. " + this._detailedMessage + "
    " + + "For more details, Google it!", OutputMessageType.Alert)), }); } @@ -259,7 +286,7 @@ export class AppTools { }); } - private static _detailMsg = "This is a description of the alert with lots and lots of words that explains what the user did & what they can do to remedy the situation."; //
    Hello Google! + private static _detailMsg = "This is a description of the alert with lots and lots of words that explains what the user did & what they can do to remedy the situation.
    For more info, Google it!"; public static get warningMessageStickyCommand() { return new CommandItemDef({ commandId: "warningMessage", diff --git a/test-apps/ui-test-app/src/frontend/tools/ToolWithSettings.ts b/test-apps/ui-test-app/src/frontend/tools/ToolWithSettings.ts new file mode 100644 index 0000000..026d97e --- /dev/null +++ b/test-apps/ui-test-app/src/frontend/tools/ToolWithSettings.ts @@ -0,0 +1,296 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import { + IModelApp, PrimitiveTool, + BeButtonEvent, EventHandled, + ToolSettingsPropertyRecord, PropertyDescription, PrimitiveValue, ToolSettingsValue, ToolSettingsPropertySyncItem, + NotifyMessageDetails, OutputMessagePriority, PropertyEditorParamTypes, +} from "@bentley/imodeljs-frontend"; + +import { Point3d } from "@bentley/geometry-core"; + +const enum ToolOptions { + Red, + White, + Blue, + Yellow, +} + +export class ToolWithSettings extends PrimitiveTool { + public static toolId = "ToolWithSettings"; + public readonly points: Point3d[] = []; + private _showCoordinatesOnPointerMove = false; + + private toggleCoordinateUpdate() { + this._showCoordinatesOnPointerMove = !this._showCoordinatesOnPointerMove; + } + + // Tool Setting Properties + // ------------- Enum based picklist --------------- + private static enumAsPicklistMessage(str: string) { return IModelApp.i18n.translate("SampleApp:tools.ToolWithSettings.Options." + str); } + private static _optionsName = "enumAsPicklist"; + private static _getEnumAsPicklistDescription = (): PropertyDescription => { + return { + name: ToolWithSettings._optionsName, + displayLabel: IModelApp.i18n.translate("SampleApp:tools.ToolWithSettings.Prompts.Options"), + typename: "enum", + enum: { + choices: [ + { label: ToolWithSettings.enumAsPicklistMessage("Red"), value: ToolOptions.Red }, + { label: ToolWithSettings.enumAsPicklistMessage("White"), value: ToolOptions.White }, + { label: ToolWithSettings.enumAsPicklistMessage("Blue"), value: ToolOptions.Blue }, + { label: ToolWithSettings.enumAsPicklistMessage("Yellow"), value: ToolOptions.Yellow }, + ], + }, + }; + } + + private _optionsValue = new ToolSettingsValue(ToolOptions.Blue); + + public get option(): ToolOptions { + return this._optionsValue.value as ToolOptions; + } + + public set option(option: ToolOptions) { + this._optionsValue.value = option; + } + + // ------------- boolean based toggle button --------------- + private static _lockToggleName = "lockToggle"; + private static _getLockToggleDescription = (): PropertyDescription => { + return { + name: ToolWithSettings._lockToggleName, + displayLabel: IModelApp.i18n.translate("SampleApp:tools.ToolWithSettings.Prompts.Lock"), + typename: "boolean", + editor: { name: "toggle" }, + }; + } + + private _lockValue = new ToolSettingsValue(true); + + public get lock(): boolean { + return this._lockValue.value as boolean; + } + + public set lock(option: boolean) { + this._lockValue.value = option; + } + + // ------------- text based edit field --------------- + private static _cityName = "city"; + private static _getCityDescription = (): PropertyDescription => { + return { + name: ToolWithSettings._cityName, + displayLabel: IModelApp.i18n.translate("SampleApp:tools.ToolWithSettings.Prompts.City"), + typename: "string", + }; + } + + private _cityValue = new ToolSettingsValue("Exton"); + + public get city(): string { + return this._cityValue.value as string; + } + + public set city(option: string) { + this._cityValue.value = option; + } + + // ------------- text based edit field --------------- + private static _stateName = "state"; + private static _getStateDescription = (): PropertyDescription => { + return { + name: ToolWithSettings._stateName, + displayLabel: IModelApp.i18n.translate("SampleApp:tools.ToolWithSettings.Prompts.State"), + typename: "string", + editor: { + params: [ + { + type: PropertyEditorParamTypes.InputEditorSize, + size: 4, + /* maxLength: 60,*/ + }, + ], + }, + }; + } + + private _stateValue = new ToolSettingsValue("PA"); + + public get state(): string { + return this._stateValue.value as string; + } + + public set state(option: string) { + this._stateValue.value = option; + } + + // ------------- text based edit field --------------- + private static _coordinateName = "coordinate"; + private static _getCoordinateDescription = (): PropertyDescription => { + return { + name: ToolWithSettings._coordinateName, + displayLabel: IModelApp.i18n.translate("SampleApp:tools.ToolWithSettings.Prompts.Coordinate"), + typename: "string", + }; + } + + private _coordinateValue = new ToolSettingsValue("0.0, 0.0, 0.0"); + + public get coordinate(): string { + return this._coordinateValue.value as string; + } + + public set coordinate(option: string) { + this._coordinateValue.value = option; + } + + // ------------- use length toggle --------------- + private static _useLengthName = "useLength"; + private static _getUseLengthDescription = (): PropertyDescription => { + return { + name: ToolWithSettings._useLengthName, + displayLabel: "", + typename: "boolean", + editor: { + params: [ + { + type: PropertyEditorParamTypes.SuppressEditorLabel, + suppressLabelPlaceholder: true, + }, + ], + }, + }; + } + + private _useLengthValue = new ToolSettingsValue(true); + + public get useLength(): boolean { + return this._useLengthValue.value as boolean; + } + + public set useLength(option: boolean) { + this._useLengthValue.value = option; + } + + // ------------- text based edit field (TODO: make quantity field) --------------- + private static _lengthName = "length"; + private static _getLengthDescription = (): PropertyDescription => { + return { + name: ToolWithSettings._lengthName, + displayLabel: IModelApp.i18n.translate("SampleApp:tools.ToolWithSettings.Prompts.Length"), + typename: "string", + }; + } + + private _lengthValue = new ToolSettingsValue("0.0"); + + public get length(): string { + return this._lengthValue.value as string; + } + + public set length(option: string) { + this._lengthValue.value = option; + } + + // -------- end of ToolSettings ---------- + + public requireWriteableTarget(): boolean { return false; } + public onPostInstall() { super.onPostInstall(); this.setupAndPromptForNextAction(); } + + public setupAndPromptForNextAction(): void { + IModelApp.notifications.outputPromptByKey("SampleApp:tools.ToolWithSettings.Prompts.GetPoint"); + } + + public async onDataButtonDown(ev: BeButtonEvent): Promise { + this.points.push(ev.point.clone()); + this.toggleCoordinateUpdate(); + this.setupAndPromptForNextAction(); + return EventHandled.No; + } + + public async onResetButtonUp(_ev: BeButtonEvent): Promise { + IModelApp.toolAdmin.startDefaultTool(); + return EventHandled.No; + } + + private syncCoordinateValue(coordinate: string): void { + const coordinateValue = new ToolSettingsValue(coordinate); + // clone coordinateValue if storing value within tool - in this case we are not + const syncItem: ToolSettingsPropertySyncItem = { value: coordinateValue, propertyName: ToolWithSettings._coordinateName }; + this.syncToolSettingsProperties([syncItem]); + } + + public async onMouseMotion(ev: BeButtonEvent): Promise { + if (!this._showCoordinatesOnPointerMove) + return; + + const point = ev.point.clone(); + const formattedString: string = `${point.x.toFixed(2)}, ${point.y.toFixed(2)}, ${point.z.toFixed(2)}`; + this.syncCoordinateValue(formattedString); + } + + public onRestartTool(): void { + const tool = new ToolWithSettings(); + if (!tool.run()) + this.exitTool(); + } + + /** Used to supply DefaultToolSettingProvider with a list of properties to use to generate ToolSettings. If undefined then no ToolSettings will be displayed */ + public supplyToolSettingsProperties(): ToolSettingsPropertyRecord[] | undefined { + const readonly = true; + const toolSettings = new Array(); + toolSettings.push(new ToolSettingsPropertyRecord(this._optionsValue.clone() as PrimitiveValue, ToolWithSettings._getEnumAsPicklistDescription(), { rowPriority: 0, columnIndex: 2 })); + toolSettings.push(new ToolSettingsPropertyRecord(this._lockValue.clone() as PrimitiveValue, ToolWithSettings._getLockToggleDescription(), { rowPriority: 5, columnIndex: 2 })); + toolSettings.push(new ToolSettingsPropertyRecord(this._cityValue.clone() as PrimitiveValue, ToolWithSettings._getCityDescription(), { rowPriority: 10, columnIndex: 2 })); + toolSettings.push(new ToolSettingsPropertyRecord(this._stateValue.clone() as PrimitiveValue, ToolWithSettings._getStateDescription(), { rowPriority: 10, columnIndex: 4 })); + toolSettings.push(new ToolSettingsPropertyRecord(this._coordinateValue.clone() as PrimitiveValue, ToolWithSettings._getCoordinateDescription(), { rowPriority: 15, columnIndex: 2, columnSpan: 3 }, readonly)); + toolSettings.push(new ToolSettingsPropertyRecord(this._useLengthValue.clone() as PrimitiveValue, ToolWithSettings._getUseLengthDescription(), { rowPriority: 20, columnIndex: 0 })); + toolSettings.push(new ToolSettingsPropertyRecord(this._lengthValue.clone() as PrimitiveValue, ToolWithSettings._getLengthDescription(), { rowPriority: 20, columnIndex: 2 })); + return toolSettings; + } + + private showInfoFromUi(updatedValue: ToolSettingsPropertySyncItem) { + const msg = `Property '${updatedValue.propertyName}' updated to value ${updatedValue.value.value}`; + IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Info, msg)); + } + + private syncLengthState(): void { + const lengthValue = new ToolSettingsValue(this.length); + const syncItem: ToolSettingsPropertySyncItem = { value: lengthValue, propertyName: ToolWithSettings._lengthName, isDisabled: !this.useLength }; + this.syncToolSettingsProperties([syncItem]); + } + + /** Used to send changes from UI back to Tool */ + public applyToolSettingPropertyChange(updatedValue: ToolSettingsPropertySyncItem): boolean { + if (updatedValue.propertyName === ToolWithSettings._optionsName) { + if (this._optionsValue.value !== updatedValue.value.value) { + this.option = updatedValue.value.value as ToolOptions; + this.showInfoFromUi(updatedValue); + } + } else if (updatedValue.propertyName === ToolWithSettings._lockToggleName) { + this.lock = updatedValue.value.value as boolean; + this.showInfoFromUi(updatedValue); + } else if (updatedValue.propertyName === ToolWithSettings._cityName) { + this.city = updatedValue.value.value as string; + this.showInfoFromUi(updatedValue); + } else if (updatedValue.propertyName === ToolWithSettings._stateName) { + this.state = updatedValue.value.value as string; + this.showInfoFromUi(updatedValue); + } else if (updatedValue.propertyName === ToolWithSettings._useLengthName) { + this.useLength = updatedValue.value.value as boolean; + this.showInfoFromUi(updatedValue); + this.syncLengthState(); + } else if (updatedValue.propertyName === ToolWithSettings._lengthName) { + this.length = updatedValue.value.value as string; + this.showInfoFromUi(updatedValue); + } + + // return true is change is valid + return true; + } + +} diff --git a/test-apps/ui-test-app/tsconfig.json b/test-apps/ui-test-app/tsconfig.json index 7915a7f..521c56e 100644 --- a/test-apps/ui-test-app/tsconfig.json +++ b/test-apps/ui-test-app/tsconfig.json @@ -1,6 +1,8 @@ { "extends": "./node_modules/@bentley/build-tools/tsconfig-base.json", "compilerOptions": { + "declaration": false, + "declarationMap": false, "skipLibCheck": true, "outDir": "./lib", "jsx": "react", diff --git a/test-apps/webworker-test-app/assets/galvanized03.jpg b/test-apps/webworker-test-app/assets/galvanized03.jpg new file mode 100644 index 0000000..9a94cda Binary files /dev/null and b/test-apps/webworker-test-app/assets/galvanized03.jpg differ diff --git a/test-apps/webworker-test-app/package.json b/test-apps/webworker-test-app/package.json new file mode 100644 index 0000000..3cbc502 --- /dev/null +++ b/test-apps/webworker-test-app/package.json @@ -0,0 +1,61 @@ +{ + "name": "webworker-test-app", + "description": "iModel.js Webworker Test Application", + "private": true, + "license": "MIT", + "version": "0.0.0", + "scripts": { + "build": "node ./node_modules/@bentley/webpack-tools/bin/buildIModelJsModule", + "lint": "tslint -p . 1>&2", + "clean": "rimraf lib dist package-deps.json", + "test": "", + "cover": "", + "docs": "" + }, + "iModelJs": { + "buildModule": { + "type": "webworker", + "tscOptions": "-build", + "webpack": { + "dest": "./lib/webresources", + "entry": "./lib/webworker/webWorkerMain.js", + "bundleName": "testWebWorker" + }, + "subModules": [ + { + "dest": "./lib/webresources", + "entry": "./lib/plugin/startWebWorker.js", + "bundleName": "startWebWorkerPlugin", + "type": "plugin" + } + ] + } + }, + "repository": {}, + "keywords": [ + "Bentley", + "BIM", + "iModel" + ], + "author": { + "name": "Bentley Systems, Inc.", + "url": "http://www.bentley.com" + }, + "//devDependencies": [ + "NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install", + "NOTE: All tools used by scripts in this package must be listed as devDependencies" + ], + "devDependencies": { + "@bentley/webpack-tools": "0.189.0", + "@bentley/build-tools": "0.189.0", + "rimraf": "^2.6.2", + "tsconfig-paths": "^3.3.2", + "tslint": "^5.11.0", + "typescript": "~3.2.2" + }, + "dependencies": { + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/imodeljs-frontend": "0.189.0" + } +} diff --git a/test-apps/webworker-test-app/src/plugin/startWebWorker.ts b/test-apps/webworker-test-app/src/plugin/startWebWorker.ts new file mode 100644 index 0000000..651c1b0 --- /dev/null +++ b/test-apps/webworker-test-app/src/plugin/startWebWorker.ts @@ -0,0 +1,229 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +/* ------------------------ + * This is an example of a Plugin that starts a webworker. + * This particular example supports several different operations that the WebWorker can perform. + * Each request returns a Promise that is fulfilled when the WebWorker responds. + * Multiple requests can be made. They are processed serially in the order they are received. + * A more sophisticated example could set up multiple WebWorkers and distribute the requests. + * ------------------------ */ +// tslint:disable:no-console + +import { Plugin, PluginAdmin } from "@bentley/imodeljs-frontend"; + +type resolveFunc = ((arg: any) => void); +type rejectFunc = ((arg: Error) => void); + +class RequestMessage { + constructor(public msgId: number, public operation: string, public operands: any[]) { } +} + +// Subclass WorkerOperation for different request types. +// Instantiate the subclass and then call the sendMessage method. +abstract class WorkerOperation { + private _resolve: resolveFunc | undefined = undefined; + private _reject: rejectFunc | undefined = undefined; + public msgId: number; + + constructor(private _worker: IMJsWorker, private _operation: string, private _operands: any[]) { + this.msgId = _worker.nextMsgId; + } + + // This is the executor method that is called immediately when you instantiate a Promise. + // Here, we store the resolve and reject functions for use when we handle the message from the worker (see handleMessage). + public executor(resolve: resolveFunc, reject: rejectFunc) { + // save the resolve and reject functions to dispatch when we get reply back from the web worker. + this._resolve = resolve; + this._reject = reject; + const message = new RequestMessage(this.msgId, this._operation, this._operands); + + // start the operation in the web worker. + this._worker.webWorker.postMessage(message); + } + + // This method puts together the request, sends it, and returns the Promise. + public async sendMessage(): Promise { + return new Promise(this.executor.bind(this)); + } + + // Called when the correct message is returned from the Web Worker. + // This should be called only from the handleMessage method of IMJsWorker + public doResolve(event: MessageEvent): void { + // the return number is in event.data.result. + this._resolve!(event.data.result); + } + + // This should be called only from the handleMessage method of IMJsWorker. + public doReject(errorEvent: ErrorEvent): void { + this._reject!(new Error(`Error ${errorEvent.message} at line number ${errorEvent.lineno} of file ${errorEvent.filename}, in the webworker thread`)); + } +} + +// Operation that adds two numbers. +class AddOperation extends WorkerOperation { + // store the operands. + constructor(worker: IMJsWorker, operand1: number, operand2: number) { + super(worker, "add", [operand1, operand2]); + } +} + +// Oeration that returns the factorial of a number +class FactorialOperation extends WorkerOperation { + // store the operand + constructor(worker: IMJsWorker, _operand: number) { + super(worker, "factorial", [_operand]); + } +} + +class UrlToImageBitmapOperation extends WorkerOperation { + constructor(worker: IMJsWorker, _url: string) { + super(worker, "urlToImageBitmap", [_url]); + } +} + +/* ---------------- ifdef draco ---------------------- +class DracoUrlDecodeOperation extends WorkerOperation { + constructor(worker: IMJsWorker, _url: string) { + super(worker, "dracoDecode", [_url]); + } +} + ----------------------------------------------- */ + +// Class that supervises the webWorker. +class IMJsWorker { + public webWorker: Worker; + private _queue: Map; + private _nextMsgId: number; + + public constructor(workerFile: string) { + this.webWorker = new Worker(workerFile); + this.webWorker.onmessage = this.handleMessage.bind(this); + this.webWorker.onerror = this.handleError.bind(this); + this._queue = new Map(); + this._nextMsgId = 1; + } + + // this is the method that gets responses from the webWorker. + private handleMessage(event: MessageEvent) { + const msgId: number = event.data.msgId; + const wo = this._queue.get(msgId); + if (wo) { + wo.doResolve(event); + this._queue.delete(msgId); + } + } + + // this is the method that gets errors from the webworker. + // Try to get the msgId out of the error we got back. That doesn't always work, because + // sometimes we don't throw the error if it happens while the worker is loading. In that case + // we find the message with the lowest msgId, since that's the order we're processing them. + private handleError(error: ErrorEvent) { + let errorMsgId = 0; + let rejectError = error; + if (error.hasOwnProperty("msgId") && error.hasOwnProperty("originalError")) { + errorMsgId = (error as any).msgId; + rejectError = (error as any).originalError; + } else { + // find the lowest number msgId. + errorMsgId = Number.MAX_SAFE_INTEGER; + for (const msgId of this._queue.keys()) { + errorMsgId = Math.min(msgId, errorMsgId); + } + } + // reject the promise and remove the message from the queue. + const wo = this._queue.get(errorMsgId); + if (wo) { + wo.doReject(rejectError); + this._queue.delete(errorMsgId); + } + } + + private async queueOperation(wo: WorkerOperation): Promise { + this._queue.set(wo.msgId, wo); + return wo.sendMessage(); + } + + // returns the nextMsgId. Used only from the WorkerOperation class + public get nextMsgId(): number { + return this._nextMsgId++; + } + + // queue the add operation + public async queueAddOperation(operand1: number, operand2: number): Promise { + const ao = new AddOperation(this, operand1, operand2); + return this.queueOperation(ao); + } + + // queue the factorial operation + public async queueFactorialOperation(operand: number): Promise { + const fo = new FactorialOperation(this, operand); + return this.queueOperation(fo); + } + + public async queueUrlToBitmapOperation(url: string): Promise { + const uToBo = new UrlToImageBitmapOperation(this, url); + return this.queueOperation(uToBo); + } + + /* ---------------- ifdef draco ---------------------- + public queueDracoUrlDecodeOperation(url: string): Promise { + const ddo = new DracoUrlDecodeOperation(this, url); + return this.queueOperation(ddo); + } + * ----------------------------------------------- */ +} + +// The Plugin class that starts the web worker and sends it work each time it is invoked. +class StartWebWorker extends Plugin { + private _testWorker: IMJsWorker | undefined; + + constructor(name: string, versionsRequired: string) { + super(name, versionsRequired); + } + + public async onExecute(_args: string[]) { + // here we are sending tasks to the webworker. + // make one request + try { + const result: number = await this._testWorker!.queueAddOperation(3, 4); + console.log("add result is ", result); + } catch (error) { console.log(error); } + + try { + const imageBitmap: ImageBitmap = await this._testWorker!.queueUrlToBitmapOperation("galvanized03.jpg"); + console.log("image bitmap is", imageBitmap); + } catch (error) { console.log(error); } + + /* ---------------- ifdef draco ---------------------- + try { + const dracoResponse: any = await this._testWorker!.queueDracoUrlDecodeOperation("bunny.drc"); + console.log("draco response is", dracoResponse); + } catch (error) { console.log(error); } + ----------------------------------------------- */ + + // make three requests. They are worked on serially by the webworker. A more sophisticated example could queue them up an use multiple workers. + try { + const results = await Promise.all([this._testWorker!.queueAddOperation(9, 10), this._testWorker!.queueAddOperation(7, 8), this._testWorker!.queueUrlToBitmapOperation("accudraw.png")]); + let resultNum = 1; + for (const result of results) { + console.log("result number ", resultNum++, "is", result); + } + } catch (error) { console.log(error); } + } + + public async onLoad(_args: string[]) { + // This is where we start the webworker. The requests to it are made in the onExecute method. + if (!(window as any).Worker) + return; + this._testWorker = new IMJsWorker("testWebWorker.js"); + } +} + +// boilerplate plugin code that is executed when the module is loaded. +declare var IMODELJS_VERSIONS_REQUIRED: string; +declare var PLUGIN_NAME: string; +export const startWebWorker = new StartWebWorker(PLUGIN_NAME, IMODELJS_VERSIONS_REQUIRED); +PluginAdmin.register(startWebWorker); diff --git a/ui/core/src/ui-core/base/WaitSpinner.scss b/test-apps/webworker-test-app/src/webworker/draco3d.d.ts similarity index 62% rename from ui/core/src/ui-core/base/WaitSpinner.scss rename to test-apps/webworker-test-app/src/webworker/draco3d.d.ts index 0ce70c5..5be989e 100644 --- a/ui/core/src/ui-core/base/WaitSpinner.scss +++ b/test-apps/webworker-test-app/src/webworker/draco3d.d.ts @@ -1,15 +1,10 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ -@import "@bentley/bwc/lib/mixins"; - -.bui-waitspinner-div { - background-color: #eef0f3; - text-align: center; - line-height: 100px; - } - - .bui-waitspinner-loader { - @include bwc-loaders-medium; - } +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +// not currently used - uncomment draco stuff in startWebWorker.ts and webWorkerMain.ts +declare module 'draco3d' { + export function createDecoderModule(DracoDecoderModule: any, ...args: any[]): any; +} + diff --git a/test-apps/webworker-test-app/src/webworker/tsconfig.json b/test-apps/webworker-test-app/src/webworker/tsconfig.json new file mode 100644 index 0000000..12110c2 --- /dev/null +++ b/test-apps/webworker-test-app/src/webworker/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../node_modules/@bentley/build-tools/tsconfig-base.json", + "compilerOptions": { + "skipLibCheck": true, + "outDir": "../../lib/webworker", + "composite": true, + "lib": [ + "es2017", + "webworker" + ], + }, + "include": [ + "./**/*.ts", + ] +} \ No newline at end of file diff --git a/test-apps/webworker-test-app/src/webworker/webWorkerMain.ts b/test-apps/webworker-test-app/src/webworker/webWorkerMain.ts new file mode 100644 index 0000000..1cc520f --- /dev/null +++ b/test-apps/webworker-test-app/src/webworker/webWorkerMain.ts @@ -0,0 +1,115 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +/* ---------------- ifdef draco ---------------------- + * import * as draco3d from "draco3d"; + * Notes on draco: + * I have commented out the draco stuff. If you uncomment it and add a dependency on draco3d, it will work. + * importing draco3d (as is commented off above) unfortunately webpacks in both the encoder and decoder, + * adding 1.8 mbyte to the webpack. I tried several strategies for using only the encoder: + * 1. Use importScripts rather than import so it isn't put in the webpack. I wanted to importScripts + * and then look at module.exports to get the CreateDracoModule function, but I couldn't figure out + * how to stop it from giving me referenceErrors on module. + * 2. Try to import "draco3d/draco_decoder_nodejs" rather than draco. I couldn't get that to work either. + * It defined module.exports as a function rather than an object. Not sure whether that's the problem, + * but couldn't get it to work. + * ----------------------------------------------- */ +// reply sent through PostMessage +class WorkerReply { + constructor(public msgId: number, public result: any) { } +} + +// error thrown when we detect or catch an error. +class WorkerError extends Error { + constructor(public msgId: number, public originalError: Error) { + super("Worker Error"); + } +} + +class TestWebWorker { + public static add(a: number, b: number): Promise { + return Promise.resolve(a + b); + } + + public static factorial(a: number): Promise { + if (a <= 1) + Promise.resolve(1); + let result: number = 1; + for (; a > 1; a--) { + result = result * a; + } + return Promise.resolve(result); + } + + public static async urlToImageBitmap(url: string): Promise { + // first we have to fetch the file. + const response: Response = await fetch(url, { mode: "cors" }); + const blob = await response.blob(); + const imageBitMap = await createImageBitmap(blob); + return { result: imageBitMap, transferable: true }; + } + + /* ---------------- ifdef draco ---------------------- + public static async dracoDecode(url: string): Promise { + const response: Response = await fetch(url, { mode: "cors" }); + const rawBuffer: ArrayBuffer = await response.arrayBuffer(); + const decoderModule: any = draco3d.createDecoderModule({}); + const decoder: any = new decoderModule.Decoder(); + + const dracoBuffer = new decoderModule.DecoderBuffer(); + dracoBuffer.Init(new Int8Array(rawBuffer), rawBuffer.byteLength); + const geometryType = decoder.GetEncodedGeometryType(dracoBuffer); + + let dracoGeometry: any; + if (geometryType === decoderModule.TRIANGULAR_MESH) { + dracoGeometry = new decoderModule.Mesh(); + decoder.DecodeBufferToMesh(dracoBuffer, dracoGeometry); + } else if (geometryType === decoderModule.POINT_CLOUD) { + dracoGeometry = new decoderModule.PointCloud(); + decoder.DecodeBufferToPointCloud(dracoBuffer, dracoGeometry); + } else { + throw new Error("Draco Error: Unknown geometry type."); + } + // dispose of draco variables. + decoderModule.destroy(dracoBuffer); + decoderModule.destroy(decoder); + + // after copying the data back to the main thread, destroy it in the callback. + return { result: dracoGeometry, transferable: false, callback: () => { decoderModule.destroy(dracoGeometry); } }; + } + * ----------------------------------------------- */ + public static messageHandler(event: MessageEvent) { + if (!event.data.hasOwnProperty("msgId")) { + throw new WorkerError(0, new Error("Improperly formatted message: msgId property needed")); + } + if (!event.data.hasOwnProperty("operation")) { + throw new WorkerError(event.data.msgId, new Error("Improperly formatted message: operation property needed")); + } + if (!event.data.hasOwnProperty("operands")) { + throw new WorkerError(event.data.msgId, new Error("Improperly formatted message: operands array property needed")); + } + const operation = event.data.operation; + const operands = event.data.operands; + const response = (TestWebWorker as any)[operation].apply(TestWebWorker, operands); + response.then((result: any) => { + // We got an object back from the operation. + if (typeof result === "object") { + const reply: WorkerReply = new WorkerReply(event.data.msgId, result.result); + postMessage(reply, result.transferable ? [reply.result] : undefined); + if (result.callback) + result.callback(); + } + else { + const reply: WorkerReply = new WorkerReply(event.data.msgId, result); + postMessage(reply); + } + }).catch((error: Error) => { + // the setTimeout here came from a search of trying to send to the onerror: https://stackoverflow.com/questions/39992417/how-to-bubble-a-web-worker-error-in-a-promise-via-worker-onerror + setTimeout(() => { throw new WorkerError(event.data.msgId, error); }); + }); + } +} + +onmessage = TestWebWorker.messageHandler; diff --git a/test-apps/webworker-test-app/tsconfig.json b/test-apps/webworker-test-app/tsconfig.json new file mode 100644 index 0000000..6f7391c --- /dev/null +++ b/test-apps/webworker-test-app/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "./node_modules/@bentley/build-tools/tsconfig-base.json", + "compilerOptions": { + "outDir": "./lib", + "rootDir": "./src" + }, + "include": [ + "./**/*.ts", + ], + "exclude": [ + "./src/webworker/**/*", + "./lib/." + ], + "references": [ + { + "path": "./src/webworker" + } + ] +} diff --git a/test-apps/webworker-test-app/tslint.json b/test-apps/webworker-test-app/tslint.json new file mode 100644 index 0000000..66909b8 --- /dev/null +++ b/test-apps/webworker-test-app/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": "@bentley/build-tools/tslint.json" +} \ No newline at end of file diff --git a/tools/build/CHANGELOG.json b/tools/build/CHANGELOG.json index d42208b..58039a5 100644 --- a/tools/build/CHANGELOG.json +++ b/tools/build/CHANGELOG.json @@ -1,6 +1,45 @@ { "name": "@bentley/build-tools", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/build-tools_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "Add new lint rule that checks that all method names are camel cased." + }, + { + "comment": "Changes package.json to include api-extractor and adds api-extractor.json" + }, + { + "comment": "Changes extract-api script to remove tsdoc-metadata.json files" + }, + { + "comment": "Changes error hadling in extract-api" + }, + { + "comment": "Use new buildIModelJsBuild script" + }, + { + "comment": "Remove uneeded typedoc plugin depedency" + }, + { + "comment": "Changed stripInternal to false so @internal-marked symbols still put in .d.ts files" + }, + { + "comment": "upgrade to TypeScript 3.2.2" + } + ] + } + }, + { + "version": "0.188.0", + "tag": "@bentley/build-tools_v0.188.0", + "date": "Wed, 16 Jan 2019 16:36:09 GMT", + "comments": {} + }, { "version": "0.187.0", "tag": "@bentley/build-tools_v0.187.0", diff --git a/tools/build/CHANGELOG.md b/tools/build/CHANGELOG.md index 4f4f2c8..a6939ab 100644 --- a/tools/build/CHANGELOG.md +++ b/tools/build/CHANGELOG.md @@ -1,6 +1,25 @@ # Change Log - @bentley/build-tools -This log was last generated on Tue, 15 Jan 2019 15:18:59 GMT and should not be manually modified. +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- Add new lint rule that checks that all method names are camel cased. +- Changes package.json to include api-extractor and adds api-extractor.json +- Changes extract-api script to remove tsdoc-metadata.json files +- Changes error hadling in extract-api +- Use new buildIModelJsBuild script +- Remove uneeded typedoc plugin depedency +- Changed stripInternal to false so @internal-marked symbols still put in .d.ts files +- upgrade to TypeScript 3.2.2 + +## 0.188.0 +Wed, 16 Jan 2019 16:36:09 GMT + +*Version update only* ## 0.187.0 Tue, 15 Jan 2019 15:18:59 GMT diff --git a/tools/build/package.json b/tools/build/package.json index fce7da2..d4597fd 100644 --- a/tools/build/package.json +++ b/tools/build/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/build-tools", - "version": "0.187.0", + "version": "0.189.0", "description": "Bentley build tools", "license": "MIT", "repository": { @@ -9,7 +9,7 @@ }, "scripts": { "copy:rules": "cpx \"./node_modules/tslint-consistent-codestyle/rules/**/*\" ./tslint-rules/tslint-consistent-codestyle/rules && cpx \"./node_modules/tslint-consistent-codestyle/src/**/*\" ./tslint-rules/tslint-consistent-codestyle/src", - "build": "tsc 1>&2 && npm run copy:rules", + "build": "tsc 1>&2 && npm run copy:rules && node ./scripts/ignoreargs.js 1>&2", "clean": "rimraf tslint-rules ../../modules", "docs": "", "lint": "", @@ -26,6 +26,7 @@ "url": "http://www.bentley.com" }, "dependencies": { + "@microsoft/api-extractor": "^6.3.0", "cache-require-paths": "^0.3.0", "chai": "^4.1.2", "chalk": "^2.4.1", @@ -59,7 +60,9 @@ "tsutils": "^3.6.0", "tslint-consistent-codestyle": "^1.11.0", "typedoc": "^0.11.1", - "typescript": "~3.1.0", + "typedoc-plugin-external-module-name": "1.1.1", + "typedoc-plugin-internal-external": "1.0.10", + "typescript": "~3.2.2", "uglifyjs-webpack-plugin": "^1.2.5", "url-loader": "^1.0.1", "webpack-node-externals": "^1.7.2", diff --git a/tools/build/scripts/docs.js b/tools/build/scripts/docs.js index ffae7a1..0e13cf0 100644 --- a/tools/build/scripts/docs.js +++ b/tools/build/scripts/docs.js @@ -63,6 +63,9 @@ const args = [ "--mode", "modules", "--readme", readmeOption, "--module", "commonjs", + "--plugin", "typedoc-plugin-external-module-name,typedoc-plugin-internal-external", + "--external-aliases", "alpha,internal", + "--internal-aliases", "UNUSED", ...baseUrlOptions, ...includeOptions ] diff --git a/tools/build/scripts/extract-api.js b/tools/build/scripts/extract-api.js new file mode 100644 index 0000000..94f6731 --- /dev/null +++ b/tools/build/scripts/extract-api.js @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +"use strict"; + +const spawn = require('cross-spawn'); +const argv = require("yargs").argv; +const fs = require("fs-extra"); + +if (argv.entry === undefined) { + console.log("No argument found"); + return; +} + +const isCI = (process.env.TF_BUILD); +var errorCode = 0; +const entryPointFileName = argv.entry; +const isPresentation = argv.isPresentation; + +const config = { + $schema: "https://developer.microsoft.com/json-schemas/api-extractor/api-extractor.schema.json", + compiler: { + configType: "tsconfig", + rootFolder: isPresentation ? "./src" : "." + }, + project: { + entryPointSourceFile: isPresentation ? `../lib/${entryPointFileName}.d.ts` : `lib/${entryPointFileName}.d.ts` + }, + policies: { + namespaceSupport: "permissive" + }, + validationRules: { + missingReleaseTags: "allow" + }, + apiJsonFile: { + enabled: false + }, + apiReviewFile: { + enabled: true, + apiReviewFolder: isPresentation ? "../../../common/api" : "../../common/api", + tempFolder: isPresentation ? "../../../common/temp/api" : "../../common/temp/api" + } +}; + +const configFileName = `lib/${entryPointFileName}.json`; +fs.writeFileSync(configFileName, JSON.stringify(config, null, 2)); + +const args = [ + "run", + "-c", configFileName +]; +if (!isCI) + args.push("-l"); + + +//Temporarily re-implementing features of simple-spawn till version 7 of api-extractor is released +//Spawns a child process to run api-extractor and pipes the errors to be handled in this script +const cwd = process.cwd(); +const child = spawn("api-extractor", args, { + cwd: cwd, +}); +child.stdout.on('data', (data) => { + process.stdout.write(data); +}) +child.stderr.on('data', (data) => { + if (data.includes("You have changed the public API signature for this project.") && isCI) + errorCode = 1; + if (data.includes("The @param block") || data.includes("TSDoc") || data.includes("HTML") || data.includes("Structured content") || data.includes("The code span") || data.includes("A backslash can only be used") || data.includes("for a code fence")) { + //Filter out these errors + } else { + process.stderr.write(data); + if (isCI) { + errorCode = 1; + } + +}) +child.on('error', (data) => { + console.log(data); +}); +child.on('close', (code) => { + if (fs.existsSync(configFileName)) + fs.unlinkSync(configFileName); + + if (fs.existsSync("dist/tsdoc-metadata.json")) { + fs.unlinkSync("dist/tsdoc-metadata.json"); + fs.rmdirSync("dist"); + } + process.exit(errorCode); +}); \ No newline at end of file diff --git a/tools/build/scripts/ignoreargs.js b/tools/build/scripts/ignoreargs.js new file mode 100644 index 0000000..afcce7f --- /dev/null +++ b/tools/build/scripts/ignoreargs.js @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +// This script is used to ignore arguments. It is needed because the "rush build" command passes the +// same arguments to the build scripts in all the packages. When we want to pass arguments to buildIModelJsModule, +// we must have a way of "absorbing" the arguments so they don't cause problems. This script is put at the end +// of the build instructions. +const yargs = require("yargs"); + +// Get the arguments using the ubiquitous yargs package. +function getArgs() { + const args = yargs + .option("verbose", { + alias: "v" + }) + .option("detail", { + alias: "d", + type: "number", + }) + .help().argv; + return args; +} + +cmdLineArgs = getArgs(); +if (cmdLineArgs.verbose || cmdLineArgs.detail > 2) + console.log("ignoring these arguments", cmdLineArgs); \ No newline at end of file diff --git a/tools/build/scripts/utils/validateTags.js b/tools/build/scripts/utils/validateTags.js index b03c8a7..d2ccc0b 100644 --- a/tools/build/scripts/utils/validateTags.js +++ b/tools/build/scripts/utils/validateTags.js @@ -4,104 +4,117 @@ *--------------------------------------------------------------------------------------------*/ const FS = require('fs-extra'); -const validTags = ["see", "note", "throws", "param", "deprecated", "module", "type", "minimum", "minlength", "pattern", "beta", "wip", "experimental", "default", "example"]; +const validTags = [ + "see", + "note", + "throws", + "param", + "module", + "type", + "minimum", + "minlength", + "default", + "example", + "pattern", + + // Following flags are added to support API-extractor (https://api-extractor.com/pages/tsdoc/syntax/#release-tags) + "alpha", + "beta", + "deprecated", + "internal", + "public", +]; function validateTags(path) { - let tags = parseFile(path); - return tags; + return parseFile(path); } function parseFile(path) { - let allTags = {}; - - if (FS.existsSync(path) && FS.statSync(path).isFile()) { - const contents = FS.readFileSync(path, 'utf-8'); - let jsonContents = JSON.parse(contents); - - let tags = findValues(jsonContents, 'tags'); - - for (let j = 0; j < tags.length; j++) { - for (let i = 0; i < tags[j].length; i++) { - allTags[tags[j][i]['tag']] = allTags[tags[j][i]['tag']] ? allTags[tags[j][i]['tag']] + 1 : 1; - } - } - - let invalidTagObjects = []; - for (tag in allTags) { - if (!validTags.includes(tag)) { - invalidTagObjects.push(tag, findSource(jsonContents, 'tag', tag)); - } - } - return invalidTagObjects; + let allTags = {}; + + if (FS.existsSync(path) && FS.statSync(path).isFile()) { + const contents = FS.readFileSync(path, 'utf-8'); + let jsonContents = JSON.parse(contents); + + let tags = findValues(jsonContents, 'tags'); + + for (let j = 0; j < tags.length; j++) { + for (let i = 0; i < tags[j].length; i++) + allTags[tags[j][i]['tag']] = allTags[tags[j][i]['tag']] ? allTags[tags[j][i]['tag']] + 1 : 1; } + + let invalidTagObjects = []; + for (tag in allTags) { + if (!validTags.includes(tag)) + invalidTagObjects.push(tag, findSource(jsonContents, 'tag', tag)); + } + return invalidTagObjects; + } } function findValues(obj, key) { - return findValuesHelper(obj, key, []); + return findValuesHelper(obj, key, []); } function findValuesHelper(obj, key, list) { - if (!obj) return list; - if (obj instanceof Array) { - for (var i in obj) { - list = list.concat(findValuesHelper(obj[i], key, [])); - } - return list; - } - if (obj[key]) list.push(obj[key]); - - if ((typeof obj == "object") && (obj !== null)) { - var children = Object.keys(obj); - if (children.length > 0) { - for (i = 0; i < children.length; i++) { - list = list.concat(findValuesHelper(obj[children[i]], key, [])); - } - } - } + if (!obj) return list; + if (obj instanceof Array) { + for (var i in obj) + list = list.concat(findValuesHelper(obj[i], key, [])); return list; + } + + if (obj[key]) list.push(obj[key]); + + if ((typeof obj == "object") && (obj !== null)) { + const children = Object.keys(obj); + if (children.length > 0) { + for (i = 0; i < children.length; i++) + list = list.concat(findValuesHelper(obj[children[i]], key, [])); + } + } + return list; } function findSource(obj, key, value) { - return findSourceHelper(obj, key, value, []); + return findSourceHelper(obj, key, value, []); } function findSourceHelper(obj, key, value, list) { - if (!obj) return list; - if (obj instanceof Array) { - for (var i in obj) { - list = list.concat(findSourceHelper(obj[i], key, value, [])); - } - return list; + if (!obj) return list; + if (obj instanceof Array) { + for (var i in obj) + list = list.concat(findSourceHelper(obj[i], key, value, [])); + return list; + } + + //Look for tag in signature or in comment + if (obj['signatures']) { + if (obj['signatures'][0] && obj['signatures'][0]['comment'] && obj['signatures'][0]['comment']['tags']) { + for (let tag in obj['signatures'][0]['comment']['tags']) { + if (obj['signatures'][0]['comment']['tags'][tag].tag === value && obj['sources'] && obj['sources'][0]) + list.push(obj['sources'][0]); + } } + } - //Look for tag in signature or in comment - if (obj['signatures']) { - if (obj['signatures'][0] && obj['signatures'][0]['comment'] && obj['signatures'][0]['comment']['tags']) { - for (let tag in obj['signatures'][0]['comment']['tags']) { - if (obj['signatures'][0]['comment']['tags'][tag].tag === value && obj['sources'] && obj['sources'][0]) - list.push(obj['sources'][0]); - - } - } - } - if (obj['comment'] && obj['comment']['tags']) { - for (let tag in obj['comment']['tags']) { - if (obj['comment']['tags'][tag].tag === value && obj['sources'] && obj['sources'][0]) - list.push(obj['sources'][0]); - } + if (obj['comment'] && obj['comment']['tags']) { + for (let tag in obj['comment']['tags']) { + if (obj['comment']['tags'][tag].tag === value && obj['sources'] && obj['sources'][0]) + list.push(obj['sources'][0]); } + } - if ((typeof obj == "object") && (obj !== null)) { - var children = Object.keys(obj); - if (children.length > 0) { - for (i = 0; i < children.length; i++) { - list = list.concat(findSourceHelper(obj[children[i]], key, value, [])); - } - } + if ((typeof obj == "object") && (obj !== null)) { + const children = Object.keys(obj); + if (children.length > 0) { + for (i = 0; i < children.length; i++) + list = list.concat(findSourceHelper(obj[children[i]], key, value, [])); } - return list; + } + return list; } module.exports = { - validateTags: validateTags, -}; \ No newline at end of file + validateTags: validateTags, +}; diff --git a/tools/build/tsconfig-base.json b/tools/build/tsconfig-base.json index 66368bd..cf30268 100644 --- a/tools/build/tsconfig-base.json +++ b/tools/build/tsconfig-base.json @@ -11,7 +11,7 @@ "scripthost" ], "module": "commonjs", - "stripInternal": true, + "stripInternal": false, "declaration": true, "strict": true, /* Enable all strict type-checking options. */ "noImplicitAny": true, /* Raise error on expressions and declarations with an implied "any" type. */ diff --git a/tools/build/tslint.json b/tools/build/tslint.json index ae02740..2efc1fb 100644 --- a/tools/build/tslint.json +++ b/tools/build/tslint.json @@ -106,6 +106,11 @@ "type": "property", "modifiers": "protected", "leadingUnderscore": "require" + }, + // require camelCase on all methods + { + "type": "method", + "format": "camelCase" } ], // Custom rules ======================================================== diff --git a/tools/config-loader/CHANGELOG.json b/tools/config-loader/CHANGELOG.json index b374918..4cfa2f1 100644 --- a/tools/config-loader/CHANGELOG.json +++ b/tools/config-loader/CHANGELOG.json @@ -1,6 +1,27 @@ { "name": "@bentley/config-loader", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/config-loader_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "Use new buildIModelJsBuild script" + }, + { + "comment": "upgrade to TypeScript 3.2.2" + } + ] + } + }, + { + "version": "0.188.0", + "tag": "@bentley/config-loader_v0.188.0", + "date": "Wed, 16 Jan 2019 16:36:09 GMT", + "comments": {} + }, { "version": "0.187.0", "tag": "@bentley/config-loader_v0.187.0", diff --git a/tools/config-loader/CHANGELOG.md b/tools/config-loader/CHANGELOG.md index 1e4ec8e..496fc0c 100644 --- a/tools/config-loader/CHANGELOG.md +++ b/tools/config-loader/CHANGELOG.md @@ -1,6 +1,19 @@ # Change Log - @bentley/config-loader -This log was last generated on Tue, 15 Jan 2019 15:18:59 GMT and should not be manually modified. +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- Use new buildIModelJsBuild script +- upgrade to TypeScript 3.2.2 + +## 0.188.0 +Wed, 16 Jan 2019 16:36:09 GMT + +*Version update only* ## 0.187.0 Tue, 15 Jan 2019 15:18:59 GMT diff --git a/tools/config-loader/package.json b/tools/config-loader/package.json index 80343b2..7aae2c8 100644 --- a/tools/config-loader/package.json +++ b/tools/config-loader/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/config-loader", - "version": "0.187.0", + "version": "0.189.0", "description": "Configuration loader", "license": "MIT", "repository": { @@ -9,7 +9,7 @@ }, "typings": "lib/index", "scripts": { - "build": "tsc 1>&2", + "build": "tsc 1>&2 && node ./node_modules/@bentley/build-tools/scripts/ignoreargs.js 1>&2", "clean": "rimraf lib package-deps.json", "docs": "", "lint": "tslint --project . 1>&2", @@ -30,11 +30,11 @@ "json5": "^2.1.0" }, "devDependencies": { - "@bentley/build-tools": "0.187.0", + "@bentley/build-tools": "0.189.0", "@types/json5": "0.0.30", - "@types/node": "10.10.3", + "@types/node": "10.12.18", "rimraf": "^2.6.2", "tslint": "^5.11.0", - "typescript": "~3.1.0" + "typescript": "~3.2.2" } } diff --git a/tools/webpack/CHANGELOG.json b/tools/webpack/CHANGELOG.json index 254d0a7..ced743b 100644 --- a/tools/webpack/CHANGELOG.json +++ b/tools/webpack/CHANGELOG.json @@ -1,6 +1,54 @@ { "name": "@bentley/webpack-tools", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/webpack-tools_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "Fix BuildIModelJsModule for packages external to monorepository" + }, + { + "comment": "Added buildIModelJsBuild script to sequence frontend module builds" + }, + { + "comment": "Uglify Options changed to fix regex known problem." + }, + { + "comment": "Change default mode to copy rather than symlink files." + }, + { + "comment": "Webworker module type added" + }, + { + "comment": "Fix for CopyExternalModules for indirect iModel.js dependent modules." + }, + { + "comment": "Provide a way to copy rather than symlink sourceResources" + }, + { + "comment": "Convert IndexedPolyface params from array-of-points to GrowableXYArray." + }, + { + "comment": "Changes to define BUILD_SEMVER in modules, define SEMVERs required for iModelJs system modules for Plugins." + }, + { + "comment": "Rewrite of CopyExternalModules" + }, + { + "comment": "upgrade to TypeScript 3.2.2" + } + ] + } + }, + { + "version": "0.188.0", + "tag": "@bentley/webpack-tools_v0.188.0", + "date": "Wed, 16 Jan 2019 16:36:09 GMT", + "comments": {} + }, { "version": "0.187.0", "tag": "@bentley/webpack-tools_v0.187.0", diff --git a/tools/webpack/CHANGELOG.md b/tools/webpack/CHANGELOG.md index d16c97f..ebf9e69 100644 --- a/tools/webpack/CHANGELOG.md +++ b/tools/webpack/CHANGELOG.md @@ -1,6 +1,28 @@ # Change Log - @bentley/webpack-tools -This log was last generated on Tue, 15 Jan 2019 15:18:59 GMT and should not be manually modified. +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- Fix BuildIModelJsModule for packages external to monorepository +- Added buildIModelJsBuild script to sequence frontend module builds +- Uglify Options changed to fix regex known problem. +- Change default mode to copy rather than symlink files. +- Webworker module type added +- Fix for CopyExternalModules for indirect iModel.js dependent modules. +- Provide a way to copy rather than symlink sourceResources +- Convert IndexedPolyface params from array-of-points to GrowableXYArray. +- Changes to define BUILD_SEMVER in modules, define SEMVERs required for iModelJs system modules for Plugins. +- Rewrite of CopyExternalModules +- upgrade to TypeScript 3.2.2 + +## 0.188.0 +Wed, 16 Jan 2019 16:36:09 GMT + +*Version update only* ## 0.187.0 Tue, 15 Jan 2019 15:18:59 GMT diff --git a/tools/webpack/bin/buildIModelJsModule.js b/tools/webpack/bin/buildIModelJsModule.js new file mode 100644 index 0000000..f5f46c2 --- /dev/null +++ b/tools/webpack/bin/buildIModelJsModule.js @@ -0,0 +1,854 @@ +#!/usr/bin/env node +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const path = require("path"); +const fs = require("fs"); +const yargs = require("yargs"); +const child_process = require("child_process"); +const glob = require("glob"); +// Get the arguments using the ubiquitous yargs package. +function getArgs() { + const args = yargs + .wrap(120) + .option("production", { + alias: ["prod", "p"], + describe: "Build production version of module", + default: false, + type: "boolean" + }) + .option("verbose", { + alias: ["v"], + describe: "Show detail level 1", + default: false, + type: "boolean" + }) + .option("detail", { + alias: "d", + options: [0, 1, 2, 3, 4, 5], + type: "number", + default: 0, + describe: "Sets detail level (0 to 4) to reveal more about the build process" + }) + .option("stats", { + alias: "s", + type: "boolean", + default: false, + describe: "Creates the webpack stats json file for system modules" + }) + .help().argv; + return args; +} +// Utilities for file system operations +class Utils { + static readPackageFileContents(packageRoot) { + const packageFileName = path.resolve(packageRoot, "./package.json"); + if (!fs.existsSync(packageFileName)) + return {}; + const packageFileContents = fs.readFileSync(packageFileName, "utf8"); + return JSON.parse(packageFileContents); + } + static symlinkFiles(cwd, source, dest, alwaysCopy, detail) { + // first we must create the destination directory, if it isn't already there. + const sourceSpecification = path.resolve(cwd, source); + let sourceDirectory = path.dirname(sourceSpecification); + if (sourceDirectory.endsWith("**")) { + sourceDirectory = sourceDirectory.slice(0, sourceDirectory.length - 3); + } + const destinationPath = path.resolve(cwd, dest); + try { + Utils.makeDirectoryNoError(destinationPath); + const found = glob.sync(sourceSpecification, { nodir: true }); + for (const fileName of found) { + // find it relative to source. + const relativePath = path.relative(sourceDirectory, fileName); + const outputPath = path.resolve(destinationPath, relativePath); + if (fs.existsSync(outputPath)) { + if (detail > 3) + console.log(" File", outputPath, "already exists"); + } + else { + if (detail > 3) + console.log(alwaysCopy ? " Copying" : " Symlinking", fileName, "to", outputPath); + Utils.makeDirectoryNoError(path.dirname(outputPath)); + if (alwaysCopy) + fs.copyFileSync(fileName, outputPath); + else + fs.symlinkSync(fileName, outputPath); + } + } + } + catch (error) { + return new Result("Symlink or Copy Source Resources", 1, error); + } + return new Result("Symlink or Copy Source Resources", 0); + } + // we use this where there is a possible race condition where the compiler might be creating directories in parallel to our linking. + static makeDirectoryNoError(outDirectory) { + // Note: mkdirSync with option of { recursive: true } did not work on Linux, necessitating this workaround/ + const directoriesToCreate = []; + // work backwards through the outDirectory to find the first one that exists. + let thisDirectory = outDirectory; + try { + while (!fs.existsSync(thisDirectory)) { + directoriesToCreate.push(thisDirectory); + const parsedPath = path.parse(thisDirectory); + thisDirectory = parsedPath.dir; + } + let createDir; + while (createDir = directoriesToCreate.pop()) { + fs.mkdirSync(createDir); + } + } + catch (_error) { + // do nothing on error. + } + } +} +// description of each node_module. +class ModuleInfo { + constructor(isDevelopment, useVersion, moduleName, destFileName, relativePath, publicResourceDirectory) { + this.isDevelopment = isDevelopment; + this.useVersion = useVersion; + this.moduleName = moduleName; + this.publicResourceDirectory = publicResourceDirectory; + // if relativePath not supplied, it's one of our @bentley modules and we can figure it out. + this.destFileName = destFileName ? destFileName : moduleName + ".js"; + if (!relativePath) { + this.relativePath = path.join(moduleName, isDevelopment ? "lib/module/dev" : "lib/module/prod", this.destFileName); + } + else { + this.relativePath = relativePath; + } + } +} +// keeps track of each dependent's information. +class DependentInfo { + constructor(name, packageRoot, parentPackageRoot, externalModule, version) { + this.name = name; + this.packageRoot = packageRoot; + this.parentPackageRoot = parentPackageRoot; + this.externalModule = externalModule; + this.version = version; + } +} +// class that copies (or symlinks) the external modules needed my iModel.js into the web resources directory. +class ModuleCopier { + // these are all modules that are listed as external in our webpack configuration, and therefore need to be copied to the web resources directory. + constructor(_nodeModulesDirectory, _isDevelopment, _detail, _alwaysCopy) { + this._nodeModulesDirectory = _nodeModulesDirectory; + this._isDevelopment = _isDevelopment; + this._detail = _detail; + this._alwaysCopy = _alwaysCopy; + this._dependentList = []; + this._externalModules = [ + new ModuleInfo(_isDevelopment, true, "@bentley/bentleyjs-core", "bentleyjs-core.js", undefined), + new ModuleInfo(_isDevelopment, true, "@bentley/geometry-core", "geometry-core.js", undefined), + new ModuleInfo(_isDevelopment, true, "@bentley/imodeljs-i18n", "imodeljs-i18n.js", undefined), + new ModuleInfo(_isDevelopment, true, "@bentley/imodeljs-clients", "imodeljs-clients.js", undefined), + new ModuleInfo(_isDevelopment, true, "@bentley/imodeljs-common", "imodeljs-common.js", undefined), + new ModuleInfo(_isDevelopment, true, "@bentley/imodeljs-quantity", "imodeljs-quantity.js", undefined), + new ModuleInfo(_isDevelopment, true, "@bentley/imodeljs-frontend", "imodeljs-frontend.js", undefined, "lib/public"), + new ModuleInfo(_isDevelopment, true, "@bentley/ui-core", "ui-core.js", undefined, "lib/public"), + new ModuleInfo(_isDevelopment, true, "@bentley/ui-components", "ui-components.js", undefined, "lib/public"), + new ModuleInfo(_isDevelopment, true, "@bentley/ui-framework", "ui-framework.js", undefined, "lib/public"), + new ModuleInfo(_isDevelopment, true, "@bentley/ui-ninezone", "ui-ninezone.js", undefined), + new ModuleInfo(_isDevelopment, true, "@bentley/presentation-common", "presentation-common.js", undefined), + new ModuleInfo(_isDevelopment, true, "@bentley/presentation-components", "presentation-components.js", undefined, "lib/public"), + new ModuleInfo(_isDevelopment, true, "@bentley/presentation-frontend", "presentation-frontend.js", undefined), + new ModuleInfo(_isDevelopment, false, "react", undefined, path.join("react", _isDevelopment ? "umd/react.development.js" : "umd/react.production.min.js")), + new ModuleInfo(_isDevelopment, false, "react-dnd", undefined, path.join("react-dnd", _isDevelopment ? "dist/ReactDnD.js" : "dist/ReactDnD.min.js")), + new ModuleInfo(_isDevelopment, false, "react-dnd-html5-backend", undefined, path.join("react-dnd-html5-backend", _isDevelopment ? "dist/ReactDnDHTML5Backend.js" : "dist/ReactDnDHTML5Backend.min.js")), + new ModuleInfo(_isDevelopment, false, "react-dom", undefined, path.join("react-dom", _isDevelopment ? "umd/react-dom.development.js" : "umd/react-dom.production.min.js")), + new ModuleInfo(_isDevelopment, false, "react-redux", undefined, path.join("react-redux", _isDevelopment ? "dist/react-redux.js" : "dist/react-redux.min.js")), + new ModuleInfo(_isDevelopment, false, "redux", undefined, path.join("redux", _isDevelopment ? "dist/redux.js" : "dist/redux.min.js")), + new ModuleInfo(_isDevelopment, false, "inspire-tree", undefined, path.join("inspire-tree", _isDevelopment ? "dist/inspire-tree.js" : "dist/inspire-tree.min.js")), + new ModuleInfo(_isDevelopment, false, "lodash", undefined, path.join("lodash", _isDevelopment ? "lodash.js" : "lodash.min.js")), + ]; + } + // symlinks the public static files from a module into the output web resources directories. + symlinkOrCopyPublicStaticFiles(sourcePublicDirectory, outputPublicDirectory) { + const symlinkSource = `${sourcePublicDirectory}/**/*`; + Utils.symlinkFiles(process.cwd(), symlinkSource, outputPublicDirectory, this._alwaysCopy, this._detail); + } + // checks contents of existing symlink and replaces it if necessary + ensureSymlink(sourceFile, outFilePath) { + try { + if (fs.existsSync(outFilePath)) { + try { + const linkContents = fs.readlinkSync(outFilePath, { encoding: "utf8" }); + if (linkContents === sourceFile) { + if (this._detail > 3) + console.log(" File", outFilePath, "already exists"); + return 0; + } + } + catch (_error) { + // It's not a link, do nothing and let it get deleted. + } + if (this._detail > 3) + console.log(" Removing existing symlink found in", outFilePath); + fs.unlinkSync(outFilePath); + } + if (this._detail > 3) + console.log(" Symlinking", sourceFile, "to", outFilePath); + fs.symlinkSync(sourceFile, outFilePath); + } + catch (error) { + console.log(error); + return 1; + } + return 0; // success + } + // symlinks the module file and source map file if available. + symlinkOrCopyModuleFile(moduleSourceFile, outFilePath) { + if (this._alwaysCopy) { + // copy the module file. + if (this._detail > 3) + console.log(" Copying file", moduleSourceFile, "to", outFilePath); + // if there is a symlink already there, copyFileSync fails, so check for that case. + if (fs.existsSync(outFilePath)) + fs.unlinkSync(outFilePath); + fs.copyFileSync(moduleSourceFile, outFilePath); + // if there's a source map file, link that, too. + const mapFile = moduleSourceFile + ".map"; + if (fs.existsSync(mapFile)) { + const outMapFile = outFilePath + ".map"; + if (this._detail > 3) + console.log(" Copying file", mapFile, "to", outMapFile); + // if there is a symlink already there, copyFileSync fails, so check for that case. + if (fs.existsSync(outMapFile)) + fs.unlinkSync(outMapFile); + fs.copyFileSync(mapFile, outMapFile); + } + } + else { + // symlink the module file. + this.ensureSymlink(moduleSourceFile, outFilePath); + // if there's a source map file, link that, too. + const mapFile = moduleSourceFile + ".map"; + const outMapFile = outFilePath + ".map"; + if (fs.existsSync(mapFile)) + this.ensureSymlink(mapFile, outMapFile); + } + } + // this function adds the dependencies found in package.json that are external modules. Recurses, but only to depth 1. + findExternalModuleDependents(parentPackageRoot, depth) { + const packageFileContents = Utils.readPackageFileContents(parentPackageRoot); + // find new dependents from this packageFileContents + const newDependents = []; + for (const dependent in packageFileContents.dependencies) { + // see if we already have this dependent. + if (undefined !== this._dependentList.find((existingDependent) => { return (existingDependent.name === dependent); })) { + continue; + } + // we only care about external modules and their dependents that might be external modules. + for (const externalModule of this._externalModules) { + if (externalModule.moduleName === dependent) { + const dependentPackageRoot = path.resolve(parentPackageRoot, "node_modules", dependent); + newDependents.push(new DependentInfo(dependent, dependentPackageRoot, parentPackageRoot, externalModule, packageFileContents.dependencies[dependent])); + } + } + } + // add the newly discovered dependents to the master list of dependents. + for (const newDependent of newDependents) { + this._dependentList.push(newDependent); + } + // we need to check the first level of dependents of our direct dependents to find the non-imodeljs dependencies like lodash, react, redux, etc. + if (depth < 2) { + for (const newDependent of newDependents) { + this.findExternalModuleDependents(newDependent.packageRoot, depth + 1); + } + } + } + // finds peerDependencies and makes sure they are in the list of dependencies. + checkPeerDependencies() { + const missingList = []; + for (const thisDependent of this._dependentList) { + const packageFileContents = Utils.readPackageFileContents(thisDependent.packageRoot); + for (const peerDependent in packageFileContents.peerDependencies) { + // don't bother to a peerDependent twice. + if (-1 !== missingList.indexOf(peerDependent)) + continue; + // we only report miss peerDependencies of our external modules. + if (undefined === (this._externalModules.find((externalModule) => { return (externalModule.moduleName === peerDependent); }))) + continue; + if (undefined === this._dependentList.find((thisDependent) => { return (thisDependent.name === peerDependent); })) { + if (this._detail > 0) + console.log(" Dependent", thisDependent.name, "requires a peerDependency of", peerDependent, "but none found. Add ", peerDependent, "to dependencies in package.json"); + missingList.push(peerDependent); + } + } + } + return missingList; + } + // finds the source module file. It is either relative to our localNodeModules, or relative to the dependent path where we found the package file. + findExternalModuleFile(dependent, relativePath) { + const localNodeModuleFile = path.resolve(this._nodeModulesDirectory, relativePath); + if (fs.existsSync(localNodeModuleFile)) + return localNodeModuleFile; + const dependentNodeModuleFile = path.resolve(dependent.parentPackageRoot, "node_modules", relativePath); + if (fs.existsSync(dependentNodeModuleFile)) + return dependentNodeModuleFile; + return undefined; + } + // finds dependents, symlinks them to destination directory. + symlinkOrCopyExternalModules(outputDirectory) { + if (this._detail > 0) + console.log("Starting symlink or copy external modules"); + try { + // create the output directory, if it doesn't exist. + if (!fs.existsSync(outputDirectory)) { + fs.mkdirSync(outputDirectory, { recursive: true }); + } + // Read the package file for the current directory, and add the dependents recursively (to depth 2) to the sub-dependencies of the iModelJs modules. + this.findExternalModuleDependents(process.cwd(), 0); + const missingPeerDependencies = this.checkPeerDependencies(); + if (missingPeerDependencies.length > 0) + return new Result("Symlink or Copy External Modules", 1, undefined, undefined, "You are missing one or more dependencies in package.json: \n".concat(...missingPeerDependencies)); + let missingModule = false; + const missingModuleList = []; + for (const dependent of this._dependentList) { + const externalModule = dependent.externalModule; + const versionString = (externalModule.useVersion) ? dependent.version : undefined; + const moduleSourceFile = this.findExternalModuleFile(dependent, externalModule.relativePath); + // report all the module files we can't find. + if (!moduleSourceFile) { + missingModuleList.push(`Unable to locate required module file ${externalModule.moduleName} - looking for relative path ${externalModule.relativePath}\n`); + missingModule = true; + } + else { + let outFilePath = outputDirectory; + if (versionString) { + outFilePath = path.resolve(outputDirectory, "v" + versionString); + // create subdirectory if needed. + if (!fs.existsSync(outFilePath)) { + fs.mkdirSync(outFilePath, { recursive: true }); + } + } + const fullFilePath = path.resolve(outFilePath, externalModule.destFileName); + this.symlinkOrCopyModuleFile(moduleSourceFile, fullFilePath); + // symlink any subModules in the build. + const packageFileContents = Utils.readPackageFileContents(dependent.packageRoot); + if (packageFileContents.iModelJs && packageFileContents.iModelJs.buildModule && packageFileContents.iModelJs.buildModule.subModules && Array.isArray(packageFileContents.iModelJs.buildModule.subModules)) { + for (const subModule of packageFileContents.iModelJs.buildModule.subModules) { + if (subModule.bundleName) { + const parsedPath = path.parse(moduleSourceFile); + const thisDirectory = parsedPath.dir; + const subModuleFileName = subModule.bundleName + ".js"; + const subModuleSourceFile = path.resolve(thisDirectory, subModuleFileName); + const destFullFilePath = path.resolve(outFilePath, subModuleFileName); + this.symlinkOrCopyModuleFile(subModuleSourceFile, destFullFilePath); + } + } + } + // symlink the external modules resource files if necessary. + if (externalModule.publicResourceDirectory) { + const publicPath = path.resolve(dependent.packageRoot, externalModule.publicResourceDirectory); + this.symlinkOrCopyPublicStaticFiles(publicPath, outputDirectory); + } + } + } + if (missingModule) { + return new Result("Symlink or Copy External Modules", 1, undefined, undefined, "Could not find one or more dependencies:\n".concat(...missingModuleList)); + } + // link the IModelJsLoader.js from imodeljs/frontend also. NOTE: imodeljs-frontend must always be in package.json's dependencies. + const loaderFile = path.resolve(process.cwd(), "node_modules/@bentley/imodeljs-frontend", this._isDevelopment ? "lib/module/dev/IModelJsLoader.js" : "lib/module/prod/IModelJsLoader.js"); + this.symlinkOrCopyModuleFile(loaderFile, path.resolve(outputDirectory, "iModelJsLoader.js")); + } + catch (e) { + return new Result("Symlink or Copy External Modules", 1, e); + } + return new Result("Symlink or Copy External Modules", 0); + } +} +// this class creates pseudo-localized versions of all the locale .json files specified in the sourceDirectory. +class PseudoLocalizer { + constructor(_sourceDirectory, _destDirectory, _detail) { + this._sourceDirectory = _sourceDirectory; + this._destDirectory = _destDirectory; + this._detail = _detail; + } + // pseudoLocalizes a string + convertString(inputString) { + let inReplace = 0; + let outString = ""; + let replaceIndex = 0; // Note: the pseudoLocalize algorithm in Bim02 uses random, but here we cycle through because Javascript doesn't allow setting of the seed for Math.random. + for (let iChar = 0; iChar < inputString.length; iChar++) { + let thisChar = inputString.charAt(iChar); + let nextChar = ((iChar + 1) < inputString.length) ? inputString.charAt(iChar + 1) : 0; + // handle the {{ and }} delimiters for placeholders - don't want to do anything to characters in between. + if (('{' === thisChar) && ('{' === nextChar)) { + inReplace++; + iChar++; + outString = outString.concat("{{"); + } + else if (('}' === thisChar) && ('}' === nextChar) && (inReplace > 0)) { + inReplace--; + iChar++; + outString = outString.concat("}}"); + } + else { + let replacementChar = thisChar; + if (0 === inReplace) { + let replacementsForChar = PseudoLocalizer.replacements[thisChar]; + if (undefined !== replacementsForChar) { + replacementChar = replacementsForChar.charAt(replaceIndex++ % replacementsForChar.length); + } + } + outString = outString.concat(replacementChar); + } + } + return outString; + } + // converts the JSON object + convertObject(objIn) { + const objOut = {}; + for (const prop in objIn) { + if (objIn.hasOwnProperty(prop)) { + if (typeof objIn[prop] === "string") { + objOut[prop] = this.convertString(objIn[prop]); + } + else if (typeof objIn[prop] === "object") { + objOut[prop] = this.convertObject(objIn[prop]); + } + } + } + return objOut; + } + // converts each JSON file. + convertFile(inputFilePath, outputFile) { + // read the file + let jsonIn = fs.readFileSync(inputFilePath, { encoding: "utf8" }); + let objIn = JSON.parse(jsonIn); + let objOut = this.convertObject(objIn); + fs.writeFileSync(outputFile, JSON.stringify(objOut, null, 2)); + return 0; + } + convertAll() { + try { + Utils.makeDirectoryNoError(this._destDirectory); + const sourceSpecification = path.join(this._sourceDirectory, "**/*.json"); + const found = glob.sync(sourceSpecification, { nodir: true }); + for (const fileName of found) { + // find it relative to source. + const relativePath = path.relative(this._sourceDirectory, fileName); + const outputPath = path.resolve(this._destDirectory, relativePath); + if (fs.existsSync(outputPath)) { + if (this._detail > 3) + console.log(" File", outputPath, "already exists"); + } + else { + if (this._detail > 3) + console.log(" PseudoLocalizing", fileName, "to", outputPath); + Utils.makeDirectoryNoError(path.dirname(outputPath)); + this.convertFile(fileName, outputPath); + } + } + } + catch (error) { + return new Result("PseudoLocalize", 1, error); + } + return new Result("PseudoLocalize", 0); + } +} +PseudoLocalizer.replacements = { + A: "\u00C0\u00C1,\u00C2\u00C3,\u00C4\u00C5", + a: "\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5", + B: "\u00DF", + c: "\u00A2\u00E7", + C: "\u00C7\u0028", + D: "\u00D0", + E: "\u00C8\u00C9\u00CA\u00CB", + e: "\u00E8\u00E9\u00EA\u00EB", + I: "\u00CC\u00CD\u00CE\u00CF", + i: "\u00EC\u00ED\u00EE\u00EF", + L: "\u00A3", + N: "\u00D1", + n: "\u00F1", + O: "\u00D2\u00D3\u00D4\u00D5\u00D6", + o: "\u00F2\u00F3\u00F4\u00F5\u00F6\u00F8", + S: "\u0024\u00A7", + U: "\u00D9\u00DA\u00DB\u00DC", + u: "\u00B5\u00F9\u00FA\u00FB\u00FC", + x: "\u00D7", + Y: "\u00DD\u00A5", + y: "\u00FD\u00FF", +}; +// Class that holds the results of a build operation. +class Result { + constructor(operation, exitCode, error, stdout, stderr) { + this.operation = operation; + this.exitCode = exitCode; + this.error = error; + this.stdout = stdout; + this.stderr = stderr; + } +} +// Class that contains a method for each step in building an iModelJs module. +class IModelJsModuleBuilder { + // constructor + constructor(_moduleDescription, _detail, _isDevelopment, _webpackStats) { + this._moduleDescription = _moduleDescription; + this._detail = _detail; + this._isDevelopment = _isDevelopment; + this._webpackStats = _webpackStats; + this._alwaysCopy = process.env.BUILDIMODEL_SYMLINKS === undefined; + } + // checks the iModelJs buildModule property. + checkDefinition() { + // check the type. + if (!this._moduleDescription.type) { + console.log('iModelJs.buildModule.type must have a type property with value of "system", "application", "plugin", or "webworker"'); + return true; + } + if ((this._moduleDescription.type !== "system") && (this._moduleDescription.type !== "application") && + (this._moduleDescription.type !== "plugin") && (this._moduleDescription.type != "webworker")) { + console.log('iModelJs.buildModule.type must be one of "system", "application", "plugin", or "webworker"'); + return true; + } + return false; + } + // compiles the tsc source according to tsconfig.js. (generally, compiles src/**/*.ts to lib) + compileSource() { + // The version of typescript required must be specified in package.json devDependencies. + // In rush monorepos, it can be specified in the rush common/config/rush/common-version.json file. + // npm (or rush) install mechanism puts tsc into the node_modules/.bin directory where npm will find it. + // Note: I tried setting shell: true in the options (third) argument to execFile, and then just specifying + // "tsc" (rather than tsc or tsc.cmd based on platform). Unfortunately, that wasn't reliable. + return new Promise((resolve, _reject) => { + if (this._detail > 0) + console.log("Starting tsc compilation"); + const tscCommand = process.platform === "win32" ? "tsc.cmd" : "tsc"; + const tscFullPath = path.resolve(process.cwd(), "node_modules", ".bin", tscCommand); + const args = []; + if (this._moduleDescription.tscOptions) { + const tscArgs = this._moduleDescription.tscOptions.split(" "); + for (const tscArg of tscArgs) + args.push(tscArg); + } + args.push("1>&2"); + child_process.execFile(tscFullPath, args, { cwd: process.cwd(), shell: true }, (error, stdout, stderr) => { + if (this._detail > 0) + console.log("Finished compilation"); + resolve(new Result("Compile .tsc files", (null !== error) || (stderr && stderr.length) ? 1 : 0, error, stdout, stderr)); + }); + }); + } + // symlinks the web resources (like .scss and .svg files) into the lib directory for webpack to use later. + async symlinkSourceResources() { + // are there any files to symlink? + if (!this._moduleDescription.sourceResources) { + if (this._detail > 2) + console.log("Skipping Symlink Source Resource, no iModelJs.buildModule.sourceResources property"); + return Promise.resolve(new Result("Symlink Or Copy Source Resources", 0)); + } + // otherwise this should be an array of {source, dest} objects. + if (!Array.isArray(this._moduleDescription.sourceResources)) + return Promise.resolve(new Result("Symlink Or Copy Source Resources", 1, undefined, undefined, "iModelJs.buildModule.sourceResources must be an array of {source, dest} pairs")); + for (const resource of this._moduleDescription.sourceResources) { + if (!resource.source || !resource.dest) { + return Promise.resolve(new Result("Symlink Or Copy Source Resources", 1, undefined, undefined, "iModelJs.buildModule.sourceResources must be an array of {source, dest} pairs")); + } + if (this._detail > 0) + console.log(this._alwaysCopy ? "Copying files from" : "Symlinking files from", resource.source, "to", resource.dest); + const alwaysCopy = this._alwaysCopy || (resource.copy && resource.copy === true); + const result = Utils.symlinkFiles(process.cwd(), resource.source, resource.dest, alwaysCopy, this._detail); + if (0 != result.exitCode) + return result; + } + return Promise.resolve(new Result("Symlink or Copy Source Resources", 0)); + } + // If this is an application, symlink the external modules that the application uses into the web server's directory. + symlinkRequiredExternalModules() { + const nodeModulesDir = path.resolve(process.cwd(), "node_modules"); + const moduleCopier = new ModuleCopier(nodeModulesDir, this._isDevelopment, this._detail, this._alwaysCopy); + if (this._moduleDescription.type !== "application") + return Promise.resolve(new Result("Symlink or Copy External Modules", 0)); + if (!this._moduleDescription.webpack || !this._moduleDescription.webpack.dest) + return Promise.resolve(new Result("Symlink Or Copy External Modules", 0)); + const outputDirectory = path.resolve(process.cwd(), this._moduleDescription.webpack.dest); + return Promise.resolve(moduleCopier.symlinkOrCopyExternalModules(outputDirectory)); + } + makeConfig() { + if (!this._moduleDescription.makeConfig) + return Promise.resolve(new Result("makeConfig", 0)); + if (!this._moduleDescription.makeConfig.dest) + return Promise.resolve(new Result("makeConfig", 1, undefined, undefined, 'The iModelJs.buildModule.makeConfig must have a "dest" property')); + const makeConfigFullPath = path.resolve(process.cwd(), "node_modules/@bentley/webpack-tools/node_modules/@bentley/config-loader/scripts/write.js"); + const args = [makeConfigFullPath, this._moduleDescription.makeConfig.dest]; + if (this._moduleDescription.makeConfig.filter) + args.push(this._moduleDescription.makeConfig.filter); + if (this._detail > 0) + console.log("Starting makeConfig"); + return new Promise((resolve, _reject) => { + child_process.execFile("node", args, { cwd: process.cwd() }, (error, stdout, stderr) => { + if (this._detail > 0) + console.log("Finished makeConfig"); + resolve(new Result("makeConfig", (null !== error) || (stderr && stderr.length) ? 1 : 0, error, stdout, stderr)); + }); + }); + } + // find webpack executable. + findWebpack() { + // first look in node_modules/webpack + const webpackCommand = process.platform === "win32" ? "webpack.cmd" : "webpack"; + const inLocalNodeModules = path.resolve(process.cwd(), "node_modules/.bin", webpackCommand); + if (fs.existsSync(inLocalNodeModules)) + return inLocalNodeModules; + const inToolsWebpackNodeModules = path.resolve(process.cwd(), "node_modules/@bentley/webpack-tools/node_modules/.bin", webpackCommand); + if (fs.existsSync(inToolsWebpackNodeModules)) + return inToolsWebpackNodeModules; + return undefined; + } + // spawns a webpack process + startWebpack(operation, outputPath, entry, bundleName, styleSheets, buildType, isDevelopment, doStats, htmlTemplate) { + const webpackFullPath = this.findWebpack(); + if (!webpackFullPath) { + return Promise.resolve(new Result(operation, 1, undefined, undefined, "Unable to locate webpack")); + } + const args = []; + const configPath = path.resolve(process.cwd(), "node_modules/@bentley/webpack-tools/modules/webpackModule.config.js"); + args.push(`--config=${configPath}`); + args.push(`--env.outdir=${outputPath}`); + args.push(`--env.entry=${entry}`); + args.push(`--env.bundlename=${bundleName}`); + if (styleSheets) + args.push("--env.stylesheets"); + if (buildType === "plugin") + args.push("--env.plugin"); + else if (buildType === "webworker") + args.push("--env.webworker"); + if (!isDevelopment) + args.push("--env.prod"); + if (htmlTemplate) + args.push(`--env.htmltemplate=${htmlTemplate}`); + if (doStats) { + // make sure the output directory exists. + if (!fs.existsSync(outputPath)) { + fs.mkdirSync(outputPath, { recursive: true }); + } + const jsonFile = path.resolve(outputPath, "webpackStats.json"); + args.push("--json"); + args.push(">" + jsonFile); + } + return new Promise((resolve, _reject) => { + child_process.execFile(webpackFullPath, args, { cwd: process.cwd() }, (error, stdout, stderr) => { + if (this._detail > 0) + console.log("Finished", operation); + resolve(new Result(operation, (null !== error) || (stderr && stderr.length) ? 1 : 0, error, stdout, stderr)); + }); + }); + } + // Webpack the module. + webpackModule() { + // if no webpack property, skip it. + if (!this._moduleDescription.webpack) { + if (this._detail > 2) + console.log("Skipping Webpack, no iModelJs.buildModule.webpack property"); + return Promise.resolve(new Result("Webpack", 0)); + } + // make sure the required keys are there. + const webpack = this._moduleDescription.webpack; + if (!webpack.dest || !webpack.entry || !webpack.bundleName) { + return Promise.resolve(new Result("Webpack", 1, undefined, undefined, 'IModelJs.buildModule.webpack must have "dest", "entry", and "bundleName" properties')); + } + let outputPath = path.resolve(process.cwd(), webpack.dest); + if (this._moduleDescription.type === "system") { + outputPath = path.resolve(outputPath, this._isDevelopment ? "dev" : "prod"); + } + // start the webpack process according to the arguments. + const styleSheets = webpack.styleSheets ? true : false; + if (this._detail > 0) + console.log("Starting Webpack Module"); + return this.startWebpack("Webpack Module", outputPath, webpack.entry, webpack.bundleName, styleSheets, this._moduleDescription.type, this._isDevelopment, this._webpackStats, webpack.htmlTemplate); + } + // build the array of subModules. + async buildSubModules() { + if (!this._moduleDescription.subModules) { + if (this._detail > 2) + console.log("Skipping Build SubModules - No iModelJs.buildModule.subModules property"); + return Promise.resolve([new Result("Build SubModules", 0)]); + } + if (!Array.isArray(this._moduleDescription.subModules)) { + return Promise.resolve([new Result("Build SubModules", 1, undefined, undefined, "iModelJs.buildModule.subModules must be an array of {dest, entry, bundleName} objects")]); + } + const results = []; + for (const subModule of this._moduleDescription.subModules) { + if (!subModule.dest || !subModule.entry || !subModule.bundleName) { + results.push(new Result("Build SubModules", 1, undefined, undefined, 'Each subModule must have a "dest", "entry", and "bundleName" property')); + return Promise.resolve(results); + } + const styleSheets = subModule.styleSheets ? true : false; + // this is a special case for the iModelJsLoader - set plugin.type to "system" or "webworker" to avoid plugin treatment. + const subType = subModule.type || "plugin"; + if ((subType !== "system") && (subType !== "plugin") && (subType != "webworker")) { + console.log('the "type" property for a subModule must be one of "system", "plugin", or "webworker"'); + } + let outputPath = path.resolve(process.cwd(), subModule.dest); + if ((subType === "system") || (subType === "webworker")) + outputPath = path.resolve(outputPath, this._isDevelopment ? "dev" : "prod"); + if (this._detail > 0) + console.log("Starting webpack of", subModule.entry); + const pluginResult = await this.startWebpack(`Webpack Plugin ${subModule.entry}`, outputPath, subModule.entry, subModule.bundleName, styleSheets, subType, this._isDevelopment, this._webpackStats); + results.push(pluginResult); + if (pluginResult.error || pluginResult.stderr) { + return Promise.resolve(results); + } + } + return Promise.resolve(results); + } + pseudoLocalize() { + if (!this._moduleDescription.pseudoLocalize) { + if (this._detail > 2) + console.log("Skipping Symlink Source Resource, no iModelJs.buildModule.sourceResources property"); + return new Result("Pseudo localize", 0); + } + if (!this._moduleDescription.pseudoLocalize.source || !this._moduleDescription.pseudoLocalize.dest) { + return new Result("Pseudo localize", 1, undefined, undefined, 'IModelJs.buildModule.pseudoLocalize must have "source" and "dest" properties'); + } + const sourceDirectory = path.resolve(process.cwd(), this._moduleDescription.pseudoLocalize.source); + const destDirectory = path.resolve(process.cwd(), this._moduleDescription.pseudoLocalize.dest); + if (this._detail > 0) + console.log("Starting pseudoLocalize"); + const pseudoLocalizer = new PseudoLocalizer(sourceDirectory, destDirectory, this._detail); + if (this._detail > 0) + console.log("Finished PseudoLocalize"); + return pseudoLocalizer.convertAll(); + } + // If there's an error in the Result, report it and set the exitCode. + reportError(result) { + let exitCode = result.exitCode; + if (result.error && !result.stderr) { + console.error("\n-------- Operation:", result.operation, "--------"); + console.error(result.error.toString()); + console.error(result.error); + if (result.stdout) { + console.error("Output:"); + console.error(result.stdout); + } + } + if (result.stderr) { + console.error("\n-------- Operation:", result.operation, "--------"); + console.error("Errors:"); + console.error(result.stderr); + if (result.stdout) { + console.error("Output:"); + console.error(result.stdout); + } + exitCode = 1; + } + if (result.stdout && (this._detail > 1)) { + console.log("\n-------- Operation:", result.operation, "--------"); + console.log("Output:"); + console.log(result.stdout); + } + return exitCode; + } + // report results for a list of steps. + reportResults(results) { + // check all results + let exitCode = 0; + for (const result of results) { + const thisExitCode = this.reportError(result); + if (0 !== thisExitCode) + exitCode = thisExitCode; + } + return exitCode; + } + /* ---------------------- + // step one - parallel compile and symlink of source needed in webpack. + // Not currently used. Ran into problem where the two process would collide + // trying to create the output directories. + private async _compileAndSymlinkSources(): Promise { + // compile the .ts and .tsx files + const compileResult = this.compileSource(); + + // symlink the source resource ().scss and .svg files, public locale files, etc.) to the lib directory for inclusion in the webpack. + const symlinkSourceResourcesResult = this.symlinkSourceResources(); + + // wait for all of those operations to finish. + return Promise.all([compileResult, symlinkSourceResourcesResult]) + } + ------------------------ */ + // step two - parallel webpack and symlink of external modules needed for applications + async webpackAndSymlinkExternalModules() { + // webpack the module. + const webpackResults = this.webpackModule(); + // If this is an application, symlink the required external modules to the webresources directory. + const symlinkExternalModulesResult = this.symlinkRequiredExternalModules(); + // wait for the webpack and external modules operations to finish. + return Promise.all([webpackResults, symlinkExternalModulesResult]); + } + // this is the method that sequences the build. + async sequenceBuild() { + const symlinkResults = await this.symlinkSourceResources(); + let exitCode = this.reportResults([symlinkResults]); + if (0 !== exitCode) + return exitCode; + const compileResults = await this.compileSource(); + exitCode = this.reportResults([compileResults]); + if (0 !== exitCode) + return exitCode; + const stepTwoResults = await this.webpackAndSymlinkExternalModules(); + exitCode = this.reportResults(stepTwoResults); + if (0 !== exitCode) + return exitCode; + const pluginResults = await this.buildSubModules(); + exitCode = this.reportResults(pluginResults); + if (0 !== exitCode) + return exitCode; + const makeConfigResult = await this.makeConfig(); + exitCode = this.reportResults([makeConfigResult]); + if (0 != exitCode) + return exitCode; + const pseudoLocalizeResult = this.pseudoLocalize(); + exitCode = this.reportResults([pseudoLocalizeResult]); + return exitCode; + } +} +// build iModelJs module according to the specifications found in package.json's iModelJs.buildModule property. +// The package.iModelJs.buildModule property has these keys: +// type - (required) string with value of "system", "application", or "plugin". +// detail - (optional) number between 0 and 4. The greater of module.detail and args.detail is used. +// sourceResources - (optional) object array with each object containing a "source" and "dest" key with the specifications of files to symlink from +// source to dest. Typically something like { "source": "./src/**/*.scss", "dest": ".lib"}; Usually used for application and plugin types. +// tscOptions - (optional) options to pass to the tsc command line. Usually, the build just does "tsc" and uses tsconfig.json for all options. +// webpack - (optional) object with properties: +// "dest" - (required) output directory for webpacked bundle. Typically "lib/module" for modules and "lib/webresources" for applications and plugins. +// "entry" - (required) entry script file point for webpack to start. Typicall "lib/index.js" or "lib/main.js". +// "bundleName" - (required) the name of the bundle. For applications, "main.js". For modules, the name of the module. For plugins, the name of the plugin. +// "styleSheets" - (optional) if present and true, indicates that webpack needs to use the stylesheet loading plugins. Slows webpack down considerably. +// "htmlTemplate" - (optional) For applications only, uses the specified html file as a template to create the webpage's html file. +// +async function main() { + const cmdLineArgs = getArgs(); + const packageContents = Utils.readPackageFileContents(process.cwd()); + // figure out the detail level. + let detail = 0; + if (cmdLineArgs.verbose) + detail = 1; + if (cmdLineArgs.detail) + detail = (cmdLineArgs.detail === true) ? 1 : cmdLineArgs.detail; + // package.json can increase detail level. + if (packageContents.iModelJs && packageContents.iModelJs.buildModule.detail && (packageContents.iModelJs.buildModule.detail > detail)) + detail = packageContents.iModelJs.buildModule.detail; + // report command line. + if (detail > 0) { + console.log("detail:", detail); + console.log("production:", cmdLineArgs.production); + console.log("stats:", cmdLineArgs.stats); + } + if (!packageContents.iModelJs || !packageContents.iModelJs.buildModule) { + console.log("Building an iModel.js module requires an iModelJs.buildModule entry in package.json"); + return 1; + } + // instantiate the builder + const isDevelopment = !cmdLineArgs.production; + const doStats = cmdLineArgs.stats; + const builder = new IModelJsModuleBuilder(packageContents.iModelJs.buildModule, detail, isDevelopment, doStats); + if (builder.checkDefinition()) + return 1; + return await builder.sequenceBuild(); +} +main().then((exitCode) => { process.exit(exitCode); }); +//# sourceMappingURL=buildIModelJsModule.js.map \ No newline at end of file diff --git a/tools/webpack/config/wdio/wdio.config.electron.js b/tools/webpack/config/wdio/wdio.config.electron.js index 432dfb1..248e117 100644 --- a/tools/webpack/config/wdio/wdio.config.electron.js +++ b/tools/webpack/config/wdio/wdio.config.electron.js @@ -19,7 +19,8 @@ exports.config = mergeWdioConfigs(base.config, { chromeOptions: { binary: require.resolve("electron/dist/electron.exe"), args: [ - "--disable-gpu", + "disable-gpu", + "remote-debugging-port=54199", `app=${paths.appBuiltMainJs}`, ] }, diff --git a/tools/webpack/config/webpack.config.test.js b/tools/webpack/config/webpack.config.test.js index 499c89f..cea78b5 100644 --- a/tools/webpack/config/webpack.config.test.js +++ b/tools/webpack/config/webpack.config.test.js @@ -114,7 +114,7 @@ const config = { // Compile .tsx? { test: /\.(ts|tsx)$/, - exclude: /(node_modules|bower_components)/, + exclude: /(node_modules|bower_components|\.d\.ts)/, use: { loader: require.resolve("ts-loader"), options: { diff --git a/tools/webpack/index.d.ts b/tools/webpack/index.d.ts index e88bc6a..801e3d2 100644 --- a/tools/webpack/index.d.ts +++ b/tools/webpack/index.d.ts @@ -10,7 +10,6 @@ /// /// -declare var React: typeof import("react"); declare var sinon: typeof import("sinon"); declare var expect: Chai.ExpectStatic; declare var shallow: typeof import("enzyme").shallow; diff --git a/tools/webpack/modules/buildIModelJsModule.ts b/tools/webpack/modules/buildIModelJsModule.ts new file mode 100644 index 0000000..17214a6 --- /dev/null +++ b/tools/webpack/modules/buildIModelJsModule.ts @@ -0,0 +1,955 @@ +#!/usr/bin/env node + +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +// ------------------------------- NOTE ----------------------------------- +// Do not edit buildIModelJsModule.js !!! +// It is compiled from buildIModelJsModule.ts and is under source code control +// so it can be reliably made into an npm binary (see "bin" in package.json). +// ------------------------------- NOTE ----------------------------------- + +import * as path from "path"; +import * as fs from "fs"; +import * as yargs from "yargs"; +import * as child_process from "child_process"; +import * as glob from "glob"; + + +// Get the arguments using the ubiquitous yargs package. +function getArgs(): any { + const args = yargs + .wrap(120) + .option("production", { + alias: ["prod", "p"], + describe: "Build production version of module", + default: false, + type: "boolean" + }) + .option("verbose", { + alias: ["v"], + describe: "Show detail level 1", + default: false, + type: "boolean" + }) + .option("detail", { + alias: "d", + options: [0, 1, 2, 3, 4, 5], + type: "number", + default: 0, + describe: "Sets detail level (0 to 4) to reveal more about the build process" + }) + .option("stats", { + alias: "s", + type: "boolean", + default: false, + describe: "Creates the webpack stats json file for system modules" + }) + .help().argv; + + return args; +} + +// Utilities for file system operations +class Utils { + public static readPackageFileContents(packageRoot: string): any { + const packageFileName = path.resolve(packageRoot, "./package.json"); + if (!fs.existsSync(packageFileName)) + return {}; + + const packageFileContents = fs.readFileSync(packageFileName, "utf8"); + return JSON.parse(packageFileContents); + } + + public static symlinkFiles(cwd: string, source: string, dest: string, alwaysCopy: boolean, detail: number): Result { + // first we must create the destination directory, if it isn't already there. + const sourceSpecification: string = path.resolve(cwd, source); + let sourceDirectory: string = path.dirname(sourceSpecification); + if (sourceDirectory.endsWith("**")) { + sourceDirectory = sourceDirectory.slice(0, sourceDirectory.length - 3); + } + + const destinationPath: string = path.resolve(cwd, dest); + try { + Utils.makeDirectoryNoError(destinationPath); + + const found: string[] = glob.sync(sourceSpecification, { nodir: true }); + + for (const fileName of found) { + // find it relative to source. + const relativePath = path.relative(sourceDirectory, fileName); + const outputPath = path.resolve(destinationPath, relativePath); + if (fs.existsSync(outputPath)) { + if (detail > 3) + console.log(" File", outputPath, "already exists"); + } else { + if (detail > 3) + console.log(alwaysCopy ? " Copying" : " Symlinking", fileName, "to", outputPath); + Utils.makeDirectoryNoError(path.dirname(outputPath)); + if (alwaysCopy) + fs.copyFileSync(fileName, outputPath); + else + fs.symlinkSync(fileName, outputPath); + } + } + } catch (error) { + return new Result("Symlink or Copy Source Resources", 1, error); + } + return new Result("Symlink or Copy Source Resources", 0); + } + + // we use this where there is a possible race condition where the compiler might be creating directories in parallel to our linking. + public static makeDirectoryNoError(outDirectory: string) { + // Note: mkdirSync with option of { recursive: true } did not work on Linux, necessitating this workaround/ + const directoriesToCreate: string[] = []; + + // work backwards through the outDirectory to find the first one that exists. + let thisDirectory = outDirectory; + try { + while (!fs.existsSync(thisDirectory)) { + directoriesToCreate.push(thisDirectory); + const parsedPath = path.parse(thisDirectory); + thisDirectory = parsedPath.dir; + } + + let createDir; + while (createDir = directoriesToCreate.pop()) { + fs.mkdirSync(createDir); + } + + } catch (_error) { + // do nothing on error. + } + } +} + +// description of each node_module. +class ModuleInfo { + public destFileName: string; + public relativePath: string; + + constructor(public isDevelopment: boolean, public useVersion: boolean, public moduleName: string, destFileName: string | undefined, relativePath?: string | undefined, public publicResourceDirectory?: string | undefined) { + // if relativePath not supplied, it's one of our @bentley modules and we can figure it out. + this.destFileName = destFileName ? destFileName : moduleName + ".js"; + if (!relativePath) { + this.relativePath = path.join(moduleName, isDevelopment ? "lib/module/dev" : "lib/module/prod", this.destFileName); + } else { + this.relativePath = relativePath; + } + } +} + +// keeps track of each dependent's information. +class DependentInfo { + constructor(public name: string, public packageRoot: string, public parentPackageRoot: string, public externalModule: ModuleInfo, public version: string) { + } +} + +// class that copies (or symlinks) the external modules needed my iModel.js into the web resources directory. +class ModuleCopier { + private _dependentList: DependentInfo[]; + private _externalModules: ModuleInfo[]; + + // these are all modules that are listed as external in our webpack configuration, and therefore need to be copied to the web resources directory. + constructor(private _nodeModulesDirectory: string, private _isDevelopment: boolean, private _detail: number, private _alwaysCopy: boolean) { + this._dependentList = []; + this._externalModules = [ + new ModuleInfo(_isDevelopment, true, "@bentley/bentleyjs-core", "bentleyjs-core.js", undefined), + new ModuleInfo(_isDevelopment, true, "@bentley/geometry-core", "geometry-core.js", undefined), + new ModuleInfo(_isDevelopment, true, "@bentley/imodeljs-i18n", "imodeljs-i18n.js", undefined), + new ModuleInfo(_isDevelopment, true, "@bentley/imodeljs-clients", "imodeljs-clients.js", undefined), + new ModuleInfo(_isDevelopment, true, "@bentley/imodeljs-common", "imodeljs-common.js", undefined), + new ModuleInfo(_isDevelopment, true, "@bentley/imodeljs-quantity", "imodeljs-quantity.js", undefined), + new ModuleInfo(_isDevelopment, true, "@bentley/imodeljs-frontend", "imodeljs-frontend.js", undefined, "lib/public"), + new ModuleInfo(_isDevelopment, true, "@bentley/ui-core", "ui-core.js", undefined, "lib/public"), + new ModuleInfo(_isDevelopment, true, "@bentley/ui-components", "ui-components.js", undefined, "lib/public"), + new ModuleInfo(_isDevelopment, true, "@bentley/ui-framework", "ui-framework.js", undefined, "lib/public"), + new ModuleInfo(_isDevelopment, true, "@bentley/ui-ninezone", "ui-ninezone.js", undefined), + new ModuleInfo(_isDevelopment, true, "@bentley/presentation-common", "presentation-common.js", undefined), + new ModuleInfo(_isDevelopment, true, "@bentley/presentation-components", "presentation-components.js", undefined, "lib/public"), + new ModuleInfo(_isDevelopment, true, "@bentley/presentation-frontend", "presentation-frontend.js", undefined), + new ModuleInfo(_isDevelopment, false, "react", undefined, path.join("react", _isDevelopment ? "umd/react.development.js" : "umd/react.production.min.js")), + new ModuleInfo(_isDevelopment, false, "react-dnd", undefined, path.join("react-dnd", _isDevelopment ? "dist/ReactDnD.js" : "dist/ReactDnD.min.js")), + new ModuleInfo(_isDevelopment, false, "react-dnd-html5-backend", undefined, path.join("react-dnd-html5-backend", _isDevelopment ? "dist/ReactDnDHTML5Backend.js" : "dist/ReactDnDHTML5Backend.min.js")), + new ModuleInfo(_isDevelopment, false, "react-dom", undefined, path.join("react-dom", _isDevelopment ? "umd/react-dom.development.js" : "umd/react-dom.production.min.js")), + new ModuleInfo(_isDevelopment, false, "react-redux", undefined, path.join("react-redux", _isDevelopment ? "dist/react-redux.js" : "dist/react-redux.min.js")), + new ModuleInfo(_isDevelopment, false, "redux", undefined, path.join("redux", _isDevelopment ? "dist/redux.js" : "dist/redux.min.js")), + new ModuleInfo(_isDevelopment, false, "inspire-tree", undefined, path.join("inspire-tree", _isDevelopment ? "dist/inspire-tree.js" : "dist/inspire-tree.min.js")), + new ModuleInfo(_isDevelopment, false, "lodash", undefined, path.join("lodash", _isDevelopment ? "lodash.js" : "lodash.min.js")), + ]; + } + + // symlinks the public static files from a module into the output web resources directories. + private symlinkOrCopyPublicStaticFiles(sourcePublicDirectory: string, outputPublicDirectory: string) { + const symlinkSource = `${sourcePublicDirectory}/**/*`; + Utils.symlinkFiles(process.cwd(), symlinkSource, outputPublicDirectory, this._alwaysCopy, this._detail); + } + + // checks contents of existing symlink and replaces it if necessary + private ensureSymlink(sourceFile: string, outFilePath: string): number { + + try { + if (fs.existsSync(outFilePath)) { + try { + const linkContents = fs.readlinkSync(outFilePath, { encoding: "utf8" }); + if (linkContents === sourceFile) { + if (this._detail > 3) + console.log(" File", outFilePath, "already exists"); + return 0; + } + } catch (_error) { + // It's not a link, do nothing and let it get deleted. + } + if (this._detail > 3) + console.log(" Removing existing symlink found in", outFilePath); + fs.unlinkSync(outFilePath); + } + if (this._detail > 3) + console.log(" Symlinking", sourceFile, "to", outFilePath); + fs.symlinkSync(sourceFile, outFilePath); + } catch (error) { + console.log(error); + return 1; + } + return 0; // success + } + + // symlinks the module file and source map file if available. + private symlinkOrCopyModuleFile(moduleSourceFile: string, outFilePath: string) { + if (this._alwaysCopy) { + // copy the module file. + if (this._detail > 3) + console.log(" Copying file", moduleSourceFile, "to", outFilePath); + + // if there is a symlink already there, copyFileSync fails, so check for that case. + if (fs.existsSync(outFilePath)) + fs.unlinkSync(outFilePath) + + fs.copyFileSync(moduleSourceFile, outFilePath); + + // if there's a source map file, link that, too. + const mapFile = moduleSourceFile + ".map"; + + if (fs.existsSync(mapFile)) { + const outMapFile = outFilePath + ".map"; + if (this._detail > 3) + console.log(" Copying file", mapFile, "to", outMapFile); + + // if there is a symlink already there, copyFileSync fails, so check for that case. + if (fs.existsSync(outMapFile)) + fs.unlinkSync(outMapFile) + + fs.copyFileSync(mapFile, outMapFile); + } + } else { + // symlink the module file. + this.ensureSymlink(moduleSourceFile, outFilePath); + + // if there's a source map file, link that, too. + const mapFile = moduleSourceFile + ".map"; + const outMapFile = outFilePath + ".map"; + if (fs.existsSync(mapFile)) + this.ensureSymlink(mapFile, outMapFile); + } + } + + // this function adds the dependencies found in package.json that are external modules. Recurses, but only to depth 1. + private findExternalModuleDependents(parentPackageRoot: string, depth: number) { + const packageFileContents: any = Utils.readPackageFileContents(parentPackageRoot); + + // find new dependents from this packageFileContents + const newDependents: DependentInfo[] = []; + for (const dependent in packageFileContents.dependencies) { + // see if we already have this dependent. + if (undefined !== this._dependentList.find((existingDependent) => { return (existingDependent.name === dependent); })) { + continue; + } + // we only care about external modules and their dependents that might be external modules. + for (const externalModule of this._externalModules) { + if (externalModule.moduleName === dependent) { + const dependentPackageRoot = path.resolve(parentPackageRoot, "node_modules", dependent); + newDependents.push(new DependentInfo(dependent, dependentPackageRoot, parentPackageRoot, externalModule, packageFileContents.dependencies[dependent])); + } + } + } + + // add the newly discovered dependents to the master list of dependents. + for (const newDependent of newDependents) { + this._dependentList.push(newDependent); + } + + // we need to check the first level of dependents of our direct dependents to find the non-imodeljs dependencies like lodash, react, redux, etc. + if (depth < 2) { + for (const newDependent of newDependents) { + this.findExternalModuleDependents(newDependent.packageRoot, depth + 1); + } + } + } + + // finds peerDependencies and makes sure they are in the list of dependencies. + private checkPeerDependencies(): string[] { + const missingList: string[] = []; + for (const thisDependent of this._dependentList) { + const packageFileContents = Utils.readPackageFileContents(thisDependent.packageRoot); + for (const peerDependent in packageFileContents.peerDependencies) { + // don't bother to a peerDependent twice. + if (-1 !== missingList.indexOf(peerDependent)) + continue; + + // we only report miss peerDependencies of our external modules. + if (undefined === (this._externalModules.find((externalModule) => { return (externalModule.moduleName === peerDependent); }))) + continue; + + if (undefined === this._dependentList.find((thisDependent) => { return (thisDependent.name === peerDependent); })) { + if (this._detail > 0) + console.log(" Dependent", thisDependent.name, "requires a peerDependency of", peerDependent, "but none found. Add ", peerDependent, "to dependencies in package.json"); + missingList.push(peerDependent); + } + } + } + return missingList; + } + + // finds the source module file. It is either relative to our localNodeModules, or relative to the dependent path where we found the package file. + private findExternalModuleFile(dependent: DependentInfo, relativePath: string) { + const localNodeModuleFile = path.resolve(this._nodeModulesDirectory, relativePath); + if (fs.existsSync(localNodeModuleFile)) + return localNodeModuleFile; + + const dependentNodeModuleFile = path.resolve(dependent.parentPackageRoot, "node_modules", relativePath); + if (fs.existsSync(dependentNodeModuleFile)) + return dependentNodeModuleFile; + + return undefined; + } + + // finds dependents, symlinks them to destination directory. + public symlinkOrCopyExternalModules(outputDirectory: string): Result { + if (this._detail > 0) + console.log("Starting symlink or copy external modules"); + + try { + // create the output directory, if it doesn't exist. + if (!fs.existsSync(outputDirectory)) { + fs.mkdirSync(outputDirectory, { recursive: true }); + } + + // Read the package file for the current directory, and add the dependents recursively (to depth 2) to the sub-dependencies of the iModelJs modules. + this.findExternalModuleDependents(process.cwd(), 0); + const missingPeerDependencies = this.checkPeerDependencies(); + if (missingPeerDependencies.length > 0) + return new Result("Symlink or Copy External Modules", 1, undefined, undefined, "You are missing one or more dependencies in package.json: \n".concat(...missingPeerDependencies)); + + let missingModule = false; + const missingModuleList: string[] = []; + for (const dependent of this._dependentList) { + const externalModule = dependent.externalModule; + const versionString = (externalModule.useVersion) ? dependent.version : undefined; + + const moduleSourceFile = this.findExternalModuleFile(dependent, externalModule.relativePath); + + // report all the module files we can't find. + if (!moduleSourceFile) { + missingModuleList.push(`Unable to locate required module file ${externalModule.moduleName} - looking for relative path ${externalModule.relativePath}\n`); + missingModule = true; + } else { + let outFilePath = outputDirectory; + if (versionString) { + outFilePath = path.resolve(outputDirectory, "v" + versionString); + // create subdirectory if needed. + if (!fs.existsSync(outFilePath)) { + fs.mkdirSync(outFilePath, { recursive: true }); + } + } + const fullFilePath = path.resolve(outFilePath, externalModule.destFileName); + this.symlinkOrCopyModuleFile(moduleSourceFile, fullFilePath); + + // symlink any subModules in the build. + const packageFileContents: any = Utils.readPackageFileContents(dependent.packageRoot); + if (packageFileContents.iModelJs && packageFileContents.iModelJs.buildModule && packageFileContents.iModelJs.buildModule.subModules && Array.isArray(packageFileContents.iModelJs.buildModule.subModules)) { + for (const subModule of packageFileContents.iModelJs.buildModule.subModules) { + if (subModule.bundleName) { + const parsedPath = path.parse(moduleSourceFile); + const thisDirectory = parsedPath.dir; + const subModuleFileName = subModule.bundleName + ".js"; + const subModuleSourceFile = path.resolve(thisDirectory, subModuleFileName); + const destFullFilePath = path.resolve(outFilePath, subModuleFileName); + this.symlinkOrCopyModuleFile(subModuleSourceFile, destFullFilePath); + } + } + } + + // symlink the external modules resource files if necessary. + if (externalModule.publicResourceDirectory) { + const publicPath = path.resolve(dependent.packageRoot, externalModule.publicResourceDirectory); + this.symlinkOrCopyPublicStaticFiles(publicPath, outputDirectory); + } + } + } + + if (missingModule) { + return new Result("Symlink or Copy External Modules", 1, undefined, undefined, "Could not find one or more dependencies:\n".concat(...missingModuleList)); + } + + // link the IModelJsLoader.js from imodeljs/frontend also. NOTE: imodeljs-frontend must always be in package.json's dependencies. + const loaderFile = path.resolve(process.cwd(), "node_modules/@bentley/imodeljs-frontend", this._isDevelopment ? "lib/module/dev/IModelJsLoader.js" : "lib/module/prod/IModelJsLoader.js"); + this.symlinkOrCopyModuleFile(loaderFile, path.resolve(outputDirectory, "iModelJsLoader.js")); + + } catch (e) { + return new Result("Symlink or Copy External Modules", 1, e); + } + return new Result("Symlink or Copy External Modules", 0); + } +} + +// this class creates pseudo-localized versions of all the locale .json files specified in the sourceDirectory. +class PseudoLocalizer { + constructor(private _sourceDirectory: string, private _destDirectory: string, private _detail: number) { } + + static replacements: any = { + A: "\u00C0\u00C1,\u00C2\u00C3,\u00C4\u00C5", + a: "\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5", + B: "\u00DF", + c: "\u00A2\u00E7", + C: "\u00C7\u0028", + D: "\u00D0", + E: "\u00C8\u00C9\u00CA\u00CB", + e: "\u00E8\u00E9\u00EA\u00EB", + I: "\u00CC\u00CD\u00CE\u00CF", + i: "\u00EC\u00ED\u00EE\u00EF", + L: "\u00A3", + N: "\u00D1", + n: "\u00F1", + O: "\u00D2\u00D3\u00D4\u00D5\u00D6", + o: "\u00F2\u00F3\u00F4\u00F5\u00F6\u00F8", + S: "\u0024\u00A7", + U: "\u00D9\u00DA\u00DB\u00DC", + u: "\u00B5\u00F9\u00FA\u00FB\u00FC", + x: "\u00D7", + Y: "\u00DD\u00A5", + y: "\u00FD\u00FF", + }; + + // pseudoLocalizes a string + private convertString(inputString: string): string { + let inReplace = 0; + let outString = ""; + let replaceIndex = 0; // Note: the pseudoLocalize algorithm in Bim02 uses random, but here we cycle through because Javascript doesn't allow setting of the seed for Math.random. + for (let iChar = 0; iChar < inputString.length; iChar++) { + let thisChar = inputString.charAt(iChar); + let nextChar = ((iChar + 1) < inputString.length) ? inputString.charAt(iChar + 1) : 0; + + // handle the {{ and }} delimiters for placeholders - don't want to do anything to characters in between. + if (('{' === thisChar) && ('{' === nextChar)) { + inReplace++; + iChar++; + outString = outString.concat("{{"); + } else if (('}' === thisChar) && ('}' === nextChar) && (inReplace > 0)) { + inReplace--; + iChar++; + outString = outString.concat("}}"); + } else { + let replacementChar = thisChar; + if (0 === inReplace) { + let replacementsForChar = PseudoLocalizer.replacements[thisChar]; + if (undefined !== replacementsForChar) { + replacementChar = replacementsForChar.charAt(replaceIndex++ % replacementsForChar.length); + } + } + outString = outString.concat(replacementChar); + } + } + return outString; + } + + // converts the JSON object + private convertObject(objIn: any): any { + const objOut: any = {}; + for (const prop in objIn) { + if (objIn.hasOwnProperty(prop)) { + if (typeof objIn[prop] === "string") { + objOut[prop] = this.convertString(objIn[prop]) + } else if (typeof objIn[prop] === "object") { + objOut[prop] = this.convertObject(objIn[prop]) + } + } + } + return objOut; + } + + // converts each JSON file. + private convertFile(inputFilePath: string, outputFile: string): number { + // read the file + let jsonIn = fs.readFileSync(inputFilePath, { encoding: "utf8" }); + let objIn = JSON.parse(jsonIn); + + let objOut = this.convertObject(objIn); + fs.writeFileSync(outputFile, JSON.stringify(objOut, null, 2)); + + return 0; + } + + public convertAll(): Result { + try { + Utils.makeDirectoryNoError(this._destDirectory); + + const sourceSpecification: string = path.join(this._sourceDirectory, "**/*.json"); + const found: string[] = glob.sync(sourceSpecification, { nodir: true }); + + for (const fileName of found) { + // find it relative to source. + const relativePath = path.relative(this._sourceDirectory, fileName); + const outputPath = path.resolve(this._destDirectory, relativePath); + if (fs.existsSync(outputPath)) { + if (this._detail > 3) + console.log(" File", outputPath, "already exists"); + } else { + if (this._detail > 3) + console.log(" PseudoLocalizing", fileName, "to", outputPath); + Utils.makeDirectoryNoError(path.dirname(outputPath)); + this.convertFile(fileName, outputPath); + } + } + } catch (error) { + return new Result("PseudoLocalize", 1, error); + } + return new Result("PseudoLocalize", 0); + } +} + +// Class that holds the results of a build operation. +class Result { + public constructor(public operation: string, public exitCode: number, public error?: any, public stdout?: string | undefined, public stderr?: string | undefined) { + } +} + + +// Class that contains a method for each step in building an iModelJs module. +class IModelJsModuleBuilder { + private _alwaysCopy: boolean; + + // constructor + constructor(private _moduleDescription: any, private _detail: number, private _isDevelopment: boolean, private _webpackStats: boolean) { + this._alwaysCopy = process.env.BUILDIMODEL_SYMLINKS === undefined; + } + + // checks the iModelJs buildModule property. + public checkDefinition(): boolean { + // check the type. + if (!this._moduleDescription.type) { + console.log('iModelJs.buildModule.type must have a type property with value of "system", "application", "plugin", or "webworker"'); + return true; + } + if ((this._moduleDescription.type !== "system") && (this._moduleDescription.type !== "application") && + (this._moduleDescription.type !== "plugin") && (this._moduleDescription.type != "webworker")) { + console.log('iModelJs.buildModule.type must be one of "system", "application", "plugin", or "webworker"'); + return true; + } + return false; + } + + // compiles the tsc source according to tsconfig.js. (generally, compiles src/**/*.ts to lib) + private compileSource(): Promise { + + // The version of typescript required must be specified in package.json devDependencies. + // In rush monorepos, it can be specified in the rush common/config/rush/common-version.json file. + // npm (or rush) install mechanism puts tsc into the node_modules/.bin directory where npm will find it. + + // Note: I tried setting shell: true in the options (third) argument to execFile, and then just specifying + // "tsc" (rather than tsc or tsc.cmd based on platform). Unfortunately, that wasn't reliable. + return new Promise((resolve, _reject) => { + if (this._detail > 0) + console.log("Starting tsc compilation"); + + const tscCommand: string = process.platform === "win32" ? "tsc.cmd" : "tsc"; + const tscFullPath = path.resolve(process.cwd(), "node_modules", ".bin", tscCommand); + const args = []; + if (this._moduleDescription.tscOptions) { + const tscArgs = this._moduleDescription.tscOptions.split(" "); + for (const tscArg of tscArgs) + args.push(tscArg); + } + args.push("1>&2"); + child_process.execFile(tscFullPath, args, { cwd: process.cwd(), shell: true } as any, (error: Error | null, stdout: string, stderr: string) => { + if (this._detail > 0) + console.log("Finished compilation"); + resolve(new Result("Compile .tsc files", (null !== error) || (stderr && stderr.length) ? 1 : 0, error, stdout, stderr)); + }) + }) + } + + // symlinks the web resources (like .scss and .svg files) into the lib directory for webpack to use later. + private async symlinkSourceResources(): Promise { + + // are there any files to symlink? + if (!this._moduleDescription.sourceResources) { + if (this._detail > 2) + console.log("Skipping Symlink Source Resource, no iModelJs.buildModule.sourceResources property"); + return Promise.resolve(new Result("Symlink Or Copy Source Resources", 0)); + } + + // otherwise this should be an array of {source, dest} objects. + if (!Array.isArray(this._moduleDescription.sourceResources)) + return Promise.resolve(new Result("Symlink Or Copy Source Resources", 1, undefined, undefined, "iModelJs.buildModule.sourceResources must be an array of {source, dest} pairs")); + + for (const resource of this._moduleDescription.sourceResources) { + if (!resource.source || !resource.dest) { + return Promise.resolve(new Result("Symlink Or Copy Source Resources", 1, undefined, undefined, "iModelJs.buildModule.sourceResources must be an array of {source, dest} pairs")); + } + + if (this._detail > 0) + console.log(this._alwaysCopy ? "Copying files from" : "Symlinking files from", resource.source, "to", resource.dest); + + const alwaysCopy = this._alwaysCopy || (resource.copy && resource.copy === true); + const result = Utils.symlinkFiles(process.cwd(), resource.source, resource.dest, alwaysCopy, this._detail); + if (0 != result.exitCode) + return result; + } + return Promise.resolve(new Result("Symlink or Copy Source Resources", 0)); + } + + // If this is an application, symlink the external modules that the application uses into the web server's directory. + private symlinkRequiredExternalModules(): Promise { + const nodeModulesDir: string = path.resolve(process.cwd(), "node_modules"); + const moduleCopier = new ModuleCopier(nodeModulesDir, this._isDevelopment, this._detail, this._alwaysCopy); + if (this._moduleDescription.type !== "application") + return Promise.resolve(new Result("Symlink or Copy External Modules", 0)); + if (!this._moduleDescription.webpack || !this._moduleDescription.webpack.dest) + return Promise.resolve(new Result("Symlink Or Copy External Modules", 0)); + const outputDirectory: string = path.resolve(process.cwd(), this._moduleDescription.webpack.dest); + return Promise.resolve(moduleCopier.symlinkOrCopyExternalModules(outputDirectory)); + } + + private makeConfig(): Promise { + if (!this._moduleDescription.makeConfig) + return Promise.resolve(new Result("makeConfig", 0)); + if (!this._moduleDescription.makeConfig.dest) + return Promise.resolve(new Result("makeConfig", 1, undefined, undefined, 'The iModelJs.buildModule.makeConfig must have a "dest" property')); + + const makeConfigFullPath = path.resolve(process.cwd(), "node_modules/@bentley/webpack-tools/node_modules/@bentley/config-loader/scripts/write.js"); + const args: string[] = [makeConfigFullPath, this._moduleDescription.makeConfig.dest]; + if (this._moduleDescription.makeConfig.filter) + args.push(this._moduleDescription.makeConfig.filter); + + if (this._detail > 0) + console.log("Starting makeConfig"); + + return new Promise((resolve, _reject) => { + child_process.execFile("node", args, { cwd: process.cwd() }, (error: Error | null, stdout: string, stderr: string) => { + if (this._detail > 0) + console.log("Finished makeConfig"); + resolve(new Result("makeConfig", (null !== error) || (stderr && stderr.length) ? 1 : 0, error, stdout, stderr)); + }) + }); + } + + // find webpack executable. + private findWebpack(): string | undefined { + // first look in node_modules/webpack + const webpackCommand: string = process.platform === "win32" ? "webpack.cmd" : "webpack"; + const inLocalNodeModules = path.resolve (process.cwd(), "node_modules/.bin", webpackCommand); + if (fs.existsSync (inLocalNodeModules)) + return inLocalNodeModules; + + const inToolsWebpackNodeModules = path.resolve(process.cwd(), "node_modules/@bentley/webpack-tools/node_modules/.bin", webpackCommand); + if (fs.existsSync (inToolsWebpackNodeModules)) + return inToolsWebpackNodeModules; + + return undefined; + } + + // spawns a webpack process + private startWebpack(operation: string, outputPath: string, entry: string, bundleName: string, styleSheets: boolean, buildType: string, isDevelopment: boolean, doStats: boolean, htmlTemplate?: string): Promise { + const webpackFullPath = this.findWebpack(); + if (!webpackFullPath) { + return Promise.resolve(new Result(operation, 1, undefined, undefined, "Unable to locate webpack")); + } + + const args: string[] = []; + const configPath = path.resolve(process.cwd(), "node_modules/@bentley/webpack-tools/modules/webpackModule.config.js"); + args.push(`--config=${configPath}`); + args.push(`--env.outdir=${outputPath}`); + args.push(`--env.entry=${entry}`); + args.push(`--env.bundlename=${bundleName}`); + if (styleSheets) + args.push("--env.stylesheets"); + + if (buildType === "plugin") + args.push("--env.plugin"); + else if (buildType === "webworker") + args.push("--env.webworker"); + + if (!isDevelopment) + args.push("--env.prod"); + if (htmlTemplate) + args.push(`--env.htmltemplate=${htmlTemplate}`); + if (doStats) { + // make sure the output directory exists. + if (!fs.existsSync(outputPath)) { + fs.mkdirSync(outputPath, { recursive: true }); + } + const jsonFile: string = path.resolve(outputPath, "webpackStats.json"); + args.push("--json"); + args.push(">" + jsonFile); + } + + return new Promise((resolve, _reject) => { + child_process.execFile(webpackFullPath, args, { cwd: process.cwd() }, (error: Error | null, stdout: string, stderr: string) => { + if (this._detail > 0) + console.log("Finished", operation); + resolve(new Result(operation, (null !== error) || (stderr && stderr.length) ? 1 : 0, error, stdout, stderr)); + }) + }); + } + + // Webpack the module. + private webpackModule(): Promise { + // if no webpack property, skip it. + if (!this._moduleDescription.webpack) { + if (this._detail > 2) + console.log("Skipping Webpack, no iModelJs.buildModule.webpack property"); + return Promise.resolve(new Result("Webpack", 0)); + } + + // make sure the required keys are there. + const webpack: any = this._moduleDescription.webpack; + if (!webpack.dest || !webpack.entry || !webpack.bundleName) { + return Promise.resolve(new Result("Webpack", 1, undefined, undefined, 'IModelJs.buildModule.webpack must have "dest", "entry", and "bundleName" properties')); + } + + let outputPath = path.resolve(process.cwd(), webpack.dest); + if (this._moduleDescription.type === "system") { + outputPath = path.resolve(outputPath, this._isDevelopment ? "dev" : "prod"); + } + + // start the webpack process according to the arguments. + const styleSheets: boolean = webpack.styleSheets ? true : false; + if (this._detail > 0) + console.log("Starting Webpack Module"); + return this.startWebpack("Webpack Module", outputPath, webpack.entry, webpack.bundleName, styleSheets, this._moduleDescription.type, this._isDevelopment, this._webpackStats, webpack.htmlTemplate); + } + + // build the array of subModules. + private async buildSubModules(): Promise { + if (!this._moduleDescription.subModules) { + if (this._detail > 2) + console.log("Skipping Build SubModules - No iModelJs.buildModule.subModules property"); + return Promise.resolve([new Result("Build SubModules", 0)]); + } + + if (!Array.isArray(this._moduleDescription.subModules)) { + return Promise.resolve([new Result("Build SubModules", 1, undefined, undefined, "iModelJs.buildModule.subModules must be an array of {dest, entry, bundleName} objects")]); + } + + const results: Result[] = []; + for (const subModule of this._moduleDescription.subModules) { + if (!subModule.dest || !subModule.entry || !subModule.bundleName) { + results.push(new Result("Build SubModules", 1, undefined, undefined, 'Each subModule must have a "dest", "entry", and "bundleName" property')); + return Promise.resolve(results); + } + + const styleSheets: boolean = subModule.styleSheets ? true : false; + // this is a special case for the iModelJsLoader - set plugin.type to "system" or "webworker" to avoid plugin treatment. + const subType: string = subModule.type || "plugin"; + if ((subType !== "system") && (subType !== "plugin") && (subType != "webworker")) { + console.log('the "type" property for a subModule must be one of "system", "plugin", or "webworker"'); + } + + let outputPath = path.resolve(process.cwd(), subModule.dest); + if ((subType === "system") || (subType === "webworker")) + outputPath = path.resolve(outputPath, this._isDevelopment ? "dev" : "prod"); + + if (this._detail > 0) + console.log("Starting webpack of", subModule.entry); + + const pluginResult: Result = await this.startWebpack(`Webpack Plugin ${subModule.entry}`, outputPath, subModule.entry, subModule.bundleName, styleSheets, subType, this._isDevelopment, this._webpackStats); + results.push(pluginResult); + if (pluginResult.error || pluginResult.stderr) { + return Promise.resolve(results); + } + } + return Promise.resolve(results); + } + + private pseudoLocalize(): Result { + if (!this._moduleDescription.pseudoLocalize) { + if (this._detail > 2) + console.log("Skipping Symlink Source Resource, no iModelJs.buildModule.sourceResources property"); + return new Result("Pseudo localize", 0); + } + if (!this._moduleDescription.pseudoLocalize.source || !this._moduleDescription.pseudoLocalize.dest) { + return new Result("Pseudo localize", 1, undefined, undefined, 'IModelJs.buildModule.pseudoLocalize must have "source" and "dest" properties'); + } + + const sourceDirectory = path.resolve(process.cwd(), this._moduleDescription.pseudoLocalize.source); + const destDirectory = path.resolve(process.cwd(), this._moduleDescription.pseudoLocalize.dest); + if (this._detail > 0) + console.log("Starting pseudoLocalize"); + + const pseudoLocalizer = new PseudoLocalizer(sourceDirectory, destDirectory, this._detail); + if (this._detail > 0) + console.log("Finished PseudoLocalize"); + return pseudoLocalizer.convertAll(); + } + + // If there's an error in the Result, report it and set the exitCode. + private reportError(result: Result): number { + let exitCode = result.exitCode; + if (result.error && !result.stderr) { + console.error("\n-------- Operation:", result.operation, "--------"); + console.error(result.error.toString()); + console.error(result.error); + if (result.stdout) { + console.error("Output:") + console.error(result.stdout); + } + } + if (result.stderr) { + console.error("\n-------- Operation:", result.operation, "--------"); + console.error("Errors:") + console.error(result.stderr); + if (result.stdout) { + console.error("Output:") + console.error(result.stdout); + } + exitCode = 1; + } + if (result.stdout && (this._detail > 1)) { + console.log("\n-------- Operation:", result.operation, "--------"); + console.log("Output:"); + console.log(result.stdout); + } + return exitCode; + } + + // report results for a list of steps. + private reportResults(results: Result[]) { + // check all results + let exitCode = 0; + for (const result of results) { + const thisExitCode = this.reportError(result); + if (0 !== thisExitCode) + exitCode = thisExitCode; + } + return exitCode; + } + + /* ---------------------- + // step one - parallel compile and symlink of source needed in webpack. + // Not currently used. Ran into problem where the two process would collide + // trying to create the output directories. + private async _compileAndSymlinkSources(): Promise { + // compile the .ts and .tsx files + const compileResult = this.compileSource(); + + // symlink the source resource ().scss and .svg files, public locale files, etc.) to the lib directory for inclusion in the webpack. + const symlinkSourceResourcesResult = this.symlinkSourceResources(); + + // wait for all of those operations to finish. + return Promise.all([compileResult, symlinkSourceResourcesResult]) + } + ------------------------ */ + + // step two - parallel webpack and symlink of external modules needed for applications + private async webpackAndSymlinkExternalModules(): Promise { + // webpack the module. + const webpackResults = this.webpackModule(); + + // If this is an application, symlink the required external modules to the webresources directory. + const symlinkExternalModulesResult = this.symlinkRequiredExternalModules(); + + // wait for the webpack and external modules operations to finish. + return Promise.all([webpackResults, symlinkExternalModulesResult]) + } + + // this is the method that sequences the build. + public async sequenceBuild(): Promise { + + const symlinkResults = await this.symlinkSourceResources(); + let exitCode = this.reportResults([symlinkResults]); + if (0 !== exitCode) + return exitCode; + + const compileResults = await this.compileSource() + exitCode = this.reportResults([compileResults]); + if (0 !== exitCode) + return exitCode; + + const stepTwoResults: Result[] = await this.webpackAndSymlinkExternalModules(); + exitCode = this.reportResults(stepTwoResults); + if (0 !== exitCode) + return exitCode; + + const pluginResults: Result[] = await this.buildSubModules(); + exitCode = this.reportResults(pluginResults); + if (0 !== exitCode) + return exitCode; + + const makeConfigResult: Result = await this.makeConfig(); + exitCode = this.reportResults([makeConfigResult]); + if (0 != exitCode) + return exitCode; + + const pseudoLocalizeResult = this.pseudoLocalize(); + exitCode = this.reportResults([pseudoLocalizeResult]); + + return exitCode; + } + +} + +// build iModelJs module according to the specifications found in package.json's iModelJs.buildModule property. +// The package.iModelJs.buildModule property has these keys: +// type - (required) string with value of "system", "application", or "plugin". +// detail - (optional) number between 0 and 4. The greater of module.detail and args.detail is used. +// sourceResources - (optional) object array with each object containing a "source" and "dest" key with the specifications of files to symlink from +// source to dest. Typically something like { "source": "./src/**/*.scss", "dest": ".lib"}; Usually used for application and plugin types. +// tscOptions - (optional) options to pass to the tsc command line. Usually, the build just does "tsc" and uses tsconfig.json for all options. +// webpack - (optional) object with properties: +// "dest" - (required) output directory for webpacked bundle. Typically "lib/module" for modules and "lib/webresources" for applications and plugins. +// "entry" - (required) entry script file point for webpack to start. Typicall "lib/index.js" or "lib/main.js". +// "bundleName" - (required) the name of the bundle. For applications, "main.js". For modules, the name of the module. For plugins, the name of the plugin. +// "styleSheets" - (optional) if present and true, indicates that webpack needs to use the stylesheet loading plugins. Slows webpack down considerably. +// "htmlTemplate" - (optional) For applications only, uses the specified html file as a template to create the webpage's html file. +// +async function main(): Promise { + const cmdLineArgs: any = getArgs(); + const packageContents: any = Utils.readPackageFileContents(process.cwd()); + + // figure out the detail level. + let detail = 0; + if (cmdLineArgs.verbose) + detail = 1; + if (cmdLineArgs.detail) + detail = (cmdLineArgs.detail === true) ? 1 : cmdLineArgs.detail; + + // package.json can increase detail level. + if (packageContents.iModelJs && packageContents.iModelJs.buildModule.detail && (packageContents.iModelJs.buildModule.detail > detail)) + detail = packageContents.iModelJs.buildModule.detail; + + // report command line. + if (detail > 0) { + console.log("detail:", detail); + console.log("production:", cmdLineArgs.production); + console.log("stats:", cmdLineArgs.stats); + } + + if (!packageContents.iModelJs || !packageContents.iModelJs.buildModule) { + console.log("Building an iModel.js module requires an iModelJs.buildModule entry in package.json"); + return 1; + } + + // instantiate the builder + const isDevelopment = !cmdLineArgs.production; + const doStats: boolean = cmdLineArgs.stats; + const builder = new IModelJsModuleBuilder(packageContents.iModelJs.buildModule, detail, isDevelopment, doStats); + + if (builder.checkDefinition()) + return 1; + + return await builder.sequenceBuild(); +} + +main().then((exitCode) => { process.exit(exitCode); }); diff --git a/tools/webpack/modules/copyExternalModules.js b/tools/webpack/modules/copyExternalModules.js index 0eba37d..cc25964 100644 --- a/tools/webpack/modules/copyExternalModules.js +++ b/tools/webpack/modules/copyExternalModules.js @@ -8,216 +8,274 @@ const path = require("path"); const fs = require("fs-extra") const argv = require("yargs").argv; -// the arguments are --package={packagefile} // --destDir={destination directory} // -- type={dev|prod} -function makeModulePath(localNodeModules, moduleName, relativePath) { - return path.resolve(localNodeModules, moduleName, relativePath); +// description of each node_module. +class ModuleInfo { + constructor(isDevelopment, useVersion, moduleName, relativePath, publicResourceDirectory) { + this.useVersion = useVersion; + this.moduleName = moduleName; + this.relativePath = relativePath; + this.publicResourceDirectory = publicResourceDirectory; + // if relativePath not supplied, it's one of our @bentley modules and we can figure it out. + if (!this.relativePath) { + this.fileName = (0 === moduleName.indexOf("@bentley")) ? moduleName.slice(9) + ".js" : moduleName + ".js"; + this.relativePath = path.join(moduleName, isDevelopment ? "lib/module/dev" : "lib/module/prod", this.fileName); + } else { + this.fileName = this.moduleName + ".js"; + } + } } -function makeBentleyModulePath(localNodeModules, moduleName, isDev) { - return path.resolve(localNodeModules, moduleName, isDev ? "lib/module/dev" : "lib/module/prod"); +class DependentInfo { + constructor(name, packageRoot, parentPackageRoot, externalModule, version) { + this.name = name; + this.packageRoot = packageRoot; + this.parentPackageRoot = parentPackageRoot; + this.externalModule = externalModule; + this.version = version; + } } -function getBentleyVersionString(localNodeModules, moduleName, buildType) { - if (buildType !== "prod") { - return "-latest" - } else { - const packageFileName = path.resolve(localNodeModules, moduleName, "./package.json"); - const packageFileContents = fs.readFileSync(packageFileName, "utf8"); +// class that copies (actually symlinks) the external modules needed my iModel.js into the web resources directory. +class ModuleCopier { - // parse it - const package = JSON.parse(packageFileContents); - return package.version; + // these are all modules that are listed as external in our webpack configuration, and therefore need to be copied to the web resources directory. + constructor(nodeModulesDirectory, isDevelopment) { + this.nodeModulesDirectory = nodeModulesDirectory; + this.dependentList = []; + this.isDevelopment = isDevelopment; + this.externalModules = [ + new ModuleInfo(isDevelopment, true, "@bentley/bentleyjs-core", undefined), + new ModuleInfo(isDevelopment, true, "@bentley/geometry-core", undefined), + new ModuleInfo(isDevelopment, true, "@bentley/imodeljs-i18n", undefined), + new ModuleInfo(isDevelopment, true, "@bentley/imodeljs-clients", undefined), + new ModuleInfo(isDevelopment, true, "@bentley/imodeljs-common", undefined), + new ModuleInfo(isDevelopment, true, "@bentley/imodeljs-quantity", undefined), + new ModuleInfo(isDevelopment, true, "@bentley/imodeljs-frontend", undefined, "lib/public"), + new ModuleInfo(isDevelopment, true, "@bentley/ui-core", undefined, "lib/public"), + new ModuleInfo(isDevelopment, true, "@bentley/ui-components", undefined, "lib/public"), + new ModuleInfo(isDevelopment, true, "@bentley/ui-framework", undefined, "lib/public"), + new ModuleInfo(isDevelopment, true, "@bentley/ui-ninezone", undefined), + new ModuleInfo(isDevelopment, true, "@bentley/presentation-common", undefined), + new ModuleInfo(isDevelopment, true, "@bentley/presentation-components", undefined), + new ModuleInfo(isDevelopment, true, "@bentley/presentation-frontend", undefined), + new ModuleInfo(isDevelopment, false, "react", path.join("react", isDevelopment ? "umd/react.development.js" : "umd/react.production.min.js")), + new ModuleInfo(isDevelopment, false, "react-dnd", path.join("react-dnd", isDevelopment ? "dist/ReactDnD.js" : "dist/ReactDnD.min.js")), + new ModuleInfo(isDevelopment, false, "react-dnd-html5-backend", path.join("react-dnd-html5-backend", isDevelopment ? "dist/ReactDnDHTML5Backend.js" : "dist/ReactDnDHTML5Backend.min.js")), + new ModuleInfo(isDevelopment, false, "react-dom", path.join("react-dom", isDevelopment ? "umd/react-dom.development.js" : "umd/react-dom.production.min.js")), + new ModuleInfo(isDevelopment, false, "react-redux", path.join("react-redux", isDevelopment ? "dist/react-redux.js" : "dist/react-redux.min.js")), + new ModuleInfo(isDevelopment, false, "redux", path.join("redux", isDevelopment ? "dist/redux.js" : "dist/redux.min.js")), + new ModuleInfo(isDevelopment, false, "inspire-tree", path.join("inspire-tree", isDevelopment ? "dist/inspire-tree.js" : "dist/inspire-tree.min.js")), + new ModuleInfo(isDevelopment, false, "lodash", path.join("lodash", isDevelopment ? "lodash.js" : "lodash.min.js")), + ]; } -} + // reads the package contents for the given module. + static readPackageContents(packageRoot) { + const packageFileName = path.resolve(packageRoot, "./package.json"); + if (!fs.existsSync(packageFileName)) + return {}; -function isFile(fileName) { - fs.statSync(fileName); -} + const packageFileContents = fs.readFileSync(packageFileName, "utf8"); + return JSON.parse(packageFileContents); + } -function linkRecursive(sourceDirectory, outputDirectory) { - const entries = fs.readdirSync(sourceDirectory); - // first go through and link all the files. - for (thisEntry of entries) { - sourceFile = path.resolve(sourceDirectory, thisEntry); - const stats = fs.statSync(sourceFile); - if (stats.isFile()) { - outputFile = path.resolve(outputDirectory, thisEntry); - if (fs.existsSync(outputFile)) { - console.log("file", outputFile, "already exists, not linking."); - } else { - fs.symlinkSync(sourceFile, outputFile); + // links all files in the source directory to the output directory, recursively through subdirectories. + static symlinkRecursive(sourceDirectory, outputDirectory) { + const entries = fs.readdirSync(sourceDirectory); + // first go through and link all the files. + for (const thisEntry of entries) { + const sourceFile = path.resolve(sourceDirectory, thisEntry); + const stats = fs.statSync(sourceFile); + if (stats.isFile()) { + const outputFile = path.resolve(outputDirectory, thisEntry); + if (!fs.existsSync(outputFile)) { + fs.symlinkSync(sourceFile, outputFile); + } } } - } - // then go through and do all the subdirectories. - for (thisEntry of entries) { - sourceSubDirectory = path.resolve(sourceDirectory, thisEntry); - const stats = fs.statSync(sourceSubDirectory); - if (stats.isDirectory()) { - // go through the subdirectory - outputSubDirectory = path.resolve(outputDirectory, thisEntry); - if (!fs.existsSync(outputSubDirectory)) - fs.mkdirSync(outputSubDirectory, { recursive: true }); - linkRecursive(sourceSubDirectory, outputSubDirectory); + // then go through and do all the subdirectories. + for (const thisEntry of entries) { + const sourceSubDirectory = path.resolve(sourceDirectory, thisEntry); + const stats = fs.statSync(sourceSubDirectory); + if (stats.isDirectory()) { + // go through the subdirectory + const outputSubDirectory = path.resolve(outputDirectory, thisEntry); + if (!fs.existsSync(outputSubDirectory)) + fs.mkdirSync(outputSubDirectory, { recursive: true }); + ModuleCopier.symlinkRecursive(sourceSubDirectory, outputSubDirectory); + } } } -} -function linkStaticFiles(sourceDirectory, outputDirectory) { - const sourceStaticDirectory = path.resolve(sourceDirectory, "static"); - if (fs.existsSync(sourceStaticDirectory)) { - const outStaticDirectory = path.resolve(outputDirectory, "static"); - if (!fs.existsSync(outStaticDirectory)) - fs.mkdirSync(outStaticDirectory); - linkRecursive(sourceStaticDirectory, outStaticDirectory); + // symlinks the public static files from a module into the output web resources directories. + static symlinkPublicStaticFiles(sourcePublicDirectory, outputPublicDirectory) { + if (fs.existsSync(sourcePublicDirectory)) { + ModuleCopier.symlinkRecursive(sourcePublicDirectory, outputPublicDirectory); + } } -} -function linkPublicStaticFiles(sourcePublicDirectory, outputPublicDirectory) { - console.log("linkPublicStaticFiles", sourcePublicDirectory, outputPublicDirectory); - if (fs.existsSync(sourcePublicDirectory)) { - linkRecursive(sourcePublicDirectory, outputPublicDirectory); - } -} + // symlinks the module file and source map file if available. + static symlinkModuleFile(moduleSourceFile, outFilePath) { + // symlink the module file. + if (!fs.existsSync(outFilePath)) { + fs.symlinkSync(moduleSourceFile, outFilePath); + } -function linkModuleFile(moduleSourceFile, outFilePath) { - if (fs.existsSync(outFilePath)) { - console.log("file", outFilePath, "already exists, skipping"); - } else { - console.log("linking .js file", moduleSourceFile, "to", outFilePath); - fs.symlinkSync(moduleSourceFile, outFilePath); + // if there's a source map file, link that, too. + const mapFile = moduleSourceFile + ".map"; + const outMapFile = outFilePath + ".map"; + if (fs.existsSync(mapFile) && !fs.existsSync(outMapFile)) { + fs.symlinkSync(mapFile, outMapFile); + } } - // if there's a map file, link that, too. - const mapFile = moduleSourceFile + ".map"; - const outMapFile = outFilePath + ".map"; - if (fs.existsSync(mapFile) && !fs.existsSync(outMapFile)) { - fs.symlinkSync(mapFile, outMapFile); - console.log("linking .js.map file", mapFile, "to", outMapFile); + + // this function adds the dependencies found in package.json that are external modules. Recurses, but only to depth 1. + findExternalModuleDependents(parentPackageRoot, depth) { + const packageFileContents = ModuleCopier.readPackageContents(parentPackageRoot); + + // find new dependents from this packageFileContents + const newDependents = []; + for (const dependent in packageFileContents.dependencies) { + // see if we already have this dependent. + if (undefined !== this.dependentList.find((existingDependent) => { return (existingDependent.name === dependent); })) { + continue; + } + // we only care about external modules and their dependents that might be external modules. + for (const externalModule of this.externalModules) { + if (externalModule.moduleName === dependent) { + const dependentPackageRoot = path.resolve(parentPackageRoot, "node_modules", dependent); + newDependents.push(new DependentInfo(dependent, dependentPackageRoot, parentPackageRoot, externalModule, packageFileContents.dependencies[dependent])); + } + } + } + + // add the newly discovered dependents to the master list of dependents. + for (const newDependent of newDependents) { + this.dependentList.push(newDependent); + } + + // we need to check the first level of dependents of our direct dependents to find the non-imodeljs dependencies like lodash, react, redux, etc. + if (depth < 2) { + for (const newDependent of newDependents) { + this.findExternalModuleDependents(newDependent.packageRoot, depth + 1); + } + } } -} -class ModuleInfo { - constructor(inRushRepo, moduleName, moduleSourceFile, publicResourceDirectory) { - this.inRushRepo = inRushRepo; - this.moduleName = moduleName; - this.moduleSourceFile = moduleSourceFile; - this.publicResourceDirectory = publicResourceDirectory; + // finds peerDependencies and makes sure they are in the list of dependencies. + checkPeerDependencies() { + let peerMissing = false; + const missingList = []; + for (const thisDependent of this.dependentList) { + const packageFileContents = ModuleCopier.readPackageContents(thisDependent.packageRoot); + for (const peerDependent in packageFileContents.peerDependencies) { + // don't bother to a peerDependent twice. + if (-1 !== missingList.indexOf(peerDependent)) + continue; + + // we only report miss peerDependencies of our external modules. + if (undefined === (this.externalModules.find((externalModule) => { return (externalModule.moduleName === peerDependent); }))) + continue; + + if (undefined === this.dependentList.find((thisDependent) => { return (thisDependent.name === peerDependent); })) { + console.log("Dependent", thisDependent.name, "requires a peerDependency of", peerDependent, "but none found. Add ", peerDependent, "to dependencies in package.json"); + missingList.push(peerDependent); + peerMissing = true; + } + } + } + return peerMissing ? 1 : 0 } -} -// these are the modules that we have specified as externals when we webpack -function main() { - const packageFileName = (argv.packageFile === undefined) ? "./package.json" : argv.packageFile; - const buildType = (argv.type == undefined) ? "dev" : argv.type; - const isDev = buildType === "dev"; - const localNodeModules = path.resolve(process.cwd(), "node_modules"); - if (!fs.existsSync(localNodeModules)) { - console.log("Local node modules directory", localNodeModules, "does not exist, aborting"); - return; + // finds the source module file. It is either relative to our localNodeModules, or relative to the dependent path where we found the package file. + findExternalModuleFile(dependent, relativePath) { + const localNodeModuleFile = path.resolve(this.nodeModulesDirectory, relativePath); + if (fs.existsSync(localNodeModuleFile)) + return localNodeModuleFile; + + const dependentNodeModuleFile = path.resolve(dependent.parentPackageRoot, "node_modules", relativePath); + if (fs.existsSync(dependentNodeModuleFile)) + return dependentNodeModuleFile; + + return undefined; } - const externalModules = [ - new ModuleInfo(true, "@bentley/bentleyjs-core", undefined), - new ModuleInfo(true, "@bentley/geometry-core", undefined), - new ModuleInfo(false, "@bentley/bwc", makeModulePath(localNodeModules, "@bentley/bwc", isDev ? "lib/module/dev/bwc.js" : "lib/module/prod/bwc.js")), - new ModuleInfo(true, "@bentley/imodeljs-i18n", undefined), - new ModuleInfo(true, "@bentley/imodeljs-clients", undefined), - new ModuleInfo(true, "@bentley/imodeljs-common", undefined), - new ModuleInfo(true, "@bentley/imodeljs-quantity", undefined), - new ModuleInfo(true, "@bentley/imodeljs-frontend", undefined, "lib/public"), - new ModuleInfo(true, "@bentley/ui-core", undefined, "lib/public"), - new ModuleInfo(true, "@bentley/ui-components", undefined, "lib/public"), - new ModuleInfo(true, "@bentley/ui-framework", undefined, "lib/public"), - new ModuleInfo(true, "@bentley/ui-ninezone", undefined), - new ModuleInfo(true, "@bentley/presentation-common", undefined), - new ModuleInfo(true, "@bentley/presentation-components", undefined), - new ModuleInfo(true, "@bentley/presentation-frontend", undefined), - new ModuleInfo(false, "react", makeModulePath(localNodeModules, "react", isDev ? "umd/react.development.js" : "umd/react.production.min.js")), - new ModuleInfo(false, "react-dnd", makeModulePath(localNodeModules, "react-dnd", isDev ? "dist/ReactDnd.js" : "dist/ReactDnD.min.js")), - new ModuleInfo(false, "react-dnd-html5-backend", makeModulePath(localNodeModules, "react-dnd-html5-backend", isDev ? "dist/ReactDnDHTML5Backend.js" : "dist/ReactDnDHTML5Backend.min.js")), - new ModuleInfo(false, "react-dom", makeModulePath(localNodeModules, "react-dom", isDev ? "umd/react-dom.development.js" : "umd/react-dom.production.min.js")), - new ModuleInfo(false, "react-redux", makeModulePath(localNodeModules, "react-redux", isDev ? "dist/react-redux.js" : "dist/react-redux.min.js")), - new ModuleInfo(false, "redux", makeModulePath(localNodeModules, "redux", isDev ? "dist/redux.js" : "dist/redux.min.js")), - new ModuleInfo(false, "inspire-tree", makeModulePath(localNodeModules, "inspire-tree", isDev ? "dist/inspire-tree.js" : "dist/inspire-tree.min.js")), - new ModuleInfo(false, "lodash", makeModulePath(localNodeModules, "lodash", isDev ? "lodash.js" : "lodash.min.js")), - ]; - - try { - const outputDirectory = path.resolve(process.cwd(), argv.destDir); - // create the output directory, if it doesn't exist. - if (!fs.existsSync(outputDirectory)) { - fs.mkdirSync(outputDirectory, { recursive: true }); - } + // finds dependents, symlinks them to destination directory. + symlinkExternalModules(relativeDestinationDir) { + try { + const outputDirectory = path.resolve(process.cwd(), relativeDestinationDir); + // create the output directory, if it doesn't exist. + if (!fs.existsSync(outputDirectory)) { + fs.mkdirSync(outputDirectory, { recursive: true }); + } - // open and read the package.json file. - const packagePath = path.resolve(process.cwd(), packageFileName); - const packageFileContents = fs.readFileSync(packagePath, "utf8"); - - // parse it - const package = JSON.parse(packageFileContents); - for (const dependent in package.dependencies) { - // see if it matches one of our externals. - for (const externalModule of externalModules) { - if (dependent === externalModule.moduleName) { - // yes, link the file. - let fileName = ""; - let moduleSourceFile = undefined; - let versionString = undefined; - if (externalModule.inRushRepo) { - // These are our iModelJs modules. Get the version from the package.json file. - versionString = getBentleyVersionString(localNodeModules, externalModule.moduleName, buildType); - const modulePath = makeBentleyModulePath(localNodeModules, externalModule.moduleName, isDev); - fileName = externalModule.moduleName.slice(9) + ".js"; - moduleSourceFile = path.resolve(modulePath, fileName); - } else { - moduleSourceFile = externalModule.moduleSourceFile; // we stored the correct source file name in the externalModules object. - if (0 === externalModule.moduleName.indexOf("@bentley")) { - // this is for @bentley/bwc - - fileName = externalModule.moduleName.slice(9) + ".js"; - } - else { - fileName = externalModule.moduleName + ".js"; + // Read the package file for the current directory, and add the dependents recursively (to depth 2) to the sub-dependencies of the iModelJs modules. + this.findExternalModuleDependents(process.cwd(), 0); + if (this.checkPeerDependencies()) + return 1; + + let missingModule = false; + for (const dependent of this.dependentList) { + const externalModule = dependent.externalModule; + const versionString = (externalModule.useVersion) ? dependent.version : undefined; + + const moduleSourceFile = this.findExternalModuleFile(dependent, externalModule.relativePath) + + // report all the module files we can't find. + if (!moduleSourceFile) { + console.log("Unable to locate required module file", externalModule.moduleName, " - looking for relative path ", externalModule.relativePath); + missingModule = true; + } else { + let outFilePath = outputDirectory; + if (versionString) { + outFilePath = path.resolve(outputDirectory, "v" + versionString); + // create subdirectory if needed. + if (!fs.existsSync(outFilePath)) { + fs.mkdirSync(outFilePath, { recursive: true }); } } - if (!fs.existsSync(moduleSourceFile)) { - console.log("problem: File", moduleSourceFile, "does not exist"); - } else { - let outFilePath = undefined; - if (versionString) { - // create subdirectory if needed. - const outSubDirectory = path.resolve(outputDirectory, "v" + versionString); - if (!fs.existsSync(outSubDirectory)) { - fs.mkdirSync(outSubDirectory, { recursive: true }); - } - outFilePath = path.resolve(outSubDirectory, fileName) - } else { - outFilePath = path.resolve(outputDirectory, fileName); - } - linkModuleFile(moduleSourceFile, outFilePath); - if (externalModule.publicResourceDirectory) { - const publicPath = path.resolve(localNodeModules, externalModule.moduleName, externalModule.publicResourceDirectory); - linkPublicStaticFiles(publicPath, outputDirectory); - } - // found dependent in list of external, no need to look at the rest of the externals. - break; + const fullFilePath = path.resolve(outFilePath, externalModule.fileName); + ModuleCopier.symlinkModuleFile(moduleSourceFile, fullFilePath); + + // symlink the external modules resource files if necessary. + if (externalModule.publicResourceDirectory) { + const publicPath = path.resolve(dependent.packageRoot, externalModule.publicResourceDirectory); + ModuleCopier.symlinkPublicStaticFiles(publicPath, outputDirectory); } } } + + if (missingModule) + return 1; + + // link the IModelJsLoader.js from imodeljs/frontend also. NOTE: imodeljs-frontend must always be in package.json's dependencies. + const loaderFile = path.resolve(process.cwd(), "node_modules/@bentley/imodeljs-frontend", this.isDevelopment ? "lib/module/dev/IModelJsLoader.js" : "lib/module/prod/IModelJsLoader.js"); + ModuleCopier.symlinkModuleFile(loaderFile, path.resolve(outputDirectory, "iModelJsLoader.js")); + + } catch (e) { + console.log("Error", e); + return 1; } + return 0; + } +} - // link the IModelJsLoader.js from imodeljs/frontend also. - const loaderPath = makeBentleyModulePath(localNodeModules, "@bentley/imodeljs-frontend", isDev); - const loaderFile = path.resolve(loaderPath, "IModelJsLoader.js"); - const outFilePath = path.resolve(outputDirectory, "IModelJsLoader.js"); - linkModuleFile(loaderFile, outFilePath); - } catch (e) { - console.log("Error", e); +function main() { + const buildType = (argv.type == undefined) ? "dev" : argv.type; + const isDevelopment = buildType === "dev"; + const localNodeModules = path.resolve(process.cwd(), "node_modules"); + if (!fs.existsSync(localNodeModules)) { + console.log("Local node modules directory", localNodeModules, "does not exist, aborting"); + return 1; } + + const moduleCopier = new ModuleCopier(localNodeModules, isDevelopment); + return moduleCopier.symlinkExternalModules(argv.destDir); } -main(); +// run the ModuleCopier +process.exitCode = main(); diff --git a/tools/webpack/modules/webpackModule.config.js b/tools/webpack/modules/webpackModule.config.js index a69721b..b6df864 100644 --- a/tools/webpack/modules/webpackModule.config.js +++ b/tools/webpack/modules/webpackModule.config.js @@ -8,38 +8,87 @@ // All the other @bentley dependencies are set to external. // arguments (specified on the package.json script line) -// env.sourcedir= : the source directory for the package. (Optional. If not specified, use ./) -// env.outdir= : the output directory for the webpack result, relative to env.sourcedir. (Optional. If not specified, use {env.sourcedir}/lib/module/{env.prod ? "prod", "dev"}/ +// env.outdir= : the output directory for the webpack result, relative to the package file dir. (Optional. If not specified, use ./lib/module/{env.prod ? "prod", "dev"}/ // env.entry= : the entry point of the module in the compiled file (e.g. ./lib/imodeljs-frontend.js). Required // env.bundlename= : the name of the bundled module (e.g. imodeljs-frontend). Required // env.stylesheets : if specified (no value needed), sets up webpack to process .scss files into the bundle. Optional. // env.prod : if specified, makes the production version of the module. (Optional. If not specified, builds the development version) +// env.htmltemplate: if specified, reads the html template and substitutes the versions required into the lodash tempolate '<%= htmlWebpackPlugin.options.imjsVersions %>', +// which should appear as the value for the data-imjsversions property of the IModelJsLoader script tag. +// env.plugin: if specified, saves the versions of the iModelJs modules required into the webpack output so that the plugin can verify their presence at runtime. const path = require("path"); +const fs = require("fs-extra"); +const webpack = require("webpack"); const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); const SpriteLoaderPlugin = require("svg-sprite-loader/plugin"); const autoprefixer = require("autoprefixer"); // NOTE: This was set up to return an array of configs, one for target: "web" and one for target: "node", but the node target didn't work, so I dropped it. -module.exports = (env) => { return getConfig(env, false); }; +module.exports = (env) => { return getConfig(env); }; + +function getIModelJsVersionsFromPackage(iModelJsVersions, packageContents, sourceDir, externalList, depth) { + // we need the dependents and peer dependents. We care only about those that start with @bentley and are also in externalList. + let dependentsAndPeerDependents = []; + if (packageContents.dependencies) + dependentsAndPeerDependents = Object.getOwnPropertyNames(packageContents.dependencies); + if (packageContents.peerDependencies) + dependentsAndPeerDependents = dependentsAndPeerDependents.concat(Object.getOwnPropertyNames(packageContents.peerDependencies)); + for (dependent of dependentsAndPeerDependents) { + if (dependent.startsWith("@bentley") && externalList[dependent]) { + const packageName = dependent.substr(9); + // if we don't already have it, get its info. + if (!iModelJsVersions[packageName]) { + const version = packageContents.dependencies[dependent] || packageContents.peerDependencies[dependent]; + iModelJsVersions[packageName] = version; + } + } + } + + // we need to get the versions of the next level of @bentley dependents (but only one deep, that's enough to find them all) + if (depth < 1) { + for (dependent of dependentsAndPeerDependents) { + if (dependent.startsWith("@bentley") && externalList[dependent]) { + const subDirectory = path.join(sourceDir, "node_modules", dependent); + const dependentPackageContents = getPackageFromJson(subDirectory); + getIModelJsVersionsFromPackage(iModelJsVersions, dependentPackageContents, subDirectory, externalList, depth + 1); + } + } + } +} + +// gets a Map with the dependent iModel.js modules as the keys and the required version as the values. +function getIModelJsVersions(sourceDir, packageContents, externalList) { + const iModelJsVersions = new Object(); + getIModelJsVersionsFromPackage(iModelJsVersions, packageContents, sourceDir, externalList, 0); + return iModelJsVersions; +} + + +// gets the version from this modules package.json file, so it can be injected into the output. +function getPackageFromJson(sourceDir) { + const packageFileName = path.resolve(sourceDir, "./package.json"); + const packageFileContents = fs.readFileSync(packageFileName, "utf8"); + const packageContents = JSON.parse(packageFileContents); + return packageContents; +} function dropDashes(name) { return name.replace("-", "_"); } -function getConfig(env, nodeAsTarget) { - // set sourcedir if not specified in arguments. - if (!env.sourcedir) - env.sourcedir = "./"; +function getConfig(env) { + // sourceDir is always the current directory, which npm sets to that of the package file. + const sourceDir = process.cwd(); if (!env.outdir) env.outdir = "./lib/module" + (env.prod ? "/prod" : "/dev"); // get the directory for the bundle. - bundleDirectory = path.resolve(env.sourcedir, env.outdir); + bundleDirectory = path.resolve(sourceDir, env.outdir); // the context directory (for looking up imports, etc.) is the original module source directory. - const contextDirectory = path.resolve(env.sourcedir); + const contextDirectory = path.resolve(sourceDir); // unless specified with env.prod, create a development build. const devMode = !(env.prod); @@ -50,12 +99,16 @@ function getConfig(env, nodeAsTarget) { // name of the output bundle. const bundleName = env.bundlename; + // get the version number for the javascript currently being webpacked. + const packageContents = getPackageFromJson(sourceDir); + // build the object for the webpack configuration const webpackLib = { + bail: true, // don't continue on error. context: contextDirectory, output: { path: bundleDirectory, - filename: nodeAsTarget ? '[name].node.js' : '[name].js', + filename: '[name].js', library: dropDashes(bundleName), libraryTarget: 'umd', umdNamedDefine: true, @@ -63,11 +116,10 @@ function getConfig(env, nodeAsTarget) { globalObject: 'this', // used only for web target. devtoolModuleFilenameTemplate: "file:///[absolute-resource-path]" }, - target: nodeAsTarget ? 'node' : 'web', + target: env.webworker ? 'webworker' : 'web', externals: { '@bentley/bentleyjs-core': 'bentleyjs_core', '@bentley/geometry-core': 'geometry_core', - '@bentley/bwc': 'bwc', '@bentley/imodeljs-i18n': 'imodeljs_i18n', '@bentley/imodeljs-clients': 'imodeljs_clients', '@bentley/imodeljs-common': 'imodeljs_common', @@ -90,18 +142,8 @@ function getConfig(env, nodeAsTarget) { 'lodash': { root: '_', commonjs2: 'lodash', commonjs: 'lodash', amd: 'lodash' }, 'electron': 'commonjs electron', }, - module: { - rules: [ - { - test: /\.js$/, - use: "source-map-loader", - enforce: "pre" - }, - ] - }, optimization: { // create only one runtime chunk. - runtimeChunk: "single", moduleIds: "named", }, node: { @@ -112,22 +154,96 @@ function getConfig(env, nodeAsTarget) { plugins: [] }; - Object.defineProperty(webpackLib, "entry", { configurable: true, enumerable: true, writable: true, value: {} }); - Object.defineProperty(webpackLib.entry, bundleName, { configurable: true, enumerable: true, writable: true, value: bundleEntry }); - Object.defineProperty(webpackLib, "mode", { configurable: true, enumerable: true, writable: true, value: devMode ? "development" : "production" }); - Object.defineProperty(webpackLib, "devtool", { configurable: true, enumerable: true, writable: true, value: devMode ? "cheap-module-source-map" : "source-map" }); + // we want to separate out the runtimeChunk, except in the webworker case. + if (!env.webworker) { + webpackLib.optimization.runtimeChunk = "single"; + } + + // Set up for the DefinePlugin. We always want the BUILD_SEMVER to be available in the webpacked module, will add more definitions as needed. + definePluginDefinitions = { "BUILD_SEMVER": JSON.stringify(packageContents.version) }; + + webpackLib.entry = {} + webpackLib.entry[bundleName] = bundleEntry; + webpackLib.mode = devMode ? "development" : "production"; + webpackLib.devtool = devMode ? "cheap-module-source-map" : "source-map"; + + // Loaders setup: + // always use source-map-loader, use strip-assert-loader on production builds; + const stripAssertLoader = path.resolve(__dirname, "../config/strip-assert-loader.js"); + const sourceMapLoader = require.resolve("source-map-loader"); + const jsLoaders = env.prod ? [sourceMapLoader, stripAssertLoader] : [sourceMapLoader]; + webpackLib.module = {}; + webpackLib.module.rules = [{ + test: /\.js$/, + enforce: "pre", + use: jsLoaders + }]; + // set up Uglify. if (!devMode) { webpackLib.optimization.minimizer = [ new UglifyJSPlugin({ uglifyOptions: { - keep_classnames: true, + ecma: 8, + mangle: { + safari10: true, + // NEEDSWORK: Mangling classnames appears to break gateway marshalling... + keep_classnames: true, + }, + compress: { + warnings: false, + // Disabled because of an issue with Uglify breaking seemingly valid code: + // https://github.com/facebook/create-react-app/issues/2376 + // Pending further investigation: + // https://github.com/mishoo/UglifyJS2/issues/2011 + comparisons: false, + // Compressing classnames also breaks reflection + keep_classnames: true, + }, + output: { + comments: false, + // Turned on because emoji and regex is not minified properly using default + // https://github.com/facebook/create-react-app/issues/2488 + ascii_only: true, + }, }, - sourceMap: true - }) + // Use multi-process parallel running to improve the build speed + // Default number of concurrent runs: os.cpus().length - 1 + parallel: true, + // Enable file caching + cache: true, + sourceMap: true, + }), ]; } + // env.htmltemplate is passed to webpackModule.config.js only for applications that are creating an HtmlTemplate. + // The reason for it is to set the version of the iModelJs modules that the application requires into index.html. + // It gets that by reading the version of imodeljs-frontend listed in package.json. + if (env.htmltemplate || env.plugin) { + const iModelJsVersions = getIModelJsVersions(sourceDir, packageContents, webpackLib.externals); + const versionString = JSON.stringify(iModelJsVersions); + + if (env.htmltemplate) { + const HtmlWebpackPlugin = require("html-webpack-plugin"); + webpackLib.plugins.push(new HtmlWebpackPlugin({ + imjsVersions: versionString, + template: env.htmltemplate, + filename: "./index.html", + minify: "false", + chunks: [], // we don't want it to add any .js to the template, those are already in there. + })); + } + + if (env.plugin) { + definePluginDefinitions.IMODELJS_VERSIONS_REQUIRED = JSON.stringify(versionString); + definePluginDefinitions.PLUGIN_NAME = JSON.stringify(bundleName); + } + } + + // add the DefinePlugin. + webpackLib.plugins.push(new webpack.DefinePlugin(definePluginDefinitions)); + // if using style sheets (import "xxx.scss" lines in .ts or .tsx files), then we need the sass-loader if (env.stylesheets) { cssRules = [{ @@ -226,7 +342,7 @@ function getConfig(env, nodeAsTarget) { ]; webpackLib.module.rules = webpackLib.module.rules.concat(cssRules); - webpackLib.plugins = webpackLib.plugins.concat([new SpriteLoaderPlugin()]); + webpackLib.plugins.push(new SpriteLoaderPlugin()); } webpackLib.output.library = dropDashes(webpackLib.output.library); diff --git a/tools/webpack/package.json b/tools/webpack/package.json index a4de05c..590bb84 100644 --- a/tools/webpack/package.json +++ b/tools/webpack/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/webpack-tools", - "version": "0.187.0", + "version": "0.189.0", "description": "Configuration and scripts for imodeljs apps and services.", "license": "MIT", "repository": { @@ -12,22 +12,23 @@ "url": "http://www.bentley.com" }, "scripts": { - "build": "", - "clean": "", + "build": "tsc 1>&2 && cpx ./lib/buildIModelJsModule.js ./bin && node ../build/scripts/ignoreargs.js 1>&2", + "clean": "rimraf lib", "docs": "", "lint": "", "test": "", "cover": "" }, "bin": { - "bentley-webpack-tools": "./bin/bentley-webpack-tools.js" + "bentley-webpack-tools": "./bin/bentley-webpack-tools.js", + "buildIModelJsModule": "./bin/buildIModelJsModule.js" }, "dependencies": { - "@bentley/config-loader": "0.187.0", + "@bentley/config-loader": "0.189.0", "@types/chai": "^4.1.4", "@types/enzyme": "^3.1.12", "@types/mocha": "^5.2.5", - "@types/node": "10.10.3", + "@types/node": "10.12.18", "@types/sinon": "^5.0.5", "@types/webdriverio": "^4.8.6", "app-root-path": "^2.0.1", @@ -100,23 +101,23 @@ }, "peerDependencies": { "@types/react": "^16.4.6", - "@types/react-dom": "16.0.7", - "chromedriver": "^2.34.1", - "electron-chromedriver": "^2.0.0", + "@types/react-dom": "16.0.11", "react": "^16.4.2", "react-dom": "^16.4.2", "react-test-renderer": "^16.4.2", "typescript": ">=2.9.1" }, "devDependencies": { - "@types/react": "^16.4.14", - "@types/react-dom": "16.0.7", - "chromedriver": "^2.41.0", - "electron-chromedriver": "^2.0.0", + "@types/glob": "^5.0.35", + "@types/react": "16.7.22", + "@types/react-dom": "16.0.11", + "@types/yargs": "^12.0.5", + "cpx": "^1.5.0", "react": "^16.4.2", "react-dom": "^16.4.2", "react-test-renderer": "^16.4.2", - "typescript": "~3.1.0" + "rimraf": "^2.6.2", + "typescript": "~3.2.2" }, "types": "./index.d.ts" } diff --git a/tools/webpack/tsconfig.json b/tools/webpack/tsconfig.json index 91e5433..eab7274 100644 --- a/tools/webpack/tsconfig.json +++ b/tools/webpack/tsconfig.json @@ -1,18 +1,15 @@ { + "extends": "../build/tsconfig-base.json", "compilerOptions": { - "module": "esnext", - "target": "es2015", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react", - "moduleResolution": "node", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - } -} \ No newline at end of file + "target": "es2017", + "module": "commonjs", + "outDir": "./lib" + }, + "include": [ + "./**/*.ts" + ], + "exclude": [ + "lib", + "node_modules" + ] +} diff --git a/ui/components/.npmignore b/ui/components/.npmignore index 3f8d657..a4ed373 100644 --- a/ui/components/.npmignore +++ b/ui/components/.npmignore @@ -2,7 +2,7 @@ * !lib/**/*.d.ts !lib/**/*.js -!lib/**/*.scss +!lib/**/*.*css !lib/public/**/* lib/test !*.md diff --git a/ui/components/CHANGELOG.json b/ui/components/CHANGELOG.json index b109a97..556eb1e 100644 --- a/ui/components/CHANGELOG.json +++ b/ui/components/CHANGELOG.json @@ -1,6 +1,132 @@ { "name": "@bentley/ui-components", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/ui-components_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "UI documentation fixes" + }, + { + "comment": "Added ToggleEditor. Support for defaultTool in Frontstage. Fixed BooleanEditor sizing." + }, + { + "comment": "Added 100% test coverage for Breadcrumb/BreadcrumbDetails" + }, + { + "comment": "Use new buildIModelJsBuild script" + }, + { + "comment": "Remove uneeded typedoc plugin depedency" + }, + { + "comment": "Added EnumEditor & BooleanEditor type editors" + }, + { + "comment": "Minor UI Color Theme changes" + }, + { + "comment": "Support for including CSS files in published UI packages" + }, + { + "comment": "Updated type editors to support updated PropertyRecord. Moved setFocus to props in type editors.." + }, + { + "comment": "Removed dependency on BWC. Parts of BWC copied into ui-core in preparation for theming support." + }, + { + "comment": "Added ToggleEditor. Support for defaultTool in Frontstage." + }, + { + "comment": "Save BUILD_SEMVER to globally accessible map" + }, + { + "comment": "Change setImmediate to setTimeout. Fixed cube rotation issue." + }, + { + "comment": "Added ItemStyle and ItemStyle provider." + }, + { + "comment": "CellItem and TreeNodeItem now have the same style property." + }, + { + "comment": "Added TableCell and TableCellContent React components." + }, + { + "comment": "Changed table css class names." + }, + { + "comment": "Changed CellItem interface property - 'alignment' type to be a restricted string instead of an enum." + }, + { + "comment": " Cleanup of DefaultToolSetting provider and EnumButtonGroup editor" + }, + { + "comment": "Add EnumButtonGroupEditor." + }, + { + "comment": "Primitive property value renderers now render links specified in property records." + }, + { + "comment": "Renamed class names that start with \"ui-components-\" to start with just \"components-\"" + }, + { + "comment": "Added IImageLoader, ImageRenderer and TreeImageLoader." + }, + { + "comment": "Added showIcons and imageLoader props to Tree component." + }, + { + "comment": "Added a property to provide rowHeight value or function that calculates it." + }, + { + "comment": "Tree now accepts one checkbox prop object instead of multiple props." + }, + { + "comment": "Tree now accepts one cell editing prop object instead of multiple props." + }, + { + "comment": "Split tree node label and description rendering into a separate component - TreeNodeContent." + }, + { + "comment": "Added an ability to show node descriptions in the Tree component via showDescriptions property." + }, + { + "comment": "Fix tree failing to load nodes in special cases" + }, + { + "comment": "Added support for UI color themes" + }, + { + "comment": "Add a way to specify checkbox states asynchronously in Tree component" + }, + { + "comment": "Breadcrumb fixes" + }, + { + "comment": "Add a way to specify custom Tree checkbox renderer" + }, + { + "comment": "Update to use newer generic-icons-webfont package." + }, + { + "comment": "upgrade to TypeScript 3.2.2" + }, + { + "comment": "WIP: ViewportComponent unit tests. Removed imodeljs-clients-backend dependency from ui-framework" + } + ] + } + }, + { + "version": "0.188.0", + "tag": "@bentley/ui-components_v0.188.0", + "date": "Wed, 16 Jan 2019 16:36:09 GMT", + "comments": {} + }, { "version": "0.187.0", "tag": "@bentley/ui-components_v0.187.0", diff --git a/ui/components/CHANGELOG.md b/ui/components/CHANGELOG.md index 4a229d5..986ff76 100644 --- a/ui/components/CHANGELOG.md +++ b/ui/components/CHANGELOG.md @@ -1,6 +1,54 @@ # Change Log - @bentley/ui-components -This log was last generated on Tue, 15 Jan 2019 15:18:59 GMT and should not be manually modified. +This log was last generated on Wed, 06 Mar 2019 15:41:22 GMT and should not be manually modified. + +## 0.189.0 +Wed, 06 Mar 2019 15:41:22 GMT + +### Updates + +- UI documentation fixes +- Added ToggleEditor. Support for defaultTool in Frontstage. Fixed BooleanEditor sizing. +- Added 100% test coverage for Breadcrumb/BreadcrumbDetails +- Use new buildIModelJsBuild script +- Remove uneeded typedoc plugin depedency +- Added EnumEditor & BooleanEditor type editors +- Minor UI Color Theme changes +- Support for including CSS files in published UI packages +- Updated type editors to support updated PropertyRecord. Moved setFocus to props in type editors.. +- Removed dependency on BWC. Parts of BWC copied into ui-core in preparation for theming support. +- Added ToggleEditor. Support for defaultTool in Frontstage. +- Save BUILD_SEMVER to globally accessible map +- Change setImmediate to setTimeout. Fixed cube rotation issue. +- Added ItemStyle and ItemStyle provider. +- CellItem and TreeNodeItem now have the same style property. +- Added TableCell and TableCellContent React components. +- Changed table css class names. +- Changed CellItem interface property - 'alignment' type to be a restricted string instead of an enum. +- Cleanup of DefaultToolSetting provider and EnumButtonGroup editor +- Add EnumButtonGroupEditor. +- Primitive property value renderers now render links specified in property records. +- Renamed class names that start with "ui-components-" to start with just "components-" +- Added IImageLoader, ImageRenderer and TreeImageLoader. +- Added showIcons and imageLoader props to Tree component. +- Added a property to provide rowHeight value or function that calculates it. +- Tree now accepts one checkbox prop object instead of multiple props. +- Tree now accepts one cell editing prop object instead of multiple props. +- Split tree node label and description rendering into a separate component - TreeNodeContent. +- Added an ability to show node descriptions in the Tree component via showDescriptions property. +- Fix tree failing to load nodes in special cases +- Added support for UI color themes +- Add a way to specify checkbox states asynchronously in Tree component +- Breadcrumb fixes +- Add a way to specify custom Tree checkbox renderer +- Update to use newer generic-icons-webfont package. +- upgrade to TypeScript 3.2.2 +- WIP: ViewportComponent unit tests. Removed imodeljs-clients-backend dependency from ui-framework + +## 0.188.0 +Wed, 16 Jan 2019 16:36:09 GMT + +*Version update only* ## 0.187.0 Tue, 15 Jan 2019 15:18:59 GMT diff --git a/ui/components/package.json b/ui/components/package.json index d4fc9a4..0f4b968 100644 --- a/ui/components/package.json +++ b/ui/components/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/ui-components", - "version": "0.187.0", + "version": "0.189.0", "description": "iModel.js UI complex components", "license": "MIT", "repository": { @@ -10,19 +10,35 @@ "main": "lib/ui-components.js", "typings": "lib/ui-components", "scripts": { - "build": "tsc 1>&2 && npm run build:scss && npm run copy:public && npm run webpackModule-dev", - "build:scss": "cpx \"./src/**/*.scss\" ./lib", - "copy:public": "cpx \"./public/**/*\" ./lib/public && npm run pseudolocalize", + "build": "node ./node_modules/@bentley/webpack-tools/bin/buildIModelJsModule.js", "clean": "rimraf ./lib package-deps.json", "cover": "nyc npm test", "docs": "node ./node_modules/@bentley/build-tools/scripts/docs.js --source=./src --includes=../../generated-docs/extract --json=../../generated-docs/ui/ui-components/file.json --tsIndexFile=./ui-components.ts --onlyJson %TYPEDOC_THEME%", "lint": "tslint --project . 1>&2", "pack": "node ../../scripts/pack.js", - "pseudolocalize": "node ./node_modules/@bentley/build-tools/scripts/pseudolocalize.js --englishDir ./public/locales/en --out ./lib/public/locales/en-pseudo", "test": "mocha --opts ../mocha.opts lib/test/**/*.test.js", - "test:watch": "npm test -- --reporter min --watch-extensions ts,tsx --watch", - "webpackModule-dev": "make-dir ./lib/module/dev/ && webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/ui-components.js --env.bundlename=ui-components --env.stylesheets --json >./lib/module/dev/webpackStats.json", - "webpackModule-prod": "webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/ui=components.js --env.bundlename=ui-components --env.prod --env.stylesheets" + "test:watch": "npm test -- --reporter min --watch-extensions ts,tsx --watch" + }, + "iModelJs": { + "buildModule": { + "type": "system", + "sourceResources": [ + { + "source": "./src/**/*.*css", + "dest": "./lib" + }, + { + "source": "./public/**/*", + "dest": "./lib/public" + } + ], + "webpack": { + "dest": "./lib/module", + "entry": "./lib/ui-components.js", + "bundleName": "ui-components", + "styleSheets": true + } + } }, "keywords": [ "Bentley", @@ -34,26 +50,26 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/geometry-core": "0.187.0", - "@bentley/imodeljs-i18n": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-frontend": "0.187.0", - "@bentley/ui-core": "0.187.0" + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/imodeljs-i18n": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-frontend": "0.189.0", + "@bentley/ui-core": "0.189.0" }, "//devDependencies": [ "NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install", "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/bentleyjs-core": "0.187.0", - "@bentley/build-tools": "0.187.0", - "@bentley/geometry-core": "0.187.0", - "@bentley/imodeljs-i18n": "0.187.0", - "@bentley/imodeljs-common": "0.187.0", - "@bentley/imodeljs-frontend": "0.187.0", - "@bentley/ui-core": "0.187.0", - "@bentley/webpack-tools": "0.187.0", + "@bentley/bentleyjs-core": "0.189.0", + "@bentley/build-tools": "0.189.0", + "@bentley/geometry-core": "0.189.0", + "@bentley/imodeljs-i18n": "0.189.0", + "@bentley/imodeljs-common": "0.189.0", + "@bentley/imodeljs-frontend": "0.189.0", + "@bentley/ui-core": "0.189.0", + "@bentley/webpack-tools": "0.189.0", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/chai-jest-snapshot": "^1.3.0", @@ -64,11 +80,11 @@ "@types/faker": "^4.1.0", "@types/lodash": "^4.14.0", "@types/mocha": "^5.2.5", - "@types/react": "^16.4.14", + "@types/react": "16.7.22", "@types/react-highlight-words": "^0.11.1", "@types/react-resize-detector": "^3.1.0", "@types/react-data-grid": "^4.0.0", - "@types/react-dom": "16.0.7", + "@types/react-dom": "16.0.11", "@types/react-virtualized": "^9.18.7", "@types/sinon": "^5.0.5", "@types/sinon-chai": "^3.2.0", @@ -77,7 +93,6 @@ "chai-jest-snapshot": "^2.0.0", "chai-spies": "1.0.0", "chai-string": "^1.5.0", - "cpx": "^1.5.0", "cross-env": "^5.1.4", "enzyme": "^3.4.0", "enzyme-adapter-react-16": "^1.2.0", @@ -86,7 +101,6 @@ "ignore-styles": "^5.0.1", "jsdom": "^11.12.0", "jsdom-global": "3.0.2", - "make-dir-cli": "^1.0.0", "mocha": "^5.2.0", "nyc": "^13.0.1", "raf": "^3.4.0", @@ -94,25 +108,20 @@ "rimraf": "^2.6.2", "sinon": "^7.1.1", "sinon-chai": "^3.2.0", - "source-map-loader": "^0.2.3", "ts-node": "^7.0.1", "tsconfig-paths": "^3.3.2", "tslint": "^5.11.0", "typedoc": "^0.11.1", - "typedoc-plugin-external-module-name": "^1.1.1", "typemoq": "^2.1.0", - "typescript": "~3.1.0", - "xmlhttprequest": "^1.8.0", - "webpack": "^4.20.2", - "webpack-cli": "^3.1.0" + "typescript": "~3.2.2", + "xmlhttprequest": "^1.8.0" }, "//dependencies": [ "NOTE: these dependencies should be only for things that DO NOT APPEAR IN THE API", "NOTE: imodeljs-frontend should remain UI technology agnostic, so no react/angular dependencies are allowed" ], "dependencies": { - "@bentley/bwc": "^7.1.0", - "@bentley/icons-generic-webfont": "^0.0.6", + "@bentley/icons-generic-webfont": "^0.0.9", "callable-instance2": "1.0.0", "classnames": "^2.2.5", "eventemitter2": "^5.0.1", diff --git a/ui/components/public/locales/en/UiComponents.json b/ui/components/public/locales/en/UiComponents.json index 17fe7db..288ddd4 100644 --- a/ui/components/public/locales/en/UiComponents.json +++ b/ui/components/public/locales/en/UiComponents.json @@ -15,7 +15,10 @@ }, "breadcrumb": { "invalidBreadcrumbPath": "Path not found.", - "errorUnknownMode": "Error: Unknown Breadcrumb mode." + "errorUnknownMode": "Error: Unknown Breadcrumb mode.", + "name": "Name", + "icon": "Icon", + "description": "Description" }, "tree": { "noResultsForFilter": "0 matches found for \"{{searchText}}\"" diff --git a/ui/components/src/test/TestUtils.ts b/ui/components/src/test/TestUtils.ts index 249677a..c4af98b 100644 --- a/ui/components/src/test/TestUtils.ts +++ b/ui/components/src/test/TestUtils.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import { I18N } from "@bentley/imodeljs-i18n"; -import { UiComponents, PropertyRecord, PrimitiveValue, PropertyValueFormat, PropertyDescription, ArrayValue, StructValue } from "../ui-components"; +import { PropertyRecord, PrimitiveValue, PropertyValueFormat, PropertyDescription, ArrayValue, StructValue, PropertyEditorParamTypes } from "@bentley/imodeljs-frontend"; +import { UiComponents } from "../ui-components"; import { UiCore } from "@bentley/ui-core"; export default class TestUtils { @@ -38,7 +39,7 @@ export default class TestUtils { /** Waits until all async operations finish */ public static async flushAsyncOperations() { - return new Promise((resolve) => setImmediate(resolve)); + return new Promise((resolve) => setTimeout(resolve)); } public static createPrimitiveStringProperty(name: string, rawValue: string, displayValue: string = rawValue.toString()) { @@ -97,4 +98,73 @@ export default class TestUtils { property.isReadonly = false; return property; } + + public static createEnumProperty(name: string, index: string | number) { + const value: PrimitiveValue = { + displayValue: "", + value: index, + valueFormat: PropertyValueFormat.Primitive, + }; + + const description: PropertyDescription = { + displayLabel: name, + name, + typename: "enum", + }; + + const propertyRecord = new PropertyRecord(value, description); + propertyRecord.isReadonly = false; + propertyRecord.property.enum = { choices: [], isStrict: false }; + propertyRecord.property.enum.choices = [ + { label: "Yellow", value: 0 }, + { label: "Red", value: 1 }, + { label: "Green", value: 2 }, + { label: "Blue", value: 3 }, + ]; + + return propertyRecord; + } + + public static blueEnumValueIsEnabled = true; + public static toggleBlueEnumValueEnabled() { TestUtils.blueEnumValueIsEnabled = !TestUtils.blueEnumValueIsEnabled; } + public static addEnumButtonGroupEditorSpecification(propertyRecord: PropertyRecord) { + propertyRecord.property.editor = { + name: "enum-buttongroup", + params: [ + { + type: PropertyEditorParamTypes.ButtonGroupData, + buttons: [ + { iconClass: "icon-yellow" }, + { iconClass: "icon-red" }, + { iconClass: "icon-green" }, + { + iconClass: "icon-blue", + isEnabledFunction: () => TestUtils.blueEnumValueIsEnabled, + }, + ], + }, + ], + }; + } + + public static createBooleanProperty(name: string, booleanValue: boolean, editor?: string) { + const value: PrimitiveValue = { + displayValue: "", + value: booleanValue, + valueFormat: PropertyValueFormat.Primitive, + }; + + const description: PropertyDescription = { + displayLabel: name, + name, + typename: "boolean", + editor: editor ? { name: editor } : undefined, + }; + + const propertyRecord = new PropertyRecord(value, description); + propertyRecord.isReadonly = false; + + return propertyRecord; + } + } diff --git a/ui/components/src/test/breadcrumb/Breadcrumb.test.tsx b/ui/components/src/test/breadcrumb/Breadcrumb.test.tsx index 523576f..4f1a11a 100644 --- a/ui/components/src/test/breadcrumb/Breadcrumb.test.tsx +++ b/ui/components/src/test/breadcrumb/Breadcrumb.test.tsx @@ -10,6 +10,8 @@ import { render, cleanup, fireEvent, RenderResult, waitForElement } from "react- import { Breadcrumb, BreadcrumbMode, BreadcrumbPath } from "../../ui-components"; import { mockRawTreeDataProvider, mockInterfaceTreeDataProvider, mockMutableInterfaceTreeDataProvider, mockRawTreeDataProvider2 } from "./mockTreeDataProvider"; import TestUtils from "../TestUtils"; +import { BreadcrumbNodeProps, BreadcrumbNode } from "../../ui-components/breadcrumb/Breadcrumb"; +import { TreeNodeItem } from "../../ui-components/tree/TreeDataProvider"; describe("Breadcrumb", () => { let renderSpy: sinon.SinonSpy; @@ -36,7 +38,7 @@ describe("Breadcrumb", () => { describe("load callbacks", () => { it("should call onRootNodesLoaded correctly", async () => { const onRootNodesLoadedSpy = sinon.spy(); - await waitForUpdate(() => renderedComponent = render(), renderSpy, 1); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); expect(onRootNodesLoadedSpy).to.have.been.calledOnce; }); it("should call onChildrenLoaded correctly", async () => { @@ -46,6 +48,14 @@ describe("Breadcrumb", () => { }); }); + it("should render with renderTable defined", async () => { + const renderNode = (props: BreadcrumbNodeProps, _node?: TreeNodeItem, _parent?: TreeNodeItem) => { + return ; + }; + await waitForUpdate(() => render(), renderSpy, 1); + expect(renderSpy).to.be.called; + }); + describe("with current prop", () => { it("should render with current", async () => { await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); @@ -56,10 +66,10 @@ describe("Breadcrumb", () => { describe("Interface DataProvider", () => { it("should rerender from raw dataProvider to interface dataProvider", async () => { const nodeRaw = mockRawTreeDataProvider[1]; - renderedComponent = render(); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); expect(await waitForElement(() => renderedComponent.getByText(nodeRaw.label))).to.exist; const nodeInterface = (await mockInterfaceTreeDataProvider.getNodes())[0]; - await waitForUpdate(() => renderedComponent.rerender(), renderSpy, 3); + await waitForUpdate(() => renderedComponent.rerender(), renderSpy, 2); expect(await waitForElement(() => renderedComponent.getByText(nodeInterface.label))).to.exist; }); @@ -105,14 +115,14 @@ describe("Breadcrumb", () => { }); it("subscribes to `onTreeNodeChanged` on provider change", async () => { - await waitForUpdate(() => renderedComponent = render(), renderSpy, 1); - await waitForUpdate(() => renderedComponent.rerender(), renderSpy, 3); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); + await waitForUpdate(() => renderedComponent.rerender(), renderSpy, 2); expect(mockInterfaceTreeDataProvider.onTreeNodeChanged!.numberOfListeners).to.eq(1); }); it("unsubscribes to `onTreeNodeChanged` on provider change", async () => { await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); - await waitForUpdate(() => renderedComponent.rerender(), renderSpy, 3); + await waitForUpdate(() => renderedComponent.rerender(), renderSpy, 2); expect(mockInterfaceTreeDataProvider.onTreeNodeChanged!.numberOfListeners).to.eq(0); }); }); @@ -122,19 +132,19 @@ describe("Breadcrumb", () => { it("should rerender from interface DataProvider to raw dataProvider", async () => { await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); expect(renderedComponent.getByText("Interface Node 2")).to.exist; - renderedComponent.rerender(); + await waitForUpdate(() => renderedComponent.rerender(), renderSpy, 2); expect(await waitForElement(() => renderedComponent.getByText("Raw Node 2"))).to.exist; }); it("should rerender from raw DataProvider to raw dataProvider", async () => { - await waitForUpdate(() => renderedComponent = render(), renderSpy, 1); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); const rootNode = mockRawTreeDataProvider[1]; expect(await waitForElement(() => renderedComponent.getByText(rootNode.label))).to.exist; - await waitForUpdate(() => renderedComponent.rerender(), renderSpy, 3); + await waitForUpdate(() => renderedComponent.rerender(), renderSpy, 2); const rootNode2 = mockRawTreeDataProvider2[1]; expect(await waitForElement(() => renderedComponent.getByText(rootNode2.label))).to.exist; }); it("should have one child in parent element", async () => { - await waitForUpdate(() => renderedComponent = render(), renderSpy, 1); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); const dropdownInputParent = renderedComponent.getByTestId("breadcrumb-dropdown-input-parent"); expect(dropdownInputParent).to.exist; // should only every have input or dropdown @@ -144,7 +154,7 @@ describe("Breadcrumb", () => { describe("dropdownOnly", () => { it("should not change to input mode when dropdown background is clicked", async () => { - await waitForUpdate(() => renderedComponent = render(), renderSpy, 1); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); const dropdownBackground = renderedComponent.getByTestId("breadcrumb-dropdown-background"); fireEvent.click(dropdownBackground); expect(renderedComponent.queryByTestId("breadcrumb-input-root")).to.not.exist; @@ -153,16 +163,20 @@ describe("Breadcrumb", () => { describe("staticOnly", () => { it("should not render navigation controls", async () => { - await waitForUpdate(() => renderedComponent = render(), renderSpy, 1); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); expect(renderedComponent.queryByTestId("breadcrumb-up-dir")).to.not.exist; expect(renderedComponent.queryAllByTestId("breadcrumb-static-button")).to.exist; }); it("should not change to input mode when dropdown background is clicked", async () => { - await waitForUpdate(() => renderedComponent = render(), renderSpy, 1); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); const dropdownBackground = renderedComponent.getByTestId("breadcrumb-dropdown-background"); fireEvent.click(dropdownBackground); expect(renderedComponent.queryByTestId("breadcrumb-input-root")).to.not.exist; }); + it("should not change to input mode when dropdown background is clicked", async () => { + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); + expect(renderedComponent.getByText(mockRawTreeDataProvider[0].label)).to.exist; + }); }); describe("With path", () => { @@ -171,15 +185,21 @@ describe("Breadcrumb", () => { render(); }); it("should update path to node", async () => { - await waitForUpdate(() => renderedComponent = render(), renderSpy, 1); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); const rootNode = mockRawTreeDataProvider[1]; expect(await waitForElement(() => renderedComponent.getByText(rootNode.label))).to.exist; const node = mockRawTreeDataProvider[1].children![0]; path.setCurrentNode(node); expect(await waitForElement(() => renderedComponent.getByText(node.label))).to.exist; }); + it("should not update if node isn't found", async () => { + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); + const node = { ...mockRawTreeDataProvider[1].children![0], id: "INVALID ID" }; + path.setCurrentNode(node); + expect(renderedComponent.queryByText(node.label)).to.not.exist; + }); it("should update path to root", async () => { - await waitForUpdate(() => renderedComponent = render(), renderSpy, 1); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); const node = mockRawTreeDataProvider[1].children![0]; path.setCurrentNode(node); expect(await waitForElement(() => renderedComponent.getByText(node.label))).to.exist; @@ -191,12 +211,21 @@ describe("Breadcrumb", () => { }); describe("Breadcrumb modes", () => { it("should submit undefined node", async () => { - await waitForUpdate(() => renderedComponent = render(), renderSpy, 1); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); expect(renderedComponent.getByTestId("breadcrumb-error-unknown-mode")).to.exist; }); describe("BreadcrumbInput mode", () => { it("should submit undefined node", async () => { - await waitForUpdate(() => renderedComponent = render(), renderSpy, 1); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); + const breadcrumbInput = renderedComponent.getByTestId("breadcrumb-input") as HTMLInputElement; + breadcrumbInput.value = ""; + breadcrumbInput.setSelectionRange(0, 0); + fireEvent.click(breadcrumbInput); + await waitForUpdate(() => fireEvent.keyUp(breadcrumbInput, { keyCode: 13 /* */ }), renderSpy, 1); + expect(renderedComponent.getByTestId("breadcrumb-dropdown-background")).to.exist; + }); + it("should submit undefined node with parentsOnly={false}", async () => { + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); const breadcrumbInput = renderedComponent.getByTestId("breadcrumb-input") as HTMLInputElement; breadcrumbInput.value = ""; breadcrumbInput.setSelectionRange(0, 0); @@ -205,17 +234,43 @@ describe("Breadcrumb", () => { expect(renderedComponent.getByTestId("breadcrumb-dropdown-background")).to.exist; }); it("should submit node", async () => { - await waitForUpdate(() => renderedComponent = render(), renderSpy, 1); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); const breadcrumbInput = renderedComponent.getByTestId("breadcrumb-input") as HTMLInputElement; const label = mockRawTreeDataProvider[1].label; breadcrumbInput.value = label + "\\"; const l = breadcrumbInput.value.length; breadcrumbInput.setSelectionRange(l, l); fireEvent.click(breadcrumbInput); - await waitForUpdate(() => fireEvent.keyUp(breadcrumbInput, { keyCode: 13 /* */ }), renderSpy, 3); + await waitForUpdate(() => fireEvent.keyUp(breadcrumbInput, { keyCode: 13 /* */ }), renderSpy, 2); expect(renderedComponent.getByTestId("breadcrumb-dropdown-background")).to.exist; expect(renderedComponent.getByText(label)).to.exist; }); + it("should submit node without trailing slash", async () => { + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); + const breadcrumbInput = renderedComponent.getByTestId("breadcrumb-input") as HTMLInputElement; + const label = mockRawTreeDataProvider[1].label; + breadcrumbInput.value = label; + const l = breadcrumbInput.value.length; + breadcrumbInput.setSelectionRange(l, l); + fireEvent.click(breadcrumbInput); + await waitForUpdate(() => fireEvent.keyUp(breadcrumbInput, { keyCode: 13 /* */ }), renderSpy, 2); + expect(renderedComponent.getByTestId("breadcrumb-dropdown-background")).to.exist; + expect(renderedComponent.getByText(label)).to.exist; + }); + it("should submit node with parentsOnly={false}", async () => { + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); + const breadcrumbInput = renderedComponent.getByTestId("breadcrumb-input") as HTMLInputElement; + const label1 = mockRawTreeDataProvider[1].label; + const label2 = mockRawTreeDataProvider[1].children![1].label; + breadcrumbInput.value = label1 + "\\" + label2; + const l = breadcrumbInput.value.length; + breadcrumbInput.setSelectionRange(l, l); + fireEvent.click(breadcrumbInput); + await waitForUpdate(() => fireEvent.keyUp(breadcrumbInput, { keyCode: 13 /* */ }), renderSpy, 2); + expect(renderedComponent.getByTestId("breadcrumb-dropdown-background")).to.exist; + expect(renderedComponent.getByText(label1)).to.exist; + expect(renderedComponent.getByText(label2)).to.exist; + }); it("should submit node from preselected node", async () => { const node = mockRawTreeDataProvider[0]; await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); @@ -225,24 +280,27 @@ describe("Breadcrumb", () => { const l = breadcrumbInput.value.length; breadcrumbInput.setSelectionRange(l, l); fireEvent.click(breadcrumbInput); - await waitForUpdate(() => fireEvent.keyUp(breadcrumbInput, { keyCode: 13 /* */ }), renderSpy, 3); + await waitForUpdate(() => fireEvent.keyUp(breadcrumbInput, { keyCode: 13 /* */ }), renderSpy, 2); expect(renderedComponent.getByTestId("breadcrumb-dropdown-background")).to.exist; expect(renderedComponent.getByText(label)).to.exist; }); - it("should submit node", async () => { - await waitForUpdate(() => renderedComponent = render(), renderSpy, 1); + it("should submit invalid node", async () => { + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); const breadcrumbInput = renderedComponent.getByTestId("breadcrumb-input") as HTMLInputElement; breadcrumbInput.value = "\\invalidInput\\"; const l = breadcrumbInput.value.length; breadcrumbInput.setSelectionRange(l, l); fireEvent.click(breadcrumbInput); fireEvent.keyUp(breadcrumbInput, { keyCode: 13 /* */ }); - expect(renderedComponent.queryByTestId("breadcrumb-dropdown-background")).to.not.exist; + const close = await waitForElement(() => renderedComponent.getByTestId("dialog-close")); + expect(close).to.exist; + fireEvent.click(close); + expect(renderedComponent.queryByTestId("dialog-close")).to.not.exist; }); it("should submit node from path", async () => { const path = new BreadcrumbPath(mockRawTreeDataProvider); path.setCurrentNode(mockRawTreeDataProvider[0]); - await waitForUpdate(() => renderedComponent = render(), renderSpy, 1); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); const breadcrumbInput = renderedComponent.getByTestId("breadcrumb-input") as HTMLInputElement; const updateEvent = sinon.spy(); path.BreadcrumbUpdateEvent.addListener(updateEvent); @@ -254,33 +312,98 @@ describe("Breadcrumb", () => { expect(updateEvent).to.be.calledWithMatch({ currentNode: undefined }); }); it("should change to input mode when dropdown background is clicked", async () => { - await waitForUpdate(() => renderedComponent = render(), renderSpy, 1); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); const dropdownBackground = renderedComponent.getByTestId("breadcrumb-dropdown-background"); await waitForUpdate(() => fireEvent.click(dropdownBackground), renderSpy, 1); expect(renderedComponent.getByTestId("breadcrumb-input-root")).to.exist; }); it("should change to dropdown mode when outside is clicked", async () => { - await waitForUpdate(() => renderedComponent = render(), renderSpy, 1); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); const dropdownInputParent = renderedComponent.getByTestId("breadcrumb-dropdown-input-parent"); await waitForUpdate(() => fireEvent.click(dropdownInputParent), renderSpy, 1); expect(renderedComponent.getByTestId("breadcrumb-dropdown-background")).to.exist; }); it("should change back from input mode when (X) button is clicked", async () => { - await waitForUpdate(() => renderedComponent = render(), renderSpy, 1); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); const inputRoot = renderedComponent.getByTestId("breadcrumb-input-close"); await waitForUpdate(() => fireEvent.click(inputRoot), renderSpy, 1); expect(renderedComponent.getByTestId("breadcrumb-dropdown-background")).to.exist; }); + it("should focus inputElement on pressed", async () => { + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); + const menu = renderedComponent.getByTestId("context-menu-root"); + fireEvent.keyUp(menu, { keyCode: 27 }); + }); + it("should navigate to node when autocomplete item chosen", async () => { + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); + const menuItem = renderedComponent.getAllByTestId("context-menu-item"); + fireEvent.click(menuItem[0]); + expect(await waitForElement(() => renderedComponent.getByDisplayValue(mockRawTreeDataProvider[1].label + "\\"))).to.exist; + }); + describe("Keyboard Navigation", () => { + it("Should close context menu on ", async () => { + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); + const input = renderedComponent.getByTestId("breadcrumb-input"); + fireEvent.keyUp(input, { keyCode: 27 }); + expect(renderedComponent.getByTestId("context-menu-container").classList.contains("opened")).to.be.false; + }); + it("Should prevent default on and keydown", async () => { + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); + const input = renderedComponent.getByTestId("breadcrumb-input"); + fireEvent.keyDown(input, { keyCode: 38 }); + fireEvent.keyDown(input, { keyCode: 40 }); + }); + it("Should prevent default on and keyup", async () => { + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); + const input = renderedComponent.getByTestId("breadcrumb-input"); + fireEvent.keyUp(input, { keyCode: 38 }); + fireEvent.keyUp(input, { keyCode: 40 }); + }); + it("Should reopen autocomplete on Up/Down keypress", async () => { + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); + const input = renderedComponent.getByTestId("breadcrumb-input"); + fireEvent.keyUp(input, { keyCode: 27 }); + expect(renderedComponent.getByTestId("context-menu-container").classList.contains("opened")).to.be.false; + fireEvent.keyUp(input, { keyCode: 38 }); + fireEvent.keyUp(input, { keyCode: 40 }); + expect(renderedComponent.getByTestId("context-menu-container").classList.contains("opened")).to.be.true; + }); + }); }); describe("BreadcrumbDropDown mode", () => { it("should set current to root/undefined", async () => { const node = mockRawTreeDataProvider[0]; - renderedComponent = render(); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); expect(waitForElement(() => renderedComponent.getByText(node.label))).to.exist; const menuItems = renderedComponent.getAllByTestId("context-menu-item"); - await waitForUpdate(() => fireEvent.click(menuItems[0]), renderSpy, 2); + await waitForUpdate(() => fireEvent.click(menuItems[0]), renderSpy, 1); expect(renderedComponent.queryByText(node.label)).to.not.exist; }); + it("should set current to root/undefined from split button press", async () => { + const node = mockRawTreeDataProvider[0]; + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); + const nodes = renderedComponent.getAllByTestId("split-button-label"); + fireEvent.click(nodes[0]); + expect(renderedComponent.queryAllByTestId("breadcrumb-node")).to.have.lengthOf(1); + }); + it("should navigate to root on updir button press when initialCurrent is a root level node", async () => { + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); + const upDir = renderedComponent.getByTestId("breadcrumb-up-dir"); + fireEvent.click(upDir); + expect(renderedComponent.queryAllByTestId("breadcrumb-node")).to.have.lengthOf(1); + }); + it("should navigate to parent of node on updir button press when initialCurrent is a non-root level node", async () => { + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); + const upDir = renderedComponent.getByTestId("breadcrumb-up-dir"); + fireEvent.click(upDir); + expect(renderedComponent.queryAllByTestId("breadcrumb-node")).to.have.lengthOf(2); + }); + it("should not change node on updir button press when on root node", async () => { + await waitForUpdate(() => renderedComponent = render(), renderSpy, 2); + const upDir = renderedComponent.getByTestId("breadcrumb-up-dir"); + fireEvent.click(upDir); + expect(renderedComponent.queryAllByTestId("breadcrumb-node")).to.have.lengthOf(1); + }); }); }); }); diff --git a/ui/components/src/test/breadcrumb/BreadcrumbTreeUtils.test.ts b/ui/components/src/test/breadcrumb/BreadcrumbTreeUtils.test.ts new file mode 100644 index 0000000..7c74a83 --- /dev/null +++ b/ui/components/src/test/breadcrumb/BreadcrumbTreeUtils.test.ts @@ -0,0 +1,187 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { expect } from "chai"; +import { BreadcrumbTreeUtils } from "../../ui-components/breadcrumb/BreadcrumbTreeUtils"; +import { TreeDataProvider, ImmediatelyLoadedTreeNodeItem } from "../../ui-components/tree/TreeDataProvider"; +import { RowItem, CellItem } from "../../ui-components/table/TableDataProvider"; +import { PropertyRecord, PropertyValueFormat } from "@bentley/imodeljs-frontend"; +import TestUtils from "../TestUtils"; +import { mockInterfaceTreeDataProvider } from "./mockTreeDataProvider"; + +describe("BreadcrumbTreeUtils", () => { + before(async () => { + await TestUtils.initializeUiComponents(); // tslint:disable-line:no-floating-promises + }); + + const getColFromRowByKey = async (row: RowItem, key: string): Promise => { + for (const col of row.cells) { + if (col.key === key) { + return col; + } + } + return undefined; + }; + it("should flatten root nodes", async () => { + const treeDataProvider: TreeDataProvider = [{ id: "1", icon: "icon-placeholder", label: "Raw Node 1" }]; + const columns = [{ key: "icon", label: "", icon: true }, { key: "label", label: "Name" }]; + + const table = BreadcrumbTreeUtils.aliasNodeListToTableDataProvider(treeDataProvider, columns, treeDataProvider); + + const row = await table.getRow(0); + + const icon = await getColFromRowByKey(row, "icon"); + expect(icon).to.exist; + expect((icon!.record!.value as any).value).to.equal("icon-placeholder"); + expect(row.extendedData!.icon).to.equal("icon-placeholder"); + + const label = await getColFromRowByKey(row, "label"); + expect(label).to.exist; + expect((label!.record!.value as any).value).to.equal("Raw Node 1"); + expect(row.extendedData!.label).to.equal("Raw Node 1"); + }); + + it("should pass description through when used", async () => { + const treeDataProvider: TreeDataProvider = [{ label: "Raw Node 1", id: "1", description: "node 1 child" }]; + const columns = [ + { key: "icon", label: "", icon: true }, + { key: "label", label: "Name" }, + { key: "description", label: "Description" }, + ]; + const table = BreadcrumbTreeUtils.aliasNodeListToTableDataProvider(treeDataProvider, columns, treeDataProvider); + + const row = await table.getRow(0); + + const description = await getColFromRowByKey(row, "description"); + expect(description).to.exist; + expect((description!.record!.value as any).value).to.equal("node 1 child"); + }); + + it("should add children to extendedData", async () => { + const treeDataProvider: TreeDataProvider = [ + { + label: "Raw Node 1", id: "1", description: "node 1 child", + children: [ + { label: "Raw Node 1.1", id: "1.1", parentId: "1", description: "node 1.1 child" }, + ] as ImmediatelyLoadedTreeNodeItem[], + }, + ]; + const columns = [ + { key: "icon", label: "", icon: true }, + { key: "label", label: "Name" }, + { key: "description", label: "Description" }, + ]; + + const table = BreadcrumbTreeUtils.aliasNodeListToTableDataProvider(treeDataProvider, columns, treeDataProvider); + + const row = await table.getRow(0); + expect(row.extendedData!.children.length).to.equal(1); + }); + + it("should add hasChildren to extendedData for Interface TreeDataProvider", async () => { + const rootNodes = await mockInterfaceTreeDataProvider.getNodes(); + const columns = [ + { key: "icon", label: "", icon: true }, + { key: "label", label: "Name" }, + { key: "description", label: "Description" }, + ]; + + const table = BreadcrumbTreeUtils.aliasNodeListToTableDataProvider(rootNodes, columns, mockInterfaceTreeDataProvider); + + const row = await table.getRow(1); + expect(row.extendedData!.hasChildren).to.be.true; + }); + + it("should count rows with getRowsCount", async () => { + const treeDataProvider: TreeDataProvider = [{ label: "Raw Node 1", id: "1", description: "node 1 child" }]; + const columns = [ + { key: "icon", label: "", icon: true }, + { key: "label", label: "Name" }, + { key: "description", label: "Description" }, + ]; + + const table = BreadcrumbTreeUtils.aliasNodeListToTableDataProvider(treeDataProvider, columns, treeDataProvider); + expect(await table.getRowsCount()).to.equal(treeDataProvider.length); + }); + + it("should pass columns through to getColumns", async () => { + const treeDataProvider: TreeDataProvider = [{ label: "Raw Node 1", id: "1", description: "node 1 child" }]; + const columns = [ + { key: "icon", label: "", icon: true }, + { key: "label", label: "Name" }, + { key: "description", label: "Description" }, + ]; + + const table = BreadcrumbTreeUtils.aliasNodeListToTableDataProvider(treeDataProvider, columns, treeDataProvider); + expect(await table.getColumns()).to.equal(columns); + }); + + it("should send blank row when getRow gets a rowIndex out of range", async () => { + const treeDataProvider: TreeDataProvider = [{ label: "Raw Node 1", id: "1", description: "node 1 child" }]; + const columns = [ + { key: "icon", label: "", icon: true }, + { key: "label", label: "Name" }, + { key: "description", label: "Description" }, + ]; + + const table = BreadcrumbTreeUtils.aliasNodeListToTableDataProvider(treeDataProvider, columns, treeDataProvider); + const invalidRowLt = await table.getRow(-2); + expect(invalidRowLt.key).to.equal(""); + + const invalidRowGt = await table.getRow(2); + expect(invalidRowGt.key).to.equal(""); + }); + + describe("extendedData", async () => { + const columns = [{ key: "icon", label: "", icon: true }, { key: "label", label: "Name" }, { key: "description", label: "Description" }]; + const treeDataProvider: TreeDataProvider = [ + { + id: "1", icon: "icon-placeholder", label: "Raw Node 1", description: "node 1 child", extendedData: { + testProp: new PropertyRecord( + { value: "test prop", valueFormat: PropertyValueFormat.Primitive, displayValue: "test prop" }, + { name: "test", displayLabel: "Test", typename: "text" }), + testStr: "TEST", + testBool: false, + testNum: 0, + }, + }, + ]; + it("should transfer PropertyRecord through, and add as a cell", async () => { + const table = BreadcrumbTreeUtils.aliasNodeListToTableDataProvider(treeDataProvider, columns, treeDataProvider); + + const row = await table.getRow(0); + const testProp = await getColFromRowByKey(row, "testProp"); + expect(testProp).to.exist; + expect((testProp!.record!.value as any).value).to.equal("test prop"); + expect((row.extendedData!.testProp.value as any).value).to.equal("test prop"); + }); + it("should transfer string through, and add as a text cell", async () => { + const table = BreadcrumbTreeUtils.aliasNodeListToTableDataProvider(treeDataProvider, columns, treeDataProvider); + + const row = await table.getRow(0); + const testStr = await getColFromRowByKey(row, "testStr"); + expect(testStr).to.exist; + expect((testStr!.record!.value as any).value).to.equal("TEST"); + expect(row.extendedData!.testStr).to.equal("TEST"); + }); + it("should transfer boolean through, and add as a text cell", async () => { + const table = BreadcrumbTreeUtils.aliasNodeListToTableDataProvider(treeDataProvider, columns, treeDataProvider); + + const row = await table.getRow(0); + const testBool = await getColFromRowByKey(row, "testBool"); + expect(testBool).to.exist; + expect((testBool!.record!.value as any).value).to.equal("false"); + expect(row.extendedData!.testBool).to.equal(false); + }); + it("should transfer number through, and add as a text cell", async () => { + const table = BreadcrumbTreeUtils.aliasNodeListToTableDataProvider(treeDataProvider, columns, treeDataProvider); + + const row = await table.getRow(0); + const testNum = await getColFromRowByKey(row, "testNum"); + expect(testNum).to.exist; + expect((testNum!.record!.value as any).value).to.equal("0"); + expect(row.extendedData!.testNum).to.equal(0); + }); + }); +}); diff --git a/ui/components/src/test/breadcrumb/breadcrumbdetails/BreadcrumbDetails.test.tsx b/ui/components/src/test/breadcrumb/breadcrumbdetails/BreadcrumbDetails.test.tsx index 7cd917f..13bfec4 100644 --- a/ui/components/src/test/breadcrumb/breadcrumbdetails/BreadcrumbDetails.test.tsx +++ b/ui/components/src/test/breadcrumb/breadcrumbdetails/BreadcrumbDetails.test.tsx @@ -6,13 +6,21 @@ import * as React from "react"; import { expect } from "chai"; import * as sinon from "sinon"; import { waitForUpdate } from "../../test-helpers/misc"; -import { render, cleanup, RenderResult, waitForElement } from "react-testing-library"; +import { render, cleanup, RenderResult, waitForElement, wait } from "react-testing-library"; +import TestUtils from "../../TestUtils"; import { BreadcrumbDetails, BreadcrumbPath } from "../../../ui-components"; import { mockRawTreeDataProvider, mockInterfaceTreeDataProvider } from "../mockTreeDataProvider"; +import { ImmediatelyLoadedTreeNodeItem, TreeNodeItem } from "../../../ui-components/tree/TreeDataProvider"; +import { TableProps, Table } from "../../../ui-components/table/component/Table"; describe("BreadcrumbDetails", () => { let renderSpy: sinon.SinonSpy; let renderedComponent: RenderResult; + + before(async () => { + await TestUtils.initializeUiComponents(); // tslint:disable-line:no-floating-promises + }); + beforeEach(() => { sinon.restore(); renderSpy = sinon.spy(); @@ -25,11 +33,22 @@ describe("BreadcrumbDetails", () => { const path = new BreadcrumbPath(mockRawTreeDataProvider); render(); }); + it("should render with interface dataProvider", () => { const path = new BreadcrumbPath(mockInterfaceTreeDataProvider); render(); }); + it("should render with renderTable defined", async () => { + const path = new BreadcrumbPath(mockRawTreeDataProvider); + const renderTable = (props: TableProps, _node: TreeNodeItem | undefined, _children: TreeNodeItem[]) => { + return ; + }; + render(); + + await wait(() => renderSpy.called); + }); + it("should update path to child", async () => { const path = new BreadcrumbPath(mockRawTreeDataProvider); path.setCurrentNode(undefined); @@ -38,33 +57,120 @@ describe("BreadcrumbDetails", () => { expect(await waitForElement(() => renderedComponent.getByText(node.label))).to.exist; }); + it("should render when node is defined", async () => { + const path = new BreadcrumbPath(mockRawTreeDataProvider); + path.setCurrentNode(mockRawTreeDataProvider[1]); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 9); + const node = mockRawTreeDataProvider[1].children![0]; + expect(await waitForElement(() => renderedComponent.getByText(node.label))).to.exist; + }); + it("should change path", async () => { const path1 = new BreadcrumbPath(mockRawTreeDataProvider); await waitForUpdate(() => renderedComponent = render(), renderSpy, 12); const path2 = new BreadcrumbPath(mockRawTreeDataProvider); await waitForUpdate(() => renderedComponent.rerender(), renderSpy, 2); }); + + it("should rerender from interface dataProvider to raw dataProvider", async () => { + const nodeInterface = (await mockInterfaceTreeDataProvider.getNodes())[1]; + const path = new BreadcrumbPath(mockInterfaceTreeDataProvider); + renderedComponent = render(); + expect(await waitForElement(() => renderedComponent.getByText(nodeInterface.label))).to.exist; + path.setDataProvider(mockRawTreeDataProvider); + await waitForUpdate(() => renderedComponent.rerender(), renderSpy, 9); + }); + + it("should rerender from raw dataProvider to interface dataProvider", async () => { + const nodeRaw = mockRawTreeDataProvider[1]; + const path = new BreadcrumbPath(mockRawTreeDataProvider); + renderedComponent = render(); + expect(await waitForElement(() => renderedComponent.getByText(nodeRaw.label))).to.exist; + path.setDataProvider(mockInterfaceTreeDataProvider); + await waitForUpdate(() => renderedComponent.rerender(), renderSpy, 4); + }); + + it("rerenders when currentNode is set to undefined", async () => { + const path = new BreadcrumbPath(mockInterfaceTreeDataProvider); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 7); + renderSpy.resetHistory(); + path.setCurrentNode(undefined); + expect(renderSpy).to.have.been.called; + }); + + it("rerenders when currentNode is set to node", async () => { + const node = (await mockInterfaceTreeDataProvider.getNodes())[0]; + const path = new BreadcrumbPath(mockInterfaceTreeDataProvider); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 7); + renderSpy.resetHistory(); + path.setCurrentNode(node); + expect(renderSpy).to.have.been.called; + }); + + it("pops a tree level when currentNode is set to node without children", async () => { + const node = mockRawTreeDataProvider[1].children![1]; + const path = new BreadcrumbPath(mockRawTreeDataProvider); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 12); + renderSpy.resetHistory(); + path.setCurrentNode(node); + expect(renderSpy).to.have.been.called; + }); + + it("pops a tree level to a non root node when currentNode is set to a deep node without children", async () => { + const node = (mockRawTreeDataProvider[1].children![0] as ImmediatelyLoadedTreeNodeItem).children![0]; + const path = new BreadcrumbPath(mockRawTreeDataProvider); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 12); + renderSpy.resetHistory(); + path.setCurrentNode(node); + expect(renderSpy).to.have.been.called; + }); + + it("calls onRowsSelected when row is clicked and sets currentNode to path", async () => { + const node = mockRawTreeDataProvider[1]; + const path = new BreadcrumbPath(mockRawTreeDataProvider); + const pathUpdateSpy = sinon.stub(); + path.BreadcrumbUpdateEvent.addListener(pathUpdateSpy); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 12); + const listRow = renderedComponent.getByText(node.label); + const event = new MouseEvent("click", { bubbles: true }); + await waitForUpdate(() => listRow.dispatchEvent(event), pathUpdateSpy, 1); + expect(pathUpdateSpy).to.have.been.called; + }); + describe("Interface DataProvider", () => { - it("should rerender from raw dataProvider to interface dataProvider", async () => { - const nodeRaw = mockRawTreeDataProvider[1]; - const path = new BreadcrumbPath(mockRawTreeDataProvider); - path.setCurrentNode(nodeRaw); - renderedComponent = render(); - expect(await waitForElement(() => renderedComponent.getByText(nodeRaw.label))).to.exist; - const nodeInterface = (await mockInterfaceTreeDataProvider.getNodes())[1]; - path.setDataProvider(mockInterfaceTreeDataProvider); - path.setCurrentNode(nodeInterface); - renderedComponent.rerender(); + it("rerenders when `onTreeNodeChanged` is broadcasted with undefined", async () => { + const path = new BreadcrumbPath(mockInterfaceTreeDataProvider); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 7); + renderSpy.resetHistory(); + mockInterfaceTreeDataProvider.onTreeNodeChanged!.raiseEvent([undefined]); + expect(renderSpy).to.have.been.called; }); - describe("listening to `ITreeDataProvider.onTreeNodeChanged` events", () => { - it("rerenders when `onTreeNodeChanged` is broadcasted with node", async () => { - const node = (await mockInterfaceTreeDataProvider.getNodes())[0]; - const path = new BreadcrumbPath(mockRawTreeDataProvider); - path.setCurrentNode(node); - await waitForUpdate(() => renderedComponent = render(), renderSpy, 12); - mockInterfaceTreeDataProvider.onTreeNodeChanged!.raiseEvent([node]); - }); + + it("rerenders when `onTreeNodeChanged` is broadcasted with node", async () => { + const node = (await mockInterfaceTreeDataProvider.getNodes())[0]; + const path = new BreadcrumbPath(mockInterfaceTreeDataProvider); + path.setCurrentNode(node); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 7); + renderSpy.resetHistory(); + mockInterfaceTreeDataProvider.onTreeNodeChanged!.raiseEvent([node]); + expect(renderSpy).to.have.been.called; }); }); }); + describe("load callbacks", () => { + it("should call onRootNodesLoaded correctly", async () => { + const onRootNodesLoadedSpy = sinon.spy(); + const path = new BreadcrumbPath(mockRawTreeDataProvider); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 12); + expect(onRootNodesLoadedSpy).to.have.been.calledOnce; + }); + it("should call onChildrenLoaded correctly", async () => { + const onChildrenLoadedSpy = sinon.spy(); + const node = (await mockInterfaceTreeDataProvider.getNodes())[0]; + const path = new BreadcrumbPath(mockInterfaceTreeDataProvider); + path.setCurrentNode(node); + await waitForUpdate(() => renderedComponent = render(), renderSpy, 7); + expect(onChildrenLoadedSpy).to.have.have.callCount(3); + }); + }); }); diff --git a/ui/components/src/test/breadcrumb/mockTreeDataProvider.ts b/ui/components/src/test/breadcrumb/mockTreeDataProvider.ts index a59a445..4b3db7d 100644 --- a/ui/components/src/test/breadcrumb/mockTreeDataProvider.ts +++ b/ui/components/src/test/breadcrumb/mockTreeDataProvider.ts @@ -227,9 +227,10 @@ export const mockRawTreeDataProvider: ImmediatelyLoadedTreeNodeItem[] = [ { label: "Raw Node 2.1", id: "2.1", parentId: "2", description: "node 2.1 child", children: [ - { label: "Raw Node 2.1.1", id: "2.1.1", description: "node 2.1.1 child" }, + { label: "Raw Node 2.1.1", id: "2.1.1", parentId: "2.1", description: "node 2.1.1 child" }, ] as ImmediatelyLoadedTreeNodeItem[], }, + { label: "Raw Node 2.2", id: "2.2", parentId: "2", description: "node 2.2 child" }, ] as ImmediatelyLoadedTreeNodeItem[], }, { label: "Raw Node 3", id: "3", description: "node 3 child" }, diff --git a/ui/components/src/test/color/Swatch.test.tsx b/ui/components/src/test/color/Swatch.test.tsx new file mode 100644 index 0000000..47225d9 --- /dev/null +++ b/ui/components/src/test/color/Swatch.test.tsx @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import React from "react"; +import { render, cleanup, fireEvent } from "react-testing-library"; // , waitForElement +import { expect } from "chai"; +import sinon from "sinon"; +import { ColorSwatch } from "../../ui-components/color/Swatch"; +import TestUtils from "../TestUtils"; + +describe("", () => { + afterEach(cleanup); + + it("should render", () => { + const renderedComponent = render(); + expect(renderedComponent).not.to.be.undefined; + }); + + it("Fire click event to pick color", async () => { + const spyOnPick = sinon.spy(); + function handleColorPick(color: string): void { + expect(color).to.be.equal("rgba(255,0,0,255)"); + spyOnPick(); + } + + const renderedComponent = render(); + const colorSwatch = renderedComponent.container.firstChild as HTMLElement; + expect(colorSwatch).not.to.be.null; + expect(colorSwatch.tagName).to.be.equal("BUTTON"); + fireEvent.click(colorSwatch); + await TestUtils.flushAsyncOperations(); + expect(spyOnPick.calledOnce).to.be.true; + }); + +}); diff --git a/ui/components/src/test/common/ImageRenderer.test.snap b/ui/components/src/test/common/ImageRenderer.test.snap new file mode 100644 index 0000000..ad9b7e4 --- /dev/null +++ b/ui/components/src/test/common/ImageRenderer.test.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ImageRenderer render renders svg 1`] = ` +"
    + + + +
    " +`; diff --git a/ui/components/src/test/common/ImageRenderer.test.tsx b/ui/components/src/test/common/ImageRenderer.test.tsx new file mode 100644 index 0000000..bee0432 --- /dev/null +++ b/ui/components/src/test/common/ImageRenderer.test.tsx @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import * as React from "react"; +import { expect } from "chai"; +import { LoadedBinaryImage } from "../../ui-components/common/IImageLoader"; +import { render } from "react-testing-library"; +import { ImageRenderer } from "../../ui-components/common/ImageRenderer"; + +describe("ImageRenderer", () => { + const imageRenderer = new ImageRenderer(); + const svg = ` + + + + + + + `; + + const hex = "89504e470d0a1a0a0000000d49484452000000080000000808020000004b6d29dc000000097048597300002e2300002e230178a53f76000000434944415408d7858dc109804010c4b26213c1fe4b93edc2f171277e3c9c67481858ac0088bea8fbe11a2e8c468206d887657956034766bbad3e66d1f4703bedfff9e76ec62115e8243cfe640000000049454e44ae426082"; + const hexBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAQ0lEQVQI14WNwQmAQBDEsmITwf5Lk+3C8XEnfjycZ0gYWKwAiL6o++EaLoxGggbYh2V5VgNHZrutPmbR9HA77f/5527GIRXoJDz+ZAAAAABJRU5ErkJggg=="; + + describe("render", () => { + it("renders binary", () => { + const image = imageRenderer.render({ sourceType: "binary", fileFormat: "png", value: hex } as LoadedBinaryImage); + + const imageRender = render(<>{image}); + + const imgElement = imageRender.container.children[0] as HTMLImageElement; + + expect(imgElement.tagName).to.equal("IMG"); + expect(imgElement.src).to.equal(`data:image/png;base64,${hexBase64}`); + }); + + it("renders svg", () => { + const image = imageRenderer.render({ sourceType: "svg", value: svg }); + + const imageRender = render(<>{image}); + + expect(imageRender.container.innerHTML).to.matchSnapshot(); + }); + + it("renders url", () => { + const image = imageRenderer.render({ sourceType: "url", value: "some-image.png" }); + + const imageRender = render(<>{image}); + + const imgElement = imageRender.container.children[0] as HTMLImageElement; + expect(imgElement.tagName).to.equal("IMG"); + expect(imgElement.src).to.equal("some-image.png"); + }); + + it("renders core-icon", () => { + const image = imageRenderer.render({ sourceType: "core-icon", value: "icon-placeholder" }); + + const imageRender = render(<>{image}); + + expect(imageRender.container.querySelector(".icon-placeholder")).to.not.be.null; + }); + + it("throws when provided image source is not supported", () => { + expect(() => imageRenderer.render({ sourceType: "random-type" } as any)).to.throw("ImageRenderer: Can't handle sourceType"); + }); + }); + +}); diff --git a/ui/components/src/test/converters/EnumTypeConverter.test.ts b/ui/components/src/test/converters/EnumTypeConverter.test.ts index c0bf87f..ff332da 100644 --- a/ui/components/src/test/converters/EnumTypeConverter.test.ts +++ b/ui/components/src/test/converters/EnumTypeConverter.test.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; -import { EnumTypeConverter, PropertyDescription } from "../../ui-components"; +import { PropertyDescription } from "@bentley/imodeljs-frontend"; +import { EnumTypeConverter } from "../../ui-components"; import TestUtils from "../TestUtils"; describe("EnumTypeConverter", () => { diff --git a/ui/components/src/test/converters/TypeConverter.test.ts b/ui/components/src/test/converters/TypeConverter.test.ts index 3a91100..37b01ac 100644 --- a/ui/components/src/test/converters/TypeConverter.test.ts +++ b/ui/components/src/test/converters/TypeConverter.test.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; import * as moq from "typemoq"; -import { TypeConverter, PropertyDescription, PrimitiveValue } from "../../ui-components"; +import { PropertyDescription, PrimitiveValue } from "@bentley/imodeljs-frontend"; +import { TypeConverter } from "../../ui-components"; import TestUtils from "../TestUtils"; describe("TypeConverter", () => { diff --git a/ui/components/src/test/editors/BooleanEditor.test.snap b/ui/components/src/test/editors/BooleanEditor.test.snap new file mode 100644 index 0000000..e6afe84 --- /dev/null +++ b/ui/components/src/test/editors/BooleanEditor.test.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders correctly 1`] = ` + +`; diff --git a/ui/components/src/test/editors/BooleanEditor.test.tsx b/ui/components/src/test/editors/BooleanEditor.test.tsx new file mode 100644 index 0000000..81b0882 --- /dev/null +++ b/ui/components/src/test/editors/BooleanEditor.test.tsx @@ -0,0 +1,92 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import * as React from "react"; +import { mount, shallow } from "enzyme"; +import { expect } from "chai"; +import sinon from "sinon"; +import { BooleanEditor } from "../../ui-components/editors/BooleanEditor"; +import { EditorContainer, PropertyUpdatedArgs } from "../../ui-components/editors/EditorContainer"; +import TestUtils from "../TestUtils"; +import { PrimitiveValue } from "@bentley/imodeljs-frontend"; + +describe("", () => { + it("should render", () => { + mount(); + }); + + it("renders correctly", () => { + shallow().should.matchSnapshot(); + }); + + it("getValue returns proper value after componentDidMount & setState", async () => { + const record = TestUtils.createBooleanProperty("Test", false); + const wrapper = mount(); + + await TestUtils.flushAsyncOperations(); + const enumEditor = wrapper.instance() as BooleanEditor; + expect(enumEditor.getValue()).to.equal(false); + + wrapper.unmount(); + }); + + it("HTML input onChange updates boolean value", async () => { + const record = TestUtils.createBooleanProperty("Test1", false); + const spyOnCommit = sinon.spy(); + function handleCommit(_commit: PropertyUpdatedArgs): void { + spyOnCommit(); + } + const wrapper = mount(); + const enumEditor = wrapper.instance() as BooleanEditor; + const inputNode = wrapper.find("input"); + + expect(inputNode.length).to.eq(1); + if (inputNode) { + const testValue = true; + inputNode.simulate("change", { target: { value: testValue } }); + wrapper.update(); + expect(enumEditor.getValue()).to.equal(testValue); + await TestUtils.flushAsyncOperations(); + expect(spyOnCommit.calledOnce).to.be.true; + } + }); + + it("onCommit should be called for Space", async () => { + let booleanValue = false; + const propertyRecord = TestUtils.createBooleanProperty("Test2", booleanValue); + const spyOnCommit = sinon.spy(); + function handleCommit(commit: PropertyUpdatedArgs): void { + booleanValue = (commit.newValue as PrimitiveValue).value as boolean; + spyOnCommit(); + } + const wrapper = mount( { }} />); + const inputNode = wrapper.find("input"); + expect(inputNode.length).to.eq(1); + + const testValue = true; + inputNode.simulate("change", { target: { value: testValue } }); + + await TestUtils.flushAsyncOperations(); + expect(spyOnCommit.calledOnce).to.be.true; + expect(booleanValue).to.be.true; + }); + + it("componentDidUpdate updates the value", async () => { + const record = TestUtils.createBooleanProperty("Test", false); + const wrapper = mount(); + + await TestUtils.flushAsyncOperations(); + const enumEditor = wrapper.instance() as BooleanEditor; + expect(enumEditor.getValue()).to.equal(false); + + const newRecord = TestUtils.createBooleanProperty("Test", true); + wrapper.setProps({ propertyRecord: newRecord }); + await TestUtils.flushAsyncOperations(); + expect(enumEditor.getValue()).to.equal(true); + + wrapper.unmount(); + }); + +}); diff --git a/ui/components/src/test/editors/EditorContainer.test.snap b/ui/components/src/test/editors/EditorContainer.test.snap index 79b23a8..c623a90 100644 --- a/ui/components/src/test/editors/EditorContainer.test.snap +++ b/ui/components/src/test/editors/EditorContainer.test.snap @@ -3,6 +3,7 @@ exports[` renders correctly 1`] = ` renders correctly 1`] = ` > renders correctly 1`] = ` }, } } + setFocus={true} /> `; diff --git a/ui/components/src/test/editors/EditorContainer.test.tsx b/ui/components/src/test/editors/EditorContainer.test.tsx index 61c1bd9..3defd04 100644 --- a/ui/components/src/test/editors/EditorContainer.test.tsx +++ b/ui/components/src/test/editors/EditorContainer.test.tsx @@ -27,7 +27,7 @@ describe("", () => { expect(wrapper.find(".components-text-editor").length).to.eq(1); }); - it("calls onCommit for Enter", () => { + it("calls onCommit for Enter", async () => { const propertyRecord = TestUtils.createPrimitiveStringProperty("Test1", "my value"); const spyOnCommit = sinon.spy(); function handleCommit(_commit: PropertyUpdatedArgs): void { @@ -38,10 +38,11 @@ describe("", () => { expect(inputNode.length).to.eq(1); inputNode.simulate("keyDown", { key: "Enter" }); + await TestUtils.flushAsyncOperations(); expect(spyOnCommit.calledOnce).to.be.true; }); - it("calls onCancel for Escape", () => { + it("calls onCancel for Escape", async () => { const propertyRecord = TestUtils.createPrimitiveStringProperty("Test1", "my value"); const spyonCancel = sinon.spy(); const wrapper = mount( { }} onCancel={spyonCancel} />); @@ -52,7 +53,7 @@ describe("", () => { expect(spyonCancel.calledOnce).to.be.true; }); - it("calls onCommit for Tab", () => { + it("calls onCommit for Tab", async () => { const propertyRecord = TestUtils.createPrimitiveStringProperty("Test1", "my value"); const spyOnCommit = sinon.spy(); function handleCommit(_commit: PropertyUpdatedArgs): void { @@ -63,6 +64,7 @@ describe("", () => { expect(inputNode.length).to.eq(1); inputNode.simulate("keyDown", { key: "Tab" }); + await TestUtils.flushAsyncOperations(); expect(spyOnCommit.calledOnce).to.be.true; }); diff --git a/ui/components/src/test/editors/EnumButtonGroupEditor.test.tsx b/ui/components/src/test/editors/EnumButtonGroupEditor.test.tsx new file mode 100644 index 0000000..62a9afe --- /dev/null +++ b/ui/components/src/test/editors/EnumButtonGroupEditor.test.tsx @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +import React from "react"; +import { render, cleanup, fireEvent, waitForElement } from "react-testing-library"; +import { expect } from "chai"; +import sinon from "sinon"; +import { EnumButtonGroupEditor } from "../../ui-components/editors/EnumButtonGroupEditor"; +import { PropertyUpdatedArgs } from "../../ui-components/editors/EditorContainer"; +import TestUtils from "../TestUtils"; +import { PrimitiveValue } from "@bentley/imodeljs-frontend"; + +describe("", () => { + afterEach(cleanup); + + it("should render", () => { + const renderedComponent = render(); + expect(renderedComponent).not.to.be.undefined; + }); + + it("editor with buttons renders correctly", async () => { + const record = TestUtils.createEnumProperty("Test", 0); + TestUtils.addEnumButtonGroupEditorSpecification(record); + const renderedComponent = render(); + expect(renderedComponent).not.to.be.undefined; + }); + + it("button press updates value and display", async () => { + const record = TestUtils.createEnumProperty("Test", 0); + TestUtils.addEnumButtonGroupEditorSpecification(record); + + const originalValue = (record.value as PrimitiveValue).value as number; + expect(originalValue).to.be.equal(0); + + const spyOnCommit = sinon.spy(); + function handleCommit(commit: PropertyUpdatedArgs): void { + const newValue = (commit.newValue as PrimitiveValue).value as number; + expect(newValue).to.be.equal(2); + spyOnCommit(); + } + + const renderedComponent = render(); + const greenButton = renderedComponent.getByTestId("Green"); + expect(greenButton.tagName).to.be.equal("BUTTON"); + expect(greenButton.classList.contains("nz-is-active")).to.be.false; + + fireEvent.click(greenButton); + await TestUtils.flushAsyncOperations(); + expect(greenButton.classList.contains("nz-is-active")).to.be.true; + expect(spyOnCommit.calledOnce).to.be.true; + }); + + it("test support for enable/disable button states", async () => { + const record = TestUtils.createEnumProperty("Test", 0); + TestUtils.addEnumButtonGroupEditorSpecification(record); + + const renderedComponent = render(); + + const blueButton = renderedComponent.getByTestId("Blue"); + expect(blueButton.tagName).to.be.equal("BUTTON"); + expect(blueButton.classList.contains("nz-is-disabled")).to.be.equal(!TestUtils.blueEnumValueIsEnabled); + TestUtils.toggleBlueEnumValueEnabled(); + renderedComponent.rerender(); + await waitForElement(() => renderedComponent.getByTestId("Blue")); + expect(blueButton.classList.contains("nz-is-disabled")).to.be.equal(!TestUtils.blueEnumValueIsEnabled); + }); + +}); diff --git a/ui/components/src/test/editors/EnumEditor.test.snap b/ui/components/src/test/editors/EnumEditor.test.snap new file mode 100644 index 0000000..2570dfe --- /dev/null +++ b/ui/components/src/test/editors/EnumEditor.test.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders correctly 1`] = ` + this._checkboxElement = node} + onBlur={this.props.onBlur} + className={className} + checked={checked} + onChange={this._updateCheckboxValue} + data-testid="components-checkbox-editor"> + + ); + } +} + +/** BooleanPropertyEditor React component that uses the [[BooleanEditor]] property editor. */ +export class BooleanPropertyEditor extends PropertyEditorBase { + + public get reactElement(): React.ReactNode { + return ; + } +} + +PropertyEditorManager.registerEditor("bool", BooleanPropertyEditor); +PropertyEditorManager.registerEditor("boolean", BooleanPropertyEditor); diff --git a/ui/components/src/ui-components/editors/EditorContainer.tsx b/ui/components/src/ui-components/editors/EditorContainer.tsx index 5698c50..cde6056 100644 --- a/ui/components/src/ui-components/editors/EditorContainer.tsx +++ b/ui/components/src/ui-components/editors/EditorContainer.tsx @@ -5,7 +5,7 @@ /** @module PropertyEditors */ import * as React from "react"; -import { PropertyRecord } from "../properties/Record"; +import { PropertyRecord, PropertyValue } from "@bentley/imodeljs-frontend"; import { PropertyEditorBase, PropertyEditorManager } from "./PropertyEditorManager"; import "./EditorContainer.scss"; @@ -15,55 +15,72 @@ export interface PropertyUpdatedArgs { /** The property being updated. */ propertyRecord: PropertyRecord; /** The new value for the property. */ - newValue: any; + newValue: PropertyValue; +} + +/** Properties for a property editor component */ +export interface PropertyEditorProps { + propertyRecord?: PropertyRecord; + onCommit?: (args: PropertyUpdatedArgs) => void; + onCancel?: () => void; + onBlur?: (event: React.FocusEvent) => void; + setFocus?: boolean; } /** [[EditorContainer]] React component properties */ export interface EditorContainerProps { propertyRecord: PropertyRecord; title?: string; - onCommit: (commit: PropertyUpdatedArgs) => void; + onCommit: (args: PropertyUpdatedArgs) => void; onCancel: () => void; + setFocus?: boolean; /** @hidden */ ignoreEditorBlur?: boolean; } +interface CloneProps extends PropertyEditorProps { + ref: (ref: any) => void; +} + +/** Interface implemented by React based type editors */ +export interface TypeEditor { + getPropertyValue: () => Promise; +} + /** * EditorContainer React component */ -export class EditorContainer extends React.Component { +export class EditorContainer extends React.PureComponent { private _editorRef: any; - private _propertyEditor: PropertyEditorBase | null = null; + private _propertyEditor: PropertyEditorBase | undefined; - private getEditor(): any { + private getEditor(): TypeEditor { return this._editorRef; } private createEditor(): React.ReactNode { - const editorRef = (c: any) => this._editorRef = c; + const editorRef = (ref: any) => this._editorRef = ref; - const editorProps = { + const editorProps: CloneProps = { ref: editorRef, + onCommit: this._handleEditorCommit, + onCancel: this._handleEditorCancel, onBlur: this._handleEditorBlur, - value: this.props.propertyRecord, + propertyRecord: this.props.propertyRecord, + setFocus: this.props.setFocus !== undefined ? this.props.setFocus : true, }; - if (this.props.propertyRecord) { - let editorNode: React.ReactNode; - const propDescription = this.props.propertyRecord.property; + let editorNode: React.ReactNode; + const propDescription = this.props.propertyRecord.property; - if (propDescription.typename) { - const editorName = propDescription.editor !== undefined ? propDescription.editor.name : undefined; - this._propertyEditor = PropertyEditorManager.createEditor(propDescription.typename, editorName, propDescription.dataController); - if (this._propertyEditor) - editorNode = this._propertyEditor.reactElement; - } + const editorName = propDescription.editor !== undefined ? propDescription.editor.name : undefined; + this._propertyEditor = PropertyEditorManager.createEditor(propDescription.typename, editorName, propDescription.dataController); + editorNode = this._propertyEditor.reactElement; - if (React.isValidElement(editorNode)) { - return React.cloneElement(editorNode, editorProps); - } + if (React.isValidElement(editorNode)) { + return React.cloneElement(editorNode, editorProps); } return null; @@ -71,7 +88,7 @@ export class EditorContainer extends React.Component { private _handleEditorBlur = (_e: React.FocusEvent) => { if (!this.props.ignoreEditorBlur) - this._commit(); + this._commit(); // tslint:disable-line: no-floating-promises } private _handleContainerBlur = (e: React.FocusEvent) => { @@ -104,74 +121,60 @@ export class EditorContainer extends React.Component { e.stopPropagation(); } - private onPressEscape(e: React.KeyboardEvent): void { - if (!this.editorIsSelectOpen()) { - this._commitCancel(); - } else { - // prevent event from bubbling if editor has results to select - e.stopPropagation(); - } + private onPressEscape(_e: React.KeyboardEvent): void { + this._commitCancel(); } private onPressEnter(_e: React.KeyboardEvent): void { - this._commit(); + this._commit(); // tslint:disable-line: no-floating-promises } private onPressTab(_e: React.KeyboardEvent): void { - this._commit(); - } - - private editorIsSelectOpen(): boolean { - if (isFunction(this.getEditor().isSelectOpen)) { - return this.getEditor().isSelectOpen(); - } - - return false; + this._commit(); // tslint:disable-line: no-floating-promises } - private async isNewValueValid(value: any): Promise { - if (isFunction(this.getEditor().validate)) { - const isValid = this.getEditor().validate(value); - if (!isValid) { - this.setState({ isInvalid: !isValid }); - return isValid; - } - } - + private async isNewValueValid(value: PropertyValue): Promise { if (this._propertyEditor && this.props.propertyRecord) { - const valueResult = await this._propertyEditor.validateValue(value, this.props.propertyRecord); - if (valueResult.encounteredError) { - this.setState({ isInvalid: valueResult.encounteredError }); + const validateResult = await this._propertyEditor.validateValue(value, this.props.propertyRecord); + if (validateResult.encounteredError) { + this.setState({ isInvalid: validateResult.encounteredError }); // TODO - display InputField - return !valueResult.encounteredError; + return !validateResult.encounteredError; } } return true; } - private _commit = () => { - const newValue = this.getEditor().getValue(); - if (this.isNewValueValid(newValue)) { - this.props.onCommit({ propertyRecord: this.props.propertyRecord, newValue }); - } + private _handleEditorCommit = (args: PropertyUpdatedArgs): void => { + this.props.onCommit(args); } - private _commitCancel = () => { - this.props.onCancel(); - } + private _commit = async () => { + const newValue = await this.getEditor().getPropertyValue(); + if (newValue) { + const isValid = await this.isNewValueValid(newValue); + if (isValid) { + let doCommit = true; + if (this._propertyEditor && this.props.propertyRecord) { + const commitResult = await this._propertyEditor.commitValue(newValue, this.props.propertyRecord); + if (commitResult.encounteredError) + doCommit = false; + } - public componentDidMount() { - const inputNode = this.getInputNode(); - if (inputNode) { - inputNode.focus(); + if (doCommit) { + this.props.onCommit({ propertyRecord: this.props.propertyRecord, newValue }); + } + } } } - private getInputNode(): HTMLInputElement | null { - if (this.getEditor() && isFunction(this.getEditor().getInputNode)) - return this.getEditor().getInputNode(); - return null; + private _handleEditorCancel = () => { + this._commitCancel(); + } + + private _commitCancel = () => { + this.props.onCancel(); } public render() { @@ -182,14 +185,10 @@ export class EditorContainer extends React.Component { onClick={this._handleClick} onContextMenu={this._handleRightClick} title={this.props.title} + data-testid="editor-container" > {this.createEditor()} ); } } - -const isFunction = (functionToCheck: any): boolean => { - const getType = {}; - return functionToCheck && getType.toString.call(functionToCheck) === "[object Function]"; -}; diff --git a/ui/components/src/ui-components/editors/EnumButtonGroupEditor.scss b/ui/components/src/ui-components/editors/EnumButtonGroupEditor.scss new file mode 100644 index 0000000..65a6e19 --- /dev/null +++ b/ui/components/src/ui-components/editors/EnumButtonGroupEditor.scss @@ -0,0 +1,177 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +@import "@bentley/ui-core/lib/ui-core/index"; + +$desktop-tool-stripe-width: 2px; +$desktop-tool-stripe-height: 22px; + +$icon-size: 16px; +$icon-color: $buic-icon-color; +$hovered-icon-color: $primary-tone; +$pressed-icon-color: $buic-icon-color-reverse; +$active-icon-color: $primary-base; +$disabled-icon-color: $buic-icon-color-disabled; +$icon-shadow-color: $buic-icon-color-reverse; +$icon-shadow: drop-shadow(0 1px 0 $icon-shadow-color); +$no-shadow: drop-shadow(0 0 0 transparent); + +$components-enumbuttongroup-editor-item-width: 32px; +$components-enumbuttongroup-editor-item-height: 32px; + +$components-enumbuttongroup-editor-item-border-width: 1px; +$components-enumbuttongroup-editor-space-between-toolbar-items: 1px; + +.components-enumbuttongroup-editor { + font-family: $uicore-font-family; + font-size: $uicore-font-size; + background: $buic-background-control; + width: 100%; + + $divider-color: $buic-background-toolbutton-stroke; + $border-color: $buic-background-toolbutton-stroke; + $border-radius: 3px; + + border-color: $border-color; + border-width: $components-enumbuttongroup-editor-item-border-width; + border-radius: $border-radius; + border-style: solid; + overflow: hidden; + display: inline-block; + vertical-align: bottom; + box-sizing: border-box; + + height: $components-enumbuttongroup-editor-item-height; + + >* { + display: inline-block; + height: 100%; + width: $components-enumbuttongroup-editor-item-width; + + &:not(:first-child) { + margin-left: $components-enumbuttongroup-editor-space-between-toolbar-items; + + &:before { + content: ''; + background-color: $divider-color; + display: block; + position: absolute; + pointer-events: none; + width: $components-enumbuttongroup-editor-space-between-toolbar-items; + height: 100%; + left: -$components-enumbuttongroup-editor-space-between-toolbar-items; + top: 0; + } + } + } + +} + +.components-enumbuttongroup-button { + border-width: 0; + background: $buic-background-control; + background-origin: border-box; + border-color: $buic-background-control-stroke; + border-style: solid; + cursor: pointer; + transition: all 500ms ease; + position: relative; + background: $buic-background-control linear-gradient(to bottom, $buic-button-gradient1, $buic-button-gradient2); + height: $components-enumbuttongroup-editor-item-height; + width: $components-enumbuttongroup-editor-item-width; + + >i { + font-size: $icon-size; + width: 100%; + height: 100%; + display: grid; + align-content: center; + justify-content: center; + color: $icon-color; + filter: $icon-shadow; + transition: all 500ms ease; + + svg { + filter: $icon-shadow; + } + + img { + width: 100%; + height: 100%; + } + } + + &:hover { + >i { + color: $hovered-icon-color; + transition-duration: 150ms; + } + } + + &:active { + >i { + color: $pressed-icon-color; + filter: $no-shadow; + + svg { + filter: $no-shadow; + } + } + } + + &:active:not(.nz-is-disabled) { + background: $primary-tone; + color: $buic-foreground-body-reverse; + transition-duration: 150ms; + transition-timing-function: cubic-bezier(1, 0.1, 1, 0.5); + } + + &.nz-is-active { + background: $buic-background-control; + + &:not(:active):not(.nz-is-disabled) { + transition-duration: 0s; + + >i { + color: $primary-base; + filter: $no-shadow; + transition-duration: 150ms; + transition-timing-function: cubic-bezier(1, 0.1, 1, 0.5); + + svg { + filter: $no-shadow; + } + } + + &:after { + content: ''; + display: block; + position: absolute; + background: $primary-base; + left: 3px; + top: ($components-enumbuttongroup-editor-item-height - $desktop-tool-stripe-height) / 2; + border-radius: 2px; + width: $desktop-tool-stripe-width; + height: $desktop-tool-stripe-height; + } + } + } + + &.nz-is-disabled { + pointer-events: none; + + >i { + color: $disabled-icon-color; + } + + &:hover { + cursor: auto; + } + + &:active { + pointer-events: none; + } + } + +} \ No newline at end of file diff --git a/ui/components/src/ui-components/editors/EnumButtonGroupEditor.tsx b/ui/components/src/ui-components/editors/EnumButtonGroupEditor.tsx new file mode 100644 index 0000000..838422e --- /dev/null +++ b/ui/components/src/ui-components/editors/EnumButtonGroupEditor.tsx @@ -0,0 +1,224 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module PropertyEditors */ + +import * as React from "react"; +import classnames from "classnames"; +import { + PropertyValueFormat, PrimitiveValue, PropertyValue, EnumerationChoice, + PropertyEditorParamTypes, IconDefinition, PropertyEditorParams, ButtonGroupEditorParams, PropertyRecord, +} from "@bentley/imodeljs-frontend"; +import { PropertyEditorManager, PropertyEditorBase } from "./PropertyEditorManager"; +import { PropertyEditorProps, TypeEditor } from "./EditorContainer"; +import "./EnumButtonGroupEditor.scss"; + +interface EnumEditorState { + selectValue: string | number; + valueIsNumber: boolean; +} + +/** EnumButtonGroupEditor React component that is a property editor with select input */ +export class EnumButtonGroupEditor extends React.Component implements TypeEditor { + private _isMounted = false; + private _enumIcons?: IconDefinition[]; + private _btnRefs = new Map(); + + /** @hidden */ + public readonly state: Readonly = { + selectValue: "", + valueIsNumber: false, + }; + + constructor(props: PropertyEditorProps) { + super(props); + + const state = EnumButtonGroupEditor.getStateFromProps(props); + if (state) + this.state = state; + + this.loadIcons(); + } + + private loadIcons(): void { + if (this._enumIcons) + return; + + const { propertyRecord } = this.props; + + if (propertyRecord) { + if (propertyRecord.property.enum) { + const numChoices = propertyRecord.property.enum.choices.length; + this._enumIcons = new Array(numChoices); + this._enumIcons.fill({ iconClass: "icon icon-placeholder" }); + + if (propertyRecord.property.editor && propertyRecord.property.editor.params) { + const bgParams = propertyRecord.property.editor.params.find((param: PropertyEditorParams) => param.type === PropertyEditorParamTypes.ButtonGroupData) as ButtonGroupEditorParams; + if (bgParams) { + bgParams.buttons.forEach((iconDef: IconDefinition, index: number) => { + if (index < numChoices) { + this._enumIcons![index] = iconDef; + } + }); + } + } + } + } + } + + public getValue(): string | number { + return this.state.selectValue; + } + + public async getPropertyValue(): Promise { + const record = this.props.propertyRecord; + let propertyValue: PropertyValue | undefined; + + // istanbul ignore else + if (record && record.value.valueFormat === PropertyValueFormat.Primitive) { + propertyValue = { + valueFormat: PropertyValueFormat.Primitive, + value: this.state.selectValue, + displayValue: "", + }; + } + + return propertyValue; + } + + public setFocus(): void { + const button = this._btnRefs.get(this.state.selectValue); + // istanbul ignore else + if (button) + button.focus(); + } + + public componentDidMount() { + this._isMounted = true; + } + + public componentWillUnmount() { + this._isMounted = false; + } + + /** @hidden */ + public componentDidUpdate(prevProps: PropertyEditorProps, _prevState: EnumEditorState) { + // if the props have changed then we need to update the state + const prevRecord = prevProps.propertyRecord; + const currentRecord = this.props.propertyRecord; + if (prevRecord !== currentRecord) { + const state = EnumButtonGroupEditor.getStateFromProps(this.props); + this.setState(state); + const button = this._btnRefs.get(state!.selectValue); + // istanbul ignore else + if (button) + button.focus(); + } + } + + private static getStateFromProps(props: PropertyEditorProps): EnumEditorState | null { + const propertyRecord = props.propertyRecord; + let selectValue: string | number; + let valueIsNumber: boolean; + + // istanbul ignore else + if (propertyRecord && propertyRecord.value.valueFormat === PropertyValueFormat.Primitive) { + const primitiveValue = (propertyRecord.value as PrimitiveValue).value; + if (typeof primitiveValue === "string") { + selectValue = primitiveValue as string; + valueIsNumber = false; + } else { + selectValue = primitiveValue as number; + valueIsNumber = true; + } + return { selectValue, valueIsNumber }; + } + return null; + } + + private getIcon(index: number) { + if (this._enumIcons && this._enumIcons.length > index) + return (); + return null; + } + + private _handleButtonClick = (index: number) => { + const propertyRecord = this.props.propertyRecord as PropertyRecord; + const choices = propertyRecord ? propertyRecord.property.enum!.choices : undefined; + + // istanbul ignore else + if (this._isMounted && choices && choices.length > index) { + const selectValue = choices[index].value; + + this.setState({ + selectValue, + }, async () => { + // istanbul ignore else + if (propertyRecord && this.props.onCommit) { + const propertyValue = await this.getPropertyValue(); + // istanbul ignore else + if (propertyValue) { + this.props.onCommit({ propertyRecord, newValue: propertyValue }); + } + } + }); + } + } + + private getButton(choice: EnumerationChoice, index: number) { + const { propertyRecord } = this.props; + const choiceValue = propertyRecord!.property.enum!.choices[index].value; + const isActive = (choiceValue === this.state.selectValue) ? true : false; + let isDisabled = false; + if (this._enumIcons && this._enumIcons.length > index) { + const isEnabledFunction = this._enumIcons![index].isEnabledFunction; + if (isEnabledFunction) { + isDisabled = !isEnabledFunction(); + } + } + + const className = classnames( + "components-enumbuttongroup-button", + isDisabled && "nz-is-disabled", + isActive && "nz-is-active", + ); + + return ( + + ); + } + + public render() { + const { propertyRecord } = this.props; + let choices: EnumerationChoice[] | undefined; + + // istanbul ignore else + if (propertyRecord && propertyRecord.property.enum) + choices = propertyRecord.property.enum.choices; + + return ( +
    + {choices && choices.map((choice: EnumerationChoice, index: number) => this.getButton(choice, index))} +
    ); + } +} + +/** EnumPropertyButtonGroupEditor React component that uses the [[EnumButtonGroupEditor]] property editor. */ +export class EnumPropertyButtonGroupEditor extends PropertyEditorBase { + + public get reactElement(): React.ReactNode { + return ; + } +} + +PropertyEditorManager.registerEditor("enum", EnumPropertyButtonGroupEditor, "enum-buttongroup"); diff --git a/ui/components/src/ui-components/editors/EnumEditor.scss b/ui/components/src/ui-components/editors/EnumEditor.scss new file mode 100644 index 0000000..0b45377 --- /dev/null +++ b/ui/components/src/ui-components/editors/EnumEditor.scss @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +@import "@bentley/ui-core/lib/ui-core/index"; + +.components-enum-editor { + font-family: $uicore-font-family; + font-size: $uicore-font-size; + color: $buic-foreground-body; + background: $buic-background-control; + border: 1px solid $buic-inputs-border; + box-sizing: border-box; + width: 100%; + height: 100%; +} diff --git a/ui/components/src/ui-components/editors/EnumEditor.tsx b/ui/components/src/ui-components/editors/EnumEditor.tsx new file mode 100644 index 0000000..6a29702 --- /dev/null +++ b/ui/components/src/ui-components/editors/EnumEditor.tsx @@ -0,0 +1,165 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module PropertyEditors */ + +import * as React from "react"; +import classnames from "classnames"; +import { PropertyValueFormat, PrimitiveValue, PropertyValue, EnumerationChoice } from "@bentley/imodeljs-frontend"; +import { PropertyEditorManager, PropertyEditorBase } from "./PropertyEditorManager"; +import { PropertyEditorProps, TypeEditor } from "./EditorContainer"; +import "./EnumEditor.scss"; + +interface EnumEditorState { + selectValue: string | number; + valueIsNumber: boolean; +} + +/** EnumEditor React component that is a property editor with select input */ +export class EnumEditor extends React.PureComponent implements TypeEditor { + private _selectElement: HTMLSelectElement | null = null; + private _isMounted = false; + + /** @hidden */ + public readonly state: Readonly = { + selectValue: "", + valueIsNumber: false, + }; + + public getValue(): string | number { + return this.state.selectValue; + } + + public async getPropertyValue(): Promise { + const record = this.props.propertyRecord; + let propertyValue: PropertyValue | undefined; + + // istanbul ignore else + if (record && record.value.valueFormat === PropertyValueFormat.Primitive) { + propertyValue = { + valueFormat: PropertyValueFormat.Primitive, + value: this.state.selectValue, + displayValue: "", + }; + } + + return propertyValue; + } + + private setFocus(): void { + // istanbul ignore else + if (this._selectElement) { + this._selectElement.focus(); + } + } + + private _updateSelectValue = (e: React.ChangeEvent) => { + // istanbul ignore else + if (this._isMounted) { + let selectValue: string | number; + + if (this.state.valueIsNumber) + selectValue = parseInt(e.target.value, 10); + else + selectValue = e.target.value; + + this.setState({ + selectValue, + }, async () => { + // istanbul ignore else + if (this.props.propertyRecord && this.props.onCommit) { + const propertyValue = await this.getPropertyValue(); + // istanbul ignore else + if (propertyValue) { + this.props.onCommit({ propertyRecord: this.props.propertyRecord, newValue: propertyValue }); + } + } + }); + } + } + + public componentDidMount() { + this._isMounted = true; + this.setStateFromProps(); // tslint:disable-line:no-floating-promises + } + + public componentWillUnmount() { + this._isMounted = false; + } + + public componentDidUpdate(prevProps: PropertyEditorProps) { + if (this.props.propertyRecord !== prevProps.propertyRecord) { + this.setStateFromProps(); // tslint:disable-line:no-floating-promises + } + } + + private async setStateFromProps() { + const { propertyRecord } = this.props; + let initialValue: string | number = ""; + let valueIsNumber: boolean = false; + + // istanbul ignore else + if (propertyRecord && propertyRecord.value.valueFormat === PropertyValueFormat.Primitive) { + const primitiveValue = (propertyRecord.value as PrimitiveValue).value; + if (typeof primitiveValue === "string") { + initialValue = primitiveValue as string; + valueIsNumber = false; + } else { + initialValue = primitiveValue as number; + valueIsNumber = true; + } + } + + // istanbul ignore else + if (this._isMounted) + this.setState( + { selectValue: initialValue, valueIsNumber }, + () => { + if (this.props.setFocus) + this.setFocus(); + }, + ); + } + + public render() { + const className = classnames("cell", "components-cell-editor", "components-enum-editor"); + const { propertyRecord } = this.props; + const selectValue = this.state.selectValue ? this.state.selectValue.toString() : undefined; + let choices: EnumerationChoice[] | undefined; + + if (propertyRecord && propertyRecord.property.enum) + choices = propertyRecord.property.enum.choices; + + return ( + + ); + } +} + +/** EnumPropertyEditor React component that uses the [[EnumEditor]] property editor. */ +export class EnumPropertyEditor extends PropertyEditorBase { + + public get reactElement(): React.ReactNode { + return ; + } +} + +PropertyEditorManager.registerEditor("enum", EnumPropertyEditor); diff --git a/ui/components/src/ui-components/editors/PropertyEditorManager.tsx b/ui/components/src/ui-components/editors/PropertyEditorManager.tsx index 09fd493..f0d24be 100644 --- a/ui/components/src/ui-components/editors/PropertyEditorManager.tsx +++ b/ui/components/src/ui-components/editors/PropertyEditorManager.tsx @@ -4,10 +4,7 @@ *--------------------------------------------------------------------------------------------*/ /** @module PropertyEditors */ -import { PropertyValue } from "../properties/Value"; -import { PropertyRecord } from "../properties/Record"; -import { PropertyDescription } from "../properties/Description"; - +import { PropertyValue, PropertyRecord, PropertyDescription } from "@bentley/imodeljs-frontend"; import * as React from "react"; import { AsyncValueProcessingResult } from "../converters/TypeConverter"; import { TextEditor } from "./TextEditor"; @@ -63,9 +60,7 @@ export class PropertyEditorManager { private static _dataControllers: { [index: string]: (new () => DataControllerBase) } = {}; public static registerEditor(editType: string, editor: new () => PropertyEditorBase, editorName?: string): void { - let fullEditorName = editType; - if (editorName) - fullEditorName += ":" + editorName; + const fullEditorName = PropertyEditorManager.getFullEditorName(editType, editorName); if (PropertyEditorManager._editors.hasOwnProperty(fullEditorName)) { const nameOfEditor = PropertyEditorManager._editors[fullEditorName].name; @@ -74,17 +69,22 @@ export class PropertyEditorManager { PropertyEditorManager._editors[fullEditorName] = editor; } + private static getFullEditorName(editType: string, editorName?: string): string { + let fullEditorName = editType; + if (editorName) + fullEditorName += ":" + editorName; + return fullEditorName; + } + public static registerDataController(controllerName: string, controller: new () => DataControllerBase): void { if (PropertyEditorManager._dataControllers.hasOwnProperty(controllerName)) { - throw Error("PropertyEditorManager.RegisterDataController error: type '" + controllerName + "' already registered to '" + (typeof PropertyEditorManager._dataControllers[controllerName]).toString() + "'"); + throw Error("PropertyEditorManager.registerDataController error: type '" + controllerName + "' already registered to '" + (typeof PropertyEditorManager._dataControllers[controllerName]).toString() + "'"); } PropertyEditorManager._dataControllers[controllerName] = controller; } - public static createEditor(editType: string, editorName?: string, dataControllerName?: string): PropertyEditorBase | null { - let fullEditorName = editType; - if (editorName) - fullEditorName += ":" + editorName; + public static createEditor(editType: string, editorName?: string, dataControllerName?: string): PropertyEditorBase { + const fullEditorName = PropertyEditorManager.getFullEditorName(editType, editorName); let editor: PropertyEditorBase; if (PropertyEditorManager._editors.hasOwnProperty(fullEditorName)) @@ -97,13 +97,15 @@ export class PropertyEditorManager { if (dataControllerName) { if (PropertyEditorManager._dataControllers.hasOwnProperty(dataControllerName)) editor.customDataController = new PropertyEditorManager._dataControllers[dataControllerName](); + else + throw Error("PropertyEditorManager.createEditor error: data controller '" + dataControllerName + "' is not registered"); } return editor; } public static hasCustomEditor(editType: string, editorName: string): boolean { - const fullEditorName = editType + ":" + editorName; + const fullEditorName = PropertyEditorManager.getFullEditorName(editType, editorName); return PropertyEditorManager._editors.hasOwnProperty(fullEditorName); } } diff --git a/ui/components/src/ui-components/editors/TextEditor.scss b/ui/components/src/ui-components/editors/TextEditor.scss index f799558..6962211 100644 --- a/ui/components/src/ui-components/editors/TextEditor.scss +++ b/ui/components/src/ui-components/editors/TextEditor.scss @@ -2,9 +2,15 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -@import "@bentley/bwc/lib/mixins"; +@import "@bentley/ui-core/lib/ui-core/index"; .components-text-editor { - font-family: $bwc-font-family; - font-size: $bwc-font-size; + font-family: $uicore-font-family; + font-size: $uicore-font-size; + color: $buic-foreground-body; + background: $buic-background-control; + border: 1px solid $buic-inputs-border; + box-sizing: border-box; + width: 100%; + height: 100%; } \ No newline at end of file diff --git a/ui/components/src/ui-components/editors/TextEditor.tsx b/ui/components/src/ui-components/editors/TextEditor.tsx index 1795617..933f7d0 100644 --- a/ui/components/src/ui-components/editors/TextEditor.tsx +++ b/ui/components/src/ui-components/editors/TextEditor.tsx @@ -6,41 +6,57 @@ import * as React from "react"; import classnames from "classnames"; -import { PropertyRecord } from "../properties/Record"; -import { PropertyValueFormat, PrimitiveValue } from "../properties/Value"; +import { PropertyValueFormat, PropertyValue, PrimitiveValue, PropertyEditorParams, PropertyEditorParamTypes, InputEditorSizeParams } from "@bentley/imodeljs-frontend"; +import { PropertyEditorProps, TypeEditor } from "./EditorContainer"; import { TypeConverterManager } from "../converters/TypeConverterManager"; import "./TextEditor.scss"; -/** Properties for [[TextEditor]] component */ -export interface TextEditorProps { - onBlur?: (event: any) => void; - value?: PropertyRecord; -} - interface TextEditorState { inputValue: string; + readonly: boolean; + isDisabled?: boolean; + size?: number; + maxLength?: number; } /** TextEditor React component that is a property editor with text input */ -export class TextEditor extends React.Component { +export class TextEditor extends React.PureComponent implements TypeEditor { private _input: HTMLInputElement | null = null; private _isMounted = false; /** @hidden */ public readonly state: Readonly = { inputValue: "", + readonly: false, }; public getValue(): string { return this.state.inputValue; } - public getInputNode(): HTMLInputElement | null { - return this._input; + public async getPropertyValue(): Promise { + const record = this.props.propertyRecord; + let propertyValue: PropertyValue | undefined; + + // istanbul ignore else + if (record && record.value.valueFormat === PropertyValueFormat.Primitive) { + propertyValue = await TypeConverterManager.getConverter(record.property.typename).convertFromStringToPropertyValue(this.state.inputValue, record); + (propertyValue as PrimitiveValue).displayValue = this.state.inputValue; + } + + return propertyValue; + } + + private setFocus(): void { + // istanbul ignore else + if (this._input && !this.state.isDisabled) { + this._input.focus(); + } } private _updateInputValue = (e: React.ChangeEvent) => { + // istanbul ignore else if (this._isMounted) this.setState({ inputValue: e.target.value, @@ -49,29 +65,58 @@ export class TextEditor extends React.Component param.type === PropertyEditorParamTypes.InputEditorSize) as InputEditorSizeParams; + // istanbul ignore else + if (editorSizeParams) { + // istanbul ignore else + if (editorSizeParams.size) + size = editorSizeParams.size; + // istanbul ignore else + if (editorSizeParams.maxLength) + maxLength = editorSizeParams.maxLength; + } + } + + // istanbul ignore else if (this._isMounted) this.setState( - () => ({ inputValue: initialValue }), + { inputValue: initialValue, readonly, size, maxLength, isDisabled }, () => { - if (this._input) { - this._input.focus(); - this._input.select(); + if (this.props.setFocus) { + this.setFocus(); + // istanbul ignore else + if (this._input) + this._input.select(); } }, ); @@ -86,6 +131,10 @@ export class TextEditor extends React.Component implements TypeEditor { + private _isMounted = false; + + /** @hidden */ + public readonly state: Readonly = { + toggleValue: false, + }; + + public getValue(): boolean { + return this.state.toggleValue; + } + + public async getPropertyValue(): Promise { + const record = this.props.propertyRecord; + let propertyValue: PropertyValue | undefined; + + // istanbul ignore else + if (record && record.value.valueFormat === PropertyValueFormat.Primitive) { + propertyValue = { + valueFormat: PropertyValueFormat.Primitive, + value: this.state.toggleValue, + displayValue: "", + }; + } + return propertyValue; + } + + private setFocus(): void { + } + + private _updateToggleValue = (toggleValue: boolean): any => { + // istanbul ignore else + if (this._isMounted) { + + this.setState({ + toggleValue, + }, async () => { + // istanbul ignore else + if (this.props.propertyRecord && this.props.onCommit) { + const propertyValue = await this.getPropertyValue(); + // istanbul ignore else + if (propertyValue) { + this.props.onCommit({ propertyRecord: this.props.propertyRecord, newValue: propertyValue }); + } + } + }); + } + } + + public componentDidMount() { + this._isMounted = true; + this.setStateFromProps(); // tslint:disable-line:no-floating-promises + } + + public componentWillUnmount() { + this._isMounted = false; + } + + public componentDidUpdate(prevProps: PropertyEditorProps) { + if (this.props.propertyRecord !== prevProps.propertyRecord) { + this.setStateFromProps(); // tslint:disable-line:no-floating-promises + } + } + + private async setStateFromProps() { + const { propertyRecord } = this.props; + let toggleValue = false; + + // istanbul ignore else + if (propertyRecord && propertyRecord.value.valueFormat === PropertyValueFormat.Primitive) { + const primitiveValue = (propertyRecord.value as PrimitiveValue).value; + toggleValue = primitiveValue as boolean; + } + + // istanbul ignore else + if (this._isMounted) + this.setState( + { toggleValue }, + () => { + if (this.props.setFocus) + this.setFocus(); + }, + ); + } + + public render() { + const className = classnames("cell", "components-cell-editor", "components-toggle-editor"); + const inOn = this.state.toggleValue; + + return ( + + ); + } +} + +/** TogglePropertyEditor React component that uses the [[ToggleEditor]] property editor. */ +export class TogglePropertyEditor extends PropertyEditorBase { + + public get reactElement(): React.ReactNode { + return ; + } +} + +PropertyEditorManager.registerEditor("bool", TogglePropertyEditor, "toggle"); +PropertyEditorManager.registerEditor("boolean", TogglePropertyEditor, "toggle"); diff --git a/ui/components/src/ui-components/filtering/FilteringInput.scss b/ui/components/src/ui-components/filtering/FilteringInput.scss index 5858ac6..65199b7 100644 --- a/ui/components/src/ui-components/filtering/FilteringInput.scss +++ b/ui/components/src/ui-components/filtering/FilteringInput.scss @@ -1,57 +1,56 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ -@import "@bentley/bwc/lib/mixins"; - -.filtering-input { - display: flex; - align-items: center; - justify-content: space-between; - margin: 5px; -} - -// Input wrapper -.filtering-input-input{ - position: relative; - flex: 1; - - // The input itself - input{ - @include bwc-inputs-input; - box-sizing:border-box; - } - - input::-ms-clear { - display: none; - } -} - -.filtering-input-input-components{ - display: flex; - position: absolute; - right: 8px; - top: 1px; - bottom: 0px; -} - -@mixin filtering-button-base { - @include bwc-buttons-hollow; - margin-left: 6px; -} - -.filtering-input-button{ - @include filtering-button-base; -} - -.filtering-input-clear{ - @include filtering-button-base; - color: $bwc-gray-5; - font-size: "12px"; -} - -.filtering-input-loader { - @include bwc-loaders-medium; - margin-bottom: auto; - margin-top: auto; -} +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +@import "@bentley/ui-core/lib/ui-core/index"; + +.filtering-input { + display: flex; + align-items: center; + justify-content: space-between; + margin: 5px; +} + +// Input wrapper +.filtering-input-input{ + position: relative; + flex: 1; + + // The input itself + input{ + @include uicore-inputs-input; + box-sizing:border-box; + } + + input::-ms-clear { + display: none; + } +} + +.filtering-input-input-components{ + display: flex; + position: absolute; + right: 8px; + top: 1px; + bottom: 0px; +} + +@mixin filtering-button-base { + @include uicore-buttons-hollow; + margin-left: 6px; +} + +.filtering-input-button{ + @include filtering-button-base; +} + +.filtering-input-clear{ + @include filtering-button-base; + color: $uicore-gray-5; + font-size: "12px"; +} + +.filtering-input-loader { + margin-bottom: auto; + margin-top: auto; +} diff --git a/ui/components/src/ui-components/filtering/FilteringInput.tsx b/ui/components/src/ui-components/filtering/FilteringInput.tsx index 6242e99..a6b2ba2 100644 --- a/ui/components/src/ui-components/filtering/FilteringInput.tsx +++ b/ui/components/src/ui-components/filtering/FilteringInput.tsx @@ -9,6 +9,7 @@ import { Key } from "ts-key-enum"; import { ResultSelector, ResultSelectorProps } from "./ResultSelector"; import "./FilteringInput.scss"; import UiComponents from "../UiComponents"; +import { Spinner, SpinnerSize } from "@bentley/ui-core"; /** [[FilteringInput]] React Component state */ export interface FilteringInputState { @@ -112,7 +113,10 @@ export class FilteringInput extends React.Component {this.state.context === InputContext.FilteringInProgress ? -
    : undefined} +
    + +
    + : undefined} {this.state.context === InputContext.FilteringFinished ? : undefined} diff --git a/ui/components/src/ui-components/filtering/ResultSelector.scss b/ui/components/src/ui-components/filtering/ResultSelector.scss index b6e1c5e..3352370 100644 --- a/ui/components/src/ui-components/filtering/ResultSelector.scss +++ b/ui/components/src/ui-components/filtering/ResultSelector.scss @@ -1,68 +1,68 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ - @import "@bentley/bwc/lib/mixins"; - -.result-selector { - display: flex; - align-items: center; - font-size: 12px; - - @include bwc-font-family; - color: $bwc-text-color; -} - -.result-selector-current-result { - margin: 0 2px; - padding-top: 2px; - - input[type="number"]::-webkit-outer-spin-button, - input[type="number"]::-webkit-inner-spin-button { - -webkit-appearance: none; - margin: 0; - } - input[type="number"]{ - -moz-appearance:textfield; - display: inline-block; - text-align: center; - font-size: 12px; - padding: 0px; - min-width: 2em; - } -} - -.result-selector-current-result:hover { - cursor: pointer; -} - -.result-selector-button { - @include bwc-buttons-hollow; - font-size: 12px; - height: 20px; - width: 20px; - border-style: none; - color: $bwc-gray-5; - margin: 0 4px; -} - -.result-selector-button:hover { - background-color: $bwc-gray-b; - border-radius: 2px; -} - -/* Overriding bwc-buttons-hollow defaults */ -.result-selector-button, -.result-selector-button:active, -.result-selector-button:focus, -.result-selector-button:disabled, -.result-selector-button[disabled]:hover { - border-style: none; - padding: 0px; -} - -.result-selector-button:disabled, -.result-selector-button[disabled]:hover { - background-color: $bwc-gray-9; - cursor: default; +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +@import "@bentley/ui-core/lib/ui-core/index"; + +.result-selector { + display: flex; + align-items: center; + font-size: 12px; + + @include uicore-font-family; + color: $uicore-text-color; +} + +.result-selector-current-result { + margin: 0 2px; + padding-top: 2px; + + input[type="number"]::-webkit-outer-spin-button, + input[type="number"]::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; + } + input[type="number"]{ + -moz-appearance:textfield; + display: inline-block; + text-align: center; + font-size: 12px; + padding: 0px; + min-width: 2em; + } +} + +.result-selector-current-result:hover { + cursor: pointer; +} + +.result-selector-button { + @include uicore-buttons-hollow; + font-size: 12px; + height: 20px; + width: 20px; + border-style: none; + color: $uicore-gray-5; + margin: 0 4px; +} + +.result-selector-button:hover { + background-color: $uicore-gray-b; + border-radius: 2px; +} + +/* Overriding uicore-buttons-hollow defaults */ +.result-selector-button, +.result-selector-button:active, +.result-selector-button:focus, +.result-selector-button:disabled, +.result-selector-button[disabled]:hover { + border-style: none; + padding: 0px; +} + +.result-selector-button:disabled, +.result-selector-button[disabled]:hover { + background-color: $uicore-gray-9; + cursor: default; } \ No newline at end of file diff --git a/ui/components/src/ui-components/properties/ItemStyle.ts b/ui/components/src/ui-components/properties/ItemStyle.ts new file mode 100644 index 0000000..bef6375 --- /dev/null +++ b/ui/components/src/ui-components/properties/ItemStyle.ts @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { CSSProperties } from "react"; + +/** Converts a color value from a number to an HTML/CSS hex string */ +const colorDecimalToHex = (decimal: number) => `#${decimal.toString(16).padStart(6, "0")}`; + +/** Style properties for styled item like [[CellItem]] and [[TreeNodeItem]] */ +export interface ItemStyle { + /** Is text bolded */ + isBold?: boolean; + /** Is text italic */ + isItalic?: boolean; + /** Color overrides for styled item */ + colorOverrides?: ItemColorOverrides; +} + +/** Color overrides for styled item */ +export interface ItemColorOverrides { + /** Text/foreground color */ + color?: number; + /** Background color */ + backgroundColor?: number; + /** Selected item text/foreground color */ + colorSelected?: number; + /** Selected item background color */ + backgroundColorSelected?: number; +} + +function getBackgroundColor(isSelected: boolean, colorOverrides?: ItemColorOverrides) { + if (!colorOverrides) + return undefined; + + if (isSelected) + return colorOverrides.backgroundColorSelected !== undefined + ? colorDecimalToHex(colorOverrides.backgroundColorSelected) + : undefined; + + if (colorOverrides.backgroundColor) + return colorDecimalToHex(colorOverrides.backgroundColor); + + return undefined; +} + +function getForegroundColor(isSelected: boolean, colorOverrides?: ItemColorOverrides) { + if (!colorOverrides) + return undefined; + + if (isSelected) + return colorOverrides.colorSelected !== undefined + ? colorDecimalToHex(colorOverrides.colorSelected) + : undefined; + + if (colorOverrides.color) + return colorDecimalToHex(colorOverrides.color); + + return undefined; +} + +/** + * Style provider for stylable items like [[CellItem]] and [[TreeNodeItem]] + */ +// tslint:disable-next-line:variable-name +export const ItemStyleProvider = { + /** + * Create CSS style from [[ItemStyle]] + * @param isSelected Is item currently selected + */ + createStyle: ({ colorOverrides, isBold, isItalic }: ItemStyle, isSelected?: boolean): CSSProperties => ({ + color: getForegroundColor(!!isSelected, colorOverrides), + backgroundColor: getBackgroundColor(!!isSelected, colorOverrides), + fontWeight: isBold ? "bold" : undefined, + fontStyle: isItalic ? "italic" : undefined, + }), +}; + +/** + * Style provider for table rows + */ +// tslint:disable-next-line:variable-name +export const TableRowStyleProvider = { + /** + * Create CSS style from [[ItemStyle]] + */ + createStyle: ({ color, backgroundColor }: ItemColorOverrides): CSSProperties => ({ + color: color ? colorDecimalToHex(color) : undefined, + backgroundColor: backgroundColor ? colorDecimalToHex(backgroundColor) : undefined, + }), +}; diff --git a/ui/components/src/ui-components/properties/LinkHandler.tsx b/ui/components/src/ui-components/properties/LinkHandler.tsx new file mode 100644 index 0000000..195e33a --- /dev/null +++ b/ui/components/src/ui-components/properties/LinkHandler.tsx @@ -0,0 +1,86 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module Properties */ + +import * as React from "react"; +import { PropertyRecord } from "@bentley/imodeljs-frontend"; +import { isPromiseLike, UnderlinedButton } from "@bentley/ui-core"; + +/** Render a single anchor tag */ +function renderTag(text: string, record: PropertyRecord) { + return ( + { + e.preventDefault(); + e.stopPropagation(); + record.links!.onClick(record, text); + }} + > + {text} + + ); +} + +interface Match { start: number; end: number; } + +function matchComparison(matchA: Match, matchB: Match) { + if (matchA.start > matchB.start) + return 1; + if (matchB.start > matchA.start) + return -1; + return 0; +} + +function renderText(text: string, record: PropertyRecord): React.ReactNode { + const { matcher } = record.links!; + + if (!matcher) + return renderTag(text, record); + + const matches = matcher(text); + + // Sort just to be sure + matches.sort(matchComparison); + + const parts: React.ReactNode[] = []; + let lastIndex = 0; + for (const match of matches) { + // If matches overlap there must be something wrong with the matcher + if (lastIndex > match.start) + throw new Error("renderAnchorTag: matcher returned overlapping matches"); + + if (lastIndex < match.start) + parts.push(text.substring(lastIndex, match.start)); + + const anchorText = text.substring(match.start, match.end); + parts.push(renderTag(anchorText, record)); + + lastIndex = match.end; + } + if (text.length > lastIndex) + parts.push(text.substring(lastIndex)); + + // Need to map, because React complains about the lack of keys + return parts.map((part, index) => {part}); +} + +/** Returns true if property record has an anchor tag */ +export const hasLinks = (record: PropertyRecord) => !!record.links; + +/** Renders anchor tag by wrapping or splitting provided text */ +export const renderLinks = (text: string | Promise, record: PropertyRecord): React.ReactNode | Promise => { + if (isPromiseLike(text)) { + return text.then((result) => renderText(result, record)); + } + + return renderText(text, record); +}; + +/** If record has links, wraps stringValue in them, otherwise returns unchanged stringValue */ +export const withLinks = (record: PropertyRecord, stringValue: string | Promise): React.ReactNode | Promise => { + if (hasLinks(record)) + return renderLinks(stringValue, record); + return stringValue; +}; diff --git a/ui/components/src/ui-components/properties/ValueRendererManager.tsx b/ui/components/src/ui-components/properties/ValueRendererManager.tsx index 5b9e7be..0d9893b 100644 --- a/ui/components/src/ui-components/properties/ValueRendererManager.tsx +++ b/ui/components/src/ui-components/properties/ValueRendererManager.tsx @@ -4,10 +4,9 @@ *--------------------------------------------------------------------------------------------*/ /** @module Properties */ -import React from "react"; +import * as React from "react"; import { Orientation } from "@bentley/ui-core"; -import { PropertyRecord } from "./Record"; -import { PropertyValueFormat } from "./Value"; +import { PropertyRecord, PropertyValueFormat } from "@bentley/imodeljs-frontend"; import { PrimitivePropertyValueRenderer } from "./renderers/value/PrimitivePropertyValueRenderer"; import { ArrayPropertyValueRenderer } from "./renderers/value/ArrayPropertyValueRenderer"; import { StructPropertyValueRenderer } from "./renderers/value/StructPropertyValueRenderer"; diff --git a/ui/components/src/ui-components/properties/renderers/NonPrimitivePropertyRenderer.scss b/ui/components/src/ui-components/properties/renderers/NonPrimitivePropertyRenderer.scss index 3f50ee8..214130c 100644 --- a/ui/components/src/ui-components/properties/renderers/NonPrimitivePropertyRenderer.scss +++ b/ui/components/src/ui-components/properties/renderers/NonPrimitivePropertyRenderer.scss @@ -1,17 +1,17 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ - -@import "@bentley/bwc/lib/mixins"; - -.components-nonprimitive-property{ - padding: 10px; - display: grid; - grid-row-gap: 10px; - - border-radius: 3px; - border: solid 1px $bwc-gray-9; - - background-color: #ffffff; +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ + +@import "@bentley/ui-core/lib/ui-core/index"; + +.components-nonprimitive-property{ + padding: 10px; + display: grid; + grid-row-gap: 10px; + + border-radius: 3px; + border: solid 1px $uicore-gray-9; + + background-color: #ffffff; } \ No newline at end of file diff --git a/ui/components/src/ui-components/properties/renderers/NonPrimitivePropertyRenderer.tsx b/ui/components/src/ui-components/properties/renderers/NonPrimitivePropertyRenderer.tsx index 58e9f1f..e38f40c 100644 --- a/ui/components/src/ui-components/properties/renderers/NonPrimitivePropertyRenderer.tsx +++ b/ui/components/src/ui-components/properties/renderers/NonPrimitivePropertyRenderer.tsx @@ -6,14 +6,13 @@ import * as React from "react"; import _ from "lodash"; -import { PropertyValueFormat, StructValue, ArrayValue } from "../Value"; -import { PropertyRecord } from "../Record"; import { NonPrimitivePropertyLabelRenderer } from "./label/NonPrimitivePropertyLabelRenderer"; import { PropertyView } from "./PropertyView"; import { PrimitiveRendererProps } from "./PrimitivePropertyRenderer"; import { PropertyRenderer } from "./PropertyRenderer"; import UiComponents from "../../UiComponents"; import { Orientation } from "@bentley/ui-core"; +import { PropertyRecord, PropertyValueFormat, StructValue, ArrayValue } from "@bentley/imodeljs-frontend"; import "./NonPrimitivePropertyRenderer.scss"; diff --git a/ui/components/src/ui-components/properties/renderers/PropertyRenderer.tsx b/ui/components/src/ui-components/properties/renderers/PropertyRenderer.tsx index b9128dd..2f97a6a 100644 --- a/ui/components/src/ui-components/properties/renderers/PropertyRenderer.tsx +++ b/ui/components/src/ui-components/properties/renderers/PropertyRenderer.tsx @@ -7,8 +7,7 @@ import * as React from "react"; import _ from "lodash"; import { Orientation } from "@bentley/ui-core"; -import { PropertyValueFormat, ArrayValue } from "../Value"; -import { PropertyRecord } from "../Record"; +import { PropertyValueFormat, ArrayValue, PropertyRecord } from "@bentley/imodeljs-frontend"; import { PropertyValueRendererManager, PropertyValueRendererContext, PropertyContainerType } from "../ValueRendererManager"; import { PrimitiveRendererProps, PrimitivePropertyRenderer } from "./PrimitivePropertyRenderer"; import { NonPrimitivePropertyRenderer } from "./NonPrimitivePropertyRenderer"; @@ -120,6 +119,7 @@ export class PropertyRenderer extends React.Component, }); } diff --git a/ui/components/src/ui-components/properties/renderers/PropertyView.scss b/ui/components/src/ui-components/properties/renderers/PropertyView.scss index c1307b7..2d618e7 100644 --- a/ui/components/src/ui-components/properties/renderers/PropertyView.scss +++ b/ui/components/src/ui-components/properties/renderers/PropertyView.scss @@ -1,60 +1,67 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ -@import "@bentley/bwc/lib/mixins"; - -$text-font-size: $bwc-font-size; -$text-font-color: $bwc-text-color; - -.components-property-record--horizontal { - min-height: 24px; - display: grid; - - .components-property-record-label { - font-size: $text-font-size; - color: $text-font-color; - } - - .components-property-record-value { - min-height: 20px; - font-size: $text-font-size; - color: $text-font-color; - } -} - -.components-property-record--vertical { - .components-property-record-label { - font-weight: bold; - font-size: $text-font-size; - color: $text-font-color; - margin-top: 0px; - margin-bottom: 0px; - padding-top: 6px; - padding-bottom: 0px; - } - - .components-property-record-value { - min-height: 20px; - font-size: $text-font-size; - color: $text-font-color; - margin-top: 0px; - margin-bottom: 0px; - - padding-top: 0px; - padding-bottom: 4px; - } -} - -@mixin record-cell { - display: flex; - align-items: center; -} - -.components-property-record-label { - @include record-cell; -} - -.components-property-record-value { - @include record-cell; -} +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +@import "@bentley/ui-core/lib/ui-core/index"; + +$text-font-size: $uicore-font-size; +$text-font-color: $buic-text-color; + +.components-property-record--horizontal { + min-height: 24px; + display: grid; + + .components-property-record-label { + font-size: $text-font-size; + color: $text-font-color; + } + + .components-property-record-value { + min-height: 20px; + font-size: $text-font-size; + color: $text-font-color; + } +} + +.components-property-record--vertical { + .components-property-record-label { + font-weight: bold; + font-size: $text-font-size; + color: $text-font-color; + margin-top: 0px; + margin-bottom: 0px; + padding-top: 6px; + padding-bottom: 0px; + } + + .components-property-record-value { + min-height: 20px; + font-size: $text-font-size; + color: $text-font-color; + margin-top: 0px; + margin-bottom: 0px; + + padding-top: 0px; + padding-bottom: 4px; + } +} + +@mixin record-cell { + display: flex; + align-items: center; +} + +.components-property-record-label { + @include record-cell; +} + +.components-property-record-value { + @include record-cell; + + .core-underlined-button { + // Offset by 2px, because value is centered vertically + // and the button has an underline which offsets it's content, + // which in turn makes it not in line with the rest of text + margin-top: 2px; + } +} diff --git a/ui/components/src/ui-components/properties/renderers/PropertyView.tsx b/ui/components/src/ui-components/properties/renderers/PropertyView.tsx index 6392f3e..2cf3bb8 100644 --- a/ui/components/src/ui-components/properties/renderers/PropertyView.tsx +++ b/ui/components/src/ui-components/properties/renderers/PropertyView.tsx @@ -9,7 +9,7 @@ import { Orientation, ElementSeparator } from "@bentley/ui-core"; import { SharedRendererProps } from "./PropertyRenderer"; import "./PropertyView.scss"; -import { PropertyValueFormat } from "../Value"; +import { PropertyValueFormat } from "@bentley/imodeljs-frontend"; /** Properties of [[PropertyView]] React component */ export interface PropertyViewProps extends SharedRendererProps { diff --git a/ui/components/src/ui-components/properties/renderers/label/PropertyLabelRenderer.scss b/ui/components/src/ui-components/properties/renderers/label/PropertyLabelRenderer.scss index b6c0861..3b51b9e 100644 --- a/ui/components/src/ui-components/properties/renderers/label/PropertyLabelRenderer.scss +++ b/ui/components/src/ui-components/properties/renderers/label/PropertyLabelRenderer.scss @@ -1,77 +1,77 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ -@import "@bentley/bwc/lib/mixins"; - -@mixin rotate($amount) { - -webkit-transform: rotate($amount); - -ms-transform: rotate($amount); - transform: rotate($amount); -} - -@mixin transition($seconds, $type) { - -webkit-transition: -webkit-transform $seconds $type; - -ms-transition: -ms-transform $seconds $type; - transition: transform $seconds $type; -} - -.components-property-label-renderer { - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - margin-top: auto; - margin-bottom: auto; -} - -.components-property-label-renderer-colon{ - margin: auto auto auto 3px; -} - -@mixin specific-property-label-renderer { - position: relative; - display: flex; - - .components-label-popup { - background: white; - width: max-content; - padding: 5px; - } -} - -.components-primitive-property-label-renderer { - @include specific-property-label-renderer; -} - -.components-property-record--vertical { - .components-nonprimitive-property-label-renderer { - margin-bottom: 4px; - } -} - -.components-nonprimitive-property-label-renderer { - @include specific-property-label-renderer; - border: none; - background: transparent; - cursor: pointer; - color: $bwc-gray-3; - height: 100%; - width: 100%; - - > i { - transition: transform 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out; - margin: auto 5px auto 0px; - width: 14px; - height: 14px; - font-size: 12px; - } - - > .components-expanded { - @include transition(0.15s, ease-in-out); - @include rotate(90deg); - } - - &:hover { - color: $bwc-gray-1; - } -} +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +@import "@bentley/ui-core/lib/ui-core/index"; + +@mixin rotate($amount) { + -webkit-transform: rotate($amount); + -ms-transform: rotate($amount); + transform: rotate($amount); +} + +@mixin transition($seconds, $type) { + -webkit-transition: -webkit-transform $seconds $type; + -ms-transition: -ms-transform $seconds $type; + transition: transform $seconds $type; +} + +.components-property-label-renderer { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + margin-top: auto; + margin-bottom: auto; +} + +.components-property-label-renderer-colon{ + margin: auto auto auto 3px; +} + +@mixin specific-property-label-renderer { + position: relative; + display: flex; + + .components-label-popup { + background: white; + width: max-content; + padding: 5px; + } +} + +.components-primitive-property-label-renderer { + @include specific-property-label-renderer; +} + +.components-property-record--vertical { + .components-nonprimitive-property-label-renderer { + margin-bottom: 4px; + } +} + +.components-nonprimitive-property-label-renderer { + @include specific-property-label-renderer; + border: none; + background: transparent; + cursor: pointer; + color: $uicore-gray-3; + height: 100%; + width: 100%; + + > i { + transition: transform 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out; + margin: auto 5px auto 0px; + width: 14px; + height: 14px; + font-size: 12px; + } + + > .components-expanded { + @include transition(0.15s, ease-in-out); + @include rotate(90deg); + } + + &:hover { + color: $uicore-gray-1; + } +} diff --git a/ui/components/src/ui-components/properties/renderers/value/ArrayPropertyValueRenderer.tsx b/ui/components/src/ui-components/properties/renderers/value/ArrayPropertyValueRenderer.tsx index 3f6c6c0..b35ba72 100644 --- a/ui/components/src/ui-components/properties/renderers/value/ArrayPropertyValueRenderer.tsx +++ b/ui/components/src/ui-components/properties/renderers/value/ArrayPropertyValueRenderer.tsx @@ -4,10 +4,9 @@ *--------------------------------------------------------------------------------------------*/ /** @module Properties */ -import React from "react"; +import * as React from "react"; import { IPropertyValueRenderer, PropertyValueRendererContext, PropertyContainerType } from "../../ValueRendererManager"; -import { PropertyRecord } from "../../Record"; -import { PropertyValueFormat, ArrayValue } from "../../Value"; +import { PropertyRecord, PropertyValueFormat, ArrayValue } from "@bentley/imodeljs-frontend"; import { Orientation } from "@bentley/ui-core"; import { TableArrayValueRenderer } from "./table/ArrayValueRenderer"; import { withContextStyle } from "./WithContextStyle"; @@ -33,7 +32,7 @@ export class ArrayPropertyValueRenderer implements IPropertyValueRenderer { } if (context && context.containerType === PropertyContainerType.PropertyPane) { - return ""; + return withContextStyle("", context); } return withContextStyle( diff --git a/ui/components/src/ui-components/properties/renderers/value/DoublePropertyValueRenderer.tsx b/ui/components/src/ui-components/properties/renderers/value/DoublePropertyValueRenderer.tsx index 3758d7f..435640e 100644 --- a/ui/components/src/ui-components/properties/renderers/value/DoublePropertyValueRenderer.tsx +++ b/ui/components/src/ui-components/properties/renderers/value/DoublePropertyValueRenderer.tsx @@ -5,10 +5,10 @@ /** @module Properties */ import { IPropertyValueRenderer, PropertyValueRendererContext } from "../../ValueRendererManager"; -import { PropertyRecord } from "../../Record"; -import { PropertyValueFormat, PrimitiveValue } from "../../Value"; +import { PropertyRecord, PropertyValueFormat, PrimitiveValue } from "@bentley/imodeljs-frontend"; import { TypeConverterManager } from "../../../converters/TypeConverterManager"; import { withContextStyle } from "./WithContextStyle"; +import { withLinks } from "../../LinkHandler"; /** Default Double Property Renderer */ export class DoublePropertyValueRenderer implements IPropertyValueRenderer { @@ -20,8 +20,15 @@ export class DoublePropertyValueRenderer implements IPropertyValueRenderer { public render(record: PropertyRecord, context?: PropertyValueRendererContext) { const primitive = record.value as PrimitiveValue; - if (primitive.displayValue) - return primitive.displayValue; - return withContextStyle(TypeConverterManager.getConverter(record.property.typename).convertPropertyToString(record.property, primitive.value), context); + + let stringValue: string | Promise; + + if (primitive.displayValue) { + stringValue = primitive.displayValue; + } else { + stringValue = TypeConverterManager.getConverter(record.property.typename).convertPropertyToString(record.property, primitive.value); + } + + return withContextStyle(withLinks(record, stringValue), context); } } diff --git a/ui/components/src/ui-components/properties/renderers/value/MergedPropertyValueRenderer.tsx b/ui/components/src/ui-components/properties/renderers/value/MergedPropertyValueRenderer.tsx index 4315c47..d2ca3ab 100644 --- a/ui/components/src/ui-components/properties/renderers/value/MergedPropertyValueRenderer.tsx +++ b/ui/components/src/ui-components/properties/renderers/value/MergedPropertyValueRenderer.tsx @@ -5,8 +5,7 @@ /** @module Properties */ import { IPropertyValueRenderer, PropertyValueRendererContext } from "../../ValueRendererManager"; -import { PropertyRecord } from "../../Record"; -import { PropertyValueFormat } from "../../Value"; +import { PropertyRecord, PropertyValueFormat } from "@bentley/imodeljs-frontend"; import { UiComponents } from "../../../UiComponents"; import { withContextStyle } from "./WithContextStyle"; diff --git a/ui/components/src/ui-components/properties/renderers/value/NavigationPropertyValueRenderer.tsx b/ui/components/src/ui-components/properties/renderers/value/NavigationPropertyValueRenderer.tsx index 0419133..342a30a 100644 --- a/ui/components/src/ui-components/properties/renderers/value/NavigationPropertyValueRenderer.tsx +++ b/ui/components/src/ui-components/properties/renderers/value/NavigationPropertyValueRenderer.tsx @@ -5,10 +5,10 @@ /** @module Properties */ import { IPropertyValueRenderer, PropertyValueRendererContext } from "../../ValueRendererManager"; -import { PropertyRecord } from "../../Record"; -import { PropertyValueFormat, PrimitiveValue } from "../../Value"; +import { PropertyRecord, PropertyValueFormat, PrimitiveValue } from "@bentley/imodeljs-frontend"; import { TypeConverterManager } from "../../../converters/TypeConverterManager"; import { withContextStyle } from "./WithContextStyle"; +import { withLinks } from "../../LinkHandler"; /** Default Navigation Property Renderer */ export class NavigationPropertyValueRenderer implements IPropertyValueRenderer { @@ -20,8 +20,15 @@ export class NavigationPropertyValueRenderer implements IPropertyValueRenderer { public render(record: PropertyRecord, context?: PropertyValueRendererContext) { const primitive = record.value as PrimitiveValue; - if (primitive.displayValue) - return primitive.displayValue; - return withContextStyle(TypeConverterManager.getConverter(record.property.typename).convertPropertyToString(record.property, primitive.value), context); + + let stringValue: string | Promise; + + if (primitive.displayValue) { + stringValue = primitive.displayValue; + } else { + stringValue = TypeConverterManager.getConverter(record.property.typename).convertPropertyToString(record.property, primitive.value); + } + + return withContextStyle(withLinks(record, stringValue), context); } } diff --git a/ui/components/src/ui-components/properties/renderers/value/PrimitivePropertyValueRenderer.tsx b/ui/components/src/ui-components/properties/renderers/value/PrimitivePropertyValueRenderer.tsx index 57100f4..abcf8de 100644 --- a/ui/components/src/ui-components/properties/renderers/value/PrimitivePropertyValueRenderer.tsx +++ b/ui/components/src/ui-components/properties/renderers/value/PrimitivePropertyValueRenderer.tsx @@ -5,10 +5,10 @@ /** @module Properties */ import { IPropertyValueRenderer, PropertyValueRendererContext, PropertyContainerType } from "../../ValueRendererManager"; -import { PropertyRecord } from "../../Record"; -import { PropertyValueFormat, PrimitiveValue } from "../../Value"; +import { PropertyRecord, PropertyValueFormat, PrimitiveValue } from "@bentley/imodeljs-frontend"; import { TypeConverterManager } from "../../../converters/TypeConverterManager"; import { withContextStyle } from "./WithContextStyle"; +import { withLinks } from "../../LinkHandler"; /** Default Primitive Property Renderer */ export class PrimitivePropertyValueRenderer implements IPropertyValueRenderer { @@ -22,9 +22,12 @@ export class PrimitivePropertyValueRenderer implements IPropertyValueRenderer { return withContextStyle(context.decoratedTextElement, context); const value = (record.value as PrimitiveValue).value; - if (value !== undefined) - return withContextStyle(TypeConverterManager.getConverter(record.property.typename).convertPropertyToString(record.property, value), context); - return ""; + if (value === undefined) + return withContextStyle("", context); + + const stringValue = TypeConverterManager.getConverter(record.property.typename).convertPropertyToString(record.property, value); + + return withContextStyle(withLinks(record, stringValue), context); } } diff --git a/ui/components/src/ui-components/properties/renderers/value/StructPropertyValueRenderer.tsx b/ui/components/src/ui-components/properties/renderers/value/StructPropertyValueRenderer.tsx index 9e92447..0cb383e 100644 --- a/ui/components/src/ui-components/properties/renderers/value/StructPropertyValueRenderer.tsx +++ b/ui/components/src/ui-components/properties/renderers/value/StructPropertyValueRenderer.tsx @@ -4,10 +4,9 @@ *--------------------------------------------------------------------------------------------*/ /** @module Properties */ -import React from "react"; +import * as React from "react"; import { IPropertyValueRenderer, PropertyValueRendererContext, PropertyContainerType } from "../../ValueRendererManager"; -import { PropertyRecord } from "../../Record"; -import { PropertyValueFormat } from "../../Value"; +import { PropertyRecord, PropertyValueFormat } from "@bentley/imodeljs-frontend"; import { Orientation } from "@bentley/ui-core"; import { TableStructValueRenderer } from "./table/StructValueRenderer"; import { withContextStyle } from "./WithContextStyle"; @@ -31,7 +30,7 @@ export class StructPropertyValueRenderer implements IPropertyValueRenderer { } if (context && context.containerType === PropertyContainerType.PropertyPane) { - return ""; + return withContextStyle("", context); } return withContextStyle(`{${record.property.typename}}`, context); diff --git a/ui/components/src/ui-components/properties/renderers/value/WithContextStyle.tsx b/ui/components/src/ui-components/properties/renderers/value/WithContextStyle.tsx index 453d2c5..a0d9c3c 100644 --- a/ui/components/src/ui-components/properties/renderers/value/WithContextStyle.tsx +++ b/ui/components/src/ui-components/properties/renderers/value/WithContextStyle.tsx @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ /** @module Properties */ -import React from "react"; +import * as React from "react"; +import { isPromiseLike } from "@bentley/ui-core"; import { PropertyValueRendererContext } from "../../ValueRendererManager"; const internalWithContextStyle = (node: React.ReactNode, context?: PropertyValueRendererContext) => { @@ -13,12 +14,9 @@ const internalWithContextStyle = (node: React.ReactNode, context?: PropertyValue return ({node}); }; -const isPromise = (value: React.ReactNode | Promise): value is Promise => { - return (undefined !== value) && (undefined !== (value as Promise).then); -}; - +/** Wraps a React component with a span element with a given style attribute */ export const withContextStyle = (value: React.ReactNode | Promise, context?: PropertyValueRendererContext) => { - if (isPromise(value)) + if (isPromiseLike(value)) return value.then((v) => internalWithContextStyle(v, context)); return internalWithContextStyle(value, context); }; diff --git a/ui/components/src/ui-components/properties/renderers/value/table/ArrayValueRenderer.tsx b/ui/components/src/ui-components/properties/renderers/value/table/ArrayValueRenderer.tsx index aa657df..bdcd2c4 100644 --- a/ui/components/src/ui-components/properties/renderers/value/table/ArrayValueRenderer.tsx +++ b/ui/components/src/ui-components/properties/renderers/value/table/ArrayValueRenderer.tsx @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ /** @module Properties */ -import React from "react"; +import * as React from "react"; import { TableNonPrimitiveValueRenderer as TableValueRenderer, TableSpecificValueRendererProps } from "./NonPrimitiveValueRenderer"; import { NonPrimitivePropertyRenderer } from "../../NonPrimitivePropertyRenderer"; -import { ArrayValue } from "../../../Value"; +import { ArrayValue } from "@bentley/imodeljs-frontend"; /** A react component which renders array property value as a button with text */ export class TableArrayValueRenderer extends React.PureComponent { diff --git a/ui/components/src/ui-components/properties/renderers/value/table/NonPrimitiveValueRenderer.scss b/ui/components/src/ui-components/properties/renderers/value/table/NonPrimitiveValueRenderer.scss index 02eaba7..8077f69 100644 --- a/ui/components/src/ui-components/properties/renderers/value/table/NonPrimitiveValueRenderer.scss +++ b/ui/components/src/ui-components/properties/renderers/value/table/NonPrimitiveValueRenderer.scss @@ -1,28 +1,11 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ -@import "@bentley/bwc/lib/mixins"; - -.components-table-value-button { - @include bwc-buttons-hollow; - width: calc(100% - 16px) !important; // !important because of row * {width: 100%} rule - height: 100%; -} - -.components-table-value-button, .components-table-value-button:active, .components-table-value-button:focus { - padding: 0px; - border: none; - - > span { - border-bottom: solid; - border-width: 1px; - width: fit-content; - } -} - -.components-table-value-popup { - background: $bwc-gray-c; - padding: 5px; - border-radius: 3px; -} \ No newline at end of file +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +// @import "@bentley/ui-core/lib/ui-core/index"; + +// .components-table-value-popup { +// background: $uicore-gray-c; +// padding: 5px; +// border-radius: 3px; +// } \ No newline at end of file diff --git a/ui/components/src/ui-components/properties/renderers/value/table/NonPrimitiveValueRenderer.tsx b/ui/components/src/ui-components/properties/renderers/value/table/NonPrimitiveValueRenderer.tsx index 247e39c..a554832 100644 --- a/ui/components/src/ui-components/properties/renderers/value/table/NonPrimitiveValueRenderer.tsx +++ b/ui/components/src/ui-components/properties/renderers/value/table/NonPrimitiveValueRenderer.tsx @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ /** @module Properties */ -import React from "react"; -import { Orientation } from "@bentley/ui-core"; +import * as React from "react"; +import { Orientation, UnderlinedButton } from "@bentley/ui-core"; +import { PropertyRecord } from "@bentley/imodeljs-frontend"; +import { PropertyDialogState } from "../../../ValueRendererManager"; import "./NonPrimitiveValueRenderer.scss"; -import { PropertyRecord } from "../../../Record"; -import { PropertyDialogState } from "../../../ValueRendererManager"; /** Properties for [[TableArrayValueRenderer]] and [[TableStructValueRenderer]] React component */ export interface TableSpecificValueRendererProps extends SharedTableNonPrimitiveValueRendererProps { @@ -44,7 +44,7 @@ export interface TableNonPrimitiveValueRendererProps extends SharedTableNonPrimi * When clicked, a dialog appears that shows the value in greater detail. */ export class TableNonPrimitiveValueRenderer extends React.PureComponent { - private _buttonRef = React.createRef(); + // private _buttonRef = React.createRef(); private _onClick = () => { if (!this.props.onDialogOpen) @@ -90,20 +90,16 @@ export class TableNonPrimitiveValueRenderer extends React.PureComponent - - + + {this.props.buttonLabel} + ); } } diff --git a/ui/components/src/ui-components/properties/renderers/value/table/StructValueRenderer.tsx b/ui/components/src/ui-components/properties/renderers/value/table/StructValueRenderer.tsx index 850f84d..84510a2 100644 --- a/ui/components/src/ui-components/properties/renderers/value/table/StructValueRenderer.tsx +++ b/ui/components/src/ui-components/properties/renderers/value/table/StructValueRenderer.tsx @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ /** @module Properties */ -import React from "react"; +import * as React from "react"; import { TableNonPrimitiveValueRenderer as TableValueRenderer, TableSpecificValueRendererProps } from "./NonPrimitiveValueRenderer"; import { NonPrimitivePropertyRenderer } from "../../NonPrimitivePropertyRenderer"; diff --git a/ui/components/src/ui-components/propertygrid/PropertyDataProvider.ts b/ui/components/src/ui-components/propertygrid/PropertyDataProvider.ts index f3ca859..8d71e6c 100644 --- a/ui/components/src/ui-components/propertygrid/PropertyDataProvider.ts +++ b/ui/components/src/ui-components/propertygrid/PropertyDataProvider.ts @@ -5,7 +5,7 @@ /** @module PropertyGrid */ import { BeEvent } from "@bentley/bentleyjs-core"; -import { PropertyRecord } from "../properties/Record"; +import { PropertyRecord } from "@bentley/imodeljs-frontend"; /** * Contains metadata about a group of Properties. diff --git a/ui/components/src/ui-components/propertygrid/SimplePropertyDataProvider.ts b/ui/components/src/ui-components/propertygrid/SimplePropertyDataProvider.ts index bbefd7f..bcdcbcf 100644 --- a/ui/components/src/ui-components/propertygrid/SimplePropertyDataProvider.ts +++ b/ui/components/src/ui-components/propertygrid/SimplePropertyDataProvider.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ /** @module PropertyGrid */ -import { PropertyRecord } from "../properties/Record"; +import { PropertyRecord } from "@bentley/imodeljs-frontend"; import { IPropertyDataProvider, PropertyData, PropertyCategory, PropertyDataChangeEvent } from "./PropertyDataProvider"; /** @@ -54,7 +54,6 @@ export class SimplePropertyDataProvider implements IPropertyDataProvider, Proper }); if (index >= 0) { this.records[this.categories[categoryIdx].name].splice(index, 1, newRecord); - this.onDataChanged.raiseEvent(); return true; } return false; diff --git a/ui/components/src/ui-components/propertygrid/component/PropertyCategoryBlock.scss b/ui/components/src/ui-components/propertygrid/component/PropertyCategoryBlock.scss deleted file mode 100644 index ee2c448..0000000 --- a/ui/components/src/ui-components/propertygrid/component/PropertyCategoryBlock.scss +++ /dev/null @@ -1,50 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ - -@import "@bentley/bwc/lib/mixins"; - -.components-property-category-block { - border: solid 1px #c7ccd1; - border-radius: 3px; - - &.is-expanded > .header, > .header { - > .icon-container { - color: #677480; - margin-right: 6px; - -webkit-transition: 0.15s ease-in-out; - -o-transition: 0.15s ease-in-out; - transition: 0.15s ease-in-out; - } - - > .title { - -webkit-transition: 0.15s ease-in-out; - -o-transition: 0.15s ease-in-out; - transition: 0.15s ease-in-out; - color: #677480; - } - } - - &.is-expanded:hover > .header, &:hover > .header { - > .icon-container { - color: #000; - } - > .title{ - color: #000; - } - } - - .header, &.is-expanded > .header, &.is-expanded:hover > .header { - - border-style: none; - background: #fff; - border-color: #c7ccd1; - } - - &.is-expanded > .content, &.is-expanded:hover > .content { - padding: 11px 9px 11px 34px; - background-color: #fff; - border-style: none; - } -} \ No newline at end of file diff --git a/ui/components/src/ui-components/propertygrid/component/PropertyCategoryBlock.tsx b/ui/components/src/ui-components/propertygrid/component/PropertyCategoryBlock.tsx index ed8d543..cbf7432 100644 --- a/ui/components/src/ui-components/propertygrid/component/PropertyCategoryBlock.tsx +++ b/ui/components/src/ui-components/propertygrid/component/PropertyCategoryBlock.tsx @@ -8,8 +8,6 @@ import * as React from "react"; import { ExpandableBlock } from "@bentley/ui-core"; import { PropertyCategory } from "../PropertyDataProvider"; -import "./PropertyCategoryBlock.scss"; - /** * Properties for the [[PropertyCategoryBlock]] React component */ @@ -51,7 +49,6 @@ export class PropertyCategoryBlock extends React.Component {this.props.children} diff --git a/ui/components/src/ui-components/propertygrid/component/PropertyGrid.scss b/ui/components/src/ui-components/propertygrid/component/PropertyGrid.scss index 149cb6e..bd014ad 100644 --- a/ui/components/src/ui-components/propertygrid/component/PropertyGrid.scss +++ b/ui/components/src/ui-components/propertygrid/component/PropertyGrid.scss @@ -1,71 +1,67 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ -@import "@bentley/bwc/lib/mixins"; -@import "@bentley/ui-core/lib/ui-core/_scrollbar"; - -.components-property-grid-loader { - @include bwc-loaders-large; - position: relative; - display: block; - left: calc(50% - 24px); - top: calc(50% - 24px); -} - -.components-property-grid { - @include bwc-expandable-blocks-list; - - padding-top: 8px; - padding-bottom: 8px; - display: grid; - grid-row-gap: 10px; - user-select: none; -} - -.components-property-grid-wrapper { - width: 100%; - height: 100%; - overflow-y: auto; - - @include uicore-touch-scrolling; - @include uicore-scrollbar(); -} - -@mixin components-property-list { - width: 100%; - - .components--selected { - background-color: $bwc-blue-a; - } - - .components--clickable { - cursor: pointer; - } - - .components--hoverable:hover { - background-color: $bwc-blue-highlight; - } - - .components-cell-editor { - border: #c7ccd1 solid 1px; - border-radius: 3px; - background: #fff; - box-sizing: border-box; - width: 100%; - height: 100%; - padding: 2px 8px; - } -} - -.components-property-list--horizontal { - @include components-property-list; - - display: grid; - grid-row-gap: 1px; // A gap of 10px is too wasteful -} - -.components-property-list--vertical { - @include components-property-list; - margin-top: -13px; -} +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +@import "@bentley/ui-core/lib/ui-core/index"; + +.components-property-grid-loader { + position: relative; + display: block; + left: calc(50% - 24px); + top: calc(50% - 24px); +} + +.components-property-grid { + @include uicore-expandable-blocks-list; + + padding-top: 8px; + padding-bottom: 8px; + display: grid; + grid-row-gap: 10px; + user-select: none; + color: $buic-foreground-body; + background-color: $buic-background-control; +} + +.components-property-grid-wrapper { + width: 100%; + height: 100%; + overflow-y: auto; + + @include uicore-touch-scrolling; + @include uicore-scrollbar(); +} + +@mixin components-property-list { + width: 100%; + + .components--selected { + background-color: $buic-row-selection; + } + + .components--clickable { + cursor: pointer; + } + + .components--hoverable:hover { + background-color: $buic-row-hover; + } + + .components-cell-editor { + border: $buic-foreground-primary solid 1px; + border-radius: 3px; + box-sizing: border-box; + } +} + +.components-property-list--horizontal { + @include components-property-list; + + display: grid; + grid-row-gap: 1px; // A gap of 10px is too wasteful +} + +.components-property-list--vertical { + @include components-property-list; + margin-top: -13px; +} diff --git a/ui/components/src/ui-components/propertygrid/component/PropertyGrid.tsx b/ui/components/src/ui-components/propertygrid/component/PropertyGrid.tsx index 5265510..ce0d485 100644 --- a/ui/components/src/ui-components/propertygrid/component/PropertyGrid.tsx +++ b/ui/components/src/ui-components/propertygrid/component/PropertyGrid.tsx @@ -7,9 +7,8 @@ import * as React from "react"; import ResizeObserver from "resize-observer-polyfill"; import { DisposeFunc } from "@bentley/bentleyjs-core"; -import { Orientation } from "@bentley/ui-core"; -import { PropertyRecord } from "../../properties/Record"; -import { PropertyValueFormat } from "../../properties/Value"; +import { Orientation, Spinner, SpinnerSize } from "@bentley/ui-core"; +import { PropertyRecord, PropertyValueFormat } from "@bentley/imodeljs-frontend"; import { IPropertyDataProvider, PropertyCategory, PropertyData } from "../PropertyDataProvider"; import { SelectablePropertyBlock } from "./SelectablePropertyBlock"; import { PropertyValueRendererManager } from "../../properties/ValueRendererManager"; @@ -288,7 +287,7 @@ export class PropertyGrid extends React.Component - + ); } diff --git a/ui/components/src/ui-components/propertygrid/component/PropertyList.tsx b/ui/components/src/ui-components/propertygrid/component/PropertyList.tsx index 4281a00..fa9c31a 100644 --- a/ui/components/src/ui-components/propertygrid/component/PropertyList.tsx +++ b/ui/components/src/ui-components/propertygrid/component/PropertyList.tsx @@ -6,8 +6,7 @@ import * as React from "react"; import { Orientation } from "@bentley/ui-core"; -import { PropertyRecord } from "../../properties/Record"; -import { PropertyValueFormat } from "../../properties/Value"; +import { PropertyRecord, PropertyValueFormat } from "@bentley/imodeljs-frontend"; import { PropertyRenderer } from "../../properties/renderers/PropertyRenderer"; import { PropertyCategory } from "../PropertyDataProvider"; import { PropertyValueRendererManager } from "../../properties/ValueRendererManager"; diff --git a/ui/components/src/ui-components/propertygrid/component/SelectablePropertyBlock.tsx b/ui/components/src/ui-components/propertygrid/component/SelectablePropertyBlock.tsx index 652eef1..b5c894a 100644 --- a/ui/components/src/ui-components/propertygrid/component/SelectablePropertyBlock.tsx +++ b/ui/components/src/ui-components/propertygrid/component/SelectablePropertyBlock.tsx @@ -2,7 +2,7 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -import React from "react"; +import * as React from "react"; import _ from "lodash"; import { Omit } from "@bentley/ui-core"; import { PropertyCategoryBlock, PropertyCategoryBlockProps } from "./PropertyCategoryBlock"; diff --git a/ui/components/src/ui-components/table/SimpleTableDataProvider.ts b/ui/components/src/ui-components/table/SimpleTableDataProvider.ts index d9fa6c6..66cbc87 100644 --- a/ui/components/src/ui-components/table/SimpleTableDataProvider.ts +++ b/ui/components/src/ui-components/table/SimpleTableDataProvider.ts @@ -6,11 +6,8 @@ import { SortDirection } from "@bentley/ui-core"; import { MutableTableDataProvider, ColumnDescription, RowItem, TableDataChangeEvent } from "./TableDataProvider"; -import { PropertyRecord } from "../properties/Record"; -import { PropertyValueFormat } from "../properties/Value"; import { TypeConverterManager } from "../converters/TypeConverterManager"; -import * as Primitives from "../converters/valuetypes/PrimitiveTypes"; - +import { Primitives, PropertyRecord, PropertyValueFormat } from "@bentley/imodeljs-frontend"; /** * A Table Data Provider using an array of items. */ diff --git a/ui/components/src/ui-components/table/TableDataProvider.ts b/ui/components/src/ui-components/table/TableDataProvider.ts index 94b591a..bc18c6a 100644 --- a/ui/components/src/ui-components/table/TableDataProvider.ts +++ b/ui/components/src/ui-components/table/TableDataProvider.ts @@ -5,9 +5,10 @@ /** @module Table */ import { BeEvent } from "@bentley/bentleyjs-core"; -import { HorizontalAlignment, SortDirection } from "@bentley/ui-core"; -import { PropertyRecord } from "../properties/Record"; -import { PropertyDescription } from "../properties/Description"; +import { PropertyRecord, PropertyDescription } from "@bentley/imodeljs-frontend"; +import { SortDirection } from "@bentley/ui-core"; +import { ItemColorOverrides, ItemStyle } from "../properties/ItemStyle"; +type HorizontalAlignment = "left" | "center" | "right" | "justify"; /** * Column definition provided to Table. @@ -46,21 +47,9 @@ export interface CellItem { record?: PropertyRecord; isDisabled?: boolean; - isBold?: boolean; - isItalic?: boolean; alignment?: HorizontalAlignment; - colorOverrides?: ColorOverrides; -} - -/** - * Color Overrides for Table rows or cells. - */ -export interface ColorOverrides { - foreColor?: number; - backColor?: number; - foreColorSelected?: number; - backColorSelected?: number; + style?: ItemStyle; } /** @@ -76,7 +65,7 @@ export interface RowItem { extendedData?: any; isDisabled?: boolean; - colorOverrides?: ColorOverrides; + colorOverrides?: ItemColorOverrides; } /** diff --git a/ui/components/src/ui-components/table/component/DragDropHeaderCell.tsx b/ui/components/src/ui-components/table/component/DragDropHeaderCell.tsx index 28cc3d6..c11f960 100644 --- a/ui/components/src/ui-components/table/component/DragDropHeaderCell.tsx +++ b/ui/components/src/ui-components/table/component/DragDropHeaderCell.tsx @@ -7,13 +7,14 @@ import * as React from "react"; import classnames from "classnames"; import * as RDG from "react-data-grid"; +import { DndComponentClass } from "react-dnd"; // tslint:disable-next-line:variable-name const HeaderCell = (RDG && (RDG as any).HeaderCell); // react-data-grid @types does not support the HeaderCell export, but it is exported in the js-only library. import { DragSourceArguments, DropTargetArguments } from "../../dragdrop/DragDropDef"; -import { withDragSource } from "../../dragdrop/withDragSource"; -import { withDropTarget } from "../../dragdrop/withDropTarget"; +import { withDragSource, WithDragSourceProps } from "../../dragdrop/withDragSource"; +import { withDropTarget, WithDropTargetProps } from "../../dragdrop/withDropTarget"; import { ColumnDragLayer } from "./ColumnDragLayer"; /** @hidden */ @@ -60,7 +61,7 @@ class HeaderWrapper extends React.Component { } /** @hidden */ -export const DragDropHeaderWrapper = withDragSource(withDropTarget(HeaderWrapper)); // tslint:disable-line:variable-name +export const DragDropHeaderWrapper: DndComponentClass & WithDragSourceProps> = withDragSource(withDropTarget(HeaderWrapper)); // tslint:disable-line:variable-name // Used only internally in ./Table.tsx /** @hidden */ diff --git a/ui/components/src/ui-components/table/component/Grid.tsx b/ui/components/src/ui-components/table/component/Grid.tsx deleted file mode 100644 index 3e9f2c9..0000000 --- a/ui/components/src/ui-components/table/component/Grid.tsx +++ /dev/null @@ -1,64 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ -/** @module Table */ - -import * as React from "react"; -import ReactDataGrid from "react-data-grid"; -import "./Grid.scss"; - -/** Properties for the [[Grid]] React component */ -export interface GridProps { - columns: any[]; - rows: any[]; -} - -interface GridState { - selectedRow: any; -} - -/** - * Grid React component - */ -export class Grid extends React.Component { - - public readonly state: Readonly = { - selectedRow: undefined, - }; - - public rowGetter(i: number) { - return this.props.rows[i]; - } - - private _onRowClick = (rowIdx: number | undefined, _row: any) => { - if (this.state.selectedRow === rowIdx) - this.setState({ selectedRow: undefined }); - else - this.setState({ selectedRow: rowIdx }); - } - - public render() { - return ( -
    - -
    - ); - } -} diff --git a/ui/components/src/ui-components/table/component/Grid.scss b/ui/components/src/ui-components/table/component/Table.scss similarity index 67% rename from ui/components/src/ui-components/table/component/Grid.scss rename to ui/components/src/ui-components/table/component/Table.scss index a0076a2..7c16349 100644 --- a/ui/components/src/ui-components/table/component/Grid.scss +++ b/ui/components/src/ui-components/table/component/Table.scss @@ -2,13 +2,18 @@ * Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -@import "@bentley/bwc/lib/mixins"; -@import "@bentley/ui-core/lib/ui-core/_scrollbar"; +@import "@bentley/ui-core/lib/ui-core/index"; -.react-data-grid-wrapper { +.components-table { height: 100%; width: 100%; user-select: none; + color: $buic-foreground-body; + background-color: $buic-background-control; + + .rdg-selected { + border: none; + } .react-grid-Canvas { height: calc(100% - 25px) !important; @@ -30,18 +35,13 @@ width: 100% !important; } - // .react-grid-HeaderRow { - // width: calc(100% - 28px) !important; - // overflow-x: visible !important; /* May cause unknown issues; However, fixes unnecessary clipping for reorder styling. - // } - .react-grid-Main, .react-grid-Grid, .react-grid-Viewport, .react-grid-Canvas, .react-grid-Row { border: none; - background: #F9F9F9; + background: $buic-background-control; outline: none; font-size: 14px; } @@ -51,16 +51,18 @@ } .is-hover-enabled .react-grid-Row:hover, .is-hover-enabled .react-grid-Row:hover * { - background: #e4f5fd !important; + background: $buic-row-hover !important; } .react-grid-Row.row-selected, .react-grid-Row.row-selected * { - background: #a6d7f5; + background: $buic-row-selection; } .react-grid-Cell { padding-left: 0px; padding-right: 0px; + color: $buic-foreground-body; + background: $buic-background-control; } .react-grid-Cell--locked:last-of-type { @@ -72,26 +74,20 @@ outline: none !important; } - .react-grid-Cell__value { - position: absolute; - width: 100%; - height: 100%; - } - .react-grid-Header { - background: #dce0e3; + background: $buic-background-titlebar-outoffocus; } .react-grid-HeaderCell { padding: 4px 0 0 8px; box-sizing: border-box; - background: #dce0e3; + background: $buic-background-titlebar-outoffocus; font-size: 14px; border: none; } .react-grid-HeaderCell__draggable { - background: #dce0e3; + background: $buic-background-titlebar-outoffocus; width: 8px !important; left: 100%; z-index: 2; @@ -124,46 +120,13 @@ margin-top: 4px; } - .angry-text { - font-weight: 400; - color: red; - } - .react-grid-HeaderCell--locked { z-index: 3; } - // row * causes lots of problems down the hierarchy - .row, .row * { - width: 100%; - height: 100%; - } - - .cell { - padding-left: 8px; - padding-right: 8px; - position: absolute; - width: 100%; - height: 100%; - top: 0px; - left: 0px; - background-color: #F9F9F9; - user-select: none; - display: flex; - align-items: center; - - &.is-selected { - background-color: #a6d7f5; - } - } - .react-grid-Cell__value { - position: absolute; - width: 100%; - height: 100%; - &.is-hover-enabled:hover { - background: #e4f5fd !important; + background: $buic-row-hover !important; } } @@ -172,9 +135,14 @@ background: inherit; } + .components-editor-container { + margin-left: 6px; + margin-right: 6px; + } + .components-cell-editor { - border: $bwc-blue solid 2px; - background: #F9F9F9 !important; + border: $buic-foreground-primary solid 1px; + background: $buic-background-control !important; box-sizing: border-box; height: 100%; } diff --git a/ui/components/src/ui-components/table/component/Table.tsx b/ui/components/src/ui-components/table/component/Table.tsx index 26f072b..198c19a 100644 --- a/ui/components/src/ui-components/table/component/Table.tsx +++ b/ui/components/src/ui-components/table/component/Table.tsx @@ -7,10 +7,11 @@ import { memoize } from "lodash"; import * as React from "react"; import ReactDataGrid from "react-data-grid"; +import ReactResizeDetector from "react-resize-detector"; import classnames from "classnames"; import { DisposableList, Guid, GuidString } from "@bentley/bentleyjs-core"; import { - SortDirection, Dialog, HorizontalAlignment, + SortDirection, Dialog, LocalUiSettings, UiSettings, UiSettingsStatus, } from "@bentley/ui-core"; import { TableDataProvider, ColumnDescription, RowItem, CellItem } from "../TableDataProvider"; @@ -19,15 +20,18 @@ import { SelectionHandler, SingleSelectionHandler, MultiSelectionHandler, OnItemsSelectedCallback, OnItemsDeselectedCallback, } from "../../common/selection/SelectionHandler"; -import ReactResizeDetector from "react-resize-detector"; - -import "./Grid.scss"; -import { EditorContainer, PropertyUpdatedArgs } from "../../editors/EditorContainer"; -import { PropertyValueRendererManager, PropertyContainerType, PropertyDialogState, PropertyValueRendererContext } from "../../properties/ValueRendererManager"; -import { PropertyValueFormat, PrimitiveValue } from "../../properties/Value"; +import { PropertyUpdatedArgs } from "../../editors/EditorContainer"; +import { PropertyValueRendererManager, PropertyDialogState } from "../../properties/ValueRendererManager"; +import { PropertyValueFormat, PrimitiveValue } from "@bentley/imodeljs-frontend"; import { TypeConverterManager } from "../../converters/TypeConverterManager"; import { DragDropHeaderCell } from "./DragDropHeaderCell"; import { ShowHideMenu } from "../../common/showhide/ShowHideMenu"; +import { TableIconCellContent, TableCellContent, TableCell } from "./TableCell"; + +import "./Table.scss"; +import { TableRowStyleProvider } from "../../properties/ItemStyle"; + +const TABLE_ROW_HEIGHT = 25; /** * Specifies table selection target. @@ -81,7 +85,10 @@ export interface TableProps { settingsIdentifier?: string; /** Custom property value renderer manager */ propertyValueRendererManager?: PropertyValueRendererManager; - /** @hidden */ + /** + * Gets called when rendering is finished. Should be used while testing to know when asynchronous rendering has finished. + * @hidden + */ onRender?: () => void; } @@ -89,7 +96,7 @@ export interface TableProps { export interface CellProps { item: CellItem; displayValue: string; - render: () => React.ReactNode; + render: React.ComponentType<{ isSelected: boolean }>; } /** Properties for a Table row */ @@ -200,7 +207,7 @@ export class Table extends React.Component { private _rowItemSelectionHandlers?: Array>; private _cellItemSelectionHandlers?: Array>>; private _pressedItemSelected: boolean = false; - private _reactPortalRef = React.createRef(); + private _tableRef = React.createRef(); public readonly state: Readonly = initialState; @@ -292,25 +299,24 @@ export class Table extends React.Component { } public componentDidUpdate(previousProps: TableProps) { - /* istanbul ignore next */ - if (this.props.onRender) - this.props.onRender(); - if (this.props.dataProvider !== previousProps.dataProvider) { // tslint:disable-next-line:no-floating-promises this.update(); - } else if (this.props.isCellSelected !== previousProps.isCellSelected + return; + } + if (this.props.isCellSelected !== previousProps.isCellSelected || this.props.isRowSelected !== previousProps.isRowSelected) { this.updateSelectedRows(); this.updateSelectedCells(); } - } - public componentDidMount() { - this._isMounted = true; /* istanbul ignore next */ if (this.props.onRender) this.props.onRender(); + } + + public componentDidMount() { + this._isMounted = true; // tslint:disable-next-line:no-floating-promises this.update(); @@ -418,10 +424,16 @@ export class Table extends React.Component { /** @hidden */ public async update(): Promise { - const status = await this.updateColumns(); - if (status === UpdateStatus.Abort) - return status; - return this.updateRows(); + let status = await this.updateColumns(); + + if (status !== UpdateStatus.Abort) + status = await this.updateRows(); + + /* istanbul ignore next */ + if (this.props.onRender) + this.props.onRender(); + + return status; } public updateSelectedRows() { @@ -681,36 +693,21 @@ export class Table extends React.Component { }); }); - private async createCellRenderer(cellItem: CellItem, column: ReactDataGridColumn, displayValue: string): Promise<() => React.ReactNode> { - if (!cellItem.record) - return () => undefined; + private async renderCellContent(cellItem: CellItem, column: ReactDataGridColumn, displayValue: string): Promise> { if (column.icon) - return () => ; - - const cellStyle: React.CSSProperties = { - color: cellItem.colorOverrides && cellItem.colorOverrides.foreColor ? colorDecimalToHex(cellItem.colorOverrides.foreColor) : undefined, - backgroundColor: cellItem.colorOverrides && cellItem.colorOverrides.backColor ? colorDecimalToHex(cellItem.colorOverrides.backColor) : undefined, - fontWeight: cellItem.isBold ? "bold" : undefined, - fontStyle: cellItem.isItalic ? "italic" : undefined, - textAlign: cellItem.alignment ? horizontalAlignmentToCssAlignment(cellItem.alignment) : undefined, - }; - - const rendererContext: PropertyValueRendererContext = { - containerType: PropertyContainerType.Table, - onDialogOpen: this._onDialogOpen, - style: cellStyle, - // TODO: Enable, when table gets refactored. Explanation in ./../table/NonPrimitiveValueRenderer - // onPopupShow: this._onPopupShow, - // onPopupHide: this._onPopupHide, - }; - - let renderedElement: React.ReactNode; - - if (this.props.propertyValueRendererManager) - renderedElement = await this.props.propertyValueRendererManager.render(cellItem.record!, rendererContext); - else - renderedElement = await PropertyValueRendererManager.defaultManager.render(cellItem.record!, rendererContext); - return () => renderedElement; + return () => ; + + return (props: { isSelected: boolean }) => ( + + ); } private async getCellDisplayValue(cellItem: CellItem): Promise { @@ -737,13 +734,10 @@ export class Table extends React.Component { cellProps[column.key] = { item: cellItem, displayValue, - render: await this.createCellRenderer(cellItem, column, displayValue), + render: await this.renderCellContent(cellItem, column, displayValue), }; } - const rowStyle: React.CSSProperties = { - color: item.colorOverrides && item.colorOverrides.foreColor ? colorDecimalToHex(item.colorOverrides.foreColor) : undefined, - backgroundColor: item.colorOverrides && item.colorOverrides.backColor ? colorDecimalToHex(item.colorOverrides.backColor) : undefined, - }; + const rowStyle = TableRowStyleProvider.createStyle(item.colorOverrides ? item.colorOverrides : {}); return { item, index, @@ -847,59 +841,71 @@ export class Table extends React.Component { this.updateRows(); // tslint:disable-line:no-floating-promises } - private createRowCells(rowProps: RowProps): { [columnKey: string]: React.ReactNode } { + private createRowCells(rowProps: RowProps, isSelected: boolean): { [columnKey: string]: React.ReactNode } { const cells: { [columnKey: string]: React.ReactNode } = {}; - let columnIndex = -1; - for (const column of this.state.columns) { - columnIndex++; + + for (let index = 0; index < this.state.columns.length; index++) { + const column = this.state.columns[index]; + const cellProps = rowProps.cells[column.key]; if (!cellProps) { continue; } - const cell = cellProps.render(); + // tslint:disable-next-line:variable-name + const CellContent = cellProps.render; const isEditorCell = this.state.cellEditorState.active && this.state.cellEditorState.rowIndex === rowProps.index - && this.state.cellEditorState.colIndex === columnIndex; + && this.state.cellEditorState.colIndex === index + && cellProps.item.record; + + let onClick: ((e: React.MouseEvent) => void) | undefined; + let onMouseMove: ((e: React.MouseEvent) => void) | undefined; + let onMouseDown: ((e: React.MouseEvent) => void) | undefined; + let className: string | undefined; if (this._tableSelectionTarget === TableSelectionTarget.Cell) { const cellKey = { rowIndex: rowProps.index, columnKey: column.key }; const selectionHandler = this.createCellItemSelectionHandler(cellKey); const selectionFunction = this._cellSelectionHandler.createSelectionFunction(this._cellComponentSelectionHandler, selectionHandler); - const onClick = (e: React.MouseEvent) => selectionFunction(e.shiftKey, e.ctrlKey); - const onMouseMove = (e: React.MouseEvent) => { if (e.buttons === 1) this._cellSelectionHandler.updateDragAction(cellKey); }; - const onMouseDown = () => { + onClick = (e: React.MouseEvent) => selectionFunction(e.shiftKey, e.ctrlKey); + onMouseMove = (e: React.MouseEvent) => { if (e.buttons === 1) this._cellSelectionHandler.updateDragAction(cellKey); }; + onMouseDown = () => { this._cellSelectionHandler.createDragAction(this._cellComponentSelectionHandler, this.cellItemSelectionHandlers, cellKey); }; - const className = classnames("cell", this.isCellSelected(cellKey) ? "is-selected" : "is-hover-enabled"); - cells[column.key] =
    - {cell} -
    ; - } else { - if (isEditorCell && cellProps.item.record) - cells[column.key] = ; - else - cells[column.key] =
    {cell}
    ; - } + onMouseDown={onMouseDown} + cellEditingProps={isEditorCell ? { + onCancel: this._deactivateCellEditor, + onCommit: this._onCellCommit, + propertyRecord: cellProps.item.record!, + setFocus: true, + } : undefined} + > + + + ); } return cells; } private _createRowRenderer = () => { - return (props: { row: RowProps, [k: string]: any }) => { + return (props: { row: RowProps, [k: string]: React.ReactNode }) => { const renderRow = this.props.renderRow ? this.props.renderRow : this.renderRow; const { row: rowProps, ...reactDataGridRowProps } = props; - const cells = this.createRowCells(rowProps); if (this._tableSelectionTarget === TableSelectionTarget.Row) { const selectionFunction = this._rowSelectionHandler.createSelectionFunction(this._rowComponentSelectionHandler, this.createRowItemSelectionHandler(props.row.index)); - const onClick = (e: React.MouseEvent) => { - selectionFunction(e.shiftKey, e.ctrlKey); - }; - const onMouseDown = (_e: React.MouseEvent) => { + const onClick = (e: React.MouseEvent) => selectionFunction(e.shiftKey, e.ctrlKey); + const onMouseDown = () => { this._rowSelectionHandler.createDragAction(this._rowComponentSelectionHandler, [this.rowItemSelectionHandlers], props.row.index); }; const onMouseMove = (e: React.MouseEvent) => { @@ -907,17 +913,20 @@ export class Table extends React.Component { this._rowSelectionHandler.updateDragAction(props.row.index); }; const isSelected = this._selectedRowIndices.has(props.row.index); + const cells = this.createRowCells(rowProps, isSelected); const row = renderRow(rowProps.item, { ...reactDataGridRowProps, cells, isSelected }); return
    {row}
    ; + } else { + const cells = this.createRowCells(rowProps, false); + return renderRow(rowProps.item, { ...reactDataGridRowProps, cells }); } - return renderRow(rowProps.item, { ...reactDataGridRowProps, cells }); }; } @@ -1048,21 +1057,14 @@ export class Table extends React.Component { return true; } - private _onDialogOpen = (dialogState: PropertyDialogState) => { - this.setState({ dialog: dialogState }); - } + private _onDialogOpen = (dialogState: PropertyDialogState) => this.setState({ dialog: dialogState }); - private _onDialogClose = () => { - this.setState({ dialog: undefined }); - } + private _onDialogClose = () => this.setState({ dialog: undefined }); // TODO: Enable, when table gets refactored. Explanation in ./../table/NonPrimitiveValueRenderer - // private _onPopupShow = (popupState: PropertyPopupState) => { - // this.setState({ popup: popupState }); - // } - // private _onPopupHide = () => { - // this.setState({ popup: undefined }); - // } + // private _onPopupShow = (popupState: PropertyPopupState) => this.setState({ popup: popupState }); + + // private _onPopupHide = () => this.setState({ popup: undefined }); public render() { const rowRenderer = this._createRowRenderer()} />; @@ -1070,7 +1072,7 @@ export class Table extends React.Component { const visibleColumns = this._getVisibleColumns(); return ( <> -
    +
    {this.props.showHideColumns && { onShowHideChange={this._handleShowHideChange} /> } - {(width: number, height: number) => } + {(width: number, height: number) => + }
    -
    +
    {this.state.dialog ? { {this.state.dialog.content} : undefined} - {/* TODO: Enable, when table gets refactored. Explanation in ./../table/NonPrimitiveValueRenderer */} + {/* TODO: Enable, when table gets refactored. Explanation in ./../../properties/renderers/value/table/NonPrimitiveValueRenderer */} {/* {this.state.popup ? { } } -const horizontalAlignmentToCssAlignment = (alignment: HorizontalAlignment) => { - switch (alignment) { - case HorizontalAlignment.Left: return "left"; - case HorizontalAlignment.Center: return "center"; - case HorizontalAlignment.Right: return "right"; - case HorizontalAlignment.Justify: return "justify"; - } - return "left"; -}; - -const colorDecimalToHex = (decimal: number) => `#${decimal.toString(16).padStart(6, "0")}`; - -/** Properties for the [[IconCell]] component */ -export interface IconCellProps { - /** Icon name */ - value: string; -} - -/** - * Formatter for Bentley icons. - */ -export class IconCell extends React.Component { - public render() { - return
    ; - } -} - /** * Props for the [[TableRow]] component */ diff --git a/ui/components/src/ui-components/table/component/TableCell.scss b/ui/components/src/ui-components/table/component/TableCell.scss new file mode 100644 index 0000000..b04c288 --- /dev/null +++ b/ui/components/src/ui-components/table/component/TableCell.scss @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +@import "@bentley/ui-core/lib/ui-core/index"; + +.components-table-cell { + user-select: none; + display: flex; + align-items: center; + color: $buic-foreground-body; + background-color: $buic-background-control; + + &.is-selected { + background-color: $buic-row-selection; + } + + &:focus { + outline: none; + } + + &>* { + padding: 8px 0 0 8px; + height: 100%; + width: calc(100% - 8px); + } + + >span:empty:before { + content: "\200b"; // unicode zero width space character + } +} \ No newline at end of file diff --git a/ui/components/src/ui-components/table/component/TableCell.tsx b/ui/components/src/ui-components/table/component/TableCell.tsx new file mode 100644 index 0000000..8ca323e --- /dev/null +++ b/ui/components/src/ui-components/table/component/TableCell.tsx @@ -0,0 +1,165 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module Table */ + +import * as React from "react"; +import classnames from "classnames"; +import { Omit } from "@bentley/ui-core"; +import { + EditorContainerProps, EditorContainer, CellItem, PropertyValueRendererManager, + PropertyDialogState, PropertyValueRendererContext, PropertyContainerType, +} from "../../../ui-components"; + +import "./TableCell.scss"; +import { ItemStyleProvider } from "../../properties/ItemStyle"; + +/** + * Properties of the [[TableCell]] React component + */ +export interface TableCellProps { + /** Additional class name for the cell container */ + className?: string; + /** Title of the cell container */ + title: string; + /** Cell content */ + children?: React.ReactNode; + /** Click event callback */ + onClick?: (e: React.MouseEvent) => void; + /** MouseMove event callback */ + onMouseMove?: (e: React.MouseEvent) => void; + /** MouseDown event callback */ + onMouseDown?: (e: React.MouseEvent) => void; + /** Properties for [[EditorContainer]]. Activates cell editing and provides properties to the container */ + cellEditingProps?: Omit; +} + +/** + * A React component that renders a table cell + */ +export class TableCell extends React.PureComponent { + public render() { + if (this.props.cellEditingProps) + return ( + + ); + + return ( +
    + {this.props.children} +
    + ); + } +} + +/** Properties of the [[TableCellContent]] React component */ +export interface TableCellContentProps { + /** Indicates, whether container cell is selected or not */ + isSelected: boolean; + /** Props for the item that will be rendered */ + cellItem: CellItem; + /** Height of the component */ + height?: number; + /** Callback to DialogOpen event */ + onDialogOpen?: (state: PropertyDialogState) => void; + /** Property value renderer manager */ + propertyValueRendererManager: PropertyValueRendererManager; +} + +/** State of the [[TableCellContent]] React component */ +export interface TableCellContentState { + /** Rendered content */ + content: React.ReactNode; +} + +/** A React component that renders table cell content */ +export class TableCellContent extends React.PureComponent { + public readonly state: TableCellContentState = { + content:
    , + }; + + private _isMounted = false; + + private getStyle(cellItem: CellItem, isSelected: boolean, height?: number): React.CSSProperties { + return { + ...ItemStyleProvider.createStyle(cellItem.style ? cellItem.style : {}, isSelected), + textAlign: cellItem.alignment, + height, + }; + } + + private async renderContent(props: TableCellContentProps) { + const style = this.getStyle(props.cellItem, props.isSelected, props.height); + + if (!props.cellItem.record) + return
    ; + + const rendererContext: PropertyValueRendererContext = { + containerType: PropertyContainerType.Table, + onDialogOpen: this.props.onDialogOpen, + style, + // TODO: Enable, when table gets refactored. Explanation in ./../table/NonPrimitiveValueRenderer + // onPopupShow: this._onPopupShow, + // onPopupHide: this._onPopupHide, + }; + + return this.props.propertyValueRendererManager.render(props.cellItem.record, rendererContext); + } + + private doPropsDiffer(props1: TableCellContentProps, props2: TableCellContentProps) { + return props1.cellItem !== props2.cellItem + || props1.isSelected !== props2.isSelected + || props1.onDialogOpen !== props2.onDialogOpen + || props1.propertyValueRendererManager !== props2.propertyValueRendererManager; + } + + public async componentDidMount() { + this._isMounted = true; + const content = await this.renderContent(this.props); + + if (this._isMounted) + this.setState({ content }); + } + + public componentWillUnmount() { + this._isMounted = false; + } + + public async componentDidUpdate(prevProps: TableCellContentProps) { + if (this.doPropsDiffer(prevProps, this.props)) { + const content = await this.renderContent(this.props); + + if (this._isMounted) + this.setState({ content }); + } + } + + public render() { + return this.state.content; + } +} + +/** Properties for the [[TableIconCellContent]] React component */ +export interface TableIconCellContentProps { + /** Icon name */ + iconName: string; +} + +/** + * A React component that renders table cell content as a Bentley icon + */ +export class TableIconCellContent extends React.PureComponent { + public render() { + return
    ; + } +} diff --git a/ui/components/src/ui-components/table/hocs/DragDropRow.scss b/ui/components/src/ui-components/table/hocs/DragDropRow.scss index 9bb5b87..65344d8 100644 --- a/ui/components/src/ui-components/table/hocs/DragDropRow.scss +++ b/ui/components/src/ui-components/table/hocs/DragDropRow.scss @@ -1,53 +1,53 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ -@import "@bentley/bwc/lib/mixins"; - -.column-drag-layer { - border: 1px solid rgba($bwc-black, 0.5); - border-radius: $bwc-border-radius; - padding: 0px; - margin: 0px; - line-height: $bwc-line-height; - color: rgba($bwc-text-color, 0.5); - font-weight: bold; -} - -.table-header-drag-drop { - &.dragging .react-grid-HeaderCell { - background: $bwc-gray; - } - &.left .react-grid-HeaderCell { - border-left: solid black 2px; - } - &.right .react-grid-HeaderCell { - border-right: solid black 2px; - } -} - -.table-drop-target { - border-top: solid transparent 1px; - border-bottom: solid transparent 1px; - - &.above { - border-top: solid black 1px; - } - - &.on { - .react-grid-Row * { - /* !important used because react-data-grid did. only way to override */ - background-color: $bwc-blue-8 !important; - } - } - - &.below { - border-bottom: solid black 1px; - } - - &.dragging { - .react-grid-Row * { - color: $bwc-gray; - } - } -} +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +@import "@bentley/ui-core/lib/ui-core/index"; + +.column-drag-layer { + border: 1px solid rgba($uicore-black, 0.5); + border-radius: $uicore-border-radius; + padding: 0px; + margin: 0px; + line-height: $uicore-line-height; + color: rgba($uicore-text-color, 0.5); + font-weight: bold; +} + +.table-header-drag-drop { + &.dragging .react-grid-HeaderCell { + background: $uicore-gray; + } + &.left .react-grid-HeaderCell { + border-left: solid black 2px; + } + &.right .react-grid-HeaderCell { + border-right: solid black 2px; + } +} + +.table-drop-target { + border-top: solid transparent 1px; + border-bottom: solid transparent 1px; + + &.above { + border-top: solid black 1px; + } + + &.on { + .react-grid-Row * { + /* !important used because react-data-grid did. only way to override */ + background-color: $uicore-blue-8 !important; + } + } + + &.below { + border-bottom: solid black 1px; + } + + &.dragging { + .react-grid-Row * { + color: $uicore-gray; + } + } +} diff --git a/ui/components/src/ui-components/table/hocs/DragDropRow.tsx b/ui/components/src/ui-components/table/hocs/DragDropRow.tsx index c9906e2..2ab8fb4 100644 --- a/ui/components/src/ui-components/table/hocs/DragDropRow.tsx +++ b/ui/components/src/ui-components/table/hocs/DragDropRow.tsx @@ -40,13 +40,14 @@ interface RowWrapperState { hoverMode: HoverMode; } +/** @hidden */ export class DragDropRowWrapper extends React.Component { private _root: HTMLDivElement | null = null; public readonly state: RowWrapperState = { hoverMode: HoverMode.Above, }; public render(): React.ReactElement { - const { isDragging, isOver, canDrag, canDrop, canDropOn, onRender, children, ...props } = this.props as RowWrapperProps; + const { isDragging, isOver, canDrop, children } = this.props as RowWrapperProps; const mode = this.state.hoverMode; const classes = classnames("table-drop-target", { above: canDrop && isOver && mode === HoverMode.Above, @@ -55,7 +56,7 @@ export class DragDropRowWrapper extends React.Component { this._root = el; }} onDragOver={this._handleDragOver}> diff --git a/ui/components/src/ui-components/tree/CellEditingEngine.tsx b/ui/components/src/ui-components/tree/CellEditingEngine.tsx new file mode 100644 index 0000000..a53c45f --- /dev/null +++ b/ui/components/src/ui-components/tree/CellEditingEngine.tsx @@ -0,0 +1,162 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module Tree */ + +import * as React from "react"; +import { PrimitiveValue, PropertyRecord, PropertyValueFormat, PropertyDescription } from "@bentley/imodeljs-frontend"; +import { BeInspireTreeNode } from "./component/BeInspireTree"; +import { TreeNodeItem } from "./TreeDataProvider"; +import { PropertyUpdatedArgs, EditorContainer } from "../editors/EditorContainer"; + +/** Properties for a tree that has cell editing enabled */ +export interface EditableTreeProps { + /** Callback to when editing starts */ + onCellEditing: (currentlyActiveNode?: BeInspireTreeNode) => void; + /** Callback to when editing finishes */ + onCellUpdated: (args: TreeCellUpdatedArgs) => Promise; + + /** @hidden */ + ignoreEditorBlur?: boolean; +} + +/** Arguments for the Tree Cell Updated event callback */ +export interface TreeCellUpdatedArgs { + /** The cell being updated. */ + node: BeInspireTreeNode; + /** The new value for the cell. */ + newValue: string; +} + +/** @hidden */ +export type SetCurrentlyEditedNode = (currentlyEditedNode?: BeInspireTreeNode) => void; + +/** @hidden */ +export type GetCurrentlyEditedNode = () => BeInspireTreeNode | undefined; + +/** @hidden */ +export class CellEditingEngine { + private _getCurrentlyEditedNode?: GetCurrentlyEditedNode; + private _setCurrentlyEditedNode?: SetCurrentlyEditedNode; + private readonly _props: EditableTreeProps; + + /** + * @param props Cell editing properties + */ + constructor(props: EditableTreeProps) { + this._props = props; + } + + /** + * @param getEditorState Function, that returns currently edited node + * @param setEditorState Function, that sets currently edited node + */ + public subscribe(getCurrentNode: GetCurrentlyEditedNode, setCurrentNode: SetCurrentlyEditedNode) { + this._getCurrentlyEditedNode = getCurrentNode; + this._setCurrentlyEditedNode = setCurrentNode; + } + + public get hasSubscriptions() { + return !!(this._getCurrentlyEditedNode && this._setCurrentlyEditedNode); + } + + public unsubscribe() { + this._getCurrentlyEditedNode = undefined; + this._setCurrentlyEditedNode = undefined; + } + + public static createPropertyRecord(value: string, typename: string = "text", editor?: string) { + const v: PrimitiveValue = { + valueFormat: PropertyValueFormat.Primitive, + value, + displayValue: value, + }; + + const p: PropertyDescription = { + name: "tree-cell-editor", + displayLabel: "Tree Cell Editor", + typename, + }; + + if (editor) + p.editor = { name: editor, params: [] }; + + const record = new PropertyRecord(v, p); + record.description = ""; + record.isReadonly = false; + + return record; + } + + private _onCommit = async (args: PropertyUpdatedArgs) => { + if (!this._getCurrentlyEditedNode || !this._setCurrentlyEditedNode) + return; + + if (this._getCurrentlyEditedNode()) { + const newValue = (args.newValue as PrimitiveValue).value as string; + const cellUpdatedArgs: TreeCellUpdatedArgs = { + node: this._getCurrentlyEditedNode()!, + newValue, + }; + const allowed = await this._props.onCellUpdated(cellUpdatedArgs); + if (allowed) + this._getCurrentlyEditedNode()!.setDirty(true); + } + this.deactivateEditor(); + } + + public deactivateEditor = (): void => { + if (!this._setCurrentlyEditedNode || !this._getCurrentlyEditedNode) + return; + + const node = this._getCurrentlyEditedNode(); + if (!node) + return; + + node.setDirty(true); + this._setCurrentlyEditedNode(undefined); + } + + public checkStatus = (node: BeInspireTreeNode, isPressedItemSelected: boolean) => { + if (node.selected() && isPressedItemSelected && node.payload && node.payload.isEditable) + this.activateEditor(node); + else + this.deactivateEditor(); + } + + public activateEditor = (node: BeInspireTreeNode) => { + if (!this._setCurrentlyEditedNode || !this._getCurrentlyEditedNode) + return; + + const currentNode = this._getCurrentlyEditedNode(); + if (currentNode === node) + return; + + if (currentNode) + currentNode.setDirty(true); + + this._setCurrentlyEditedNode(node); + this._props.onCellEditing(node); + } + + public isEditingEnabled(node: BeInspireTreeNode) { + return this._props && this._getCurrentlyEditedNode && node === this._getCurrentlyEditedNode(); + } + + public renderEditor(node: BeInspireTreeNode, style?: React.CSSProperties) { + const record = CellEditingEngine.createPropertyRecord(node.text); + return ( + + + + ); + } +} diff --git a/ui/components/src/ui-components/tree/HighlightingEngine.scss b/ui/components/src/ui-components/tree/HighlightingEngine.scss index ea01678..07d99e1 100644 --- a/ui/components/src/ui-components/tree/HighlightingEngine.scss +++ b/ui/components/src/ui-components/tree/HighlightingEngine.scss @@ -3,6 +3,6 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license terms. *--------------------------------------------------------------------------------------------*/ -.ui-components-activehighlight { +.components-activehighlight { background-color: orange; } \ No newline at end of file diff --git a/ui/components/src/ui-components/tree/HighlightingEngine.tsx b/ui/components/src/ui-components/tree/HighlightingEngine.tsx index d342b0b..965d189 100644 --- a/ui/components/src/ui-components/tree/HighlightingEngine.tsx +++ b/ui/components/src/ui-components/tree/HighlightingEngine.tsx @@ -31,7 +31,7 @@ export interface HighlightableTreeNodeProps { export default class HighlightingEngine { private _searchText: string; private _activeMatch?: ActiveMatchInfo; - public static readonly ACTIVE_CLASS_NAME = "ui-components-activehighlight"; + public static readonly ACTIVE_CLASS_NAME = "components-activehighlight"; constructor(props: HighlightableTreeProps) { this._searchText = props.searchText; diff --git a/ui/components/src/ui-components/tree/ImageLoader.ts b/ui/components/src/ui-components/tree/ImageLoader.ts new file mode 100644 index 0000000..4921b1f --- /dev/null +++ b/ui/components/src/ui-components/tree/ImageLoader.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module Tree */ + +import { IImageLoader, LoadedImage } from "../common/IImageLoader"; +import { TreeNodeItem } from "./TreeDataProvider"; +import { BeInspireTreeNodeITree } from "./component/BeInspireTree"; + +/** Interface for a tree image loader */ +export interface ITreeImageLoader extends IImageLoader { + load: (item: TreeNodeItem | BeInspireTreeNodeITree) => LoadedImage | undefined; +} + +/** Default image loader for the tree */ +export class TreeImageLoader implements ITreeImageLoader { + /** Loads image data from either [[TreeNodeItem]] or [[BeInspireTreeNodeITree]] */ + public load(item: TreeNodeItem | BeInspireTreeNodeITree): LoadedImage | undefined { + if (!item.icon) + return undefined; + + return { + sourceType: "core-icon", + value: item.icon, + }; + } +} diff --git a/ui/components/src/ui-components/tree/TreeDataProvider.ts b/ui/components/src/ui-components/tree/TreeDataProvider.ts index d8e6490..b49d1d7 100644 --- a/ui/components/src/ui-components/tree/TreeDataProvider.ts +++ b/ui/components/src/ui-components/tree/TreeDataProvider.ts @@ -7,6 +7,7 @@ import { CheckBoxState } from "@bentley/ui-core"; import { PageOptions } from "../common/PageOptions"; import { BeEvent } from "@bentley/bentleyjs-core"; +import { ItemStyle } from "../properties/ItemStyle"; /** * A node item which can be displayed in a tree. @@ -17,10 +18,6 @@ export interface TreeNodeItem { label: string; description?: string; autoExpand?: boolean; - labelForeColor?: number; - labelBackColor?: number; - labelBold?: boolean; - labelItalic?: boolean; icon?: string; isCheckboxVisible?: boolean; isCheckboxDisabled?: boolean; @@ -29,6 +26,8 @@ export interface TreeNodeItem { isEditable?: boolean; /** Primitive typename. See PropertyRecord.PropertyDescription */ typename?: string; + + style?: ItemStyle; } /** A [[TreeNodeItem]] for immediately loaded trees */ diff --git a/ui/components/src/ui-components/tree/component/BeInspireTree.ts b/ui/components/src/ui-components/tree/component/BeInspireTree.ts index f7d895f..b5bba7c 100644 --- a/ui/components/src/ui-components/tree/component/BeInspireTree.ts +++ b/ui/components/src/ui-components/tree/component/BeInspireTree.ts @@ -8,6 +8,7 @@ import InspireTree, * as Inspire from "inspire-tree"; import { isArrayLike } from "lodash"; import { CallableInstance } from "callable-instance2/import"; import { IDisposable, using } from "@bentley/bentleyjs-core"; +import { CheckBoxInfo, CheckBoxState, isPromiseLike } from "@bentley/ui-core"; import { PageOptions } from "../../common/PageOptions"; /** @@ -37,10 +38,32 @@ export enum BeInspireTreeEvent { } /** Be alias for Inspire.NodeConfig */ -export type BeInspireTreeNodeConfig = Inspire.NodeConfig; +export interface BeInspireTreeNodeConfig { + children?: BeInspireTreeNodeConfig[] | true; + id?: string; + text: string; + itree?: BeInspireTreeNodeITree; +} + +/** Be alias for Inspire.NodeConfig.ITree */ +export interface BeInspireTreeNodeITree { + icon?: string; + state?: { + checkboxVisible?: boolean; + checkboxDisabled?: boolean; + checked?: boolean; + collapsed?: boolean; + editable?: boolean; + focused?: boolean; + indeterminate?: boolean; + loading?: boolean; + selectable?: boolean; + selected?: boolean; + }; +} /** Data structure for [[BeInspireTreeNodeConfig]] with our injected props */ -export interface BeInspireTreeNodePayloadConfig extends Inspire.NodeConfig { +export interface BeInspireTreeNodePayloadConfig extends BeInspireTreeNodeConfig { /** Node's data. May be `undefined` if this is placeholder node. */ payload?: TPayload; /** Index of the node at the parent level. Only set if this is a placeholder node. */ @@ -165,7 +188,7 @@ interface DeferredLoadingHandler { /** * Configuration properties for [[BeInspireTree]] */ -export interface Props { +export interface BeInspireTreeProps { dataProvider: BeInspireTreeDataProvider; mapPayloadToInspireNodeConfig: MapPayloadToInspireNodeCallback; pageSize?: number; @@ -183,9 +206,9 @@ export class BeInspireTree { private _deferredLoadingHandler?: DeferredLoadingHandler; private _visibleCached?: BeInspireTreeNodes; private _suspendedRendering?: EventsMuteContext; - public props: Props; + public props: BeInspireTreeProps; - constructor(props: Props) { + constructor(props: BeInspireTreeProps) { this.props = props; this._eventMutes = new Map(); @@ -232,7 +255,7 @@ export class BeInspireTree { const baseTreeLoad = this._tree.load; this._tree.load = async (loader): Promise => { const result = await baseTreeLoad.call(this._tree, loader); - await using(this.pauseRendering(), async () => { + await using(this.pauseRendering(), async (_r) => { await ensureNodesAutoExpanded(result); }); return result; @@ -259,7 +282,7 @@ export class BeInspireTree { }).then(async () => { // note: the following is only needed for the initial load of the tree // when our `load` override isn't assigned yet - await using(this.pauseRendering(), async () => { + await using(this.pauseRendering(), async (_r) => { await ensureNodesAutoExpanded(this._tree.nodes()); }); }); @@ -304,7 +327,7 @@ export class BeInspireTree { // tslint:disable-next-line:naming-convention private doEmit = (events: BeInspireTreeEvent[]) => { - this._tree.emit(events); + events.forEach((e) => this._tree.emit(e)); } /** @@ -411,7 +434,7 @@ export class BeInspireTree { /** Reload the tree */ public async reload() { - await using(this.pauseRendering(), async () => { + await using(this.pauseRendering(), async (_r) => { const rootNodes = await this._tree.reload(); rootNodes.forEach((n) => toNode(n).setDirty(true)); }); @@ -421,7 +444,7 @@ export class BeInspireTree { * Deselects all nodes */ public deselectAll(muteEvents = true) { - using(this.mute((muteEvents) ? [BeInspireTreeEvent.NodeDeselected] : []), () => { + using(this.mute((muteEvents) ? [BeInspireTreeEvent.NodeDeselected] : []), (_r) => { this._tree.deselectDeep(); }); } @@ -432,7 +455,7 @@ export class BeInspireTree { * Note: order of supplied nodes is not important */ public selectBetween(node1: BeInspireTreeNode, node2: BeInspireTreeNode, muteEvents = true): Array> { - return using(this.mute((muteEvents) ? [BeInspireTreeEvent.NodeSelected] : []), () => { + return using(this.mute((muteEvents) ? [BeInspireTreeEvent.NodeSelected] : []), (_r) => { let start, end: BeInspireTreeNode; if (node1.indexPath() <= node2.indexPath()) { start = node1; @@ -478,7 +501,7 @@ export class BeInspireTree { if (!predicate) return; - using(this.mute((muteEvents) ? [BeInspireTreeEvent.NodeSelected, BeInspireTreeEvent.NodeDeselected] : []), () => { + using(this.mute((muteEvents) ? [BeInspireTreeEvent.NodeSelected, BeInspireTreeEvent.NodeDeselected] : []), (_r) => { this._tree.disableDeselection(); selectHandler(predicate); this._tree.enableDeselection(); @@ -511,6 +534,63 @@ export class BeInspireTree { return this.updateSelection(selectFunc, nodesToSelect, muteEvents); } + private updateNodeCheckboxInfo(node: BeInspireTreeNode, status: CheckBoxInfo) { + let hasChanges = false; + if (node.itree!.state!.checkboxVisible !== status.isVisible) { + node.itree!.state!.checkboxVisible = status.isVisible; + hasChanges = true; + } + if (node.itree!.state!.checkboxDisabled !== status.isDisabled) { + node.itree!.state!.checkboxDisabled = status.isDisabled; + hasChanges = true; + } + // note: can't use `check()` & `uncheck()` because they also fiddle with + // parent node which we don't want + if (status.state === CheckBoxState.On && !node.itree!.state!.checked) { + node.itree!.state!.checked = true; + this._tree.emit(BeInspireTreeEvent.NodeChecked, node); + hasChanges = true; + } else if (status.state === CheckBoxState.Off && node.itree!.state!.checked) { + node.itree!.state!.checked = false; + this._tree.emit(BeInspireTreeEvent.NodeUnchecked, node); + hasChanges = true; + } + if (hasChanges) { + node.setDirty(true); + this.applyChanges(); + } + } + + /** + * Updates checkbox states of the whole tree using the `checkboxInfo` callback function + */ + public async updateTreeCheckboxes(checkboxInfo: ((payload: TNodePayload) => CheckBoxInfo | Promise), muteEvents = true) { + await this.updateNodesCheckboxes(this.flatten(), checkboxInfo, muteEvents); + } + + /** + * Updates checkbox states of provided `nodes` based on `checkboxInfo` callback function + */ + public async updateNodesCheckboxes(nodes: BeInspireTreeNodes, checkboxInfo: ((payload: TNodePayload) => CheckBoxInfo | Promise), muteEvents = true) { + await using(this.pauseRendering(), async (_r1) => { + await using(this.mute((muteEvents) ? [BeInspireTreeEvent.NodeChecked, BeInspireTreeEvent.NodeUnchecked] : []), async (_r2) => { + const promises = new Array>(); + nodes.forEach((n) => { + if (!n.payload) + return; + + const status = checkboxInfo(n.payload); + if (isPromiseLike(status)) + promises.push(status.then((s) => this.updateNodeCheckboxInfo(n, s))); + else + this.updateNodeCheckboxInfo(n, status); + }); + if (promises.length !== 0) + await Promise.all(promises); + }); + }); + } + /** @hidden */ public createPlaceholderNode(index: number, parent?: BeInspireTreeNode): BeInspireTreeNode { const node = toNode(this._tree.createNode({ @@ -780,7 +860,7 @@ class WrappedInterfaceProvider extends CallableInstance implements Def // merge current nodes with stash), there's no point to show the // `loading` state and then re-render with `completed` state + nodes. // instead, just pause rendering until we have the nodes - await using(this._tree.pauseRendering(), async () => { + await using(this._tree.pauseRendering(), async (_r) => { // request children for `parent` to reload - the load handler // will merge the current children with stash (see `inspireLoad`) if (parentId) @@ -823,7 +903,7 @@ class WrappedInterfaceProvider extends CallableInstance implements Def } /** Called by inspire-tree */ - private async inspireLoad(parent: BeInspireTreeNode | undefined, resolve: (nodes: Array>, totalCount: number) => any) { + private inspireLoad(parent: BeInspireTreeNode | undefined, resolve: (nodes: Array>, totalCount: number) => any) { if (!this._paginationHelper) { // pagination is disabled - just load all nodes for the parent const payload = parent ? parent.payload : undefined; @@ -838,18 +918,22 @@ class WrappedInterfaceProvider extends CallableInstance implements Def // paginated behavior const parentId = parent ? parent.id : undefined; + const complete = () => { + const pagedNodes = this.createPagedNodesResult(parent); + if (parent && isArrayLike(parent.children)) { + // reset so concat doesn't duplicate nodes + parent.children = true; + } + onNodesDelayLoaded(parent, pagedNodes); + resolve(pagedNodes, pagedNodes.length); + }; if (!this._paginationHelper.hasOrWillHaveLoadedPages(parentId)) { // parent has no children yet - initiate a request and wait this._initialRequests.add(parentId); - await this.requestNodeLoad(parent, 0); - } - const pagedNodes = this.createPagedNodesResult(parent); - if (parent && isArrayLike(parent.children)) { - // reset so concat doesn't duplicate nodes - parent.children = true; + this.requestNodeLoad(parent, 0).then(complete); // tslint:disable-line: no-floating-promises + } else { + complete(); } - onNodesDelayLoaded(parent, pagedNodes); - resolve(pagedNodes, pagedNodes.length); } } interface WrappedInterfaceProvider { diff --git a/ui/components/src/ui-components/tree/component/Node.tsx b/ui/components/src/ui-components/tree/component/Node.tsx index 14895fe..e9db9de 100644 --- a/ui/components/src/ui-components/tree/component/Node.tsx +++ b/ui/components/src/ui-components/tree/component/Node.tsx @@ -5,23 +5,27 @@ /** @module Tree */ import * as React from "react"; -import { TreeCellEditorState, RenderNodeLabelProps, Tree } from "./Tree"; -import { PropertyUpdatedArgs } from "../../editors/EditorContainer"; +import { Tree } from "./Tree"; import { BeInspireTreeNode } from "./BeInspireTree"; import { TreeNodeItem } from "../TreeDataProvider"; +import { + TreeNode as TreeNodeBase, NodeCheckboxProps as CheckboxProps, Omit, + CheckBoxState, NodeCheckboxRenderer, shallowDiffers, +} from "@bentley/ui-core"; +import { TreeNodeContent } from "./NodeContent"; +import { CellEditingEngine } from "../CellEditingEngine"; import { HighlightableTreeNodeProps } from "../HighlightingEngine"; -import { TreeNode as TreeNodeBase, TreeNodePlaceholder, shallowDiffers, CheckBoxState } from "@bentley/ui-core"; import { PropertyValueRendererManager } from "../../properties/ValueRendererManager"; +import { ITreeImageLoader } from "../ImageLoader"; +import { Image } from "../../common/IImageLoader"; +import { ImageRenderer } from "../../common/ImageRenderer"; /** - * Properties related to a Tree node cell editor + * Properties for Checkbox in [[TreeNode]] * @hidden */ -export interface TreeNodeCellEditorProps { - cellEditorState: TreeCellEditorState; - onCellEditCommit: (args: PropertyUpdatedArgs) => void; - onCellEditCancel: () => void; - ignoreEditorBlur?: boolean; +export interface NodeCheckboxProps extends Omit { + onClick: (node: BeInspireTreeNode, newState: CheckBoxState) => void; } /** @@ -30,19 +34,24 @@ export interface TreeNodeCellEditorProps { */ export interface TreeNodeProps { node: BeInspireTreeNode; - highlightProps?: HighlightableTreeNodeProps; - isCheckboxVisible?: boolean; - isCheckboxDisabled?: boolean; - onCheckboxClick?: (node: BeInspireTreeNode) => void; - checkboxState?: CheckBoxState; - cellEditorProps?: TreeNodeCellEditorProps; - /** A function that renders node label. Both synchronous and asynchronous can be handled */ - renderLabel: (props: RenderNodeLabelProps) => React.ReactNode | Promise; + checkboxProps?: NodeCheckboxProps; onClick?: (e: React.MouseEvent) => void; onMouseDown?: (e: React.MouseEvent) => void; onMouseMove?: (e: React.MouseEvent) => void; onMouseUp?: (e: React.MouseEvent) => void; - valueRendererManager?: PropertyValueRendererManager; + + cellEditing?: CellEditingEngine; + highlightProps?: HighlightableTreeNodeProps; + showDescription?: boolean; + valueRendererManager: PropertyValueRendererManager; + + /** If specified, icon from node will be loaded by provided ImageLoader */ + imageLoader?: ITreeImageLoader; + + renderOverrides?: { + renderCheckbox?: NodeCheckboxRenderer; + }; + /** * Called when all of the component tasks are done. * There are 3 different conditions: @@ -55,96 +64,46 @@ export interface TreeNodeProps { * Id specified by the parent component to identify all * nodes rendered at one request */ - renderId: string; -} - -/** - * State of [[TreeNode]] React component - * @hidden - */ -export interface TreeNodeState { - prevProps: TreeNodeProps; - renderedLabel?: React.ReactNode; - onLabelRendered: (renderedLabel: React.ReactNode) => void; + renderId?: string; } /** * Default component for rendering a node for the [[Tree]] * @hidden */ -export class TreeNode extends React.Component { - private _isMounted = false; - - public constructor(props: TreeNodeProps) { - super(props); - const label = createLabel(props); - if (isPromise(label)) { - // tslint:disable-next-line: no-floating-promises - label.then(this._onLabelRendered); - } - this.state = { - prevProps: props, - renderedLabel: isPromise(label) ? undefined : label, - onLabelRendered: this._onLabelRendered, - }; - } - - private _onLabelRendered = (renderedLabel: React.ReactNode) => { - if (this._isMounted) - this.setState({ renderedLabel }); - } - - public static getDerivedStateFromProps(props: TreeNodeProps, state: TreeNodeState): TreeNodeState | null { - const base = { ...state, prevProps: props }; - const needReset = (props.node.isDirty() || doPropsDiffer(props, state.prevProps)) - && props.renderId !== state.prevProps.renderId; - if (!needReset) - return base; - - const label = createLabel(props); - if (isPromise(label)) { - // tslint:disable-next-line: no-floating-promises - label.then(state.onLabelRendered); - return { ...base, renderedLabel: undefined }; - } - - return { ...base, renderedLabel: label }; - } - - public componentDidMount() { - this._isMounted = true; - if (this.state.renderedLabel && this.props.onFinalRenderComplete) - this.props.onFinalRenderComplete(this.props.renderId); - } - - public componentWillUnmount() { - this._isMounted = false; - } - - public shouldComponentUpdate(nextProps: TreeNodeProps, nextState: TreeNodeState) { - if (this.state.renderedLabel !== nextState.renderedLabel || nextProps.node.isDirty()) +export class TreeNode extends React.Component { + public shouldComponentUpdate(nextProps: TreeNodeProps) { + if (nextProps.node.isDirty() || doPropsDiffer(this.props, nextProps)) return true; - if (nextState.renderedLabel) { - // This is an anti-pattern, but it's main purpose is for testing. - // We need to know when all of the nodes have finished rendering - // and asynchronous updates make it very difficult. If it should not - // render, let the parent know that it's already fully rendered - if (nextProps.onFinalRenderComplete) - nextProps.onFinalRenderComplete(nextProps.renderId); - } + // This is an anti-pattern, but it's main purpose is for testing. + // We need to know when all of the nodes have finished rendering + // and asynchronous updates make it very difficult. + // If it should not render, let the parent know that it's + // already fully rendered + if (nextProps.renderId && nextProps.onFinalRenderComplete) + nextProps.onFinalRenderComplete(nextProps.renderId); return false; } - public componentDidUpdate(_prevProps: TreeNodeProps) { - if (this.state.renderedLabel && this.props.onFinalRenderComplete) - this.props.onFinalRenderComplete(this.props.renderId); - } - public render() { - // note: props get mutated here - this.props.node.setDirty(false); + const checkboxProps: CheckboxProps | undefined = this.props.checkboxProps ? { + ...this.props.checkboxProps, + onClick: this._onCheckboxClick, + } : undefined; + + const label = ( + ); + return ( { isSelected={this.props.node.selected()} isLoading={this.props.node.loading()} isLeaf={!this.props.node.hasOrWillHaveChildren()} - label={this.state.renderedLabel ? this.state.renderedLabel : } - icon={this.props.node.itree && this.props.node.itree.icon ? : undefined} - isCheckboxVisible={this.props.isCheckboxVisible} - isCheckboxDisabled={this.props.isCheckboxDisabled} - onCheckboxClick={this._onCheckboxClick} - checkboxState={this.props.checkboxState} + label={label} + icon={this.props.imageLoader ? : undefined} + checkboxProps={checkboxProps} level={this.props.node.getParents().length} + renderOverrides={{ renderCheckbox: this.props.renderOverrides ? this.props.renderOverrides.renderCheckbox : undefined }} onClick={this.props.onClick} onMouseMove={this.props.onMouseMove} onMouseDown={this.props.onMouseDown} @@ -167,28 +124,40 @@ export class TreeNode extends React.Component { ); } - private _onCheckboxClick = () => { - if (this.props.onCheckboxClick) - this.props.onCheckboxClick(this.props.node); + private _onCheckboxClick = (newValue: CheckBoxState) => { + if (this.props.checkboxProps && this.props.checkboxProps.onClick) + this.props.checkboxProps.onClick(this.props.node, newValue); } } -function doPropsDiffer(props1: TreeNodeProps, props2: TreeNodeProps | null) { - return null === props2 - || shallowDiffers(props1.highlightProps, props2.highlightProps) +function doPropsDiffer(props1: TreeNodeProps, props2: TreeNodeProps) { + return shallowDiffers(props1.highlightProps, props2.highlightProps) + || shallowDiffers(props1.renderOverrides, props2.renderOverrides) || props1.valueRendererManager !== props2.valueRendererManager - || shallowDiffers(props1.cellEditorProps, props2.cellEditorProps); + || props1.cellEditing !== props2.cellEditing + || props1.showDescription !== props2.showDescription + || shallowDiffers(props1.checkboxProps, props2.checkboxProps) + || props1.imageLoader !== props2.imageLoader; } -function isPromise(value: any): value is Promise { - return !!(value && value.then && value.catch); +/** Properties for [[TreeNodeIcon]] React component */ +export interface TreeNodeIconProps extends React.Attributes { + node: BeInspireTreeNode; + imageLoader: ITreeImageLoader; } -function createLabel(props: TreeNodeProps) { - return props.renderLabel({ - node: props.node, - highlightProps: props.highlightProps, - cellEditorProps: props.cellEditorProps, - valueRendererManager: props.valueRendererManager, - }); -} +/** React component that renders tree node icons */ +export const TreeNodeIcon: React.FunctionComponent = ({ imageLoader, node }) => { // tslint:disable-line:variable-name + let image: Image | undefined; + + if (node.itree && node.itree.icon) + image = imageLoader.load(node.itree); + else if (node.payload && node.payload.icon) + image = imageLoader.load(node.payload); + + if (!image) + return null; + + const renderer = new ImageRenderer(); + return <>{renderer.render(image)}; +}; diff --git a/ui/components/src/ui-components/tree/component/NodeContent.scss b/ui/components/src/ui-components/tree/component/NodeContent.scss new file mode 100644 index 0000000..19e56ac --- /dev/null +++ b/ui/components/src/ui-components/tree/component/NodeContent.scss @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +@import "@bentley/ui-core/lib/ui-core/index"; + +.components-tree-node-description { + color: $uicore-gray; + font-size: $uicore-font-size-small; + margin-top: 4px; + + &.with-editor { + margin-top: 0; + } +} + +.components-tree-node-content { + display: inline-block; + + &.with-description { + padding: 5px 0; + } +} \ No newline at end of file diff --git a/ui/components/src/ui-components/tree/component/NodeContent.tsx b/ui/components/src/ui-components/tree/component/NodeContent.tsx new file mode 100644 index 0000000..404d689 --- /dev/null +++ b/ui/components/src/ui-components/tree/component/NodeContent.tsx @@ -0,0 +1,183 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +/** @module Tree */ + +import * as React from "react"; +import classnames from "classnames"; +import { PrimitiveValue, PropertyRecord, PropertyValueFormat, PropertyDescription } from "@bentley/imodeljs-frontend"; +import HighlightingEngine, { HighlightableTreeNodeProps } from "../HighlightingEngine"; +import { PropertyValueRendererManager, PropertyValueRendererContext, PropertyContainerType } from "../../properties/ValueRendererManager"; +import { BeInspireTreeNode } from "./BeInspireTree"; +import { TreeNodeItem } from "../TreeDataProvider"; +import { CellEditingEngine } from "../CellEditingEngine"; +import { TreeNodePlaceholder, shallowDiffers, isPromiseLike } from "@bentley/ui-core"; +import { UiComponents } from "../../UiComponents"; +import { ItemStyleProvider, ItemStyle } from "../../properties/ItemStyle"; + +import "./NodeContent.scss"; + +export interface TreeNodeContentProps { + node: BeInspireTreeNode; + showDescription?: boolean; + highlightProps?: HighlightableTreeNodeProps; + valueRendererManager: PropertyValueRendererManager; + cellEditing?: CellEditingEngine; + + /** + * Called when all of the component tasks are done. + * There are 3 different conditions: + * * Props and node did not update so there is no need to render. + * * Provided label function was synchronous and the render finished. + * * Provided label function was asynchronous and the second render finished. + */ + onFinalRenderComplete?: (renderId: string) => void; + /** + * Id specified by the parent component to identify all + * nodes rendered at one request + */ + renderId?: string; +} + +export interface TreeNodeContentState { + label: React.ReactNode; +} + +export class TreeNodeContent extends React.Component { + public readonly state: TreeNodeContentState = { + label: , + }; + + private _isMounted = false; + + private getStyle(style?: ItemStyle, isSelected?: boolean): React.CSSProperties { + return ItemStyleProvider.createStyle(style ? style : {}, isSelected); + } + + private nodeToPropertyRecord(node: BeInspireTreeNode) { + const value: PrimitiveValue = { + displayValue: node.text, + value: node.text, + valueFormat: PropertyValueFormat.Primitive, + }; + const property: PropertyDescription = { + displayLabel: UiComponents.i18n.translate("UiComponents:general.label"), + typename: node.payload && node.payload.typename ? node.payload.typename : "string", + name: "node_label", + }; + + return new PropertyRecord(value, property); + } + + private getLabel(props: TreeNodeContentProps) { + // handle filtered matches' highlighting + let labelElement: React.ReactNode = props.node.text; + if (props.highlightProps) + labelElement = HighlightingEngine.renderNodeLabel(props.node.text, props.highlightProps); + + // handle custom cell rendering + const context: PropertyValueRendererContext = { + containerType: PropertyContainerType.Tree, + decoratedTextElement: labelElement, + style: props.node.payload ? this.getStyle(props.node.payload.style, props.node.selected()) : undefined, + }; + + const nodeRecord = this.nodeToPropertyRecord(props.node); + return props.valueRendererManager.render(nodeRecord, context); + } + + private _onLabelStateChanged = () => { + if (this.props.renderId && this.props.onFinalRenderComplete) + this.props.onFinalRenderComplete(this.props.renderId); + } + + private async updateLabel(props: TreeNodeContentProps) { + const label = this.getLabel(props); + if (isPromiseLike(label)) { + const result = await label; + + if (this._isMounted) + this.setState({ label: result }, this._onLabelStateChanged); + } else { + this.setState({ label }, this._onLabelStateChanged); + } + } + + public componentDidMount() { + this._isMounted = true; + + this.updateLabel(this.props); // tslint:disable-line:no-floating-promises + } + + private doPropsDiffer(props1: TreeNodeContentProps, props2: TreeNodeContentProps) { + return shallowDiffers(props1.highlightProps, props2.highlightProps) + || props1.valueRendererManager !== props2.valueRendererManager + || props1.cellEditing !== props2.cellEditing + || props1.showDescription !== props2.showDescription; + } + + public componentDidUpdate(prevProps: TreeNodeContentProps) { + if (this.props.node.isDirty() || this.doPropsDiffer(prevProps, this.props)) { + this.props.node.setDirty(false); + + this.updateLabel(this.props); // tslint:disable-line:no-floating-promises + } + } + + public componentWillUnmount() { + this._isMounted = false; + } + + public shouldComponentUpdate(nextProps: TreeNodeContentProps, nextState: TreeNodeContentState) { + if (this.state.label !== nextState.label || nextProps.node.isDirty() || this.doPropsDiffer(this.props, nextProps)) + return true; + + if (nextState.label) { + // This is an anti-pattern, but it's main purpose is for testing. + // We need to know when all of the nodes have finished rendering + // and asynchronous updates make it very difficult. + // If it should not render, let the parent know that it's + // already fully rendered + if (nextProps.renderId && nextProps.onFinalRenderComplete) + nextProps.onFinalRenderComplete(nextProps.renderId); + } + + return false; + } + + public render() { + let editor: JSX.Element | undefined; + + // handle cell editing + if (this.props.cellEditing && this.props.cellEditing.isEditingEnabled(this.props.node)) { + // if cell editing is enabled, return editor instead of the label + const style = this.props.node.payload ? this.getStyle(this.props.node.payload.style, this.props.node.selected()) : undefined; + + editor = this.props.cellEditing.renderEditor(this.props.node, style); + } + + const isDescriptionEnabled = this.props.node.payload && this.props.node.payload.description && this.props.showDescription; + + const containerClassName = classnames( + "components-tree-node-content", + isDescriptionEnabled ? "with-description" : undefined, + ); + + const descriptionClassName = classnames( + "components-tree-node-description", + editor ? "with-editor" : undefined, + ); + + return ( +
    + {editor ? editor : this.state.label} + {isDescriptionEnabled ? +
    + {this.props.node.payload!.description} +
    + : undefined} +
    + ); + } +} diff --git a/ui/components/src/ui-components/tree/component/Tree.scss b/ui/components/src/ui-components/tree/component/Tree.scss index 11ec430..c8e2874 100644 --- a/ui/components/src/ui-components/tree/component/Tree.scss +++ b/ui/components/src/ui-components/tree/component/Tree.scss @@ -1,62 +1,62 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ -@import "@bentley/bwc/lib/mixins"; -@import "@bentley/ui-core/lib/ui-core/_scrollbar"; - -.ui-components-tree-loader { - @include bwc-loaders-large; - position: relative; - display: block; - left: calc(50% - 24px); - top: calc(50% - 24px); -} - -.ui-components-tree-errormessage { - padding-left: "10px" -} - -.ui-components-tree { - - .components-cell-editor { - border: $bwc-blue solid 1px; - background: #F9F9F9 !important; - box-sizing: border-box; - height: 100%; - width: 100%; - padding-top: 1px; - padding-bottom: 3px; - } - - /** - * Workaround for horizontal scroll issue. - * based on https://github.com/bvaughn/react-virtualized/issues/1248 - */ - .ReactVirtualized__Grid { - overflow: auto !important; - - @include uicore-scrollbar(); - - &:focus { - outline: none; - } - - > .ReactVirtualized__Grid__innerScrollContainer { - overflow: visible !important; - width: fit-content !important; - min-width: 100% !important; - max-width: none !important; - > .node-wrapper { - // The virtualized list wants to use absolute layout, but we - // need the inner container to grow in width based on the widest - // node. To workaround, we use relative layout with 0 heigh nodes. - position: relative !important; - height: 0 !important; - width: fit-content !important; - min-width: 100% !important; - overflow: visible !important; - } - } - } -} +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +@import "@bentley/ui-core/lib/ui-core/index"; + +.components-tree-loader { + position: relative; + display: block; + left: calc(50% - 24px); + top: calc(50% - 24px); +} + +.components-tree-errormessage { + padding-left: "10px" +} + +.components-tree { + color: $buic-foreground-body; + background-color: $buic-background-control; + /** + * Workaround for horizontal scroll issue. + * based on https://github.com/bvaughn/react-virtualized/issues/1248 + */ + .ReactVirtualized__Grid { + overflow: auto !important; + + @include uicore-scrollbar(); + + &:focus { + outline: none; + } + + > .ReactVirtualized__Grid__innerScrollContainer { + overflow: visible !important; + width: fit-content !important; + min-width: 100% !important; + max-width: none !important; + > .node-wrapper { + // The virtualized list wants to use absolute layout, but we + // need the inner container to grow in width based on the widest + // node. To workaround, we use relative layout with 0 heigh nodes. + position: relative !important; + height: 0 !important; + width: fit-content !important; + min-width: 100% !important; + overflow: visible !important; + } + } + } + + .components-cell-editor { + border: $buic-foreground-primary solid 1px; + background: $buic-background-control !important; + box-sizing: border-box; + height: 100%; + width: 100%; + padding-top: 1px; + padding-bottom: 3px; + } + +} diff --git a/ui/components/src/ui-components/tree/component/Tree.tsx b/ui/components/src/ui-components/tree/component/Tree.tsx index 7ced8bf..2fe5f21 100644 --- a/ui/components/src/ui-components/tree/component/Tree.tsx +++ b/ui/components/src/ui-components/tree/component/Tree.tsx @@ -10,7 +10,11 @@ import * as React from "react"; import { AutoSizer, Size, List as VirtualizedList, ListRowProps as VirtualizedListRowProps } from "react-virtualized"; // bentley imports import { using, Guid } from "@bentley/bentleyjs-core"; -import { Tree as TreeBase, TreeNodePlaceholder, shallowDiffers, CheckBoxState } from "@bentley/ui-core"; +import { + Tree as TreeBase, TreeNodePlaceholder, shallowDiffers, + CheckBoxState, CheckBoxInfo, NodeCheckboxRenderer, + Spinner, SpinnerSize, +} from "@bentley/ui-core"; // tree-related imports import { BeInspireTree, BeInspireTreeNode, BeInspireTreeNodes, BeInspireTreeNodeConfig, @@ -21,23 +25,21 @@ import { DelayLoadedTreeNodeItem, ImmediatelyLoadedTreeNodeItem, isTreeDataProviderInterface, } from "../TreeDataProvider"; -import { TreeNodeProps, TreeNode, TreeNodeCellEditorProps } from "./Node"; -import { PropertyValueRendererManager, PropertyValueRendererContext, PropertyContainerType } from "../../properties/ValueRendererManager"; +import { TreeNodeProps, TreeNode } from "./Node"; +import { PropertyValueRendererManager } from "../../properties/ValueRendererManager"; // selection-related imports import { SelectionMode } from "../../common/selection/SelectionModes"; import { SelectionHandler, SingleSelectionHandler, MultiSelectionHandler, OnItemsSelectedCallback, OnItemsDeselectedCallback, } from "../../common/selection/SelectionHandler"; -// cell editing imports -import { EditorContainer, PropertyUpdatedArgs } from "../../editors/EditorContainer"; -import { PropertyRecord } from "../../properties/Record"; -import { PropertyValueFormat, PrimitiveValue } from "../../properties/Value"; -import { PropertyDescription } from "../../properties/Description"; // node highlighting -import HighlightingEngine, { HighlightableTreeProps, HighlightableTreeNodeProps } from "../HighlightingEngine"; +import HighlightingEngine, { HighlightableTreeProps } from "../HighlightingEngine"; // misc import UiComponents from "../../UiComponents"; +import { CellEditingEngine, EditableTreeProps } from "../CellEditingEngine"; +import { ITreeImageLoader, TreeImageLoader } from "../ImageLoader"; + // css import "./Tree.scss"; @@ -135,23 +137,52 @@ export interface TreeProps { */ onChildrenLoaded?: (parent: TreeNodeItem, children: TreeNodeItem[]) => void; - renderNode?: NodeRenderer; + /** Contains render overrides for different pieces of the tree component */ + renderOverrides?: { + /** Callback to render a node */ + renderNode?: NodeRenderer; + /** Callback to render a node checkbox. When a custom node renderer is used, it's responsible for calling this callback. */ + renderCheckbox?: NodeCheckboxRenderer; + }; + /** @hidden */ onRender?: () => void; /** @hidden */ onNodesRender?: () => void; + /** Properties for node highlighting logic. If not provided, node highlighting is disabled. */ nodeHighlightingProps?: HighlightableTreeProps; - onCellEditing?: (cellEditorState: TreeCellEditorState) => void; - onCellUpdated?: (args: TreeCellUpdatedArgs) => Promise; - /** @hidden */ - ignoreEditorBlur?: boolean; + /** Properties for cell editing logic. If not provided, cell editing is disabled. */ + cellEditing?: EditableTreeProps; - onCheckboxClick?: (node: TreeNodeItem) => void; + /** + * Describes nodes that should be checked. May be defined as: + * - an array of node ids + * - a callback that takes a node and returns a boolean + * + * **Note:** when set, this property overrides checkbox-related TreeNodeItem attributes + */ + checkboxInfo?: (node: TreeNodeItem) => CheckBoxInfo | Promise; + /** + * A callback that gets fired when checkbox state changes + */ + onCheckboxClick?: (node: TreeNodeItem, newState: CheckBoxState) => void; /** Custom property value renderer manager */ propertyValueRendererManager?: PropertyValueRendererManager; + + /** Turns on node description rendering when enabled */ + showDescriptions?: boolean; + + /** Turns on icon rendering when enabled */ + showIcons?: boolean; + + /** Custom image loader. Default ImageLoader loads icons already bundled in the library */ + imageLoader?: ITreeImageLoader; + + /** A constant value for row height, or a function that calculates row height based on rendered node */ + rowHeight?: ((node?: TreeNodeItem, index?: number) => number) | number; } /** State for the [[Tree]] component */ @@ -160,13 +191,15 @@ export interface TreeState { dataProvider: TreeDataProvider; modelReady: boolean; selectedNodes?: string[] | ((node: TreeNodeItem) => boolean); + checkboxInfo?: (node: TreeNodeItem) => CheckBoxInfo | Promise; nodeHighlightingProps?: HighlightableTreeProps; + cellEditing?: EditableTreeProps; }; model: BeInspireTree; modelReady: boolean; - cellEditorState: TreeCellEditorState; + currentlyEditedNode?: BeInspireTreeNode; pendingSelectionChange?: { range: [string, string]; @@ -180,28 +213,9 @@ export interface TreeState { /** @hidden */ highlightingEngine?: HighlightingEngine; -} - -/** Tree Cell Editor state */ -export interface TreeCellEditorState { - active: boolean; - node?: BeInspireTreeNode; -} -/** Arguments for the Tree Cell Updated event callback */ -export interface TreeCellUpdatedArgs { - /** The cell being updated. */ - node: BeInspireTreeNode; - /** The new value for the cell. */ - newValue: any; -} - -/** Params to the TreeNodeProps.renderLabel method */ -export interface RenderNodeLabelProps { - node: BeInspireTreeNode; - highlightProps?: HighlightableTreeNodeProps; - cellEditorProps?: TreeNodeCellEditorProps; - valueRendererManager?: PropertyValueRendererManager; + /** @hidden */ + cellEditingEngine?: CellEditingEngine; } /** @@ -220,6 +234,7 @@ export class Tree extends React.Component { private _nodesRenderInfo?: { total: number, rendered: number, renderId: string }; /** Used to delay the automatic scrolling when stepping through highlighted text */ private _shouldScrollToActiveNode = false; + private _defaultImageLoader = new TreeImageLoader(); public static readonly defaultProps: Partial = { selectionMode: SelectionMode.Single, @@ -236,14 +251,17 @@ export class Tree extends React.Component { prev: { dataProvider: props.dataProvider, selectedNodes: props.selectedNodes, + checkboxInfo: props.checkboxInfo, modelReady: false, }, model: Tree.createModel(props), modelReady: false, - cellEditorState: { active: false }, }; } + private _setCellEditorState = (state?: BeInspireTreeNode) => this.setState({ currentlyEditedNode: state }); + private _getCellEditorState = () => this.state.currentlyEditedNode; + private static createModel(props: TreeProps) { return new BeInspireTree({ dataProvider: props.dataProvider, @@ -256,6 +274,7 @@ export class Tree extends React.Component { public static getDerivedStateFromProps(props: TreeProps, state: TreeState): TreeState | null { const providerChanged = (props.dataProvider !== state.prev.dataProvider); const selectedNodesChanged = (props.selectedNodes !== state.prev.selectedNodes); + const checkboxInfoChanged = (props.checkboxInfo !== state.prev.checkboxInfo); const modelReadyChanged = (state.modelReady !== state.prev.modelReady); // create derived state that just updates `prev` values @@ -266,7 +285,9 @@ export class Tree extends React.Component { dataProvider: props.dataProvider, modelReady: state.modelReady, selectedNodes: props.selectedNodes, + checkboxInfo: props.checkboxInfo, nodeHighlightingProps: props.nodeHighlightingProps, + cellEditing: props.cellEditing, }, }; @@ -275,19 +296,35 @@ export class Tree extends React.Component { derivedState.highlightingEngine = props.nodeHighlightingProps ? new HighlightingEngine(props.nodeHighlightingProps) : undefined; } + // update cell editing engine if related props changed + if (shallowDiffers(props.cellEditing, state.prev.cellEditing)) { + derivedState.cellEditingEngine = props.cellEditing ? new CellEditingEngine(props.cellEditing) : undefined; + } + // in case provider changed, have to re-create `model` and reset `modelReady` if (providerChanged) { derivedState.model = Tree.createModel(props); derivedState.modelReady = false; - } - - // update tree selection if either selected nodes changed or model became ready - if (!providerChanged && (selectedNodesChanged || (modelReadyChanged && state.modelReady))) { - using((state.model.mute([BeInspireTreeEvent.ChangesApplied])), () => { - // note: calling this may actually mutate `model` - // in state, but that should be fine - state.model.updateTreeSelection(props.selectedNodes); - }); + } else { + const modelBecameReady = (modelReadyChanged && state.modelReady); + if (modelBecameReady || selectedNodesChanged) { + // note: not using `pauseRendering()` here to avoid firing `ChangesApplied` + // when the EventsMuteContext is disposed - the component is going to be + // rendered anyway if we got here + using((state.model.mute([BeInspireTreeEvent.ChangesApplied])), (_r) => { + // note: calling this may mutate `model` in state + state.model.updateTreeSelection(props.selectedNodes); + }); + } + if ((modelBecameReady || checkboxInfoChanged) && props.checkboxInfo) { + // note: using `pauseRendering()` here - need it to fire `ChangesApplied` + // event after checkboxes are asynchronously updated + // tslint:disable-next-line: no-floating-promises + using((state.model.pauseRendering()), async (_r) => { + // note: calling this may actually mutate `model` in state + await state.model.updateTreeCheckboxes(props.checkboxInfo!); + }); + } } return derivedState; @@ -298,6 +335,9 @@ export class Tree extends React.Component { this.assignModelListeners(this.state.model); this.assignDataProviderListeners(this.props.dataProvider); + if (this.state.cellEditingEngine && !this.state.cellEditingEngine.hasSubscriptions) + this.state.cellEditingEngine.subscribe(this._getCellEditorState, this._setCellEditorState); + /* istanbul ignore next */ if (this.props.onRender) this.props.onRender(); @@ -306,6 +346,8 @@ export class Tree extends React.Component { public componentWillUnmount() { this.dropModelListeners(this.state.model); this.dropDataProviderListeners(this.props.dataProvider); + if (this.state.cellEditingEngine) + this.state.cellEditingEngine.unsubscribe(); this._mounted = false; } @@ -322,10 +364,11 @@ export class Tree extends React.Component { // otherwise, render when any of the following props / state change return this.props.selectedNodes !== nextProps.selectedNodes - || this.props.renderNode !== nextProps.renderNode + || this.props.checkboxInfo !== nextProps.checkboxInfo + || shallowDiffers(this.props.renderOverrides, nextProps.renderOverrides) || this.props.dataProvider !== nextProps.dataProvider || this.props.nodeHighlightingProps !== nextProps.nodeHighlightingProps - || this.state.cellEditorState !== nextState.cellEditorState + || this.state.currentlyEditedNode !== nextState.currentlyEditedNode || this.state.model.visible().some((n) => n.isDirty()); } @@ -353,6 +396,10 @@ export class Tree extends React.Component { this.assignDataProviderListeners(this.props.dataProvider); } + if (this.state.cellEditingEngine && !this.state.cellEditingEngine.hasSubscriptions) { + this.state.cellEditingEngine.subscribe(this._getCellEditorState, this._setCellEditorState); + } + /* istanbul ignore next */ if (this.props.onRender) this.props.onRender(); @@ -510,7 +557,11 @@ export class Tree extends React.Component { // children immediately, then node has children here. if data provider // delay loads children, then `node.getChildren()` returns empty array and // the `_onChildrenLoaded` callback is called where we do the same thing as here - this.state.model.updateNodesSelection(node.getChildren(), this.props.selectedNodes); + this.state.model.updateNodesSelection(toNodes(node.getChildren()), this.props.selectedNodes); + if (this.props.checkboxInfo) { + // tslint:disable-next-line: no-floating-promises + this.state.model.updateNodesCheckboxes(toNodes(node.getChildren()), this.props.checkboxInfo); + } this._nodesSelectionHandlers = undefined; } @@ -543,6 +594,11 @@ export class Tree extends React.Component { // continue loading selection this.state.pendingSelectionChange.continue(); } + + if (this.props.checkboxInfo) { + // tslint:disable-next-line: no-floating-promises + this.state.model.updateNodesCheckboxes(nodes, this.props.checkboxInfo); + } } private _onModelLoaded = (rootNodes: BeInspireTreeNodes) => { @@ -579,7 +635,7 @@ export class Tree extends React.Component { const node = this.state.model.node(item.id); if (node) { const wasExpanded = node.expanded(); - node.assign(Tree.inspireNodeFromTreeNodeItem(item, Tree.inspireNodeFromTreeNodeItem.bind(this), node)); + node.assign(Tree.inspireNodeFromTreeNodeItem(item, Tree.inspireNodeFromTreeNodeItem, node)); if (wasExpanded) await node.loadChildren(); } @@ -611,6 +667,13 @@ export class Tree extends React.Component { return this._nodesSelectionHandlers; } + private _getNodeHeight = (node?: TreeNodeItem) => { + if (this.props.showDescriptions && node && node.description) + return 44; + + return 24; + } + /** map TreeNodeItem into an InspireNode */ public static inspireNodeFromTreeNodeItem(item: TreeNodeItem, remapper: MapPayloadToInspireNodeCallback, base?: BeInspireTreeNodeConfig): BeInspireTreeNodeConfig { base = base || { text: "" }; @@ -625,12 +688,34 @@ export class Tree extends React.Component { }, }, }; - if (item.checkBoxState === CheckBoxState.On) + + if (item.isCheckboxVisible) + node.itree!.state!.checkboxVisible = true; + else + delete node.itree!.state!.checkboxVisible; + + if (item.isCheckboxDisabled) + node.itree!.state!.checkboxDisabled = true; + else + delete node.itree!.state!.checkboxDisabled; + + if (item.checkBoxState === CheckBoxState.Partial) { + node.itree!.state!.indeterminate = true; + delete node.itree!.state!.checked; + } else if (item.checkBoxState === CheckBoxState.On) { node.itree!.state!.checked = true; + delete node.itree!.state!.indeterminate; + } else { + delete node.itree!.state!.checked; + delete node.itree!.state!.indeterminate; + } + if (item.icon) node.itree!.icon = item.icon; + if (item.autoExpand) node.itree!.state!.collapsed = false; + if ((item as DelayLoadedTreeNodeItem).hasChildren) node.children = true; else if ((item as ImmediatelyLoadedTreeNodeItem).children) @@ -655,7 +740,8 @@ export class Tree extends React.Component { deselectAll: () => { this.state.model.deselectAll(); if (!this._pressedItemSelected) { - this._deactivateCellEditor(); + if (this.state.cellEditingEngine) + this.state.cellEditingEngine.deactivateEditor(); this.forceUpdate(); } }, @@ -666,117 +752,13 @@ export class Tree extends React.Component { areEqual: (item1: BeInspireTreeNode, item2: BeInspireTreeNode) => item1 === item2, }; - private _checkCellEditorStatus = (node: BeInspireTreeNode): void => { - let activate = false; - - const isSelected = node.selected(); - const nodeItem = node.payload; - if (nodeItem && isSelected && this._pressedItemSelected && nodeItem.isEditable) - activate = true; - - if (activate) - this._activateCellEditor(node); - else - this._deactivateCellEditor(); - } - - private _activateCellEditor = (node: BeInspireTreeNode): void => { - const cellEditorState: TreeCellEditorState = { active: true, node }; - if (cellEditorState !== this.state.cellEditorState) { - this.setState( - { cellEditorState }, - () => { - if (this.props.onCellEditing) - this.props.onCellEditing(cellEditorState); - }, - ); - } - } - - private _deactivateCellEditor = (): void => { - if (this.state.cellEditorState.active) { - if (this.state.cellEditorState.node) - this.state.cellEditorState.node.setDirty(true); - const cellEditorState: TreeCellEditorState = { active: false, node: undefined }; - this.setState({ cellEditorState }); - } - } - - private _onCellEditCommit = async (args: PropertyUpdatedArgs) => { - if (this.props.onCellUpdated && this.state.cellEditorState.node) { - const cellUpdatedArgs: TreeCellUpdatedArgs = { - node: this.state.cellEditorState.node, - newValue: args.newValue, - }; - const allowed = await this.props.onCellUpdated(cellUpdatedArgs); - if (allowed) - this.state.cellEditorState.node.setDirty(true); - } - this._deactivateCellEditor(); - } - - private nodeToPropertyRecord(node: BeInspireTreeNode) { - const value: PrimitiveValue = { - displayValue: node.text, - value: node.text, - valueFormat: PropertyValueFormat.Primitive, - }; - const property: PropertyDescription = { - displayLabel: UiComponents.i18n.translate("UiComponents:general.label"), - typename: node.payload && node.payload.typename ? node.payload.typename : "string", - name: "node_label", - }; - - return new PropertyRecord(value, property); - } - - // tslint:disable-next-line:naming-convention - private renderLabelComponent = ({ node, highlightProps, cellEditorProps, valueRendererManager }: RenderNodeLabelProps): React.ReactNode | Promise => { - const labelStyle: React.CSSProperties = { - color: node.payload!.labelForeColor ? node.payload!.labelForeColor!.toString(16) : undefined, - backgroundColor: node.payload!.labelBackColor ? node.payload!.labelBackColor!.toString(16) : undefined, - fontWeight: node.payload!.labelBold ? "bold" : undefined, - fontStyle: node.payload!.labelItalic ? "italic" : undefined, - }; - - // handle cell editing - if (cellEditorProps) { - if (cellEditorProps.cellEditorState.active && node === cellEditorProps.cellEditorState.node) { - // if cell editing is enabled, return editor instead of the label - const record = new CellEditorPropertyRecord(node.text); - return ( - - - - ); - } - } - - // handle filtered matches' highlighting - let labelElement: React.ReactNode = node.text; - if (highlightProps) - labelElement = HighlightingEngine.renderNodeLabel(node.text, highlightProps); - - // handle custom cell rendering - const context: PropertyValueRendererContext = { - containerType: PropertyContainerType.Tree, - decoratedTextElement: labelElement, - style: labelStyle, - }; - const nodeRecord = this.nodeToPropertyRecord(node); - if (!valueRendererManager) - valueRendererManager = PropertyValueRendererManager.defaultManager; - return valueRendererManager.render(nodeRecord, context); - } - - private _onCheckboxClick = (node: BeInspireTreeNode) => { + private _onCheckboxClick = (node: BeInspireTreeNode, newState: CheckBoxState) => { if (this.props.onCheckboxClick) - this.props.onCheckboxClick(node.payload!); + this.props.onCheckboxClick(node.payload!, newState); } // tslint:disable-next-line:naming-convention - private renderNode = (node: BeInspireTreeNode, props: TreeNodeProps): React.ReactNode => { + private defaultRenderNode = (node: BeInspireTreeNode, props: TreeNodeProps): React.ReactNode => { return ( { this._nodesRenderInfo = undefined; } - private _createTreeNodeProps = (node: BeInspireTreeNode, props: TreeProps, state: TreeState): TreeNodeProps => { + private createTreeNodeProps(node: BeInspireTreeNode): TreeNodeProps { const onNodeSelectionChanged = this._selectionHandler.createSelectionFunction(this._multiSelectionHandler, this._createItemSelectionHandler(node)); return { node, - highlightProps: state.highlightingEngine ? state.highlightingEngine.createRenderProps(node) : undefined, - isCheckboxVisible: node.payload!.isCheckboxVisible, - isCheckboxDisabled: node.payload!.isCheckboxDisabled, - checkboxState: node.payload!.checkBoxState, - onCheckboxClick: this._onCheckboxClick, - renderLabel: this.renderLabelComponent, + checkboxProps: node.itree!.state!.checkboxVisible ? { + isDisabled: node.itree!.state!.checkboxDisabled, + state: node.itree!.state!.checked ? CheckBoxState.On : CheckBoxState.Off, + onClick: this._onCheckboxClick, + } : undefined, + cellEditing: this.state.cellEditingEngine, + showDescription: this.props.showDescriptions, + renderOverrides: { + renderCheckbox: this.props.renderOverrides ? this.props.renderOverrides.renderCheckbox : undefined, + }, + renderId: this._nodesRenderInfo ? this._nodesRenderInfo.renderId : undefined, + onFinalRenderComplete: this._onNodeFullyRendered, + highlightProps: this.state.highlightingEngine + ? this.state.highlightingEngine.createRenderProps(node) + : undefined, + valueRendererManager: this.props.propertyValueRendererManager + ? this.props.propertyValueRendererManager + : PropertyValueRendererManager.defaultManager, + imageLoader: this.props.showIcons ? (this.props.imageLoader ? this.props.imageLoader : this._defaultImageLoader) : undefined, onClick: (e: React.MouseEvent) => { onNodeSelectionChanged(e.shiftKey, e.ctrlKey); - this._checkCellEditorStatus(node); + if (this.state.cellEditingEngine) + this.state.cellEditingEngine.checkStatus(node, this._pressedItemSelected); }, onMouseDown: () => this._selectionHandler.createDragAction(this._multiSelectionHandler, [this.nodesSelectionHandlers], node), onMouseMove: (e: React.MouseEvent) => { if (e.buttons === 1) this._selectionHandler.updateDragAction(node); }, - cellEditorProps: { - cellEditorState: state.cellEditorState, - onCellEditCommit: this._onCellEditCommit, - onCellEditCancel: this._deactivateCellEditor, - ignoreEditorBlur: props.ignoreEditorBlur, - }, - valueRendererManager: props.propertyValueRendererManager, - onFinalRenderComplete: this._onNodeFullyRendered, - renderId: this._nodesRenderInfo!.renderId, + }; } public render() { if (!this.state.modelReady) { return ( -
    - +
    +
    ); } @@ -844,7 +832,7 @@ export class Tree extends React.Component { const nodes = this.state.model.visible(); if (nodes.length === 0) { return ( -

    +

    {this.props.nodeHighlightingProps ? UiComponents.i18n.translate("UiComponents:tree.noResultsForFilter", { searchText: this.props.nodeHighlightingProps.searchText }) : UiComponents.i18n.translate("UiComponents:general.noData")} @@ -852,6 +840,11 @@ export class Tree extends React.Component { ); } + // This is a hack (since it doesn't have a proper public method) to force Virtualized List to clear cell cache. + // There is recomputeRowHeights, but it forces an update + if (this._scrollableContainerRef.current && this._scrollableContainerRef.current.Grid) + (this._scrollableContainerRef.current.Grid as any).state.instanceProps.rowSizeAndPositionManager.resetCell(0); + const getNodesRenderInfo = () => { if (!this._nodesRenderInfo) this._nodesRenderInfo = { total: 0, rendered: 0, renderId: Guid.createValue() }; @@ -859,9 +852,10 @@ export class Tree extends React.Component { }; this._nodesRenderInfo = undefined; - const baseRenderNode = this.props.renderNode ? this.props.renderNode : this.renderNode; - const renderNode = ({ index, key, style, isScrolling }: VirtualizedListRowProps) => { + const baseRenderNode = this.props.renderOverrides && this.props.renderOverrides.renderNode ? this.props.renderOverrides.renderNode : this.defaultRenderNode; + const renderNode = ({ index, style, isScrolling }: VirtualizedListRowProps) => { const node = nodes[index]; + const key = node.id ? node.id : node.text; if (!node.payload) { if (!isScrolling) { // tslint:disable-next-line:no-floating-promises @@ -876,7 +870,7 @@ export class Tree extends React.Component { getNodesRenderInfo().total++; - const props = this._createTreeNodeProps(node, this.props, this.state); + const props = this.createTreeNodeProps(node); return (

    {baseRenderNode(node, props)} @@ -884,8 +878,17 @@ export class Tree extends React.Component { ); }; + const getRowHeight = ({ index }: { index: number }) => { + if (this.props.rowHeight && typeof (this.props.rowHeight) === "number") + return this.props.rowHeight; + + const getHeight = this.props.rowHeight && typeof (this.props.rowHeight) === "function" ? this.props.rowHeight : this._getNodeHeight; + + return getHeight(nodes[index].payload, index); + }; + return ( - + {({ width, height }: Size) => ( { width={width} height={height} rowCount={nodes.length} overscanRowCount={10} - rowHeight={24} + rowHeight={getRowHeight} rowRenderer={renderNode} autoContainerWidth={false} /> @@ -911,6 +914,7 @@ export namespace Tree { Node = "tree-node", NodeContents = "tree-node-contents", NodeExpansionToggle = "tree-node-expansion-toggle", + NodeCheckbox = "tree-node-checkbox", } } @@ -928,26 +932,3 @@ class PlaceholderNode extends React.Component<{ node: BeInspireTreeNode; } } - -/** PropertyRecord for cell editing */ -class CellEditorPropertyRecord extends PropertyRecord { - constructor(value: any, typename: string = "string", editor?: string) { - const name = "cell-editor"; - const v: PrimitiveValue = { - valueFormat: PropertyValueFormat.Primitive, - value, - displayValue: value.toString(), - }; - const p: PropertyDescription = { - name, - displayLabel: "Cell Editor", - typename, - }; - if (editor) - p.editor = { name: editor, params: [] }; - super(v, p); - - this.description = ""; - this.isReadonly = false; - } -} diff --git a/ui/components/src/ui-components/tree/hocs/DragDropTreeNode.scss b/ui/components/src/ui-components/tree/hocs/DragDropTreeNode.scss index a83b98e..8b8cc5a 100644 --- a/ui/components/src/ui-components/tree/hocs/DragDropTreeNode.scss +++ b/ui/components/src/ui-components/tree/hocs/DragDropTreeNode.scss @@ -1,26 +1,25 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ - -@import "@bentley/bwc/lib/mixins"; - -.node-drop-target { - &.above { - border-top: solid black 1px; - } - - &.on { - background-color: $bwc-blue-8; - } - - &.below { - border-bottom: solid black 1px; - } - - &.dragging { - .contents { - color: $bwc-gray; - } - } -} +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +@import "@bentley/ui-core/lib/ui-core/index"; + +.node-drop-target { + &.above { + border-top: solid black 1px; + } + + &.on { + background-color: $uicore-blue-8; + } + + &.below { + border-bottom: solid black 1px; + } + + &.dragging { + .contents { + color: $uicore-gray; + } + } +} diff --git a/ui/components/src/ui-components/tree/hocs/withDragDrop.tsx b/ui/components/src/ui-components/tree/hocs/withDragDrop.tsx index 12d778b..6371676 100644 --- a/ui/components/src/ui-components/tree/hocs/withDragDrop.tsx +++ b/ui/components/src/ui-components/tree/hocs/withDragDrop.tsx @@ -49,7 +49,6 @@ export function withTreeDragDrop

    = { onDropTargetOver: (args: DropTargetArguments) => { - // istanbul ignore else if (onDropTargetOver) { args.dropLocation = this.props.dataProvider as DragDropObject; onDropTargetOver(args); @@ -81,14 +80,12 @@ export function withTreeDragDrop

    ) => { - // istanbul ignore else if (onDragSourceEnd) { args.parentObject = ((parent && parent.payload) || this.props.dataProvider) as DragDropObject; onDragSourceEnd(args); } }, objectType: () => { - // istanbul ignore else if (objectType) { if (typeof objectType === "function") return objectType(item.payload!.extendedData as DragDropObject); @@ -111,7 +108,6 @@ export function withTreeDragDrop

    = { onDropTargetOver: (args: DropTargetArguments) => { - // istanbul ignore else if (onDropTargetOver) { // populate tree information while it's accessible args.dropLocation = item.payload as DragDropObject; @@ -156,7 +152,7 @@ export function withTreeDragDrop

    , props: TreeNodeProps): React.ReactNode => { - const baseNode = this.props.renderNode ? /* istanbul ignore next */ this.props.renderNode(item, props) : ; + const baseNode = this.props.renderOverrides && this.props.renderOverrides.renderNode ? this.props.renderOverrides.renderNode(item, props) : ; const DDTreeNode = DragDropTreeNode(); // tslint:disable-line:variable-name return ( { ViewportComponentEvents.setViewMatrix(vp); if (this._viewClassFullName !== vp.view.classFullName) { - setImmediate(() => { + setTimeout(() => { ViewportComponentEvents.onViewClassFullNameChangedEvent.emit({ viewport: vp, oldName: this._viewClassFullName, newName: vp.view.classFullName }); this._viewClassFullName = vp.view.classFullName; }); } if (this._viewId !== vp.view.id) { - setImmediate(() => { + setTimeout(() => { ViewportComponentEvents.onViewIdChangedEvent.emit({ viewport: vp, oldId: this._viewId, newId: vp.view.id }); this._viewId = vp.view.id; }); diff --git a/ui/components/src/ui-components/viewport/ViewportComponentEvents.ts b/ui/components/src/ui-components/viewport/ViewportComponentEvents.ts index 3f1a9ba..bfaac0a 100644 --- a/ui/components/src/ui-components/viewport/ViewportComponentEvents.ts +++ b/ui/components/src/ui-components/viewport/ViewportComponentEvents.ts @@ -79,20 +79,17 @@ export class ViewportComponentEvents { } public static setCubeMatrix(rotMatrix: Matrix3d, animationTime?: number): void { - setImmediate(() => { - this.rMatrix.setFrom(rotMatrix); - this.onCubeRotationChangeEvent.emit({ rotMatrix, animationTime }); - }); + this.rMatrix.setFrom(rotMatrix); + this.onCubeRotationChangeEvent.emit({ rotMatrix, animationTime }); } public static setStandardRotation(standardRotation: StandardViewId): void { - setImmediate(() => { - this.onStandardRotationChangeEvent.emit({ standardRotation }); - }); + this.onStandardRotationChangeEvent.emit({ standardRotation }); } public static setViewMatrix(viewport: Viewport, animationTime?: number): void { - setImmediate(() => { + // When handling onViewChanged, use setTimeout + setTimeout(() => { this.rMatrix.setFrom(viewport.rotation); this.onViewRotationChangeEvent.emit({ viewport, animationTime }); }); diff --git a/ui/components/tmp.out b/ui/components/tmp.out deleted file mode 100644 index fd0ddc8..0000000 --- a/ui/components/tmp.out +++ /dev/null @@ -1,20 +0,0 @@ - -> @bentley/ui-components@0.168.0 build D:\bentleyjs\imodeljs\ui\components -> tsc 1>&2 && npm run build:scss && npm run copy:public && npm run webpackModule-dev - - -> @bentley/ui-components@0.168.0 build:scss D:\bentleyjs\imodeljs\ui\components -> cpx "./src/**/*.scss" ./lib - - -> @bentley/ui-components@0.168.0 copy:public D:\bentleyjs\imodeljs\ui\components -> cpx "./public/**/*" ./lib/public && npm run pseudolocalize - - -> @bentley/ui-components@0.168.0 pseudolocalize D:\bentleyjs\imodeljs\ui\components -> node ./node_modules/@bentley/build-tools/scripts/pseudolocalize.js --englishDir ./public/locales/en --out ./lib/public/locales/en-pseudo - - -> @bentley/ui-components@0.168.0 webpackModule-dev D:\bentleyjs\imodeljs\ui\components -> make-dir ./lib/module/dev/ && webpack --config ./node_modules/@bentley/webpack-tools/modules/webpackModule.config.js --env.entry=./lib/ui-components.js --env.bundlename=ui-components --env.stylesheets --json >./lib/module/dev/webpackStats.json - diff --git a/ui/core/.npmignore b/ui/core/.npmignore index 3f8d657..a4ed373 100644 --- a/ui/core/.npmignore +++ b/ui/core/.npmignore @@ -2,7 +2,7 @@ * !lib/**/*.d.ts !lib/**/*.js -!lib/**/*.scss +!lib/**/*.*css !lib/public/**/* lib/test !*.md diff --git a/ui/core/CHANGELOG.json b/ui/core/CHANGELOG.json index fd21215..c07caaf 100644 --- a/ui/core/CHANGELOG.json +++ b/ui/core/CHANGELOG.json @@ -1,6 +1,117 @@ { "name": "@bentley/ui-core", "entries": [ + { + "version": "0.189.0", + "tag": "@bentley/ui-core_v0.189.0", + "date": "Wed, 06 Mar 2019 15:41:22 GMT", + "comments": { + "none": [ + { + "comment": "Renamed CSS files to SCSS" + }, + { + "comment": "UI documentation fixes" + }, + { + "comment": "Added ToggleEditor. Support for defaultTool in Frontstage. Fixed BooleanEditor sizing." + }, + { + "comment": "Added testing props to SplitButton" + }, + { + "comment": "Added tilde-prefixed hotkey support to ContextMenu" + }, + { + "comment": "Use new buildIModelJsBuild script" + }, + { + "comment": "Changed Checkbox container from

    `; diff --git a/ui/core/src/test/base/WaitSpinner.test.snap b/ui/core/src/test/base/WaitSpinner.test.snap deleted file mode 100644 index 8000603..0000000 --- a/ui/core/src/test/base/WaitSpinner.test.snap +++ /dev/null @@ -1,18 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`WaitSpinner renders correctly 1`] = ` -
    -
    - - - - - - -
    -
    -`; diff --git a/ui/core/src/test/base/WebFontIcon.test.snap b/ui/core/src/test/base/WebFontIcon.test.snap deleted file mode 100644 index 4123c8f..0000000 --- a/ui/core/src/test/base/WebFontIcon.test.snap +++ /dev/null @@ -1,7 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`WebFontIcon renders correctly 1`] = ` - -`; diff --git a/ui/core/src/test/button/Button.test.snap b/ui/core/src/test/button/Button.test.snap new file mode 100644 index 0000000..713ae2b --- /dev/null +++ b/ui/core/src/test/button/Button.test.snap @@ -0,0 +1,59 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` +`; + +exports[` +`; diff --git a/ui/core/src/test/button/Button.test.tsx b/ui/core/src/test/button/Button.test.tsx new file mode 100644 index 0000000..f04c8db --- /dev/null +++ b/ui/core/src/test/button/Button.test.tsx @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved. +* Licensed under the MIT License. See LICENSE.md in the project root for license terms. +*--------------------------------------------------------------------------------------------*/ +import { mount, shallow } from "enzyme"; +import * as React from "react"; +import { Button, ButtonType, ButtonSize } from "../../ui-core/button/Button"; + +describe(").should.matchSnapshot(); + }); + + it("disabled renders correctly", () => { + shallow().should.matchSnapshot(); + }); + + it("should render blue", () => { + shallow(