var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        if (typeof b !== "function" && b !== null)
            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
    return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (g && (g = 0, op[0] && (_ = 0)), _) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
var __read = (this && this.__read) || function (o, n) {
    var m = typeof Symbol === "function" && o[Symbol.iterator];
    if (!m) return o;
    var i = m.call(o), r, ar = [], e;
    try {
        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
    }
    catch (error) { e = { error: error }; }
    finally {
        try {
            if (r && !r.done && (m = i["return"])) m.call(i);
        }
        finally { if (e) throw e.error; }
    }
    return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
    if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
        if (ar || !(i in from)) {
            if (!ar) ar = Array.prototype.slice.call(from, 0, i);
            ar[i] = from[i];
        }
    }
    return to.concat(ar || Array.prototype.slice.call(from));
};
import { AcDbSpline, AcGePoint2d, AcGePoint3d } from '@mlightcad/data-model';
import { AcApDocManager } from '../app';
import { AcEdCommand, AcEdOpenMode, AcEdPreviewJig, AcEdPromptIntegerOptions, AcEdPromptKeywordOptions, AcEdPromptPointOptions, AcEdPromptStateMachine, AcEdPromptStatus } from '../editor';
import { AcApI18n } from '../i18n';
/**
 * Dynamic preview jig for SPLINE command.
 *
 * Reuses a transient spline entity and rebuilds it on each cursor move.
 */
var AcApSplineJig = /** @class */ (function (_super) {
    __extends(AcApSplineJig, _super);
    /**
     * Creates a dynamic spline preview jig.
     *
     * @param view - Active editor view.
     * @param points - Accepted points before the dynamic cursor point.
     * @param settings - Current method/degree/knots settings.
     * @param closed - Closed flag used for preview behavior.
     */
    function AcApSplineJig(view, points, settings, closed) {
        var _this = _super.call(this, view) || this;
        _this._points = points;
        _this._settings = settings;
        _this._closed = closed;
        _this._spline = _this.createSpline(points);
        return _this;
    }
    Object.defineProperty(AcApSplineJig.prototype, "entity", {
        /**
         * Gets transient spline entity used by this jig.
         */
        get: function () {
            return this._spline;
        },
        enumerable: false,
        configurable: true
    });
    /**
     * Rebuilds preview spline by combining fixed points with cursor point.
     *
     * @param point - Current cursor/input point.
     */
    AcApSplineJig.prototype.update = function (point) {
        var dynamicPoint = new AcGePoint2d(point);
        var lastPoint = this._points[this._points.length - 1];
        var hasDistinctDynamicPoint = !lastPoint || !isNearlySamePoint2d(lastPoint, dynamicPoint);
        var newPoints = hasDistinctDynamicPoint
            ? __spreadArray(__spreadArray([], __read(this._points), false), [dynamicPoint], false) : __spreadArray([], __read(this._points), false);
        try {
            this.rebuildSpline(newPoints);
        }
        catch (_a) {
            // Keep the command alive even when temporary preview points
            // produce a degenerate interpolation system.
            this.rebuildSpline(this._points);
        }
    };
    /**
     * Creates initial spline entity from current settings.
     *
     * @param points - Initial fixed points.
     * @returns New spline entity.
     */
    AcApSplineJig.prototype.createSpline = function (points) {
        return buildSplineEntity(points, this._settings, this._closed);
    };
    /**
     * Rebuilds existing transient spline without replacing entity instance.
     *
     * @param points - Point list used for rebuild.
     */
    AcApSplineJig.prototype.rebuildSpline = function (points) {
        if (points.length < 2) {
            var defaultPoints = [new AcGePoint3d(0, 0, 0), new AcGePoint3d(1, 1, 0)];
            var defaultDegree = 1;
            var defaultKnots = createKnots(defaultPoints.length, defaultDegree);
            this._spline.rebuild(defaultPoints, defaultKnots, undefined, defaultDegree);
            return;
        }
        var points3d = points.map(function (p) { return new AcGePoint3d(p.x, p.y, 0); });
        var degree = resolveSplineDegree(points3d.length, this._settings);
        var isClosed = this._closed && points3d.length >= 3;
        if (this._settings.method === 'fit') {
            this._spline.rebuild(points3d, mapKnotsTypeToParameterization(this._settings.knotsType), degree, isClosed);
            return;
        }
        var knots = createKnots(points3d.length, degree);
        this._spline.rebuild(points3d, knots, undefined, degree, false);
        if (isClosed) {
            this._spline.closed = true;
        }
    };
    return AcApSplineJig;
}(AcEdPreviewJig));
export { AcApSplineJig };
/**
 * Command to create one spline.
 */
