/* * Copyright 2017 Laszlo Balazs-Csiki * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU * General Public License, version 3 as published by the Free * Software Foundation. * * Pixelitor is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Pixelitor. If not, see <http://www.gnu.org/licenses/>. */ package pixelitor.filters.gui; import com.jhlabs.image.ImageMath; import javax.swing.*; import java.awt.Rectangle; import static pixelitor.filters.gui.RandomizePolicy.ALLOW_RANDOMIZE; /** * Two or more range params that are grouped and can be linked. */ public class GroupedRangeParam extends AbstractFilterParam implements RangeBasedOnImageSize { private final RangeParam[] rangeParams; private final ButtonModel checkBoxModel; private final boolean linkedByDefault; private boolean linkable = true; // whether a "Linked" checkbox appears /** * 2 linked params: "Horizontal" and "Vertical", linked by default */ public GroupedRangeParam(String name, int min, int defaultV, int max) { this(name, min, defaultV, max, true); } /** * 2 linked params: "Horizontal" and "Vertical" */ public GroupedRangeParam(String name, int min, int defaultV, int max, boolean linked) { this(name, "Horizontal", "Vertical", min, defaultV, max, linked); } /** * 2 linked params */ public GroupedRangeParam(String name, String firstRangeName, String secondRangeName, int min, int defaultV, int max, boolean linked) { this(name, new String[]{firstRangeName, secondRangeName}, min, defaultV, max, linked); } /** * Any number of linked params */ public GroupedRangeParam(String name, String[] rangeNames, int min, int defaultV, int max, boolean linked) { this(name, createParams(rangeNames, min, defaultV, max), linked); } public GroupedRangeParam(String name, RangeParam[] params, boolean linked) { super(name, ALLOW_RANDOMIZE); rangeParams = params; checkBoxModel = new JToggleButton.ToggleButtonModel(); this.linkedByDefault = linked; setLinked(linkedByDefault); linkParams(); } @Override public JComponent createGUI() { GroupedRangeParamGUI gui = new GroupedRangeParamGUI(this); paramGUI = gui; setParamGUIEnabledState(); return gui; } private void linkParams() { for (RangeParam param : rangeParams) { param.addChangeListener(e -> { if (isLinked()) { // set the value of every other param to the value of the current param for (RangeParam otherParam : rangeParams) { if (otherParam != param) { int newValue = param.getValue(); otherParam.setValueNoTrigger(newValue); } } } }); } } public ButtonModel getCheckBoxModel() { return checkBoxModel; } @Override public void setAdjustmentListener(ParamAdjustmentListener listener) { for (RangeParam param : rangeParams) { param.setAdjustmentListener(listener); } adjustmentListener = listener; } public int getValue(int index) { return rangeParams[index].getValue(); } public float getValueAsFloat(int index) { return rangeParams[index].getValueAsFloat(); } public double getValueAsDouble(int index) { return rangeParams[index].getValueAsDouble(); } public void setValue(int index, int newValue) { rangeParams[index].setValue(newValue); // if linked, the others will be set automatically } public boolean isLinked() { return checkBoxModel.isSelected(); } public void setLinked(boolean linked) { checkBoxModel.setSelected(linked); } @Override public int getNrOfGridBagCols() { return 1; } @Override public void randomize() { if (isLinked()) { rangeParams[0].randomize(); } else { for (RangeParam param : rangeParams) { param.randomize(); } } } @Override public boolean isSetToDefault() { if (isLinked() != linkedByDefault) { return false; } for (RangeParam param : rangeParams) { if (!param.isSetToDefault()) { return false; } } return true; } @Override public void reset(boolean triggerAction) { for (RangeParam param : rangeParams) { // call the individual params without trigger... param.reset(false); } // ... and then trigger only once if (triggerAction) { adjustmentListener.paramAdjusted(); } setLinked(linkedByDefault); } public RangeParam getRangeParam(int index) { return rangeParams[index]; } @Override public void considerImageSize(Rectangle bounds) { for (RangeParam param : rangeParams) { param.considerImageSize(bounds); } } @Override public GroupedRangeParam withAdjustedRange(double ratio) { for (RangeParam param : rangeParams) { param.withAdjustedRange(ratio); } return this; } public float getValueAsPercentage(int index) { return rangeParams[index].getValueAsPercentage(); } public int getNumParams() { return rangeParams.length; } public GroupedRangeParam setLinkable(boolean linkable) { this.linkable = linkable; return this; } public boolean isLinkable() { return linkable; } @Override public boolean canBeAnimated() { return true; } @Override public ParamState copyState() { int numParams = rangeParams.length; double[] values = new double[numParams]; for (int i = 0; i < numParams; i++) { values[i] = rangeParams[i].getValue(); } return new GRState(values); } @Override public void setState(ParamState state) { GRState grState = (GRState) state; double[] values = grState.values; for (int i = 0; i < values.length; i++) { double value = values[i]; rangeParams[i].setValueAsDouble(value); } } private static class GRState implements ParamState { private final double[] values; public GRState(double[] values) { this.values = values; } @Override public ParamState interpolate(ParamState endState, double progress) { GRState apEndState = (GRState) endState; double[] interpolatedValues = new double[values.length]; for (int i = 0; i < values.length; i++) { interpolatedValues[i] = ImageMath.lerp(progress, values[i], apEndState.values[i]); } return new GRState(interpolatedValues); } } private static RangeParam[] createParams(String[] rangeNames, int min, int defaultV, int max) { RangeParam[] rangeParams = new RangeParam[rangeNames.length]; for (int i = 0; i < rangeNames.length; i++) { String rangeName = rangeNames[i]; rangeParams[i] = new RangeParam(rangeName, min, defaultV, max); } return rangeParams; } }