text: import macro from 'vtk.js/Sources/macros';
  import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor';
  import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray';
  import vtkGlyph3DMapper from 'vtk.js/Sources/Rendering/Core/Glyph3DMapper';
  import vtkHandleRepresentation from 'vtk.js/Sources/Widgets/Representations/HandleRepresentation';
  import vtkPixelSpaceCallbackMapper from 'vtk.js/Sources/Rendering/Core/PixelSpaceCallbackMapper';
  import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData';
  import vtkSphereSource from 'vtk.js/Sources/Filters/Sources/SphereSource';
  
  import { ScalarMode } from 'vtk.js/Sources/Rendering/Core/Mapper/Constants';
  
  // ----------------------------------------------------------------------------
  // vtkSphereHandleRepresentation methods
  // ----------------------------------------------------------------------------
  
  function vtkSphereHandleRepresentation(publicAPI, model) {
    // Set our className
    model.classHierarchy.push('vtkSphereHandleRepresentation');
  
    // --------------------------------------------------------------------------
    // Internal polydata dataset
    // --------------------------------------------------------------------------
  
    model.internalPolyData = vtkPolyData.newInstance({ mtime: 0 });
    model.internalArrays = {
      points: model.internalPolyData.getPoints(),
      scale: vtkDataArray.newInstance({
        name: 'scale',
        numberOfComponents: 1,
        empty: true,
      }),
      color: vtkDataArray.newInstance({
        name: 'color',
        numberOfComponents: 1,
        empty: true,
      }),
    };
  
    model.internalPolyData.getPointData().addArray(model.internalArrays.scale);
    model.internalPolyData.getPointData().addArray(model.internalArrays.color);
  
    // --------------------------------------------------------------------------
    // Generic rendering pipeline
    // --------------------------------------------------------------------------
  
    /*
     * displayActors and displayMappers are used to render objects in HTML, allowing objects
     * to be 'rendered' internally in a VTK scene without being visible on the final output
     */
  
    model.displayMapper = vtkPixelSpaceCallbackMapper.newInstance();
    model.displayActor = vtkActor.newInstance({ parentProp: publicAPI });
    // model.displayActor.getProperty().setOpacity(0); // don't show in 3D
    model.displayActor.setMapper(model.displayMapper);
    model.displayMapper.setInputConnection(publicAPI.getOutputPort());
    publicAPI.addActor(model.displayActor);
    model.alwaysVisibleActors = [model.displayActor];
  
    model.mapper = vtkGlyph3DMapper.newInstance({
      scaleArray: 'scale',
      colorByArrayName: 'color',
      scalarMode: ScalarMode.USE_POINT_FIELD_DATA,
    });
    model.actor = vtkActor.newInstance({ parentProp: publicAPI });
    model.glyph = vtkSphereSource.newInstance({
      phiResolution: model.glyphResolution,
      thetaResolution: model.glyphResolution,
    });
  
    model.mapper.setInputConnection(publicAPI.getOutputPort(), 0);
    model.mapper.setInputConnection(model.glyph.getOutputPort(), 1);
    model.actor.setMapper(model.mapper);
  
    publicAPI.addActor(model.actor);
  
    // --------------------------------------------------------------------------
  
    publicAPI.setGlyphResolution = macro.chain(
      publicAPI.setGlyphResolution,
      (r) => model.glyph.setPhiResolution(r) && model.glyph.setThetaResolution(r)
    );
  
    // --------------------------------------------------------------------------
  
    function callbackProxy(coords) {
      if (model.displayCallback) {
        const filteredList = [];
        const states = publicAPI.getRepresentationStates();
        for (let i = 0; i < states.length; i++) {
          if (states[i].getActive()) {
            filteredList.push(coords[i]);
          }
        }
        if (filteredList.length) {
          model.displayCallback(filteredList);
          return;
        }
      }
      model.displayCallback();
    }
  
    publicAPI.setDisplayCallback = (callback) => {
      model.displayCallback = callback;
      model.displayMapper.setCallback(callback ? callbackProxy : null);
    };
  
    const superGetRepresentationStates = publicAPI.getRepresentationStates;
    publicAPI.getRepresentationStates = (input = model.inputData[0]) =>
      superGetRepresentationStates(input).filter(
        (state) => state.getOrigin?.() && state.isVisible?.()
      );
  
    // --------------------------------------------------------------------------
  
    publicAPI.requestData = (inData, outData) => {
      const { points, scale, color } = model.internalArrays;
      const list = publicAPI.getRepresentationStates(inData[0]);
      const totalCount = list.length;
  
      if (color.getNumberOfValues() !== totalCount) {
        // Need to resize dataset
        points.setData(new Float32Array(3 * totalCount), 3);
        scale.setData(new Float32Array(totalCount));
        color.setData(new Float32Array(totalCount));
      }
      const typedArray = {
        points: points.getData(),
        scale: scale.getData(),
        color: color.getData(),
      };
  
      for (let i = 0; i < totalCount; i++) {
        const state = list[i];
        const isActive = state.getActive();
        const scaleFactor = isActive ? model.activeScaleFactor : 1;
  
        const coord = state.getOrigin();
        typedArray.points[i * 3 + 0] = coord[0];
        typedArray.points[i * 3 + 1] = coord[1];
        typedArray.points[i * 3 + 2] = coord[2];
  
        typedArray.scale[i] =
          scaleFactor *
          (state.getScale1 ? state.getScale1() : model.defaultScale);
  
        if (publicAPI.getScaleInPixels()) {
          typedArray.scale[i] *= publicAPI.getPixelWorldHeightAtCoord(coord);
        }
  
        typedArray.color[i] =
          model.useActiveColor && isActive ? model.activeColor : state.getColor();
      }
  
      model.internalPolyData.modified();
      outData[0] = model.internalPolyData;
    };
  }
  
  // ----------------------------------------------------------------------------
  // Object factory
  // ----------------------------------------------------------------------------
  
  const DEFAULT_VALUES = {
    glyphResolution: 8,
    defaultScale: 1,
  };
  
  // ----------------------------------------------------------------------------
  
  export function extend(publicAPI, model, initialValues = {}) {
    Object.assign(model, DEFAULT_VALUES, initialValues);
  
    vtkHandleRepresentation.extend(publicAPI, model, initialValues);
    macro.get(publicAPI, model, ['glyph', 'mapper', 'actor']);
  
    // Object specific methods
    vtkSphereHandleRepresentation(publicAPI, model);
  }
  
  // ----------------------------------------------------------------------------
  
  export const newInstance = macro.newInstance(
    extend,
    'vtkSphereHandleRepresentation'
  );
  
  // ----------------------------------------------------------------------------
  
  export default { newInstance, extend };
  