import { h } from '../aleph.core.js';

import { a as DisplayMode, b as Orientation } from './chunk-1519ad9e.js';

var TypeKeys;
(function (TypeKeys) {
    TypeKeys["NULL"] = "NULL";
    TypeKeys["ERROR"] = "ERROR";
    TypeKeys["APP_SET_SRC"] = "APP_SET_SRC";
    TypeKeys["APP_SET_SRC_LOADED"] = "APP_SET_SRC_LOADED";
    TypeKeys["APP_SET_NODE"] = "APP_SET_NODE";
    TypeKeys["APP_DELETE_NODE"] = "APP_DELETE_NODE";
    TypeKeys["APP_SELECT_NODE"] = "APP_SELECT_NODE";
    TypeKeys["APP_CLEAR_NODES"] = "APP_LOAD_NODES";
    TypeKeys["APP_SET_EDGE"] = "APP_SET_EDGE";
    TypeKeys["APP_DELETE_EDGE"] = "APP_DELETE_EDGE";
    TypeKeys["APP_SELECT_EDGE"] = "APP_SELECT_EDGE";
    TypeKeys["APP_CLEAR_EDGES"] = "APP_LOAD_EDGES";
    TypeKeys["APP_SET_ANGLE"] = "APP_SET_ANGLE";
    TypeKeys["APP_DELETE_ANGLE"] = "APP_DELETE_ANGLE";
    TypeKeys["APP_SELECT_ANGLE"] = "APP_SELECT_ANGLE";
    TypeKeys["APP_CLEAR_ANGLES"] = "APP_LOAD_ANGLES";
    TypeKeys["APP_SET_DISPLAY_MODE"] = "APP_SET_DISPLAY_MODE";
    TypeKeys["APP_SET_ORIENTATION"] = "APP_SET_ORIENTATION";
    TypeKeys["APP_SET_NODES_ENABLED"] = "APP_SET_NODES_ENABLED";
    TypeKeys["APP_SET_BOUNDINGBOX_VISIBLE"] = "APP_SET_BOUNDINGBOX_VISIBLE";
    TypeKeys["APP_SET_SLICES_INDEX"] = "APP_SET_SLICES_INDEX";
    TypeKeys["APP_SET_SLICES_WINDOW_WIDTH"] = "APP_SET_SLICES_WINDOW_WIDTH";
    TypeKeys["APP_SET_SLICES_WINDOW_CENTER"] = "APP_SET_SLICES_WINDOW_CENTER";
    TypeKeys["APP_SET_VOLUME_STEPS"] = "APP_SET_VOLUME_STEPS";
    TypeKeys["APP_SET_VOLUME_WINDOW_WIDTH"] = "APP_SET_VOLUME_WINDOW_WIDTH";
    TypeKeys["APP_SET_VOLUME_WINDOW_CENTER"] = "APP_SET_VOLUME_WINDOW_CENTER";
    TypeKeys["APP_SET_CAMERA"] = "APP_SET_CAMERA";
    TypeKeys["APP_SET_CONTROLS_ENABLED"] = "APP_SET_CONTROLS_ENABLED";
})(TypeKeys || (TypeKeys = {}));
const appSetSrc = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_SET_SRC,
        payload: payload
    });
};
const appSetSrcLoaded = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_SET_SRC_LOADED,
        payload: payload
    });
};
const appSetNode = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_SET_NODE,
        payload: payload
    });
};
const appDeleteNode = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_DELETE_NODE,
        payload: payload
    });
};
const appSelectNode = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_SELECT_NODE,
        payload: payload
    });
};
const appClearNodes = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_CLEAR_NODES,
        payload: payload
    });
};
const appSetEdge = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_SET_EDGE,
        payload: payload
    });
};
const appDeleteEdge = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_DELETE_EDGE,
        payload: payload
    });
};
const appSelectEdge = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_SELECT_EDGE,
        payload: payload
    });
};
const appClearEdges = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_CLEAR_EDGES,
        payload: payload
    });
};
const appSetAngle = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_SET_ANGLE,
        payload: payload
    });
};
const appDeleteAngle = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_DELETE_ANGLE,
        payload: payload
    });
};
const appSelectAngle = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_SELECT_ANGLE,
        payload: payload
    });
};
const appClearAngles = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_CLEAR_ANGLES,
        payload: payload
    });
};
const appSetDisplayMode = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_SET_DISPLAY_MODE,
        payload: payload
    });
};
const appSetNodesEnabled = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_SET_NODES_ENABLED,
        payload: payload
    });
};
const appSetBoundingBoxVisible = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_SET_BOUNDINGBOX_VISIBLE,
        payload: payload
    });
};
const appSetOrientation = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_SET_ORIENTATION,
        payload: payload
    });
};
const appSetSlicesIndex = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_SET_SLICES_INDEX,
        payload: payload
    });
};
const appSetSlicesWindowWidth = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_SET_SLICES_WINDOW_WIDTH,
        payload: payload
    });
};
const appSetSlicesWindowCenter = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_SET_SLICES_WINDOW_CENTER,
        payload: payload
    });
};
const appSetVolumeSteps = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_SET_VOLUME_STEPS,
        payload: payload
    });
};
const appSetVolumeWindowWidth = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_SET_VOLUME_WINDOW_WIDTH,
        payload: payload
    });
};
const appSetVolumeWindowCenter = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_SET_VOLUME_WINDOW_CENTER,
        payload: payload
    });
};
const appSetCamera = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_SET_CAMERA,
        payload: payload
    });
};
const appSetControlsEnabled = (payload) => async (dispatch, _getState) => {
    return dispatch({
        type: TypeKeys.APP_SET_CONTROLS_ENABLED,
        payload: payload
    });
};

function symbolObservablePonyfill(root) {
	var result;
	var Symbol = root.Symbol;

	if (typeof Symbol === 'function') {
		if (Symbol.observable) {
			result = Symbol.observable;
		} else {
			result = Symbol('observable');
			Symbol.observable = result;
		}
	} else {
		result = '@@observable';
	}

	return result;
}

/* global window */

var root;

if (typeof self !== 'undefined') {
  root = self;
} else if (typeof window !== 'undefined') {
  root = window;
} else if (typeof global !== 'undefined') {
  root = global;
} else if (typeof module !== 'undefined') {
  root = module;
} else {
  root = Function('return this')();
}

var result = symbolObservablePonyfill(root);

/**
 * These are private action types reserved by Redux.
 * For any unknown actions, you must return the current state.
 * If the current state is undefined, you must return the initial state.
 * Do not reference these action types directly in your code.
 */
var randomString = function randomString() {
  return Math.random().toString(36).substring(7).split('').join('.');
};

var ActionTypes = {
  INIT: "@@redux/INIT" + randomString(),
  REPLACE: "@@redux/REPLACE" + randomString(),
  PROBE_UNKNOWN_ACTION: function PROBE_UNKNOWN_ACTION() {
    return "@@redux/PROBE_UNKNOWN_ACTION" + randomString();
  }
};

/**
 * @param {any} obj The object to inspect.
 * @returns {boolean} True if the argument appears to be a plain object.
 */
function isPlainObject(obj) {
  if (typeof obj !== 'object' || obj === null) return false;
  var proto = obj;

  while (Object.getPrototypeOf(proto) !== null) {
    proto = Object.getPrototypeOf(proto);
  }

  return Object.getPrototypeOf(obj) === proto;
}

/**
 * Creates a Redux store that holds the state tree.
 * The only way to change the data in the store is to call `dispatch()` on it.
 *
 * There should only be a single store in your app. To specify how different
 * parts of the state tree respond to actions, you may combine several reducers
 * into a single reducer function by using `combineReducers`.
 *
 * @param {Function} reducer A function that returns the next state tree, given
 * the current state tree and the action to handle.
 *
 * @param {any} [preloadedState] The initial state. You may optionally specify it
 * to hydrate the state from the server in universal apps, or to restore a
 * previously serialized user session.
 * If you use `combineReducers` to produce the root reducer function, this must be
 * an object with the same shape as `combineReducers` keys.
 *
 * @param {Function} [enhancer] The store enhancer. You may optionally specify it
 * to enhance the store with third-party capabilities such as middleware,
 * time travel, persistence, etc. The only store enhancer that ships with Redux
 * is `applyMiddleware()`.
 *
 * @returns {Store} A Redux store that lets you read the state, dispatch actions
 * and subscribe to changes.
 */