var AcApSplineCmd = /** @class */ (function (_super) {
    __extends(AcApSplineCmd, _super);
    /**
     * Creates SPLINE command instance.
     */
    function AcApSplineCmd() {
        var _this = _super.call(this) || this;
        _this.mode = AcEdOpenMode.Write;
        return _this;
    }
    /**
     * Command entry point.
     *
     * Uses a point-prompt state machine to support Fit/CV workflows, dynamic
     * method switching, undo, close, and option prompts.
     *
     * @param context - Current application/document context.
     */
    AcApSplineCmd.prototype.execute = function (context) {
        return __awaiter(this, void 0, void 0, function () {
            var points, settings, closed, appendPoint, undoLast, getCurrentPoint, canClose, createPreviewJig, createStaticJig, promptMethod, promptKnotsType, promptDegree, applyCommonPromptOptions, FitState, CvState, firstPointPrompt, firstPointResult, changed, machine, db, spline;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        points = [];
                        settings = {
                            method: 'fit',
                            knotsType: 'chord',
                            degree: 3
                        };
                        closed = false;
                        appendPoint = function (point) {
                            points.push(new AcGePoint2d(point));
                        };
                        undoLast = function () {
                            if (points.length <= 1)
                                return;
                            points.pop();
                        };
                        getCurrentPoint = function () { return points[points.length - 1]; };
                        canClose = function () { return points.length >= 3; };
                        createPreviewJig = function () {
                            return new AcApSplineJig(context.view, points, settings, closed);
                        };
                        createStaticJig = function () {
                            return new AcApSplineStaticJig(context.view, points, settings, closed);
                        };
                        promptMethod = function () { return __awaiter(_this, void 0, void 0, function () {
                            var methodPrompt, methodResult;
                            return __generator(this, function (_a) {
                                switch (_a.label) {
                                    case 0:
                                        methodPrompt = new AcEdPromptKeywordOptions(AcApI18n.t('jig.spline.methodPrompt'));
                                        methodPrompt.keywords.add(AcApI18n.t('jig.spline.keywords.fit.display'), AcApI18n.t('jig.spline.keywords.fit.global'), AcApI18n.t('jig.spline.keywords.fit.local'));
                                        methodPrompt.keywords.add(AcApI18n.t('jig.spline.keywords.cv.display'), AcApI18n.t('jig.spline.keywords.cv.global'), AcApI18n.t('jig.spline.keywords.cv.local'));
                                        return [4 /*yield*/, AcApDocManager.instance.editor.getKeywords(methodPrompt)];
                                    case 1:
                                        methodResult = _a.sent();
                                        if (methodResult.status !== AcEdPromptStatus.OK)
                                            return [2 /*return*/, false];
                                        if (methodResult.stringResult === 'Fit') {
                                            settings.method = 'fit';
                                        }
                                        else if (methodResult.stringResult === 'CV') {
                                            settings.method = 'cv';
                                        }
                                        return [2 /*return*/, true];
                                }
                            });
                        }); };
                        promptKnotsType = function () { return __awaiter(_this, void 0, void 0, function () {
                            var knotsPrompt, knotsResult;
                            return __generator(this, function (_a) {
                                switch (_a.label) {
                                    case 0:
                                        knotsPrompt = new AcEdPromptKeywordOptions(AcApI18n.t('jig.spline.knotsPrompt'));
                                        knotsPrompt.jig = createStaticJig();
                                        knotsPrompt.keywords.add(AcApI18n.t('jig.spline.keywords.chord.display'), AcApI18n.t('jig.spline.keywords.chord.global'), AcApI18n.t('jig.spline.keywords.chord.local'));
                                        knotsPrompt.keywords.add(AcApI18n.t('jig.spline.keywords.sqrtChord.display'), AcApI18n.t('jig.spline.keywords.sqrtChord.global'), AcApI18n.t('jig.spline.keywords.sqrtChord.local'));
                                        knotsPrompt.keywords.add(AcApI18n.t('jig.spline.keywords.uniform.display'), AcApI18n.t('jig.spline.keywords.uniform.global'), AcApI18n.t('jig.spline.keywords.uniform.local'));
                                        return [4 /*yield*/, AcApDocManager.instance.editor.getKeywords(knotsPrompt)];
                                    case 1:
                                        knotsResult = _a.sent();
                                        if (knotsResult.status !== AcEdPromptStatus.OK)
                                            return [2 /*return*/, false];
                                        if (knotsResult.stringResult === 'Chord') {
                                            settings.knotsType = 'chord';
                                        }
                                        else if (knotsResult.stringResult === 'SqrtChord') {
                                            settings.knotsType = 'sqrtChord';
                                        }
                                        else if (knotsResult.stringResult === 'Uniform') {
                                            settings.knotsType = 'uniform';
                                        }
                                        return [2 /*return*/, true];
                                }
                            });
                        }); };
                        promptDegree = function () { return __awaiter(_this, void 0, void 0, function () {
                            var degreePrompt, degreeResult, degree;
                            var _a;
                            return __generator(this, function (_b) {
                                switch (_b.label) {
                                    case 0:
                                        degreePrompt = new AcEdPromptIntegerOptions(AcApI18n.t('jig.spline.degreePrompt'), 1, 10);
                                        degreePrompt.jig = createStaticJig();
                                        return [4 /*yield*/, AcApDocManager.instance.editor.getInteger(degreePrompt)];
                                    case 1:
                                        degreeResult = _b.sent();
                                        if (degreeResult.status !== AcEdPromptStatus.OK)
                                            return [2 /*return*/, false];
                                        degree = Math.floor((_a = degreeResult.value) !== null && _a !== void 0 ? _a : settings.degree);
                                        settings.degree = Math.max(1, degree);
                                        return [2 /*return*/, true];
                                }
                            });
                        }); };
                        applyCommonPromptOptions = function (prompt) {
                            prompt.useDashedLine = true;
                            prompt.useBasePoint = true;
                            var basePoint = getCurrentPoint();
                            if (basePoint) {
                                prompt.basePoint = new AcGePoint3d(basePoint);
                            }
                            prompt.jig = createPreviewJig();
                        };
                        FitState = /** @class */ (function () {
                            /**
                             * Creates Fit state.
                             *
                             * @param machine - Owning prompt state machine.
                             */
                            function FitState(machine) {
                                this.machine = machine;
                            }
                            /**
                             * Builds point prompt for fit workflow.
                             *
                             * @returns Configured point prompt with fit-related keywords.
                             */
                            FitState.prototype.buildPrompt = function () {
                                var prompt = new AcEdPromptPointOptions(AcApI18n.t('jig.spline.nextPointWithFitOptions'));
                                prompt.keywords.add(AcApI18n.t('jig.spline.keywords.method.display'), AcApI18n.t('jig.spline.keywords.method.global'), AcApI18n.t('jig.spline.keywords.method.local'));
                                prompt.keywords.add(AcApI18n.t('jig.spline.keywords.knots.display'), AcApI18n.t('jig.spline.keywords.knots.global'), AcApI18n.t('jig.spline.keywords.knots.local'));
                                prompt.keywords.add(AcApI18n.t('jig.spline.keywords.undo.display'), AcApI18n.t('jig.spline.keywords.undo.global'), AcApI18n.t('jig.spline.keywords.undo.local'));
                                prompt.keywords.add(AcApI18n.t('jig.spline.keywords.close.display'), AcApI18n.t('jig.spline.keywords.close.global'), AcApI18n.t('jig.spline.keywords.close.local'));
                                applyCommonPromptOptions(prompt);
                                return prompt;
                            };
                            /**
                             * Handles fit-state prompt result and returns next state-machine action.
                             *
                             * @param result - Point prompt result.
                             * @returns State-machine step result.
                             */
                            FitState.prototype.handleResult = function (result) {
                                return __awaiter(this, void 0, void 0, function () {
                                    var keyword, changed, changed;
                                    var _a;
                                    return __generator(this, function (_b) {
                                        switch (_b.label) {
                                            case 0:
                                                if (result.status === AcEdPromptStatus.OK) {
                                                    appendPoint(result.value);
                                                    return [2 /*return*/, 'continue'];
                                                }
                                                if (result.status !== AcEdPromptStatus.Keyword)
                                                    return [2 /*return*/, 'finish'];
                                                keyword = (_a = result.stringResult) !== null && _a !== void 0 ? _a : '';
                                                if (!(keyword === 'Method')) return [3 /*break*/, 2];
                                                return [4 /*yield*/, promptMethod()];
                                            case 1:
                                                changed = _b.sent();
                                                if (!changed)
                                                    return [2 /*return*/, 'finish'];
                                                if (settings.method === 'cv') {
                                                    this.machine.setState(new CvState(this.machine));
                                                }
                                                return [2 /*return*/, 'continue'];
                                            case 2:
                                                if (!(keyword === 'Knots')) return [3 /*break*/, 4];
                                                return [4 /*yield*/, promptKnotsType()];
                                            case 3:
                                                changed = _b.sent();
                                                return [2 /*return*/, changed ? 'continue' : 'finish'];
                                            case 4:
                                                if (keyword === 'Undo') {
                                                    undoLast();
                                                    return [2 /*return*/, 'continue'];
                                                }
                                                if (keyword === 'Close' && canClose()) {
                                                    closed = true;
                                                    return [2 /*return*/, 'finish'];
                                                }
                                                return [2 /*return*/, 'continue'];
                                        }
                                    });
                                });
                            };
                            return FitState;
                        }());
                        CvState = /** @class */ (function () {
                            /**
                             * Creates CV state.
                             *
                             * @param machine - Owning prompt state machine.
                             */
                            function CvState(machine) {
                                this.machine = machine;
                            }
                            /**
                             * Builds point prompt for CV workflow.
                             *
                             * @returns Configured point prompt with CV-related keywords.
                             */
                            CvState.prototype.buildPrompt = function () {
                                var prompt = new AcEdPromptPointOptions(AcApI18n.t('jig.spline.nextPointWithCvOptions'));
                                prompt.keywords.add(AcApI18n.t('jig.spline.keywords.method.display'), AcApI18n.t('jig.spline.keywords.method.global'), AcApI18n.t('jig.spline.keywords.method.local'));
                                prompt.keywords.add(AcApI18n.t('jig.spline.keywords.degree.display'), AcApI18n.t('jig.spline.keywords.degree.global'), AcApI18n.t('jig.spline.keywords.degree.local'));
                                prompt.keywords.add(AcApI18n.t('jig.spline.keywords.undo.display'), AcApI18n.t('jig.spline.keywords.undo.global'), AcApI18n.t('jig.spline.keywords.undo.local'));
                                prompt.keywords.add(AcApI18n.t('jig.spline.keywords.close.display'), AcApI18n.t('jig.spline.keywords.close.global'), AcApI18n.t('jig.spline.keywords.close.local'));
                                applyCommonPromptOptions(prompt);
                                return prompt;
                            };
                            /**
                             * Handles CV-state prompt result and returns next state-machine action.
                             *
                             * @param result - Point prompt result.
                             * @returns State-machine step result.
                             */
                            CvState.prototype.handleResult = function (result) {
                                return __awaiter(this, void 0, void 0, function () {
                                    var keyword, changed, changed;
                                    var _a;
                                    return __generator(this, function (_b) {
                                        switch (_b.label) {
                                            case 0:
                                                if (result.status === AcEdPromptStatus.OK) {
                                                    appendPoint(result.value);
                                                    return [2 /*return*/, 'continue'];
                                                }
                                                if (result.status !== AcEdPromptStatus.Keyword)
                                                    return [2 /*return*/, 'finish'];
                                                keyword = (_a = result.stringResult) !== null && _a !== void 0 ? _a : '';
                                                if (!(keyword === 'Method')) return [3 /*break*/, 2];
                                                return [4 /*yield*/, promptMethod()];
                                            case 1:
                                                changed = _b.sent();
                                                if (!changed)
                                                    return [2 /*return*/, 'finish'];
                                                if (settings.method === 'fit') {
                                                    this.machine.setState(new FitState(this.machine));
                                                }
                                                return [2 /*return*/, 'continue'];
                                            case 2:
                                                if (!(keyword === 'Degree')) return [3 /*break*/, 4];
                                                return [4 /*yield*/, promptDegree()];
                                            case 3:
                                                changed = _b.sent();
                                                return [2 /*return*/, changed ? 'continue' : 'finish'];
                                            case 4:
                                                if (keyword === 'Undo') {
                                                    undoLast();
                                                    return [2 /*return*/, 'continue'];
                                                }
                                                if (keyword === 'Close' && canClose()) {
                                                    closed = true;
                                                    return [2 /*return*/, 'finish'];
                                                }
                                                return [2 /*return*/, 'continue'];
                                        }
                                    });
                                });
                            };
                            return CvState;
                        }());
                        _a.label = 1;
                    case 1:
                        if (!(points.length === 0)) return [3 /*break*/, 5];
                        firstPointPrompt = new AcEdPromptPointOptions(AcApI18n.t('jig.spline.firstPointWithOptions'));
                        firstPointPrompt.keywords.add(AcApI18n.t('jig.spline.keywords.method.display'), AcApI18n.t('jig.spline.keywords.method.global'), AcApI18n.t('jig.spline.keywords.method.local'));
                        return [4 /*yield*/, AcApDocManager.instance.editor.getPoint(firstPointPrompt)];
                    case 2:
                        firstPointResult = _a.sent();
                        if (firstPointResult.status === AcEdPromptStatus.OK) {
                            appendPoint(firstPointResult.value);
                            return [3 /*break*/, 5];
                        }
                        if (!(firstPointResult.status === AcEdPromptStatus.Keyword &&
                            firstPointResult.stringResult === 'Method')) return [3 /*break*/, 4];
                        return [4 /*yield*/, promptMethod()];
                    case 3:
                        changed = _a.sent();
                        if (!changed)
                            return [2 /*return*/];
                        return [3 /*break*/, 1];
                    case 4: return [2 /*return*/];
                    case 5:
                        machine = new AcEdPromptStateMachine();
                        machine.setState(settings.method === 'fit' ? new FitState(machine) : new CvState(machine));
                        return [4 /*yield*/, machine.run(function (prompt) { return AcApDocManager.instance.editor.getPoint(prompt); })
                            // Create spline if we have at least two points
                        ];
                    case 6:
                        _a.sent();
                        // Create spline if we have at least two points
                        if (points.length >= 2) {
                            db = context.doc.database;
                            spline = buildSplineEntity(points, settings, closed);
                            db.tables.blockTable.modelSpace.appendEntity(spline);
                        }
                        return [2 /*return*/];
                }
            });
        });
    };
    return AcApSplineCmd;
}(AcEdCommand));
export { AcApSplineCmd };
/**
 * Static spline preview jig used by non-point option prompts.
 *
 * @typeParam T - Prompt value type bound to this jig.
 */
