import { Box3, Object3D, Plane, Vector3 } from "three";
import type { Context } from "../engine/engine_setup.js";
import { GameObject } from "./Component.js";
/**
 * Context passed to {@link IDragConstraint.init} at drag start and whenever the drag context
 * resets (e.g. transitioning from multi-touch back to single-pointer).
 * Provides a snapshot of the dragged object and attachment data a constraint needs
 * to initialize or re-initialize its locked reference values.
 */
export interface IDragConstraintContext {
    /** The object being dragged. Cast to IGameObject for world-space properties. */
    readonly gameObject: Object3D;
    /** Drag attachment point in the dragged object's local space. */
    readonly hitPointInLocalSpace: Vector3;
    /** Surface normal at drag attachment in the dragged object's local space. */
    readonly hitNormalInLocalSpace: Vector3;
    /**
     * Local-space bounding box at scale=1. Non-null only for multi-touch drags
     * where scale-aware constraints (e.g. keeping the bounds bottom grounded) are needed.
     */
    readonly boundsAtScaleOne: Box3 | null;
    /**
     * World scale of the object captured at the start of the current drag phase.
     * Non-null alongside boundsAtScaleOne for multi-touch; null for single-pointer.
     */
    readonly initialWorldScale: Vector3 | null;
}
/**
 * Contract for drag constraints applied after a follow object's position is resolved each frame.
 * Implement this interface to add custom position/rotation/scale restrictions.
 */
export interface IDragConstraint {
    /**
     * Called once at drag start and again whenever the drag context resets
     * (e.g. multi-touch → single-pointer transition). Capture any object snapshot
     * (position, rotation, scale) you need to hold fixed during the drag.
     * Constraints that need no dynamic initialization may omit this method.
     */
    init?(context: IDragConstraintContext): void;
    /** Modifies followObject in-place. Invoked after position is resolved each frame. */
    apply(followObject: GameObject): void;
}
/** Snaps the follow object's world position to a uniform grid. Resolution ≤ 0 is a no-op.
 *  For XZ-plane mode, use {@link GrabPointPlaneConstraint} instead (it handles both
 *  plane-clamping and optional grid-snapping on the plane). */
export declare class GridSnapConstraint implements IDragConstraint {
    snapGridResolution: number;
    constructor(snapGridResolution: number);
    apply(followObject: GameObject): void;
}
/** Projects the grabbed attachment point back onto a plane after position is resolved,
 *  with optional grid snapping on that plane.
 *  Owned by the strategy that requires plane-clamping (e.g. {@link XZPlaneDragStrategy}). */
export declare class GrabPointPlaneConstraint implements IDragConstraint {
    /** Plane to project the grabbed point onto. Typically the strategy's fixed drag plane. */
    readonly plane: Plane;
    /** Attachment point in dragged-object local space. Set once at drag start; the handler
     *  mutates this Vector3 in-place so updates are reflected automatically. */
    hitPointInLocalSpace: Vector3 | null;
    /** Grid snap resolution on the plane. 0 = projection only, no snapping. */
    snapResolution: number;
    constructor(plane: Plane);
    init(ctx: IDragConstraintContext): void;
    apply(followObject: GameObject): void;
}
/** Locks the follow object's world rotation to the quaternion captured at drag-start. */
export declare class KeepRotationConstraint implements IDragConstraint {
    private readonly _savedQuat;
    init(ctx: IDragConstraintContext): void;
    apply(followObject: GameObject): void;
}
/** Locks the follow object's world scale to the value captured at drag-start. */
export declare class KeepScaleConstraint implements IDragConstraint {
    private readonly _savedScale;
    init(ctx: IDragConstraintContext): void;
    apply(followObject: GameObject): void;
}
/**
 * Clamps the world scale of the dragged object to a [min, max] range.
 *
 * When `relativeToInitialScale` is `true` (default), `min` and `max` are treated as
 * multipliers of the object's world scale captured at drag start — for example, `min=0.01`
 * means the object cannot shrink below 1% of its original size.
 *
 * When `relativeToInitialScale` is `false`, `min` and `max` are applied directly as
 * absolute world-space scale values to each axis independently.
 *
 * @example Prevent negative / near-zero scale with default relative mode:
 * ```ts
 * new ScaleLimitConstraint(0.01, 10000, true, object.worldScale.clone());
 * ```
 */