function createStore(reducer, preloadedState, enhancer) {
  var _ref2;

  if (typeof preloadedState === 'function' && typeof enhancer === 'function' || typeof enhancer === 'function' && typeof arguments[3] === 'function') {
    throw new Error('It looks like you are passing several store enhancers to ' + 'createStore(). This is not supported. Instead, compose them ' + 'together to a single function');
  }

  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState;
    preloadedState = undefined;
  }

  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.');
    }

    return enhancer(createStore)(reducer, preloadedState);
  }

  if (typeof reducer !== 'function') {
    throw new Error('Expected the reducer to be a function.');
  }

  var currentReducer = reducer;
  var currentState = preloadedState;
  var currentListeners = [];
  var nextListeners = currentListeners;
  var isDispatching = false;

  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice();
    }
  }
  /**
   * Reads the state tree managed by the store.
   *
   * @returns {any} The current state tree of your application.
   */


  function getState() {
    if (isDispatching) {
      throw new Error('You may not call store.getState() while the reducer is executing. ' + 'The reducer has already received the state as an argument. ' + 'Pass it down from the top reducer instead of reading it from the store.');
    }

    return currentState;
  }
  /**
   * Adds a change listener. It will be called any time an action is dispatched,
   * and some part of the state tree may potentially have changed. You may then
   * call `getState()` to read the current state tree inside the callback.
   *
   * You may call `dispatch()` from a change listener, with the following
   * caveats:
   *
   * 1. The subscriptions are snapshotted just before every `dispatch()` call.
   * If you subscribe or unsubscribe while the listeners are being invoked, this
   * will not have any effect on the `dispatch()` that is currently in progress.
   * However, the next `dispatch()` call, whether nested or not, will use a more
   * recent snapshot of the subscription list.
   *
   * 2. The listener should not expect to see all state changes, as the state
   * might have been updated multiple times during a nested `dispatch()` before
   * the listener is called. It is, however, guaranteed that all subscribers
   * registered before the `dispatch()` started will be called with the latest
   * state by the time it exits.
   *
   * @param {Function} listener A callback to be invoked on every dispatch.
   * @returns {Function} A function to remove this change listener.
   */


  function subscribe(listener) {
    if (typeof listener !== 'function') {
      throw new Error('Expected the listener to be a function.');
    }

    if (isDispatching) {
      throw new Error('You may not call store.subscribe() while the reducer is executing. ' + 'If you would like to be notified after the store has been updated, subscribe from a ' + 'component and invoke store.getState() in the callback to access the latest state. ' + 'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.');
    }

    var isSubscribed = true;
    ensureCanMutateNextListeners();
    nextListeners.push(listener);
    return function unsubscribe() {
      if (!isSubscribed) {
        return;
      }

      if (isDispatching) {
        throw new Error('You may not unsubscribe from a store listener while the reducer is executing. ' + 'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.');
      }

      isSubscribed = false;
      ensureCanMutateNextListeners();
      var index = nextListeners.indexOf(listener);
      nextListeners.splice(index, 1);
    };
  }
  /**
   * Dispatches an action. It is the only way to trigger a state change.
   *
   * The `reducer` function, used to create the store, will be called with the
   * current state tree and the given `action`. Its return value will
   * be considered the **next** state of the tree, and the change listeners
   * will be notified.
   *
   * The base implementation only supports plain object actions. If you want to
   * dispatch a Promise, an Observable, a thunk, or something else, you need to
   * wrap your store creating function into the corresponding middleware. For
   * example, see the documentation for the `redux-thunk` package. Even the
   * middleware will eventually dispatch plain object actions using this method.
   *
   * @param {Object} action A plain object representing “what changed”. It is
   * a good idea to keep actions serializable so you can record and replay user
   * sessions, or use the time travelling `redux-devtools`. An action must have
   * a `type` property which may not be `undefined`. It is a good idea to use
   * string constants for action types.
   *
   * @returns {Object} For convenience, the same action object you dispatched.
   *
   * Note that, if you use a custom middleware, it may wrap `dispatch()` to
   * return something else (for example, a Promise you can await).
   */


  function dispatch(action) {
    if (!isPlainObject(action)) {
      throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.');
    }

    if (typeof action.type === 'undefined') {
      throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?');
    }

    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.');
    }

    try {
      isDispatching = true;
      currentState = currentReducer(currentState, action);
    } finally {
      isDispatching = false;
    }

    var listeners = currentListeners = nextListeners;

    for (var i = 0; i < listeners.length; i++) {
      var listener = listeners[i];
      listener();
    }

    return action;
  }
  /**
   * Replaces the reducer currently used by the store to calculate the state.
   *
   * You might need this if your app implements code splitting and you want to
   * load some of the reducers dynamically. You might also need this if you
   * implement a hot reloading mechanism for Redux.
   *
   * @param {Function} nextReducer The reducer for the store to use instead.
   * @returns {void}
   */


  function replaceReducer(nextReducer) {
    if (typeof nextReducer !== 'function') {
      throw new Error('Expected the nextReducer to be a function.');
    }

    currentReducer = nextReducer;
    dispatch({
      type: ActionTypes.REPLACE
    });
  }
  /**
   * Interoperability point for observable/reactive libraries.
   * @returns {observable} A minimal observable of state changes.
   * For more information, see the observable proposal:
   * https://github.com/tc39/proposal-observable
   */


  function observable() {
    var _ref;

    var outerSubscribe = subscribe;
    return _ref = {
      /**
       * The minimal observable subscription method.
       * @param {Object} observer Any object that can be used as an observer.
       * The observer object should have a `next` method.
       * @returns {subscription} An object with an `unsubscribe` method that can
       * be used to unsubscribe the observable from the store, and prevent further
       * emission of values from the observable.
       */
      subscribe: function subscribe(observer) {
        if (typeof observer !== 'object' || observer === null) {
          throw new TypeError('Expected the observer to be an object.');
        }

        function observeState() {
          if (observer.next) {
            observer.next(getState());
          }
        }

        observeState();
        var unsubscribe = outerSubscribe(observeState);
        return {
          unsubscribe: unsubscribe
        };
      }
    }, _ref[result] = function () {
      return this;
    }, _ref;
  } // When a store is created, an "INIT" action is dispatched so that every
  // reducer returns their initial state. This effectively populates
  // the initial state tree.


  dispatch({
    type: ActionTypes.INIT
  });
  return _ref2 = {
    dispatch: dispatch,
    subscribe: subscribe,
    getState: getState,
    replaceReducer: replaceReducer
  }, _ref2[result] = observable, _ref2;
}

function getUndefinedStateErrorMessage(key, action) {
  var actionType = action && action.type;
  var actionDescription = actionType && "action \"" + String(actionType) + "\"" || 'an action';
  return "Given " + actionDescription + ", reducer \"" + key + "\" returned undefined. " + "To ignore an action, you must explicitly return the previous state. " + "If you want this reducer to hold no value, you can return null instead of undefined.";
}

function assertReducerShape(reducers) {
  Object.keys(reducers).forEach(function (key) {
    var reducer = reducers[key];
    var initialState = reducer(undefined, {
      type: ActionTypes.INIT
    });

    if (typeof initialState === 'undefined') {
      throw new Error("Reducer \"" + key + "\" returned undefined during initialization. " + "If the state passed to the reducer is undefined, you must " + "explicitly return the initial state. The initial state may " + "not be undefined. If you don't want to set a value for this reducer, " + "you can use null instead of undefined.");
    }

    if (typeof reducer(undefined, {
      type: ActionTypes.PROBE_UNKNOWN_ACTION()
    }) === 'undefined') {
      throw new Error("Reducer \"" + key + "\" returned undefined when probed with a random type. " + ("Don't try to handle " + ActionTypes.INIT + " or other actions in \"redux/*\" ") + "namespace. They are considered private. Instead, you must return the " + "current state for any unknown actions, unless it is undefined, " + "in which case you must return the initial state, regardless of the " + "action type. The initial state may not be undefined, but can be null.");
    }
  });
}
/**
 * Turns an object whose values are different reducer functions, into a single
 * reducer function. It will call every child reducer, and gather their results
 * into a single state object, whose keys correspond to the keys of the passed
 * reducer functions.
 *
 * @param {Object} reducers An object whose values correspond to different
 * reducer functions that need to be combined into one. One handy way to obtain
 * it is to use ES6 `import * as reducers` syntax. The reducers may never return
 * undefined for any action. Instead, they should return their initial state
 * if the state passed to them was undefined, and the current state for any
 * unrecognized action.
 *
 * @returns {Function} A reducer function that invokes every reducer inside the
 * passed object, and builds a state object with the same shape.
 */


function combineReducers(reducers) {
  var reducerKeys = Object.keys(reducers);
  var finalReducers = {};

  for (var i = 0; i < reducerKeys.length; i++) {
    var key = reducerKeys[i];

    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key];
    }
  }

  var finalReducerKeys = Object.keys(finalReducers);

  var shapeAssertionError;

  try {
    assertReducerShape(finalReducers);
  } catch (e) {
    shapeAssertionError = e;
  }

  return function combination(state, action) {
    if (state === void 0) {
      state = {};
    }

    if (shapeAssertionError) {
      throw shapeAssertionError;
    }

    var hasChanged = false;
    var nextState = {};

    for (var _i = 0; _i < finalReducerKeys.length; _i++) {
      var _key = finalReducerKeys[_i];
      var reducer = finalReducers[_key];
      var previousStateForKey = state[_key];
      var nextStateForKey = reducer(previousStateForKey, action);

      if (typeof nextStateForKey === 'undefined') {
        var errorMessage = getUndefinedStateErrorMessage(_key, action);
        throw new Error(errorMessage);
      }

      nextState[_key] = nextStateForKey;
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
    }

    return hasChanged ? nextState : state;
  };
}

function _defineProperty(obj, key, value) {
  if (key in obj) {
    Object.defineProperty(obj, key, {
      value: value,
      enumerable: true,
      configurable: true,
      writable: true
    });
  } else {
    obj[key] = value;
  }

  return obj;
}

function _objectSpread(target) {
  for (var i = 1; i < arguments.length; i++) {
    var source = arguments[i] != null ? arguments[i] : {};
    var ownKeys = Object.keys(source);

    if (typeof Object.getOwnPropertySymbols === 'function') {
      ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) {
        return Object.getOwnPropertyDescriptor(source, sym).enumerable;
      }));
    }

    ownKeys.forEach(function (key) {
      _defineProperty(target, key, source[key]);
    });
  }

  return target;
}

/**
 * Composes single-argument functions from right to left. The rightmost
 * function can take multiple arguments as it provides the signature for
 * the resulting composite function.
 *
 * @param {...Function} funcs The functions to compose.
 * @returns {Function} A function obtained by composing the argument functions
 * from right to left. For example, compose(f, g, h) is identical to doing
 * (...args) => f(g(h(...args))).
 */
function compose() {
  for (var _len = arguments.length, funcs = new Array(_len), _key = 0; _key < _len; _key++) {
    funcs[_key] = arguments[_key];
  }

  if (funcs.length === 0) {
    return function (arg) {
      return arg;
    };
  }

  if (funcs.length === 1) {
    return funcs[0];
  }

  return funcs.reduce(function (a, b) {
    return function () {
      return a(b.apply(void 0, arguments));
    };
  });
}

/**
 * Creates a store enhancer that applies middleware to the dispatch method
 * of the Redux store. This is handy for a variety of tasks, such as expressing
 * asynchronous actions in a concise manner, or logging every action payload.
 *
 * See `redux-thunk` package as an example of the Redux middleware.
 *
 * Because middleware is potentially asynchronous, this should be the first
 * store enhancer in the composition chain.
 *
 * Note that each middleware will be given the `dispatch` and `getState` functions
 * as named arguments.
 *
 * @param {...Function} middlewares The middleware chain to be applied.
 * @returns {Function} A store enhancer applying the middleware.
 */

