"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module DisplayStyles
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.RenderSchedule = void 0;
const core_bentley_1 = require("@itwin/core-bentley");
const core_geometry_1 = require("@itwin/core-geometry");
const RgbColor_1 = require("./RgbColor");
const FeatureSymbology_1 = require("./FeatureSymbology");
function interpolate(start, end, fraction) {
    return start + fraction * (end - start);
}
function interpolateRgb(start, end, fraction) {
    return new RgbColor_1.RgbColor(interpolate(start.r, end.r, fraction), interpolate(start.g, end.g, fraction), interpolate(start.b, end.b, fraction));
}
function compareXYZ(lhs, rhs) {
    return (0, core_bentley_1.compareNumbers)(lhs.x, rhs.x) || (0, core_bentley_1.compareNumbers)(lhs.y, rhs.y) || (0, core_bentley_1.compareNumbers)(lhs.z, rhs.z);
}
function compare4d(lhs, rhs) {
    return (0, core_bentley_1.compareNumbers)(lhs.x, rhs.x) || (0, core_bentley_1.compareNumbers)(lhs.y, rhs.y) || (0, core_bentley_1.compareNumbers)(lhs.z, rhs.z) || (0, core_bentley_1.compareNumbers)(lhs.w, rhs.w);
}
const scratchVec3a = new core_geometry_1.Vector3d();
const scratchVec3b = new core_geometry_1.Vector3d();
function compareMatrices(lhs, rhs) {
    return compareXYZ(lhs.columnX(scratchVec3a), rhs.columnX(scratchVec3b))
        || compareXYZ(lhs.columnY(scratchVec3a), rhs.columnY(scratchVec3b))
        || compareXYZ(lhs.columnZ(scratchVec3a), rhs.columnZ(scratchVec3b));
}
function compareDurations(lhs, rhs) {
    return (0, core_bentley_1.compareNumbers)(lhs.low, rhs.low) || (0, core_bentley_1.compareNumbers)(lhs.high, rhs.high);
}
/** Namespace containing types that collectively define a script that animates the contents of a view by adjusting the visibility, position,
 * and/or symbology of groups of elements over time. A [[RenderSchedule.Script]] is hosted by a [RenderTimeline]($backend) element. The script
 * can be associated with a [DisplayStyleState]($frontend) by way of its [[DisplayStyleSettings.renderTimeline]] property.
 * @public
 */
