private static List<Point3i> findAllControllerBlocks(SparseMatrix<Block> grid) {
   List<Point3i> blocks = new ArrayList<Point3i>();
   for (Iterator<Point3i> i = grid.iteratorNonNull(); i.hasNext(); ) {
     Point3i pp = i.next();
     Block b = grid.get(pp);
     if (BlockTypes.isController(b.getBlockID())) blocks.add(pp);
   }
   return blocks;
 }
 public static void dump(Logic logic, SparseMatrix<Block> grid) {
   System.out.println(
       "Logic, unknown="
           + logic.getUnknown1()
           + ", #controllers="
           + logic.getControllers().size());
   for (ControllerEntry controller : logic.getControllers()) {
     Block controllerBlock = grid.get(controller.getPosition());
     System.out.println(
         "  Controller at "
             + controller.getPosition()
             + " ("
             + BlockTypes.BLOCK_NAMES.get(controllerBlock.getBlockID())
             + "), #groups="
             + controller.getGroups().size());
     for (GroupEntry group : controller.getGroups()) {
       System.out.println(
           "    Group of "
               + group.getBlocks().size()
               + " "
               + BlockTypes.BLOCK_NAMES.get(group.getBlockID()));
       //                for (Point3s block : group.getBlocks())
       //                {
       //                    Block groupBlock = grid.get(block);
       //                    System.out.println("      "+block+"
       // ("+BlockTypes.BLOCK_NAMES.get(groupBlock.getBlockID())+")");
       //                }
     }
   }
 }
 @Override
 public SparseMatrix<Block> modify(
     SparseMatrix<Block> original, Object p, StarMade sm, IPluginCallback cb) {
   ReplaceBlocksParameters params;
   params = (ReplaceBlocksParameters) p;
   Point3i upper;
   upper = new Point3i();
   Point3i lower;
   lower = new Point3i();
   PluginUtils.getEffectiveSelection(sm, original, lower, upper);
   log.log(Level.INFO, "Params: color1=" + params.getColor1() + ", color2=" + params.getColor2());
   cb.setStatus("Replacing colors");
   cb.startTask(PluginUtils.getVolume(lower, upper));
   SparseMatrix<Block> modified;
   modified = new SparseMatrix<>(original);
   for (Iterator<Point3i> i = new CubeIterator(lower, upper); i.hasNext(); ) {
     cb.workTask(1);
     Point3i xyz;
     xyz = i.next();
     Block b;
     b = original.get(xyz);
     if (b == null) {
       continue;
     }
     if (b.getBlockID() == params.getColor1()) {
       if (params.getColor2() == 0) {
         b = null;
       } else {
         short oldOri;
         oldOri = b.getOrientation();
         b = new Block(params.getColor2());
         b.setOrientation(oldOri);
       }
     }
     modified.set(xyz, b);
   }
   cb.endTask();
   return modified;
 }
  public static Logic make(SparseMatrix<Block> grid) {
    Logic logic = new Logic();
    logic.setUnknown1(0);
    Point3i coreLocation = ShipLogic.findCore(grid);
    if (coreLocation == null) throw new IllegalArgumentException("No core!");
    ControllerEntry coreController = new ControllerEntry();
    coreController.setPosition(
        new Point3s((short) coreLocation.x, (short) coreLocation.y, (short) coreLocation.z));
    logic.getControllers().add(coreController);
    // controlled blocks
    List<Point3i> controllerBlocks = findAllControllerBlocks(grid);
    for (Point3i controllerBlockPosition : controllerBlocks) {
      Block controllerBlock = grid.get(controllerBlockPosition);
      GroupEntry controllerGroup = new GroupEntry();
      controllerGroup.setBlockID(controllerBlock.getBlockID());
      coreController.getGroups().add(controllerGroup);
      controllerGroup.getBlocks().add(new Point3s(controllerBlockPosition));

      ControllerEntry controlledEntry = new ControllerEntry();
      logic.getControllers().add(controlledEntry);
      controlledEntry.setPosition(new Point3s(controllerBlockPosition));
      GroupEntry controlledGroup = new GroupEntry();
      controlledEntry.getGroups().add(controlledGroup);
      short controlledBlockID = BlockTypes.CONTROLLER_IDS.get(controllerBlock.getBlockID());
      controlledGroup.setBlockID(controlledBlockID);
      List<Point3i> controlledBlocks = ShipLogic.findBlocks(grid, controlledBlockID);
      for (Point3i block : controlledBlocks) controlledGroup.getBlocks().add(new Point3s(block));
    }
    // uncontrolled blocks
    for (short controlledBlockID : new short[] {BlockTypes.THRUSTER_ID}) {
      List<Point3i> controlledBlocks = ShipLogic.findBlocks(grid, controlledBlockID);
      if (controlledBlocks.size() == 0) continue;
      GroupEntry controlledGroup = new GroupEntry();
      coreController.getGroups().add(controlledGroup);
      controlledGroup.setBlockID(controlledBlockID);
      for (Point3i p : controlledBlocks) controlledGroup.getBlocks().add(new Point3s(p));
    }
    return logic;
  }