function applyMiddleware() {
  for (var _len = arguments.length, middlewares = new Array(_len), _key = 0; _key < _len; _key++) {
    middlewares[_key] = arguments[_key];
  }

  return function (createStore) {
    return function () {
      var store = createStore.apply(void 0, arguments);

      var _dispatch = function dispatch() {
        throw new Error("Dispatching while constructing your middleware is not allowed. " + "Other middleware would not be applied to this dispatch.");
      };

      var middlewareAPI = {
        getState: store.getState,
        dispatch: function dispatch() {
          return _dispatch.apply(void 0, arguments);
        }
      };
      var chain = middlewares.map(function (middleware) {
        return middleware(middlewareAPI);
      });
      _dispatch = compose.apply(void 0, chain)(store.dispatch);
      return _objectSpread({}, store, {
        dispatch: _dispatch
      });
    };
  };
}

function createThunkMiddleware(extraArgument) {
  return function (_ref) {
    var dispatch = _ref.dispatch,
        getState = _ref.getState;
    return function (next) {
      return function (action) {
        if (typeof action === 'function') {
          return action(dispatch, getState, extraArgument);
        }

        return next(action);
      };
    };
  };
}

var thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

const getInitialState = () => {
    return {
        angles: new Map(),
        boundingBoxVisible: false,
        camera: null,
        controlsEnabled: true,
        displayMode: DisplayMode.MESH,
        edges: new Map(),
        nodes: new Map(),
        nodesEnabled: false,
        orientation: Orientation.CORONAL,
        selected: null,
        slicesIndex: undefined,
        slicesWindowCenter: undefined,
        slicesWindowWidth: undefined,
        src: null,
        srcLoaded: false,
        volumeSteps: undefined,
        volumeWindowCenter: undefined,
        volumeWindowWidth: undefined
    };
};
const app = (state = getInitialState(), action) => {
    switch (action.type) {
        case TypeKeys.APP_SET_SRC: {
            return Object.assign({}, state, { src: action.payload, srcLoaded: false, selected: null, nodes: new Map(), edges: new Map(), angles: new Map() });
        }
        case TypeKeys.APP_SET_SRC_LOADED: {
            return Object.assign({}, state, { srcLoaded: action.payload });
        }
        case TypeKeys.APP_SET_NODE: {
            const [key, value] = action.payload;
            const sanitisedValue = JSON.parse(JSON.stringify(value));
            const currentValue = state.nodes.get(key);
            let nextValue = Object.assign({}, currentValue, sanitisedValue);
            return Object.assign({}, state, { selected: currentValue ? state.selected : key, nodes: new Map(state.nodes).set(key, nextValue) });
        }
        case TypeKeys.APP_DELETE_NODE: {
            return Object.assign({}, state, { selected: state.selected === action.payload ? null : state.selected, nodes: new Map([...state.nodes].filter(([key]) => key !== action.payload)) });
        }
        case TypeKeys.APP_SELECT_NODE: {
            return Object.assign({}, state, { selected: action.payload });
        }
        case TypeKeys.APP_CLEAR_NODES: {
            return Object.assign({}, state, { nodes: new Map() });
        }
        case TypeKeys.APP_SET_EDGE: {
            const [key, value] = action.payload;
            const sanitisedValue = JSON.parse(JSON.stringify(value));
            const currentValue = state.edges.get(key);
            let nextValue = Object.assign({}, currentValue, sanitisedValue);
            return Object.assign({}, state, { selected: currentValue ? state.selected : key, edges: new Map(state.edges).set(key, nextValue) });
        }
        case TypeKeys.APP_DELETE_EDGE: {
            return Object.assign({}, state, { selected: state.selected === action.payload ? null : state.selected, edges: new Map([...state.edges].filter(([key]) => key !== action.payload)) });
        }
        case TypeKeys.APP_SELECT_EDGE: {
            return Object.assign({}, state, { selected: action.payload });
        }
        case TypeKeys.APP_CLEAR_EDGES: {
            return Object.assign({}, state, { edges: new Map() });
        }
        case TypeKeys.APP_SET_ANGLE: {
            const [key, value] = action.payload;
            const sanitisedValue = JSON.parse(JSON.stringify(value));
            const currentValue = state.angles.get(key);
            let nextValue = Object.assign({}, currentValue, sanitisedValue);
            return Object.assign({}, state, { selected: currentValue ? state.selected : key, angles: new Map(state.angles).set(key, nextValue) });
        }
        case TypeKeys.APP_DELETE_ANGLE: {
            return Object.assign({}, state, { selected: state.selected === action.payload ? null : state.selected, angles: new Map([...state.angles].filter(([key]) => key !== action.payload)) });
        }
        case TypeKeys.APP_SELECT_ANGLE: {
            return Object.assign({}, state, { selected: action.payload });
        }
        case TypeKeys.APP_CLEAR_ANGLES: {
            return Object.assign({}, state, { angles: new Map() });
        }
        case TypeKeys.APP_SET_DISPLAY_MODE: {
            return Object.assign({}, state, { boundingBoxVisible: action.payload === DisplayMode.SLICES, displayMode: action.payload });
        }
        case TypeKeys.APP_SET_ORIENTATION: {
            return Object.assign({}, state, { slicesIndex: undefined, orientation: action.payload });
        }
        case TypeKeys.APP_SET_NODES_ENABLED: {
            return Object.assign({}, state, { nodesEnabled: action.payload });
        }
        case TypeKeys.APP_SET_BOUNDINGBOX_VISIBLE: {
            return Object.assign({}, state, { boundingBoxVisible: action.payload });
        }
        case TypeKeys.APP_SET_SLICES_INDEX: {
            return Object.assign({}, state, { slicesIndex: action.payload });
        }
        case TypeKeys.APP_SET_SLICES_WINDOW_WIDTH: {
            return Object.assign({}, state, { slicesWindowWidth: action.payload });
        }
        case TypeKeys.APP_SET_SLICES_WINDOW_CENTER: {
            return Object.assign({}, state, { slicesWindowCenter: action.payload });
        }
        case TypeKeys.APP_SET_VOLUME_STEPS: {
            return Object.assign({}, state, { volumeSteps: action.payload });
        }
        case TypeKeys.APP_SET_VOLUME_WINDOW_WIDTH: {
            return Object.assign({}, state, { volumeWindowWidth: action.payload });
        }
        case TypeKeys.APP_SET_VOLUME_WINDOW_CENTER: {
            return Object.assign({}, state, { volumeWindowCenter: action.payload });
        }
        case TypeKeys.APP_SET_CAMERA: {
            return Object.assign({}, state, { camera: Object.assign({}, state.camera, action.payload) });
        }
        case TypeKeys.APP_SET_CONTROLS_ENABLED: {
            return Object.assign({}, state, { controlsEnabled: action.payload });
        }
    }
    return state;
};
const rootReducer = combineReducers({
    app
});

const configureStore = (preloadedState) => createStore(rootReducer, preloadedState, applyMiddleware(thunk));

class Constants {
}
Constants.fontSize = 1.5;
Constants.movedEventString = "-moved";
Constants.titleIdString = "-title";
Constants.nodeSizeRatio = 50;
Constants.splashBackSize = 5;
Constants.zoomFactor = 2.5;
Constants.minLoadingMS = 500;
Constants.maxAnimationSteps = 25;
Constants.minFrameMS = 15;
Constants.minTimeForThrottle = Constants.minFrameMS * 5;
Constants.nodeColors = {
    selected: "#76ff03",
    hovered: "#f50057",
    normal: "#00b0ff"
};
Constants.edgeColors = {
    selected: "#c0ff8c",
    hovered: "#ff89b3",
    normal: "#8cdbff"
};
Constants.colorValues = {
    red: "#f50057",
    blue: "#00b0ff",
    black: "#000000",
    white: "#ffffff",
    yellow: "#ffeb3b",
    green: "#76ff03",
    lightRed: "#f77777"
};
Constants.lightValues = {
    ambientLightColor: 0xd0d0d0,
    ambientLightIntensity: 1,
    directionalLight1Color: 0xffffff,
    directionalLight1Intensity: 0.75,
    directionalLight2Color: 0x002958,
    directionalLight2Intensity: 0.5
};
Constants.cameraValues = {
    near: 0.05,
    far: 10000,
    fov: 45,
    maxPolarAngle: 165,
    rotateSpeed: 0.5,
    zoomSpeed: 1,
    dampingFactor: 0.25,
    minDistance: 0
};

class GetUtils {
    static getFileExtension(file) {
        return file.substring(file.lastIndexOf(".") + 1);
    }
    static getNodeWithHighestId(nodes) {
        if (nodes.size) {
            return Math.max.apply(Math, [...nodes].map(([nodeId]) => {
                return this.getNodeIdNumber(nodeId);
            }));
        }
        return -1;
    }
    static getEdgeWithHighestId(edges) {
        if (edges.size) {
            return Math.max.apply(Math, [...edges].map(([edgeId]) => {
                return this.getNodeIdNumber(edgeId);
            }));
        }
        return -1;
    }
    static getNodeIdNumber(nodeId) {
        return Number(nodeId.split("-")[1]);
    }
    static getNextNodeId(nodes) {
        return "node-" + Number(this.getNodeWithHighestId(nodes) + 1);
    }
    static getNextEdgeId(edges) {
        return "edge-" + Number(this.getEdgeWithHighestId(edges) + 1);
    }
    static getGeometryCenter(geometry) {
        let geom;
        if (geometry instanceof THREE.BufferGeometry) {
            geom = new THREE.Geometry().fromBufferGeometry(geometry);
        }
        else {
            geom = geometry;
        }
        geom.computeBoundingSphere();
        return geom.boundingSphere.center;
    }
    static getCameraStateFromEntity(entity) {
        const entityMap = entity.object3DMap;
        let entityMesh = entityMap.mesh;
        let sceneCenter;
        let initialPosition;
        let sceneDistance;
        if (entityMesh) {
            const geom = entityMesh.geometry;
            if (!geom.boundingSphere) {
                geom.computeBoundingSphere();
            }
            sceneCenter = entityMesh.position;
            sceneDistance =
                (Constants.zoomFactor * geom.boundingSphere.radius) /
                    Math.tan((Constants.cameraValues.fov * Math.PI) / 180);
            initialPosition = new THREE.Vector3();
            initialPosition.copy(sceneCenter);
            initialPosition.z += sceneDistance;
            return {
                target: sceneCenter,
                position: initialPosition
            };
        }
        return null;
    }
    static getCameraStateFromNode(node, radius) {
        if (!node) {
            return null;
        }
        let targ = new THREE.Vector3();
        targ.copy(ThreeUtils.stringToVector3(node.target));
        let pos = new THREE.Vector3();
        pos.copy(ThreeUtils.stringToVector3(node.position));
        let dir = pos
            .clone()
            .sub(targ.clone())
            .normalize();
        let camPos = new THREE.Vector3();
        camPos.copy(pos);
        camPos.add(dir.clone().multiplyScalar(radius * Constants.zoomFactor));
        return {
            target: targ,
            position: camPos
        };
    }
}

