@itwin/core-frontend 3.6.0 → 3.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,183 +1,210 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GeoServices = exports.GeoConverter = void 0;
3
+ exports.GeoServices = exports.GeoConverter = exports.CoordinateConverter = void 0;
4
+ /*---------------------------------------------------------------------------------------------
5
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
6
+ * See LICENSE.md in the project root for license terms and full copyright notice.
7
+ *--------------------------------------------------------------------------------------------*/
8
+ // cspell:ignore GCRS
9
+ const core_bentley_1 = require("@itwin/core-bentley");
4
10
  const core_common_1 = require("@itwin/core-common");
5
- // this class is used to cache results from conversion of geoCoordinates to IModelCoordinates.
6
- class GCtoIMCResultCache {
7
- constructor(iModel, source) {
8
- this._iModel = iModel;
9
- this._cache = {};
10
- this._source = source;
11
+ const FrontendLoggerCategory_1 = require("./FrontendLoggerCategory");
12
+ function compareXYAndZ(lhs, rhs) {
13
+ return lhs.x - rhs.x || lhs.y - rhs.y || lhs.z - rhs.z;
14
+ }
15
+ function cloneXYAndZ(xyz) {
16
+ return { x: xyz.x, y: xyz.y, z: xyz.z };
17
+ }
18
+ /** Performs conversion of coordinates from one coordinate system to another.
19
+ * A [[GeoConverter]] has a pair of these for converting between iModel coordinates and geographic coordinates.
20
+ * Uses a cache to avoid repeatedly requesting the same points, and a batching strategy to avoid making frequent small requests.
21
+ * The cache stores every point that was ever converted by [[convert]]. It is currently permitted to grow to unbounded size.
22
+ * The batching works as follows:
23
+ * When a conversion is requested via [[convert]], if all the requested points are in the cache, they are returned immediately.
24
+ * Otherwise, any points not in the cache and not in the current in-flight request (if any) are placed onto the queue of pending requests.
25
+ * A pending request is scheduled if one hasn't already been scheduled, via requestAnimationFrame.
26
+ * In the animation frame callback, the pending requests are split into batches of no more than options.maxPointsPerRequest and dispatched to the backend.
27
+ * Once the response is received, the results are loaded into and returned from the cache.
28
+ * If more calls to convert occurred while the request was in flight, another request is dispatched.
29
+ * @internal exported strictly for tests.
30
+ */
31
+ class CoordinateConverter {
32
+ constructor(opts) {
33
+ var _a;
34
+ this._state = "idle";
35
+ // An event fired when the next request completes.
36
+ this._onCompleted = new core_bentley_1.BeEvent();
37
+ // Used for creating cache keys (XYAndZ) from XYZProps without having to allocate temporary objects.
38
+ this._scratchXYZ = { x: 0, y: 0, z: 0 };
39
+ this._maxPointsPerRequest = Math.max(1, (_a = opts.maxPointsPerRequest) !== null && _a !== void 0 ? _a : 300);
40
+ this._iModel = opts.iModel;
41
+ this._requestPoints = opts.requestPoints;
42
+ this._cache = new core_bentley_1.Dictionary(compareXYAndZ, cloneXYAndZ);
43
+ this._pending = new core_bentley_1.SortedArray(compareXYAndZ, false, cloneXYAndZ);
44
+ this._inflight = new core_bentley_1.SortedArray(compareXYAndZ, false, cloneXYAndZ);
11
45
  }
12
- /** @internal */
13
- findInCache(geoPoints) {
14
- const result = [];
15
- let missing;
16
- for (const geoPoint of geoPoints) {
17
- const key = JSON.stringify(geoPoint);
18
- const imodelCoord = this._cache[key];
19
- result.push(imodelCoord);
20
- if (undefined === imodelCoord) {
21
- if (undefined === missing)
22
- missing = [];
23
- missing.push(geoPoint);
24
- }
46
+ toXYAndZ(input, output) {
47
+ var _a, _b, _c, _d, _e, _f;
48
+ if (Array.isArray(input)) {
49
+ output.x = (_a = input[0]) !== null && _a !== void 0 ? _a : 0;
50
+ output.y = (_b = input[1]) !== null && _b !== void 0 ? _b : 0;
51
+ output.z = (_c = input[2]) !== null && _c !== void 0 ? _c : 0;
25
52
  }
26
- return { result, missing };
27
- }
28
- async findInCacheOrRequest(request) {
29
- const response = { iModelCoords: [], fromCache: 0 };
30
- let missing;
31
- // Index by cache key to obtain index in input array.
32
- const originalPositions = {};
33
- for (let iPoint = 0; iPoint < request.geoCoords.length; ++iPoint) {
34
- const thisGeoCoord = request.geoCoords[iPoint];
35
- // we use the JSON string as the key into our cache of previously returned results.
36
- const thisCacheKey = JSON.stringify(thisGeoCoord);
37
- // put something in each output that corresponds to the input.
38
- if (this._cache[thisCacheKey]) {
39
- response.iModelCoords.push(this._cache[thisCacheKey]);
40
- }
41
- else {
42
- if (undefined === missing)
43
- missing = [];
44
- // add this geoCoord to the request we are going to send.
45
- missing.push(thisGeoCoord);
46
- // keep track of the original position of this point.
47
- if (originalPositions.hasOwnProperty(thisCacheKey)) {
48
- // it is a duplicate of an earlier point (or points)
49
- if (Array.isArray(originalPositions[thisCacheKey])) {
50
- originalPositions[thisCacheKey].push(iPoint);
51
- }
52
- else {
53
- const list = [originalPositions[thisCacheKey], iPoint];
54
- originalPositions[thisCacheKey] = list;
55
- }
56
- }
57
- else {
58
- originalPositions[thisCacheKey] = iPoint;
59
- }
60
- // mark the response as pending.
61
- response.iModelCoords.push({ p: [0, 0, 0], s: core_common_1.GeoCoordStatus.Pending });
62
- }
53
+ else {
54
+ output.x = (_d = input.x) !== null && _d !== void 0 ? _d : 0;
55
+ output.y = (_e = input.y) !== null && _e !== void 0 ? _e : 0;
56
+ output.z = (_f = input.z) !== null && _f !== void 0 ? _f : 0;
63
57
  }
64
- // if none are missing from the cache, resolve the promise immediately
65
- if (undefined === missing) {
66
- response.fromCache = request.geoCoords.length;
58
+ return output;
59
+ }
60
+ async dispatch() {
61
+ (0, core_bentley_1.assert)(this._state === "scheduled");
62
+ if (this._iModel.isClosed || this._pending.isEmpty) {
63
+ this._state = "idle";
64
+ this._onCompleted.raiseEvent();
65
+ return;
67
66
  }
68
- else {
69
- // keep track of how many came from the cache (mostly for tests).
70
- response.fromCache = request.geoCoords.length - missing.length;
71
- // Avoiding requesting too many points at once, exceeding max request length (this definition of "too many" should be safely conservative) - but enough to load 4 levels of tile corners.
72
- const maxPointsPerRequest = 300;
73
- const promises = [];
74
- for (let i = 0; i < missing.length; i += maxPointsPerRequest) {
75
- const remainingRequest = { source: this._source, geoCoords: missing.slice(i, i + maxPointsPerRequest) };
76
- const promise = core_common_1.IModelReadRpcInterface.getClientForRouting(this._iModel.routingContext.token).getIModelCoordinatesFromGeoCoordinates(this._iModel.getRpcProps(), remainingRequest).then((remainingResponse) => {
77
- // put the responses into the cache, and fill in the output response for each
78
- for (let iResponse = 0; iResponse < remainingResponse.iModelCoords.length; ++iResponse) {
79
- const thisPoint = remainingResponse.iModelCoords[iResponse];
80
- // put the answer in the cache.
81
- const thisGeoCoord = remainingRequest.geoCoords[iResponse];
82
- const thisCacheKey = JSON.stringify(thisGeoCoord);
83
- this._cache[thisCacheKey] = thisPoint;
84
- // transfer the answer stored in remainingResponse to the correct position in the overall response.
85
- const responseIndex = originalPositions[thisCacheKey];
86
- if (Array.isArray(responseIndex)) {
87
- for (const thisIndex of responseIndex) {
88
- response.iModelCoords[thisIndex] = thisPoint;
89
- }
90
- }
91
- else {
92
- response.iModelCoords[responseIndex] = thisPoint;
93
- }
94
- }
95
- });
96
- promises.push(promise);
97
- }
98
- await Promise.all(promises);
67
+ this._state = "in-flight";
68
+ // Ensure subsequently-enqueued requests listen for the *next* response to be received.
69
+ const onCompleted = this._onCompleted;
70
+ this._onCompleted = new core_bentley_1.BeEvent();
71
+ // Pending requests are now in flight. Start a new list of pending requests. It's cheaper to swap than to allocate new objects.
72
+ const inflight = this._pending;
73
+ this._pending = this._inflight;
74
+ (0, core_bentley_1.assert)(this._pending.isEmpty);
75
+ this._inflight = inflight;
76
+ // Split requests if necessary to avoid requesting more than the maximum allowed number of points.
77
+ const promises = [];
78
+ for (let i = 0; i < inflight.length; i += this._maxPointsPerRequest) {
79
+ const requests = inflight.slice(i, i + this._maxPointsPerRequest).extractArray();
80
+ const promise = this._requestPoints(requests).then((results) => {
81
+ if (this._iModel.isClosed)
82
+ return;
83
+ if (results.length !== requests.length)
84
+ core_bentley_1.Logger.logError(`${FrontendLoggerCategory_1.FrontendLoggerCategory.Package}.geoservices`, `requested conversion of ${requests.length} points, but received ${results.length} points`);
85
+ for (let j = 0; j < results.length; j++) {
86
+ if (j < requests.length)
87
+ this._cache.set(requests[j], results[j]);
88
+ }
89
+ }).catch((err) => {
90
+ core_bentley_1.Logger.logException(`${FrontendLoggerCategory_1.FrontendLoggerCategory.Package}.geoservices`, err);
91
+ });
92
+ promises.push(promise);
99
93
  }
100
- return response;
94
+ await Promise.all(promises);
95
+ (0, core_bentley_1.assert)(this._state === "in-flight");
96
+ this._state = "idle";
97
+ this._inflight.clear();
98
+ // If any more pending conversions arrived while awaiting this request, schedule another request.
99
+ if (!this._pending.isEmpty)
100
+ this.scheduleDispatch(); // eslint-disable-line @typescript-eslint/no-floating-promises
101
+ // Resolve promises of all callers who were awaiting this request.
102
+ onCompleted.raiseEvent();
101
103
  }
102
- }
103
- // this class is used to cache results from conversion of IModelCoordinates to GeoCoordinates.
104
- class IMCtoGCResultCache {
105
- constructor(iModel, target) {
106
- this._iModel = iModel;
107
- this._cache = {};
108
- this._target = target;
104
+ // Add any points not present in cache to pending request list.
105
+ // Return the number of points present in cache.
106
+ enqueue(points) {
107
+ let numInCache = 0;
108
+ for (const point of points) {
109
+ const xyz = this.toXYAndZ(point, this._scratchXYZ);
110
+ if (this._cache.get(xyz))
111
+ ++numInCache;
112
+ else if (!this._inflight.contains(xyz))
113
+ this._pending.insert(xyz);
114
+ }
115
+ return numInCache;
109
116
  }
110
- async findInCacheOrRequest(request) {
111
- let missing = false;
112
- const response = { geoCoords: [], fromCache: 0 };
113
- let remainingRequest;
114
- const originalPositions = [];
115
- for (let iPoint = 0; iPoint < request.iModelCoords.length; ++iPoint) {
116
- const thisIModelCoord = request.iModelCoords[iPoint];
117
- // we use the JSON string as the key into our cache of previously returned results.
118
- const thisCacheKey = JSON.stringify(thisIModelCoord);
119
- // put something in each output that corresponds to the input.
120
- if (this._cache[thisCacheKey]) {
121
- response.geoCoords.push(this._cache[thisCacheKey]);
122
- }
123
- else {
124
- if (!remainingRequest)
125
- remainingRequest = { target: this._target, iModelCoords: [] };
126
- // add this geoCoord to the request we are going to send.
127
- remainingRequest.iModelCoords.push(thisIModelCoord);
128
- // keep track of the original position of this point.
129
- originalPositions.push(iPoint);
130
- // mark the response as pending.
131
- response.geoCoords.push({ p: [0, 0, 0], s: core_common_1.GeoCoordStatus.Pending });
132
- missing = true;
133
- }
117
+ // Obtain converted points from the cache. The assumption is that every point in `inputs` is already present in the cache.
118
+ // Any point not present will be returned unconverted with an error status.
119
+ getFromCache(inputs) {
120
+ const outputs = [];
121
+ for (const input of inputs) {
122
+ const xyz = this.toXYAndZ(input, this._scratchXYZ);
123
+ let output = this._cache.get(xyz);
124
+ if (!output)
125
+ output = { p: { ...xyz }, s: core_common_1.GeoCoordStatus.CSMapError };
126
+ outputs.push(output);
134
127
  }
135
- // if none are missing from the cache, resolve the promise immediately
136
- if (!missing) {
137
- response.fromCache = request.iModelCoords.length;
138
- return response;
128
+ return outputs;
129
+ }
130
+ async scheduleDispatch() {
131
+ if ("idle" === this._state) {
132
+ this._state = "scheduled";
133
+ requestAnimationFrame(() => {
134
+ this.dispatch(); // eslint-disable-line @typescript-eslint/no-floating-promises
135
+ });
139
136
  }
140
- else {
141
- // keep track of how many came from the cache (mostly for tests).
142
- response.fromCache = request.iModelCoords.length - originalPositions.length;
143
- const remainingResponse = await core_common_1.IModelReadRpcInterface.getClientForRouting(this._iModel.routingContext.token).getGeoCoordinatesFromIModelCoordinates(this._iModel.getRpcProps(), remainingRequest);
144
- // put the responses into the cache, and fill in the output response for each
145
- for (let iResponse = 0; iResponse < remainingResponse.geoCoords.length; ++iResponse) {
146
- const thisPoint = remainingResponse.geoCoords[iResponse];
147
- // transfer the answer stored in remainingResponse to the correct position in the overall response.
148
- const responseIndex = originalPositions[iResponse];
149
- response.geoCoords[responseIndex] = thisPoint;
150
- // put the answer in the cache.
151
- const thisIModelCoord = remainingRequest.iModelCoords[iResponse];
152
- const thisCacheKey = JSON.stringify(thisIModelCoord);
153
- this._cache[thisCacheKey] = thisPoint;
137
+ return new Promise((resolve) => {
138
+ this._onCompleted.addOnce(() => resolve());
139
+ });
140
+ }
141
+ async convert(inputs) {
142
+ const fromCache = this.enqueue(inputs);
143
+ (0, core_bentley_1.assert)(fromCache >= 0);
144
+ (0, core_bentley_1.assert)(fromCache <= inputs.length);
145
+ if (fromCache === inputs.length)
146
+ return { points: this.getFromCache(inputs), fromCache };
147
+ await this.scheduleDispatch();
148
+ return { points: this.getFromCache(inputs), fromCache };
149
+ }
150
+ findCached(inputs) {
151
+ const result = [];
152
+ let missing;
153
+ for (const input of inputs) {
154
+ const key = this.toXYAndZ(input, this._scratchXYZ);
155
+ const output = this._cache.get(key);
156
+ result.push(output);
157
+ if (!output) {
158
+ if (!missing)
159
+ missing = [];
160
+ missing.push(input);
154
161
  }
155
- return response;
156
162
  }
163
+ return { result, missing };
157
164
  }
158
165
  }
166
+ exports.CoordinateConverter = CoordinateConverter;
159
167
  /** The GeoConverter class communicates with the backend to convert longitude/latitude coordinates to iModel coordinates and vice-versa
160
168
  * @internal
161
169
  */
162
170
  class GeoConverter {
163
171
  constructor(iModel, datumOrGCRS) {
164
- if (typeof (datumOrGCRS) === "object")
165
- this._datumOrGCRS = JSON.stringify(datumOrGCRS);
166
- else
167
- this._datumOrGCRS = datumOrGCRS;
168
- this._gCtoIMCResultCache = new GCtoIMCResultCache(iModel, this._datumOrGCRS);
169
- this._iMCtoGCResultCache = new IMCtoGCResultCache(iModel, this._datumOrGCRS);
172
+ const datum = typeof datumOrGCRS === "object" ? JSON.stringify(datumOrGCRS) : datumOrGCRS;
173
+ this._geoToIModel = new CoordinateConverter({
174
+ iModel,
175
+ requestPoints: async (geoCoords) => {
176
+ const request = { source: datum, geoCoords };
177
+ const rpc = core_common_1.IModelReadRpcInterface.getClientForRouting(iModel.routingContext.token);
178
+ const response = await rpc.getIModelCoordinatesFromGeoCoordinates(iModel.getRpcProps(), request);
179
+ return response.iModelCoords;
180
+ },
181
+ });
182
+ this._iModelToGeo = new CoordinateConverter({
183
+ iModel,
184
+ requestPoints: async (iModelCoords) => {
185
+ const request = { target: datum, iModelCoords };
186
+ const rpc = core_common_1.IModelReadRpcInterface.getClientForRouting(iModel.routingContext.token);
187
+ const response = await rpc.getGeoCoordinatesFromIModelCoordinates(iModel.getRpcProps(), request);
188
+ return response.geoCoords;
189
+ },
190
+ });
170
191
  }
171
192
  async getIModelCoordinatesFromGeoCoordinates(geoPoints) {
172
- const requestProps = { source: this._datumOrGCRS, geoCoords: geoPoints };
173
- return this._gCtoIMCResultCache.findInCacheOrRequest(requestProps);
174
- }
175
- getCachedIModelCoordinatesFromGeoCoordinates(geoPoints) {
176
- return this._gCtoIMCResultCache.findInCache(geoPoints);
193
+ const result = await this._geoToIModel.convert(geoPoints);
194
+ return {
195
+ iModelCoords: result.points,
196
+ fromCache: result.fromCache,
197
+ };
177
198
  }
178
199
  async getGeoCoordinatesFromIModelCoordinates(iModelPoints) {
179
- const requestProps = { target: this._datumOrGCRS, iModelCoords: iModelPoints };
180
- return this._iMCtoGCResultCache.findInCacheOrRequest(requestProps);
200
+ const result = await this._iModelToGeo.convert(iModelPoints);
201
+ return {
202
+ geoCoords: result.points,
203
+ fromCache: result.fromCache,
204
+ };
205
+ }
206
+ getCachedIModelCoordinatesFromGeoCoordinates(geoPoints) {
207
+ return this._geoToIModel.findCached(geoPoints);
181
208
  }
182
209
  }
183
210
  exports.GeoConverter = GeoConverter;
@@ -1 +1 @@
1
- {"version":3,"file":"GeoServices.js","sourceRoot":"","sources":["../../src/GeoServices.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\n* See LICENSE.md in the project root for license terms and full copyright notice.\n*--------------------------------------------------------------------------------------------*/\n// cspell:ignore GCRS\nimport { XYZProps } from \"@itwin/core-geometry\";\nimport {\n GeoCoordinatesRequestProps, GeoCoordinatesResponseProps, GeoCoordStatus, GeographicCRSProps, IModelCoordinatesRequestProps, IModelCoordinatesResponseProps,\n IModelReadRpcInterface, PointWithStatus,\n} from \"@itwin/core-common\";\nimport { IModelConnection } from \"./IModelConnection\";\n\n/** Response to a request to obtain imodel coordinates from cache.\n * @internal\n */\nexport interface CachedIModelCoordinatesResponseProps {\n /** An array of the same length as the input array, with undefined entries at indices corresponding to points not found in cache. */\n result: Array<PointWithStatus | undefined>;\n /** An array of points in the input array which were not found in the cache, or undefined if all points were found in the cache. */\n missing?: XYZProps[];\n}\n\n// this class is used to cache results from conversion of geoCoordinates to IModelCoordinates.\nclass GCtoIMCResultCache {\n // see fast-memoize npm package where the author demonstrated that an object is the fastest\n // lookup (faster than Map), and JSON.stringify is the fastest serializer.\n private _cache: any;\n private _iModel: IModelConnection;\n private _source: string;\n\n constructor(iModel: IModelConnection, source: string) {\n this._iModel = iModel;\n this._cache = {};\n this._source = source;\n }\n\n /** @internal */\n public findInCache(geoPoints: XYZProps[]): CachedIModelCoordinatesResponseProps {\n const result: Array<PointWithStatus | undefined> = [];\n let missing: XYZProps[] | undefined;\n for (const geoPoint of geoPoints) {\n const key = JSON.stringify(geoPoint);\n const imodelCoord = this._cache[key];\n result.push(imodelCoord);\n if (undefined === imodelCoord) {\n if (undefined === missing)\n missing = [];\n\n missing.push(geoPoint);\n }\n }\n\n return { result, missing };\n }\n\n public async findInCacheOrRequest(request: IModelCoordinatesRequestProps): Promise<IModelCoordinatesResponseProps> {\n const response: IModelCoordinatesResponseProps = { iModelCoords: [], fromCache: 0 };\n let missing: XYZProps[] | undefined;\n\n // Index by cache key to obtain index in input array.\n const originalPositions: any = {};\n\n for (let iPoint: number = 0; iPoint < request.geoCoords.length; ++iPoint) {\n const thisGeoCoord: XYZProps = request.geoCoords[iPoint];\n\n // we use the JSON string as the key into our cache of previously returned results.\n const thisCacheKey: string = JSON.stringify(thisGeoCoord);\n\n // put something in each output that corresponds to the input.\n if (this._cache[thisCacheKey]) {\n response.iModelCoords.push(this._cache[thisCacheKey]);\n } else {\n if (undefined === missing)\n missing = [];\n\n // add this geoCoord to the request we are going to send.\n missing.push(thisGeoCoord);\n\n // keep track of the original position of this point.\n if (originalPositions.hasOwnProperty(thisCacheKey)) {\n // it is a duplicate of an earlier point (or points)\n if (Array.isArray(originalPositions[thisCacheKey])) {\n originalPositions[thisCacheKey].push(iPoint);\n } else {\n const list: number[] = [originalPositions[thisCacheKey], iPoint];\n originalPositions[thisCacheKey] = list;\n }\n } else {\n originalPositions[thisCacheKey] = iPoint;\n }\n\n // mark the response as pending.\n response.iModelCoords.push({ p: [0, 0, 0], s: GeoCoordStatus.Pending });\n }\n }\n\n // if none are missing from the cache, resolve the promise immediately\n if (undefined === missing) {\n response.fromCache = request.geoCoords.length;\n } else {\n // keep track of how many came from the cache (mostly for tests).\n response.fromCache = request.geoCoords.length - missing.length;\n\n // Avoiding requesting too many points at once, exceeding max request length (this definition of \"too many\" should be safely conservative) - but enough to load 4 levels of tile corners.\n const maxPointsPerRequest = 300;\n const promises: Array<Promise<void>> = [];\n for (let i = 0; i < missing.length; i += maxPointsPerRequest) {\n const remainingRequest = { source: this._source, geoCoords: missing.slice(i, i + maxPointsPerRequest) };\n const promise = IModelReadRpcInterface.getClientForRouting(this._iModel.routingContext.token).getIModelCoordinatesFromGeoCoordinates(this._iModel.getRpcProps(), remainingRequest).then((remainingResponse) => {\n // put the responses into the cache, and fill in the output response for each\n for (let iResponse: number = 0; iResponse < remainingResponse.iModelCoords.length; ++iResponse) {\n const thisPoint: PointWithStatus = remainingResponse.iModelCoords[iResponse];\n\n // put the answer in the cache.\n const thisGeoCoord: XYZProps = remainingRequest.geoCoords[iResponse];\n const thisCacheKey: string = JSON.stringify(thisGeoCoord);\n this._cache[thisCacheKey] = thisPoint;\n\n // transfer the answer stored in remainingResponse to the correct position in the overall response.\n const responseIndex = originalPositions[thisCacheKey];\n if (Array.isArray(responseIndex)) {\n for (const thisIndex of responseIndex) {\n response.iModelCoords[thisIndex] = thisPoint;\n }\n } else {\n response.iModelCoords[responseIndex] = thisPoint;\n }\n }\n });\n\n promises.push(promise);\n }\n\n await Promise.all(promises);\n }\n\n return response;\n }\n}\n\n// this class is used to cache results from conversion of IModelCoordinates to GeoCoordinates.\nclass IMCtoGCResultCache {\n // see fast-memoize npm package where the author demonstrated that an object is the fastest\n // lookup (faster than Map), and JSON.stringify is the fastest serializer.\n private _cache: any;\n private _iModel: IModelConnection;\n private _target: string;\n\n constructor(iModel: IModelConnection, target: string) {\n this._iModel = iModel;\n this._cache = {};\n this._target = target;\n }\n\n public async findInCacheOrRequest(request: GeoCoordinatesRequestProps): Promise<GeoCoordinatesResponseProps> {\n let missing: boolean = false;\n const response: GeoCoordinatesResponseProps = { geoCoords: [], fromCache: 0 };\n let remainingRequest: GeoCoordinatesRequestProps | undefined;\n const originalPositions: number[] = [];\n\n for (let iPoint = 0; iPoint < request.iModelCoords.length; ++iPoint) {\n const thisIModelCoord = request.iModelCoords[iPoint];\n\n // we use the JSON string as the key into our cache of previously returned results.\n const thisCacheKey = JSON.stringify(thisIModelCoord);\n\n // put something in each output that corresponds to the input.\n if (this._cache[thisCacheKey]) {\n response.geoCoords.push(this._cache[thisCacheKey]);\n } else {\n if (!remainingRequest)\n remainingRequest = { target: this._target, iModelCoords: [] };\n\n // add this geoCoord to the request we are going to send.\n remainingRequest.iModelCoords.push(thisIModelCoord);\n // keep track of the original position of this point.\n originalPositions.push(iPoint);\n\n // mark the response as pending.\n response.geoCoords.push({ p: [0, 0, 0], s: GeoCoordStatus.Pending });\n\n missing = true;\n }\n }\n\n // if none are missing from the cache, resolve the promise immediately\n if (!missing) {\n response.fromCache = request.iModelCoords.length;\n return response;\n } else {\n // keep track of how many came from the cache (mostly for tests).\n response.fromCache = request.iModelCoords.length - originalPositions.length;\n const remainingResponse = await IModelReadRpcInterface.getClientForRouting(this._iModel.routingContext.token).getGeoCoordinatesFromIModelCoordinates(this._iModel.getRpcProps(), remainingRequest!);\n // put the responses into the cache, and fill in the output response for each\n for (let iResponse = 0; iResponse < remainingResponse.geoCoords.length; ++iResponse) {\n const thisPoint: PointWithStatus = remainingResponse.geoCoords[iResponse];\n\n // transfer the answer stored in remainingResponse to the correct position in the overall response.\n const responseIndex = originalPositions[iResponse];\n response.geoCoords[responseIndex] = thisPoint;\n\n // put the answer in the cache.\n const thisIModelCoord = remainingRequest!.iModelCoords[iResponse];\n const thisCacheKey = JSON.stringify(thisIModelCoord);\n this._cache[thisCacheKey] = thisPoint;\n }\n return response;\n }\n }\n}\n\n/** The GeoConverter class communicates with the backend to convert longitude/latitude coordinates to iModel coordinates and vice-versa\n * @internal\n */\nexport class GeoConverter {\n private _datumOrGCRS: string;\n private _gCtoIMCResultCache: GCtoIMCResultCache;\n private _iMCtoGCResultCache: IMCtoGCResultCache;\n constructor(iModel: IModelConnection, datumOrGCRS: string | GeographicCRSProps) {\n if (typeof (datumOrGCRS) === \"object\")\n this._datumOrGCRS = JSON.stringify(datumOrGCRS);\n else\n this._datumOrGCRS = datumOrGCRS;\n this._gCtoIMCResultCache = new GCtoIMCResultCache(iModel, this._datumOrGCRS);\n this._iMCtoGCResultCache = new IMCtoGCResultCache(iModel, this._datumOrGCRS);\n }\n\n public async getIModelCoordinatesFromGeoCoordinates(geoPoints: XYZProps[]): Promise<IModelCoordinatesResponseProps> {\n const requestProps: IModelCoordinatesRequestProps = { source: this._datumOrGCRS, geoCoords: geoPoints };\n return this._gCtoIMCResultCache.findInCacheOrRequest(requestProps);\n }\n\n public getCachedIModelCoordinatesFromGeoCoordinates(geoPoints: XYZProps[]): CachedIModelCoordinatesResponseProps {\n return this._gCtoIMCResultCache.findInCache(geoPoints);\n }\n\n public async getGeoCoordinatesFromIModelCoordinates(iModelPoints: XYZProps[]): Promise<GeoCoordinatesResponseProps> {\n const requestProps: GeoCoordinatesRequestProps = { target: this._datumOrGCRS, iModelCoords: iModelPoints };\n return this._iMCtoGCResultCache.findInCacheOrRequest(requestProps);\n }\n}\n\n/** The Geographic Services available for an [[IModelConnection]].\n * @internal\n */\nexport class GeoServices {\n private _iModel: IModelConnection;\n\n constructor(iModel: IModelConnection) {\n this._iModel = iModel;\n }\n\n public getConverter(datumOrGCRS?: string | GeographicCRSProps): GeoConverter | undefined {\n return this._iModel.isOpen ? new GeoConverter(this._iModel, datumOrGCRS ? datumOrGCRS : \"\") : undefined;\n }\n}\n"]}
1
+ {"version":3,"file":"GeoServices.js","sourceRoot":"","sources":["../../src/GeoServices.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\n* See LICENSE.md in the project root for license terms and full copyright notice.\n*--------------------------------------------------------------------------------------------*/\n// cspell:ignore GCRS\nimport {\n assert, BeEvent, Dictionary, Logger, SortedArray,\n} from \"@itwin/core-bentley\";\nimport { WritableXYAndZ, XYAndZ, XYZProps } from \"@itwin/core-geometry\";\nimport {\n GeoCoordinatesResponseProps, GeoCoordStatus, GeographicCRSProps, IModelCoordinatesResponseProps, IModelReadRpcInterface, PointWithStatus,\n} from \"@itwin/core-common\";\nimport { IModelConnection } from \"./IModelConnection\";\nimport { FrontendLoggerCategory } from \"./FrontendLoggerCategory\";\n\n/** Options used to create a [[CoordinateConverter]].\n * @internal exported strictly for tests.\n */\nexport interface CoordinateConverterOptions {\n /** The iModel for which to perform the conversions. */\n iModel: IModelConnection;\n /** Asynchronously convert each point. The resultant array should have the same number and order of points as the input. */\n requestPoints: (points: XYAndZ[]) => Promise<PointWithStatus[]>;\n /** Maximum number of points to include in each request. Default: 300. */\n maxPointsPerRequest?: number;\n}\n\nfunction compareXYAndZ(lhs: XYAndZ, rhs: XYAndZ): number {\n return lhs.x - rhs.x || lhs.y - rhs.y || lhs.z - rhs.z;\n}\n\nfunction cloneXYAndZ(xyz: XYAndZ): XYAndZ {\n return { x: xyz.x, y: xyz.y, z: xyz.z };\n}\n\ntype CoordinateConverterState =\n // No pending requests.\n \"idle\" |\n // We have scheduled a requestAnimationFrame to dispatch all pending requests.\n \"scheduled\" |\n // We have dispatched all requests that were pending at the most recent requestAnimationFrame callback.\n \"in-flight\";\n\n/** Performs conversion of coordinates from one coordinate system to another.\n * A [[GeoConverter]] has a pair of these for converting between iModel coordinates and geographic coordinates.\n * Uses a cache to avoid repeatedly requesting the same points, and a batching strategy to avoid making frequent small requests.\n * The cache stores every point that was ever converted by [[convert]]. It is currently permitted to grow to unbounded size.\n * The batching works as follows:\n * When a conversion is requested via [[convert]], if all the requested points are in the cache, they are returned immediately.\n * Otherwise, any points not in the cache and not in the current in-flight request (if any) are placed onto the queue of pending requests.\n * A pending request is scheduled if one hasn't already been scheduled, via requestAnimationFrame.\n * In the animation frame callback, the pending requests are split into batches of no more than options.maxPointsPerRequest and dispatched to the backend.\n * Once the response is received, the results are loaded into and returned from the cache.\n * If more calls to convert occurred while the request was in flight, another request is dispatched.\n * @internal exported strictly for tests.\n */\nexport class CoordinateConverter {\n protected readonly _cache: Dictionary<XYAndZ, PointWithStatus>;\n protected _state: CoordinateConverterState = \"idle\";\n // The accumulated set of points to be converted by the next request.\n protected _pending: SortedArray<XYAndZ>;\n // The set of points that were included in the current in-flight request, if any.\n protected _inflight: SortedArray<XYAndZ>;\n // An event fired when the next request completes.\n protected _onCompleted = new BeEvent<() => void>();\n // Used for creating cache keys (XYAndZ) from XYZProps without having to allocate temporary objects.\n protected readonly _scratchXYZ = { x: 0, y: 0, z: 0 };\n protected readonly _maxPointsPerRequest: number;\n protected readonly _iModel: IModelConnection;\n protected readonly _requestPoints: (points: XYAndZ[]) => Promise<PointWithStatus[]>;\n\n protected toXYAndZ(input: XYZProps, output: WritableXYAndZ): XYAndZ {\n if (Array.isArray(input)) {\n output.x = input[0] ?? 0;\n output.y = input[1] ?? 0;\n output.z = input[2] ?? 0;\n } else {\n output.x = input.x ?? 0;\n output.y = input.y ?? 0;\n output.z = input.z ?? 0;\n }\n\n return output;\n }\n\n public constructor(opts: CoordinateConverterOptions) {\n this._maxPointsPerRequest = Math.max(1, opts.maxPointsPerRequest ?? 300);\n this._iModel = opts.iModel;\n this._requestPoints = opts.requestPoints;\n\n this._cache = new Dictionary<XYAndZ, PointWithStatus>(compareXYAndZ, cloneXYAndZ);\n this._pending = new SortedArray<XYAndZ>(compareXYAndZ, false, cloneXYAndZ);\n this._inflight = new SortedArray<XYAndZ>(compareXYAndZ, false, cloneXYAndZ);\n }\n\n protected async dispatch(): Promise<void> {\n assert(this._state === \"scheduled\");\n if (this._iModel.isClosed || this._pending.isEmpty) {\n this._state = \"idle\";\n this._onCompleted.raiseEvent();\n return;\n }\n\n this._state = \"in-flight\";\n\n // Ensure subsequently-enqueued requests listen for the *next* response to be received.\n const onCompleted = this._onCompleted;\n this._onCompleted = new BeEvent<() => void>();\n\n // Pending requests are now in flight. Start a new list of pending requests. It's cheaper to swap than to allocate new objects.\n const inflight = this._pending;\n this._pending = this._inflight;\n assert(this._pending.isEmpty);\n this._inflight = inflight;\n\n // Split requests if necessary to avoid requesting more than the maximum allowed number of points.\n const promises: Array<Promise<void>> = [];\n for (let i = 0; i < inflight.length; i += this._maxPointsPerRequest) {\n const requests = inflight.slice(i, i + this._maxPointsPerRequest).extractArray();\n const promise = this._requestPoints(requests).then((results) => {\n if (this._iModel.isClosed)\n return;\n\n if (results.length !== requests.length)\n Logger.logError(`${FrontendLoggerCategory.Package}.geoservices`, `requested conversion of ${requests.length} points, but received ${results.length} points`);\n\n for (let j = 0; j < results.length; j++) {\n if (j < requests.length)\n this._cache.set(requests[j], results[j]);\n }\n }).catch((err) => {\n Logger.logException(`${FrontendLoggerCategory.Package}.geoservices`, err);\n });\n\n promises.push(promise);\n }\n\n await Promise.all(promises);\n\n assert(this._state === \"in-flight\");\n this._state = \"idle\";\n this._inflight.clear();\n\n // If any more pending conversions arrived while awaiting this request, schedule another request.\n if (!this._pending.isEmpty)\n this.scheduleDispatch(); // eslint-disable-line @typescript-eslint/no-floating-promises\n\n // Resolve promises of all callers who were awaiting this request.\n onCompleted.raiseEvent();\n }\n\n // Add any points not present in cache to pending request list.\n // Return the number of points present in cache.\n protected enqueue(points: XYZProps[]): number {\n let numInCache = 0;\n for (const point of points) {\n const xyz = this.toXYAndZ(point, this._scratchXYZ);\n if (this._cache.get(xyz))\n ++numInCache;\n else if (!this._inflight.contains(xyz))\n this._pending.insert(xyz);\n }\n\n return numInCache;\n }\n\n // Obtain converted points from the cache. The assumption is that every point in `inputs` is already present in the cache.\n // Any point not present will be returned unconverted with an error status.\n protected getFromCache(inputs: XYZProps[]): PointWithStatus[] {\n const outputs: PointWithStatus[] = [];\n for (const input of inputs) {\n const xyz = this.toXYAndZ(input, this._scratchXYZ);\n let output = this._cache.get(xyz);\n if (!output)\n output = { p: { ...xyz }, s: GeoCoordStatus.CSMapError };\n\n outputs.push(output);\n }\n\n return outputs;\n }\n\n protected async scheduleDispatch(): Promise<void> {\n if (\"idle\" === this._state) {\n this._state = \"scheduled\";\n requestAnimationFrame(() => {\n this.dispatch(); // eslint-disable-line @typescript-eslint/no-floating-promises\n });\n }\n\n return new Promise((resolve) => {\n this._onCompleted.addOnce(() => resolve());\n });\n }\n\n public async convert(inputs: XYZProps[]): Promise<{ points: PointWithStatus[], fromCache: number }> {\n const fromCache = this.enqueue(inputs);\n assert(fromCache >= 0);\n assert(fromCache <= inputs.length);\n\n if (fromCache === inputs.length)\n return { points: this.getFromCache(inputs), fromCache };\n\n await this.scheduleDispatch();\n\n return { points: this.getFromCache(inputs), fromCache };\n }\n\n public findCached(inputs: XYZProps[]): CachedIModelCoordinatesResponseProps {\n const result: Array<PointWithStatus | undefined> = [];\n let missing: XYZProps[] | undefined;\n for (const input of inputs) {\n const key = this.toXYAndZ(input, this._scratchXYZ);\n const output = this._cache.get(key);\n result.push(output);\n if (!output) {\n if (!missing)\n missing = [];\n\n missing.push(input);\n }\n }\n\n return { result, missing };\n }\n}\n\n/** Response to a request to obtain imodel coordinates from cache.\n * @internal\n */\nexport interface CachedIModelCoordinatesResponseProps {\n /** An array of the same length as the input array, with undefined entries at indices corresponding to points not found in cache. */\n result: Array<PointWithStatus | undefined>;\n /** An array of points in the input array which were not found in the cache, or undefined if all points were found in the cache. */\n missing?: XYZProps[];\n}\n\n/** The GeoConverter class communicates with the backend to convert longitude/latitude coordinates to iModel coordinates and vice-versa\n * @internal\n */\nexport class GeoConverter {\n private readonly _geoToIModel: CoordinateConverter;\n private readonly _iModelToGeo: CoordinateConverter;\n\n constructor(iModel: IModelConnection, datumOrGCRS: string | GeographicCRSProps) {\n const datum = typeof datumOrGCRS === \"object\" ? JSON.stringify(datumOrGCRS) : datumOrGCRS;\n\n this._geoToIModel = new CoordinateConverter({\n iModel,\n requestPoints: async (geoCoords: XYAndZ[]) => {\n const request = { source: datum, geoCoords };\n const rpc = IModelReadRpcInterface.getClientForRouting(iModel.routingContext.token);\n const response = await rpc.getIModelCoordinatesFromGeoCoordinates(iModel.getRpcProps(), request);\n return response.iModelCoords;\n },\n });\n\n this._iModelToGeo = new CoordinateConverter({\n iModel,\n requestPoints: async (iModelCoords: XYAndZ[]) => {\n const request = { target: datum, iModelCoords };\n const rpc = IModelReadRpcInterface.getClientForRouting(iModel.routingContext.token);\n const response = await rpc.getGeoCoordinatesFromIModelCoordinates(iModel.getRpcProps(), request);\n return response.geoCoords;\n },\n });\n }\n\n public async getIModelCoordinatesFromGeoCoordinates(geoPoints: XYZProps[]): Promise<IModelCoordinatesResponseProps> {\n const result = await this._geoToIModel.convert(geoPoints);\n return {\n iModelCoords: result.points,\n fromCache: result.fromCache,\n };\n }\n\n public async getGeoCoordinatesFromIModelCoordinates(iModelPoints: XYZProps[]): Promise<GeoCoordinatesResponseProps> {\n const result = await this._iModelToGeo.convert(iModelPoints);\n return {\n geoCoords: result.points,\n fromCache: result.fromCache,\n };\n }\n\n public getCachedIModelCoordinatesFromGeoCoordinates(geoPoints: XYZProps[]): CachedIModelCoordinatesResponseProps {\n return this._geoToIModel.findCached(geoPoints);\n }\n}\n\n/** The Geographic Services available for an [[IModelConnection]].\n * @internal\n */\nexport class GeoServices {\n private _iModel: IModelConnection;\n\n constructor(iModel: IModelConnection) {\n this._iModel = iModel;\n }\n\n public getConverter(datumOrGCRS?: string | GeographicCRSProps): GeoConverter | undefined {\n return this._iModel.isOpen ? new GeoConverter(this._iModel, datumOrGCRS ? datumOrGCRS : \"\") : undefined;\n }\n}\n"]}
@@ -29,7 +29,7 @@ export declare class ViewRect {
29
29
  /** The bottommost side of this ViewRect. */
30
30
  get bottom(): number;
31
31
  set bottom(val: number);
32
- /** True if this ViewRect has an area > 0. */
32
+ /** True if this ViewRect has an area <= 0. */
33
33
  get isNull(): boolean;
34
34
  /** True if `!isNull` */
35
35
  get isValid(): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"ViewRect.d.ts","sourceRoot":"","sources":["../../src/ViewRect.ts"],"names":[],"mappings":"AAIA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;;;GAMG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,KAAK,CAAU;IACvB,OAAO,CAAC,IAAI,CAAU;IACtB,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,OAAO,CAAU;IAEzB,OAAO,CAAC,IAAI;IAIZ,gCAAgC;gBACb,IAAI,SAAI,EAAE,GAAG,SAAI,EAAE,KAAK,SAAI,EAAE,MAAM,SAAI;IAC3D,0CAA0C;IAC1C,IAAW,IAAI,IAAI,MAAM,CAAuB;IAChD,IAAW,IAAI,CAAC,GAAG,EAAE,MAAM,EAA8B;IACzD,yCAAyC;IACzC,IAAW,GAAG,IAAI,MAAM,CAAsB;IAC9C,IAAW,GAAG,CAAC,GAAG,EAAE,MAAM,EAA6B;IACvD,2CAA2C;IAC3C,IAAW,KAAK,IAAI,MAAM,CAAwB;IAClD,IAAW,KAAK,CAAC,GAAG,EAAE,MAAM,EAA+B;IAC3D,4CAA4C;IAC5C,IAAW,MAAM,IAAI,MAAM,CAAyB;IACpD,IAAW,MAAM,CAAC,GAAG,EAAE,MAAM,EAAgC;IAC7D,6CAA6C;IAC7C,IAAW,MAAM,IAAI,OAAO,CAA+D;IAC3F,wBAAwB;IACxB,IAAW,OAAO,IAAI,OAAO,CAAyB;IACtD,+CAA+C;IAC/C,IAAW,KAAK,IACQ,MAAM,CADuB;IACrD,IAAW,KAAK,CAAC,KAAK,EAAE,MAAM,EAAqC;IACnE,gDAAgD;IAChD,IAAW,MAAM,IACS,MAAM,CADsB;IACtD,IAAW,MAAM,CAAC,MAAM,EAAE,MAAM,EAAsC;IACtE,wDAAwD;IACxD,IAAW,MAAM,WAA2D;IAC5E,gDAAgD;IAChD,IAAW,IAAI,WAAyD;IACxE,0EAA0E;IACnE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAMpE;;;OAGG;IACI,cAAc,CAAC,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,GAAG,IAAI;IAC/D;;OAEG;IACI,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAC/C;;OAEG;IACI,MAAM,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO;IACvC,sDAAsD;IAC/C,OAAO,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IACrC;;OAEG;IACI,KAAK,CAAC,MAAM,CAAC,EAAE,QAAQ,GAAG,QAAQ;IAOlC,MAAM,CAAC,KAAK,EAAE,QAAQ;IAW7B;;;OAGG;IACI,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAalD;;;OAGG;IACI,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAEzC,0EAA0E;IACnE,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAQ7D;;;;OAIG;IACI,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAE5C,8FAA8F;IACvF,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO;IAE5C;;;OAGG;IACI,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO;IAE3C,wDAAwD;IACjD,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO;IAEzC;;OAEG;IACI,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS;CAgB7E"}
1
+ {"version":3,"file":"ViewRect.d.ts","sourceRoot":"","sources":["../../src/ViewRect.ts"],"names":[],"mappings":"AAIA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;;;GAMG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,KAAK,CAAU;IACvB,OAAO,CAAC,IAAI,CAAU;IACtB,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,OAAO,CAAU;IAEzB,OAAO,CAAC,IAAI;IAIZ,gCAAgC;gBACb,IAAI,SAAI,EAAE,GAAG,SAAI,EAAE,KAAK,SAAI,EAAE,MAAM,SAAI;IAC3D,0CAA0C;IAC1C,IAAW,IAAI,IAAI,MAAM,CAAuB;IAChD,IAAW,IAAI,CAAC,GAAG,EAAE,MAAM,EAA8B;IACzD,yCAAyC;IACzC,IAAW,GAAG,IAAI,MAAM,CAAsB;IAC9C,IAAW,GAAG,CAAC,GAAG,EAAE,MAAM,EAA6B;IACvD,2CAA2C;IAC3C,IAAW,KAAK,IAAI,MAAM,CAAwB;IAClD,IAAW,KAAK,CAAC,GAAG,EAAE,MAAM,EAA+B;IAC3D,4CAA4C;IAC5C,IAAW,MAAM,IAAI,MAAM,CAAyB;IACpD,IAAW,MAAM,CAAC,GAAG,EAAE,MAAM,EAAgC;IAC7D,8CAA8C;IAC9C,IAAW,MAAM,IAAI,OAAO,CAA+D;IAC3F,wBAAwB;IACxB,IAAW,OAAO,IAAI,OAAO,CAAyB;IACtD,+CAA+C;IAC/C,IAAW,KAAK,IACQ,MAAM,CADuB;IACrD,IAAW,KAAK,CAAC,KAAK,EAAE,MAAM,EAAqC;IACnE,gDAAgD;IAChD,IAAW,MAAM,IACS,MAAM,CADsB;IACtD,IAAW,MAAM,CAAC,MAAM,EAAE,MAAM,EAAsC;IACtE,wDAAwD;IACxD,IAAW,MAAM,WAA2D;IAC5E,gDAAgD;IAChD,IAAW,IAAI,WAAyD;IACxE,0EAA0E;IACnE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAMpE;;;OAGG;IACI,cAAc,CAAC,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,GAAG,IAAI;IAC/D;;OAEG;IACI,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAC/C;;OAEG;IACI,MAAM,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO;IACvC,sDAAsD;IAC/C,OAAO,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IACrC;;OAEG;IACI,KAAK,CAAC,MAAM,CAAC,EAAE,QAAQ,GAAG,QAAQ;IAOlC,MAAM,CAAC,KAAK,EAAE,QAAQ;IAW7B;;;OAGG;IACI,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAalD;;;OAGG;IACI,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAEzC,0EAA0E;IACnE,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAQ7D;;;;OAIG;IACI,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAE5C,8FAA8F;IACvF,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO;IAE5C;;;OAGG;IACI,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO;IAE3C,wDAAwD;IACjD,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO;IAEzC;;OAEG;IACI,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS;CAgB7E"}
@@ -33,7 +33,7 @@ class ViewRect {
33
33
  /** The bottommost side of this ViewRect. */
34
34
  get bottom() { return this._bottom; }
35
35
  set bottom(val) { this._set("_bottom", val); }
36
- /** True if this ViewRect has an area > 0. */
36
+ /** True if this ViewRect has an area <= 0. */
37
37
  get isNull() { return this.right <= this.left || this.bottom <= this.top; }
38
38
  /** True if `!isNull` */
39
39
  get isValid() { return !this.isNull; }
@@ -1 +1 @@
1
- {"version":3,"file":"ViewRect.js","sourceRoot":"","sources":["../../src/ViewRect.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\n* See LICENSE.md in the project root for license terms and full copyright notice.\n*--------------------------------------------------------------------------------------------*/\n/** @packageDocumentation\n * @module Views\n */\n\nimport { LowAndHighXY, XAndY } from \"@itwin/core-geometry\";\n\n/** A rectangle in unsigned integer view coordinates with (0,0) corresponding to the top-left corner of the view.\n * Increasing **x** moves from left to right, and increasing **y** moves from top to bottom.\n * [[left]], [[top]], [[right]], and [[bottom]] are required to be non-negative integers; any negative inputs are treated as\n * zero and any non-integer inputs are rounded down to the nearest integer.\n * @public\n * @extensions\n */\nexport class ViewRect {\n private _left!: number;\n private _top!: number;\n private _right!: number;\n private _bottom!: number;\n\n private _set(key: \"_left\" | \"_right\" | \"_top\" | \"_bottom\", value: number): void {\n this[key] = Math.max(0, Math.floor(value));\n }\n\n /** Construct a new ViewRect. */\n public constructor(left = 0, top = 0, right = 0, bottom = 0) { this.init(left, top, right, bottom); }\n /** The leftmost side of this ViewRect. */\n public get left(): number { return this._left; }\n public set left(val: number) { this._set(\"_left\", val); }\n /** The topmost side of this ViewRect. */\n public get top(): number { return this._top; }\n public set top(val: number) { this._set(\"_top\", val); }\n /** The rightmost side of this ViewRect. */\n public get right(): number { return this._right; }\n public set right(val: number) { this._set(\"_right\", val); }\n /** The bottommost side of this ViewRect. */\n public get bottom(): number { return this._bottom; }\n public set bottom(val: number) { this._set(\"_bottom\", val); }\n /** True if this ViewRect has an area > 0. */\n public get isNull(): boolean { return this.right <= this.left || this.bottom <= this.top; }\n /** True if `!isNull` */\n public get isValid(): boolean { return !this.isNull; }\n /** The width (right-left) of this ViewRect. */\n public get width() { return this.right - this.left; }\n public set width(width: number) { this.right = this.left + width; }\n /** The height (bottom-top) of this ViewRect. */\n public get height() { return this.bottom - this.top; }\n public set height(height: number) { this.bottom = this.top + height; }\n /** The aspect ratio (width/height) of this ViewRect. */\n public get aspect() { return this.isNull ? 1.0 : this.width / this.height; }\n /** The area (width*height) of this ViewRect. */\n public get area() { return this.isNull ? 0 : this.width * this.height; }\n /** Initialize this ViewRect from its left/top/right/bottom parameters. */\n public init(left: number, top: number, right: number, bottom: number) {\n this.left = left;\n this.bottom = bottom, this.right = right;\n this.top = top;\n }\n\n /** Initialize this ViewRect from two points.\n * @param topLeft The top-left corner.\n * @param bottomRight The bottom-right corner.\n */\n public initFromPoints(topLeft: XAndY, bottomRight: XAndY): void { this.init(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y); }\n /** Initialize this ViewRect from a range.\n * @param input The Range to use. `input.low` defines the top-left and `input.high` defines the bottom-right.\n */\n public initFromRange(input: LowAndHighXY): void { this.initFromPoints(input.low, input.high); }\n /** Return true is this ViewRect is exactly equal to another ViewRect.\n * @param other The other ViewRect to compare\n */\n public equals(other: ViewRect): boolean { return this.left === other.left && this.right === other.right && this.bottom === other.bottom && this.top === other.top; }\n /** Initialize this ViewRect from another ViewRect. */\n public setFrom(other: ViewRect): void { this.init(other.left, other.top, other.right, other.bottom); }\n /** Duplicate this ViewRect.\n * @param result Optional ViewRect for result. If undefined, a new ViewRect is created.\n */\n public clone(result?: ViewRect): ViewRect {\n if (undefined !== result) {\n result.setFrom(this);\n return result;\n }\n return new ViewRect(this.left, this.top, this.right, this.bottom);\n }\n public extend(other: ViewRect) {\n if (this.left > other.left)\n this.left = other.left;\n if (this.top > other.top)\n this.top = other.top;\n if (this.right < other.right)\n this.right = other.right;\n if (this.bottom < other.bottom)\n this.bottom = other.bottom;\n }\n\n /** Inset this ViewRect by values in the x and y directions. Positive values make the ViewRect smaller, and negative values will make it larger.\n * @param deltaX The distance to inset the ViewRect in the x direction.\n * @param deltaY The distance to inset the ViewRect in the y direction.\n */\n public inset(deltaX: number, deltaY: number): void {\n deltaX = Math.floor(deltaX);\n deltaY = Math.floor(deltaY);\n if (this.width - 2 * deltaX <= 0 || this.height - 2 * deltaY <= 0) {\n this.init(0, 0, 0, 0);\n return;\n }\n this._left += deltaX;\n this._right -= deltaX;\n this._top += deltaY;\n this._bottom -= deltaY;\n }\n\n /** Inset this ViewRect by the same value in all directions.\n * @param offset The distance to inset this ViewRect. Positive values will make this ViewRect smaller and negative values will make it larger.\n * @note The inset operation can cause a previously valid ViewRect to become invalid.\n */\n public insetUniform(offset: number): void { this.inset(offset, offset); }\n\n /** Scale this ViewRect about its center by the supplied scale factors. */\n public scaleAboutCenter(xScale: number, yScale: number): void {\n const w = this.width;\n const h = this.height;\n const xDelta = (w - (w * xScale)) * 0.5;\n const yDelta = (h - (h * yScale)) * 0.5;\n this.inset(xDelta, yDelta);\n }\n\n /** Inset this ViewRect by a percentage of its current width.\n * @param percent The percentage of this ViewRect's width to inset in all directions.\n * @note The ViewRect will become smaller (or larger, if percent is negative) by `percent * width * 2` in each direction, since each side is moved by that distance.\n * @see [[inset]]\n */\n public insetByPercent(percent: number): void { this.insetUniform(this.width * percent); }\n\n /** Determine if this ViewRect is entirely contained within the bounds of another ViewRect. */\n public isContained(other: ViewRect): boolean { return this.left >= other.left && this.right <= other.right && this.bottom <= other.bottom && this.top >= other.top; }\n\n /** Return true if the supplied point is contained in this ViewRect.\n * @param point The point to test.\n * @note if the point is exactly on the left or top edges, this method returns true. If the point is exactly on the right or bottom edge, it returns false.\n */\n public containsPoint(point: XAndY): boolean { return point.x >= this.left && point.x < this.right && point.y >= this.top && point.y < this.bottom; }\n\n /** Determine whether this ViewRect overlaps another. */\n public overlaps(other: ViewRect): boolean { return this.left <= other.right && this.top <= other.bottom && this.right >= other.left && this.bottom >= other.top; }\n\n /** Return a ViewRect that is the overlap (intersection) of this ViewRect and another ViewRect.\n * If the two ViewRects are equal, their value is the result. Otherwise, the result will always be smaller than either of them.\n */\n public computeOverlap(other: ViewRect, out?: ViewRect): ViewRect | undefined {\n const maxOrgX = Math.max(this.left, other.left);\n const maxOrgY = Math.max(this.top, other.top);\n const minCrnX = Math.min(this.right, other.right);\n const minCrnY = Math.min(this.bottom, other.bottom);\n\n if (maxOrgX > minCrnX || maxOrgY > minCrnY)\n return undefined;\n\n const result = undefined !== out ? out : new ViewRect();\n result.left = maxOrgX;\n result.right = minCrnX;\n result.top = maxOrgY;\n result.bottom = minCrnY;\n return result;\n }\n}\n"]}
1
+ {"version":3,"file":"ViewRect.js","sourceRoot":"","sources":["../../src/ViewRect.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\n* See LICENSE.md in the project root for license terms and full copyright notice.\n*--------------------------------------------------------------------------------------------*/\n/** @packageDocumentation\n * @module Views\n */\n\nimport { LowAndHighXY, XAndY } from \"@itwin/core-geometry\";\n\n/** A rectangle in unsigned integer view coordinates with (0,0) corresponding to the top-left corner of the view.\n * Increasing **x** moves from left to right, and increasing **y** moves from top to bottom.\n * [[left]], [[top]], [[right]], and [[bottom]] are required to be non-negative integers; any negative inputs are treated as\n * zero and any non-integer inputs are rounded down to the nearest integer.\n * @public\n * @extensions\n */\nexport class ViewRect {\n private _left!: number;\n private _top!: number;\n private _right!: number;\n private _bottom!: number;\n\n private _set(key: \"_left\" | \"_right\" | \"_top\" | \"_bottom\", value: number): void {\n this[key] = Math.max(0, Math.floor(value));\n }\n\n /** Construct a new ViewRect. */\n public constructor(left = 0, top = 0, right = 0, bottom = 0) { this.init(left, top, right, bottom); }\n /** The leftmost side of this ViewRect. */\n public get left(): number { return this._left; }\n public set left(val: number) { this._set(\"_left\", val); }\n /** The topmost side of this ViewRect. */\n public get top(): number { return this._top; }\n public set top(val: number) { this._set(\"_top\", val); }\n /** The rightmost side of this ViewRect. */\n public get right(): number { return this._right; }\n public set right(val: number) { this._set(\"_right\", val); }\n /** The bottommost side of this ViewRect. */\n public get bottom(): number { return this._bottom; }\n public set bottom(val: number) { this._set(\"_bottom\", val); }\n /** True if this ViewRect has an area <= 0. */\n public get isNull(): boolean { return this.right <= this.left || this.bottom <= this.top; }\n /** True if `!isNull` */\n public get isValid(): boolean { return !this.isNull; }\n /** The width (right-left) of this ViewRect. */\n public get width() { return this.right - this.left; }\n public set width(width: number) { this.right = this.left + width; }\n /** The height (bottom-top) of this ViewRect. */\n public get height() { return this.bottom - this.top; }\n public set height(height: number) { this.bottom = this.top + height; }\n /** The aspect ratio (width/height) of this ViewRect. */\n public get aspect() { return this.isNull ? 1.0 : this.width / this.height; }\n /** The area (width*height) of this ViewRect. */\n public get area() { return this.isNull ? 0 : this.width * this.height; }\n /** Initialize this ViewRect from its left/top/right/bottom parameters. */\n public init(left: number, top: number, right: number, bottom: number) {\n this.left = left;\n this.bottom = bottom, this.right = right;\n this.top = top;\n }\n\n /** Initialize this ViewRect from two points.\n * @param topLeft The top-left corner.\n * @param bottomRight The bottom-right corner.\n */\n public initFromPoints(topLeft: XAndY, bottomRight: XAndY): void { this.init(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y); }\n /** Initialize this ViewRect from a range.\n * @param input The Range to use. `input.low` defines the top-left and `input.high` defines the bottom-right.\n */\n public initFromRange(input: LowAndHighXY): void { this.initFromPoints(input.low, input.high); }\n /** Return true is this ViewRect is exactly equal to another ViewRect.\n * @param other The other ViewRect to compare\n */\n public equals(other: ViewRect): boolean { return this.left === other.left && this.right === other.right && this.bottom === other.bottom && this.top === other.top; }\n /** Initialize this ViewRect from another ViewRect. */\n public setFrom(other: ViewRect): void { this.init(other.left, other.top, other.right, other.bottom); }\n /** Duplicate this ViewRect.\n * @param result Optional ViewRect for result. If undefined, a new ViewRect is created.\n */\n public clone(result?: ViewRect): ViewRect {\n if (undefined !== result) {\n result.setFrom(this);\n return result;\n }\n return new ViewRect(this.left, this.top, this.right, this.bottom);\n }\n public extend(other: ViewRect) {\n if (this.left > other.left)\n this.left = other.left;\n if (this.top > other.top)\n this.top = other.top;\n if (this.right < other.right)\n this.right = other.right;\n if (this.bottom < other.bottom)\n this.bottom = other.bottom;\n }\n\n /** Inset this ViewRect by values in the x and y directions. Positive values make the ViewRect smaller, and negative values will make it larger.\n * @param deltaX The distance to inset the ViewRect in the x direction.\n * @param deltaY The distance to inset the ViewRect in the y direction.\n */\n public inset(deltaX: number, deltaY: number): void {\n deltaX = Math.floor(deltaX);\n deltaY = Math.floor(deltaY);\n if (this.width - 2 * deltaX <= 0 || this.height - 2 * deltaY <= 0) {\n this.init(0, 0, 0, 0);\n return;\n }\n this._left += deltaX;\n this._right -= deltaX;\n this._top += deltaY;\n this._bottom -= deltaY;\n }\n\n /** Inset this ViewRect by the same value in all directions.\n * @param offset The distance to inset this ViewRect. Positive values will make this ViewRect smaller and negative values will make it larger.\n * @note The inset operation can cause a previously valid ViewRect to become invalid.\n */\n public insetUniform(offset: number): void { this.inset(offset, offset); }\n\n /** Scale this ViewRect about its center by the supplied scale factors. */\n public scaleAboutCenter(xScale: number, yScale: number): void {\n const w = this.width;\n const h = this.height;\n const xDelta = (w - (w * xScale)) * 0.5;\n const yDelta = (h - (h * yScale)) * 0.5;\n this.inset(xDelta, yDelta);\n }\n\n /** Inset this ViewRect by a percentage of its current width.\n * @param percent The percentage of this ViewRect's width to inset in all directions.\n * @note The ViewRect will become smaller (or larger, if percent is negative) by `percent * width * 2` in each direction, since each side is moved by that distance.\n * @see [[inset]]\n */\n public insetByPercent(percent: number): void { this.insetUniform(this.width * percent); }\n\n /** Determine if this ViewRect is entirely contained within the bounds of another ViewRect. */\n public isContained(other: ViewRect): boolean { return this.left >= other.left && this.right <= other.right && this.bottom <= other.bottom && this.top >= other.top; }\n\n /** Return true if the supplied point is contained in this ViewRect.\n * @param point The point to test.\n * @note if the point is exactly on the left or top edges, this method returns true. If the point is exactly on the right or bottom edge, it returns false.\n */\n public containsPoint(point: XAndY): boolean { return point.x >= this.left && point.x < this.right && point.y >= this.top && point.y < this.bottom; }\n\n /** Determine whether this ViewRect overlaps another. */\n public overlaps(other: ViewRect): boolean { return this.left <= other.right && this.top <= other.bottom && this.right >= other.left && this.bottom >= other.top; }\n\n /** Return a ViewRect that is the overlap (intersection) of this ViewRect and another ViewRect.\n * If the two ViewRects are equal, their value is the result. Otherwise, the result will always be smaller than either of them.\n */\n public computeOverlap(other: ViewRect, out?: ViewRect): ViewRect | undefined {\n const maxOrgX = Math.max(this.left, other.left);\n const maxOrgY = Math.max(this.top, other.top);\n const minCrnX = Math.min(this.right, other.right);\n const minCrnY = Math.min(this.bottom, other.bottom);\n\n if (maxOrgX > minCrnX || maxOrgY > minCrnY)\n return undefined;\n\n const result = undefined !== out ? out : new ViewRect();\n result.left = maxOrgX;\n result.right = minCrnX;\n result.top = maxOrgY;\n result.bottom = minCrnY;\n return result;\n }\n}\n"]}
@@ -2001,7 +2001,7 @@ class Viewport {
2001
2001
  */
2002
2002
  readPixels(rect, selector, receiver, excludeNonLocatable = false) {
2003
2003
  const viewRect = this.viewRect;
2004
- if (this.isDisposed || !rect.isContained(viewRect))
2004
+ if (this.isDisposed || rect.isNull || !rect.isContained(viewRect))
2005
2005
  receiver(undefined);
2006
2006
  else
2007
2007
  this.target.readPixels(rect, selector, receiver, excludeNonLocatable);