UNPKG

@itwin/core-frontend

Version:
408 lines • 20.3 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * 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 */ Object.defineProperty(exports, "__esModule", { value: true }); exports.GraphicAssembler = void 0; const core_bentley_1 = require("@itwin/core-bentley"); const core_geometry_1 = require("@itwin/core-geometry"); const core_common_1 = require("@itwin/core-common"); const Symbols_1 = require("../internal/Symbols"); const GraphicType_1 = require("./GraphicType"); const GeometryAccumulator_1 = require("../internal/render/GeometryAccumulator"); const DisplayParams_1 = require("../internal/render/DisplayParams"); ; /** Provides methods for assembling geometric primitives and symbology into a graphical representation. * Two concrete implementations are provided: * - [[GraphicBuilder]], for creating [[RenderGraphic]]s directly; and * - [[GraphicDescriptionBuilder]], for creating a [[GraphicDescription]] from which a [[RenderGraphic]] can be produced. * * GraphicBuilder can only be used on the main JavaScript thread, so its use should be reserved for relatively simple, quick-to-produce graphics like [[Decorations]]. * GraphicDescriptionBuilder is designed for use in a [Worker](https://developer.mozilla.org/en-US/docs/Web/API/Worker). It can be used to assemble more * complex graphics without blocking the main thread. * * @note Most of the methods which add geometry to the builder take ownership of their inputs rather than cloning them. * So, for example, if you pass an array of points to [[addLineString]], you should not subsequently modify that array. * @public * @extensions */ class GraphicAssembler { /** @internal */ [Symbols_1._accumulator]; _graphicParams = new core_common_1.GraphicParams(); /** The local-to-world transform in which the builder's geometry is to be defined. */ placement; /** The type of graphic to be produced. */ type; /** If the graphic is to be interactive, specifies its Id and other options. */ pickable; /** If true, the order in which geometry is added to the builder is preserved. * This enables overlay and background graphics, which draw without using the depth buffer, to specify which geometry display in front of others when * they overlap. * For example, to draw an overlay containing a red shape with a white outline, you would add the shape to the GraphicAssember first, followed by its outline, * to ensure the outline draws on top of the shape. * This incurs a performance penalty due to the increased number of draw calls, and is never useful for graphics that draw using the depth buffer. */ preserveOrder; /** If true, normal vectors will be generated for surfaces, allowing 3d geometry to receive lighting and reduce z-fighting. */ wantNormals; /** If true, edges are generated for surfaces, to be drawn if edge display is enabled for the view in which the graphic is drawn. */ wantEdges; /** @alpha */ analysisStyle; /** @internal */ constructor(options) { this.placement = options.placement?.clone() ?? core_geometry_1.Transform.createIdentity(); this.type = options.type; this.pickable = options.pickable; this.wantEdges = options.wantEdges; this.preserveOrder = options.preserveOrder; this.wantNormals = options.wantNormals; this.analysisStyle = options.analysisStyle; this[Symbols_1._accumulator] = new GeometryAccumulator_1.GeometryAccumulator({ analysisStyleDisplacement: this.analysisStyle?.displacement, }); if (this.pickable) { this.activateFeature(new core_common_1.Feature(this.pickable.id, this.pickable.subCategoryId, this.pickable.geometryClass)); } } /** Whether the builder's geometry is defined in [[CoordSystem.View]] coordinates. * Only graphics of type [[GraphicType.ViewBackground]] or [[GraphicType.ViewOverlay]] are defined in view coordinates. * @see [[isWorldCoordinates]]. */ get isViewCoordinates() { return this.type === GraphicType_1.GraphicType.ViewBackground || this.type === GraphicType_1.GraphicType.ViewOverlay; } /** Whether the builder's geometry is defined in [[CoordSystem.World]] coordinates - the inverse of [[isViewCoordinates]]. */ get isWorldCoordinates() { return !this.isViewCoordinates; } /** True if the builder produces a graphic of [[GraphicType.Scene]]. */ get isSceneGraphic() { return this.type === GraphicType_1.GraphicType.Scene; } /** True if the builder produces a graphic of [[GraphicType.ViewBackground]]. */ get isViewBackground() { return this.type === GraphicType_1.GraphicType.ViewBackground; } /** True if the builder produces a graphic of [[GraphicType.WorldOverlay]] or [[GraphicType.ViewOerlay]]. */ get isOverlay() { return this.type === GraphicType_1.GraphicType.ViewOverlay || this.type === GraphicType_1.GraphicType.WorldOverlay; } /** Sets the current active symbology for this builder. Any new geometry subsequently added to the builder will be drawn using the specified symbology. * @param graphicParams The symbology to apply to subsequent geometry. * @see [[setSymbology]] for a convenient way to set common symbology options. */ activateGraphicParams(graphicParams) { graphicParams.clone(this._graphicParams); } /** Change the [Feature]($common) to be associated with subsequently-added geometry. This permits multiple features to be batched together into a single graphic * for more efficient rendering. * @note This method has no effect if [[pickable]] is not defined. */ activateFeature(feature) { (0, core_bentley_1.assert)(undefined !== this.pickable, "GraphicAssembler.activateFeature has no effect if PickableGraphicOptions were not supplied"); if (this.pickable) { this[Symbols_1._accumulator].currentFeature = feature; } } /** Change the pickable Id to be associated with subsequently-added geometry. This permits multiple pickable objects to be batched together into a single graphic * for more efficient rendering. This method calls [[activateFeature]], using the subcategory Id and [GeometryClass]($common) specified in [[pickable]] * at construction, if any. * @note This method has no effect if [[pickable]] is not defined. */ activatePickableId(id) { const pick = this.pickable; this.activateFeature(new core_common_1.Feature(id, pick?.subCategoryId, pick?.geometryClass)); } /** * Appends a 3d line string to the builder. * @param points Array of vertices in the line string. */ addLineString(points) { if (2 === points.length && points[0].isAlmostEqual(points[1])) this[Symbols_1._accumulator].addPointString(points, this.getLinearDisplayParams(), this.placement); else this[Symbols_1._accumulator].addLineString(points, this.getLinearDisplayParams(), this.placement); } /** * Appends a 2d line string to the builder. * @param points Array of vertices in the line string. * @param zDepth Z value in local coordinates to use for each point. */ addLineString2d(points, zDepth) { const pts3d = copy2dTo3d(points, zDepth); this.addLineString(pts3d); } /** * Appends a 3d point string to the builder. The points are drawn disconnected, with a diameter in pixels defined by the builder's active [[GraphicParams.rasterWidth]]. * @param points Array of vertices in the point string. */ addPointString(points) { this[Symbols_1._accumulator].addPointString(points, this.getLinearDisplayParams(), this.placement); } /** * Appends a 2d point string to the builder. The points are drawn disconnected, with a diameter in pixels defined by the builder's active [[GraphicParams.rasterWidth]]. * @param points Array of vertices in the point string. * @param zDepth Z value in local coordinates to use for each point. */ addPointString2d(points, zDepth) { const pts3d = copy2dTo3d(points, zDepth); this.addPointString(pts3d); } /** * Appends a closed 3d planar region to the builder. * @param points Array of vertices of the shape. */ addShape(points) { const loop = core_geometry_1.Loop.create(core_geometry_1.LineString3d.create(points)); this[Symbols_1._accumulator].addLoop(loop, this.getMeshDisplayParams(), this.placement, false); } /** * Appends a closed 2d region to the builder. * @param points Array of vertices of the shape. * @param zDepth Z value in local coordinates to use for each point. */ addShape2d(points, zDepth) { const pts3d = copy2dTo3d(points, zDepth); this.addShape(pts3d); } /** * Appends a 3d open arc or closed ellipse to the builder. * @param arc Description of the arc or ellipse. * @param isEllipse If true, and if the arc defines a full sweep, then draw as a closed ellipse instead of an arc. * @param filled If true, and isEllipse is also true, then draw ellipse filled. */ addArc(ellipse, isEllipse, filled) { let curve; if (isEllipse || filled) { curve = core_geometry_1.Loop.create(ellipse); } else { curve = core_geometry_1.Path.create(ellipse); } if (filled && !isEllipse && !ellipse.sweep.isFullCircle) { const gapSegment = core_geometry_1.LineSegment3d.create(ellipse.startPoint(), ellipse.endPoint()); curve.children.push(gapSegment); } const displayParams = curve.isAnyRegionType ? this.getMeshDisplayParams() : this.getLinearDisplayParams(); if (curve instanceof core_geometry_1.Loop) this[Symbols_1._accumulator].addLoop(curve, displayParams, this.placement, false); else this[Symbols_1._accumulator].addPath(curve, displayParams, this.placement, false); } /** * Appends a 2d open arc or closed ellipse to the builder. * @param arc Description of the arc or ellipse. * @param isEllipse If true, and if the arc defines a full sweep, then draw as a closed ellipse instead of an arc. * @param filled If true, and isEllipse is also true, then draw ellipse filled. * @param zDepth Z value in local coordinates to use for each point in the arc or ellipse. */ addArc2d(ellipse, isEllipse, filled, zDepth) { if (0.0 === zDepth) { this.addArc(ellipse, isEllipse, filled); } else { const ell = ellipse; ell.center.z = zDepth; this.addArc(ell, isEllipse, filled); } } /** Append a 3d open path to the builder. */ addPath(path) { this[Symbols_1._accumulator].addPath(path, this.getLinearDisplayParams(), this.placement, false); } /** Append a 3d planar region to the builder. */ addLoop(loop) { this[Symbols_1._accumulator].addLoop(loop, this.getMeshDisplayParams(), this.placement, false); } /** Append a [CurvePrimitive]($core-geometry) to the builder. */ addCurvePrimitive(curve) { switch (curve.curvePrimitiveType) { case "lineString": this.addLineString(curve.points); break; case "lineSegment": this.addLineString([curve.startPoint(), curve.endPoint()]); break; case "arc": this.addArc(curve, false, false); break; default: const path = new core_geometry_1.Path(); if (path.tryAddChild(curve)) this.addPath(path); break; } } /** Append a mesh to the builder. * @param meshData Describes the mesh * @param _filled If the mesh describes a planar region, indicates whether its interior area should be drawn with fill in [[RenderMode.Wireframe]]. */ addPolyface(meshData, _filled = false) { this[Symbols_1._accumulator].addPolyface(meshData, this.getMeshDisplayParams(), this.placement); } /** Append a solid primitive to the builder. */ addSolidPrimitive(primitive) { this[Symbols_1._accumulator].addSolidPrimitive(primitive, this.getMeshDisplayParams(), this.placement); } /** Append any primitive to the builder. * @param primitive The graphic primitive to append. */ addPrimitive(primitive) { switch (primitive.type) { case "linestring": this.addLineString(primitive.points); break; case "linestring2d": this.addLineString2d(primitive.points, primitive.zDepth); break; case "pointstring": this.addPointString(primitive.points); break; case "pointstring2d": this.addPointString2d(primitive.points, primitive.zDepth); break; case "shape": this.addShape(primitive.points); break; case "shape2d": this.addShape2d(primitive.points, primitive.zDepth); break; case "arc": this.addArc(primitive.arc, true === primitive.isEllipse, true === primitive.filled); break; case "arc2d": this.addArc2d(primitive.arc, true === primitive.isEllipse, true === primitive.filled, primitive.zDepth); break; case "path": this.addPath(primitive.path); break; case "loop": this.addLoop(primitive.loop); break; case "polyface": this.addPolyface(primitive.polyface, true === primitive.filled); break; case "solidPrimitive": this.addSolidPrimitive(primitive.solidPrimitive); break; } } /** Add a box representing a volume of space. Typically used for debugging purposes. * @param range The volume of space. * @param solid If true, a [[Box]] solid primitive will be added; otherwise, a wireframe outline of the box will be added. */ addRangeBox(range, solid = false) { if (!solid) { this.addFrustum(core_common_1.Frustum.fromRange(range)); return; } const box = core_geometry_1.Box.createRange(range, true); if (box) this.addSolidPrimitive(box); } /** Add Frustum edges. Useful for debugging. */ addFrustum(frustum) { this.addRangeBoxFromCorners(frustum.points); } /** Add Frustum sides. Useful for debugging. */ addFrustumSides(frustum) { this.addRangeBoxSidesFromCorners(frustum.points); } /** Add range edges from corner points */ addRangeBoxFromCorners(p) { this.addLineString([ p[core_common_1.Npc.LeftBottomFront], p[core_common_1.Npc.LeftTopFront], p[core_common_1.Npc.RightTopFront], p[core_common_1.Npc.RightBottomFront], p[core_common_1.Npc.RightBottomRear], p[core_common_1.Npc.RightTopRear], p[core_common_1.Npc.LeftTopRear], p[core_common_1.Npc.LeftBottomRear], p[core_common_1.Npc.LeftBottomFront].clone(), p[core_common_1.Npc.RightBottomFront].clone(), ]); this.addLineString([p[core_common_1.Npc.LeftTopFront].clone(), p[core_common_1.Npc.LeftTopRear].clone()]); this.addLineString([p[core_common_1.Npc.RightTopFront].clone(), p[core_common_1.Npc.RightTopRear].clone()]); this.addLineString([p[core_common_1.Npc.LeftBottomRear].clone(), p[core_common_1.Npc.RightBottomRear].clone()]); } /** Add range sides from corner points */ addRangeBoxSidesFromCorners(p) { this.addShape([ p[core_common_1.Npc.LeftBottomFront].clone(), p[core_common_1.Npc.LeftTopFront].clone(), p[core_common_1.Npc.RightTopFront].clone(), p[core_common_1.Npc.RightBottomFront].clone(), p[core_common_1.Npc.LeftBottomFront].clone() ]); this.addShape([ p[core_common_1.Npc.RightTopRear].clone(), p[core_common_1.Npc.LeftTopRear].clone(), p[core_common_1.Npc.LeftBottomRear].clone(), p[core_common_1.Npc.RightBottomRear].clone(), p[core_common_1.Npc.RightTopRear].clone() ]); this.addShape([ p[core_common_1.Npc.RightTopRear].clone(), p[core_common_1.Npc.LeftTopRear].clone(), p[core_common_1.Npc.LeftTopFront].clone(), p[core_common_1.Npc.RightTopFront].clone(), p[core_common_1.Npc.RightTopRear].clone() ]); this.addShape([ p[core_common_1.Npc.RightTopRear].clone(), p[core_common_1.Npc.RightBottomRear].clone(), p[core_common_1.Npc.RightBottomFront].clone(), p[core_common_1.Npc.RightTopFront].clone(), p[core_common_1.Npc.RightTopRear].clone() ]); this.addShape([ p[core_common_1.Npc.LeftBottomRear].clone(), p[core_common_1.Npc.RightBottomRear].clone(), p[core_common_1.Npc.RightBottomFront].clone(), p[core_common_1.Npc.LeftBottomFront].clone(), p[core_common_1.Npc.LeftBottomRear].clone() ]); this.addShape([ p[core_common_1.Npc.LeftBottomRear].clone(), p[core_common_1.Npc.LeftTopRear].clone(), p[core_common_1.Npc.LeftTopFront].clone(), p[core_common_1.Npc.LeftBottomFront].clone(), p[core_common_1.Npc.LeftBottomRear].clone() ]); } /** Sets the current active symbology for this builder. Any new geometry subsequently added will be drawn using the specified symbology. * @param lineColor The color in which to draw lines. * @param fillColor The color in which to draw filled regions. * @param lineWidth The width in pixels to draw lines. The renderer will clamp this value to an integer in the range [1, 32]. * @param linePixels The pixel pattern in which to draw lines. * @see [[activateGraphicParams]] for additional symbology options. */ setSymbology(lineColor, fillColor, lineWidth, linePixels = core_common_1.LinePixels.Solid) { this.activateGraphicParams(core_common_1.GraphicParams.fromSymbology(lineColor, fillColor, lineWidth, linePixels)); } /** Set the current active symbology for this builder to be a blanking fill before adding a planar region. * A planar region drawn with blanking fill renders behind other geometry in the same graphic. * Blanking fill is not affected by the fill [[ViewFlags]] being disabled. * An example would be to add a line to a graphic containing a shape with blanking fill so that the line is always shown in front of the fill. * @param fillColor The color in which to draw filled regions. */ setBlankingFill(fillColor) { this.activateGraphicParams(core_common_1.GraphicParams.fromBlankingFill(fillColor)); } getMeshDisplayParams() { return DisplayParams_1.DisplayParams.createForMesh(this._graphicParams, !this.wantNormals, (grad) => this.resolveGradient(grad)); } getLinearDisplayParams() { return DisplayParams_1.DisplayParams.createForLinear(this._graphicParams); } add(geom) { this[Symbols_1._accumulator].addGeometry(geom); } } exports.GraphicAssembler = GraphicAssembler; function copy2dTo3d(pts2d, depth) { const pts3d = []; for (const point of pts2d) pts3d.push(core_geometry_1.Point3d.create(point.x, point.y, depth)); return pts3d; } //# sourceMappingURL=GraphicAssembler.js.map