class AlGltfModel {
    static get Object() {
        return {
            schema: {
                src: { type: "model", default: "" },
                dracoDecoderPath: { type: "string", default: "" }
            },
            init() {
                this.model = null;
                this.loader = new THREE.GLTFLoader();
                THREE.DRACOLoader.setDecoderPath(this.data.dracoDecoderPath);
                this.loader.setDRACOLoader(new THREE.DRACOLoader());
            },
            update() {
                let self = this;
                let el = this.el;
                let src = this.data.src;
                if (!src) {
                    return;
                }
                this.remove();
                this.loader.load(src, function gltfLoaded(gltfModel) {
                    let res = GLTFUtils.setup(gltfModel);
                    self.model = res.mesh;
                    el.setObject3D("mesh", self.model);
                    el.sceneEl.emit(AlGltfModelEvents.LOADED, {
                        format: "gltf",
                        model: self.model
                    });
                    el.sceneEl.emit(AlGltfModelEvents.MESH_DISTANCE, { dist: res.maxDist }, true);
                }, undefined, function gltfFailed(error) {
                    let message = error && error.message
                        ? error.message
                        : "Failed to load glTF model";
                    console.warn(message);
                    el.sceneEl.emit(AlGltfModelEvents.ERROR, {
                        format: "gltf",
                        src: src
                    });
                });
            },
            remove() {
                if (!this.model) {
                    return;
                }
                this.el.removeObject3D("mesh");
            }
        };
    }
    static get Tag() {
        return "al-gltf-model";
    }
}
class AlGltfModelEvents {
}
AlGltfModelEvents.LOADED = "al-model-loaded";
AlGltfModelEvents.MESH_DISTANCE = "al-mesh-distance";
AlGltfModelEvents.ERROR = "al-model-error";

class VolumetricLoader {
    constructor() { }
    load(src, container) {
        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();
            xhr.open("GET", src, true);
            xhr.onload = () => {
                let data = JSON.parse(xhr.responseText);
                data = this._mapfiles(data.baseurl, data.series);
                const loader = new AMI.VolumeLoader(container);
                loader
                    .load(data)
                    .then(() => {
                    const src = loader.data[0].mergeSeries(loader.data);
                    const stack = src[0].stack[0];
                    loader.free();
                    resolve(stack);
                })
                    .catch(error => {
                    console.log("Volume load error");
                    reject(error);
                });
            };
            xhr.onerror = error => {
                reject(error);
            };
            xhr.send();
        });
    }
    _mapfiles(baseurl, files) {
        return files.map(filename => {
            if (!baseurl.endsWith("/")) {
                baseurl += "/";
            }
            return `${baseurl}${filename}`;
        });
    }
}

class AlVolumetricModel {
    static get Object() {
        return {
            schema: {
                src: { type: "model", default: "" }
            },
            init() {
                this.onEnterVR = this.onEnterVR.bind(this);
                this.onExitVR = this.onExitVR.bind(this);
                this.model = null;
                this.loader = new VolumetricLoader();
            },
            update() {
                let el = this.el;
                let src = this.data.src;
                if (!src) {
                    return;
                }
                this.remove();
                this.loader.load(src, el).then(stack => {
                    console.log("stack loaded", stack);
                });
            },
            remove() {
                if (!this.model) {
                    return;
                }
                this.el.removeObject3D("mesh");
            }
        };
    }
    static get Tag() {
        return "al-volumetric-model";
    }
}

class AlNode {
    static get Object() {
        return {
            schema: {
                target: { type: "vec3" },
                scale: { type: "number", default: 1 },
                selected: { type: "boolean" },
                nodesEnabled: { type: "boolean" }
            },
            bindListeners() {
                this.elMouseDown = this.elMouseDown.bind(this);
                this.canvasMouseUp = this.canvasMouseUp.bind(this);
                this.elRaycasterIntersected = this.elRaycasterIntersected.bind(this);
                this.elRaycasterIntersectedCleared = this.elRaycasterIntersectedCleared.bind(this);
            },
            addListeners() {
                this.el.addEventListener("mousedown", this.elMouseDown, {
                    capture: false,
                    once: false,
                    passive: true
                });
                this.el.sceneEl.canvas.addEventListener("mouseup", this.canvasMouseUp, {
                    capture: false,
                    once: false,
                    passive: true
                });
                this.el.addEventListener("raycaster-intersected", this.elRaycasterIntersected, { capture: false, once: false, passive: true });
                this.el.addEventListener("raycaster-intersected-cleared", this.elRaycasterIntersectedCleared, { capture: false, once: false, passive: true });
            },
            removeListeners() {
                this.el.removeEventListener("mousedown", this.elMouseDown);
                this.el.removeEventListener("mouseup", this.canvasMouseUp);
                this.el.removeEventListener("raycaster-intersected", this.elRaycasterIntersected);
                this.el.removeEventListener("raycaster-intersected-cleared", this.elRaycasterIntersectedCleared);
            },
            elMouseDown(_event) {
                this.el.sceneEl.emit(AlNodeEvents.SELECTED, { id: this.el.id }, true);
                if (this.data.nodesEnabled) {
                    let state = this.state;
                    state.mouseDown = true;
                    this.el.sceneEl.emit(AlNodeEvents.CONTROLS_DISABLED, {}, true);
                }
            },
            canvasMouseUp(_event) {
                if (this.data.nodesEnabled) {
                    let state = this.state;
                    state.dragging = false;
                    state.mouseDown = false;
                    this.el.sceneEl.emit(AlNodeEvents.CONTROLS_ENABLED, {}, true);
                }
            },
            elRaycasterIntersected(_event) {
                let state = this.state;
                state.hovered = true;
                this.el.sceneEl.emit(AlNodeEvents.INTERSECTION, { id: this.el.id }, true);
            },
            elRaycasterIntersectedCleared(_event) {
                let state = this.state;
                state.hovered = false;
                if (state.mouseDown && state.selected) {
                    state.dragging = true;
                }
                this.el.sceneEl.emit(AlNodeEvents.INTERSECTION_CLEARED, {}, true);
            },
            init() {
                this.tickFunction = AFRAME.utils.throttle(this.tickFunction, Constants.minFrameMS, this);
                this.bindListeners();
                this.addListeners();
                const data = this.data;
                let el = this.el;
                const camera = el.sceneEl.camera.el.object3DMap.camera;
                const geometry = new THREE.SphereGeometry(data.scale, 16, 16);
                let material = new THREE.MeshBasicMaterial();
                const mesh = new THREE.Mesh(geometry, material);
                el.setObject3D("mesh", mesh);
                let targetPos = ThreeUtils.objectToVector3(data.target);
                this.state = {
                    selected: true,
                    hovered: false,
                    geometry,
                    material,
                    mesh,
                    camera,
                    target: targetPos,
                    dragging: false
                };
            },
            update() {
                let state = this.state;
                state.target = this.data.target;
                state.selected = this.data.selected;
            },
            tickFunction() {
                let state = this.state;
                if (this.data.nodesEnabled && state.dragging) {
                    this.el.sceneEl.emit(AlNodeEvents.DRAGGING, { id: this.el.id }, true);
                }
                if (state.hovered || state.dragging) {
                    state.material.color = new THREE.Color(Constants.nodeColors.hovered);
                }
                else if (state.selected) {
                    state.material.color = new THREE.Color(Constants.nodeColors.selected);
                }
                else {
                    state.material.color = new THREE.Color(Constants.nodeColors.normal);
                }
            },
            tick() {
                this.tickFunction();
            },
            remove() {
                this.removeListeners();
                this.el.removeObject3D("mesh");
            }
        };
    }
    static get Tag() {
        return "al-node";
    }
}
class AlNodeEvents {
}
AlNodeEvents.SELECTED = "al-node-selected";
AlNodeEvents.INTERSECTION = "al-node-intersection";
AlNodeEvents.INTERSECTION_CLEARED = "al-node-intersection-cleared";
AlNodeEvents.DRAGGING = "al-node-dragging";
AlNodeEvents.CONTROLS_ENABLED = "al-controls-enabled";
AlNodeEvents.CONTROLS_DISABLED = "al-controls-disabled";
AlNodeEvents.ANIMATION_STARTED = "al-animation-started";

