import {
  observeResize
} from "./chunk-6VGP7A7A.js";
import {
  EventHandler
} from "./chunk-SGIVSRN5.js";
import {
  StateContextProvider_default,
  createStateContext_default,
  useDeps_default,
  useUpdatingRef_default
} from "./chunk-W3FLVN5D.js";
import "./chunk-JSBRDJBE.js";

// src/js-tools/react-three/Three.tsx
import {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState
} from "react";
import { PerspectiveCamera, WebGLRenderer } from "three";
import isFunction from "lodash-es/isFunction";

// src/js-tools/react-three/contexts.ts
var ThreeContext = createStateContext_default();
var ThreeViewContext = createStateContext_default();

// src/js-tools/react-three/Three.tsx
import { jsx, jsxs } from "react/jsx-runtime";
function Three({
  children,
  autoResize = true,
  canvasRef
}) {
  const [views, setViews] = useState([]);
  const [renderer, setRenderer] = useState(null);
  const innerCanvasRef = useRef(null);
  const addView = useCallback(
    (view) => {
      setViews((views2) => [...views2, view]);
      return () => {
        setViews((views2) => {
          const index = views2.indexOf(view);
          if (index < 0) {
            throw new Error(
              "Cannot remove view which hasn't been added."
            );
          }
          return [
            ...views2.slice(0, index),
            ...views2.slice(index + 1)
          ];
        });
      };
    },
    [setViews]
  );
  const assignRef = useCallback(
    (e) => {
      innerCanvasRef.current = e;
      if (canvasRef) {
        if (isFunction(canvasRef)) {
          canvasRef(e);
        } else {
          canvasRef.current = e;
        }
      }
    },
    [canvasRef, innerCanvasRef]
  );
  useEffect(() => {
    const canvas = innerCanvasRef.current;
    if (canvas) {
      const parent = canvas.parentElement;
      const renderer2 = new WebGLRenderer({
        canvas,
        antialias: true,
        preserveDrawingBuffer: true
      });
      setRenderer(renderer2);
      let unObserve = null;
      if (parent && autoResize) {
        unObserve = observeResize(parent, () => {
          const parent2 = canvas.parentElement;
          if (parent2) {
            const rect = parent2.getBoundingClientRect();
            const { width, height } = rect;
            renderer2.setSize(width, height, false);
          }
        });
      }
      return () => {
        if (unObserve) unObserve();
        setRenderer(null);
        renderer2.dispose();
      };
    }
  }, [autoResize, innerCanvasRef, setRenderer]);
  useEffect(() => {
    if (renderer) {
      renderer.setAnimationLoop((time) => {
        const { width, height } = renderer.domElement;
        renderer.resetState();
        renderer.setScissorTest(views.length > 1);
        if (width > 0 && height > 0) {
          for (const view of views) {
            const { camera, scene, eventHandler, control } = view;
            const controlWidth = control.getWidth(width);
            const controlHeight = control.getHeight(height);
            if (camera instanceof PerspectiveCamera) {
              if (camera.aspect !== controlWidth / controlHeight) {
                camera.aspect = controlWidth / controlHeight;
                camera.updateProjectionMatrix();
              }
            } else {
              if (camera.right - camera.left !== controlWidth || camera.top - camera.bottom !== controlHeight) {
                camera.left = -controlWidth / 2;
                camera.right = controlWidth / 2;
                camera.top = controlHeight / 2;
                camera.bottom = -controlHeight / 2;
                camera.updateProjectionMatrix();
              }
            }
            eventHandler.dispatchEvent("beforeupdate", {
              time,
              scene,
              renderer,
              camera
            });
            if (control.update(camera)) {
              if (views.length > 1) {
                renderer.setViewport(
                  control.getXOffset(width),
                  height - (control.getYOffset(height) + controlHeight),
                  controlWidth,
                  controlHeight
                );
                renderer.setScissor(
                  control.getXOffset(width),
                  height - (control.getYOffset(height) + controlHeight),
                  controlWidth,
                  controlHeight
                );
              }
              renderer.clear();
              eventHandler.dispatchEvent("beforerender", {
                time,
                scene,
                renderer,
                camera
              });
              renderer.render(scene, camera);
              eventHandler.dispatchEvent("afterrender", {
                time,
                scene,
                renderer,
                camera
              });
              renderer.setViewport(0, 0, width, height);
              renderer.setScissor(0, 0, width, height);
            }
            eventHandler.dispatchEvent("afterupdate", {
              time,
              scene,
              renderer,
              camera
            });
          }
        }
      });
      return () => {
        renderer.setAnimationLoop(null);
      };
    }
  }, [views, renderer]);
  return useMemo(
    () => /* @__PURE__ */ jsxs(
      StateContextProvider_default,
      {
        contexts: [ThreeContext],
        values: [
          {
            renderer,
            addView
          }
        ],
        children: [
          /* @__PURE__ */ jsx(
            "canvas",
            {
              ref: assignRef,
              style: {
                maxWidth: "100%",
                maxHeight: "100%"
              }
            }
          ),
          children
        ]
      }
    ),
    [children, renderer, assignRef, addView]
  );
}
var Three_default = Three;

// src/js-tools/react-three/ThreeView.tsx
import { useContext, useEffect as useEffect2, useMemo as useMemo2 } from "react";
import { OrthographicCamera, PerspectiveCamera as PerspectiveCamera2, Scene } from "three";