export declare class ScaleLimitConstraint implements IDragConstraint {
    min: number;
    max: number;
    relativeToInitialScale: boolean;
    /**
     * @param min                    Lower bound. Relative mode: fraction of initial scale. Raw mode: absolute world-scale per axis.
     * @param max                    Upper bound. Relative mode: fraction of initial scale. Raw mode: absolute world-scale per axis.
     * @param relativeToInitialScale When `true`, clamp is relative to `initialWorldScale`. When `false`, each axis is clamped independently.
     * @param initialWorldScale      Reference to the object's world scale captured at drag start. Required for relative mode.
     */
    private readonly _initialWorldScale;
    constructor(min: number, max: number, relativeToInitialScale?: boolean);
    init(ctx: IDragConstraintContext): void;
    apply(followObject: GameObject): void;
}
/**
 * Flags controlling which rotation axes are frozen by {@link FixedRotationAxesConstraint}.
 * Values can be combined with the bitwise OR operator (`|`).
 * @example Freeze X and Z: `RotationAxis.X | RotationAxis.Z`
 */
export declare enum RotationAxis {
    X = 1,
    Y = 2,
    Z = 4
}
/**
 * Freezes individual rotation axes (X, Y, and/or Z) of the dragged object.
 * The locked axis values are captured once at construction time and restored every frame.
 *
 * Set {@link useLocalSpace} to `true` to lock axes in the object's local space;
 * leave it `false` (default) to lock axes in world space.
 *
 * @example Lock Y-axis rotation in world space:
 * ```ts
 * const c = new FixedRotationAxesConstraint(RotationAxis.Y, false);
 * // init is called automatically by DragControls, or call manually: c.init(ctx);
 * ```
 */
export declare class FixedRotationAxesConstraint implements IDragConstraint {
    frozenAxes: RotationAxis;
    useLocalSpace: boolean;
    private readonly _startEuler;
    private readonly _eulerCache;
    /**
     * @param frozenAxes       Bitfield of {@link RotationAxis} values indicating which axes to lock.
     * @param useLocalSpace    When `true`, axes are locked in the object's local space; otherwise world space.
     */
    constructor(frozenAxes: RotationAxis, useLocalSpace?: boolean);
    init(ctx: IDragConstraintContext): void;
    apply(followObject: GameObject): void;
}
/**
 * Constrains a dragged object to rotate only around a fixed world-space axis.
 * Uses swing-twist decomposition to extract only the twist component around
 * the given axis from the delta rotation since drag start, discarding any
 * perpendicular swing that would tilt the object.
 *
 * This is the correct constraint for XZ-plane dragging when the parent is
 * rotated — it restricts the object to yaw around the plane normal regardless
 * of how the plane is oriented in world space.
 */
export declare class AxisRotationConstraint implements IDragConstraint {
    private readonly _startQuat;
    private readonly _axis;
    constructor(axis: Vector3);
    init(ctx: IDragConstraintContext): void;
    apply(followObject: GameObject): void;
}
/** Locks the follow object's signed distance from a plane to a value set at drag start.
 *  Owned by {@link XZPlaneDragStrategy} to prevent slow Y drift in the side-view fallback path.
 *  Immune to grab-point projection errors, stale matrixWorld, and epsilon guards.
 *
 *  When {@link boundsBottomSignedDistFromPivot} is non-zero (set by {@link MultiTouchDragHandler}
 *  for XZPlane scaling), the effective locked height is shifted each frame so the **bottom of the
 *  object's bounds** stays at the height captured at drag start instead of the pivot. */
export declare class PlaneHeightLockConstraint implements IDragConstraint {
    private readonly plane;
    private _lockedHeight;
    private _boundsBottomSignedDistFromPivot;
    /** Current pinch-scale ratio, updated each frame by {@link MultiTouchDragHandler}. */
    currentScaleRatio: number;
    constructor(plane: Plane);
    init(ctx: IDragConstraintContext): void;
    apply(followObject: GameObject): void;
}
/**
 * Used by {@link SnapToSurfacesDragStrategy} for two-pointer (multi-touch) drags.
 * Casts a downward ray each frame from the follow object's world position and adjusts
 * it so the dragged object's bounding-box contact face rests on the detected surface.
 *
 * Only injected into the constraint pipeline for multi-touch; the single-pointer path
 * continues to use {@link SnapToSurfacesDragStrategy.update} (pointer ray + drag plane
 * intersection) unchanged.
 */
export declare class SnapToSurfaceConstraint implements IDragConstraint {
    private readonly _context;
    private _gameObject;
    private _boundsAtScaleOne;
    private readonly _t1;
    private readonly _t2;
    private readonly _t3;
    private readonly _t4;
    constructor(_context: Context);
    init(ctx: IDragConstraintContext): void;
    apply(followObject: GameObject): void;
}
/**
 * Runs the constraint pipeline — applies each constraint to followObject in order.
 * Shared by {@link DragPointerHandler} and {@link MultiTouchDragHandler}.
 */
export declare function applyFollowObjectConstraints(followObject: GameObject, constraints: readonly IDragConstraint[]): void;