class AlNodeSpawner {
    static get Object() {
        return {
            dependencies: ["raycaster"],
            schema: {
                nodesEnabled: { type: "boolean" }
            },
            bindListeners() {
                this.canvasMouseDown = this.canvasMouseDown.bind(this);
                this.elRaycasterIntersected = this.elRaycasterIntersected.bind(this);
                this.elRaycasterIntersectedCleared = this.elRaycasterIntersectedCleared.bind(this);
                this.elClick = this.elClick.bind(this);
                this.canvasMouseUp = this.canvasMouseUp.bind(this);
                this.elMouseDown = this.elMouseDown.bind(this);
                this.elMouseUp = this.elMouseUp.bind(this);
            },
            addListeners() {
                this.el.sceneEl.canvas.addEventListener("mousedown", this.canvasMouseDown, { capture: false, once: false, passive: true });
                this.el.sceneEl.canvas.addEventListener("mouseup", this.canvasMouseUp, {
                    capture: false,
                    once: false,
                    passive: true
                });
                this.el.addEventListener("raycaster-intersected", this.elRaycasterIntersected, { capture: false, once: false, passive: true });
                this.el.addEventListener("raycaster-intersected-cleared", this.elRaycasterIntersectedCleared, false);
                this.el.addEventListener("click", this.elClick, {
                    capture: false,
                    once: false,
                    passive: true
                });
                this.el.addEventListener("mousedown", this.elMouseDown, {
                    capture: false,
                    once: false,
                    passive: true
                });
                this.el.addEventListener("mouseup", this.elMouseUp, {
                    capture: false,
                    once: false,
                    passive: true
                });
            },
            removeListeners() {
                this.el.sceneEl.canvas.removeEventListener("mousedown", this.canvasMouseDown);
                this.el.sceneEl.canvas.removeEventListener("mouseup", this.canvasMouseUp);
                this.el.removeEventListener("raycaster-intersected", this.elRaycasterIntersected);
                this.el.removeEventListener("raycaster-intersected-cleared", this.elRaycasterIntersectedCleared);
                this.el.removeEventListener("click", this.elClick);
                this.el.removeEventListener("mousedown", this.elMouseDown);
                this.el.removeEventListener("mouseup", this.elMouseUp);
            },
            canvasMouseDown(event) {
                this.state.left = event.button === 0;
            },
            canvasMouseUp(_event) {
                ThreeUtils.waitOneFrame(() => {
                    this.state.left = false;
                });
            },
            elRaycasterIntersected(_event) {
                this.state.intersecting = true;
                this.el.sceneEl.emit(AlNodeSpawnerEvents.VALID_TARGET, { valid: true }, false);
            },
            elRaycasterIntersectedCleared(_event) {
                this.state.intersecting = false;
                this.el.sceneEl.emit(AlNodeSpawnerEvents.VALID_TARGET, { valid: false }, false);
            },
            elMouseDown(_event) {
                if (this.data.nodesEnabled) {
                    this.el.sceneEl.emit(AlNodeEvents.CONTROLS_DISABLED, {}, false);
                }
            },
            elMouseUp(_event) {
                if (this.data.nodesEnabled) {
                    this.el.sceneEl.emit(AlNodeEvents.CONTROLS_ENABLED, {}, false);
                }
            },
            elClick(event) {
                if (this.state.left && this.data.nodesEnabled) {
                    this.el.sceneEl.emit(AlNodeSpawnerEvents.ADD_NODE, event, false);
                }
            },
            init() {
                this.bindListeners();
                this.addListeners();
                this.state = {
                    left: false,
                    intersecting: false
                };
            },
            remove() {
                this.removeListeners();
            }
        };
    }
    static get Tag() {
        return "al-node-spawner";
    }
}
class AlNodeSpawnerEvents {
}
AlNodeSpawnerEvents.VALID_TARGET = "al-valid-target";
AlNodeSpawnerEvents.ADD_NODE = "al-add-node";

class AlOrbitControl {
    static get Object() {
        return {
            dependencies: ["camera"],
            schema: {
                autoRotate: { type: "boolean" },
                autoRotateSpeed: { default: 2 },
                controlPosition: { type: "vec3" },
                dampingFactor: { default: 0.1 },
                enabled: { default: true },
                enableDamping: { default: true },
                enableKeys: { default: true },
                enablePan: { default: true },
                enableRotate: { default: true },
                enableZoom: { default: true },
                keyPanSpeed: { default: 7 },
                maxAzimuthAngle: { type: "number", default: Infinity },
                maxDistance: { default: 1000 },
                maxPolarAngle: { default: AFRAME.utils.device.isMobile() ? 90 : 120 },
                minAzimuthAngle: { type: "number", default: -Infinity },
                minDistance: { default: 1 },
                minPolarAngle: { default: 0 },
                rotateSpeed: { default: 0.05 },
                screenSpacePanning: { default: false },
                controlTarget: { type: "vec3" },
                zoomSpeed: { type: "number", default: 0.5 },
                animating: { type: "boolean", default: false }
            },
            bindListeners() {
                this.canvasMouseUp = this.canvasMouseUp.bind(this);
                this.canvasMouseDown = this.canvasMouseDown.bind(this);
                this.canvasWheel = this.canvasWheel.bind(this);
                this.emitNewSerial = this.emitNewSerial.bind(this);
                this.catchAnimationCache = this.catchAnimationCache.bind(this);
            },
            addListeners() {
                this.el.sceneEl.canvas.addEventListener("mouseup", this.canvasMouseUp, {
                    capture: false,
                    once: false,
                    passive: true
                });
                this.el.sceneEl.canvas.addEventListener("mousedown", this.canvasMouseDown, { capture: false, once: false, passive: true });
                this.el.sceneEl.canvas.addEventListener("wheel", this.canvasWheel, {
                    capture: false,
                    once: false,
                    passive: true
                });
                this.el.sceneEl.addEventListener(AlOrbitControlEvents.ANIMATION_STARTED, this.catchAnimationCache, false);
            },
            removeListeners() {
                this.el.sceneEl.canvas.removeEventListener("mouseup", this.canvasMouseUp);
                this.el.sceneEl.canvas.removeEventListener("mousedown", this.canvasMouseDown);
                this.el.sceneEl.canvas.removeEventListener("wheel", this.canvasWheel);
                this.el.sceneEl.removeEventListener(AlOrbitControlEvents.ANIMATION_STARTED, this.catchAnimationCache, false);
            },
            catchAnimationCache(event) {
                this.state.animationCache = event.detail.cache;
            },
            emitNewSerial() {
                let res = {
                    position: this.state.controls.object.position,
                    target: this.state.controls.target
                };
                this.el.sceneEl.emit(AlOrbitControlEvents.UPDATED, { cameraSerial: res }, false);
            },
            canvasMouseUp(_event) {
                document.body.style.cursor = "grab";
                let controls = this.state.controls;
                if (controls.enabled) {
                    this.emitNewSerial();
                }
            },
            canvasMouseDown(_event) {
                document.body.style.cursor = "grabbing";
            },
            canvasWheel(_event) {
                window.clearTimeout(this.canvasWheel);
                this.canvasWheel = ThreeUtils.waitOneFrame(() => {
                    this.emitNewSerial();
                });
            },
            init() {
                let el = this.el;
                let data = this.data;
                document.body.style.cursor = "grab";
                this.tickFunction = AFRAME.utils.throttle(this.tickFunction, Constants.minFrameMS, this);
                let controls = new THREE.OrbitControls(el.getObject3D("camera"), el.sceneEl.renderer.domElement);
                let controlPosition = ThreeUtils.objectToVector3(data.controlPosition);
                let controlTarget = ThreeUtils.objectToVector3(data.controlTarget);
                controls.object.position.copy(controlPosition);
                el.getObject3D("camera").position.copy(controlPosition);
                controls.target.copy(controlTarget);
                let animationCache = [];
                this.state = {
                    controls,
                    animationCache
                };
                this.bindListeners();
                this.addListeners();
                ThreeUtils.waitOneFrame(() => {
                    this.emitNewSerial();
                });
            },
            update(_oldData) {
                let controls = this.state.controls;
                const data = this.data;
                controls.target = ThreeUtils.objectToVector3(data.controlTarget);
                controls.autoRotate = data.autoRotate;
                controls.autoRotateSpeed = data.autoRotateSpeed;
                controls.dampingFactor = data.dampingFactor;
                controls.enabled = data.enabled;
                controls.enableDamping = data.enableDamping;
                controls.enableKeys = data.enableKeys;
                controls.enablePan = data.enablePan;
                controls.enableRotate = data.enableRotate;
                controls.enableZoom = data.enableZoom;
                controls.keyPanSpeed = data.keyPanSpeed;
                controls.maxPolarAngle = THREE.Math.degToRad(data.maxPolarAngle);
                controls.maxAzimuthAngle = THREE.Math.degToRad(data.maxAzimuthAngle);
                controls.maxDistance = data.maxDistance;
                controls.minDistance = data.minDistance;
                controls.minPolarAngle = THREE.Math.degToRad(data.minPolarAngle);
                controls.minAzimuthAngle = THREE.Math.degToRad(data.minAzimuthAngle);
                controls.rotateSpeed = data.rotateSpeed;
                controls.screenSpacePanning = data.screenSpacePanning;
                controls.zoomSpeed = data.zoomSpeed;
                this.el
                    .getObject3D("camera")
                    .position.copy(ThreeUtils.objectToVector3(data.controlPosition));
            },
            tickFunction() {
                let controls = this.state.controls;
                if (!controls.enabled) {
                    return;
                }
                if (this.data.animating) {
                    let nextFrame = this.state.animationCache.shift();
                    controls.object.position.copy(nextFrame.position);
                    this.el.getObject3D("camera").position.copy(nextFrame.position);
                    controls.target.copy(nextFrame.target);
                    if (this.state.animationCache.length === 0) {
                        this.el.sceneEl.emit(AlOrbitControlEvents.ANIMATION_FINISHED, {}, false);
                    }
                }
                if (controls.enabled &&
                    (controls.enableDamping || controls.autoRotate)) {
                    controls.update();
                }
            },
            tick() {
                this.tickFunction();
            },
            remove() {
                this.removeEventListener();
                let state = this.state;
                state.controls.dispose();
                state = null;
            }
        };
    }
    static get Tag() {
        return "al-orbit-control";
    }
}
class AlOrbitControlEvents {
}
AlOrbitControlEvents.UPDATED = "al-orbit-controls-updated";
AlOrbitControlEvents.ANIMATION_STARTED = "al-orbit-controls-animation-started";
AlOrbitControlEvents.ANIMATION_FINISHED = "al-orbit-controls-animation-finished";