var AcApSplineStaticJig = /** @class */ (function (_super) {
    __extends(AcApSplineStaticJig, _super);
    /**
     * Creates static spline preview jig.
     *
     * @param view - Active editor view.
     * @param points - Current accepted spline points.
     * @param settings - Current method/degree/knots settings.
     * @param closed - Closed flag for preview.
     */
    function AcApSplineStaticJig(view, points, settings, closed) {
        var _this = _super.call(this, view) || this;
        _this._spline = buildSplineEntity(points, settings, closed);
        return _this;
    }
    Object.defineProperty(AcApSplineStaticJig.prototype, "entity", {
        /**
         * Gets transient static spline entity.
         */
        get: function () {
            return this._spline;
        },
        enumerable: false,
        configurable: true
    });
    /**
     * No-op update hook for static preview jig.
     *
     * @param _value - Prompt value (unused).
     */
    AcApSplineStaticJig.prototype.update = function (_value) {
        // Static preview only.
    };
    return AcApSplineStaticJig;
}(AcEdPreviewJig));
function buildSplineEntity(points, settings, closed) {
    if (points.length < 2) {
        var fallbackPoints = [new AcGePoint3d(0, 0, 0), new AcGePoint3d(1, 1, 0)];
        var fallbackDegree = 1;
        var fallbackKnots = createKnots(fallbackPoints.length, fallbackDegree);
        return new AcDbSpline(fallbackPoints, fallbackKnots, undefined, fallbackDegree);
    }
    var points3dRaw = points.map(function (p) { return new AcGePoint3d(p.x, p.y, 0); });
    var points3d = settings.method === 'fit'
        ? dedupeConsecutivePoints(points3dRaw)
        : points3dRaw;
    if (points3d.length < 2) {
        var fallbackPoints = [new AcGePoint3d(0, 0, 0), new AcGePoint3d(1, 1, 0)];
        var fallbackDegree = 1;
        var fallbackKnots = createKnots(fallbackPoints.length, fallbackDegree);
        return new AcDbSpline(fallbackPoints, fallbackKnots, undefined, fallbackDegree);
    }
    var degree = resolveSplineDegree(points3d.length, settings);
    var isClosed = closed && points3d.length >= 3;
    if (settings.method === 'fit') {
        return new AcDbSpline(points3d, mapKnotsTypeToParameterization(settings.knotsType), degree, isClosed);
    }
    var knots = createKnots(points3d.length, degree);
    var spline = new AcDbSpline(points3d, knots, undefined, degree, false);
    if (isClosed) {
        spline.closed = true;
    }
    return spline;
}
function resolveSplineDegree(numPoints, settings) {
    var maxDegree = Math.max(1, numPoints - 1);
    if (settings.method === 'fit') {
        return Math.min(3, maxDegree);
    }
    return Math.min(Math.max(1, settings.degree), maxDegree);
}
function dedupeConsecutivePoints(points) {
    if (points.length <= 1)
        return points;
    var deduped = [new AcGePoint3d(points[0])];
    for (var i = 1; i < points.length; i++) {
        var last = deduped[deduped.length - 1];
        var current = points[i];
        if (!isNearlySamePoint3d(last, current)) {
            deduped.push(new AcGePoint3d(current));
        }
    }
    return deduped;
}
function isNearlySamePoint2d(a, b) {
    return Math.hypot(a.x - b.x, a.y - b.y) <= 1e-9;
}
function isNearlySamePoint3d(a, b) {
    var _a, _b;
    var az = (_a = a.z) !== null && _a !== void 0 ? _a : 0;
    var bz = (_b = b.z) !== null && _b !== void 0 ? _b : 0;
    return Math.hypot(a.x - b.x, a.y - b.y, az - bz) <= 1e-9;
}
function mapKnotsTypeToParameterization(knotsType) {
    switch (knotsType) {
        case 'chord':
            return 'Chord';
        case 'sqrtChord':
            return 'SqrtChord';
        case 'uniform':
        default:
            return 'Uniform';
    }
}
function createKnots(numControlPoints, degree) {
    var knots = [];
    var numKnots = numControlPoints + degree + 1;
    for (var i = 0; i < numKnots; i++) {
        if (i < degree + 1) {
            knots.push(0);
        }
        else if (i >= numKnots - degree - 1) {
            knots.push(1);
        }
        else {
            knots.push((i - degree) / (numControlPoints - degree));
        }
    }
    return knots;
}
//# sourceMappingURL=AcApSplineCmd.js.map