UNPKG

@itwin/core-common

Version:

iTwin.js components common to frontend and backend

871 lines • 105 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ElementGeometry = exports.BRepGeometryOperation = exports.ElementGeometryOpcode = void 0; /*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module Geometry */ const flatbuffers_1 = require("flatbuffers"); const core_bentley_1 = require("@itwin/core-bentley"); const core_geometry_1 = require("@itwin/core-geometry"); const ElementGeometryFB_1 = require("./ElementGeometryFB"); const Base64EncodedString_1 = require("../Base64EncodedString"); const TextString_1 = require("./TextString"); const ColorDef_1 = require("../ColorDef"); const GeometryParams_1 = require("../GeometryParams"); const Gradient_1 = require("../Gradient"); const ThematicDisplay_1 = require("../ThematicDisplay"); const AreaPattern_1 = require("./AreaPattern"); const GeometryStream_1 = require("./GeometryStream"); const ImageGraphic_1 = require("./ImageGraphic"); const LineStyle_1 = require("./LineStyle"); const Placement_1 = require("./Placement"); const ElementProps_1 = require("../ElementProps"); /** Specifies the type of an entry in a geometry stream. * @see [[ElementGeometryDataEntry.opcode]]. * @public * @extensions */ var ElementGeometryOpcode; (function (ElementGeometryOpcode) { /** Local range of the next geometric primitive in the geometry stream. */ ElementGeometryOpcode[ElementGeometryOpcode["SubGraphicRange"] = 2] = "SubGraphicRange"; /** A reference to a [GeometryPart]($backend). */ ElementGeometryOpcode[ElementGeometryOpcode["PartReference"] = 3] = "PartReference"; /** Sets symbology for subsequent geometry to override [SubCategory]($backend) appearance */ ElementGeometryOpcode[ElementGeometryOpcode["BasicSymbology"] = 4] = "BasicSymbology"; /** A line, line string, shape, or point string (automatic simplification of a [CurvePrimitive]($core-geometry) or [CurveCollection]($core-geometry)) */ ElementGeometryOpcode[ElementGeometryOpcode["PointPrimitive"] = 5] = "PointPrimitive"; /** A 2d line, line string, shape, or point string (automatic simplification of a [CurvePrimitive]($core-geometry) or [CurveCollection]($core-geometry)) */ ElementGeometryOpcode[ElementGeometryOpcode["PointPrimitive2d"] = 6] = "PointPrimitive2d"; /** Arc or ellipse (automatic simplification of a [CurvePrimitive]($core-geometry) or [CurveCollection]($core-geometry)) */ ElementGeometryOpcode[ElementGeometryOpcode["ArcPrimitive"] = 7] = "ArcPrimitive"; /** [CurveCollection]($core-geometry) */ // eslint-disable-next-line @typescript-eslint/no-shadow ElementGeometryOpcode[ElementGeometryOpcode["CurveCollection"] = 8] = "CurveCollection"; /** [Polyface]($core-geometry) */ // eslint-disable-next-line @typescript-eslint/no-shadow ElementGeometryOpcode[ElementGeometryOpcode["Polyface"] = 9] = "Polyface"; /** [CurvePrimitive]($core-geometry) */ ElementGeometryOpcode[ElementGeometryOpcode["CurvePrimitive"] = 10] = "CurvePrimitive"; /** [SolidPrimitive]($core-geometry) */ // eslint-disable-next-line @typescript-eslint/no-shadow ElementGeometryOpcode[ElementGeometryOpcode["SolidPrimitive"] = 11] = "SolidPrimitive"; /** [BSplineSurface3d]($core-geometry) */ ElementGeometryOpcode[ElementGeometryOpcode["BsplineSurface"] = 12] = "BsplineSurface"; /** Opaque and [[Gradient]] fills. */ ElementGeometryOpcode[ElementGeometryOpcode["Fill"] = 19] = "Fill"; /** Hatch, cross-hatch, or [[AreaPattern]]. */ ElementGeometryOpcode[ElementGeometryOpcode["Pattern"] = 20] = "Pattern"; /** [[RenderMaterial]] */ ElementGeometryOpcode[ElementGeometryOpcode["Material"] = 21] = "Material"; /** [[TextString]] */ // eslint-disable-next-line @typescript-eslint/no-shadow ElementGeometryOpcode[ElementGeometryOpcode["TextString"] = 22] = "TextString"; /** Specifies line style overrides as a [[LineStyle.Modifier]] */ ElementGeometryOpcode[ElementGeometryOpcode["LineStyleModifiers"] = 23] = "LineStyleModifiers"; /** Boundary representation solid, sheet, or wire body as a [[BRepEntity.DataProps]] */ ElementGeometryOpcode[ElementGeometryOpcode["BRep"] = 25] = "BRep"; /** Small single-tile raster image as an [[ImageGraphic]] */ ElementGeometryOpcode[ElementGeometryOpcode["Image"] = 28] = "Image"; })(ElementGeometryOpcode || (exports.ElementGeometryOpcode = ElementGeometryOpcode = {})); /** Values for [[BRepGeometryCreate.operation]] * @alpha */ var BRepGeometryOperation; (function (BRepGeometryOperation) { /** Unite target (first entry) with one or more tool entities. */ BRepGeometryOperation[BRepGeometryOperation["Unite"] = 0] = "Unite"; /** Subtract one or more tool entities from target entity (first entry) */ BRepGeometryOperation[BRepGeometryOperation["Subtract"] = 1] = "Subtract"; /** Intersect target (first entry) with one or more tool entities */ BRepGeometryOperation[BRepGeometryOperation["Intersect"] = 2] = "Intersect"; /** Sew the given set of surfaces together by joining those that share edges in common */ BRepGeometryOperation[BRepGeometryOperation["Sew"] = 3] = "Sew"; /** Create a cut in the target (first entry) using a planar region (second entry) and optional depth */ BRepGeometryOperation[BRepGeometryOperation["Cut"] = 4] = "Cut"; /** Create a pad or pocket in the target (first entry) using a planar region (second entry) */ BRepGeometryOperation[BRepGeometryOperation["Emboss"] = 5] = "Emboss"; /** Create a solid from a surface by offsetting using the specified forward and backward distances */ BRepGeometryOperation[BRepGeometryOperation["Thicken"] = 6] = "Thicken"; /** Create a shelled solid by offsetting all faces by the supplied distance */ BRepGeometryOperation[BRepGeometryOperation["Hollow"] = 7] = "Hollow"; /** Create a solid or surface by sweeping a planar profile (first entry) along a path (second entry) */ BRepGeometryOperation[BRepGeometryOperation["Sweep"] = 8] = "Sweep"; /** Create a solid or surface by lofting through a set of paths or regions */ BRepGeometryOperation[BRepGeometryOperation["Loft"] = 9] = "Loft"; /** Create a solid or sheet with all non-smooth/non-laminar edges rounded */ BRepGeometryOperation[BRepGeometryOperation["Round"] = 10] = "Round"; /** Offset all faces of a solid or sheet target by the supplied distance. */ BRepGeometryOperation[BRepGeometryOperation["Offset"] = 11] = "Offset"; })(BRepGeometryOperation || (exports.BRepGeometryOperation = BRepGeometryOperation = {})); /** Provides utility functions for working with [[ElementGeometryDataEntry]]. * @beta */ var ElementGeometry; (function (ElementGeometry) { /** [[ElementGeometry.Builder]] is a helper class for populating a [[ElementGeometryDataEntry]] array needed to create a [[GeometricElement]] or [[GeometryPart]]. */ class Builder { _localToWorld; _worldToLocal; /** GeometryStream entries */ entries = []; /** Current placement transform, converts local coordinate (placement relative) input to world */ get localToWorld() { return this._localToWorld; } /** Current inverse placement transform, converts world coordinate input to local (placement relative) */ get worldToLocal() { return this._worldToLocal; } /** Supply optional local to world transform. Used to transform world coordinate input relative to element placement. * For a [[GeometricElement]]'s placement to be meaningful, world coordinate geometry should never be appended to an element with an identity placement. * Can be called with undefined or identity transform to start appending geometry supplied in local coordinates again. */ setLocalToWorld(localToWorld) { this._localToWorld = (undefined === localToWorld || localToWorld.isIdentity ? undefined : localToWorld.clone()); this._worldToLocal = (undefined === this._localToWorld ? undefined : this._localToWorld.inverse()); } /** Supply local to world transform from a Point3d and optional YawPitchRollAngles. * @see [[Placement3d]] */ setLocalToWorld3d(origin, angles = core_geometry_1.YawPitchRollAngles.createDegrees(0.0, 0.0, 0.0)) { this.setLocalToWorld(core_geometry_1.Transform.createOriginAndMatrix(origin, angles.toMatrix3d())); } /** Supply local to world transform from a Point2d and optional Angle. * @see [[Placement2d]] */ setLocalToWorld2d(origin, angle = core_geometry_1.Angle.createDegrees(0.0)) { this.setLocalToWorld(core_geometry_1.Transform.createOriginAndMatrix(core_geometry_1.Point3d.createFrom(origin), core_geometry_1.Matrix3d.createRotationAroundVector(core_geometry_1.Vector3d.unitZ(), angle))); } /** Supply local to world transform from a PlacementProps2d or PlacementProps3d. * @see [[PlacementProps]] */ setLocalToWorldFromPlacement(props) { const placement = (0, ElementProps_1.isPlacement2dProps)(props) ? Placement_1.Placement2d.fromJSON(props) : Placement_1.Placement3d.fromJSON(props); this.setLocalToWorld(placement.transform); } /** Compute angles suitable for passing to [[setLocalToWorld3d]] from an array of 3d points. */ static placementAnglesFromPoints(pts, defaultUp, result) { const angles = result ? result : new core_geometry_1.YawPitchRollAngles(); const zVec = defaultUp ? defaultUp.clone() : core_geometry_1.Vector3d.unitZ(); const matrix = core_geometry_1.Matrix3d.createRigidHeadsUp(zVec); core_geometry_1.YawPitchRollAngles.createFromMatrix3d(matrix, angles); if (pts.length < 2 || pts[0].isAlmostEqual(pts[1])) return angles; // Check if points have a well defined normal to use instead of defaultUp... const frameTransform = core_geometry_1.FrameBuilder.createFrameToDistantPoints(pts); if (undefined !== frameTransform) { const plane = core_geometry_1.Plane3dByOriginAndUnitNormal.create(pts[0], frameTransform.matrix.getColumn(2)); if (undefined !== plane && core_geometry_1.Point3dArray.isCloseToPlane(pts, plane)) zVec.setFrom(plane.getNormalRef()); } const xVec = core_geometry_1.Vector3d.createStartEnd(pts[0], pts[1]); if (xVec.isParallelTo(zVec, true)) return angles; const yVec = xVec.unitCrossProduct(zVec); if (undefined === yVec) return angles; core_geometry_1.Matrix3d.createColumns(xVec, yVec, zVec, matrix); if (undefined === core_geometry_1.Matrix3d.createRigidFromMatrix3d(matrix, undefined, matrix)) return angles; core_geometry_1.YawPitchRollAngles.createFromMatrix3d(matrix, angles); return angles; } /** Compute angle suitable for passing to [[setLocalToWorld2d]] from an array of xy plane points. */ static placementAngleFromPoints(pts, result) { const angles = ElementGeometry.Builder.placementAnglesFromPoints(pts); if (undefined === result) return angles.yaw; result.setFrom(angles.yaw); return result; } /** Store local ranges for all subsequent geometry appended. Can improve performance of range testing for elements with a GeometryStream * containing more than one [[GeometryQuery]] differentiable by range. Not useful for a single [[GeometryQuery]] as its range and that of the [[GeometricElement]] are the same. * Ignored when defining a [[GeometryPart]] and not needed when only appending [[GeometryPart]] instances to a [[GeometricElement]] as these store their own range. */ appendGeometryRanges() { const entry = fromSubGraphicRange(core_geometry_1.Range3d.create()); // Computed on backend, just need opcode... if (undefined === entry) return false; this.entries.push(entry); return true; } /** Change [[GeometryParams]] for subsequent geometry. * It is not valid to change the sub-category when defining a [[GeometryPart]]. A [[GeometryPart]] inherits the symbology of their instance for anything not explicitly overridden. */ appendGeometryParamsChange(geomParams) { return appendGeometryParams(geomParams, this.entries, this._worldToLocal); } /** Append a [[GeometryQuery]] supplied in either local or world coordinates to the [[ElementGeometryDataEntry]] array */ appendGeometryQuery(geometry) { const entry = ElementGeometry.fromGeometryQuery(geometry, this._worldToLocal); if (undefined === entry) return false; this.entries.push(entry); return true; } /** Append a [[TextString]] supplied in either local or world coordinates to the [[ElementGeometryDataEntry]] array */ appendTextString(text) { const entry = ElementGeometry.fromTextString(text.toJSON(), this._worldToLocal); if (undefined === entry) return false; this.entries.push(entry); return true; } /** Append a series of entries representing a [[TextBlock]] to the [[ElementGeometryDataEntry]] array. * @beta */ appendTextBlock(block, geomParams) { for (const entry of block.entries) { let result; if (entry.text) { result = this.appendTextString(new TextString_1.TextString(entry.text)); } else if (entry.color) { const params = geomParams?.clone() ?? new GeometryParams_1.GeometryParams(core_bentley_1.Id64.invalid); if (entry.color !== "subcategory") { params.lineColor = ColorDef_1.ColorDef.fromJSON(entry.color); } result = this.appendGeometryParamsChange(params); } else { result = this.appendGeometryQuery(core_geometry_1.LineSegment3d.fromJSON(entry.separator)); } if (!result) { return false; } } return true; } /** Append a [[ImageGraphic]] supplied in either local or world coordinates to the [[ElementGeometryDataEntry]] array */ appendImageGraphic(image) { const entry = ElementGeometry.fromImageGraphic(image.toJSON(), this._worldToLocal); if (undefined === entry) return false; this.entries.push(entry); return true; } /** Append a [[BRepEntity.DataProps]] supplied in either local or world coordinates to the [[ElementGeometryDataEntry]] array. * Provided for compatibility with GeometryStreamBuilder only. * Backend code should use IModelDb.createBRepGeometry to create a brep [[ElementGeometryDataEntry]] directly. */ appendBRepData(brep) { const entry = ElementGeometry.fromBRep(brep, this._worldToLocal); if (undefined === entry) return false; this.entries.push(entry); return true; } /** Append a [[GeometryPart]] instance with relative transform to the [[ElementGeometryDataEntry]] array for creating a [[GeometricElement]]. * Not valid when defining a [[GeometryPart]] as nesting of parts is not supported. */ appendGeometryPart(partId, partTransform) { const entry = ElementGeometry.fromGeometryPart(partId, partTransform, this._worldToLocal); if (undefined === entry) return false; this.entries.push(entry); return true; } /** Append a [[GeometryPart]] instance with relative position, orientation, and scale to the [[ElementGeometryDataEntry]] array for creating a [[GeometricElement3d]]. * Not valid when defining a [[GeometryPart]] as nesting of parts is not supported. */ appendGeometryPart3d(partId, instanceOrigin, instanceRotation, instanceScale) { const partTransform = core_geometry_1.Transform.createOriginAndMatrix(instanceOrigin, instanceRotation ? instanceRotation.toMatrix3d() : core_geometry_1.Matrix3d.createIdentity()); if (undefined !== instanceScale) partTransform.matrix.scaleColumnsInPlace(instanceScale, instanceScale, instanceScale); return this.appendGeometryPart(partId, partTransform); } /** Append a [[GeometryPart]] instance with relative position, orientation, and scale to the [[ElementGeometryDataEntry]] array for creating a [[GeometricElement2d]]. * Not valid when defining a [[GeometryPart]] as nesting of parts is not supported. */ appendGeometryPart2d(partId, instanceOrigin, instanceRotation, instanceScale) { return this.appendGeometryPart3d(partId, instanceOrigin ? core_geometry_1.Point3d.createFrom(instanceOrigin) : undefined, instanceRotation ? new core_geometry_1.YawPitchRollAngles(instanceRotation) : undefined, instanceScale); } } ElementGeometry.Builder = Builder; class IteratorEntry { geomParams; localToWorld; localRange; _value; _applyLocalToWorld; constructor(geomParams, localToWorld, applyLocalToWorld) { this.geomParams = geomParams; this.localToWorld = localToWorld; this._applyLocalToWorld = applyLocalToWorld ? !localToWorld.isIdentity : false; } get value() { return this._value; } set value(value) { this._value = value; } get outputTransform() { return this._applyLocalToWorld ? this.localToWorld : undefined; } /** Return the [[GeometryQuery]] representation for the current entry */ toGeometryQuery() { return toGeometryQuery(this.value, this.outputTransform); } /** Return the [[BRepEntity.DataProps]] representation for the current entry for checking brep type and face attachments. */ toBRepData(wantBRepData = false) { return toBRep(this.value, wantBRepData, this.outputTransform); } /** Return the [[TextString]] representation for the current entry */ toTextString() { const props = toTextString(this.value, this.outputTransform); return (undefined !== props ? new TextString_1.TextString(props) : undefined); } /** Return the [[ImageGraphic]] representation for the current entry */ toImageGraphic() { const props = toImageGraphic(this.value, this.outputTransform); return (undefined !== props ? ImageGraphic_1.ImageGraphic.fromJSON(props) : undefined); } /** Return the GeometryPart information for the current entry */ toGeometryPart(partToLocal, partToWorld) { if (undefined === partToLocal && undefined !== partToWorld) partToLocal = core_geometry_1.Transform.createIdentity(); const partId = toGeometryPart(this.value, partToLocal); if (undefined === partId || undefined === partToLocal || undefined === partToWorld) return partId; if (undefined !== this.localToWorld) this.localToWorld.multiplyTransformTransform(partToLocal, partToWorld); return partId; } } ElementGeometry.IteratorEntry = IteratorEntry; /** [[ElementGeometry.Iterator]] is a helper class for iterating a [[ElementGeometryDataEntry]] array. * Each [[ElementGeometryDataEntry]] returned by the iterator represents exactly one displayable entry. */ class Iterator { /** GeometryStream entries */ entryArray; /** The geometric element's placement or geometry part's local range (placement.bbox) */ placement; /** If true, geometry displays oriented to face the camera */ viewIndependent; /** If true, geometry stream contained breps that were omitted or replaced as requested */ brepsPresent; /** Current entry position */ _index = 0; /** Allocated on first call to next() and reused thereafter */ _entry; /** Used to initialize this._entry */ _appearance; _localToWorld; /** Whether deserialized entry data is returned in world or local coordinates */ _applyLocalToWorld = false; /** Construct a new Iterator given a [[ElementGeometryInfo]] from either a [[GeometricElement3d]], [[GeometricElement2d]], or [[GeometryPart]]. * Supply the optional [[GeometryParams]] and localToWorld transform to iterate a [[GeometryPart]] in the context of a [[GeometricElement]] reference. */ constructor(info, categoryOrGeometryParams, localToWorld) { this.entryArray = info.entryArray; this.viewIndependent = info.viewIndependent; this.brepsPresent = info.brepsPresent; if (undefined !== info.categoryId) categoryOrGeometryParams = info.categoryId; if (undefined !== categoryOrGeometryParams) this._appearance = typeof categoryOrGeometryParams === "string" ? new GeometryParams_1.GeometryParams(categoryOrGeometryParams) : categoryOrGeometryParams; else this._appearance = new GeometryParams_1.GeometryParams(core_bentley_1.Id64.invalid); if (undefined !== info.sourceToWorld) localToWorld = ElementGeometry.toTransform(info.sourceToWorld); if (undefined !== localToWorld) this._localToWorld = localToWorld; else this._localToWorld = core_geometry_1.Transform.createIdentity(); const orgAng = core_geometry_1.YawPitchRollAngles.tryFromTransform(this._localToWorld); if (undefined === orgAng.angles) orgAng.angles = core_geometry_1.YawPitchRollAngles.createDegrees(0, 0, 0); let bbox = (undefined !== info.bbox ? ElementGeometry.toElementAlignedBox3d(info.bbox) : undefined); if (undefined === bbox) bbox = core_geometry_1.Range3d.createNull(); this.placement = new Placement_1.Placement3d(orgAng.origin, orgAng.angles, bbox); } /** Call to return deserialized entry data in world coordinates */ requestWorldCoordinates() { this._applyLocalToWorld = !this._localToWorld.isIdentity; } // eslint-disable-next-line @typescript-eslint/naming-convention get entry() { if (undefined === this._entry) this._entry = new IteratorEntry(this._appearance, this._localToWorld, this._applyLocalToWorld); return this._entry; } /** Advance to next displayable opcode (geometric entry or geometry part) while updating the current [[GeometryParams]] from appearance related opcodes. */ next() { while (this._index < this.entryArray.length) { const value = this.entryArray[this._index++]; if (ElementGeometry.isAppearanceEntry(value)) { const localToWorld = (this._applyLocalToWorld ? this._localToWorld : undefined); ElementGeometry.updateGeometryParams(value, this.entry.geomParams, localToWorld); } else if (ElementGeometryOpcode.SubGraphicRange === value.opcode) { // NOTE: localRange remains valid until the next sub-range entry is encountered... this.entry.localRange = ElementGeometry.toSubGraphicRange(value); } else if (ElementGeometryOpcode.PartReference === value.opcode) { this.entry.value = value; return { value: this.entry, done: false }; } else if (ElementGeometry.isGeometricEntry(value)) { this.entry.value = value; return { value: this.entry, done: false }; } } return { value: this.entry, done: true }; } [Symbol.iterator]() { return this; } } ElementGeometry.Iterator = Iterator; /** Return whether the supplied entry can be represented as a [[GeometryQuery]] */ function isGeometryQueryEntry(entry) { switch (entry.opcode) { case ElementGeometryOpcode.PointPrimitive: case ElementGeometryOpcode.PointPrimitive2d: case ElementGeometryOpcode.ArcPrimitive: case ElementGeometryOpcode.CurveCollection: case ElementGeometryOpcode.Polyface: case ElementGeometryOpcode.CurvePrimitive: case ElementGeometryOpcode.SolidPrimitive: case ElementGeometryOpcode.BsplineSurface: return true; default: return false; } } ElementGeometry.isGeometryQueryEntry = isGeometryQueryEntry; /** Return whether the supplied entry is displayable geometry [[GeometryQuery]], [[BRepEntity.DataProps]], [[TextString]], or [[ImageGraphic]] */ function isGeometricEntry(entry) { switch (entry.opcode) { case ElementGeometryOpcode.BRep: case ElementGeometryOpcode.TextString: case ElementGeometryOpcode.Image: return true; default: return isGeometryQueryEntry(entry); } } ElementGeometry.isGeometricEntry = isGeometricEntry; /** Return whether the supplied entry is geometric or a part reference */ function isDisplayableEntry(entry) { switch (entry.opcode) { case ElementGeometryOpcode.PartReference: return true; default: return isGeometricEntry(entry); } } ElementGeometry.isDisplayableEntry = isDisplayableEntry; /** Return whether the supplied entry represents appearance information */ function isAppearanceEntry(entry) { switch (entry.opcode) { case ElementGeometryOpcode.BasicSymbology: case ElementGeometryOpcode.Fill: case ElementGeometryOpcode.Pattern: case ElementGeometryOpcode.Material: case ElementGeometryOpcode.LineStyleModifiers: return true; default: return false; } } ElementGeometry.isAppearanceEntry = isAppearanceEntry; /** Return whether the supplied entry represents a single open curve or path */ function isCurve(entry) { switch (entry.opcode) { case ElementGeometryOpcode.PointPrimitive: { const buffer = new flatbuffers_1.flatbuffers.ByteBuffer(entry.data); const ppfb = ElementGeometryFB_1.EGFBAccessors.PointPrimitive.getRootAsPointPrimitive(buffer); return (ElementGeometryFB_1.EGFBAccessors.BoundaryType.Open === ppfb.boundary()); } case ElementGeometryOpcode.PointPrimitive2d: { const buffer = new flatbuffers_1.flatbuffers.ByteBuffer(entry.data); const ppfb = ElementGeometryFB_1.EGFBAccessors.PointPrimitive2d.getRootAsPointPrimitive2d(buffer); return (ElementGeometryFB_1.EGFBAccessors.BoundaryType.Open === ppfb.boundary()); } case ElementGeometryOpcode.ArcPrimitive: { const buffer = new flatbuffers_1.flatbuffers.ByteBuffer(entry.data); const ppfb = ElementGeometryFB_1.EGFBAccessors.ArcPrimitive.getRootAsArcPrimitive(buffer); return (ElementGeometryFB_1.EGFBAccessors.BoundaryType.Open === ppfb.boundary()); } case ElementGeometryOpcode.CurvePrimitive: { // should never be a point string or closed bcurve... return true; } case ElementGeometryOpcode.CurveCollection: { const geom = core_geometry_1.BentleyGeometryFlatBuffer.bytesToGeometry(entry.data, true); if (undefined === geom || Array.isArray(geom)) return false; return ("curveCollection" === geom.geometryCategory && !geom.isAnyRegionType); } default: return false; } } ElementGeometry.isCurve = isCurve; /** Return whether the supplied entry represents a loop, planar region, open polyface, or sheet body */ function isSurface(entry) { switch (entry.opcode) { case ElementGeometryOpcode.PointPrimitive: { const buffer = new flatbuffers_1.flatbuffers.ByteBuffer(entry.data); const ppfb = ElementGeometryFB_1.EGFBAccessors.PointPrimitive.getRootAsPointPrimitive(buffer); return (ElementGeometryFB_1.EGFBAccessors.BoundaryType.Closed === ppfb.boundary()); } case ElementGeometryOpcode.PointPrimitive2d: { const buffer = new flatbuffers_1.flatbuffers.ByteBuffer(entry.data); const ppfb = ElementGeometryFB_1.EGFBAccessors.PointPrimitive2d.getRootAsPointPrimitive2d(buffer); return (ElementGeometryFB_1.EGFBAccessors.BoundaryType.Closed === ppfb.boundary()); } case ElementGeometryOpcode.ArcPrimitive: { const buffer = new flatbuffers_1.flatbuffers.ByteBuffer(entry.data); const ppfb = ElementGeometryFB_1.EGFBAccessors.ArcPrimitive.getRootAsArcPrimitive(buffer); return (ElementGeometryFB_1.EGFBAccessors.BoundaryType.Closed === ppfb.boundary()); } case ElementGeometryOpcode.CurvePrimitive: { // should never be a closed bcurve... return false; } case ElementGeometryOpcode.CurveCollection: { const geom = core_geometry_1.BentleyGeometryFlatBuffer.bytesToGeometry(entry.data, true); if (undefined === geom || Array.isArray(geom)) return false; return ("curveCollection" === geom.geometryCategory && geom.isAnyRegionType); } case ElementGeometryOpcode.SolidPrimitive: { const geom = core_geometry_1.BentleyGeometryFlatBuffer.bytesToGeometry(entry.data, true); if (undefined === geom || Array.isArray(geom)) return false; return ("solid" === geom.geometryCategory && !geom.isClosedVolume); } case ElementGeometryOpcode.Polyface: { const geom = core_geometry_1.BentleyGeometryFlatBuffer.bytesToGeometry(entry.data, true); if (undefined === geom || Array.isArray(geom)) return false; if ("polyface" !== geom.geometryCategory) return false; const polyface = geom; switch (polyface.expectedClosure) { case 0: return !core_geometry_1.PolyfaceQuery.isPolyfaceClosedByEdgePairing(polyface); case 1: return true; case 2: default: return false; } } case ElementGeometryOpcode.BsplineSurface: { // never treated as a solid even if closed/periodic in u/v... return true; } case ElementGeometryOpcode.BRep: { const buffer = new flatbuffers_1.flatbuffers.ByteBuffer(entry.data); const ppfb = ElementGeometryFB_1.EGFBAccessors.BRepData.getRootAsBRepData(buffer); return (ElementGeometryFB_1.EGFBAccessors.BRepType.Sheet === ppfb.brepType()); } default: return false; } } ElementGeometry.isSurface = isSurface; /** Return whether the supplied entry represents a capped solid, closed polyface, or solid body */ function isSolid(entry) { switch (entry.opcode) { case ElementGeometryOpcode.SolidPrimitive: { const geom = core_geometry_1.BentleyGeometryFlatBuffer.bytesToGeometry(entry.data, true); if (undefined === geom || Array.isArray(geom)) return false; return ("solid" === geom.geometryCategory && geom.isClosedVolume); } case ElementGeometryOpcode.Polyface: { const geom = core_geometry_1.BentleyGeometryFlatBuffer.bytesToGeometry(entry.data, true); if (undefined === geom || Array.isArray(geom)) return false; if ("polyface" !== geom.geometryCategory) return false; const polyface = geom; switch (polyface.expectedClosure) { case 0: return core_geometry_1.PolyfaceQuery.isPolyfaceClosedByEdgePairing(polyface); case 2: return true; case 1: default: return false; } } case ElementGeometryOpcode.BRep: { const buffer = new flatbuffers_1.flatbuffers.ByteBuffer(entry.data); const ppfb = ElementGeometryFB_1.EGFBAccessors.BRepData.getRootAsBRepData(buffer); return (ElementGeometryFB_1.EGFBAccessors.BRepType.Solid === ppfb.brepType()); } default: return false; } } ElementGeometry.isSolid = isSolid; /** Return the body type that would be used to represent the supplied entry */ function getBRepEntityType(entry) { switch (entry.opcode) { case ElementGeometryOpcode.PointPrimitive: { const buffer = new flatbuffers_1.flatbuffers.ByteBuffer(entry.data); const ppfb = ElementGeometryFB_1.EGFBAccessors.PointPrimitive.getRootAsPointPrimitive(buffer); if (ElementGeometryFB_1.EGFBAccessors.BoundaryType.None === ppfb.boundary()) return undefined; return (ElementGeometryFB_1.EGFBAccessors.BoundaryType.Closed === ppfb.boundary() ? GeometryStream_1.BRepEntity.Type.Sheet : GeometryStream_1.BRepEntity.Type.Wire); } case ElementGeometryOpcode.PointPrimitive2d: { const buffer = new flatbuffers_1.flatbuffers.ByteBuffer(entry.data); const ppfb = ElementGeometryFB_1.EGFBAccessors.PointPrimitive2d.getRootAsPointPrimitive2d(buffer); if (ElementGeometryFB_1.EGFBAccessors.BoundaryType.None === ppfb.boundary()) return undefined; return (ElementGeometryFB_1.EGFBAccessors.BoundaryType.Closed === ppfb.boundary() ? GeometryStream_1.BRepEntity.Type.Sheet : GeometryStream_1.BRepEntity.Type.Wire); } case ElementGeometryOpcode.ArcPrimitive: { const buffer = new flatbuffers_1.flatbuffers.ByteBuffer(entry.data); const ppfb = ElementGeometryFB_1.EGFBAccessors.ArcPrimitive.getRootAsArcPrimitive(buffer); return (ElementGeometryFB_1.EGFBAccessors.BoundaryType.Closed === ppfb.boundary() ? GeometryStream_1.BRepEntity.Type.Sheet : GeometryStream_1.BRepEntity.Type.Wire); } case ElementGeometryOpcode.CurvePrimitive: { // should never be a point string or closed bcurve... return GeometryStream_1.BRepEntity.Type.Wire; } case ElementGeometryOpcode.CurveCollection: { const geom = core_geometry_1.BentleyGeometryFlatBuffer.bytesToGeometry(entry.data, true); if (undefined === geom || Array.isArray(geom)) return undefined; if ("curveCollection" !== geom.geometryCategory) return undefined; const curves = geom; return (curves.isAnyRegionType ? GeometryStream_1.BRepEntity.Type.Sheet : GeometryStream_1.BRepEntity.Type.Wire); } case ElementGeometryOpcode.SolidPrimitive: { const geom = core_geometry_1.BentleyGeometryFlatBuffer.bytesToGeometry(entry.data, true); if (undefined === geom || Array.isArray(geom)) return undefined; if ("solid" !== geom.geometryCategory) return undefined; const solid = geom; return (solid.isClosedVolume ? GeometryStream_1.BRepEntity.Type.Solid : GeometryStream_1.BRepEntity.Type.Sheet); } case ElementGeometryOpcode.BsplineSurface: { // always a surface... return GeometryStream_1.BRepEntity.Type.Sheet; } case ElementGeometryOpcode.Polyface: { const geom = core_geometry_1.BentleyGeometryFlatBuffer.bytesToGeometry(entry.data, true); if (undefined === geom || Array.isArray(geom)) return undefined; if ("polyface" !== geom.geometryCategory) return undefined; const polyface = geom; switch (polyface.expectedClosure) { case 0: return core_geometry_1.PolyfaceQuery.isPolyfaceClosedByEdgePairing(polyface) ? GeometryStream_1.BRepEntity.Type.Solid : GeometryStream_1.BRepEntity.Type.Sheet; case 1: return GeometryStream_1.BRepEntity.Type.Sheet; case 2: return GeometryStream_1.BRepEntity.Type.Solid; default: return undefined; } } case ElementGeometryOpcode.BRep: { const buffer = new flatbuffers_1.flatbuffers.ByteBuffer(entry.data); const ppfb = ElementGeometryFB_1.EGFBAccessors.BRepData.getRootAsBRepData(buffer); switch (ppfb.brepType()) { case ElementGeometryFB_1.EGFBAccessors.BRepType.Wire: return GeometryStream_1.BRepEntity.Type.Wire; // always be persisted as a curve type... case ElementGeometryFB_1.EGFBAccessors.BRepType.Sheet: return GeometryStream_1.BRepEntity.Type.Sheet; case ElementGeometryFB_1.EGFBAccessors.BRepType.Solid: return GeometryStream_1.BRepEntity.Type.Solid; default: return undefined; } } default: return undefined; } } ElementGeometry.getBRepEntityType = getBRepEntityType; /** Return entry as a [[GeometryQuery]] */ function toGeometryQuery(entry, localToWorld) { if (!isGeometryQueryEntry(entry)) return undefined; switch (entry.opcode) { case ElementGeometryOpcode.PointPrimitive: { const buffer = new flatbuffers_1.flatbuffers.ByteBuffer(entry.data); const ppfb = ElementGeometryFB_1.EGFBAccessors.PointPrimitive.getRootAsPointPrimitive(buffer); const pts = []; for (let i = 0; i < ppfb.coordsLength(); i++) pts.push(core_geometry_1.Point3d.create(ppfb.coords(i).x(), ppfb.coords(i).y(), ppfb.coords(i).z())); if (0 === pts.length) return undefined; if (undefined !== localToWorld) localToWorld.multiplyPoint3dArrayInPlace(pts); switch (ppfb.boundary()) { case ElementGeometryFB_1.EGFBAccessors.BoundaryType.Open: return core_geometry_1.LineString3d.createPoints(pts); case ElementGeometryFB_1.EGFBAccessors.BoundaryType.Closed: return core_geometry_1.Loop.createPolygon(pts); default: return core_geometry_1.PointString3d.createPoints(pts); } } case ElementGeometryOpcode.PointPrimitive2d: { const buffer = new flatbuffers_1.flatbuffers.ByteBuffer(entry.data); const ppfb = ElementGeometryFB_1.EGFBAccessors.PointPrimitive2d.getRootAsPointPrimitive2d(buffer); const pts = []; for (let i = 0; i < ppfb.coordsLength(); i++) pts.push(core_geometry_1.Point3d.create(ppfb.coords(i).x(), ppfb.coords(i).y())); if (0 === pts.length) return undefined; if (undefined !== localToWorld) localToWorld.multiplyPoint3dArrayInPlace(pts); switch (ppfb.boundary()) { case ElementGeometryFB_1.EGFBAccessors.BoundaryType.Open: return core_geometry_1.LineString3d.createPoints(pts); case ElementGeometryFB_1.EGFBAccessors.BoundaryType.Closed: return core_geometry_1.Loop.createPolygon(pts); default: return core_geometry_1.PointString3d.createPoints(pts); } } case ElementGeometryOpcode.ArcPrimitive: { const buffer = new flatbuffers_1.flatbuffers.ByteBuffer(entry.data); const ppfb = ElementGeometryFB_1.EGFBAccessors.ArcPrimitive.getRootAsArcPrimitive(buffer); const center = core_geometry_1.Point3d.create(ppfb.center().x(), ppfb.center().y(), ppfb.center().z()); const vector0 = core_geometry_1.Vector3d.create(ppfb.vector0().x(), ppfb.vector0().y(), ppfb.vector0().z()); const vector90 = core_geometry_1.Vector3d.create(ppfb.vector90().x(), ppfb.vector90().y(), ppfb.vector90().z()); const arc = core_geometry_1.Arc3d.create(center, vector0, vector90, core_geometry_1.AngleSweep.createStartSweepRadians(ppfb.start(), ppfb.sweep())); if (undefined !== localToWorld && !arc.tryTransformInPlace(localToWorld)) return undefined; return (ElementGeometryFB_1.EGFBAccessors.BoundaryType.Closed === ppfb.boundary() ? core_geometry_1.Loop.create(arc) : arc); } case ElementGeometryOpcode.CurvePrimitive: case ElementGeometryOpcode.CurveCollection: case ElementGeometryOpcode.SolidPrimitive: case ElementGeometryOpcode.BsplineSurface: case ElementGeometryOpcode.Polyface: const geom = core_geometry_1.BentleyGeometryFlatBuffer.bytesToGeometry(entry.data, true); if (undefined === geom || Array.isArray(geom)) return undefined; // Should always be a single entry not an array... if (undefined !== localToWorld && !geom.tryTransformInPlace(localToWorld)) return undefined; return geom; default: return undefined; // Not a GeometryQuery, need to be handled explicitly... } } ElementGeometry.toGeometryQuery = toGeometryQuery; /** Create entry from a [[GeometryQuery]] */ function fromGeometryQuery(geom, worldToLocal) { let opcode; switch (geom.geometryCategory) { case "bsurf": opcode = ElementGeometryOpcode.BsplineSurface; break; case "curveCollection": opcode = ElementGeometryOpcode.CurveCollection; break; case "curvePrimitive": case "pointCollection": opcode = ElementGeometryOpcode.CurvePrimitive; break; case "polyface": opcode = ElementGeometryOpcode.Polyface; break; case "solid": opcode = ElementGeometryOpcode.SolidPrimitive; break; default: return undefined; } if (undefined !== worldToLocal) { const localGeom = geom.cloneTransformed(worldToLocal); if (undefined === localGeom) return undefined; geom = localGeom; } const data = core_geometry_1.BentleyGeometryFlatBuffer.geometryToBytes(geom, true); if (undefined === data) return undefined; return { opcode, data }; } ElementGeometry.fromGeometryQuery = fromGeometryQuery; /** Return entry as a [[TextString]] */ function toTextString(entry, localToWorld) { if (ElementGeometryOpcode.TextString !== entry.opcode) return undefined; const buffer = new flatbuffers_1.flatbuffers.ByteBuffer(entry.data); const ppfb = ElementGeometryFB_1.EGFBAccessors.TextString.getRootAsTextString(buffer); const style = ppfb.style(); if (null === style) return undefined; const text = ppfb.text(); const props = { text: (null !== text ? text : ""), font: style.fontId(), height: style.height() }; props.widthFactor = style.widthFactor(); props.bold = style.isBold(); props.italic = style.isItalic(); props.underline = style.isUnderlined(); const transform = ppfb.transform(); if (null !== transform) { props.origin = core_geometry_1.Point3d.create(transform.form3d03(), transform.form3d13(), transform.form3d23()); props.rotation = core_geometry_1.YawPitchRollAngles.createFromMatrix3d(core_geometry_1.Matrix3d.createRowValues(transform.form3d00(), transform.form3d01(), transform.form3d02(), transform.form3d10(), transform.form3d11(), transform.form3d12(), transform.form3d20(), transform.form3d21(), transform.form3d22())); } if (undefined === localToWorld) return props; const textString = new TextString_1.TextString(props); if (!textString.transformInPlace(localToWorld)) return undefined; return textString.toJSON(); } ElementGeometry.toTextString = toTextString; /** Returns only the [[TextStringGlyphData]] embedded in the [[TextString]] flatbuffer. This data is only internal to the native display libaries. */ function toTextStringGlyphData(entry) { if (ElementGeometryOpcode.TextString !== entry.opcode) return undefined; const buffer = new flatbuffers_1.flatbuffers.ByteBuffer(entry.data); const ppfb = ElementGeometryFB_1.EGFBAccessors.TextString.getRootAsTextString(buffer); const glyphOriginToPoint2d = (origin) => { if (!origin) throw new Error("Value cannot be null."); return new core_geometry_1.Point2d(origin.x(), origin.y()); }; const textStringRangeToRange2d = (r) => r ? new core_geometry_1.Range2d(r.lowx(), r.lowy(), r.highx(), r.highy()) : null; const range = textStringRangeToRange2d(ppfb.range()); const glyphIds = Array.from({ length: ppfb.glyphIdsLength() }, (_, i) => ppfb.glyphIds(i) ?? 0); const glyphOrigins = Array.from({ length: ppfb.glyphOriginsLength() }, (_, i) => glyphOriginToPoint2d(ppfb.glyphOrigins(i))); if (!range || range.isNull || glyphIds.length === 0 || glyphOrigins.length === 0) return undefined; return { glyphIds, glyphOrigins, range }; } ElementGeometry.toTextStringGlyphData = toTextStringGlyphData; /** Create entry from a [[TextString]] */ function fromTextString(text, worldToLocal, glyphs) { if (undefined !== worldToLocal) { const localText = new TextString_1.TextString(text); if (!localText.transformInPlace(worldToLocal)) return undefined; text = localText.toJSON(); } const fbb = new flatbuffers_1.flatbuffers.Builder(); const builder = ElementGeometryFB_1.EGFBAccessors.TextString; const textOffset = fbb.createString(text.text); const styleOffset = ElementGeometryFB_1.EGFBAccessors.TextStringStyle.createTextStringStyle(fbb, 1, 0, text.font, undefined === text.bold ? false : text.bold, undefined === text.italic ? false : text.italic, undefined === text.underline ? false : text.underline, text.height, undefined === text.widthFactor ? 1.0 : text.widthFactor); const fbbGlyphs = (() => { if (!glyphs || glyphs.range.isNull) return undefined; const glyphIdsOffset = builder.createGlyphIdsVector(fbb, glyphs.glyphIds); builder.startGlyphOriginsVector(fbb, glyphs.glyphOrigins.length); for (const origin of [...glyphs.glyphOrigins].reverse()) { ElementGeometryFB_1.EGFBAccessors.TextStringGlyphOrigin.createTextStringGlyphOrigin(fbb, origin.x, origin.y); } const glyphOriginsOffset = fbb.endVector(); return { glyphIdsOffset, glyphOriginsOffset, range: glyphs.range }; })(); builder.startTextString(fbb); builder.addMajorVersion(fbb, 1); builder.addMinorVersion(fbb, 0); builder.addText(fbb, textOffset); builder.addStyle(fbb, styl