class AlHalo {
    static get Object() {
        return {
            schema: {},
            vertexShader: `
      varying vec3 vNormal;

      void main()
      {
          vNormal = normalize( normalMatrix * normal );
          gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
      }
      `,
            fragmentShader: `
      varying vec3 vNormal;
      uniform float r;
      uniform float g;
      uniform float b;
      uniform float a;
      uniform float c;

      void main()
      {
          float intensity = pow( c - dot( vNormal, vec3( 0.0, 0.0, 1.0 ) ), 4.0 );
          gl_FragColor = vec4(r, g, b, a) * intensity;
      }`,
            init() { }
        };
    }
    static get Tag() {
        return "al-halo";
    }
}

class AlSpinner {
    static get Object() {
        return {
            schema: {
                detail: { default: 0, min: 0, max: 5, type: "int" },
                radius: { default: 1, min: 0 }
            },
            init(data) {
                this.geometry = new THREE.TetrahedronGeometry(data.radius, data.detail);
                this.geometry.applyMatrix(new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(1, 0, -1).normalize(), Math.atan(Math.sqrt(2))));
            }
        };
    }
    static get Tag() {
        return "al-spinner";
    }
}

class AlFixedToOrbitCamera {
    static get Object() {
        return {
            schema: {
                distanceFromTarget: { type: "number", default: 0.1 },
                target: { type: "vec3" }
            },
            init(_data) {
                this.tickFunction = AFRAME.utils.throttle(this.tickFunction, Constants.minFrameMS, this);
                if (this.data.target) {
                    let targ = ThreeUtils.objectToVector3(this.data.target);
                    this.state = {
                        distanceFromTarget: this.data.distanceFromTarget,
                        target: targ
                    };
                }
                else {
                    this.state = {
                        distanceFromTarget: this.data.distanceFromTarget,
                        target: new THREE.Vector3(0, 0, 0)
                    };
                }
            },
            update(_oldData) {
                let targ = ThreeUtils.objectToVector3(this.data.target);
                this.state = {
                    distanceFromTarget: this.data.distanceFromTarget,
                    target: targ
                };
            },
            tickFunction() {
                let el = this.el;
                let state = this.state;
                const camPos = el.sceneEl.camera.position;
                const dir = state.target
                    .clone()
                    .sub(camPos.clone()).normalize();
                el.object3D.position.copy(dir.multiplyScalar(state.distanceFromTarget));
                el.object3D.lookAt(camPos);
            },
            tick() {
                this.tickFunction();
            }
        };
    }
    static get Tag() {
        return "al-fixed-to-orbit-camera";
    }
}

class AlLookToCamera {
    static get Object() {
        return {
            schema: {},
            init() {
                this.tickFunction = AFRAME.utils.throttle(this.tickFunction, Constants.minFrameMS, this);
            },
            tickFunction() {
                this.el.object3D.lookAt(this.el.sceneEl.camera.position);
            },
            tick() {
                this.tickFunction();
            }
        };
    }
    static get Tag() {
        return "al-look-to-camera";
    }
}

class AlRenderOverlaid {
    static get Object() {
        return {
            schema: {},
            init(_data) {
                let mesh = this.el.object3DMap.mesh;
                mesh.renderOrder = 999;
                mesh.material.depthTest = false;
            }
        };
    }
    static get Tag() {
        return "al-render-overlaid";
    }
}

class AlRenderOverlaidText {
    static get Object() {
        return {
            schema: {},
            init(_data) {
                let mesh = this.el.object3DMap.text;
                mesh.renderOrder = 999;
                mesh.material.depthTest = false;
            }
        };
    }
    static get Tag() {
        return "al-render-overlaid-text";
    }
}

class AlRenderOverlaidLine {
    static get Object() {
        return {
            schema: {},
            init(_data) {
                let mesh = this.el.object3DMap.line;
                mesh.renderOrder = 998;
                mesh.material.depthTest = false;
                mesh.material.color = new THREE.Color(Constants.colorValues.blue);
            }
        };
    }
    static get Tag() {
        return "al-render-overlaid-line";
    }
}

class CreateUtils {
    static createAframeComponents() {
        AFRAME.registerShader(AlHalo.Tag, AlHalo.Object);
        AFRAME.registerGeometry(AlSpinner.Tag, AlSpinner.Object);
        AFRAME.registerComponent(AlGltfModel.Tag, AlGltfModel.Object);
        AFRAME.registerComponent(AlVolumetricModel.Tag, AlVolumetricModel.Object);
        AFRAME.registerComponent(AlNode.Tag, AlNode.Object);
        AFRAME.registerComponent(AlNodeSpawner.Tag, AlNodeSpawner.Object);
        AFRAME.registerComponent(AlOrbitControl.Tag, AlOrbitControl.Object);
        AFRAME.registerComponent(AlFixedToOrbitCamera.Tag, AlFixedToOrbitCamera.Object);
        AFRAME.registerComponent(AlLookToCamera.Tag, AlLookToCamera.Object);
        AFRAME.registerComponent(AlRenderOverlaid.Tag, AlRenderOverlaid.Object);
        AFRAME.registerComponent(AlRenderOverlaidText.Tag, AlRenderOverlaidText.Object);
        AFRAME.registerComponent(AlRenderOverlaidLine.Tag, AlRenderOverlaidLine.Object);
    }
}

class ThreeUtils {
    static enableCamera(camEntity, enabled) {
        camEntity.setAttribute("al-orbit-control", `enabled: ${enabled}`);
    }
    static waitOneFrame(func) {
        window.setTimeout(() => {
            func();
        }, Constants.minFrameMS);
    }
    static objectToVector3(vec) {
        let res = new THREE.Vector3();
        res.x = vec.x;
        res.y = vec.y;
        res.z = vec.z;
        return res;
    }
    static worldToScreen(worldCoordinate, camera, container) {
        const screenCoordinates = worldCoordinate.clone();
        screenCoordinates.project(camera);
        screenCoordinates.x = Math.round(((screenCoordinates.x + 1) * container.offsetWidth) / 2);
        screenCoordinates.y = Math.round(((-screenCoordinates.y + 1) * container.offsetHeight) / 2);
        screenCoordinates.z = 0;
        return screenCoordinates;
    }
    static vector3ToString(vec) {
        return vec.toArray().join(" ");
    }
    static stringToVector3(vec) {
        let res = vec.split(" ");
        let vect = new THREE.Vector3();
        vect.x = Number(res[0]);
        vect.y = Number(res[1]);
        vect.z = Number(res[2]);
        return vect;
    }
    static slerp(start, end, percent) {
        const t = percent;
        const p0 = start;
        const p1 = end;
        let theta = p0.angleTo(p1);
        if (theta) {
            theta = THREE.Math.clamp(theta, -0.99, 0.99);
            const topP0 = Math.sin(1 - t) * theta;
            const topP1 = Math.sin(t * theta);
            const bot = Math.sin(theta);
            const p0Const = topP0 / bot;
            const p1Const = topP1 / bot;
            const left = p0.clone().multiplyScalar(p0Const);
            const right = p1.clone().multiplyScalar(p1Const);
            const result = left.add(right);
            return result;
        }
        else {
            return null;
        }
    }
    static sendAnimationCache(scene, start, end, positionChange, targetChange) {
        let cache = [];
        for (let frame = 1; frame <= Constants.maxAnimationSteps; frame++) {
            let percent = frame / Constants.maxAnimationSteps;
            cache.push({
                position: positionChange
                    ? ThreeUtils.slerp(start.position.clone(), end.position.clone(), percent)
                    : end.position,
                target: targetChange
                    ? ThreeUtils.slerp(start.target.clone(), end.target.clone(), percent)
                    : end.target
            });
        }
        scene.emit(AlOrbitControlEvents.ANIMATION_STARTED, { cache }, false);
    }
}

class GLTFUtils {
    static setup(gltf) {
        const obj = gltf.scene || gltf.scenes[0];
        let mesh;
        if (obj instanceof THREE.Mesh) {
            mesh = obj;
        }
        else {
            obj.children.forEach(child => {
                if (child instanceof THREE.Mesh) {
                    mesh = child;
                }
            });
        }
        let geom = mesh.geometry;
        if (geom instanceof THREE.BufferGeometry) {
            geom = new THREE.Geometry().fromBufferGeometry(mesh.geometry);
        }
        let maxDist = geom.vertices.reduce((a, b) => {
            return new THREE.Vector3(Math.min(a.length(), b.length()), 0, 0);
        }).x;
        return { mesh, maxDist };
    }
}

var RayType;
(function (RayType) {
    RayType["LOCK"] = "LOCK";
    RayType["MESH"] = "MESH";
})(RayType || (RayType = {}));

var MeshFileType;
(function (MeshFileType) {
    MeshFileType["GLTF"] = "gltf";
    MeshFileType["GLB"] = "glb";
})(MeshFileType || (MeshFileType = {}));

