/*---------------------------------------------------------------------------------------------
* 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
 */
import { flatbuffers } from "flatbuffers";
import { Id64 } from "@itwin/core-bentley";
import { Angle, AngleSweep, Arc3d, BentleyGeometryFlatBuffer, FrameBuilder, LineSegment3d, LineString3d, Loop, Matrix3d, Plane3dByOriginAndUnitNormal, Point2d, Point3d, Point3dArray, PointString3d, PolyfaceQuery, Range2d, Range3d, Transform, Vector3d, YawPitchRollAngles } from "@itwin/core-geometry";
import { EGFBAccessors } from "./ElementGeometryFB";
import { Base64EncodedString } from "../Base64EncodedString";
import { TextString } from "./TextString";
import { ColorDef } from "../ColorDef";
import { BackgroundFill, FillDisplay, GeometryClass, GeometryParams } from "../GeometryParams";
import { Gradient } from "../Gradient";
import { ThematicGradientSettings } from "../ThematicDisplay";
import { AreaPattern } from "./AreaPattern";
import { BRepEntity } from "./GeometryStream";
import { ImageGraphic, ImageGraphicCorners } from "./ImageGraphic";
import { LineStyle } from "./LineStyle";
import { Placement2d, Placement3d } from "./Placement";
import { isPlacement2dProps } from "../ElementProps";
/** Specifies the type of an entry in a geometry stream.
 * @see [[ElementGeometryDataEntry.opcode]].
 * @public
 * @extensions
 */
export 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 || (ElementGeometryOpcode = {}));
/** Values for [[BRepGeometryCreate.operation]]
 * @alpha
 */
export 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 || (BRepGeometryOperation = {}));
/** Provides utility functions for working with [[ElementGeometryDataEntry]].
 * @beta
 */