var RenderSchedule;
(function (RenderSchedule) {
    /** Defines how two interpolate between two entries in a [[RenderSchedule.Timeline]].
     * @note Currently only Linear and Step are supported. Any other value is treated as Step.
     * @see [[RenderSchedule.TimelineEntry]].
     */
    let Interpolation;
    (function (Interpolation) {
        /** Each timeline entry's value is discrete - the timeline jumps from one entry's value directly to another. */
        Interpolation[Interpolation["Step"] = 1] = "Step";
        /** Given two entries on the timeline and a timepoint in between them, linearly interpolate based on the timepoint's distance between the
         * two entries.
         */
        Interpolation[Interpolation["Linear"] = 2] = "Linear";
    })(Interpolation = RenderSchedule.Interpolation || (RenderSchedule.Interpolation = {}));
    /** Describes the value of some property at a specific point along a [[RenderSchedule.Timeline]].
     * @see [[RenderSchedule.VisibilityEntry]]
     * @see [[RenderSchedule.ColorEntry]]
     * @see [[RenderSchedule.TransformEntry]]
     * @see [[RenderSchedule.CuttingPlaneEntry]]
     */
    class TimelineEntry {
        /** The time point in seconds in the [Unix Epoch](https://en.wikipedia.org/wiki/Unix_time). */
        time;
        /** How to interpolate from this entry to the next entry in the timeline. */
        interpolation;
        constructor(props) {
            this.time = props.time;
            this.interpolation = props.interpolation === Interpolation.Linear ? props.interpolation : Interpolation.Step;
        }
        toJSON() {
            const props = {
                time: this.time,
            };
            if (this.interpolation === Interpolation.Linear)
                props.interpolation = this.interpolation;
            return props;
        }
        compareTo(other) {
            return (0, core_bentley_1.compareNumbers)(this.interpolation, other.interpolation) || (0, core_bentley_1.compareNumbers)(this.time, other.time);
        }
        equals(other) {
            return 0 === this.compareTo(other);
        }
    }
    RenderSchedule.TimelineEntry = TimelineEntry;
    /** A timeline entry that controls the visibility of the associated geometry. */
    class VisibilityEntry extends TimelineEntry {
        /** The visibility of the geometry at this point on the timeline, in the range [0, 100] where 0 is completely invisible, 100 is completely visible,
         * and values in between indicate increasing opacity.
         */
        value;
        constructor(props) {
            super(props);
            if (typeof props.value !== "number")
                this.value = 100;
            else
                this.value = Math.max(0, Math.min(100, props.value));
        }
        toJSON() {
            const props = super.toJSON();
            if (100 !== this.value)
                props.value = this.value;
            return props;
        }
        compareTo(other) {
            (0, core_bentley_1.assert)(other instanceof VisibilityEntry);
            return super.compareTo(other) || (0, core_bentley_1.compareNumbers)(this.value, other.value);
        }
    }
    RenderSchedule.VisibilityEntry = VisibilityEntry;
    /** A timeline entry controlling the color of the affected geometry. */
    class ColorEntry extends TimelineEntry {
        /** If defined, the color in which to draw the geometry. If undefined, the geometry is drawn in its actual color. */
        value;
        constructor(props) {
            super(props);
            if (props.value)
                this.value = new RgbColor_1.RgbColor(props.value.red, props.value.green, props.value.blue);
        }
        toJSON() {
            const props = super.toJSON();
            if (this.value) {
                props.value = {
                    red: this.value.r,
                    green: this.value.g,
                    blue: this.value.b,
                };
            }
            return props;
        }
        compareTo(other) {
            (0, core_bentley_1.assert)(other instanceof ColorEntry);
            return super.compareTo(other) || (0, core_bentley_1.comparePossiblyUndefined)((lhs, rhs) => lhs.compareTo(rhs), this.value, other.value);
        }
    }
    RenderSchedule.ColorEntry = ColorEntry;
    /** Describes the components of a [[RenderSchedule.TransformEntry]] as a rotation around a pivot point followed by a translation. */
    class TransformComponents {
        /** Pivot point - applied before rotation. */
        pivot;
        /** Quaternion rotation. */
        orientation;
        /** Translation - applied after rotation. */
        position;
        constructor(position, pivot, orientation) {
            this.position = position;
            this.pivot = pivot;
            this.orientation = orientation;
        }
        static fromJSON(props) {
            if (props.pivot && props.position && props.orientation)
                return new TransformComponents(core_geometry_1.Vector3d.fromJSON(props.position), core_geometry_1.Vector3d.fromJSON(props.pivot), core_geometry_1.Point4d.fromJSON(props.orientation));
            else
                return undefined;
        }
        toJSON() {
            return {
                position: [this.position.x, this.position.y, this.position.z],
                pivot: [this.pivot.x, this.pivot.y, this.pivot.z],
                orientation: [this.orientation.x, this.orientation.y, this.orientation.z, this.orientation.w],
            };
        }
        compareTo(other) {
            return compareXYZ(this.pivot, other.pivot) || compareXYZ(this.position, other.position) || compare4d(this.orientation, other.orientation);
        }
        equals(other) {
            return this.pivot.isAlmostEqual(other.pivot) && this.position.isAlmostEqual(other.position) && this.orientation.isAlmostEqual(other.orientation);
        }
    }
    RenderSchedule.TransformComponents = TransformComponents;
    /** A timeline entry that applies rotation, scaling, and/or translation to the affected geometry. */
    class TransformEntry extends TimelineEntry {
        /** The transform matrix to be applied to the geometry, used only if [[components]] is not defined. */
        value;
        /** The transform represented as a rotation about a pivot point followed by a translation. If undefined, [[value]] is used instead. */
        components;
        constructor(props) {
            super(props);
            this.value = props.value ? core_geometry_1.Transform.fromJSON(props.value.transform) : core_geometry_1.Transform.identity;
            if (props.value)
                this.components = TransformComponents.fromJSON(props.value);
        }
        toJSON() {
            const props = super.toJSON();
            if (this.components) {
                props.value = this.components.toJSON();
                props.value.transform = this.value.toRows();
            }
            else {
                props.value = { transform: this.value.toRows() };
            }
            return props;
        }
        compareTo(other) {
            (0, core_bentley_1.assert)(other instanceof TransformEntry);
            const cmp = super.compareTo(other);
            if (0 !== cmp)
                return cmp;
            if (this.components || other.components) {
                if (!this.components || !other.components)
                    return this.components ? 1 : -1;
                return this.components.compareTo(other.components);
            }
            return compareXYZ(this.value.origin, other.value.origin) || compareMatrices(this.value.matrix, other.value.matrix);
        }
    }
    RenderSchedule.TransformEntry = TransformEntry;
    /** Defines a [ClipPlane]($core-geometry) associated with a [[RenderSchedule.CuttingPlaneEntry]]. */
    class CuttingPlane {
        /** A point on the plane. */
        position;
        /** The direction perpendicular to the plane pointing toward the clip. */
        direction;
        /** If true, the clip plane is ignored and the geometry is never clipped. */
        visible;
        /** If true, the clip plane is ignored and the geometry is always clipped. */
        hidden;
        constructor(props) {
            this.position = core_geometry_1.Point3d.fromJSON(props.position);
            this.direction = core_geometry_1.Point3d.fromJSON(props.direction);
            this.hidden = true === props.hidden;
            this.visible = true === props.visible;
        }
        toJSON() {
            const props = {
                position: [this.position.x, this.position.y, this.position.z],
                direction: [this.direction.x, this.direction.y, this.direction.z],
            };
            if (this.visible)
                props.visible = true;
            if (this.hidden)
                props.hidden = true;
            return props;
        }
        compareTo(other) {
            return compareXYZ(this.position, other.position) || compareXYZ(this.direction, other.direction) || (0, core_bentley_1.compareBooleans)(this.visible, other.visible) || (0, core_bentley_1.compareBooleans)(this.hidden, other.hidden);
        }
        equals(other) {
            return 0 === this.compareTo(other);
        }
    }
    RenderSchedule.CuttingPlane = CuttingPlane;
    /** A timeline entry that applies a [ClipPlane]($core-geometry) to the affected geometry. */
    class CuttingPlaneEntry extends TimelineEntry {
        /** The definition of the [ClipPlane]($core-geometry), or undefined if this entry applies no clipping. */
        value;
        constructor(props) {
            super(props);
            if (props.value)
                this.value = new CuttingPlane(props.value);
        }
        toJSON() {
            const props = super.toJSON();
            if (this.value)
                props.value = this.value.toJSON();
            return props;
        }
        compareTo(other) {
            (0, core_bentley_1.assert)(other instanceof CuttingPlaneEntry);
            return super.compareTo(other) || (0, core_bentley_1.comparePossiblyUndefined)((x, y) => x.compareTo(y), this.value, other.value);
        }
    }
    RenderSchedule.CuttingPlaneEntry = CuttingPlaneEntry;
    /** Identifies a fractional position along a [[RenderSchedule.Timeline]] between any two [[RenderSchedule.TimelineEntry]]'s within a [[RenderSchedule.TimelineEntryList]].
     * @internal
     */
    class Interval {
        /** The index of the first timeline entry within the list. */
        lowerIndex;
        /** The index of the second timeline entry within the list. */
        upperIndex;
        /** The normalized distance between the two timeline entries. */
        fraction;
        constructor(lower = 0, upper = 0, fraction = 0) {
            this.init(lower, upper, fraction);
        }
        init(lower = 0, upper = 0, fraction = 0) {
            this.lowerIndex = lower;
            this.upperIndex = upper;
            this.fraction = fraction;
        }
    }
    RenderSchedule.Interval = Interval;
    /** A list of the [[RenderSchedule.TimelineEntry]] objects within a [[RenderSchedule.Timeline]]. The type parameters are:
     *  - T, a subclass of TimelineEntry with a `value` property specifying the value of the property controlled by the timeline at that entry's time point.
     *  - P, the JSON representation from which T is to be constructed.
     *  - V, the type of `T.value`.
     */
    class TimelineEntryList {
        _entries;
        /** The total time period represented by the entries in this list. */
        duration;
        constructor(props, ctor) {
            this.duration = core_geometry_1.Range1d.createNull();
            this._entries = props.map((x) => {
                const entry = new ctor(x);
                this.duration.extendX(entry.time);
                return entry;
            });
        }
        /** The number of entries in the list. */
        get length() {
            return this._entries.length;
        }
        /** An iterator over the entries in the list. */
        [Symbol.iterator]() {
            return this._entries[Symbol.iterator]();
        }
        /** Look up an entry by its position in the list. */
        getEntry(index) {
            return this._entries[index];
        }
        /** Look up the value of an entry by its position in the list. */
        getValue(index) {
            return this.getEntry(index)?.value;
        }
        toJSON() {
            return this._entries.map((x) => x.toJSON());
        }
        compareTo(other) {
            let cmp = (0, core_bentley_1.compareNumbers)(this._entries.length, other._entries.length) || compareDurations(this.duration, other.duration);
            if (0 === cmp) {
                for (let i = 0; i < this.length; i++)
                    if (0 !== (cmp = this._entries[i].compareTo(other._entries[i])))
                        break;
            }
            return cmp;
        }
        equals(other) {
            return 0 === this.compareTo(other);
        }
        /** @internal */
        findInterval(time, interval) {
            if (this.length === 0)
                return undefined;
            interval = interval ?? new Interval();
            if (time < this._entries[0].time) {
                interval.init(0, 0, 0);
                return interval;
            }
            const last = this.length - 1;
            if (time >= this._entries[last].time) {
                interval.init(last, last, 0);
                return interval;
            }
            for (let i = 0; i < last; i++) {
                const time0 = this._entries[i].time;
                const time1 = this._entries[i + 1].time;
                if (time0 <= time && time1 >= time) {
                    let fraction;
                    if (Interpolation.Linear === this._entries[i].interpolation)
                        fraction = (time - time0) / (time1 - time0);
                    else
                        fraction = 0;
                    interval.init(i, i + 1, fraction);
                    return interval;
                }
            }
            return undefined;
        }
    }
    RenderSchedule.TimelineEntryList = TimelineEntryList;
    const scratchInterval = new Interval();
    /** A list of [[RenderSchedule.VisibilityEntry]]s within a [[RenderSchedule.Timeline]]. */
    class VisibilityTimelineEntries extends TimelineEntryList {
        /** Returns the visibility value for the entry at the specified position in the list, or 100 (fully-visible) if no such entry exists. */
        getValue(index) {
            return super.getValue(index) ?? 100;
        }
    }
    RenderSchedule.VisibilityTimelineEntries = VisibilityTimelineEntries;
    /** A list of [[RenderSchedule.TransformEntry]]s within a [[RenderSchedule.Timeline]]. */
    class TransformTimelineEntries extends TimelineEntryList {
        /** Returns the transform for the entry at the specified position in the list, or an identity transform if no such entry exists. */
        getValue(index) {
            return super.getValue(index) ?? core_geometry_1.Transform.identity;
        }
    }
    RenderSchedule.TransformTimelineEntries = TransformTimelineEntries;
    /** Specifies how to animate a set of geometry over time within a [[RenderSchedule.Script]].
     * A [[RenderSchedule.Script]] can contain any number of [[RenderSchedule.Timeline]]s, each affecting different sets of geometry.
     * @see [[RenderSchedule.ElementTimeline]] and [[RenderSchedule.ModelTimeline]].
     */
    class Timeline {
        /** Sequence controlling the visibility of the geometry. */
        visibility;
        /** Sequence controlling the color of the geometry. */
        color;
        /** Sequence controlling the position, orientation, and/or scale of the geometry. */
        transform;
        /** Sequence controlling how the geometry is clipped. */
        cuttingPlane;
        /** The total time period represented by this timeline. */
        duration;
        /** Indicates whether the schedule editing session has been finalized and is no longer active.
         * @internal
         */
        isEditingCommitted = false;
        constructor(props) {
            this.duration = core_geometry_1.Range1d.createNull();
            if (props.visibilityTimeline) {
                this.visibility = new VisibilityTimelineEntries(props.visibilityTimeline, VisibilityEntry);
                this.duration.extendRange(this.visibility.duration);
            }
            if (props.colorTimeline) {
                this.color = new TimelineEntryList(props.colorTimeline, ColorEntry);
                this.duration.extendRange(this.color.duration);
            }
            if (props.transformTimeline) {
                this.transform = new TransformTimelineEntries(props.transformTimeline, TransformEntry);
                this.duration.extendRange(this.transform.duration);
            }
            if (props.cuttingPlaneTimeline) {
                this.cuttingPlane = new TimelineEntryList(props.cuttingPlaneTimeline, CuttingPlaneEntry);
                this.duration.extendRange(this.cuttingPlane.duration);
            }
        }
        toJSON() {
            return {
                visibilityTimeline: this.visibility?.toJSON(),
                colorTimeline: this.color?.toJSON(),
                transformTimeline: this.transform?.toJSON(),
                cuttingPlaneTimeline: this.cuttingPlane?.toJSON(),
            };
        }
        compareTo(other) {
            const cmp = compareDurations(this.duration, other.duration);
            if (0 !== cmp)
                return cmp;
            // Do cheaper checks before iterating through timeline entries
            if (!!this.visibility !== !!other.visibility)
                return this.visibility ? 1 : -1;
            else if (!!this.color !== !!other.color)
                return this.color ? 1 : -1;
            else if (!!this.transform !== !!other.transform)
                return this.transform ? 1 : -1;
            else if (!!this.cuttingPlane !== !!other.cuttingPlane)
                return this.cuttingPlane ? 1 : -1;
            return (0, core_bentley_1.comparePossiblyUndefined)((x, y) => x.compareTo(y), this.visibility, other.visibility) || (0, core_bentley_1.comparePossiblyUndefined)((x, y) => x.compareTo(y), this.color, other.color)
                || (0, core_bentley_1.comparePossiblyUndefined)((x, y) => x.compareTo(y), this.transform, other.transform) || (0, core_bentley_1.comparePossiblyUndefined)((x, y) => x.compareTo(y), this.cuttingPlane, other.cuttingPlane);
        }
        equals(other) {
            return 0 === this.compareTo(other);
        }
        /** Get the visibility of the geometry at the specified time point. */
        getVisibility(time) {
            let interval;
            if (!this.visibility || !(interval = this.visibility.findInterval(time, scratchInterval)))
                return 100;
            let visibility = this.visibility.getValue(interval.lowerIndex) ?? 100;
            if (interval.fraction > 0)
                visibility = interpolate(visibility, this.visibility.getValue(interval.upperIndex) ?? 100, interval.fraction);
            return visibility;
        }
        /** Get the color of the geometry at the specified time point, or undefined if the color is not overridden at that time point. */
        getColor(time) {
            let interval;
            if (!this.color || !(interval = this.color.findInterval(time, scratchInterval)))
                return undefined;
            const start = this.color.getValue(interval.lowerIndex);
            if (start && interval.fraction > 0) {
                const end = this.color.getValue(interval.upperIndex);
                if (end)
                    return interpolateRgb(start, end, interval.fraction);
            }
            return start;
        }
        /** Get the transform applied to the geometry at the specified time point. */
        getAnimationTransform(time) {
            let interval;
            if (!this.transform || !(interval = this.transform.findInterval(time, scratchInterval)))
                return core_geometry_1.Transform.identity;
            let transform = this.transform.getValue(interval.lowerIndex);
            if (interval.fraction > 0) {
                const comp0 = this.transform.getEntry(interval.lowerIndex)?.components;
                const comp1 = this.transform.getEntry(interval.upperIndex)?.components;
                if (comp0 && comp1) {
                    const sum = core_geometry_1.Point4d.interpolateQuaternions(comp0.orientation, interval.fraction, comp1.orientation);
                    const matrix = core_geometry_1.Matrix3d.createFromQuaternion(sum);
                    const pre = core_geometry_1.Transform.createTranslation(comp0.pivot);
                    const post = core_geometry_1.Transform.createTranslation(comp0.position.interpolate(interval.fraction, comp1.position));
                    const product = post.multiplyTransformMatrix3d(matrix);
                    product.multiplyTransformTransform(pre, product);
                    transform = product;
                }
                else {
                    const end = this.transform.getValue(interval.upperIndex);
                    const q0 = transform.matrix.inverse()?.toQuaternion();
                    const q1 = end.matrix.inverse()?.toQuaternion();
                    if (q0 && q1) {
                        const sum = core_geometry_1.Point4d.interpolateQuaternions(q0, interval.fraction, q1);
                        const matrix = core_geometry_1.Matrix3d.createFromQuaternion(sum);
                        const origin0 = core_geometry_1.Vector3d.createFrom(transform.origin);
                        const origin1 = core_geometry_1.Vector3d.createFrom(end.origin);
                        transform = core_geometry_1.Transform.createRefs(origin0.interpolate(interval.fraction, origin1), matrix);
                    }
                }
            }
            return transform;
        }
        /** Get the clipping plane applied to the geometry at the specified time point, or undefined if the geometry is unclipped at that time point. */
        getCuttingPlane(time) {
            let interval;
            if (!this.cuttingPlane || !(interval = this.cuttingPlane.findInterval(time, scratchInterval)))
                return undefined;
            const start = this.cuttingPlane.getValue(interval.lowerIndex);
            if (!start)
                return undefined;
            const position = core_geometry_1.Point3d.createFrom(start.position);
            const direction = core_geometry_1.Vector3d.createFrom(start.direction);
            const end = interval.fraction > 0 ? this.cuttingPlane.getValue(interval.upperIndex) : undefined;
            if (end) {
                position.interpolate(interval.fraction, end.position, position);
                direction.interpolate(interval.fraction, end.direction, direction);
            }
            else {
                if (start.hidden || start.visible)
                    return undefined;
            }
            direction.negate(direction);
            direction.normalizeInPlace();
            return core_geometry_1.Plane3dByOriginAndUnitNormal.create(position, direction);
        }
        /** Create a ClipVector from the [[RenderSchedule.CuttingPlane]] applied to the geometry at the specified time point, if any. */
        getClipVector(time) {
            const plane = this.getCuttingPlane(time);
            if (!plane)
                return undefined;
            const cp = core_geometry_1.ClipPlane.createPlane(plane);
            const cps = core_geometry_1.UnionOfConvexClipPlaneSets.createConvexSets([core_geometry_1.ConvexClipPlaneSet.createPlanes([cp])]);
            const prim = core_geometry_1.ClipPrimitive.createCapture(cps);
            return core_geometry_1.ClipVector.createCapture([prim]);
        }
        /** @internal */
        getFeatureAppearance(visibility, time) {
            const transparency = visibility < 100 ? (1 - visibility / 100) : undefined;
            const rgb = this.getColor(time);
            return undefined !== rgb || undefined !== transparency ? FeatureSymbology_1.FeatureAppearance.fromJSON({ rgb, transparency }) : undefined;
        }
    }
    RenderSchedule.Timeline = Timeline;
    /** Specifies how to animate the geometry belonging to a set of [GeometricElement]($backend)s as part of a [[RenderSchedule.Script]]. */
    class ElementTimeline extends Timeline {
        /** A positive integer that uniquely identififes this timeline among all ElementTimelines in the [[RenderSchedule.Script]]. */
        batchId;
        _elementIds;
        constructor(props) {
            super(props);
            this.batchId = props.batchId;
            this._elementIds = props.elementIds;
        }
        static fromJSON(props) {
            return new ElementTimeline(props ?? { elementIds: [], batchId: 0 });
        }
        toJSON() {
            return {
                ...super.toJSON(),
                batchId: this.batchId,
                elementIds: this._elementIds,
            };
        }
        get containsElementIds() {
            return this._elementIds.length > 0;
        }
        compareElementIds(other) {
            if (typeof this._elementIds === typeof other._elementIds) {
                const cmp = (0, core_bentley_1.compareNumbers)(this._elementIds.length, other._elementIds.length);
                if (0 !== cmp)
                    return cmp;
                if (typeof this._elementIds === "string") {
                    (0, core_bentley_1.assert)(typeof other._elementIds === "string");
                    return (0, core_bentley_1.compareStrings)(this._elementIds, other._elementIds);
                }
            }
            // One or both are stored as arrays, in which case they might contain the same Ids in different orders. We will consider them different in that case.
            const mine = this.elementIds[Symbol.iterator]();
            const theirs = other.elementIds[Symbol.iterator]();
            while (true) {
                const a = mine.next();
                const b = theirs.next();
                if (a.done !== b.done)
                    return (0, core_bentley_1.compareBooleans)(!!a.done, !!b.done);
                else if (a.done)
                    return 0;
                const cmp = (0, core_bentley_1.compareStrings)(a.value, b.value);
                if (0 !== cmp)
                    return cmp;
            }
        }
        compareTo(other) {
            (0, core_bentley_1.assert)(other instanceof ElementTimeline);
            return (0, core_bentley_1.compareNumbers)(this.batchId, other.batchId) || this.compareElementIds(other) || super.compareTo(other);
        }
        /** @internal */
        static getElementIds(ids) {
            if (typeof ids === "string")
                return core_bentley_1.CompressedId64Set.iterable(ids);
            else if (Array.isArray(ids)) {
                return ids;
            }
            else {
                return [];
            }
        }
        /** The Ids of the elements controlled by this timeline. */
        get elementIds() {
            return ElementTimeline.getElementIds(this._elementIds);
        }
        /** True if this timeline affects the color or transparency of the elements. */
        get containsFeatureOverrides() {
            return undefined !== this.visibility || undefined !== this.color;
        }
        /** If true, applying this timeline requires special tiles to be generated in which groups of elements are batched into nodes.
         * @internal
         */
        get requiresBatching() {
            if (this.cuttingPlane)
                return true;
            return this.batchId !== 0 && (undefined !== this.color || undefined !== this.visibility);
        }
        /** True if this timeline affects the position, orientation, or scale of the elements. */
        get containsTransform() {
            return undefined !== this.transform;
        }
        /** @internal */
        addSymbologyOverrides(overrides, time) {
            (0, core_bentley_1.assert)(0 !== this.batchId);
            const vis = this.getVisibility(time);
            if (vis <= 0) {
                overrides.setAnimationNodeNeverDrawn(this.batchId);
                return;
            }
            const appearance = this.getFeatureAppearance(vis, time);
            if (appearance)
                overrides.overrideAnimationNode(this.batchId, appearance);
        }
    }
    RenderSchedule.ElementTimeline = ElementTimeline;
    /** Specifies how to animate the geometry within a [GeometricModel]($backend) as part of a [[RenderSchedule.Script]]. */
    class ModelTimeline extends Timeline {
        /** The Id of the [GeometricModel]($backend) to be animated. */
        modelId;
        /** @internal */
        realityModelUrl;
        /** Timelines specifying how to animate groups of [GeometricElement]($backend)s within the model. */
        elementTimelines;
        /** @internal */
        transformBatchIds;
        /** True if this timeline affects the color or transparency of the geometry. */
        containsFeatureOverrides;
        /** True if this timeline applies clipping to the model. */
        containsModelClipping;
        /** If true, applying this timeline requires special tiles to be generated in which groups of elements are batched into nodes.
         * @internal
         */
        requiresBatching;
        /** True if this timeline affects the position, orientation, or scale of the geometry. */
        containsTransform;
        /** True if any [[RenderSchedule.ElementTimeline]]s exist and none of them contain any element Ids. This generally indicates that
         * the backend was instructed to omit the Ids to save space when supplying the script.
         */
        omitsElementIds;
        _maxBatchId;
        /** Tile tree suppliers perform **very** frequent ordered comparisons of ModelTimelines. They need to be fast. */
        _cachedComparisons = new WeakMap();
        /** When loading tiles we need to quickly map element Ids to batch Ids. This map is initialized on first call to [[getTimelineForElement]] to facilitate that lookup. */
        _idPairToElementTimeline;
        _discreteBatchIds;
        constructor(props) {
            super(props);
            this.modelId = props.modelId;
            this.realityModelUrl = props.realityModelUrl;
            this.containsModelClipping = undefined !== this.cuttingPlane;
            let containsFeatureOverrides = undefined !== this.visibility || undefined !== this.color;
            let requiresBatching = false;
            let containsTransform = false;
            const transformBatchIds = [];
            const elementTimelines = [];
            let containsElementIds = false;
            for (const elProps of props.elementTimelines) {
                const el = ElementTimeline.fromJSON(elProps);
                elementTimelines.push(el);
                this.duration.extendRange(el.duration);
                if (el.containsTransform) {
                    containsTransform = true;
                    if (el.batchId)
                        transformBatchIds.push(el.batchId);
                }
                containsFeatureOverrides ||= el.containsFeatureOverrides;
                requiresBatching ||= el.requiresBatching;
                containsElementIds = containsElementIds || el.containsElementIds;
            }
            this.elementTimelines = elementTimelines;
            this.transformBatchIds = transformBatchIds;
            this.omitsElementIds = elementTimelines.length > 0 && !containsElementIds;
            this.containsFeatureOverrides = containsFeatureOverrides;
            this.requiresBatching = requiresBatching;
            this.containsTransform = containsTransform;
        }
        static fromJSON(props) {
            return new ModelTimeline(props ?? { elementTimelines: [], modelId: core_bentley_1.Id64.invalid });
        }
        toJSON() {
            return {
                ...super.toJSON(),
                modelId: this.modelId,
                realityModelUrl: this.realityModelUrl,
                elementTimelines: this.elementTimelines.map((x) => x.toJSON()),
            };
        }
        compareTo(other) {
            if (this === other)
                return 0;
            const cached = this._cachedComparisons.get(other);
            if (undefined !== cached)
                return cached;
            (0, core_bentley_1.assert)(other instanceof ModelTimeline);
            let cmp = (0, core_bentley_1.compareStrings)(this.modelId, other.modelId) || (0, core_bentley_1.compareStringsOrUndefined)(this.realityModelUrl, other.realityModelUrl)
                || (0, core_bentley_1.compareNumbers)(this.elementTimelines.length, other.elementTimelines.length) || (0, core_bentley_1.compareBooleans)(this.containsFeatureOverrides, other.containsFeatureOverrides)
                || (0, core_bentley_1.compareBooleans)(this.containsModelClipping, other.containsModelClipping) || (0, core_bentley_1.compareBooleans)(this.containsTransform, other.containsTransform)
                || super.compareTo(other);
            if (0 === cmp) {
                for (let i = 0; i < this.elementTimelines.length; i++)
                    if (0 !== (cmp = this.elementTimelines[i].compareTo(other.elementTimelines[i])))
                        break;
            }
            this._cachedComparisons.set(other, cmp);
            other._cachedComparisons.set(this, -cmp);
            return cmp;
        }
        /** Look up the element timeline with the specified batch Id. */
        findByBatchId(batchId) {
            return this.elementTimelines.find((x) => x.batchId === batchId);
        }
        /** @internal */
        addSymbologyOverrides(overrides, time) {
            const appearance = this.getFeatureAppearance(this.getVisibility(time), time);
            if (appearance)
                overrides.override({ modelId: this.modelId, appearance });
            for (const timeline of this.elementTimelines)
                timeline.addSymbologyOverrides(overrides, time);
        }
        /** Obtain the transform applied to the model at the specified time point, if any. */
        getTransform(batchId, time) {
            return this.findByBatchId(batchId)?.getAnimationTransform(time);
        }
        /** Get the highest batchId of any ElementTimeline in this timeline. */
        get maxBatchId() {
            if (undefined === this._maxBatchId) {
                this._maxBatchId = 0;
                for (const elem of this.elementTimelines)
                    this._maxBatchId = Math.max(this._maxBatchId, elem.batchId);
            }
            return this._maxBatchId;
        }
        /** Given the two halves of an [Id64]($bentley) return the [[ElementTimeline]] containing the corresponding element.
         * @note The first call to this method populates a mapping for fast lookup.
         * @alpha
         */
        getTimelineForElement(idLo, idHi) {
            if (!this._idPairToElementTimeline) {
                this._idPairToElementTimeline = new core_bentley_1.Id64.Uint32Map();
                for (const timeline of this.elementTimelines) {
                    for (const elementId of timeline.elementIds) {
                        // NB: a malformed script may place the same element Id into multiple timelines. We're not going to check for such data errors here.
                        this._idPairToElementTimeline.setById(elementId, timeline);
                    }
                }
            }
            return this._idPairToElementTimeline.get(idLo, idHi);
        }
        /** The batch Ids of the subset of [[elementTimelines]] that apply a transform and/or cutting plane.
         * @alpha
         */
        get discreteBatchIds() {
            if (!this._discreteBatchIds) {
                this._discreteBatchIds = new Set(this.transformBatchIds);
                for (const timeline of this.elementTimelines)
                    if (!timeline.containsTransform && undefined !== timeline.cuttingPlane)
                        this._discreteBatchIds.add(timeline.batchId);
            }
            return this._discreteBatchIds;
        }
        /** @internal see ImdlReader.readAnimationBranches */
        getBatchIdForFeature(feature) {
            (0, core_bentley_1.assert)(core_bentley_1.Id64.fromUint32PairObject(feature.modelId) === this.modelId);
            const elementTimeline = this.getTimelineForElement(feature.elementId.lower, feature.elementId.upper);
            return elementTimeline?.batchId ?? 0;
        }
    }
    RenderSchedule.ModelTimeline = ModelTimeline;
    /** Specifies how to animate the contents of a [ViewState]($frontend) over time. The script contains any number of [[RenderSchedule.ModelTimeline]]s, each describing how
     * to animate one of the models in the view.
     * @see [RenderTimeline]($backend) to create an [Element]($backend) to host a script.
     * @see [[DisplayStyleSettings.renderTimeline]] to associate a [RenderTimeline]($backend)'s script with a [DisplayStyle]($backend).
     * @see [DisplayStyleState.scheduleScript]($frontend) to obtain the script associated with a display style.
     * @see [[RenderSchedule.ScriptBuilder]] to define a new script.
     */
    class Script {
        /** Timelines specifying how to animate individual models within the view. */
        modelTimelines;
        /** True if this script applies clipping to any models. */
        containsModelClipping;
        /** If true, applying this timeline requires special tiles to be generated in which groups of elements are batched into nodes.
         * @internal
         */
        requiresBatching;
        /** True if this script affects the position, orientation, or scale of the geometry. */
        containsTransform;
        /** True if this script affects the color or transparency of the geometry. */
        containsFeatureOverrides;
        /** The total time period over which this script animates. */
        duration;
        /** The batchIds of all nodes in all timelines that apply a transform.
         * @internal
         */
        transformBatchIds;
        /** Tile tree references perform **very** frequent ordered comparisons of Scripts. They need to be fast. */
        _cachedComparisons = new WeakMap();
        _discreteBatchIds;
        _lastFeatureModelTimeline;
        _maxBatchId;
        compareTo(other) {
            if (this === other)
                return 0;
            const cached = this._cachedComparisons.get(other);
            if (undefined !== cached)
                return cached;
            let cmp = (0, core_bentley_1.compareNumbers)(this.modelTimelines.length, other.modelTimelines.length) || (0, core_bentley_1.compareBooleans)(this.containsModelClipping, other.containsModelClipping)
                || (0, core_bentley_1.compareBooleans)(this.requiresBatching, other.requiresBatching) || (0, core_bentley_1.compareBooleans)(this.containsTransform, other.containsTransform)
                || (0, core_bentley_1.compareBooleans)(this.containsFeatureOverrides, other.containsFeatureOverrides) || compareDurations(this.duration, other.duration);
            if (0 === cmp) {
                for (let i = 0; i < this.modelTimelines.length; i++)
                    if (0 !== (cmp = this.modelTimelines[i].compareTo(other.modelTimelines[i])))
                        break;
            }
            this._cachedComparisons.set(other, cmp);
            other._cachedComparisons.set(this, -cmp);
            return cmp;
        }
        equals(other) {
            return 0 === this.compareTo(other);
        }
        constructor(props) {
            this.duration = core_geometry_1.Range1d.createNull();
            const transformBatchIds = new Set();
            const modelTimelines = [];
            let containsModelClipping = false;
            let requiresBatching = false;
            let containsTransform = false;
            let containsFeatureOverrides = false;
            for (const modelProps of props) {
                const model = ModelTimeline.fromJSON(modelProps);
                modelTimelines.push(model);
                this.duration.extendRange(model.duration);
                containsModelClipping ||= model.containsModelClipping;
                requiresBatching ||= model.requiresBatching;
                containsTransform ||= model.containsTransform;
                containsFeatureOverrides ||= model.containsFeatureOverrides;
                for (const batchId of model.transformBatchIds)
                    transformBatchIds.add(batchId);
            }
            this.modelTimelines = modelTimelines;
            this.containsModelClipping = containsModelClipping;
            this.containsTransform = containsTransform;
            this.requiresBatching = requiresBatching || this.containsTransform;
            this.containsFeatureOverrides = containsFeatureOverrides;
            this.transformBatchIds = transformBatchIds;
        }
        static fromJSON(props) {
            if (!Array.isArray(props) || props.length === 0)
                return undefined;
            return new Script(props);
        }
        toJSON() {
            return this.modelTimelines.map((x) => x.toJSON());
        }
        /** Look up the timeline that animates the specified model, if any. */
        find(modelId) {
            return this.modelTimelines.find((x) => x.modelId === modelId);
        }
        /** @internal */
        getTransformBatchIds(modelId) {
            return this.find(modelId)?.transformBatchIds;
        }
        /** @internal */
        getTransform(modelId, batchId, time) {
            return this.find(modelId)?.getTransform(batchId, time);
        }
        /** @internal */
        addSymbologyOverrides(overrides, time) {
            for (const timeline of this.modelTimelines)
                timeline.addSymbologyOverrides(overrides, time);
        }
        /** Used by the [Entity.collectReferenceIds]($backend) method overrides in RenderTimeline and DisplayStyle.
         * @internal
         */
        discloseIds(ids) {
            for (const model of this.modelTimelines) {
                ids.addModel(model.modelId);
                for (const element of model.elementTimelines)
                    for (const id of element.elementIds)
                        ids.addElement(id);
            }
        }
        /** @internal */
        modelRequiresBatching(modelId) {
            // Only if the script contains animation (cutting plane, transform or visibility by node ID) do we require separate tilesets for animations.
            return this.requiresBatching && this.modelTimelines.some((x) => x.modelId === modelId && x.requiresBatching);
        }
        /** The batch Ids of the subset of [[elementTimelines]] that apply a transform and/or cutting plane.
         * @alpha
         */
        get discreteBatchIds() {
            if (this._discreteBatchIds)
                return this._discreteBatchIds;
            this._discreteBatchIds = new Set();
            for (const timeline of this.modelTimelines)
                for (const batchId of timeline.discreteBatchIds)
                    this._discreteBatchIds.add(batchId);
            return this._discreteBatchIds;
        }
        /** @internal see ImdlReader.readAnimationBranches. */
        getBatchIdForFeature(feature) {
            let timeline;
            const prev = this._lastFeatureModelTimeline;
            if (prev && prev.idLower === feature.modelId.lower && prev.idUpper === feature.modelId.upper) {
                timeline = prev.timeline;
            }
            else {
                const modelId = core_bentley_1.Id64.fromUint32PairObject(feature.modelId);
                timeline = this.find(modelId);
                this._lastFeatureModelTimeline = { timeline, idLower: feature.modelId.lower, idUpper: feature.modelId.upper };
            }
            return timeline?.getBatchIdForFeature(feature) ?? 0;
        }
        /** @alpha */
        get maxBatchId() {
            return this._maxBatchId ?? (this._maxBatchId = this.modelTimelines.reduce((accum, timeline) => Math.max(accum, timeline.maxBatchId), 0));
        }
        /**
         * Replaces all elementIds in a ScriptProps object with an empty string. Returns modified ScriptProps.
         * @param scheduleScript The script props to modify.
         * @internal */
        static removeScheduleScriptElementIds(scheduleScript) {
            scheduleScript.forEach((modelTimeline) => {
                modelTimeline.elementTimelines.forEach((elementTimeline) => {
                    if (elementTimeline.elementIds) {
                        elementTimeline.elementIds = "";
                    }
                });
            });
            return scheduleScript;
        }
    }
    RenderSchedule.Script = Script;
    /** A reference to a [[RenderSchedule.Script]], optionally identifying the source of the script.
     * @see [DisplayStyle.loadScheduleScript]($backend) to obtain the script reference for a display style.
     * @see [DisplayStyleState.scheduleScript]($frontend) or [DisplayStyleState.changeRenderTimeline]($frontend) to change a display style's script on the frontend.
     */
    class ScriptReference {
        /** The Id of the element - if any - from which the script originated.
         * A schedule script may originate from one of the following sources:
         *  - A [RenderTimeline]($backend) element stored in the iModel; or
         *  - The `scheduleScript` JSON property of a [DisplayStyle]($backend) element stored in the iModel; or
         *  - Any other source outside of the iModel, such as code that generates the script on the frontend, a script obtained from some server, etc.
         *
         * The [[sourceId]] property identifies the Id of the element from which the script originated; an empty or invalid [Id64String]($bentley) indicates the script did not
         * originate from any persistent element. If the Id is valid, the contents of [[script]] are assumed to match those stored on the source element.
         */
        sourceId;
        /** The script defining the rendering timelines to be applied. */
        script;
        /** @internal Use one of the public constructor overloads which forward to this one. */
        constructor(sourceIdOrScript, scriptIfSourceId) {
            if (typeof sourceIdOrScript === "string") {
                (0, core_bentley_1.assert)(scriptIfSourceId instanceof Script);
                this.sourceId = sourceIdOrScript;
                this.script = scriptIfSourceId;
            }
            else {
                (0, core_bentley_1.assert)(undefined === scriptIfSourceId);
                this.script = sourceIdOrScript;
                this.sourceId = core_bentley_1.Id64.invalid;
            }
        }
    }
    RenderSchedule.ScriptReference = ScriptReference;
    /** Used as part of a [[RenderSchedule.ScriptBuilder]] to define a [[RenderSchedule.Timeline]]. Functions that append
     * to the timeline expect entries to be appended in chronological order - i.e., you cannot append an entry that is earlier
     * than a previously appended entry.
     * @see [[RenderSchedule.ElementTimelineBuilder]] and [[RenderSchedule.ModelTimelineBuilder]].
     */
    class TimelineBuilder {
        /** Timeline controlling visibility. */
        visibility;
        /** Timeline controlling color. */
        color;
        /** Timeline controlling position and orientation. */
        transform;
        /** Timeline controlling clipping. */
        cuttingPlane;
        /** Append a new [[RenderSchedule.VisibilityEntry]] to the timeline. `time` must be more recent than any previously-appended visibility entries. */
        addVisibility(time, visibility, interpolation = Interpolation.Linear) {
            if (!this.visibility)
                this.visibility = [];
            this.visibility.push({ time, value: visibility, interpolation });
        }
        /** Append a new [[RenderSchedule.ColorEntry]] to the timeline. `time` must be more recent than any previously-appended color entries. */
        addColor(time, color, interpolation = Interpolation.Linear) {
            if (!this.color)
                this.color = [];
            const value = color instanceof RgbColor_1.RgbColor ? { red: color.r, green: color.g, blue: color.b } : color;
            this.color.push({ time, value, interpolation });
        }
        /** Append a new [[RenderSchedule.CuttingPlaneEntry]] to the timeline.  `time` must be more recent than any previously-appended cutting plane entries. */
        addCuttingPlane(time, plane, interpolation = Interpolation.Linear) {
            if (!this.cuttingPlane)
                this.cuttingPlane = [];
            let value;
            if (plane) {
                value = {
                    position: [plane.position.x, plane.position.y, plane.position.z],
                    direction: [plane.direction.x, plane.direction.y, plane.direction.z],
                };
                if (plane.visible)
                    value.visible = true;
                if (plane.hidden)
                    value.hidden = true;
            }
            this.cuttingPlane.push({ time, value, interpolation });
        }
        /** Append a new [[RenderSchedule.TransformEntry]] to the timeline.  `time` must be more recent than any previously-appended transform entries. */
        addTransform(time, transform, components, interpolation = Interpolation.Linear) {
            if (!this.transform)
                this.transform = [];
            const value = { transform: transform?.toRows() };
            if (components) {
                value.pivot = [components.pivot.x, components.pivot.y, components.pivot.z];
                value.orientation = components.orientation.toJSON();
                value.position = [components.position.x, components.position.y, components.position.z];
            }
            this.transform.push({ time, value, interpolation });
        }
        /** Obtain the JSON representation of the [[RenderSchedule.Timeline]] produced by this builder.
         * @see [[RenderSchedule.ScriptBuilder.finish]] to obtain the JSON for the entire [[RenderSchedule.Script]].
         */
        finish() {
            const props = {};
            if (this.visibility?.length)
                props.visibilityTimeline = this.visibility;
            if (this.color?.length)
                props.colorTimeline = this.color;
            if (this.transform?.length)
                props.transformTimeline = this.transform;
            if (this.cuttingPlane?.length)
                props.cuttingPlaneTimeline = this.cuttingPlane;
            return props;
        }
    }
    RenderSchedule.TimelineBuilder = TimelineBuilder;
    /** As part of a [[RenderSchedule.ScriptBuilder]], assembles a [[RenderSchedule.ElementTimeline]].
     * @see [[RenderSchedule.ModelTimelineBuilder.addElementTimeline]].
     */
    class ElementTimelineBuilder extends TimelineBuilder {
        /** A positive integer that uniquely identifies this timeline among all element timelines in the [[RenderSchedule.Script]].
         * [[RenderSchedule.ScriptBuilder]] ensures each ElementTimelineBuilder receives a unique batch Id.
         */
        batchId;
        /** The compressed set of Ids of the elements affected by this timeline. */
        elementIds;
        /** Constructor - typically not used directly.
         * @see [[RenderSchedule.ModelTimelineBuilder.addElementTimeline]] to create an ElementTimelineBuilder.
         */
        constructor(batchId, elementIds) {
            super();
            this.batchId = batchId;
            this.elementIds = elementIds;
        }
        /** Obtain the JSON representation of the [[RenderSchedule.ElementTimeline]] produced by this builder.
         * @see [[RenderSchedule.ScriptBuilder.finish]] to obtain the JSON for the entire [[RenderSchedule.Script]].
         */
        finish() {
            const props = super.finish();
            props.batchId = this.batchId;
            props.elementIds = this.elementIds;
            return props;
        }
    }
    RenderSchedule.ElementTimelineBuilder = ElementTimelineBuilder;
    /** As part of a [[RenderSchedule.ScriptBuilder, assembles a [[RenderSchedule.ModelTimeline]].
     * @see [[RenderSchedule.ScriptBuilder.addModelTimeline]].
     */
    class ModelTimelineBuilder extends TimelineBuilder {
        /** The Id of the model affected by this timeline. */
        modelId;
        /** @internal */
        realityModelUrl;
        _obtainNextBatchId;
        _elements = [];
        /** Constructor - typically not used directly.
         * @see [[RenderSchedule.ScriptBuilder.addModelTimeline]] to create a ModelTimelineBuilder.
         */
        constructor(modelId, obtainNextBatchId) {
            super();
            this.modelId = modelId;
            this._obtainNextBatchId = obtainNextBatchId;
        }
        /** Add a new [[RenderSchedule.ElementTimeline]] to be applied to the specified elements.
         * This function will sort and compress the Ids if they are not already compressed.
         *
         */
        addElementTimeline(elementIds) {
            const batchId = this._obtainNextBatchId();
            let ids;
            // It's far too easy to accidentally pass a single Id (compiler can't help).
            if (typeof elementIds === "string" && core_bentley_1.Id64.isValidId64(elementIds))
                elementIds = [elementIds];
            if (typeof elementIds === "string") {
                // Already compressed.
                ids = elementIds;
            }
            else {
                const sorted = Array.from(elementIds);
                core_bentley_1.OrderedId64Iterable.sortArray(sorted);
                ids = core_bentley_1.CompressedId64Set.compressIds(sorted);
            }
            const builder = new ElementTimelineBuilder(batchId, ids);
            this._elements.push(builder);
            return builder;
        }
        /** Obtain the JSON representation of the [[RenderSchedule.ModelTimeline]] produced by this builder.
         * @see [[RenderSchedule.ScriptBuilder.finish]] to obtain the JSON for the entire [[RenderSchedule.Script]].
         */
        finish() {
            const props = super.finish();
            props.modelId = this.modelId;
            if (undefined !== this.realityModelUrl)
                props.realityModelUrl = this.realityModelUrl;
            props.elementTimelines = this._elements.map((x) => x.finish());
            return props;
        }
    }
    RenderSchedule.ModelTimelineBuilder = ModelTimelineBuilder;
    /** Assembles the JSON representation for a new [[RenderSchedule.Script]]. Ensure that entries on any given element timeline are added chronologically. As an extremely simple example, the following code produces a script that changes the color of a single element:
     * ```ts
     *  const script = new ScriptBuilder();
     *  const model = script.addModelTimeline("0x123");
     *  const element = model.addElementTimeline([ "0x456" ]);
     *  element.addColor(Date.now(), new RgbColor(0xff, 0x7f, 0));
     *  const scriptProps = script.finish();
     * ```
     */
    class ScriptBuilder {
        _nextBatchId = 1;
        _models = [];
        /** Add a new [[RenderSchedule.ModelTimeline]] to be applied to the specified model. */
        addModelTimeline(modelId) {
            const builder = new ModelTimelineBuilder(modelId, () => this._nextBatchId++);
            this._models.push(builder);
            return builder;
        }
        /** Obtain the JSON representation of the [[RenderSchedule.Script]] produced by this builder.
         * @see [RenderTimeline.scriptProps]($backend) to assign the new script to a RenderTimeline element.
         */
        finish() {
            return this._models.map((x) => x.finish());
        }
    }
    RenderSchedule.ScriptBuilder = ScriptBuilder;
})(RenderSchedule || (exports.RenderSchedule = RenderSchedule = {}));
//# sourceMappingURL=RenderSchedule.js.map