class Aleph {
    constructor() {
        this._intersectingNode = null;
        this._isShiftDown = false;
        this.width = "640px";
        this.height = "480px";
        this.debug = false;
        this.spinnerColor = "#fff";
    }
    async load(src) {
        const fileExtension = GetUtils.getFileExtension(src);
        if (Object.values(MeshFileType).includes(fileExtension)) {
            if (this.displayMode !== DisplayMode.MESH) {
                throw new Error("When setting 'src' to a mesh file you must set 'displayMode' to 'mesh'");
            }
        }
        else {
            if (this.displayMode === DisplayMode.MESH) {
                throw new Error("When setting 'src' to a non-mesh file you must set 'displayMode' to either 'slices' or 'volume'");
            }
        }
        if (this.src) {
            this._setSrc(null);
            setTimeout(() => {
                this._setSrc(src);
            }, Constants.minLoadingMS);
        }
        else {
            this._setSrc(src);
        }
    }
    resize() {
        if (this.srcLoaded) {
            const camera = this._scene.sceneEl
                .camera;
            const renderer = this._scene.sceneEl.renderer;
            camera.aspect =
                this._container.offsetWidth / this._container.offsetHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(this._container.offsetWidth, this._container.offsetHeight);
        }
    }
    async setNode(node) {
        this._setNode(node);
    }
    async setNodes(nodes) {
        this._setNodes(nodes);
    }
    async deleteNode(nodeId) {
        this._deleteNode(nodeId);
    }
    async clearNodes() {
        this._clearNodes();
    }
    async selectNode(nodeId) {
        this._selectNode(nodeId, true);
    }
    async setEdge(edge) {
        this._setEdge(edge);
    }
    async setDisplayMode(displayMode) {
        this.appSetDisplayMode(displayMode);
    }
    async setNodesEnabled(enabled) {
        this._setNodesEnabled(enabled);
    }
    async setBoundingBoxVisible(visible) {
        this._setBoundingBoxVisible(visible);
    }
    componentWillLoad() {
        CreateUtils.createAframeComponents();
        this.store.setStore(configureStore({}));
        this.store.mapStateToProps(this, state => {
            const { app: { angles, boundingBoxVisible, camera, controlsEnabled, displayMode, edges, nodes, nodesEnabled, orientation, selected, slicesIndex, slicesWindowCenter, slicesWindowWidth, src, srcLoaded, volumeSteps, volumeWindowCenter, volumeWindowWidth } } = state;
            return {
                angles,
                boundingBoxVisible,
                camera,
                controlsEnabled,
                displayMode,
                edges,
                nodes,
                nodesEnabled,
                orientation,
                selected,
                slicesIndex,
                slicesWindowCenter,
                slicesWindowWidth,
                src,
                srcLoaded,
                volumeSteps,
                volumeWindowCenter,
                volumeWindowWidth
            };
        });
        this.store.mapDispatchToProps(this, {
            appClearAngles,
            appClearEdges,
            appClearNodes,
            appDeleteAngle,
            appDeleteEdge,
            appDeleteNode,
            appSelectAngle,
            appSelectEdge,
            appSelectNode,
            appSetAngle,
            appSetBoundingBoxVisible,
            appSetCamera,
            appSetControlsEnabled,
            appSetDisplayMode,
            appSetEdge,
            appSetNode,
            appSetNodesEnabled,
            appSetOrientation,
            appSetSlicesIndex,
            appSetSlicesWindowCenter,
            appSetSlicesWindowWidth,
            appSetSrc,
            appSetSrcLoaded,
            appSetVolumeSteps,
            appSetVolumeWindowCenter,
            appSetVolumeWindowWidth
        });
        this._animationFinished = this._animationFinished.bind(this);
        this._canvasMouseDown = this._canvasMouseDown.bind(this);
        this._canvasMouseUp = this._canvasMouseUp.bind(this);
        this._controlsDisabledHandler = this._controlsDisabledHandler.bind(this);
        this._controlsEnabledHandler = this._controlsEnabledHandler.bind(this);
        this._controlsUpdated = this._controlsUpdated.bind(this);
        this._intersectingNodeEventHandler = this._intersectingNodeEventHandler.bind(this);
        this._intersectionClearedEventHandler = this._intersectionClearedEventHandler.bind(this);
        this._nodeMovedEventHandler = this._nodeMovedEventHandler.bind(this);
        this._nodeSelectedEventHandler = this._nodeSelectedEventHandler.bind(this);
        this._setNodeEventHandler = this._setNodeEventHandler.bind(this);
        this._srcLoaded = this._srcLoaded.bind(this);
        this._validTargetEventHandler = this._validTargetEventHandler.bind(this);
    }
    _renderSpinner() {
        if (!this.srcLoaded) {
            return (h("a-entity", { "al-fixed-to-orbit-camera": `
          distanceFromTarget: 20;
          target: ${this.camera ? this.camera.position : new THREE.Vector3(0, 0, 0)};
        ` },
                h("a-entity", { animation: "property: rotation; to: 0 120 0; loop: true; dur: 1000; easing: easeInOutQuad", geometry: "primitive: al-spinner;", material: `color: ${this.spinnerColor};` })));
        }
        return null;
    }
    _renderSrc() {
        if (!this.src) {
            return null;
        }
        let backScale = 0;
        let backBoard = null;
        if (this._boundingSphereRadius) {
            backScale = this._boundingSphereRadius * Constants.splashBackSize;
            backBoard = (h("a-entity", { class: "collidable", id: "back-board", geometry: `primitive: plane; height: ${backScale}; width: ${backScale}`, "al-fixed-to-orbit-camera": `
          distanceFromTarget: ${this._boundingSphereRadius};
          target: ${this.camera ? this.camera.target : new THREE.Vector3(0, 0, 0)};
        `, material: `
          wireframe: true;
          side: double;
        `, ref: el => (this._backBoard = el) }));
        }
        switch (this.displayMode) {
            case DisplayMode.MESH: {
                return [
                    h("a-entity", { "al-node-spawner": `
              nodesEnabled: ${this.nodesEnabled};
            `, "al-edge-spawner": true, class: "collidable", id: "target-entity", "al-gltf-model": `
              src: url(${this.src});
              dracoDecoderPath: ${this.dracoDecoderPath};
            `, position: "0 0 0", scale: "1 1 1", ref: (el) => (this._targetEntity = el) }),
                    backBoard
                ];
            }
            default: {
                return [
                    h("a-entity", { "al-node-spawner": `
              nodesEnabled: ${this.nodesEnabled};
            `, "al-edge-spawner": true, class: "collidable", id: "target-entity", "al-volumetric-model": `
              src: url(${this.src});
            `, position: "0 0 0", scale: "1 1 1", ref: (el) => (this._targetEntity = el) }),
                    backBoard
                ];
            }
        }
    }
    _renderNodes() {
        return [...this.nodes].map((n) => {
            const [nodeId, node] = n;
            let textOffset = new THREE.Vector3(0, 2.5, 0);
            textOffset.multiplyScalar(node.scale);
            return (h("a-entity", { class: "collidable", id: nodeId, position: node.position, "al-node": `
            target: ${node.target};
            scale: ${node.scale};
            selected: ${this.selected === nodeId};
            nodesEnabled: ${this.nodesEnabled};
          ` },
                h("a-entity", { text: `
              value: ${node.text};
              side: double;
              align: center;
              baseline: bottom;
              anchor: center;
            `, "al-look-to-camera": true, "al-render-overlaid-text": true, position: ThreeUtils.vector3ToString(textOffset), id: `${nodeId}-label` })));
        });
    }
    _renderEdges() {
        return [...this.edges].map((n) => {
            const [edgeId, edge] = n;
            let line = "";
            let centoid = new THREE.Vector3(0, 0, 0);
            let dist = 0;
            if (this.nodes.get(edge.node2Id)) {
                const start = this.nodes.get(edge.node1Id).position;
                const end = this.nodes.get(edge.node2Id).position;
                line = `
        start: ${this.nodes.get(edge.node1Id).position};
        end: ${this.nodes.get(edge.node2Id).position};
        `;
                const sv = ThreeUtils.stringToVector3(start);
                const ev = ThreeUtils.stringToVector3(end);
                dist = sv.clone().distanceTo(ev.clone());
                centoid.copy(sv
                    .clone()
                    .sub(ev.clone())
                    .normalize()
                    .multiplyScalar(dist / 2));
            }
            return (h("a-entity", { class: "collidable", id: edgeId, line: line, "al-render-overlaid-line": true },
                h("a-entity", { id: `${edgeId}-title`, text: `
              value: ${dist + " units"};
              side: double;
              align: center;
              baseline: bottom;
              anchor: center;
            `, "al-look-to-camera": true, "al-render-overlaid-text": true, position: ThreeUtils.vector3ToString(centoid) })));
        });
    }
    _renderLights() {
        return [
            h("a-entity", { id: "light-1", light: "type: directional; color: #ffffff; intensity: 0.75", position: "1 1 1" }),
            h("a-entity", { id: "light-2", light: "type: directional; color: #002958; intensity: 0.5", position: "-1 -1 -1" }),
            h("a-entity", { id: "light-3", light: "type: ambient; color: #d0d0d0; intensity: 1" })
        ];
    }
    _renderCamera() {
        return (h("a-camera", { fov: Constants.cameraValues.fov, near: Constants.cameraValues.near, "look-controls": "enabled: false", far: Constants.cameraValues.far, id: "mainCamera", cursor: "rayOrigin: mouse", raycaster: "objects: .collidable", "al-orbit-control": `
          maxPolarAngle: ${Constants.cameraValues.maxPolarAngle};
          minDistance: ${Constants.cameraValues.minDistance};
          screenSpacePanning: true;
          rotateSpeed: ${Constants.cameraValues.rotateSpeed};
          zoomSpeed: ${Constants.cameraValues.zoomSpeed};
          enableDamping: true;
          dampingFactor: ${Constants.cameraValues.dampingFactor};
          controlTarget: ${ThreeUtils.vector3ToString(this.camera ? this.camera.target : new THREE.Vector3(0, 0, 0))};
          controlPosition: ${ThreeUtils.vector3ToString(this.camera ? this.camera.position : new THREE.Vector3(0, 0, 0))};
          enabled: ${this.controlsEnabled};
          animating: ${this.camera && this.camera.animating ? this.camera.animating : false}
        `, ref: el => (this._camera = el) }, this._renderSpinner()));
    }
    _renderScene() {
        return (h("a-scene", { embedded: true, renderer: "colorManagement: true;", "vr-mode-ui": "enabled: false", ref: el => (this._scene = el), ortho: true },
            this._renderSrc(),
            this._renderNodes(),
            this._renderEdges(),
            this._renderLights(),
            this._renderCamera()));
    }
    render() {
        return (h("div", { id: "al-container", style: {
                width: this.width,
                height: this.height
            }, ref: el => (this._container = el) }, this._renderScene()));
    }
    _createEdge(node1, node2) {
        const newEdge = {
            node1Id: node1,
            node2Id: node2
        };
        const edgeId = GetUtils.getNextEdgeId(this.edges);
        this._setEdge([edgeId, newEdge]);
    }
    _getAppState() {
        return this.store.getState().app;
    }
    _clearNodes() {
        this.appClearNodes();
        this.onChanged.emit(this._getAppState());
    }
    _setNodes(nodes) {
        nodes.forEach((value, key) => {
            this.appSetNode([key, value]);
        });
        this.onChanged.emit(this._getAppState());
    }
    _deleteNode(nodeId) {
        this.appDeleteNode(nodeId);
        this.onChanged.emit(this._getAppState());
    }
    _setNode(node) {
        ThreeUtils.waitOneFrame(() => {
            this.appSetNode(node);
            this.onChanged.emit(this._getAppState());
        });
    }
    _setEdge(edge) {
        this.appSetEdge(edge);
        this.onChanged.emit(this._getAppState());
    }
    _selectNode(nodeId, animate) {
        if (animate && nodeId !== this.selected) {
            let animationStart = {
                position: this.camera.position.clone(),
                target: this.camera.target.clone()
            };
            let animationEnd = {
                position: new THREE.Vector3(-1, -1, -1),
                target: new THREE.Vector3(-1, -1, -1)
            };
            let result = GetUtils.getCameraStateFromNode(this.nodes.get(nodeId), this._boundingSphereRadius);
            if (result) {
                const diffPos = result.position.distanceTo(this.camera.position);
                let diffTarg;
                this.camera.target
                    ? (diffTarg = result.target.distanceTo(this.camera.target))
                    : (diffTarg = 0);
                if (diffPos > 0 || diffTarg > 0) {
                    diffPos > 0
                        ? animationEnd.position.copy(result.position)
                        : animationEnd.position.copy(this.camera.position);
                    diffTarg > 0
                        ? animationEnd.target.copy(result.target)
                        : animationEnd.target.copy(this.camera.target);
                    ThreeUtils.sendAnimationCache(this._scene, animationStart, animationEnd, diffPos > 0, diffTarg > 0);
                    ThreeUtils.waitOneFrame(() => {
                        this.appSetCamera({
                            animating: true
                        });
                        this.appSelectNode(nodeId);
                        this.onChanged.emit(this._getAppState());
                    });
                }
            }
        }
        else {
            this.appSelectNode(nodeId);
            this.onChanged.emit(this._getAppState());
        }
    }
    _setNodesEnabled(enabled) {
        this.appSetNodesEnabled(enabled);
        this.onChanged.emit(this._getAppState());
    }
    _setBoundingBoxVisible(visible) {
        this.appSetBoundingBoxVisible(visible);
        this.onChanged.emit(this._getAppState());
    }
    _setSrc(src) {
        this.appSetSrc(src);
        this.onChanged.emit(this._getAppState());
    }
    _srcLoaded() {
        const mesh = this._targetEntity.object3DMap.mesh;
        mesh.geometry.computeBoundingSphere();
        this._boundingSphereRadius = mesh.geometry.boundingSphere.radius;
        let result = GetUtils.getCameraStateFromEntity(this._targetEntity);
        if (result) {
            this.appSetCamera({
                position: result.position,
                target: result.target
            });
        }
        this.appSetSrcLoaded(true);
        this.onChanged.emit(this._getAppState());
    }
    _canvasMouseDown(event) {
        this._isShiftDown = event.shiftKey;
    }
    _canvasMouseUp(_event) {
        this._isShiftDown = false;
    }
    _controlsUpdated(event) {
        this.appSetCamera(event.detail.cameraSerial);
    }
    _controlsEnabledHandler(_event) {
        this.appSetControlsEnabled(true);
        ThreeUtils.enableCamera(this._camera, true);
    }
    _controlsDisabledHandler(_event) {
        this.appSetControlsEnabled(false);
        ThreeUtils.enableCamera(this._camera, false);
    }
    _intersectionClearedEventHandler(_event) {
        this._intersectingNode = null;
    }
    _intersectingNodeEventHandler(event) {
        this._intersectingNode = event.detail.id;
    }
    _setNodeEventHandler(event) {
        if (this.nodesEnabled &&
            this._validTarget &&
            this._intersectingNode === null) {
            let intersection = event.detail.detail.intersection;
            const nodeId = GetUtils.getNextNodeId(this.nodes);
            const newNode = {
                target: ThreeUtils.vector3ToString(this._targetEntity.object3D.position),
                position: ThreeUtils.vector3ToString(intersection.point),
                scale: this._boundingSphereRadius / Constants.nodeSizeRatio,
                text: nodeId
            };
            const previousSelected = this.selected;
            this._setNode([nodeId, newNode]);
            if (this._isShiftDown &&
                this.selected) {
                this._createEdge(previousSelected, nodeId);
            }
        }
    }
    _validTargetEventHandler(event) {
        this._validTarget = event.detail.valid;
    }
    _animationFinished(_event) {
        this.appSetCamera({
            animating: false
        });
    }
    _nodeSelectedEventHandler(event) {
        if (this._intersectingNode !== null &&
            this.nodesEnabled &&
            this.selected !== null &&
            this.selected !== this._intersectingNode &&
            this._isShiftDown) {
            this._createEdge(this.selected, this._intersectingNode);
        }
        this._selectNode(event.detail.id, false);
    }
    _nodeMovedEventHandler(event) {
        const nodeId = event.detail.id;
        const raycaster = this._camera.components.raycaster;
        let intersection = raycaster.getIntersection(this._targetEntity);
        if (!intersection) {
            intersection = raycaster.getIntersection(this._backBoard);
        }
        if (intersection) {
            this._setNode([
                nodeId,
                {
                    position: ThreeUtils.vector3ToString(intersection.point)
                }
            ]);
            const eventName = nodeId + Constants.movedEventString;
            this._scene.emit(eventName, {}, true);
        }
        else {
            console.log("No intersection!");
        }
    }
    _addEventListeners() {
        this._scene.canvas.addEventListener("mousedown", this._canvasMouseDown, {
            capture: false,
            once: false,
            passive: true
        });
        this._scene.canvas.addEventListener("mouseup", this._canvasMouseUp, {
            capture: false,
            once: false,
            passive: true
        });
        this._scene.addEventListener(AlOrbitControlEvents.ANIMATION_FINISHED, this._animationFinished, false);
        this._scene.addEventListener(AlOrbitControlEvents.UPDATED, this._controlsUpdated, false);
        this._scene.addEventListener(AlNodeEvents.CONTROLS_ENABLED, this._controlsEnabledHandler, false);
        this._scene.addEventListener(AlNodeEvents.CONTROLS_DISABLED, this._controlsDisabledHandler, false);
        this._scene.addEventListener(AlNodeEvents.DRAGGING, this._nodeMovedEventHandler, false);
        this._scene.addEventListener(AlNodeSpawnerEvents.ADD_NODE, this._setNodeEventHandler, false);
        this._scene.addEventListener(AlNodeEvents.SELECTED, this._nodeSelectedEventHandler, false);
        this._scene.addEventListener(AlNodeSpawnerEvents.VALID_TARGET, this._validTargetEventHandler, false);
        this._scene.addEventListener(AlGltfModelEvents.LOADED, this._srcLoaded, false);
        this._scene.addEventListener(AlNodeEvents.INTERSECTION, this._intersectingNodeEventHandler, false);
        this._scene.addEventListener(AlNodeEvents.INTERSECTION_CLEARED, this._intersectionClearedEventHandler, false);
    }
    componentDidLoad() { }
    componentDidUpdate() {
        if (this._scene) {
            this._addEventListeners();
        }
        if (this.debug) {
            try {
                const mat = this._camera.object3DMap.text
                    .material;
                if (mat) {
                    mat.transparent = true;
                }
            }
            catch (_a) { }
        }
    }
    static get is() { return "uv-aleph"; }
    static get properties() { return {
        "angles": {
            "state": true
        },
        "boundingBoxVisible": {
            "state": true
        },
        "camera": {
            "state": true
        },
        "clearNodes": {
            "method": true
        },
        "controlsEnabled": {
            "state": true
        },
        "debug": {
            "type": Boolean,
            "attr": "debug"
        },
        "deleteNode": {
            "method": true
        },
        "displayMode": {
            "state": true
        },
        "dracoDecoderPath": {
            "type": String,
            "attr": "draco-decoder-path"
        },
        "edges": {
            "state": true
        },
        "height": {
            "type": String,
            "attr": "height"
        },
        "load": {
            "method": true
        },
        "nodes": {
            "state": true
        },
        "nodesEnabled": {
            "state": true
        },
        "nodesVisible": {
            "state": true
        },
        "optionsEnabled": {
            "state": true
        },
        "optionsVisible": {
            "state": true
        },
        "orientation": {
            "state": true
        },
        "resize": {
            "method": true
        },
        "selected": {
            "state": true
        },
        "selectNode": {
            "method": true
        },
        "setBoundingBoxVisible": {
            "method": true
        },
        "setDisplayMode": {
            "method": true
        },
        "setEdge": {
            "method": true
        },
        "setNode": {
            "method": true
        },
        "setNodes": {
            "method": true
        },
        "setNodesEnabled": {
            "method": true
        },
        "slicesIndex": {
            "state": true
        },
        "slicesWindowCenter": {
            "state": true
        },
        "slicesWindowWidth": {
            "state": true
        },
        "spinnerColor": {
            "type": String,
            "attr": "spinner-color"
        },
        "src": {
            "state": true
        },
        "srcLoaded": {
            "state": true
        },
        "store": {
            "context": "store"
        },
        "volumeSteps": {
            "state": true
        },
        "volumeWindowCenter": {
            "state": true
        },
        "volumeWindowWidth": {
            "state": true
        },
        "width": {
            "type": String,
            "attr": "width"
        }
    }; }
    static get events() { return [{
            "name": "onChanged",
            "method": "onChanged",
            "bubbles": true,
            "cancelable": true,
            "composed": true
        }]; }
    static get style() { return "#al-container{position:relative;font-family:sans-serif}#al-container a-scene{background-color:var(--app-background-color);display:block;width:100%;height:100%}#al-container #lut-container{display:none}#al-container .progress.container{position:absolute;top:auto!important;bottom:0!important}#al-container .progress.container .load.progress,#al-container .progress.container .parse.progress{border:none!important;height:4px!important;background-color:#fff!important}"; }
}

export { Aleph as UvAleph };
