@itwin/core-frontend
Version:
iTwin.js frontend components
183 lines • 9.03 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 Rendering
*/
import { Range3d, Transform } from "@itwin/core-geometry";
import { PackedFeatureTable, QPoint3dList } from "@itwin/core-common";
import { GraphicBuilder } from "../../render/GraphicBuilder";
import { GraphicBranch } from "../../render/GraphicBranch";
import { assert } from "@itwin/core-bentley";
import { _accumulator, _implementationProhibited } from "../../common/internal/Symbols";
import { createGraphicTemplate } from "../../internal/render/GraphicTemplateImpl";
export class PrimitiveBuilder extends GraphicBuilder {
[_implementationProhibited] = undefined;
system;
primitives = [];
_options;
_viewIndependentOrigin;
constructor(system, options) {
super(options);
this.system = system;
this._options = options;
this._viewIndependentOrigin = options.viewIndependentOrigin?.clone();
}
finish() {
const template = this.toTemplate(false);
const graphic = this.system.createGraphicFromTemplate({ template });
return graphic ?? this.system.createGraphicList([]);
}
finishTemplate() {
return this.toTemplate(true);
}
toTemplate(noDispose) {
const accum = this[_accumulator];
const tolerance = this.computeTolerance(accum);
const result = this.saveToTemplate(this, tolerance, this.pickable, noDispose);
accum.clear();
return result ?? createGraphicTemplate({ nodes: [], noDispose });
}
computeTolerance(accum) {
return this._computeChordTolerance({
graphic: this,
computeRange: () => accum.geometries.computeRange(),
});
}
resolveGradient(gradient) {
return this.system.getGradientTexture(gradient, this.iModel);
}
/**
* Populate a list of Graphic objects from the accumulated Geometry objects.
* removed ViewContext
*/
saveToGraphicList(graphics, options, tolerance, pickable) {
const meshes = this[_accumulator].toMeshes(options, tolerance, pickable);
if (0 === meshes.length)
return undefined;
// If the meshes contain quantized positions, they are all quantized to the same range. If that range is small relative to the distance
// from the origin, quantization errors can produce display artifacts. Remove the translation from the quantization parameters and apply
// it in the transform instead.
//
// If the positions are not quantized, they have already been transformed to be relative to the center of the meshes' range.
// Apply the inverse translation to put them back into model space.
const branch = new GraphicBranch(true);
let transformOrigin;
let meshesRangeOffset = false;
for (const mesh of meshes) {
const verts = mesh.points;
if (branch.isEmpty) {
if (verts instanceof QPoint3dList) {
transformOrigin = verts.params.origin.clone();
verts.params.origin.setZero();
}
else {
transformOrigin = verts.range.center;
// In this case we need to modify the qOrigin of the graphic that will get created later since we have translated the origin.
// We can't modify it directly, but if we temporarily modify the range of the mesh used to create it the qOrigin will get created properly.
// Range is shared (not cloned) by all meshes and the mesh list itself, so modifying the range of the meshlist will modify it for all meshes.
// We will then later add this offset back to the range once all of the graphics have been created because it is needed unmodified for locate.
if (!meshesRangeOffset) {
meshes.range?.low.subtractInPlace(transformOrigin);
meshes.range?.high.subtractInPlace(transformOrigin);
meshesRangeOffset = true;
}
}
}
else {
assert(undefined !== transformOrigin);
if (verts instanceof QPoint3dList) {
assert(transformOrigin.isAlmostEqual(verts.params.origin));
verts.params.origin.setZero();
}
else {
assert(verts.range.center.isAlmostZero);
}
}
const graphic = this.system.createMeshGraphics(mesh, this._viewIndependentOrigin);
if (graphic)
branch.add(graphic);
}
if (!branch.isEmpty) {
assert(undefined !== transformOrigin);
const transform = Transform.createTranslation(transformOrigin);
graphics.push(this.system.createBranch(branch, transform));
if (meshesRangeOffset) { // restore the meshes range that we modified earlier.
meshes.range?.low.addInPlace(transformOrigin);
meshes.range?.high.addInPlace(transformOrigin);
}
}
return meshes;
}
saveToTemplate(options, tolerance, pickable, noDispose) {
const meshes = this[_accumulator].toMeshes(options, tolerance, pickable);
if (0 === meshes.length)
return undefined;
// If the meshes contain quantized positions, they are all quantized to the same range. If that range is small relative to the distance
// from the origin, quantization errors can produce display artifacts. Remove the translation from the quantization parameters and apply
// it in the transform instead.
//
// If the positions are not quantized, they have already been transformed to be relative to the center of the meshes' range.
// Apply the inverse translation to put them back into model space.
let transformOrigin;
let meshesRangeOffset = false;
const geometry = [];
for (const mesh of meshes) {
const verts = mesh.points;
if (!transformOrigin) {
if (verts instanceof QPoint3dList) {
transformOrigin = verts.params.origin.clone();
verts.params.origin.setZero();
}
else {
transformOrigin = verts.range.center;
// In this case we need to modify the qOrigin of the graphic that will get created later since we have translated the origin.
// We can't modify it directly, but if we temporarily modify the range of the mesh used to create it the qOrigin will get created properly.
// Range is shared (not cloned) by all meshes and the mesh list itself, so modifying the range of the meshlist will modify it for all meshes.
// We will then later add this offset back to the range once all of the graphics have been created because it is needed unmodified for locate.
if (!meshesRangeOffset) {
meshes.range?.low.subtractInPlace(transformOrigin);
meshes.range?.high.subtractInPlace(transformOrigin);
meshesRangeOffset = true;
}
}
}
else {
if (verts instanceof QPoint3dList) {
assert(transformOrigin.isAlmostEqual(verts.params.origin));
verts.params.origin.setZero();
}
else {
assert(verts.range.center.isAlmostZero);
}
}
const geom = this.system.createGeometryFromMesh(mesh, this._viewIndependentOrigin);
if (geom) {
geometry.push(geom);
}
}
let transform;
if (transformOrigin) {
transform = Transform.createTranslation(transformOrigin);
if (meshesRangeOffset) { // restore the meshes range that we modified earlier.
meshes.range?.low.addInPlace(transformOrigin);
meshes.range?.high.addInPlace(transformOrigin);
}
}
let batch;
if (meshes.features?.anyDefined) {
batch = {
featureTable: PackedFeatureTable.pack(meshes.features),
range: meshes.range ?? new Range3d(),
options: this._options.pickable,
};
}
return createGraphicTemplate({
batch,
nodes: [{ geometry, transform }],
noDispose,
});
}
}
//# sourceMappingURL=PrimitiveBuilder.js.map