export var ElementGeometry;
(function (ElementGeometry) {
    /** [[ElementGeometry.Builder]] is a helper class for populating a [[ElementGeometryDataEntry]] array needed to create a [[GeometricElement]] or [[GeometryPart]]. */
    class Builder {
        constructor() {
            /** GeometryStream entries */
            this.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 = YawPitchRollAngles.createDegrees(0.0, 0.0, 0.0)) {
            this.setLocalToWorld(Transform.createOriginAndMatrix(origin, angles.toMatrix3d()));
        }
        /** Supply local to world transform from a Point2d and optional Angle.
         * @see [[Placement2d]]
         */
        setLocalToWorld2d(origin, angle = Angle.createDegrees(0.0)) {
            this.setLocalToWorld(Transform.createOriginAndMatrix(Point3d.createFrom(origin), Matrix3d.createRotationAroundVector(Vector3d.unitZ(), angle)));
        }
        /** Supply local to world transform from a PlacementProps2d or PlacementProps3d.
         * @see [[PlacementProps]]
         */
        setLocalToWorldFromPlacement(props) {
            const placement = isPlacement2dProps(props) ? Placement2d.fromJSON(props) : 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 YawPitchRollAngles();
            const zVec = defaultUp ? defaultUp.clone() : Vector3d.unitZ();
            const matrix = Matrix3d.createRigidHeadsUp(zVec);
            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 = FrameBuilder.createFrameToDistantPoints(pts);
            if (undefined !== frameTransform) {
                const plane = Plane3dByOriginAndUnitNormal.create(pts[0], frameTransform.matrix.getColumn(2));
                if (undefined !== plane && Point3dArray.isCloseToPlane(pts, plane))
                    zVec.setFrom(plane.getNormalRef());
            }
            const xVec = Vector3d.createStartEnd(pts[0], pts[1]);
            if (xVec.isParallelTo(zVec, true))
                return angles;
            const yVec = xVec.unitCrossProduct(zVec);
            if (undefined === yVec)
                return angles;
            Matrix3d.createColumns(xVec, yVec, zVec, matrix);
            if (undefined === Matrix3d.createRigidFromMatrix3d(matrix, undefined, matrix))
                return angles;
            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(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) {
            for (const entry of block.entries) {
                let result;
                if (entry.text) {
                    result = this.appendTextString(new TextString(entry.text));
                }
                else if (entry.color) {
                    const params = new GeometryParams(Id64.invalid);
                    if (entry.color !== "subcategory") {
                        params.lineColor = ColorDef.fromJSON(entry.color);
                    }
                    result = this.appendGeometryParamsChange(params);
                }
                else {
                    result = this.appendGeometryQuery(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 = Transform.createOriginAndMatrix(instanceOrigin, instanceRotation ? instanceRotation.toMatrix3d() : 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 ? Point3d.createFrom(instanceOrigin) : undefined, instanceRotation ? new YawPitchRollAngles(instanceRotation) : undefined, instanceScale);
        }
    }
    ElementGeometry.Builder = Builder;
    class IteratorEntry {
        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(props) : undefined);
        }
        /** Return the [[ImageGraphic]] representation for the current entry */
        toImageGraphic() {
            const props = toImageGraphic(this.value, this.outputTransform);
            return (undefined !== props ? ImageGraphic.fromJSON(props) : undefined);
        }
        /** Return the GeometryPart information for the current entry */
        toGeometryPart(partToLocal, partToWorld) {
            if (undefined === partToLocal && undefined !== partToWorld)
                partToLocal = 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 {
        /** 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) {
            /** Current entry position */
            this._index = 0;
            /** Whether deserialized entry data is returned in world or local coordinates */
            this._applyLocalToWorld = false;
            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(categoryOrGeometryParams) : categoryOrGeometryParams;
            else
                this._appearance = new GeometryParams(Id64.invalid);
            if (undefined !== info.sourceToWorld)
                localToWorld = ElementGeometry.toTransform(info.sourceToWorld);
            if (undefined !== localToWorld)
                this._localToWorld = localToWorld;
            else
                this._localToWorld = Transform.createIdentity();
            const orgAng = YawPitchRollAngles.tryFromTransform(this._localToWorld);
            if (undefined === orgAng.angles)
                orgAng.angles = YawPitchRollAngles.createDegrees(0, 0, 0);
            let bbox = (undefined !== info.bbox ? ElementGeometry.toElementAlignedBox3d(info.bbox) : undefined);
            if (undefined === bbox)
                bbox = Range3d.createNull();
            this.placement = new 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.ByteBuffer(entry.data);
                const ppfb = EGFBAccessors.PointPrimitive.getRootAsPointPrimitive(buffer);
                return (EGFBAccessors.BoundaryType.Open === ppfb.boundary());
            }
            case ElementGeometryOpcode.PointPrimitive2d: {
                const buffer = new flatbuffers.ByteBuffer(entry.data);
                const ppfb = EGFBAccessors.PointPrimitive2d.getRootAsPointPrimitive2d(buffer);
                return (EGFBAccessors.BoundaryType.Open === ppfb.boundary());
            }
            case ElementGeometryOpcode.ArcPrimitive: {
                const buffer = new flatbuffers.ByteBuffer(entry.data);
                const ppfb = EGFBAccessors.ArcPrimitive.getRootAsArcPrimitive(buffer);
                return (EGFBAccessors.BoundaryType.Open === ppfb.boundary());
            }
            case ElementGeometryOpcode.CurvePrimitive: {
                // should never be a point string or closed bcurve...
                return true;
            }
            case ElementGeometryOpcode.CurveCollection: {
                const geom = 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.ByteBuffer(entry.data);
                const ppfb = EGFBAccessors.PointPrimitive.getRootAsPointPrimitive(buffer);
                return (EGFBAccessors.BoundaryType.Closed === ppfb.boundary());
            }
            case ElementGeometryOpcode.PointPrimitive2d: {
                const buffer = new flatbuffers.ByteBuffer(entry.data);
                const ppfb = EGFBAccessors.PointPrimitive2d.getRootAsPointPrimitive2d(buffer);
                return (EGFBAccessors.BoundaryType.Closed === ppfb.boundary());
            }
            case ElementGeometryOpcode.ArcPrimitive: {
                const buffer = new flatbuffers.ByteBuffer(entry.data);
                const ppfb = EGFBAccessors.ArcPrimitive.getRootAsArcPrimitive(buffer);
                return (EGFBAccessors.BoundaryType.Closed === ppfb.boundary());
            }
            case ElementGeometryOpcode.CurvePrimitive: {
                // should never be a closed bcurve...
                return false;
            }
            case ElementGeometryOpcode.CurveCollection: {
                const geom = BentleyGeometryFlatBuffer.bytesToGeometry(entry.data, true);
                if (undefined === geom || Array.isArray(geom))
                    return false;
                return ("curveCollection" === geom.geometryCategory && geom.isAnyRegionType);
            }
            case ElementGeometryOpcode.SolidPrimitive: {
                const geom = BentleyGeometryFlatBuffer.bytesToGeometry(entry.data, true);
                if (undefined === geom || Array.isArray(geom))
                    return false;
                return ("solid" === geom.geometryCategory && !geom.isClosedVolume);
            }
            case ElementGeometryOpcode.Polyface: {
                const geom = 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 !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.ByteBuffer(entry.data);
                const ppfb = EGFBAccessors.BRepData.getRootAsBRepData(buffer);
                return (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 = BentleyGeometryFlatBuffer.bytesToGeometry(entry.data, true);
                if (undefined === geom || Array.isArray(geom))
                    return false;
                return ("solid" === geom.geometryCategory && geom.isClosedVolume);
            }
            case ElementGeometryOpcode.Polyface: {
                const geom = 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 PolyfaceQuery.isPolyfaceClosedByEdgePairing(polyface);
                    case 2:
                        return true;
                    case 1:
                    default:
                        return false;
                }
            }
            case ElementGeometryOpcode.BRep: {
                const buffer = new flatbuffers.ByteBuffer(entry.data);
                const ppfb = EGFBAccessors.BRepData.getRootAsBRepData(buffer);
                return (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.ByteBuffer(entry.data);
                const ppfb = EGFBAccessors.PointPrimitive.getRootAsPointPrimitive(buffer);
                if (EGFBAccessors.BoundaryType.None === ppfb.boundary())
                    return undefined;
                return (EGFBAccessors.BoundaryType.Closed === ppfb.boundary() ? BRepEntity.Type.Sheet : BRepEntity.Type.Wire);
            }
            case ElementGeometryOpcode.PointPrimitive2d: {
                const buffer = new flatbuffers.ByteBuffer(entry.data);
                const ppfb = EGFBAccessors.PointPrimitive2d.getRootAsPointPrimitive2d(buffer);
                if (EGFBAccessors.BoundaryType.None === ppfb.boundary())
                    return undefined;
                return (EGFBAccessors.BoundaryType.Closed === ppfb.boundary() ? BRepEntity.Type.Sheet : BRepEntity.Type.Wire);
            }
            case ElementGeometryOpcode.ArcPrimitive: {
                const buffer = new flatbuffers.ByteBuffer(entry.data);
                const ppfb = EGFBAccessors.ArcPrimitive.getRootAsArcPrimitive(buffer);
                return (EGFBAccessors.BoundaryType.Closed === ppfb.boundary() ? BRepEntity.Type.Sheet : BRepEntity.Type.Wire);
            }
            case ElementGeometryOpcode.CurvePrimitive: {
                // should never be a point string or closed bcurve...
                return BRepEntity.Type.Wire;
            }
            case ElementGeometryOpcode.CurveCollection: {
                const geom = 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 ? BRepEntity.Type.Sheet : BRepEntity.Type.Wire);
            }
            case ElementGeometryOpcode.SolidPrimitive: {
                const geom = 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 ? BRepEntity.Type.Solid : BRepEntity.Type.Sheet);
            }
            case ElementGeometryOpcode.BsplineSurface: {
                // always a surface...
                return BRepEntity.Type.Sheet;
            }
            case ElementGeometryOpcode.Polyface: {
                const geom = 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 PolyfaceQuery.isPolyfaceClosedByEdgePairing(polyface) ? BRepEntity.Type.Solid : BRepEntity.Type.Sheet;
                    case 1:
                        return BRepEntity.Type.Sheet;
                    case 2:
                        return BRepEntity.Type.Solid;
                    default:
                        return undefined;
                }
            }
            case ElementGeometryOpcode.BRep: {
                const buffer = new flatbuffers.ByteBuffer(entry.data);
                const ppfb = EGFBAccessors.BRepData.getRootAsBRepData(buffer);
                switch (ppfb.brepType()) {
                    case EGFBAccessors.BRepType.Wire:
                        return BRepEntity.Type.Wire; // always be persisted as a curve type...
                    case EGFBAccessors.BRepType.Sheet:
                        return BRepEntity.Type.Sheet;
                    case EGFBAccessors.BRepType.Solid:
                        return 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.ByteBuffer(entry.data);
                const ppfb = EGFBAccessors.PointPrimitive.getRootAsPointPrimitive(buffer);
                const pts = [];
                for (let i = 0; i < ppfb.coordsLength(); i++)
                    pts.push(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 EGFBAccessors.BoundaryType.Open:
                        return LineString3d.createPoints(pts);
                    case EGFBAccessors.BoundaryType.Closed:
                        return Loop.createPolygon(pts);
                    default:
                        return PointString3d.createPoints(pts);
                }
            }
            case ElementGeometryOpcode.PointPrimitive2d: {
                const buffer = new flatbuffers.ByteBuffer(entry.data);
                const ppfb = EGFBAccessors.PointPrimitive2d.getRootAsPointPrimitive2d(buffer);
                const pts = [];
                for (let i = 0; i < ppfb.coordsLength(); i++)
                    pts.push(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 EGFBAccessors.BoundaryType.Open:
                        return LineString3d.createPoints(pts);
                    case EGFBAccessors.BoundaryType.Closed:
                        return Loop.createPolygon(pts);
                    default:
                        return PointString3d.createPoints(pts);
                }
            }
            case ElementGeometryOpcode.ArcPrimitive: {
                const buffer = new flatbuffers.ByteBuffer(entry.data);
                const ppfb = EGFBAccessors.ArcPrimitive.getRootAsArcPrimitive(buffer);
                const center = Point3d.create(ppfb.center().x(), ppfb.center().y(), ppfb.center().z());
                const vector0 = Vector3d.create(ppfb.vector0().x(), ppfb.vector0().y(), ppfb.vector0().z());
                const vector90 = Vector3d.create(ppfb.vector90().x(), ppfb.vector90().y(), ppfb.vector90().z());
                const arc = Arc3d.create(center, vector0, vector90, AngleSweep.createStartSweepRadians(ppfb.start(), ppfb.sweep()));
                if (undefined !== localToWorld && !arc.tryTransformInPlace(localToWorld))
                    return undefined;
                return (EGFBAccessors.BoundaryType.Closed === ppfb.boundary() ? Loop.create(arc) : arc);
            }
            case ElementGeometryOpcode.CurvePrimitive:
            case ElementGeometryOpcode.CurveCollection:
            case ElementGeometryOpcode.SolidPrimitive:
            case ElementGeometryOpcode.BsplineSurface:
            case ElementGeometryOpcode.Polyface:
                const geom = 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 = 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.ByteBuffer(entry.data);
        const ppfb = 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 = Point3d.create(transform.form3d03(), transform.form3d13(), transform.form3d23());
            props.rotation = YawPitchRollAngles.createFromMatrix3d(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(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.ByteBuffer(entry.data);
        const ppfb = EGFBAccessors.TextString.getRootAsTextString(buffer);
        const glyphOriginToPoint2d = (origin) => {
            if (!origin)
                throw new Error("Value cannot be null.");
            return new Point2d(origin.x(), origin.y());
        };
        const textStringRangeToRange2d = (r) => r ? new 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(text);
            if (!localText.transformInPlace(worldToLocal))
                return undefined;
            text = localText.toJSON();
        }
        const fbb = new flatbuffers.Builder();
        const builder = EGFBAccessors.TextString;
        const textOffset = fbb.createString(text.text);
        const styleOffset = 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()) {
                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, styleOffset);
        if (undefined !== text.origin || undefined !== text.rotation) {
            const origin = Point3d.fromJSON(text.origin);
            const angles = YawPitchRollAngles.fromJSON(text.rotation);
            const matrix = angles.toMatrix3d();
            const coffs = matrix.coffs;
            const transformOffset = EGFBAccessors.TextStringTransform.createTextStringTransform(fbb, coffs[0], coffs[1], coffs[2], origin.x, coffs[3], coffs[4], coffs[5], origin.y, coffs[6], coffs[7], coffs[8], origin.z);
            builder.addTransform(fbb, transformOffset);
        }
        if (fbbGlyphs) {
            builder.addRange(fbb, EGFBAccessors.TextStringRange.createTextStringRange(fbb, fbbGlyphs.range.low.x, fbbGlyphs.range.low.y, fbbGlyphs.range.high.x, fbbGlyphs.range.high.y));
            builder.addGlyphIds(fbb, fbbGlyphs.glyphIdsOffset);
            builder.addGlyphOrigins(fbb, fbbGlyphs.glyphOriginsOffset);
        }
        const mLoc = builder.endTextString(fbb);
        fbb.finish(mLoc);
        const data = fbb.asUint8Array();
        return { opcode: ElementGeometryOpcode.TextString, data };
    }
    ElementGeometry.fromTextString = fromTextString;
    /** Return entry as a [[ImageGraphic]] */
    function toImageGraphic(entry, localToWorld) {
        if (ElementGeometryOpcode.Image !== entry.opcode)
            return undefined;
        const buffer = new flatbuffers.ByteBuffer(entry.data);
        const ppfb = EGFBAccessors.Image.getRootAsImage(buffer);
        const textureLong = ppfb.textureId();
        const textureId = Id64.fromUint32Pair(textureLong.low, textureLong.high);
        const hasBorder = (1 === ppfb.drawBorder());
        const corners = new ImageGraphicCorners(Point3d.createZero(), Point3d.createZero(), Point3d.createZero(), Point3d.createZero());
        const corner0 = ppfb.tileCorner0();
        const corner1 = ppfb.tileCorner1();
        const corner2 = ppfb.tileCorner2();
        const corner3 = ppfb.tileCorner3();
        if (null !== corner0)
            corners[0].setFrom(Point3d.create(corner0.x(), corner0.y(), corner0.z()));
        if (null !== corner1)
            corners[1].setFrom(Point3d.create(corner1.x(), corner1.y(), corner1.z()));
        if (null !== corner2)
            corners[2].setFrom(Point3d.create(corner2.x(), corner2.y(), corner2.z()));
        if (null !== corner3)
            corners[3].setFrom(Point3d.create(corner3.x(), corner3.y(), corner3.z()));
        if (undefined !== localToWorld) {
            localToWorld.multiplyXYAndZInPlace(corners[0]);
            localToWorld.multiplyXYAndZInPlace(corners[1]);
            localToWorld.multiplyXYAndZInPlace(corners[2]);
            localToWorld.multiplyXYAndZInPlace(corners[3]);
        }
        return { corners: corners.toJSON(), textureId, hasBorder };
    }
    ElementGeometry.toImageGraphic = toImageGraphic;
    /** Create entry from a [[ImageGraphic]] */
    function fromImageGraphic(image, worldToLocal) {
        if (undefined !== worldToLocal) {
            const localImage = ImageGraphic.fromJSON(image).cloneTransformed(worldToLocal);
            image = localImage.toJSON();
        }
        const fbb = new flatbuffers.Builder();
        const builder = EGFBAccessors.Image;
        builder.startImage(fbb);
        const textudeIdPair = Id64.getUint32Pair(image.textureId);
        builder.addTextureId(fbb, flatbuffers.Long.create(textudeIdPair.lower, textudeIdPair.upper));
        builder.addDrawBorder(fbb, image.hasBorder ? 1 : 0);
        const corners = ImageGraphicCorners.fromJSON(image.corners);
        const cornerOffset0 = EGFBAccessors.DPoint3d.createDPoint3d(fbb, corners[0].x, corners[0].y, corners[0].z);
        builder.addTileCorner0(fbb, cornerOffset0);
        const cornerOffset1 = EGFBAccessors.DPoint3d.createDPoint3d(fbb, corners[1].x, corners[1].y, corners[1].z);
        builder.addTileCorner1(fbb, cornerOffset1);
        const cornerOffset2 = EGFBAccessors.DPoint3d.createDPoint3d(fbb, corners[2].x, corners[2].y, corners[2].z);
        builder.addTileCorner2(fbb, cornerOffset2);
        const cornerOffset3 = EGFBAccessors.DPoint3d.createDPoint3d(fbb, corners[3].x, corners[3].y, corners[3].z);
        builder.addTileCorner3(fbb, cornerOffset3);
        const mLoc = builder.endImage(fbb);
        fbb.finish(mLoc);
        const data = fbb.asUint8Array();
        return { opcode: ElementGeometryOpcode.Image, data };
    }
    ElementGeometry.fromImageGraphic = fromImageGraphic;
    /** Return entry as a [[BRepEntity.DataProps]] for checking brep type and face attachments. */
    function toBRep(entry, wantBRepData = false, localToWorld) {
        if (ElementGeometryOpcode.BRep !== entry.opcode)
            return undefined;
        const buffer = new flatbuffers.ByteBuffer(entry.data);
        const ppfb = EGFBAccessors.BRepData.getRootAsBRepData(buffer);
        const toBRepType = (typeFb) => {
            switch (typeFb) {
                case EGFBAccessors.BRepType.Wire:
                    return BRepEntity.Type.Wire;
                case EGFBAccessors.BRepType.Sheet:
                    return BRepEntity.Type.Sheet;
                default:
                    return BRepEntity.Type.Solid;
            }
        };
        const type = toBRepType(ppfb.brepType());
        let transform;
        const entityTransform = ppfb.entityTransform();
        if (null !== entityTransform)
            transform = Transform.createRowValues(entityTransform.x00(), entityTransform.x01(), entityTransform.x02(), entityTransform.tx(), entityTransform.x10(), entityTransform.x11(), entityTransform.x12(), entityTransform.ty(), entityTransform.x20(), entityTransform.x21(), entityTransform.x22(), entityTransform.tz());
        if (undefined !== localToWorld) {
            if (undefined !== transform)
                localToWorld.multiplyTransformTransform(transform, transform);
            else
                transform = localToWorld;
        }
        const faceSymbLen = ppfb.symbologyLength();
        let faceSymbology;
        if (0 !== faceSymbLen) {
            const faceSymbPropsArray = [];
            for (let index = 0; index < faceSymbLen; ++index) {
                const faceSymbFb = ppfb.symbology(index);
                const faceSymbProps = {};
                if (null !== faceSymbFb) {
                    if (1 === faceSymbFb.useColor())
                        faceSymbProps.color = faceSymbFb.color();
                    if (1 === faceSymbFb.useMaterial())
                        faceSymbProps.materialId = Id64.fromUint32Pair(faceSymbFb.materialId().low, faceSymbFb.materialId().high);
                    faceSymbProps.transparency = faceSymbFb.transparency();
                }
                faceSymbPropsArray.push(faceSymbProps);
            }
            faceSymbology = faceSymbPropsArray;
        }
        let data;
        const entityData = ppfb.entityDataArray();
        if (wantBRepData && null !== entityData)
            data = Base64EncodedString.fromUint8Array(entityData);
        return { data, type, transform: transform?.toJSON(), faceSymbology };
    }
    ElementGeometry.toBRep = toBRep;
    /** Create entry from a [[BRepEntity.DataProps]]. Provided for compatibility with GeometryStreamBuilder only. */
    function fromBRep(brep, worldToLocal) {
        if (undefined !== worldToLocal) {
            const entityTrans = Transform.fromJSON(brep.transform);
            const localTrans = worldToLocal.multiplyTransformTransform(entityTrans);
            brep = {
                data: brep.data,
                type: brep.type,
                transform: localTrans.isIdentity ? undefined : localTrans.toJSON(),
                faceSymbology: brep.faceSymbology,
            };
        }
        const fbb = new flatbuffers.Builder();
        const builder = EGFBAccessors.BRepData;
        let dataOffset;
        let faceSymbOffset;
        if (undefined !== brep.data) {
            const entityData = Base64EncodedString.toUint8Array(brep.data);
            if (entityData.length === 0)
                return undefined;
            dataOffset = builder.createEntityDataVector(fbb, entityData);
        }
        if (undefined !== brep.faceSymbology) {
            builder.startSymbologyVector(fbb, brep.faceSymbology.length);
            for (let i = brep.faceSymbology.length - 1; i >= 0; i--) {
                const symb = brep.faceSymbology[i];
                const materialIdPair = Id64.getUint32Pair(symb.materialId ? symb.materialId : Id64.invalid);
                const matLong = flatbuffers.Long.create(materialIdPair.lower, materialIdPair.upper);
                EGFBAccessors.FaceSymbology.createFaceSymbology(fbb, symb.color ? 1 : 0, symb.materialId ? 1 : 0, symb.color ? symb.color : 0, matLong, symb.transparency ? symb.transparency : 0, 0, 0);
            }
            faceSymbOffset = fbb.endVector();
        }
        builder.startBRepData(fbb);
        const toEGFBBRepType = (type) => {
            switch (type) {
                case BRepEntity.Type.Wire:
                    return EGFBAccessors.BRepType.Wire;
                case BRepEntity.Type.Sheet:
                    return EGFBAccessors.BRepType.Sheet;
                default:
                    return EGFBAccessors.BRepType.Solid;
            }
        };
        if (undefined !== brep.type)
            builder.addBrepType(fbb, toEGFBBRepType(brep.type));
        if (undefined !== brep.transform) {
            const transform = Transform.fromJSON(brep.transform);
            const transformOffset = EGFBAccessors.Transform.createTransform(fbb, transform.matrix.coffs[0], transform.matrix.coffs[1], transform.matrix.coffs[2], transform.origin.x, transform.matrix.coffs[3], transform.matrix.coffs[4], transform.matrix.coffs[5], transform.origin.y, transform.matrix.coffs[6], transform.matrix.coffs[7], transform.matrix.coffs[8], transform.origin.z);
            builder.addEntityTransform(fbb, transformOffset);
        }
        if (undefined !== dataOffset)
            builder.addEntityData(fbb, dataOffset);
        if (undefined !== faceSymbOffset)
            builder.addSymbology(fbb, faceSymbOffset);
        const mLoc = builder.endBRepData(fbb);
        fbb.finish(mLoc);
        const data = fbb.asUint8Array();
        return { opcode: ElementGeometryOpcode.BRep, data };
    }
    ElementGeometry.fromBRep = fromBRep;
    /** Apply transform directly to ElementGeometryDataEntry for a BRep */
    function transformBRep(entry, inputTransform) {
        if (ElementGeometryOpcode.BRep !== entry.opcode)
            return false;
        const buffer = new flatbuffers.ByteBuffer(entry.data);
        const ppfb = EGFBAccessors.BRepData.getRootAsBRepData(buffer);
        const fbb = new flatbuffers.Builder();
        const builder = EGFBAccessors.BRepData;
        const entityData = ppfb.entityDataArray();
        const dataOffset = (null !== entityData ? builder.createEntityDataVector(fbb, entityData) : undefined);
        let faceSymbOffset;
        const faceSymbLen = ppfb.symbologyLength();
        if (0 !== faceSymbLen) {
            builder.startSymbologyVector(fbb, faceSymbLen);
            for (let i = faceSymbLen - 1; i >= 0; i--) {
                const faceSymbFb = ppfb.symbology(i);
                if (null === faceSymbFb)
                    continue;
                EGFBAccessors.FaceSymbology.createFaceSymbology(fbb, faceSymbFb.useColor(), faceSymbFb.useMaterial(), faceSymbFb.color(), faceSymbFb.materialId(), faceSymbFb.transparency(), 0, 0);
            }
            faceSymbOffset = fbb.endVector();
        }
        builder.startBRepData(fbb);
        builder.addBrepType(fbb, ppfb.brepType());
        let transform;
        const entityTransform = ppfb.entityTransform();
        if (null !== entityTransform)
            transform = Transform.createRowValues(entityTransform.x00(), entityTransform.x01(), entityTransform.x02(), entityTransform.tx(), entityTransform.x10(), entityTransform.x11(), entityTransform.x12(), entityTransform.ty(), entityTransform.x20(), entityTransform.x21(), entityTransform.x22(), entityTransform.tz());
        if (undefined !== transform)
            inputTransform.multiplyTransformTransform(transform, transform);
        else
            transform = inputTransform;
        const transformOffset = EGFBAccessors.Transform.createTransform(fbb, transform.matrix.coffs[0], transform.matrix.coffs[1], transform.matrix.coffs[2], transform.origin.x, transform.matrix.coffs[3], transform.matrix.coffs[4], transform.matrix.coffs[5], transform.origin.y, transform.matrix.coffs[6], transform.matrix.coffs[7], transform.matrix.coffs[8], transform.origin.z);
        builder.addEntityTransform(fbb, transformOffset);
        if (undefined !== dataOffset)
            builder.addEntityData(fbb, dataOffset);
        if (undefined !== faceSymbOffset)
            builder.addSymbology(fbb, faceSymbOffset);
        const mLoc = builder.endBRepData(fbb);
        fbb.finish(mLoc);
        entry.data = fbb.asUint8Array();
        return true;
    }
    ElementGeometry.transformBRep = transformBRep;
    /** @internal */
    let StyleMod;
    (function (StyleMod) {
        StyleMod[StyleMod["Scale"] = 1] = "Scale";
        StyleMod[StyleMod["DashScale"] = 2] = "DashScale";
        StyleMod[StyleMod["GapScale"] = 4] = "GapScale";
        StyleMod[StyleMod["StartWidth"] = 8] = "StartWidth";
        StyleMod[StyleMod["EndWidth"] = 16] = "EndWidth";
        StyleMod[StyleMod["DistPhase"] = 32] = "DistPhase";
        StyleMod[StyleMod["FractPhase"] = 64] = "FractPhase";
        StyleMod[StyleMod["CenterPhase"] = 128] = "CenterPhase";
        StyleMod[StyleMod["Normal"] = 256] = "Normal";
        StyleMod[StyleMod["Rotation"] = 512] = "Rotation";
        StyleMod[StyleMod["PhysicalWidth"] = 8192] = "PhysicalWidth";
        StyleMod[StyleMod["SegMode"] = 1073741824] = "SegMode";
        StyleMod[StyleMod["NoSegMode"] = 2147483648] = "NoSegMode";
    })(StyleMod || (StyleMod = {}));
    /** Update the supplied [[GeometryParams]] from an entry with appearance information */
    function updateGeometryParams(entry, geomParams, localToWorld) {
        if (!isAppearanceEntry(entry))
            return false;
        let changed = false;
        switch (entry.opcode) {
            case ElementGeometryOpcode.BasicSymbology: {
                const buffer = new flatbuffers.ByteBuffer(entry.data);
                const ppfb = EGFBAccessors.BasicSymbology.getRootAsBasicSymbology(buffer);
                const subcatLong = ppfb.subCategoryId();
                let subcatId = Id64.fromUint32Pair(subcatLong.low, subcatLong.high);
                if (Id64.isInvalid(subcatId))
                    subcatId = geomParams.subCategoryId; // Preserve current sub-category if not explicitly stored...
                if (Id64.isValid(subcatId)) {
                    geomParams.setSubCategoryId(subcatId); // Reset to sub-category appearance...
                    changed = true;
                }
                if (ppfb.useColor()) {
                    const lineColor = ColorDef.fromTbgr(ppfb.color());
                    if (undefined === geomParams.lineColor || !lineColor.equals(geomParams.lineColor)) {
                        geomParams.lineColor = lineColor;
                        changed = true;
                    }
                }
                if (ppfb.useWeight()) {
                    const weight = ppfb.weight();
                    if (undefined === geomParams.weight || weight !== geomParams.weight) {
                        geomParams.weight = weight;
                        changed = true;
                    }
                }
                if (ppfb.useStyle()) {
                    const styleLong = ppfb.lineStyleId();
                    const styleId = Id64.fromUint32Pair(styleLong.low, styleLong.high);
                    if (undefined === geomParams.styleInfo || styleId !== geomParams.styleInfo.styleId) {
                        geomParams.styleInfo = new LineStyle.Info(styleId);
                        changed = true;
                    }
                }
                const transparency = ppfb.transparency();
                if (transparency !== (undefined !== geomParams.elmTransparency ? geomParams.elmTransparency : 0)) {
                    geomParams.elmTransparency = transparency;
                    changed = true;
                }
                const displayPriority = ppfb.displayPriority();
                if (displayPriority !== (undefined !== geomParams.elmPriority ? geomParams.elmPriority : 0)) {
                    geomParams.elmPriority = displayPriority;
                    changed = true;
                }
                const geometryClass = ppfb.geomClass();
                if (geometryClass !== (undefined !== geomParams.geometryClass ? geomParams.geometryClass : GeometryClass.Primary)) {
                    geomParams.geometryClass = geometryClass;
                    changed = true;
                }
                return changed;
            }
            case ElementGeometryOpcode.LineStyleModifiers: {
                const buffer = new flatbuffers.ByteBuffer(entry.data);
                const ppfb = EGFBAccessors.LineStyleModifiers.getRootAsLineStyleModifiers(buffer);
                const props = {};
                if (0 !== (ppfb.modifiers() & StyleMod.Scale))
                    props.scale = ppfb.scale();
                if (0 !== (ppfb.modifiers() & StyleMod.GapScale))
                    props.gapScale = ppfb.gapScale();
                if (0 !== (ppfb.modifiers() & StyleMod.DashScale))
                    props.dashScale = ppfb.dashScale();
                if (0 !== (ppfb.modifiers() & StyleMod.StartWidth))
                    props.startWidth = ppfb.startWidth();
                if (0 !== (ppfb.modifiers() & StyleMod.EndWidth))
                    props.endWidth = ppfb.endWidth();
                if (0 !== (ppfb.modifiers() & StyleMod.DistPhase))
                    props.distPhase = ppfb.distPhase();
                if (0 !== (ppfb.modifiers() & StyleMod.FractPhase))
                    props.fractPhase = ppfb.fractPhase();
                if (0 !== (ppfb.modifiers() & StyleMod.CenterPhase))
                    props.centerPhase = true;
                if (0 !== (ppfb.modifiers() & StyleMod.SegMode))
                    props.segmentMode = true;
                else if (0 !== (ppfb.modifiers() & StyleMod.NoSegMode))
                    props.segmentMode = false;
                if (0 !== (ppfb.modifiers() & StyleMod.PhysicalWidth))
                    props.physicalWidth = true;
                if (0 !== (ppfb.modifiers() & StyleMod.Normal)) {
                    const normal = ppfb.normal();
                    if (null !== normal)
                        props.normal = Vector3d.create(normal.x(), normal.y(), normal.z());
                }
                if (0 !== (ppfb.modifiers() & StyleMod.Rotation))
                    props.rotation = YawPitchRollAngles.createDegrees(ppfb.yaw(), ppfb.pitch(), ppfb.roll());
                const styleMod = new LineStyle.Modifier(props);
                if (undefined !== localToWorld)
                    styleMod.applyTransform(localToWorld);
                if (undefined === geomParams.styleInfo) {
                    geomParams.styleInfo = new LineStyle.Info(Id64.invalid, styleMod);
                    changed = true;
                }
                else if (undefined === geomParams.styleInfo.styleMod || !styleMod.equals(geomParams.styleInfo.styleMod)) {
                    geomParams.styleInfo.styleMod = styleMod;
                    changed = true;
                }
                return changed;
            }
            case ElementGeometryOpcode.Fill: {
                const buffer = new flatbuffers.ByteBuffer(entry.data);
                const ppfb = EGFBAccessors.AreaFill.getRootAsAreaFill(buffer);
                const toFillDisplay = (fill) => {
                    switch (fill) {
                        case EGFBAccessors.FillDisplay.ByView:
                            return FillDisplay.ByView;
                        case EGFBAccessors.FillDisplay.Always:
                            return FillDisplay.Always;
                        case EGFBAccessors.FillDisplay.Blanking:
                            return FillDisplay.Blanking;
                        default:
                            return FillDisplay.Never;
                    }
                };
                const fillDisplay = toFillDisplay(ppfb.fill());
                if (fillDisplay !== (undefined !== geomParams.fillDisplay ? geomParams.fillDisplay : FillDisplay.Never)) {
                    geomParams.fillDisplay = fillDisplay;
                    changed = true;
                }
                if (FillDisplay.Never === fillDisplay)
                    return changed;
                const transparency = ppfb.transparency();
                if (transparency !== (undefined !== geomParams.fillTransparency ? geomParams.fillTransparency : 0)) {
                    geomParams.fillTransparency = transparency;
                    changed = true;
                }
                if (EGFBAccessors.GradientMode.None !== ppfb.mode()) {
                    const toGradientMode = (mode) => {
                        switch (mode) {
                            case EGFBAccessors.GradientMode.Linear:
                                return Gradient.Mode.Linear;
                            case EGFBAccessors.GradientMode.Curved:
                                return Gradient.Mode.Curved;
                            case EGFBAccessors.GradientMode.Cylindrical:
                                return Gradient.Mode.Cylindrical;
                            case EGFBAccessors.GradientMode.Spherical:
                                return Gradient.Mode.Spherical;
                            case EGFBAccessors.GradientMode.Hemispherical:
                                return Gradient.Mode.Hemispherical;
                            case EGFBAccessors.GradientMode.Thematic:
                                return Gradient.Mode.Thematic;
                            default:
                                return Gradient.Mode.None;
                        }
                    };
                    const keys = [];
                    const colors = ppfb.colorsArray();
                    const values = ppfb.valuesArray();
                    if (null !== colors && null !== values && colors.length === values.length) {
                        for (let iKey = 0; iKey < colors.length; ++iKey)
                            keys.push({ value: values[iKey], color: colors[iKey] });
                    }
                    const props = { mode: toGradientMode(ppfb.mode()), keys };
                    const flags = ppfb.flags();
                    const angle = ppfb.angle();
                    const tint = ppfb.tint();
                    const shift = ppfb.shift();
                    props.flags = (0 !== flags ? flags : undefined);
                    props.angle = (0 !== angle ? { radians: angle } : undefined);
                    props.tint = (0 !== tint ? tint : undefined);
                    props.shift = (0 !== shift ? shift : undefined);
                    if (Gradient.Mode.Thematic === props.mode) {
                        const thematic = ppfb.thematicSettings();
                        if (null !== thematic) {
                            const tprops = {};
                            const mode = thematic.mode();
                            const stepCount = thematic.stepCount();
                            const marginColor = thematic.marginColor();
                            const colorScheme = thematic.colorScheme();
                            tprops.mode = (0 !== mode ? mode : undefined);
                            tprops.stepCount = (0 !== stepCount ? stepCount : undefined);
                            tprops.marginColor = (0 !== marginColor ? marginColor : undefined);
                            tprops.colorScheme = (0 !== colorScheme ? colorScheme : undefined);
                            props.thematicSettings = tprops;
                        }
                    }
                    const gradient = Gradient.Symb.fromJSON(props);
                    if (undefined === geomParams.gradient || !gradient.equals(geomParams.gradient)) {
                        geomParams.gradient = gradient;
                        changed = true;
                    }
                }
                else if (0 !== ppfb.backgroundFill()) {
                    const backgroundFill = (2 === ppfb.backgroundFill() ? BackgroundFill.Outline : BackgroundFill.Solid);
                    if (backgroundFill !== geomParams.backgroundFill) {
                        geomParams.backgroundFill = backgroundFill;
                        changed = true;
                    }
                }
                else if (ppfb.useColor()) {
                    const fillColor = ColorDef.fromTbgr(ppfb.color());
                    if (undefined === geomParams.fillColor || !fillColor.equals(geomParams.fillColor)) {
                        geomParams.fillColor = fillColor;
                        changed = true;
                    }
                }
                return changed;
            }
            case ElementGeometryOpcode.Pattern: {
                const buffer = new flatbuffers.ByteBuffer(entry.data);
                const ppfb = EGFBAccessors.AreaPattern.getRootAsAreaPattern(buffer);
                const props = {};
                const origin = ppfb.origin();
                if (null !== origin) {
                    const point = Point3d.create(origin.x(), origin.y(), origin.z());
                    if (!point.isAlmostZero)
                        props.origin = point;
                }
                const rotation = ppfb.rotation();
                if (null !== rotation) {
                    const angles = YawPitchRollAngles.createFromMatrix3d(Matrix3d.createRowValues(rotation.x00(), rotation.x01(), rotation.x02(), rotation.x10(), rotation.x11(), rotation.x12(), rotation.x20(), rotation.x21(), rotation.x22()));
                    if (undefined !== angles && !angles.isIdentity())
                        props.rotation = angles;
                }
                const space1 = ppfb.space1();
                const space2 = ppfb.space2();
                const angle1 = ppfb.angle1();
                const angle2 = ppfb.angle2();
                const scale = ppfb.scale();
                props.space1 = (0 !== space1 ? space1 : undefined);
                props.space2 = (0 !== space2 ? space2 : undefined);
                props.angle1 = (0 !== angle1 ? { radians: angle1 } : undefined);
                props.angle2 = (0 !== angle2 ? { radians: angle2 } : undefined);
                props.scale = (0 !== scale ? scale : undefined);
                if (ppfb.useColor())
                    props.color = ppfb.color();
                if (ppfb.useWeight())
                    props.weight = ppfb.weight();
                props.invisibleBoundary = (1 === ppfb.invisibleBoundary() ? true : undefined);
                props.snappable = (1 === ppfb.snappable() ? true : undefined);
                const symbolId = Id64.fromUint32Pair(ppfb.symbolId().low, ppfb.symbolId().high);
                props.symbolId = (Id64.isValid(symbolId) ? symbolId : undefined);
                const nDefLines = ppfb.defLineLength();
                if (0 !== nDefLines) {
                    const defLines = [];
                    for (let iLine = 0; iLine < nDefLines; ++iLine) {
                        const defLine = ppfb.defLine(iLine);
                        if (!defLine)
                            continue;
                        const line = {};
                        const angle = defLine.angle();
                        line.angle = (0 !== angle ? { radians: defLine.angle() } : undefined);
                        const through = defLine.through();
                        if (through) {
                            const thr = Point2d.create(through.x(), through.y());
                            if (!thr.isAlmostZero)
                                line.through = thr;
                        }
                        const offset = defLine.offset();
                        if (offset) {
                            const off = Point2d.create(offset.x(), offset.y());
                            if (!off.isAlmostZero)
                                line.offset = off;
                        }
                        const dashArray = defLine.dashesArray();
                        if (0 !== defLine.dashesLength() && null !== dashArray) {
                            const dashes = [];
                            for (const value of dashArray) {
                                dashes.push(value);
                            }
                            line.dashes = dashes;
                        }
                        defLines.push(line);
                    }
                    props.defLines = defLines;
                }
                const pattern = AreaPattern.Params.fromJSON(props);
                if (undefined !== localToWorld)
                    pattern.applyTransform(localToWorld);
                if (undefined === geomParams.pattern || !pattern.equals(geomParams.pattern)) {
                    geomParams.pattern = pattern;
                    changed = true;
                }
                return changed;
            }
            case ElementGeometryOpcode.Material: {
                const buffer = new flatbuffers.ByteBuffer(entry.data);
                const ppfb = EGFBAccessors.Material.getRootAsMaterial(buffer);
                if (ppfb.useMaterial()) {
                    const matLong = ppfb.materialId();
                    const materialId = Id64.fromUint32Pair(matLong.low, matLong.high);
                    if (undefined === geomParams.materialId || materialId !== geomParams.materialId) {
                        geomParams.materialId = materialId;
                        changed = true;
                    }
                }
                return changed;
            }
            default:
                return changed;
        }
    }
    ElementGeometry.updateGeometryParams = updateGeometryParams;
    /** Append entries to represent a [[GeometryParams]] */
    function appendGeometryParams(geomParams, entries, worldToLocal) {
        const fbbBas = new flatbuffers.Builder();
        const builder = EGFBAccessors.BasicSymbology;
        builder.startBasicSymbology(fbbBas);
        const subcatIdPair = Id64.getUint32Pair(geomParams.subCategoryId);
        builder.addSubCategoryId(fbbBas, flatbuffers.Long.create(subcatIdPair.lower, subcatIdPair.upper));
        if (undefined !== geomParams.lineColor) {
            builder.addColor(fbbBas, geomParams.lineColor.tbgr);
            builder.addUseColor(fbbBas, 1);
        }
        if (undefined !== geomParams.weight) {
            builder.addWeight(fbbBas, geomParams.weight);
            builder.addUseWeight(fbbBas, 1);
        }
        if (undefined !== geomParams.styleInfo) {
            const styleIdPair = Id64.getUint32Pair(geomParams.styleInfo.styleId);
            builder.addLineStyleId(fbbBas, flatbuffers.Long.create(styleIdPair.lower, styleIdPair.upper));
            builder.addUseStyle(fbbBas, 1);
        }
        if (undefined !== geomParams.elmTransparency && 0 !== geomParams.elmTransparency) {
            builder.addTransparency(fbbBas, geomParams.elmTransparency);
        }
        if (undefined !== geomParams.elmPriority && 0 !== geomParams.elmPriority) {
            builder.addDisplayPriority(fbbBas, geomParams.elmPriority);
        }
        if (undefined !== geomParams.geometryClass && GeometryClass.Primary !== geomParams.geometryClass) {
            builder.addGeomClass(fbbBas, geomParams.geometryClass);
        }
        const mLoc = builder.endBasicSymbology(fbbBas);
        fbbBas.finish(mLoc);
        const data = fbbBas.asUint8Array();
        entries.push({ opcode: ElementGeometryOpcode.BasicSymbology, data });
        if (undefined !== geomParams.styleInfo && undefined !== geomParams.styleInfo.styleMod) {
            const fbbLS = new flatbuffers.Builder();
            const builderLS = EGFBAccessors.LineStyleModifiers;
            builderLS.startLineStyleModifiers(fbbLS);
            let lsMods = geomParams.styleInfo.styleMod;
            if (undefined !== worldToLocal) {
                lsMods = new LineStyle.Modifier(lsMods);
                if (!lsMods.applyTransform(worldToLocal))
                    return false;
            }
            let modifiers = 0;
            if (undefined !== lsMods.scale) {
                builderLS.addScale(fbbLS, lsMods.scale);
                modifiers |= StyleMod.Scale;
            }
            if (undefined !== lsMods.dashScale) {
                builderLS.addDashScale(fbbLS, lsMods.dashScale);
                modifiers |= StyleMod.DashScale;
            }
            if (undefined !== lsMods.gapScale) {
                builderLS.addGapScale(fbbLS, lsMods.gapScale);
                modifiers |= StyleMod.GapScale;
            }
            if (undefined !== lsMods.startWidth) {
                builderLS.addStartWidth(fbbLS, lsMods.startWidth);
                modifiers |= StyleMod.StartWidth;
            }
            if (undefined !== lsMods.endWidth) {
                builderLS.addEndWidth(fbbLS, lsMods.endWidth);
                modifiers |= StyleMod.EndWidth;
            }
            if (undefined !== lsMods.distPhase) {
                builderLS.addDistPhase(fbbLS, lsMods.distPhase);
                modifiers |= StyleMod.DistPhase;
            }
            if (undefined !== lsMods.fractPhase) {
                builderLS.addFractPhase(fbbLS, lsMods.fractPhase);
                modifiers |= StyleMod.FractPhase;
            }
            if (lsMods.centerPhase) {
                modifiers |= StyleMod.CenterPhase;
            }
            if (undefined !== lsMods.segmentMode) {
                modifiers |= (lsMods.segmentMode ? StyleMod.SegMode : StyleMod.NoSegMode);
            }
            if (lsMods.physicalWidth) {
                modifiers |= StyleMod.PhysicalWidth;
            }
            if (undefined !== lsMods.normal) {
                const normalOffset = EGFBAccessors.DVec3d.createDVec3d(fbbLS, lsMods.normal.x, lsMods.normal.y, lsMods.normal.z);
                builderLS.addNormal(fbbLS, normalOffset);
                modifiers |= StyleMod.Normal;
            }
            if (undefined !== lsMods.rotation) {
                builderLS.addYaw(fbbLS, lsMods.rotation.yaw.degrees);
                builderLS.addPitch(fbbLS, lsMods.rotation.pitch.degrees);
                builderLS.addRoll(fbbLS, lsMods.rotation.roll.degrees);
                modifiers |= StyleMod.Rotation;
            }
            builderLS.addModifiers(fbbLS, modifiers);
            const mLocLS = builderLS.endLineStyleModifiers(fbbLS);
            fbbLS.finish(mLocLS);
            const dataLS = fbbLS.asUint8Array();
            if (0 !== modifiers)
                entries.push({ opcode: ElementGeometryOpcode.LineStyleModifiers, data: dataLS });
        }
        if (undefined !== geomParams.fillDisplay && FillDisplay.Never !== geomParams.fillDisplay) {
            const fbbFill = new flatbuffers.Builder();
            const builderFill = EGFBAccessors.AreaFill;
            let keyValuesOff;
            let keyColorsOff;
            let thematicOffset;
            if (undefined !== geomParams.gradient) {
                const keyColors = [];
                const keyValues = [];
                for (const key of geomParams.gradient.keys) {
                    keyColors.push(key.color.tbgr);
                    keyValues.push(key.value);
                }
                keyValuesOff = builderFill.createValuesVector(fbbFill, keyValues);
                keyColorsOff = builderFill.createColorsVector(fbbFill, keyColors);
                if (Gradient.Mode.Thematic === geomParams.gradient.mode && undefined !== geomParams.gradient.thematicSettings) {
                    const thematic = geomParams.gradient.thematicSettings;
                    const builderThematic = EGFBAccessors.ThematicSettings;
                    builderThematic.startThematicSettings(fbbFill);
                    builderThematic.addMode(fbbFill, thematic.mode);
                    builderThematic.addStepCount(fbbFill, thematic.stepCount);
                    builderThematic.addMarginColor(fbbFill, thematic.marginColor.tbgr);
                    builderThematic.addColorScheme(fbbFill, thematic.colorScheme);
                    const rangeOffset = EGFBAccessors.DRange1d.createDRange1d(fbbFill, ThematicGradientSettings.contentRange, ThematicGradientSettings.contentMax);
                    builderThematic.addRange(fbbFill, rangeOffset);
                    thematicOffset = builderThematic.endThematicSettings(fbbFill);
                }
            }
            builderFill.startAreaFill(fbbFill);
            const toEGFBFillDisplay = (fill) => {
                switch (fill) {
                    case FillDisplay.ByView:
                        return EGFBAccessors.FillDisplay.ByView;
                    case FillDisplay.Always:
                        return EGFBAccessors.FillDisplay.Always;
                    case FillDisplay.Blanking:
                        return EGFBAccessors.FillDisplay.Blanking;
                    default:
                        return EGFBAccessors.FillDisplay.None;
                }
            };
            builderFill.addFill(fbbFill, toEGFBFillDisplay(geomParams.fillDisplay));
            if (undefined !== geomParams.fillTransparency)
                builderFill.addTransparency(fbbFill, geomParams.fillTransparency);
            if (undefined !== geomParams.gradient) {
                const toEGFBGradientMode = (mode) => {
                    switch (mode) {
                        case Gradient.Mode.Linear:
                            return EGFBAccessors.GradientMode.Linear;
                        case Gradient.Mode.Curved:
                            return EGFBAccessors.GradientMode.Curved;
                        case Gradient.Mode.Cylindrical:
                            return EGFBAccessors.GradientMode.Cylindrical;
                        case Gradient.Mode.Spherical:
                            return EGFBAccessors.GradientMode.Spherical;
                        case Gradient.Mode.Hemispherical:
                            return EGFBAccessors.GradientMode.Hemispherical;
                        case Gradient.Mode.Thematic:
                            return EGFBAccessors.GradientMode.Thematic;
                        default:
                            return EGFBAccessors.GradientMode.None;
                    }
                };
                builderFill.addMode(fbbFill, toEGFBGradientMode(geomParams.gradient.mode));
                builderFill.addFlags(fbbFill, geomParams.gradient.flags);
                builderFill.addShift(fbbFill, geomParams.gradient.shift);
                if (undefined !== geomParams.gradient.tint)
                    builderFill.addTint(fbbFill, geomParams.gradient.tint);
                if (undefined !== geomParams.gradient.angle)
                    builderFill.addAngle(fbbFill, geomParams.gradient.angle.radians);
                if (undefined !== keyValuesOff)
                    builderFill.addValues(fbbFill, keyValuesOff);
                if (undefined !== keyColorsOff)
                    builderFill.addColors(fbbFill, keyColorsOff);
                if (undefined !== thematicOffset)
                    builderFill.addThematicSettings(fbbFill, thematicOffset);
            }
            else if (undefined !== geomParams.backgroundFill && BackgroundFill.None !== geomParams.backgroundFill) {
                builderFill.addBackgroundFill(fbbFill, BackgroundFill.Outline === geomParams.backgroundFill ? 2 : 1);
            }
            else if (undefined !== geomParams.fillColor) {
                builderFill.addColor(fbbFill, geomParams.fillColor.tbgr);
                builderFill.addUseColor(fbbFill, 1);
            }
            const mLocFill = builderFill.endAreaFill(fbbFill);
            fbbFill.finish(mLocFill);
            const dataFill = fbbFill.asUint8Array();
            entries.push({ opcode: ElementGeometryOpcode.Fill, data: dataFill });
        }
        if (undefined !== geomParams.pattern) {
            const fbbPat = new flatbuffers.Builder();
            const builderPat = EGFBAccessors.AreaPattern;
            let pattern = geomParams.pattern;
            if (undefined !== worldToLocal) {
                pattern = pattern.clone();
                if (!pattern.applyTransform(worldToLocal))
                    return false;
            }
            let defLineVecOffset;
            if (undefined !== pattern.defLines) {
                const defLines = pattern.defLines;
                const builderDefLines = EGFBAccessors.DwgHatchDefLine;
                const defLineOffsets = [];
                for (const line of defLines) {
                    let dashOffset;
                    if (undefined !== line.dashes)
                        dashOffset = builderDefLines.createDashesVector(fbbPat, line.dashes);
                    builderDefLines.startDwgHatchDefLine(fbbPat);
                    if (undefined !== line.angle)
                        builderDefLines.addAngle(fbbPat, line.angle.radians);
                    // NOTE: Backend requires through and offset to be present...
                    const throughOffset = EGFBAccessors.DPoint2d.createDPoint2d(fbbPat, undefined !== line.through ? line.through.x : 0, undefined !== line.through ? line.through.y : 0);
                    builderDefLines.addThrough(fbbPat, throughOffset);
                    const offsetOffset = EGFBAccessors.DPoint2d.createDPoint2d(fbbPat, undefined !== line.offset ? line.offset.x : 0, undefined !== line.offset ? line.offset.y : 0);
                    builderDefLines.addOffset(fbbPat, offsetOffset);
                    if (undefined !== dashOffset)
                        builderDefLines.addDashes(fbbPat, dashOffset);
                    defLineOffsets.push(builderDefLines.endDwgHatchDefLine(fbbPat));
                }
                if (0 !== defLineOffsets.length)
                    defLineVecOffset = builderPat.createDefLineVector(fbbPat, defLineOffsets);
            }
            builderPat.startAreaPattern(fbbPat);
            if (undefined !== pattern.origin) {
                const originOffset = EGFBAccessors.DPoint3d.createDPoint3d(fbbPat, pattern.origin.x, pattern.origin.y, pattern.origin.z);
                builderPat.addOrigin(fbbPat, originOffset);
            }
            if (undefined !== pattern.rotation) {
                const matrix = pattern.rotation.toMatrix3d();
                const coffs = matrix.coffs;
                const rotationOffset = EGFBAccessors.RotMatrix.createRotMatrix(fbbPat, coffs[0], coffs[1], coffs[2], coffs[3], coffs[4], coffs[5], coffs[6], coffs[7], coffs[8]);
                builderPat.addRotation(fbbPat, rotationOffset);
            }
            if (undefined !== pattern.space1)
                builderPat.addSpace1(fbbPat, pattern.space1);
            if (undefined !== pattern.space2)
                builderPat.addSpace2(fbbPat, pattern.space2);
            if (undefined !== pattern.angle1)
                builderPat.addAngle1(fbbPat, pattern.angle1.radians);
            if (undefined !== pattern.angle2)
                builderPat.addAngle2(fbbPat, pattern.angle2.radians);
            if (undefined !== pattern.scale)
                builderPat.addScale(fbbPat, pattern.scale);
            if (undefined !== pattern.color) {
                builderPat.addColor(fbbPat, pattern.color.tbgr);
                builderPat.addUseColor(fbbPat, 1);
            }
            if (undefined !== pattern.weight) {
                builderPat.addWeight(fbbPat, pattern.weight);
                builderPat.addUseWeight(fbbPat, 1);
            }
            if (undefined !== pattern.invisibleBoundary && pattern.invisibleBoundary)
                builderPat.addInvisibleBoundary(fbbPat, 1);
            if (undefined !== pattern.snappable && pattern.snappable)
                builderPat.addSnappable(fbbPat, 1);
            if (undefined !== pattern.symbolId) {
                const symbolIdPair = Id64.getUint32Pair(pattern.symbolId);
                builderPat.addSymbolId(fbbPat, flatbuffers.Long.create(symbolIdPair.lower, symbolIdPair.upper));
            }
            if (undefined !== defLineVecOffset)
                builderPat.addDefLine(fbbPat, defLineVecOffset);
            const mLocPat = builderPat.endAreaPattern(fbbPat);
            fbbPat.finish(mLocPat);
            const dataPat = fbbPat.asUint8Array();
            entries.push({ opcode: ElementGeometryOpcode.Pattern, data: dataPat });
        }
        if (undefined !== geomParams.materialId) {
            const fbbMat = new flatbuffers.Builder();
            const builderMat = EGFBAccessors.Material;
            builderMat.startMaterial(fbbMat);
            const matIdPair = Id64.getUint32Pair(geomParams.materialId);
            builderMat.addMaterialId(fbbMat, flatbuffers.Long.create(matIdPair.lower, matIdPair.upper));
            builderMat.addUseMaterial(fbbMat, 1);
            const mLocMat = builderMat.endMaterial(fbbMat);
            fbbMat.finish(mLocMat);
            const dataMat = fbbMat.asUint8Array();
            entries.push({ opcode: ElementGeometryOpcode.Material, data: dataMat });
        }
        return true;
    }
    ElementGeometry.appendGeometryParams = appendGeometryParams;
    /** Return entry as [[GeometryPart]] id and instance transform */
    function toGeometryPart(entry, partToElement) {
        if (ElementGeometryOpcode.PartReference !== entry.opcode)
            return undefined;
        const buffer = new flatbuffers.ByteBuffer(entry.data);
        const ppfb = EGFBAccessors.GeometryPart.getRootAsGeometryPart(buffer);
        const partLong = ppfb.geomPartId();
        const partId = Id64.fromUint32Pair(partLong.low, partLong.high);
        if (undefined !== partToElement) {
            let origin;
            const originfb = ppfb.origin();
            if (null !== originfb)
                origin = Point3d.create(originfb.x(), originfb.y(), originfb.z());
            else
                origin = Point3d.createZero();
            const angles = YawPitchRollAngles.createDegrees(ppfb.yaw(), ppfb.pitch(), ppfb.roll());
            const matrix = angles.toMatrix3d();
            if (1.0 !== ppfb.scale())
                matrix.scaleColumnsInPlace(ppfb.scale(), ppfb.scale(), ppfb.scale());
            Transform.createOriginAndMatrix(origin, matrix, partToElement);
        }
        return partId;
    }
    ElementGeometry.toGeometryPart = toGeometryPart;
    /** Create entry from a [[GeometryPart]] id and instance transform */
    function fromGeometryPart(partId, partTransform, worldToLocal) {
        let partToElement = partTransform;
        if (undefined !== worldToLocal)
            partToElement = worldToLocal.multiplyTransformTransform(undefined !== partTransform ? partTransform : Transform.createIdentity());
        const fbb = new flatbuffers.Builder();
        const builder = EGFBAccessors.GeometryPart;
        builder.startGeometryPart(fbb);
        const idPair = Id64.getUint32Pair(partId);
        builder.addGeomPartId(fbb, flatbuffers.Long.create(idPair.lower, idPair.upper));
        if (undefined !== partToElement && !partToElement.isIdentity) {
            const result = partToElement.matrix.factorRigidWithSignedScale();
            if (undefined === result)
                return undefined;
            const originOffset = EGFBAccessors.DPoint3d.createDPoint3d(fbb, partToElement.origin.x, partToElement.origin.y, partToElement.origin.z);
            builder.addOrigin(fbb, originOffset);
            const angles = YawPitchRollAngles.createFromMatrix3d(result.rigidAxes);
            if (undefined !== angles) {
                builder.addYaw(fbb, angles.yaw.degrees);
                builder.addPitch(fbb, angles.pitch.degrees);
                builder.addRoll(fbb, angles.roll.degrees);
            }
            if (result.scale > 0.0)
                builder.addScale(fbb, result.scale);
        }
        const mLoc = builder.endGeometryPart(fbb);
        fbb.finish(mLoc);
        const data = fbb.asUint8Array();
        return { opcode: ElementGeometryOpcode.PartReference, data };
    }
    ElementGeometry.fromGeometryPart = fromGeometryPart;
    /** Return entry as a [[ElementAlignedBox3d]] representing the local range of a geometric entry */
    function toSubGraphicRange(entry) {
        if (ElementGeometryOpcode.SubGraphicRange !== entry.opcode)
            return undefined;
        const buffer = new flatbuffers.ByteBuffer(entry.data);
        const ppfb = EGFBAccessors.PointPrimitive.getRootAsPointPrimitive(buffer);
        if (2 !== ppfb.coordsLength())
            return undefined;
        const low = Point3d.create(ppfb.coords(0).x(), ppfb.coords(0).y(), ppfb.coords(0).z());
        const high = Point3d.create(ppfb.coords(1).x(), ppfb.coords(1).y(), ppfb.coords(1).z());
        return Range3d.create(low, high);
    }
    ElementGeometry.toSubGraphicRange = toSubGraphicRange;
    /** Create entry from a [[ElementAlignedBox3d]] to compute and store local ranges for subsequent geometric entries */
    function fromSubGraphicRange(bbox) {
        const fbb = new flatbuffers.Builder();
        const builder = EGFBAccessors.PointPrimitive;
        builder.startCoordsVector(fbb, 2);
        fbb.addFloat64(bbox.high.z);
        fbb.addFloat64(bbox.high.y);
        fbb.addFloat64(bbox.high.x);
        fbb.addFloat64(bbox.low.z);
        fbb.addFloat64(bbox.low.y);
        fbb.addFloat64(bbox.low.x);
        const offset = fbb.endVector();
        builder.startPointPrimitive(fbb);
        builder.addCoords(fbb, offset);
        const mLoc = builder.endPointPrimitive(fbb);
        fbb.finish(mLoc);
        const data = fbb.asUint8Array();
        return { opcode: ElementGeometryOpcode.SubGraphicRange, data };
    }
    ElementGeometry.fromSubGraphicRange = fromSubGraphicRange;
    /** Create [[Transform]] from row-major storage 4x3 Float64Array */
    function toTransform(sourceToWorld) {
        if (12 !== sourceToWorld.length)
            return undefined;
        return Transform.createRowValues(sourceToWorld[0], sourceToWorld[1], sourceToWorld[2], sourceToWorld[3], sourceToWorld[4], sourceToWorld[5], sourceToWorld[6], sourceToWorld[7], sourceToWorld[8], sourceToWorld[9], sourceToWorld[10], sourceToWorld[11]);
    }
    ElementGeometry.toTransform = toTransform;
    /** Create [[ElementAlignedBox3d]] from lowX, lowY, lowZ, highX, highY, highZ Float64Array */
    function toElementAlignedBox3d(bbox) {
        if (6 !== bbox.length)
            return undefined;
        return Range3d.fromFloat64Array(bbox);
    }
    ElementGeometry.toElementAlignedBox3d = toElementAlignedBox3d;
})(ElementGeometry || (ElementGeometry = {}));
//# sourceMappingURL=ElementGeometry.js.map