diff --git a/ChannelShiftGUI.pde b/ChannelShiftGUI.pde index a74e132..bf0cb88 100644 --- a/ChannelShiftGUI.pde +++ b/ChannelShiftGUI.pde @@ -4,7 +4,7 @@ import g4p_controls.*; // Input File ------------------------------------------------------------------ // Default image to load on start -String defaultImgName = "test1"; +String defaultImgName = "test" + int(random(0,2)); String defaultImgPath = "demo/" + defaultImgName + ".jpg"; // Globals ===================================================================== @@ -53,19 +53,17 @@ void shiftChannel(PImage sourceImg, PImage targetImg, int xShift, int yShift, in for (int x = 0; x < targetImg.width; x++) { int yOffset = mod(shiftTypeManager.calculateShiftOffset(x, y, targetImg.width, targetImg.height, yShift, false), targetImg.height); int xOffset = mod(shiftTypeManager.calculateShiftOffset(x, y, targetImg.width, targetImg.height, xShift, true), targetImg.width); - // Get source pixel and its RGB vals int sourceIndex = yOffset * sourceImg.width + xOffset; color sourcePixel = sourcePixels[sourceIndex]; float[] sourceRGB = new float[]{ red(sourcePixel), green(sourcePixel), blue(sourcePixel) }; - // Get target pixel and its RGB vals int targetIndex = y * targetImg.width + x; color targetPixel = targetPixels[targetIndex]; float[] targetRGB = new float[]{ red(targetPixel), green(targetPixel), blue(targetPixel) }; - // Swap source channel w/ target channel targetRGB[targetChannel] = sourceRGB[sourceChannel]; + // TODO !!! targetRGB[sourceChannel] = sourceRGB[targetChannel] ??? targetPixels[targetIndex] = color(targetRGB[0], targetRGB[1], targetRGB[2]); } } diff --git a/LICENSE b/LICENSE index cf10db9..173d01d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 Connor de la Cruz +Copyright (c) 2021 Connor de la Cruz Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index dd7e17a..97b601c 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,9 @@ manipulating RGB color channels. * [XY Multiply](#xy-multiply) * [Options](#options-3) * [Shift Calculation](#shift-calculation-3) + * [Noise](#noise) + * [Options](#options-4) + * [Shift Calculation](#shift-calculation-4) @@ -368,3 +371,39 @@ Where: corresponding dimension +### Noise + +![Noise shift type](../assets/samples/noise.png?raw=true) + +Apply [Perlin noise](https://en.wikipedia.org/wiki/Perlin_noise) to the shift amount. + +#### Options + +- **X Start:** Starting value for x noise +- **Y Start:** Starting value for y noise +- **X Step:** Amount to increment x by each time `noise()` is called. Use a smaller number for smoother results +- **Y Step:** Amount to increment y by each time `noise()` is called. Use a smaller number for smoother results +- **Noise Multiplier:** Value to multiply result of `noise()` by. Higher values create more drastic effects + +#### Shift Calculation + +**Horizontal Offset:** + +``` +x + shift + (int)(noiseMultiplier * noise(xNoise, yNoise)) +``` + +**Vertical Offset:** + + +``` +y + shift + (int)(noiseMultiplier * noise(xNoise, yNoise)) +``` + +Where: + +- `x` and `y`: the coordinates of the pixel +- `shift`: the horizontal/vertical shift amount +- `noiseMultiplier`: the noise multiplier value +- `xNoise` and `yNoise`: noise coordinates, calculated by adding offset to start value each time the corresponding coordinate (`x` or `y`) is incremented + diff --git a/advanced.pde b/advanced.pde index 1d3fb7f..68b72b8 100644 --- a/advanced.pde +++ b/advanced.pde @@ -2,25 +2,26 @@ // Globals, logic, and event handlers related to advanced shift type options // ============================================================================= -// Constants =================================================================== - +// GLOBALS ===================================================================== // Names of different shift types -String[] SHIFT_TYPES = new String[]{"Default", "Scale", "Linear", "Skew", "XY Multiply"}; +String[] SHIFT_TYPES = new String[]{"Default", "Scale", "Linear", "Skew", "XY Multiply", "Noise"}; // Indexes int TYPE_DEFAULT = 0; int TYPE_SCALE = 1; int TYPE_LINEAR = 2; int TYPE_SKEW = 3; int TYPE_XYMULT = 4; +int TYPE_NOISE = 5; // Total # of shift types int TOTAL_SHIFT_TYPES = SHIFT_TYPES.length; -// Manager/State Classes ======================================================= +// SHIFT TYPES ================================================================= // Shift Type Interface -------------------------------------------------------- public interface ShiftTypeState { + // TODO add public String typeName // Calculate offset for this shift type public int calculateShiftOffset(int x, int y, int width, int height, int shift, boolean horizontal); // String representation of this step @@ -45,7 +46,6 @@ public class DefaultShiftType implements ShiftTypeState { public class ScaleShiftType implements ShiftTypeState { // Multiplier values specific to this shift type public float xMultiplier, yMultiplier; - // TODO negative multipliers? public ScaleShiftType(float xMult, float yMult) { xMultiplier = xMult; @@ -213,7 +213,6 @@ public class XYMultShiftType implements ShiftTypeState { this(true, true, false, true); } - // TODO flip divisor? (w/o dividing by 0) public int calculateShiftOffset(int x, int y, int width, int height, int shift, boolean horizontal) { if (horizontal) return x + shift + (multX ? (int)(xSign*x*y / height) : 0); @@ -243,7 +242,52 @@ public class XYMultShiftType implements ShiftTypeState { public boolean isPositiveY() { return ySign > 0.0; } } -// Manager --------------------------------------------------------------------- +// Noise ----------------------------------------------------------------------- + +public class NoiseShiftType implements ShiftTypeState { + public float xNoiseStart, yNoiseStart; + public float xNoiseIncrement, yNoiseIncrement; + public float noiseMultiplier; + + // TODO: noiseSeed?? + public NoiseShiftType(float xNoiseStart, float yNoiseStart, float xNoiseIncrement, float yNoiseIncrement, float noiseMultiplier) { + this.xNoiseStart = xNoiseStart; + this.yNoiseStart = yNoiseStart; + this.xNoiseIncrement = xNoiseIncrement; + this.yNoiseIncrement = yNoiseIncrement; + this.noiseMultiplier = noiseMultiplier; + } + + public NoiseShiftType() { + this(0.01, 0.01, 0.01, 0.01, 20.0); + } + + public int calculateShiftOffset(int x, int y, int width, int height, int shift, boolean horizontal) { + float xNoise = xNoiseStart + (xNoiseIncrement * x); + float yNoise = yNoiseStart + (yNoiseIncrement * y); + return (horizontal ? x : y) + shift + (int)(noiseMultiplier * noise(xNoise, yNoise)); + } + + public String stringifyStep() { + String step = "-noise-x" + xNoiseStart + "+" + xNoiseIncrement + "-y" + yNoiseStart + "+" + yNoiseIncrement + "mult" + noiseMultiplier; + return step; + } + + // Setters + public void setXNoiseStart(float val) { xNoiseStart = val; } + public void setYNoiseStart(float val) { yNoiseStart = val; } + public void setXNoiseIncrement(float val) { xNoiseIncrement = val; } + public void setYNoiseIncrement(float val) { yNoiseIncrement = val; } + public void setNoiseMultiplier(float val) { noiseMultiplier = val; } + // Getters + public float getXNoiseStart() { return xNoiseStart; } + public float getYNoiseStart() { return yNoiseStart; } + public float getXNoiseIncrement() { return xNoiseIncrement; } + public float getYNoiseIncrement() { return yNoiseIncrement; } + public float getNoiseMultiplier() { return noiseMultiplier; } +} + +// Manager ===================================================================== public class ShiftTypeManager { // Array of state objects @@ -259,6 +303,7 @@ public class ShiftTypeManager { shiftTypes[TYPE_LINEAR] = new LinearShiftType(); shiftTypes[TYPE_SKEW] = new SkewShiftType(); shiftTypes[TYPE_XYMULT] = new XYMultShiftType(); + shiftTypes[TYPE_NOISE] = new NoiseShiftType(); // Start w/ default state = TYPE_DEFAULT; } @@ -346,154 +391,37 @@ public class ShiftTypeManager { return ((XYMultShiftType)shiftTypes[TYPE_XYMULT]).isPositiveY(); } -} - - -// Event Handlers ============================================================== - -// Shift Type ------------------------------------------------------------------ - -public void shiftTypeSelect_change(GDropList source, GEvent event) { - // Hide previously selected panel - hideShiftTypePanel(shiftTypeConfigPanels[shiftTypeManager.state]); - shiftTypeManager.setShiftType(source.getSelectedIndex()); - // Show newly selected panel - showShiftTypePanel(shiftTypeConfigPanels[shiftTypeManager.state]); - showPreview(); -} - -// Scale Configs --------------------------------------------------------------- - -void multiplierInputEventHandler(GTextField source, GEvent event, boolean horizontal) { - switch(event) { - case ENTERED: - // Unfocus on enter, then do same actions as LOST_FOCUS case - source.setFocus(false); - case LOST_FOCUS: - // Sanitize and update manager - float val = sanitizeFloatInputValue(source); - if (val > -1.0) { - shiftTypeManager.scale_setMultiplier(val, horizontal); - showPreview(); - } - // Update input text to match sanitized input - // Also reverts input text in the event that it was not a valid numeric - // value after parsing - source.setText("" + shiftTypeManager.scale_getMultiplier(horizontal)); - break; - default: - break; + // Noise + public void noise_setXNoiseStart(float val) { + ((NoiseShiftType)shiftTypes[TYPE_NOISE]).setXNoiseStart(val); } -} - -public void xMultiplierInput_change(GTextField source, GEvent event) { - multiplierInputEventHandler(source, event, true); -} - -public void yMultiplierInput_change(GTextField source, GEvent event) { - multiplierInputEventHandler(source, event, false); -} - -// Linear Configs -------------------------------------------------------------- - -public void linearYEquals_clicked(GOption source, GEvent event) { - shiftTypeManager.linear_setEquationType(true); - showPreview(); -} - -public void linearXEquals_clicked(GOption source, GEvent event) { - shiftTypeManager.linear_setEquationType(false); - showPreview(); -} - -public void linearCoeffInput_change(GTextField source, GEvent event) { - switch(event) { - case ENTERED: - // Unfocus on enter, then do same actions as LOST_FOCUS case - source.setFocus(false); - case LOST_FOCUS: - // Sanitize and update manager - float val = sanitizeFloatInputValue(source); - if (val > -1.0) { - shiftTypeManager.linear_setCoefficient(val); - showPreview(); - } - // Update input text to match sanitized input - // Also reverts input text in the event that it was not a valid numeric - // value after parsing - source.setText("" + shiftTypeManager.linear_getCoefficient()); - break; - default: - break; + public float noise_xNoiseStart() { + return ((NoiseShiftType)shiftTypes[TYPE_NOISE]).getXNoiseStart(); } -} - -public void linearNegativeCoeffCheckbox_click(GCheckbox source, GEvent event) { - shiftTypeManager.linear_setCoefficientSign(!source.isSelected()); - showPreview(); -} - -// Skew Configs ---------------------------------------------------------------- - -void skewInputEventHandler(GTextField source, GEvent event, boolean horizontal) { - switch(event) { - case ENTERED: - // Unfocus on enter, then do same actions as LOST_FOCUS case - source.setFocus(false); - case LOST_FOCUS: - // Sanitize and update manager - float val = sanitizeFloatInputValue(source); - if (val > -1.0) { - shiftTypeManager.skew_setSkew(val, horizontal); - showPreview(); - } - // Update input text to match sanitized input - // Also reverts input text in the event that it was not a valid numeric - // value after parsing - source.setText("" + shiftTypeManager.skew_getSkew(horizontal)); - break; - default: - break; + public void noise_setYNoiseStart(float val) { + ((NoiseShiftType)shiftTypes[TYPE_NOISE]).setYNoiseStart(val); + } + public float noise_yNoiseStart() { + return ((NoiseShiftType)shiftTypes[TYPE_NOISE]).getYNoiseStart(); + } + public void noise_setXNoiseIncrement(float val) { + ((NoiseShiftType)shiftTypes[TYPE_NOISE]).setXNoiseIncrement(val); + } + public float noise_xNoiseIncrement() { + return ((NoiseShiftType)shiftTypes[TYPE_NOISE]).getXNoiseIncrement(); + } + public void noise_setYNoiseIncrement(float val) { + ((NoiseShiftType)shiftTypes[TYPE_NOISE]).setYNoiseIncrement(val); + } + public float noise_yNoiseIncrement() { + return ((NoiseShiftType)shiftTypes[TYPE_NOISE]).getYNoiseIncrement(); + } + public void noise_setNoiseMultiplier(float val) { + ((NoiseShiftType)shiftTypes[TYPE_NOISE]).setNoiseMultiplier(val); + } + public float noise_noiseMultiplier() { + return ((NoiseShiftType)shiftTypes[TYPE_NOISE]).getNoiseMultiplier(); } -} - -public void xSkewInput_change(GTextField source, GEvent event) { - skewInputEventHandler(source, event, true); -} - -public void xSkewNegativeCheckbox_click(GCheckbox source, GEvent event) { - shiftTypeManager.skew_setSign(!source.isSelected(), true); - showPreview(); -} - -public void ySkewInput_change(GTextField source, GEvent event) { - skewInputEventHandler(source, event, false); -} - -public void ySkewNegativeCheckbox_click(GCheckbox source, GEvent event) { - shiftTypeManager.skew_setSign(!source.isSelected(), false); - showPreview(); -} - -// X*Y Configs ----------------------------------------------------------------- - -public void multXCheckbox_click(GCheckbox source, GEvent event) { - shiftTypeManager.xymult_setMultX(source.isSelected()); - showPreview(); -} - -public void multXNegativeCheckbox_click(GCheckbox source, GEvent event) { - shiftTypeManager.xymult_setXSign(!source.isSelected()); - showPreview(); -} - -public void multYCheckbox_click(GCheckbox source, GEvent event) { - shiftTypeManager.xymult_setMultY(source.isSelected()); - showPreview(); -} -public void multYNegativeCheckbox_click(GCheckbox source, GEvent event) { - shiftTypeManager.xymult_setYSign(!source.isSelected()); - showPreview(); } diff --git a/code/G4P.jar b/code/G4P.jar index bedf4a3..c36d49d 100644 Binary files a/code/G4P.jar and b/code/G4P.jar differ diff --git a/controlsWindow.pde b/controlsWindow.pde index 5c73fe3..f34bf39 100644 --- a/controlsWindow.pde +++ b/controlsWindow.pde @@ -1,7 +1,10 @@ -// G4P Variable Declarations =================================================== +// ============================================================================= +// G4P Variable Declarations +// ============================================================================= -// Window ---------------------------------------------------------------------- +// WINDOW ====================================================================== GWindow controlsWindow; +// CHANNELS ==================================================================== // Source Toggle --------------------------------------------------------------- GPanel srcChannelPanel; GToggleGroup srcChannelToggle; @@ -14,7 +17,7 @@ GToggleGroup targChannelToggle; ChannelOption targR, targG, targB; // Keep track of toggles in global w/ index corresponding to channel ChannelOption[] targToggles; -// Randomize Button/Toggles ---------------------------------------------------- +// RANDOMIZE =================================================================== // TODO checkbox for swapping channels GPanel randomizePanel, randomizeCheckboxPanel; GButton randomizeBtn; @@ -22,39 +25,52 @@ GCheckbox randSrcCheckbox, randTargCheckbox, randXShiftCheckbox, randYShiftCheckbox; GTextField randXMaxInput, randYMaxInput; GLabel randXMaxLabel, randYMaxLabel; -// Advanced Options ------------------------------------------------------------ +GTabManager randMaxTabManager; +// ADVANCED OPTIONS ============================================================ // TODO randomize/reset buttons? add a randomize method to interface and call it on current state GPanel advancedOptionsPanel; // Type Select GDropList shiftTypeSelect; GLabel shiftTypeLabel; // Per-Type configs -GPanel defaultShiftTypePanel, scaleShiftTypePanel, linearShiftTypePanel, skewShiftTypePanel, xyMultShiftTypePanel; -// Default (just a label) +GPanel defaultShiftTypePanel, scaleShiftTypePanel, linearShiftTypePanel, skewShiftTypePanel, xyMultShiftTypePanel, noiseShiftTypePanel; +// Keep track of shift type config panels w/ indices matching globals +GPanel[] shiftTypeConfigPanels; +// Default (just a label) ------------------------------------------------------ GLabel defaultShiftConfigLabel; -// Multiply +// Multiply -------------------------------------------------------------------- // TODO RENAME TO SCALE GLabel xMultiplierLabel, yMultiplierLabel; GTextField xMultiplierInput, yMultiplierInput; GTabManager multiplierTabManager; -// Linear +// Linear ---------------------------------------------------------------------- GLabel linearCoeffLabel; GTextField linearCoeffInput; GToggleGroup linearEqTypeToggle; GOption linearYEquals, linearXEquals; GCheckbox linearNegativeCoeffCheckbox; -// Skew +// Skew ------------------------------------------------------------------------ GLabel xSkewLabel, ySkewLabel; GTextField xSkewInput, ySkewInput; GTabManager skewTabManager; GCheckbox xSkewNegativeCheckbox, ySkewNegativeCheckbox; -// X*Y +// X*Y ------------------------------------------------------------------------- GLabel multXLabel, multYLabel; GCheckbox multXCheckbox, multYCheckbox; GCheckbox multXNegativeCheckbox, multYNegativeCheckbox; - -// Keep track of shift type config panels w/ indices matching globals -GPanel[] shiftTypeConfigPanels; +// Noise ----------------------------------------------------------------------- +// Start +GLabel xNoiseStartLabel, yNoiseStartLabel; +GTextField xNoiseStartInput, yNoiseStartInput; +// Increment +GLabel xNoiseIncrementLabel, yNoiseIncrementLabel; +GTextField xNoiseIncrementInput, yNoiseIncrementInput; +// Multiplier +GLabel noiseMultiplierLabel; +GTextField noiseMultiplierInput; +// Tab MGMT +GTabManager noiseTabManager; +// SHIFT ======================================================================= // X Slider -------------------------------------------------------------------- GPanel xShiftPanel; GSlider xSlider; @@ -67,6 +83,7 @@ GSlider ySlider; GToggleGroup ySliderToggle; GOption ySliderPercent, ySliderPixels; GTextField ySliderInput; +// BOTTOM ====================================================================== // Reset/Confirm Buttons ------------------------------------------------------- GPanel resetConfirmPanel; GButton resetBtn, confirmBtn; @@ -77,8 +94,11 @@ GButton loadBtn; GButton saveBtn; -// Globals ===================================================================== +// ============================================================================= +// Globals +// ============================================================================= +// COMMON ====================================================================== // Positioning ----------------------------------------------------------------- // Start positioning at 10px to add some padding int X_START = 10; @@ -92,9 +112,10 @@ int X_MARGINS = 2 * X_MARGIN; // Panels ---------------------------------------------------------------------- // Panel labels are ~20, add this so children don't overlap int PANEL_Y_START = 20; -// Window ---------------------------------------------------------------------- -int WINDOW_X = 10; -int WINDOW_Y = 10; +// WINDOW ====================================================================== +// x/y to be set when creating window +int WINDOW_X = 0; +int WINDOW_Y = 0; int WINDOW_HEIGHT = 475; // The main sketch section int WINDOW_MAIN_WIDTH = 650; @@ -102,45 +123,47 @@ int WINDOW_MAIN_WIDTH = 650; int WINDOW_ADV_WIDTH = 200; // Total width int WINDOW_WIDTH = WINDOW_MAIN_WIDTH + WINDOW_ADV_WIDTH; -// Toggles --------------------------------------------------------------------- -// General +// CHANNELS ==================================================================== +// TODO make parent panel +// General --------------------------------------------------------------------- int CHANNEL_TOGGLE_WIDTH = 150; int CHANNEL_TOGGLE_HEIGHT = 25; int CHANNEL_PANEL_HEIGHT = 3 * CHANNEL_TOGGLE_HEIGHT + PANEL_Y_START; int R_CHANNEL_Y = PANEL_Y_START; int G_CHANNEL_Y = PANEL_Y_START + CHANNEL_TOGGLE_HEIGHT; int B_CHANNEL_Y = PANEL_Y_START + 2 * CHANNEL_TOGGLE_HEIGHT; -// Source +// Source ---------------------------------------------------------------------- int SRC_CHANNEL_X = X_START; int SRC_CHANNEL_Y = Y_START; -// Target +// Target ---------------------------------------------------------------------- int TARG_CHANNEL_X = SRC_CHANNEL_X + CHANNEL_TOGGLE_WIDTH + X_MARGINS; int TARG_CHANNEL_Y = Y_START; -// Randomize Button/Toggles ---------------------------------------------------- +// RANDOMIZE =================================================================== +// Panel ----------------------------------------------------------------------- // End of target channel panel + margins int RAND_PANEL_X = TARG_CHANNEL_X + CHANNEL_TOGGLE_WIDTH + X_MARGINS; int RAND_PANEL_Y = Y_START; // Fill rest of window (minus right margin) int RAND_PANEL_WIDTH = WINDOW_MAIN_WIDTH - RAND_PANEL_X - X_MARGIN; -// Randomize Checkboxes + Panel -int RAND_CHECKBOX_PANEL_X = 0; +// Randomize Checkboxes + Panel ------------------------------------------------ +int RAND_CHECKBOX_PANEL_X = X_MARGIN; int RAND_CHECKBOX_PANEL_Y = PANEL_Y_START; -int RAND_CHECKBOX_PANEL_WIDTH = RAND_PANEL_WIDTH; +int RAND_CHECKBOX_PANEL_WIDTH = RAND_PANEL_WIDTH - X_MARGINS; // Half of panel width, 2 checkboxes per row -int RAND_CHECKBOX_WIDTH = RAND_CHECKBOX_PANEL_WIDTH / 2; +int RAND_CHECKBOX_WIDTH = (RAND_CHECKBOX_PANEL_WIDTH / 2) - X_MARGINS; int RAND_CHECKBOX_HEIGHT = 30; // Checkbox positioning int RAND_CHECKBOX_LEFT_X = 0; -int RAND_CHECKBOX_RIGHT_X = RAND_CHECKBOX_WIDTH; +int RAND_CHECKBOX_RIGHT_X = RAND_CHECKBOX_LEFT_X + RAND_CHECKBOX_WIDTH + X_MARGINS; int RAND_CHECKBOX_TOP_Y = 0; int RAND_CHECKBOX_BOTTOM_Y = RAND_CHECKBOX_HEIGHT; int RAND_CHECKBOX_PANEL_HEIGHT = 2 * RAND_CHECKBOX_HEIGHT; -// Max Shift Inputs +// Max Shift Inputs ------------------------------------------------------------ // Labels are full width for text alignment -int RAND_MAX_LABEL_WIDTH = RAND_CHECKBOX_WIDTH; +int RAND_MAX_LABEL_WIDTH = RAND_PANEL_WIDTH / 2; int RAND_MAX_LABEL_HEIGHT = 20; // Inputs have margins (so they don't overlap) -int RAND_MAX_INPUT_WIDTH = RAND_CHECKBOX_WIDTH - X_MARGINS; +int RAND_MAX_INPUT_WIDTH = RAND_MAX_LABEL_WIDTH - X_MARGINS; int RAND_MAX_INPUT_HEIGHT = 20; int RAND_MAX_TOTAL_HEIGHT = RAND_MAX_LABEL_HEIGHT + RAND_MAX_INPUT_HEIGHT + Y_MARGIN; int RAND_MAX_LABEL_Y = RAND_CHECKBOX_PANEL_Y + RAND_CHECKBOX_PANEL_HEIGHT; @@ -150,35 +173,37 @@ int RAND_MAX_LABEL_LEFT_X = 0; int RAND_MAX_INPUT_LEFT_X = X_MARGIN; int RAND_MAX_LABEL_RIGHT_X = RAND_MAX_LABEL_WIDTH; int RAND_MAX_INPUT_RIGHT_X = RAND_MAX_INPUT_LEFT_X + RAND_MAX_INPUT_WIDTH + X_MARGINS; -// Randomize Button +// Randomize Button ------------------------------------------------------------ int RAND_BTN_WIDTH = RAND_PANEL_WIDTH; int RAND_BTN_HEIGHT = 30; int RAND_BTN_X = 0; int RAND_BTN_Y = RAND_MAX_LABEL_Y + RAND_MAX_TOTAL_HEIGHT; int RAND_PANEL_HEIGHT = RAND_CHECKBOX_PANEL_HEIGHT + RAND_MAX_TOTAL_HEIGHT + RAND_BTN_HEIGHT + PANEL_Y_START; -// Shift Type Select ----------------------------------------------------------- -// Panel +// SHIFT TYPE SELECT =========================================================== +// Panel ----------------------------------------------------------------------- int ADV_OPTS_PANEL_X = WINDOW_MAIN_WIDTH + X_MARGIN; int ADV_OPTS_PANEL_Y = Y_START; int ADV_OPTS_PANEL_WIDTH = WINDOW_ADV_WIDTH - X_MARGINS; int ADV_OPTS_PANEL_HEIGHT = WINDOW_HEIGHT - (2 * Y_MARGIN); -// Label +// Label ----------------------------------------------------------------------- int TYPE_LABEL_X = X_MARGIN; int TYPE_LABEL_Y = PANEL_Y_START; int TYPE_LABEL_WIDTH = ADV_OPTS_PANEL_WIDTH - X_MARGINS; int TYPE_LABEL_HEIGHT = 20; -// Dropdown +// Dropdown -------------------------------------------------------------------- int TYPE_SELECT_X = X_MARGIN; int TYPE_SELECT_Y = TYPE_LABEL_Y + TYPE_LABEL_HEIGHT; int TYPE_SELECT_WIDTH = ADV_OPTS_PANEL_WIDTH - X_MARGINS; int TYPE_SELECT_HEIGHT = 100; int TYPE_SELECT_MAX_ITEMS = 4; int TYPE_SELECT_BTN_WIDTH = TYPE_SELECT_WIDTH / 4; -// Common shift type configs +// ADVANCED ==================================================================== +// Panel ----------------------------------------------------------------------- int TYPE_PANEL_X = 0; int TYPE_PANEL_Y = TYPE_SELECT_Y + TYPE_SELECT_HEIGHT + Y_MARGIN; int TYPE_PANEL_WIDTH = ADV_OPTS_PANEL_WIDTH; int TYPE_PANEL_HEIGHT = ADV_OPTS_PANEL_HEIGHT - TYPE_PANEL_Y; +// SHIFT TYPES ================================================================= // Default Shift Type Panel ---------------------------------------------------- int DEFAULT_CONFIG_LABEL_X = X_MARGIN; int DEFAULT_CONFIG_LABEL_Y = PANEL_Y_START; @@ -248,8 +273,30 @@ int XYMULT_CONFIG_XNEGATIVE_CHECKBOX_Y = XYMULT_CONFIG_XMULT_CHECKBOX_Y + XYMULT int XYMULT_CONFIG_Y_LABEL_Y = XYMULT_CONFIG_XNEGATIVE_CHECKBOX_Y + XYMULT_CONFIG_CHECKBOX_HEIGHT + Y_MARGIN; int XYMULT_CONFIG_YMULT_CHECKBOX_Y = XYMULT_CONFIG_Y_LABEL_Y + XYMULT_CONFIG_LABEL_HEIGHT; int XYMULT_CONFIG_YNEGATIVE_CHECKBOX_Y = XYMULT_CONFIG_YMULT_CHECKBOX_Y + XYMULT_CONFIG_CHECKBOX_HEIGHT; -// Sliders --------------------------------------------------------------------- -// General +// Noise ----------------------------------------------------------------------- +// Common Label/Input Values +int NOISE_CONFIG_LABEL_FULL_WIDTH = TYPE_PANEL_WIDTH - X_MARGINS; +int NOISE_CONFIG_LABEL_HALF_WIDTH = (TYPE_PANEL_WIDTH / 2) - X_MARGINS; +int NOISE_CONFIG_LABEL_HEIGHT = 20; +int NOISE_CONFIG_INPUT_FULL_WIDTH = NOISE_CONFIG_LABEL_FULL_WIDTH; +int NOISE_CONFIG_INPUT_HALF_WIDTH = NOISE_CONFIG_LABEL_HALF_WIDTH; +int NOISE_CONFIG_INPUT_HEIGHT = NOISE_CONFIG_LABEL_HEIGHT; +int NOISE_CONFIG_LABEL_LEFT_X = X_MARGIN; +int NOISE_CONFIG_LABEL_RIGHT_X = NOISE_CONFIG_LABEL_LEFT_X + NOISE_CONFIG_LABEL_HALF_WIDTH + X_MARGINS; +int NOISE_CONFIG_INPUT_LEFT_X = X_MARGIN; +int NOISE_CONFIG_INPUT_RIGHT_X = NOISE_CONFIG_INPUT_LEFT_X + NOISE_CONFIG_INPUT_HALF_WIDTH + X_MARGINS; +// Noise Start Y +int NOISE_CONFIG_LABEL_START_Y = PANEL_Y_START; +int NOISE_CONFIG_INPUT_START_Y = NOISE_CONFIG_LABEL_START_Y + NOISE_CONFIG_LABEL_HEIGHT; +// Noise Increment Y +int NOISE_CONFIG_LABEL_INCREMENT_Y = NOISE_CONFIG_INPUT_START_Y + NOISE_CONFIG_LABEL_HEIGHT + Y_MARGIN; +int NOISE_CONFIG_INPUT_INCREMENT_Y = NOISE_CONFIG_LABEL_INCREMENT_Y + NOISE_CONFIG_LABEL_HEIGHT; +// Noise Multiplier Y +int NOISE_CONFIG_LABEL_MULTIPLIER_Y = NOISE_CONFIG_INPUT_INCREMENT_Y + NOISE_CONFIG_LABEL_HEIGHT + Y_MARGIN; +int NOISE_CONFIG_INPUT_MULTIPLIER_Y = NOISE_CONFIG_LABEL_MULTIPLIER_Y + NOISE_CONFIG_LABEL_HEIGHT; + +// SHIFT SLIDERS =============================================================== +// General --------------------------------------------------------------------- int SLIDER_TOGGLE_WIDTH = 75; int SLIDER_INPUT_WIDTH = 75; int SLIDER_INPUT_HEIGHT = 20; @@ -266,13 +313,13 @@ int SLIDER_Y = PANEL_Y_START; int SLIDER_TOGGLE_X = SLIDER_X + SLIDER_WIDTH; int SLIDER_PERCENT_TOGGLE_Y = PANEL_Y_START; int SLIDER_PIXELS_TOGGLE_Y = SLIDER_PERCENT_TOGGLE_Y + SLIDER_TOGGLE_HEIGHT; -// Horizontal Shift +// Horizontal Shift ------------------------------------------------------------ int X_SLIDER_PANEL_X = X_START; int X_SLIDER_PANEL_Y = RAND_PANEL_Y + RAND_PANEL_HEIGHT + Y_MARGIN; -// Vertical Shift +// Vertical Shift -------------------------------------------------------------- int Y_SLIDER_PANEL_X = X_START; int Y_SLIDER_PANEL_Y = X_SLIDER_PANEL_Y + SLIDER_PANEL_HEIGHT + Y_MARGIN; -// Load/Save Buttons ----------------------------------------------------------- +// LOAD/SAVE BUTTONS =========================================================== int LOAD_SAVE_PANEL_X = X_START; int LOAD_SAVE_PANEL_Y = Y_SLIDER_PANEL_Y + SLIDER_PANEL_HEIGHT + Y_MARGIN; int LOAD_SAVE_PANEL_WIDTH = WINDOW_MAIN_WIDTH / 2 - X_MARGINS; @@ -282,7 +329,7 @@ int LOAD_BTN_X = 0; int LOAD_BTN_Y = 0; int SAVE_BTN_X = 0; int SAVE_BTN_Y = LOAD_BTN_Y + LOAD_SAVE_BTN_HEIGHT + Y_MARGIN; -// Reset/Confirm Buttons ------------------------------------------------------- +// RESET/CONFIRM BUTTONS ======================================================= int RESET_CONFIRM_PANEL_X = LOAD_SAVE_PANEL_X + LOAD_SAVE_PANEL_WIDTH + X_MARGINS; int RESET_CONFIRM_PANEL_Y = LOAD_SAVE_PANEL_Y; int RESET_CONFIRM_PANEL_WIDTH = LOAD_SAVE_PANEL_WIDTH; @@ -297,7 +344,45 @@ int RECURSIVE_CHECKBOX_X = 0; int RECURSIVE_CHECKBOX_Y = CONFIRM_BTN_Y + RESET_CONFIRM_BTN_HEIGHT; -// Initialization ============================================================== +// ============================================================================= +// COMMON SETUP HELPERS +// ============================================================================= + +// General Configs ------------------------------------------------------------- + +// Common panel formatting +public void setupGeneralPanel(GPanel panel) { + panel.setTextBold(); + panel.setCollapsible(false); + panel.setDraggable(false); +} +// Common panel formatting w/ colorscheme +public void setupGeneralPanel(GPanel panel, int colorScheme) { + setupGeneralPanel(panel); + panel.setLocalColorScheme(colorScheme); + panel.setOpaque(true); +} + +// Common label formatting +public void setupGeneralLabel(GLabel label) { + label.setTextAlign(GAlign.CENTER, GAlign.MIDDLE); + label.setTextBold(); +} + +// General Utilities ----------------------------------------------------------- + +// Show/hide a panel (w/ collapse) +public void togglePanelVisibility(GPanel panel, boolean show) { + panel.setCollapsed(!show); + panel.setVisible(show); +} + + +// ============================================================================= +// WINDOW +// ============================================================================= + +// Setup ----------------------------------------------------------------------- public void createGUI(){ G4P.messagesEnabled(false); @@ -305,6 +390,9 @@ public void createGUI(){ G4P.setMouseOverEnabled(false); surface.setTitle("Sketch Window"); // Controls window + // displayWidth doesn't seem to work when declared at the top level, so defining x and y here + WINDOW_X = (displayWidth / 2) - (WINDOW_WIDTH + 50); + WINDOW_Y = (displayHeight / 2) - (WINDOW_HEIGHT / 2); controlsWindow = GWindow.getWindow(this, "Channel Shift", WINDOW_X, WINDOW_Y, WINDOW_WIDTH, WINDOW_HEIGHT, JAVA2D); controlsWindow.noLoop(); controlsWindow.setActionOnClose(G4P.KEEP_OPEN); @@ -330,48 +418,15 @@ public void createGUI(){ controlsWindow.loop(); } -// Event Handlers ============================================================== - -// Controls Window ------------------------------------------------------------- +// Event Handlers -------------------------------------------------------------- synchronized public void controlsWindow_draw(PApplet appc, GWinData data) { appc.background(230); } - -// Helpers ===================================================================== - -// General Configs ------------------------------------------------------------- -// TODO extended class that uses these defaults? - -// Common panel formatting -public void setupGeneralPanel(GPanel panel) { - panel.setTextBold(); - panel.setCollapsible(false); - panel.setDraggable(false); -} -// Common panel formatting w/ colorscheme -public void setupGeneralPanel(GPanel panel, int colorScheme) { - setupGeneralPanel(panel); - panel.setLocalColorScheme(colorScheme); - panel.setOpaque(true); -} - -// Common label formatting -public void setupGeneralLabel(GLabel label) { - label.setTextAlign(GAlign.CENTER, GAlign.MIDDLE); - label.setTextBold(); -} - -// General Utilities ----------------------------------------------------------- - -// Show/hide a panel (w/ collapse) -public void togglePanelVisibility(GPanel panel, boolean show) { - panel.setCollapsed(!show); - panel.setVisible(show); -} - -// Channel Toggle Panels ------------------------------------------------------- +// ============================================================================= +// CHANNEL TOGGLE PANELS +// ============================================================================= public void createChannelPanel(GPanel channelPanel, GToggleGroup channelToggle, ChannelOption R, ChannelOption G, ChannelOption B, boolean src) { // Configure options @@ -419,7 +474,9 @@ public void createTargChannelPanel() { } -// Channel Shift Panels -------------------------------------------------------- +// ============================================================================= +// CHANNEL SHIFT PANELS +// ============================================================================= /** * Common setup for channel shift slider panels. GUI objects must be @@ -489,9 +546,10 @@ public void createYShiftPanel() { createChannelShiftPanel(yShiftPanel, GCScheme.GREEN_SCHEME, ySlider, "ySlider_change", ySliderInput, "ySliderInput_change", ySliderToggle, ySliderPercent, "ySliderPercent_clicked", ySliderPixels, "ySliderPixels_clicked"); } -// Randomize/Reset Button Panel ------------------------------------------------ +// ============================================================================= +// RANDOMIZE PANEL +// ============================================================================= -// TODO: extract common? public void createRandomizePanel() { randomizePanel = new GPanel(controlsWindow, RAND_PANEL_X, RAND_PANEL_Y, RAND_PANEL_WIDTH, RAND_PANEL_HEIGHT, "Randomize Options"); setupGeneralPanel(randomizePanel, GCScheme.CYAN_SCHEME); @@ -540,11 +598,72 @@ public void createRandomizePanel() { randYMaxInput.setText("100"); randYMaxInput.addEventHandler(this, "randYMaxInput_change"); randomizePanel.addControl(randYMaxInput); + + // Tab manager for text inputs + randMaxTabManager = new GTabManager(); + randMaxTabManager.addControls(randXMaxInput, randYMaxInput); +} + + +// ============================================================================= +// LOAD/SAVE/RESET/CONFIRM PANEL +// ============================================================================= + +// Load/Save Panel ------------------------------------------------------------- + +public void createLoadSavePanel() { + loadSavePanel = new GPanel(controlsWindow, LOAD_SAVE_PANEL_X, LOAD_SAVE_PANEL_Y, LOAD_SAVE_PANEL_WIDTH, LOAD_SAVE_PANEL_HEIGHT); + setupGeneralPanel(loadSavePanel); + loadSavePanel.setOpaque(false); + // Load button + loadBtn = new GButton(controlsWindow, LOAD_BTN_X, LOAD_BTN_Y, LOAD_SAVE_PANEL_WIDTH, LOAD_SAVE_BTN_HEIGHT); + loadBtn.setText("Load Image"); + loadBtn.setTextBold(); + loadBtn.setLocalColorScheme(GCScheme.ORANGE_SCHEME); + loadBtn.addEventHandler(this, "loadBtn_click"); + loadSavePanel.addControl(loadBtn); + // Save button + saveBtn = new GButton(controlsWindow, SAVE_BTN_X, SAVE_BTN_Y, LOAD_SAVE_PANEL_WIDTH, LOAD_SAVE_BTN_HEIGHT); + saveBtn.setText("Save Result"); + saveBtn.setTextBold(); + saveBtn.setLocalColorScheme(GCScheme.GREEN_SCHEME); + saveBtn.addEventHandler(this, "saveBtn_click"); + loadSavePanel.addControl(saveBtn); +} + +// Preview/Confirm Panel ------------------------------------------------------- + +public void createResetConfirmPanel() { + resetConfirmPanel = new GPanel(controlsWindow, RESET_CONFIRM_PANEL_X, RESET_CONFIRM_PANEL_Y, RESET_CONFIRM_PANEL_WIDTH, RESET_CONFIRM_PANEL_HEIGHT); + setupGeneralPanel(resetConfirmPanel); + resetConfirmPanel.setOpaque(false); + // Reset Button + resetBtn = new GButton(controlsWindow, RESET_BTN_X, RESET_BTN_Y, RESET_CONFIRM_PANEL_WIDTH, RESET_CONFIRM_BTN_HEIGHT); + resetBtn.setText("Reset Step"); + resetBtn.setLocalColorScheme(GCScheme.YELLOW_SCHEME); + resetBtn.addEventHandler(this, "resetBtn_click"); + resetConfirmPanel.addControl(resetBtn); + // Confirm Button + confirmBtn = new GButton(controlsWindow, CONFIRM_BTN_X, CONFIRM_BTN_Y, RESET_CONFIRM_PANEL_WIDTH, RESET_CONFIRM_BTN_HEIGHT); + confirmBtn.setText("Confirm Step"); + confirmBtn.addEventHandler(this, "confirmBtn_click"); + resetConfirmPanel.addControl(confirmBtn); + // Recursive checkbox + recursiveCheckbox = new GCheckbox(controlsWindow, RECURSIVE_CHECKBOX_X, RECURSIVE_CHECKBOX_Y, RESET_CONFIRM_PANEL_WIDTH, RECURSIVE_CHECKBOX_HEIGHT); + recursiveCheckbox.setSelected(true); + recursiveCheckbox.setText("Recursive", GAlign.CENTER, GAlign.MIDDLE); + recursiveCheckbox.setOpaque(true); + recursiveCheckbox.addEventHandler(this, "recursiveCheckbox_click"); + resetConfirmPanel.addControl(recursiveCheckbox); } -// Shift Type Panel ------------------------------------------------------------ -// Advanced options panel +// ============================================================================= +// ADVANCED OPTIONS PANEL +// ============================================================================= + +// Setup ----------------------------------------------------------------------- + public void createAdvancedOptionsPanel() { advancedOptionsPanel = new GPanel(controlsWindow, ADV_OPTS_PANEL_X, ADV_OPTS_PANEL_Y, ADV_OPTS_PANEL_WIDTH, ADV_OPTS_PANEL_HEIGHT, "Advanced Options"); setupGeneralPanel(advancedOptionsPanel, GCScheme.PURPLE_SCHEME); @@ -564,9 +683,10 @@ public void createAdvancedOptionsPanel() { createLinearShiftTypePanel(); createSkewShiftTypePanel(); createXYMultShiftTypePanel(); + createNoiseShiftTypePanel(); } -// Helpers +// Helpers --------------------------------------------------------------------- public void hideShiftTypePanel(GPanel panel) { togglePanelVisibility(panel, false); @@ -579,7 +699,18 @@ public void showShiftTypePanel(GPanel panel) { togglePanelVisibility(panel, true); } -// Type config panels (called above) +// Event Handlers -------------------------------------------------------------- + +public void shiftTypeSelect_change(GDropList source, GEvent event) { + // Hide previously selected panel + hideShiftTypePanel(shiftTypeConfigPanels[shiftTypeManager.state]); + shiftTypeManager.setShiftType(source.getSelectedIndex()); + // Show newly selected panel + showShiftTypePanel(shiftTypeConfigPanels[shiftTypeManager.state]); + showPreview(); +} + +// Type config panels (called above) ------------------------------------------- public void setupShiftTypePanel(GPanel panel, int shiftTypeIndex) { setupGeneralPanel(panel); @@ -590,7 +721,13 @@ public void setupShiftTypePanel(GPanel panel, int shiftTypeIndex) { shiftTypeConfigPanels[shiftTypeIndex] = panel; } -// Shift Type Panels +// ============================================================================= +// SHIFT TYPE CONFIGS +// ============================================================================= + +// Default ===================================================================== + +// Setup ----------------------------------------------------------------------- public void createDefaultShiftTypePanel() { defaultShiftTypePanel = new GPanel(controlsWindow, TYPE_PANEL_X, TYPE_PANEL_Y, TYPE_PANEL_WIDTH, TYPE_PANEL_HEIGHT); @@ -607,6 +744,11 @@ public void createDefaultShiftTypePanel() { advancedOptionsPanel.addControl(defaultShiftTypePanel); } + +// Scale ======================================================================= + +// Setup ----------------------------------------------------------------------- + public void createScaleShiftTypePanel() { scaleShiftTypePanel = new GPanel(controlsWindow, TYPE_PANEL_X, TYPE_PANEL_Y, TYPE_PANEL_WIDTH, TYPE_PANEL_HEIGHT); setupShiftTypePanel(scaleShiftTypePanel, TYPE_SCALE); @@ -636,6 +778,43 @@ public void createScaleShiftTypePanel() { advancedOptionsPanel.addControl(scaleShiftTypePanel); } +// Event Handlers -------------------------------------------------------------- + +void multiplierInputEventHandler(GTextField source, GEvent event, boolean horizontal) { + switch(event) { + case ENTERED: + // Unfocus on enter, then do same actions as LOST_FOCUS case + source.setFocus(false); + case LOST_FOCUS: + // Sanitize and update manager + float val = sanitizeFloatInputValue(source); + if (val > -1.0) { + shiftTypeManager.scale_setMultiplier(val, horizontal); + showPreview(); + } + // Update input text to match sanitized input + // Also reverts input text in the event that it was not a valid numeric + // value after parsing + source.setText("" + shiftTypeManager.scale_getMultiplier(horizontal)); + break; + default: + break; + } +} + +public void xMultiplierInput_change(GTextField source, GEvent event) { + multiplierInputEventHandler(source, event, true); +} + +public void yMultiplierInput_change(GTextField source, GEvent event) { + multiplierInputEventHandler(source, event, false); +} + + +// Linear ====================================================================== + +// Setup ----------------------------------------------------------------------- + public void createLinearShiftTypePanel() { linearShiftTypePanel = new GPanel(controlsWindow, TYPE_PANEL_X, TYPE_PANEL_Y, TYPE_PANEL_WIDTH, TYPE_PANEL_HEIGHT); setupShiftTypePanel(linearShiftTypePanel, TYPE_LINEAR); @@ -672,6 +851,50 @@ public void createLinearShiftTypePanel() { advancedOptionsPanel.addControl(linearShiftTypePanel); } +// Event Handlers -------------------------------------------------------------- + +public void linearYEquals_clicked(GOption source, GEvent event) { + shiftTypeManager.linear_setEquationType(true); + showPreview(); +} + +public void linearXEquals_clicked(GOption source, GEvent event) { + shiftTypeManager.linear_setEquationType(false); + showPreview(); +} + +public void linearCoeffInput_change(GTextField source, GEvent event) { + switch(event) { + case ENTERED: + // Unfocus on enter, then do same actions as LOST_FOCUS case + source.setFocus(false); + case LOST_FOCUS: + // Sanitize and update manager + float val = sanitizeFloatInputValue(source); + if (val > -1.0) { + shiftTypeManager.linear_setCoefficient(val); + showPreview(); + } + // Update input text to match sanitized input + // Also reverts input text in the event that it was not a valid numeric + // value after parsing + source.setText("" + shiftTypeManager.linear_getCoefficient()); + break; + default: + break; + } +} + +public void linearNegativeCoeffCheckbox_click(GCheckbox source, GEvent event) { + shiftTypeManager.linear_setCoefficientSign(!source.isSelected()); + showPreview(); +} + + +// Skew ======================================================================== + +// Setup ----------------------------------------------------------------------- + public void createSkewShiftTypePanel() { skewShiftTypePanel = new GPanel(controlsWindow, TYPE_PANEL_X, TYPE_PANEL_Y, TYPE_PANEL_WIDTH, TYPE_PANEL_HEIGHT); setupShiftTypePanel(skewShiftTypePanel, TYPE_SKEW); @@ -707,6 +930,53 @@ public void createSkewShiftTypePanel() { advancedOptionsPanel.addControl(skewShiftTypePanel); } +// Event Handlers -------------------------------------------------------------- + +void skewInputEventHandler(GTextField source, GEvent event, boolean horizontal) { + switch(event) { + case ENTERED: + // Unfocus on enter, then do same actions as LOST_FOCUS case + source.setFocus(false); + case LOST_FOCUS: + // Sanitize and update manager + float val = sanitizeFloatInputValue(source); + if (val > -1.0) { + shiftTypeManager.skew_setSkew(val, horizontal); + showPreview(); + } + // Update input text to match sanitized input + // Also reverts input text in the event that it was not a valid numeric + // value after parsing + source.setText("" + shiftTypeManager.skew_getSkew(horizontal)); + break; + default: + break; + } +} + +public void xSkewInput_change(GTextField source, GEvent event) { + skewInputEventHandler(source, event, true); +} + +public void xSkewNegativeCheckbox_click(GCheckbox source, GEvent event) { + shiftTypeManager.skew_setSign(!source.isSelected(), true); + showPreview(); +} + +public void ySkewInput_change(GTextField source, GEvent event) { + skewInputEventHandler(source, event, false); +} + +public void ySkewNegativeCheckbox_click(GCheckbox source, GEvent event) { + shiftTypeManager.skew_setSign(!source.isSelected(), false); + showPreview(); +} + + +// X*Y ========================================================================= + +// Setup ----------------------------------------------------------------------- + public void createXYMultShiftTypePanel() { xyMultShiftTypePanel = new GPanel(controlsWindow, TYPE_PANEL_X, TYPE_PANEL_Y, TYPE_PANEL_WIDTH, TYPE_PANEL_HEIGHT); setupShiftTypePanel(xyMultShiftTypePanel, TYPE_XYMULT); @@ -735,51 +1005,187 @@ public void createXYMultShiftTypePanel() { advancedOptionsPanel.addControl(xyMultShiftTypePanel); } -// Load/Save Panel ------------------------------------------------------------- +// Event Handlers -------------------------------------------------------------- -public void createLoadSavePanel() { - loadSavePanel = new GPanel(controlsWindow, LOAD_SAVE_PANEL_X, LOAD_SAVE_PANEL_Y, LOAD_SAVE_PANEL_WIDTH, LOAD_SAVE_PANEL_HEIGHT); - setupGeneralPanel(loadSavePanel); - loadSavePanel.setOpaque(false); - // Load button - loadBtn = new GButton(controlsWindow, LOAD_BTN_X, LOAD_BTN_Y, LOAD_SAVE_PANEL_WIDTH, LOAD_SAVE_BTN_HEIGHT); - loadBtn.setText("Load Image"); - loadBtn.setTextBold(); - loadBtn.setLocalColorScheme(GCScheme.ORANGE_SCHEME); - loadBtn.addEventHandler(this, "loadBtn_click"); - loadSavePanel.addControl(loadBtn); - // Save button - saveBtn = new GButton(controlsWindow, SAVE_BTN_X, SAVE_BTN_Y, LOAD_SAVE_PANEL_WIDTH, LOAD_SAVE_BTN_HEIGHT); - saveBtn.setText("Save Result"); - saveBtn.setTextBold(); - saveBtn.setLocalColorScheme(GCScheme.GREEN_SCHEME); - saveBtn.addEventHandler(this, "saveBtn_click"); - loadSavePanel.addControl(saveBtn); +public void multXCheckbox_click(GCheckbox source, GEvent event) { + shiftTypeManager.xymult_setMultX(source.isSelected()); + showPreview(); } -// Preview/Confirm Panel ------------------------------------------------------- +public void multXNegativeCheckbox_click(GCheckbox source, GEvent event) { + shiftTypeManager.xymult_setXSign(!source.isSelected()); + showPreview(); +} -public void createResetConfirmPanel() { - resetConfirmPanel = new GPanel(controlsWindow, RESET_CONFIRM_PANEL_X, RESET_CONFIRM_PANEL_Y, RESET_CONFIRM_PANEL_WIDTH, RESET_CONFIRM_PANEL_HEIGHT); - setupGeneralPanel(resetConfirmPanel); - resetConfirmPanel.setOpaque(false); - // Reset Button - resetBtn = new GButton(controlsWindow, RESET_BTN_X, RESET_BTN_Y, RESET_CONFIRM_PANEL_WIDTH, RESET_CONFIRM_BTN_HEIGHT); - resetBtn.setText("Reset Step"); - resetBtn.setLocalColorScheme(GCScheme.YELLOW_SCHEME); - resetBtn.addEventHandler(this, "resetBtn_click"); - resetConfirmPanel.addControl(resetBtn); - // Confirm Button - confirmBtn = new GButton(controlsWindow, CONFIRM_BTN_X, CONFIRM_BTN_Y, RESET_CONFIRM_PANEL_WIDTH, RESET_CONFIRM_BTN_HEIGHT); - confirmBtn.setText("Confirm Step"); - confirmBtn.addEventHandler(this, "confirmBtn_click"); - resetConfirmPanel.addControl(confirmBtn); - // Recursive checkbox - recursiveCheckbox = new GCheckbox(controlsWindow, RECURSIVE_CHECKBOX_X, RECURSIVE_CHECKBOX_Y, RESET_CONFIRM_PANEL_WIDTH, RECURSIVE_CHECKBOX_HEIGHT); - recursiveCheckbox.setSelected(true); - recursiveCheckbox.setText("Recursive", GAlign.CENTER, GAlign.MIDDLE); - recursiveCheckbox.setOpaque(true); - recursiveCheckbox.addEventHandler(this, "recursiveCheckbox_click"); - resetConfirmPanel.addControl(recursiveCheckbox); +public void multYCheckbox_click(GCheckbox source, GEvent event) { + shiftTypeManager.xymult_setMultY(source.isSelected()); + showPreview(); +} + +public void multYNegativeCheckbox_click(GCheckbox source, GEvent event) { + shiftTypeManager.xymult_setYSign(!source.isSelected()); + showPreview(); +} + + +// Noise ======================================================================= + +// Setup ----------------------------------------------------------------------- + +public void createNoiseShiftTypePanel() { + noiseShiftTypePanel = new GPanel(controlsWindow, TYPE_PANEL_X, TYPE_PANEL_Y, TYPE_PANEL_WIDTH, TYPE_PANEL_HEIGHT); + setupShiftTypePanel(noiseShiftTypePanel, TYPE_NOISE); + // X Noise Start + xNoiseStartLabel = new GLabel(controlsWindow, NOISE_CONFIG_LABEL_LEFT_X, NOISE_CONFIG_LABEL_START_Y, NOISE_CONFIG_LABEL_HALF_WIDTH, NOISE_CONFIG_LABEL_HEIGHT); + xNoiseStartLabel.setText("X Start:"); + setupGeneralLabel(xNoiseStartLabel); + noiseShiftTypePanel.addControl(xNoiseStartLabel); + xNoiseStartInput = new GTextField(controlsWindow, NOISE_CONFIG_INPUT_LEFT_X, NOISE_CONFIG_INPUT_START_Y, NOISE_CONFIG_INPUT_HALF_WIDTH, NOISE_CONFIG_INPUT_HEIGHT); + xNoiseStartInput.setText("0.01"); // TODO pull from manager + xNoiseStartInput.addEventHandler(this, "xNoiseStartInput_change"); + noiseShiftTypePanel.addControl(xNoiseStartInput); + // Y Noise Start + yNoiseStartLabel = new GLabel(controlsWindow, NOISE_CONFIG_LABEL_RIGHT_X, NOISE_CONFIG_LABEL_START_Y, NOISE_CONFIG_LABEL_HALF_WIDTH, NOISE_CONFIG_LABEL_HEIGHT); + yNoiseStartLabel.setText("Y Start:"); + setupGeneralLabel(yNoiseStartLabel); + noiseShiftTypePanel.addControl(yNoiseStartLabel); + yNoiseStartInput = new GTextField(controlsWindow, NOISE_CONFIG_INPUT_RIGHT_X, NOISE_CONFIG_INPUT_START_Y, NOISE_CONFIG_INPUT_HALF_WIDTH, NOISE_CONFIG_INPUT_HEIGHT); + yNoiseStartInput.setText("0.01"); // TODO pull from manager + yNoiseStartInput.addEventHandler(this, "yNoiseStartInput_change"); + noiseShiftTypePanel.addControl(yNoiseStartInput); + // X Noise Increment + xNoiseIncrementLabel = new GLabel(controlsWindow, NOISE_CONFIG_LABEL_LEFT_X, NOISE_CONFIG_LABEL_INCREMENT_Y, NOISE_CONFIG_LABEL_HALF_WIDTH, NOISE_CONFIG_LABEL_HEIGHT); + xNoiseIncrementLabel.setText("X Step:"); + setupGeneralLabel(xNoiseIncrementLabel); + noiseShiftTypePanel.addControl(xNoiseIncrementLabel); + xNoiseIncrementInput = new GTextField(controlsWindow, NOISE_CONFIG_INPUT_LEFT_X, NOISE_CONFIG_INPUT_INCREMENT_Y, NOISE_CONFIG_INPUT_HALF_WIDTH, NOISE_CONFIG_INPUT_HEIGHT); + xNoiseIncrementInput.setText("0.01"); // TODO pull from manager + xNoiseIncrementInput.addEventHandler(this, "xNoiseIncrementInput_change"); + noiseShiftTypePanel.addControl(xNoiseIncrementInput); + // Y Noise Increment + yNoiseIncrementLabel = new GLabel(controlsWindow, NOISE_CONFIG_LABEL_RIGHT_X, NOISE_CONFIG_LABEL_INCREMENT_Y, NOISE_CONFIG_LABEL_HALF_WIDTH, NOISE_CONFIG_LABEL_HEIGHT); + yNoiseIncrementLabel.setText("Y Step:"); + setupGeneralLabel(yNoiseIncrementLabel); + noiseShiftTypePanel.addControl(yNoiseIncrementLabel); + yNoiseIncrementInput = new GTextField(controlsWindow, NOISE_CONFIG_INPUT_RIGHT_X, NOISE_CONFIG_INPUT_INCREMENT_Y, NOISE_CONFIG_INPUT_HALF_WIDTH, NOISE_CONFIG_INPUT_HEIGHT); + yNoiseIncrementInput.setText("0.01"); // TODO pull from manager + yNoiseIncrementInput.addEventHandler(this, "yNoiseIncrementInput_change"); + noiseShiftTypePanel.addControl(yNoiseIncrementInput); + // Noise Multiplier + noiseMultiplierLabel = new GLabel(controlsWindow, NOISE_CONFIG_LABEL_LEFT_X, NOISE_CONFIG_LABEL_MULTIPLIER_Y, NOISE_CONFIG_LABEL_FULL_WIDTH, NOISE_CONFIG_LABEL_HEIGHT); + noiseMultiplierLabel.setText("Noise Multiplier:"); + setupGeneralLabel(noiseMultiplierLabel); + noiseShiftTypePanel.addControl(noiseMultiplierLabel); + noiseMultiplierInput = new GTextField(controlsWindow, NOISE_CONFIG_INPUT_LEFT_X, NOISE_CONFIG_INPUT_MULTIPLIER_Y, NOISE_CONFIG_INPUT_FULL_WIDTH, NOISE_CONFIG_INPUT_HEIGHT); + noiseMultiplierInput.setText("20.0"); // TODO pull from manager + noiseMultiplierInput.addEventHandler(this, "noiseMultiplierInput_change"); + noiseShiftTypePanel.addControl(noiseMultiplierInput); + + // Tab manager for inputs + noiseTabManager = new GTabManager(); + noiseTabManager.addControls( + xNoiseStartInput, yNoiseStartInput, + xNoiseIncrementInput, yNoiseIncrementInput, + noiseMultiplierInput + ); + + // Add to advanced options + advancedOptionsPanel.addControl(noiseShiftTypePanel); +} + +// Event Handlers -------------------------------------------------------------- + +// Noise Start + +void noiseStartInputEventHandler(GTextField source, GEvent event, boolean isX) { + switch(event) { + case ENTERED: + // Unfocus on enter, then do same actions as LOST_FOCUS case + source.setFocus(false); + case LOST_FOCUS: + // Sanitize and update manager + float val = sanitizeFloatInputValue(source); + if (val >= 0.0) { + if (isX) + shiftTypeManager.noise_setXNoiseStart(val); + else + shiftTypeManager.noise_setYNoiseStart(val); + showPreview(); + } + // Update input text to match sanitized input + // Also reverts input text in the event that it was not a valid numeric + // value after parsing + source.setText("" + (isX ? shiftTypeManager.noise_xNoiseStart() : shiftTypeManager.noise_yNoiseStart())); + break; + default: + break; + } +} + +public void xNoiseStartInput_change(GTextField source, GEvent event) { + noiseStartInputEventHandler(source, event, true); +} + +public void yNoiseStartInput_change(GTextField source, GEvent event) { + noiseStartInputEventHandler(source, event, false); +} + +// Noise Increment + +void noiseIncrementInputEventHandler(GTextField source, GEvent event, boolean isX) { + switch(event) { + case ENTERED: + // Unfocus on enter, then do same actions as LOST_FOCUS case + source.setFocus(false); + case LOST_FOCUS: + // Sanitize and update manager + float val = sanitizeFloatInputValue(source); + if (val >= 0.0) { + if (isX) + shiftTypeManager.noise_setXNoiseIncrement(val); + else + shiftTypeManager.noise_setYNoiseIncrement(val); + showPreview(); + } + // Update input text to match sanitized input + // Also reverts input text in the event that it was not a valid numeric + // value after parsing + source.setText("" + (isX ? shiftTypeManager.noise_xNoiseIncrement() : shiftTypeManager.noise_yNoiseIncrement())); + break; + default: + break; + } +} + +public void xNoiseIncrementInput_change(GTextField source, GEvent event) { + noiseIncrementInputEventHandler(source, event, true); +} + +public void yNoiseIncrementInput_change(GTextField source, GEvent event) { + noiseIncrementInputEventHandler(source, event, false); +} + +// Noise Multiplier + +public void noiseMultiplierInput_change(GTextField source, GEvent event) { + switch(event) { + case ENTERED: + // Unfocus on enter, then do same actions as LOST_FOCUS case + source.setFocus(false); + case LOST_FOCUS: + // Sanitize and update manager + float val = sanitizeFloatInputValue(source); + if (val >= 0.0) { + shiftTypeManager.noise_setNoiseMultiplier(val); + showPreview(); + } + // Update input text to match sanitized input + // Also reverts input text in the event that it was not a valid numeric + // value after parsing + source.setText("" + shiftTypeManager.noise_noiseMultiplier()); + break; + default: + break; + } } diff --git a/previewWindow.pde b/previewWindow.pde index e7affa2..f48ef41 100644 --- a/previewWindow.pde +++ b/previewWindow.pde @@ -18,7 +18,7 @@ public class WindowManager { public WindowManager() { width = height = 0; - maxWindowSize = 600; + maxWindowSize = 800; previewImgUpdated = true; } @@ -29,6 +29,7 @@ public class WindowManager { width = dimensions[0]; height = dimensions[1]; surface.setSize(width, height); + surface.setLocation((displayWidth / 2) + 50, (displayHeight / 2) - (height / 2)); } public void previewImgUpdated(boolean wasUpdated) {