@itwin/core-backend
Version:
iTwin.js backend components
307 lines • 13.7 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module ExportGraphics
*/
import { assert } from "@itwin/core-bentley";
import { IndexedPolyface, PolyfaceData } from "@itwin/core-geometry";
/** Provides utility functions for working with data generated by [IModelDb.exportGraphics]($core-backend)
* @public
*/
export var ExportGraphics;
(function (ExportGraphics) {
/** Test if ExportPartDisplayInfos have exactly the same values.
* @public
*/
function arePartDisplayInfosEqual(lhs, rhs) {
if (lhs.categoryId !== rhs.categoryId)
return false;
if (lhs.subCategoryId !== rhs.subCategoryId)
return false;
if (lhs.materialId !== rhs.materialId)
return false;
if (lhs.elmTransparency !== rhs.elmTransparency)
return false;
if (lhs.lineColor !== rhs.lineColor)
return false;
return true;
}
ExportGraphics.arePartDisplayInfosEqual = arePartDisplayInfosEqual;
/**
* Convert an ExportGraphicsMesh to an IndexedPolyface usable by the geometry API.
* @note The resulting IndexedPolyface may have duplicate points, normals and params. If problematic, call [PolyfaceData.compress]($core-geometry)
* @public
*/
function convertToIndexedPolyface(mesh) {
const polyface = IndexedPolyface.create(true, true, false, mesh.isTwoSided);
const p = mesh.points;
for (let i = 0; i < p.length; i += 3)
polyface.data.point.pushXYZ(p[i], p[i + 1], p[i + 2]);
const n = mesh.normals;
assert(undefined !== polyface.data.normal);
for (let i = 0; i < n.length; i += 3)
polyface.data.normal.pushXYZ(n[i], n[i + 1], n[i + 2]);
const uv = mesh.params;
assert(undefined !== polyface.data.param);
for (let i = 0; i < uv.length; i += 2)
polyface.data.param.pushXY(uv[i], uv[i + 1]);
const indices = mesh.indices;
const addIndex = (idx) => {
polyface.addPointIndex(idx, true);
polyface.addNormalIndex(idx);
polyface.addParamIndex(idx);
};
for (let i = 0; i < indices.length; i += 3) {
addIndex(indices[i]);
addIndex(indices[i + 1]);
addIndex(indices[i + 2]);
polyface.terminateFacet(false);
}
return polyface;
}
ExportGraphics.convertToIndexedPolyface = convertToIndexedPolyface;
})(ExportGraphics || (ExportGraphics = {}));
/**
* * Iterator to walk the facets of an ExportGraphicsMesh and present them to the world as if visiting a Polyface.
* * Because the ExportGraphicsMesh has limited data:
* * There is no auxData in this visitor.
* * There is no color in this visitor.
* * All edgeVisible are true.
* @public
*/
export class ExportGraphicsMeshVisitor extends PolyfaceData {
_currentFacetIndex;
_nextFacetIndex;
_numWrap;
_polyface;
constructor(facets, numWrap) {
super(facets.normals.length > 0, facets.params.length > 0, false, facets.isTwoSided);
this._polyface = facets;
this._numWrap = numWrap;
this._nextFacetIndex = 0;
this._currentFacetIndex = -1;
this.reset();
}
/** Create a visitor for iterating the facets of `polyface`, with indicated number of points to be added to each facet to produce closed point arrays
* Typical wrap counts are:
* * 0 -- leave the point arrays with "missing final edge" (default)
* * 1 -- add point 0 as closure point
* * 2 -- add points 0 and 1 as closure and wrap point. This is useful when vertex visit requires two adjacent vectors, e.g. for cross products.
*/
static create(polyface, numWrap = 0) {
return new ExportGraphicsMeshVisitor(polyface, numWrap);
}
/** Restart the visitor at the first facet. */
reset() {
this.moveToReadIndex(0);
this._nextFacetIndex = 0; // so immediate moveToNextFacet stays here
}
/** Select a facet by simple index. */
moveToReadIndex(facetIndex) {
if (facetIndex < 0 || 2 + facetIndex * 3 >= this._polyface.indices.length)
return false;
if (this._currentFacetIndex !== facetIndex || 3 + this._numWrap !== this.point.length) {
this._currentFacetIndex = facetIndex;
this.point.length = 0;
this.pointIndex.length = 0;
this.edgeVisible.length = 0;
const sourcePoints = this._polyface.points;
const indices = this._polyface.indices;
const i0 = 3 * facetIndex;
for (let i = i0; i < i0 + 3; i++) {
const k = 3 * indices[i];
this.pointIndex.push(indices[i]);
this.point.pushXYZ(sourcePoints[k], sourcePoints[k + 1], sourcePoints[k + 2]);
this.edgeVisible.push(true);
}
for (let i = 0; i < this._numWrap; i++) {
this.point.pushFromGrowableXYZArray(this.point, i);
}
const sourceParams = this._polyface.params;
if (sourceParams.length > 0 && this.paramIndex && this.param) {
this.paramIndex.length = 0;
this.param.length = 0;
for (let i = i0; i < i0 + 3; i++) {
const k = 2 * indices[i];
this.paramIndex.push(indices[i]);
this.param.pushXY(sourceParams[k], sourceParams[k + 1]);
}
for (let i = 0; i < this._numWrap; i++) {
this.param.pushFromGrowableXYArray(this.param, i);
}
}
const sourceNormals = this._polyface.normals;
if (sourceNormals.length > 0 && this.normalIndex && this.normal) {
this.normalIndex.length = 0;
this.normal.length = 0;
for (let i = i0; i < i0 + 3; i++) {
const k = 3 * indices[i];
this.normalIndex.push(indices[i]);
this.normal.pushXYZ(sourceNormals[k], sourceNormals[k + 1], sourceNormals[k + 2]);
}
for (let i = 0; i < this._numWrap; i++) {
this.normal.pushFromGrowableXYZArray(this.normal, i);
}
}
}
this._nextFacetIndex = facetIndex + 1;
return true;
}
/** Load data for the next facet. */
moveToNextFacet() {
if (this._nextFacetIndex !== this._currentFacetIndex)
return this.moveToReadIndex(this._nextFacetIndex);
this._nextFacetIndex++;
return true;
}
/** Set the number of vertices to replicate in visitor arrays. */
setNumWrap(numWrap) {
this._numWrap = numWrap;
}
/** Return the index (in the client polyface) of the current facet */
currentReadIndex() {
return this._currentFacetIndex;
}
/** Return the point index of vertex i within the currently loaded facet */
clientPointIndex(i) {
return this.pointIndex[i];
}
/** Return the param index of vertex i within the currently loaded facet.
* Use the artificial paramIndex, which matches pointIndex.
*/
clientParamIndex(i) {
return this.paramIndex ? this.paramIndex[i] : -1;
}
/** Return the normal index of vertex i within the currently loaded facet.
* Use the artificial paramIndex, which matches pointIndex.
*/
clientNormalIndex(i) {
return this.normalIndex ? this.normalIndex[i] : -1;
}
/** Always returns -1 since we never have colors. */
clientColorIndex(_i) {
return -1;
}
/** Always returns -1 since we never have auxiliary data. */
clientAuxIndex(_i) {
return -1;
}
/** return the client polyface */
clientPolyface() {
return undefined;
}
/** clear the contents of all arrays. Use this along with transferDataFrom methods to build up new facets */
clearArrays() {
if (this.point !== undefined)
this.point.length = 0;
if (this.param !== undefined)
this.param.length = 0;
if (this.normal !== undefined)
this.normal.length = 0;
// ignore color and aux -- they never exist.
}
/** transfer interpolated data from the other visitor.
* * all data values are interpolated at `fraction` between `other` values at index0 and index1.
*/
pushInterpolatedDataFrom(other, index0, fraction, index1) {
this.point.pushInterpolatedFromGrowableXYZArray(other.point, index0, fraction, index1);
if (this.param && other.param && index0 < other.param.length && index1 < other.param.length)
this.param.pushInterpolatedFromGrowableXYArray(other.param, index0, fraction, index1);
if (this.normal && other.normal && index0 < other.normal.length && index1 < other.normal.length)
this.normal.pushInterpolatedFromGrowableXYZArray(other.normal, index0, fraction, index1);
}
/** transfer data from a specified index of the other visitor as new data in this visitor. */
pushDataFrom(other, index) {
this.point.pushFromGrowableXYZArray(other.point, index);
if (this.param && other.param && index < other.param.length)
this.param.pushFromGrowableXYArray(other.param, index);
if (this.normal && other.normal && index < other.normal.length)
this.normal.pushFromGrowableXYZArray(other.normal, index);
// ignore color and aux -- they never exist.
}
/** Return the number of facets this visitor is able to visit */
getVisitableFacetCount() {
return Math.floor(this._polyface.indices.length / 3);
}
/** Create a visitor for a subset of the facets visitable by the instance. */
createSubsetVisitor(facetIndices, numWrap = 0) {
return ExportGraphicsMeshSubsetVisitor.createSubsetVisitor(this._polyface, facetIndices, numWrap);
}
}
/**
* An `ExportGraphicsMeshSubsetVisitor` is an `ExportGraphicsMeshVisitor` which only visits a subset of the facets.
* * The subset is defined by an array of facet indices provided when this visitor is created.
* * Input indices (e.g., for `moveToReadIndex`) are understood to be indices into the subset array.
* @public
*/
export class ExportGraphicsMeshSubsetVisitor extends ExportGraphicsMeshVisitor {
_facetIndices;
_currentSubsetIndex; // index within _facetIndices
_nextSubsetIndex; // index within _facetIndices
constructor(polyface, facetIndices, numWrap) {
super(polyface, numWrap);
this._facetIndices = facetIndices.slice();
this._currentSubsetIndex = -1;
this._nextSubsetIndex = 0;
this.reset();
}
isValidSubsetIndex(index) {
return index >= 0 && index < this._facetIndices.length;
}
/**
* Create a visitor for iterating a subset of the facets of `polyface`.
* @param polyface reference to the client polyface, supplying facets
* @param facetIndices array of indices of facets in the client polyface to visit. This array is cloned.
* @param numWrap number of vertices replicated in the visitor arrays to facilitate simpler caller code. Default is zero.
*/
static createSubsetVisitor(polyface, facetIndices, numWrap = 0) {
return new ExportGraphicsMeshSubsetVisitor(polyface, facetIndices, numWrap);
}
/**
* Advance the iterator to a particular facet in the subset of client polyface facets.
* @param subsetIndex index into the subset array, not to be confused with the client facet index.
* @return whether the iterator was successfully moved.
*/
moveToReadIndex(subsetIndex) {
if (this.isValidSubsetIndex(subsetIndex)) {
this._currentSubsetIndex = subsetIndex;
this._nextSubsetIndex = subsetIndex + 1;
return super.moveToReadIndex(this._facetIndices[subsetIndex]);
}
return false;
}
/**
* Advance the iterator to the next facet in the subset of client polyface facets.
* @return whether the iterator was successfully moved.
*/
moveToNextFacet() {
if (this._nextSubsetIndex !== this._currentSubsetIndex)
return this.moveToReadIndex(this._nextSubsetIndex);
this._nextSubsetIndex++;
return true;
}
/** Restart the visitor at the first facet. */
reset() {
if (this._facetIndices) { // avoid crash during super ctor when we aren't yet initialized
this.moveToReadIndex(0);
this._nextSubsetIndex = 0; // so immediate moveToNextFacet stays here
}
}
/**
* Return the client polyface facet index (aka "readIndex") for the given subset index.
* @param subsetIndex index into the subset array. Default is the subset index of the currently visited facet.
* @return valid client polyface facet index, or `undefined` if invalid subset index.
*/
parentFacetIndex(subsetIndex) {
if (undefined === subsetIndex)
subsetIndex = this._currentSubsetIndex;
return this.isValidSubsetIndex(subsetIndex) ? this._facetIndices[subsetIndex] : undefined;
}
/** Return the number of facets this visitor is able to visit. */
getVisitableFacetCount() {
return this._facetIndices.length;
}
}
//# sourceMappingURL=ExportGraphics.js.map