// src/js-tools/react-three/three-view-control.ts
import isFunction2 from "lodash-es/isFunction";
import { Euler, Matrix4, Vector3 } from "three";
var ThreeViewControl = class {
  constructor(xOffset, yOffset, width, height) {
    this._xOffset = xOffset;
    this._yOffset = yOffset;
    this._width = width;
    this._height = height;
    this._forceUpdate = false;
    this._projectionMatrix = new Matrix4();
    this._position = new Vector3();
    this._rotation = new Euler();
  }
  getXOffset(width) {
    return isFunction2(this._xOffset) ? this._xOffset(width) : this._xOffset;
  }
  getYOffset(height) {
    return isFunction2(this._yOffset) ? this._yOffset(height) : this._yOffset;
  }
  getWidth(width) {
    return isFunction2(this._width) ? this._width(width) : this._width;
  }
  getHeight(height) {
    return isFunction2(this._height) ? this._height(height) : this._height;
  }
  update(camera) {
    const shouldUpdate = this._forceUpdate || !this._projectionMatrix.equals(camera.projectionMatrix) || !this._position.equals(camera.position) || !this._rotation.equals(camera.rotation);
    this._projectionMatrix = camera.projectionMatrix.clone();
    this._position = camera.position.clone();
    this._rotation = camera.rotation.clone();
    this._forceUpdate = false;
    return shouldUpdate;
  }
  forceUpdate() {
    this._forceUpdate = true;
  }
};
var three_view_control_default = ThreeViewControl;

// src/js-tools/react-three/ThreeView.tsx
import { jsx as jsx2 } from "react/jsx-runtime";
function defaultWidth(width) {
  return width;
}
function defaultHeight(height) {
  return height;
}
function ThreeView({
  children,
  mode,
  xOffset = 0,
  yOffset = 0,
  width = defaultWidth,
  height = defaultHeight
}) {
  const [{ addView }] = useContext(ThreeContext);
  const control = useMemo2(
    () => new three_view_control_default(xOffset, yOffset, width, height),
    [xOffset, yOffset, width, height]
  );
  const scene = useMemo2(() => new Scene(), []);
  const camera = useMemo2(() => {
    switch (mode) {
      case "orthographic":
        return new OrthographicCamera();
      case "perspective":
        return new PerspectiveCamera2();
    }
  }, [mode]);
  const eventHandler = useMemo2(
    () => new EventHandler(),
    []
  );
  useEffect2(() => {
    return addView({
      camera,
      scene,
      eventHandler,
      control
    });
  }, [camera, scene, eventHandler, control, addView]);
  return useMemo2(
    () => /* @__PURE__ */ jsx2(
      StateContextProvider_default,
      {
        contexts: [ThreeViewContext],
        values: [{ scene, camera, control, eventHandler }],
        children
      }
    ),
    [children, scene, camera, control, eventHandler]
  );
}
var ThreeView_default = ThreeView;

// src/js-tools/react-three/useThreeEffect.ts
import { useContext as useContext2, useEffect as useEffect3 } from "react";
function useThreeEffect(effect, deps = []) {
  const [{ renderer }] = useContext2(ThreeContext);
  const [{ scene, camera, control }] = useContext2(ThreeViewContext);
  const effectRef = useUpdatingRef_default(effect);
  const userSeq = useDeps_default(deps);
  useEffect3(() => {
    if (renderer) {
      const destruct = effectRef.current({
        renderer,
        scene,
        camera
      });
      control.forceUpdate();
      return () => {
        if (destruct) {
          destruct();
          control.forceUpdate();
        }
      };
    }
  }, [
    renderer,
    scene,
    camera,
    control,
    effectRef,
    /* effect dep */
    userSeq
  ]);
}
var useThreeEffect_default = useThreeEffect;

// src/js-tools/react-three/useThreeEvent.ts
import { useContext as useContext3, useEffect as useEffect4 } from "react";
function useThreeEvent(name, callback) {
  const [{ control, eventHandler }] = useContext3(ThreeViewContext);
  const callbackRef = useUpdatingRef_default(callback);
  useEffect4(() => {
    const narrowHandler = eventHandler;
    const removeListener = narrowHandler.addListener(
      name,
      (e) => {
        callbackRef.current(e.value);
      }
    );
    control.forceUpdate();
    return () => {
      removeListener();
      control.forceUpdate();
    };
  }, [name, eventHandler, control, callbackRef]);
}
var useThreeEvent_default = useThreeEvent;

// src/js-tools/react-three/useThreeForceUpdate.ts
import { useCallback as useCallback2, useContext as useContext4 } from "react";
function useThreeForceUpdate() {
  const [{ control }] = useContext4(ThreeViewContext);
  return useCallback2(() => {
    control.forceUpdate();
  }, [control]);
}
var useThreeForceUpdate_default = useThreeForceUpdate;

// src/js-tools/react-three/useThreeObject.ts
function useThreeObject(createObject, deps = []) {
  const createObjectRef = useUpdatingRef_default(createObject);
  const userSeq = useDeps_default(deps);
  useThreeEffect_default(
    ({ renderer, scene, camera }) => {
      let obj;
      let destructor;
      const res = createObjectRef.current({ renderer, scene, camera });
      if (res) {
        if (res instanceof Array) {
          [obj, destructor] = res;
        } else {
          obj = res;
        }
      }
      if (obj) {
        scene.add(obj);
        const capturedObj = obj;
        return () => {
          scene.remove(capturedObj);
          destructor?.();
        };
      }
    },
    [
      createObjectRef,
      /* effect dep */
      userSeq
    ]
  );
}
var useThreeObject_default = useThreeObject;
export {
  Three_default as Three,
  ThreeView_default as ThreeView,
  useThreeEffect_default as useThreeEffect,
  useThreeEvent_default as useThreeEvent,
  useThreeForceUpdate_default as useThreeForceUpdate,
  useThreeObject_default as useThreeObject
};
