package com.vitco.layout.content.menu;
/**
* Handles the select bar logic.
*/
import com.vitco.core.data.container.Voxel;
import com.vitco.core.data.notification.DataChangeAdapter;
import com.vitco.manager.action.types.StateActionPrototype;
import com.vitco.manager.pref.PrefChangeListener;
import com.vitco.settings.VitcoSettings;
import com.vitco.util.misc.ColorTools;
import gnu.trove.set.hash.THashSet;
import javax.annotation.PostConstruct;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
public class SelectBarLogic extends MenuLogicPrototype implements MenuLogicInterface {
private boolean isAnimate = VitcoSettings.INITIAL_MODE_IS_ANIMATION;
// status of selection moved
private boolean voxelsAreMoved = false;
// true iff there are selected voxels
private boolean voxelsAreSelected = false;
// true iff there are voxels in layer
private boolean voxelsAreInLayer = false;
public void registerLogic(Frame frame) {
// stores the current position (keeps current)
// (used to shift copy + paste correctly in side view)
final int[] currentPos = new int[3];
for (int i = 0; i < 3; i++) {
final int finalI = i;
preferences.addPrefChangeListener("currentplane_sideview" + (i + 1), new PrefChangeListener() {
@Override
public void onPrefChange(Object o) {
currentPos[finalI] = (Integer)o;
}
});
}
// cut, copy, paste
final ArrayList<Voxel> storedVoxels = new ArrayList<Voxel>();
final int[] storedPos = new int[3];
actionGroupManager.addAction("selection_interaction", "selection_tool_cut", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
// remember what we cut
storedVoxels.clear();
Voxel[] voxels = data.getSelectedVoxels();
Collections.addAll(storedVoxels, voxels);
// remember the current position
storedPos[0] = currentPos[0];
storedPos[1] = currentPos[1];
storedPos[2] = currentPos[2];
// fetch voxel ids for cut
Integer[] voxelIds = Voxel.convertVoxelsToIdArray(voxels);
// mass delete
data.massRemoveVoxel(voxelIds);
// refresh status
actionGroupManager.refreshGroup("selection_interaction");
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreSelected;
}
});
actionGroupManager.addAction("selection_interaction", "selection_tool_copy", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
storedVoxels.clear();
Voxel[] voxels = data.getSelectedVoxels();
Collections.addAll(storedVoxels, voxels);
// remember the current position
storedPos[0] = currentPos[0];
storedPos[1] = currentPos[1];
storedPos[2] = currentPos[2];
// refresh status
actionGroupManager.refreshGroup("selection_interaction");
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreSelected;
}
});
actionGroupManager.addAction("selection_interaction", "selection_tool_paste", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
if (storedVoxels.size() > 0) {
Voxel[] voxels = new Voxel[storedVoxels.size()];
int[] pos;
// compute the shift for all voxels
int[] shift = new int[] {
storedPos[0] - currentPos[0],
storedPos[1] - currentPos[1],
storedPos[2] - currentPos[2]
};
// apply the shift
int i = 0;
for (Voxel voxel : storedVoxels) {
pos = voxel.getPosAsInt();
pos[0] -= shift[2];
pos[1] -= shift[1];
pos[2] -= shift[0];
voxels[i++] = new Voxel(voxel.id, pos, voxel.getColor(),
voxel.isSelected(), voxel.getTexture(), voxel.getLayerId());
}
// execute the (shifted) add
if (!data.massAddVoxel(voxels)) {
console.addLine(langSelector.getString("min_max_voxel_error"));
}
}
}
}
@Override
public boolean getStatus() {
return !isAnimate && storedVoxels.size() > 0;
}
});
// deselect, delete
actionGroupManager.addAction("selection_interaction", "selection_tool_deselect", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
int[] shift = data.getVoxelSelectionShift();
if (shift[0] != 0 || shift[1] != 0 || shift[2] != 0) {
data.setVoxelSelectionShift(0,0,0);
} else {
// mass deselect
Integer[] voxelIds = Voxel.convertVoxelsToIdArray(data.getSelectedVoxels());
data.massSetVoxelSelected(voxelIds, false);
}
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreSelected;
}
});
actionGroupManager.addAction("selection_interaction", "selection_tool_delete", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
// mass delete
Integer[] voxelIds = Voxel.convertVoxelsToIdArray(data.getSelectedVoxels());
data.massRemoveVoxel(voxelIds);
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreSelected;
}
});
abstract class SelectLogicAction extends StateActionPrototype {
public abstract Integer[] getVoxelsToSelect();
@Override
public void action(ActionEvent actionEvent) {
// todo make this an intent (select layer) -> to prevent two history entries
// deselect voxels (this is necessary if there are voxel selected that are not in the current layer)
Integer[] selected = Voxel.convertVoxelsToIdArray(data.getSelectedVoxels());
Integer[] toSelect = getVoxelsToSelect();
if (selected.length > 0) {
HashSet<Integer> toDeselectList = new HashSet<Integer>(Arrays.asList(selected));
HashSet<Integer> toSelectList = new HashSet<Integer>(Arrays.asList(toSelect));
toSelectList.removeAll(toDeselectList);
toDeselectList.removeAll(Arrays.asList(toSelect));
if (!toDeselectList.isEmpty()) {
Integer[] toDeselectArray = new Integer[toDeselectList.size()];
toDeselectList.toArray(toDeselectArray);
data.massSetVoxelSelected(toDeselectArray, false);
}
toSelect = new Integer[toSelectList.size()];
toSelectList.toArray(toSelect);
}
// select voxels
if (toSelect.length != 0) {
data.massSetVoxelSelected(toSelect, true);
}
}
};
// select all, expand selection, move to new layer, recolor
actionGroupManager.addAction("selection_interaction", "selection_tool_select_all", new SelectLogicAction() {
@Override
public Integer[] getVoxelsToSelect() {
return Voxel.convertVoxelsToIdArray(data.getLayerVoxels(data.getSelectedLayer()));
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreInLayer;
}
});
actionGroupManager.addAction("selection_interaction", "selection_tool_select_all_layers_all", new SelectLogicAction() {
public Integer[] getVoxelsToSelect() {
return Voxel.convertVoxelsToIdArray(data.getVisibleLayerVoxel());
}
@Override
public boolean getStatus() {
return !isAnimate;
}
});
actionGroupManager.addAction("selection_interaction", "selection_tool_expand_selection", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
// extract colors from selected voxels
THashSet<Color> colors = new THashSet<Color>();
for (Voxel voxel : data.getSelectedVoxels()) {
colors.add(voxel.getColor());
}
// add the currently selected color if no color is present
if (colors.isEmpty()) {
Color color = ColorTools.hsbToColor((float[])preferences.loadObject("currently_used_color"));
colors.add(color);
}
// identify which voxels to select
ArrayList<Integer> toSelect = new ArrayList<Integer>();
for (Voxel voxel : data.getVisibleLayerVoxel()) {
if (colors.contains(voxel.getColor())) {
toSelect.add(voxel.id);
}
}
// select voxels
if (toSelect.size() != 0) {
Integer[] toSelectArray = new Integer[toSelect.size()];
toSelect.toArray(toSelectArray);
data.massSetVoxelSelected(toSelectArray, true);
}
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreSelected;
}
});
actionGroupManager.addAction("selection_interaction", "selection_tool_as_new_layer", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
if (!data.migrateVoxels(data.getSelectedVoxels())) {
console.addLine(langSelector.getString("min_max_voxel_error"));
}
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreSelected;
}
});
actionGroupManager.addAction("selection_interaction", "selection_tool_recolor", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
if (preferences.contains("currently_used_color")) {
Integer[] voxelIds = Voxel.convertVoxelsToIdArray(data.getSelectedVoxels());
Color color = ColorTools.hsbToColor((float[])preferences.loadObject("currently_used_color"));
data.massSetColor(voxelIds, color);
}
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreSelected;
}
});
actionGroupManager.addAction("selection_interaction", "selection_tool_retexture", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
Integer[] voxelIds = Voxel.convertVoxelsToIdArray(data.getSelectedVoxels());
data.massSetTexture(voxelIds, data.getSelectedTexture());
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreSelected;
}
});
// finalize shifting
actionGroupManager.addAction("selection_interaction", "selection_tool_finalize_shifting", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
// note: shifting will deselect voxels (so no need to do it here)
Voxel[] selectedVoxels = data.getSelectedVoxels();
int[] shift = data.getVoxelSelectionShift();
if (selectedVoxels.length > 0 && (shift[0] != 0 || shift[1] != 0 || shift[2] != 0)) {
data.massMoveVoxel(data.getSelectedVoxels(), shift);
}
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreMoved && voxelsAreSelected;
}
});
// finalize shifting as copy
actionGroupManager.addAction("selection_interaction", "selection_tool_finalize_shifting_as_copy", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
// note: shifting will deselect voxels (so no need to do it here)
Voxel[] selectedVoxels = data.getSelectedVoxels();
int[] shift = data.getVoxelSelectionShift();
if (selectedVoxels.length > 0 && (shift[0] != 0 || shift[1] != 0 || shift[2] != 0)) {
// make a copy of the voxels, but shifted
Integer[] voxelIds = new Integer[selectedVoxels.length];
Voxel[] shiftedVoxels = new Voxel[selectedVoxels.length];
for (int i = 0; i < selectedVoxels.length; i++) {
Voxel voxel = selectedVoxels[i];
voxelIds[i] = voxel.id;
shiftedVoxels[i] = new Voxel(-1, new int[] {
voxel.x - shift[0],
voxel.y - shift[1],
voxel.z - shift[2]
}, voxel.getColor(), voxel.isSelected(), voxel.getTexture(), voxel.getLayerId());
}
// note: the following order makes sense if we want to copy the selection again to another place
// add the shifted voxels
// execute the (shifted) add
if (!data.massAddVoxel(shiftedVoxels)) {
console.addLine(langSelector.getString("min_max_voxel_error"));
} else {
// deselect voxels
data.massSetVoxelSelected(voxelIds, false);
}
}
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreMoved && voxelsAreSelected;
}
});
// rotate (popup buttons) - only to disable
StateActionPrototype disableAction = new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
// nothing to do
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreSelected;
}
};
actionGroupManager.addAction("selection_interaction", "selection_tool_rotatex", disableAction);
actionGroupManager.addAction("selection_interaction", "selection_tool_rotatey", disableAction);
actionGroupManager.addAction("selection_interaction", "selection_tool_rotatez", disableAction);
// rotate buttons, define the actions
actionGroupManager.addAction("selection_interaction", "selection_tool_mirrorx90", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
data.rotateVoxelCenter(data.getSelectedVoxels(), 0, 90);
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreSelected;
}
});
actionGroupManager.addAction("selection_interaction", "selection_tool_mirrorx180", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
data.rotateVoxelCenter(data.getSelectedVoxels(), 0, 180);
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreSelected;
}
});
actionGroupManager.addAction("selection_interaction", "selection_tool_mirrorx270", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
data.rotateVoxelCenter(data.getSelectedVoxels(), 0, 270);
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreSelected;
}
});
actionGroupManager.addAction("selection_interaction", "selection_tool_mirrory90", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
data.rotateVoxelCenter(data.getSelectedVoxels(), 1, 90);
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreSelected;
}
});
actionGroupManager.addAction("selection_interaction", "selection_tool_mirrory180", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
data.rotateVoxelCenter(data.getSelectedVoxels(), 1, 180);
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreSelected;
}
});
actionGroupManager.addAction("selection_interaction", "selection_tool_mirrory270", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
data.rotateVoxelCenter(data.getSelectedVoxels(), 1, 270);
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreSelected;
}
});
actionGroupManager.addAction("selection_interaction", "selection_tool_mirrorz90", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
data.rotateVoxelCenter(data.getSelectedVoxels(), 2, 90);
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreSelected;
}
});
actionGroupManager.addAction("selection_interaction", "selection_tool_mirrorz180", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
data.rotateVoxelCenter(data.getSelectedVoxels(), 2, 180);
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreSelected;
}
});
actionGroupManager.addAction("selection_interaction", "selection_tool_mirrorz270", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
data.rotateVoxelCenter(data.getSelectedVoxels(), 2, 270);
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreSelected;
}
});
// mirror actions
actionGroupManager.addAction("selection_interaction", "selection_tool_mirrorx", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
data.mirrorVoxel(data.getSelectedVoxels(), 0);
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreSelected;
}
});
actionGroupManager.addAction("selection_interaction", "selection_tool_mirrory", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
data.mirrorVoxel(data.getSelectedVoxels(), 1);
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreSelected;
}
});
actionGroupManager.addAction("selection_interaction", "selection_tool_mirrorz", new StateActionPrototype() {
@Override
public void action(ActionEvent actionEvent) {
if (getStatus()) {
data.mirrorVoxel(data.getSelectedVoxels(), 2);
}
}
@Override
public boolean getStatus() {
return !isAnimate && voxelsAreSelected;
}
});
actionGroupManager.registerGroup("selection_interaction");
}
@PostConstruct
public final void init() {
// register change of animation mode
preferences.addPrefChangeListener("is_animation_mode_active", new PrefChangeListener() {
@Override
public void onPrefChange(Object newValue) {
isAnimate = (Boolean) newValue;
actionGroupManager.refreshGroup("selection_interaction");
}
});
// register data change listener
data.addDataChangeListener(new DataChangeAdapter() {
@Override
public void onVoxelSelectionShiftChanged() {
int[] shift = data.getVoxelSelectionShift();
boolean voxelsAreMovedTemp = shift[0] != 0 || shift[1] != 0 || shift[2] != 0;
if (voxelsAreMovedTemp != voxelsAreMoved) {
voxelsAreMoved = voxelsAreMovedTemp;
actionGroupManager.refreshGroup("selection_interaction");
}
}
@Override
public void onVoxelDataChanged() {
// todo: rewrite voxel data to make fetching of these feasible (!)
boolean voxelsAreSelectedTemp = true;
boolean voxelsAreInLayerTemp = true;
if (voxelsAreSelected != voxelsAreSelectedTemp || voxelsAreInLayer != voxelsAreInLayerTemp) {
voxelsAreSelected = voxelsAreSelectedTemp;
voxelsAreInLayer = voxelsAreInLayerTemp;
actionGroupManager.refreshGroup("selection_interaction");
}
}
});
}
}