/*
* Copyright © 2009-2011 Rebecca G. Bettencourt / Kreative Software
* <p>
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* <a href="http://www.mozilla.org/MPL/">http://www.mozilla.org/MPL/</a>
* <p>
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
* <p>
* Alternatively, the contents of this file may be used under the terms
* of the GNU Lesser General Public License (the "LGPL License"), in which
* case the provisions of LGPL License are applicable instead of those
* above. If you wish to allow use of your version of this file only
* under the terms of the LGPL License and not to allow others to use
* your version of this file under the MPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the LGPL License. If you do not delete
* the provisions above, a recipient may use your version of this file
* under either the MPL or the LGPL License.
* @since PowerPaint 1.0
* @author Rebecca G. Bettencourt, Kreative Software
*/
package com.kreative.paint.tool;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import com.kreative.paint.PaintContext;
import com.kreative.paint.ToolContext;
import com.kreative.paint.form.Form;
import com.kreative.paint.form.IntegerEnumOption;
import com.kreative.paint.form.IntegerOption;
import com.kreative.paint.util.Bitmap;
import com.kreative.paint.util.CursorUtils;
public class SpinTool extends AbstractPaintTool implements ToolOptions.Custom {
private static final int K = 0xFF000000;
private static final int W = 0xFFFFFFFF;
private static final Image icon = ToolUtilities.makeIcon(
16, 16,
new int[] {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,K,K,K,K,K,K,K,K,K,K,K,K,0,0,
0,0,0,K,K,K,K,K,K,K,K,K,K,0,0,0,
0,0,0,0,K,K,K,K,K,K,K,K,0,0,0,0,
0,0,0,0,0,K,K,K,K,K,K,0,0,0,0,0,
0,0,0,0,0,0,K,K,K,K,0,0,0,0,0,0,
0,0,0,0,0,0,0,K,K,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,K,K,0,0,0,0,0,0,0,
0,0,0,0,0,0,K,K,K,K,0,0,0,0,0,0,
0,0,0,0,0,K,K,K,K,K,K,0,0,0,0,0,
0,0,0,0,K,K,K,K,K,K,K,K,0,0,0,0,
0,0,0,K,K,K,K,K,K,K,K,K,K,0,0,0,
0,0,K,K,K,K,K,K,K,K,K,K,K,K,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
}
);
private static final Cursor curs = CursorUtils.makeCursor(
13, 13,
new int[] {
0,W,W,0,0,0,0,0,0,0,0,0,0,
W,K,K,W,0,0,0,0,0,0,0,0,0,
W,K,K,K,W,0,0,0,0,0,0,0,0,
0,W,K,K,K,W,0,0,0,0,0,0,0,
0,0,W,K,K,W,0,0,0,0,0,0,0,
0,0,0,W,W,K,W,0,0,0,0,0,0,
0,0,0,0,0,W,K,W,0,0,0,0,0,
0,0,0,0,0,0,W,K,W,W,0,0,0,
0,0,0,0,0,0,0,W,K,K,W,0,0,
0,0,0,0,0,0,0,W,K,K,K,W,0,
0,0,0,0,0,0,0,0,W,K,K,K,W,
0,0,0,0,0,0,0,0,0,W,K,K,W,
0,0,0,0,0,0,0,0,0,0,W,W,0,
},
6, 6,
"Baton Rouge"
);
public ToolCategory getCategory() {
return ToolCategory.MISC;
}
protected Image getBWIcon() {
return icon;
}
private int phase = 0;
private int[][] cache = null;
private int lc = 16;
private int dc = 4;
private SpinShape sc = SPIN;
private void buildCache(ToolContext tc, PaintContext cx) {
cache = new int[64][];
lc = tc.getCustom(SpinTool.class, "length", Integer.class, 16);
dc = tc.getCustom(SpinTool.class, "delay", Integer.class, 4);
sc = tc.getCustom(SpinTool.class, "shape", SpinShape.class, SPIN);
for (int i = 0; i < 64; i++) {
BufferedImage bi = new BufferedImage(lc*4, lc*4, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
g.setStroke(cx.getStroke());
g.setPaint(Color.black);
sc.drawPhase(g, lc*2, lc*2, lc, i);
g.dispose();
cache[i] = new int[lc*4*lc*4];
bi.getRGB(0, 0, lc*4, lc*4, cache[i], 0, lc*4);
}
}
public boolean toolSelected(ToolEvent e) {
buildCache(e.tc(), e.getPaintContext());
return false;
}
public boolean toolSettingsChanged(ToolEvent e) {
buildCache(e.tc(), e.getPaintContext());
return false;
}
public boolean paintSettingsChanged(ToolEvent e) {
buildCache(e.tc(), e.getPaintContext());
return false;
}
public boolean mousePressed(ToolEvent e) {
e.beginTransaction(getName());
e.getPaintContext().applyFill(e.getPaintGraphics());
spin(e, true);
return true;
}
public boolean mouseHeld(ToolEvent e) {
spin(e, false);
return true;
}
public boolean mouseDragged(ToolEvent e) {
spin(e, false);
return true;
}
public boolean mouseReleased(ToolEvent e) {
spin(e, false);
e.commitTransaction();
return true;
}
private long etime = 0L;
private long ptime = 0L;
private void spin(ToolEvent e, boolean reset) {
if (cache == null) buildCache(e.tc(), e.getPaintContext());
long ctime = System.currentTimeMillis();
if (reset) {
etime = 0L;
ptime = ctime;
} else {
etime += (ctime-ptime);
ptime = ctime;
}
while (etime >= dc) {
new Bitmap(lc*4, lc*4, cache[(phase++) & 63]).paint(
e.getPaintGraphics(),
(int)e.getX()-lc*2,
(int)e.getY()-lc*2
);
etime -= dc;
}
}
public boolean keyPressed(ToolEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT:
case KeyEvent.VK_DOWN:
case KeyEvent.VK_1:
e.tc().decrementCustom(SpinTool.class, "length", Integer.class, 16, 1);
break;
case KeyEvent.VK_2:
e.tc().setCustom(SpinTool.class, "length", 16);
break;
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_UP:
case KeyEvent.VK_3:
e.tc().incrementCustom(SpinTool.class, "length", Integer.class, 16);
break;
case KeyEvent.VK_4:
e.tc().setCustom(SpinTool.class, "length", 8);
break;
case KeyEvent.VK_5:
e.tc().setCustom(SpinTool.class, "length", 12);
break;
case KeyEvent.VK_6:
e.tc().setCustom(SpinTool.class, "length", 16);
break;
case KeyEvent.VK_7:
e.tc().setCustom(SpinTool.class, "length", 24);
break;
case KeyEvent.VK_8:
e.tc().setCustom(SpinTool.class, "length", 32);
break;
case KeyEvent.VK_9:
e.tc().setCustom(SpinTool.class, "length", 48);
break;
case KeyEvent.VK_0:
e.tc().setCustom(SpinTool.class, "length", 64);
break;
case KeyEvent.VK_MINUS:
case KeyEvent.VK_SUBTRACT:
e.tc().setCustom(SpinTool.class, "shape", SPIN);
phase = 0;
break;
case KeyEvent.VK_EQUALS:
case KeyEvent.VK_PLUS:
case KeyEvent.VK_ADD:
e.tc().setCustom(SpinTool.class, "shape", CLOCK);
phase = 0;
break;
case KeyEvent.VK_OPEN_BRACKET:
e.tc().setCustom(SpinTool.class, "shape", BFLY);
phase = 0;
break;
case KeyEvent.VK_CLOSE_BRACKET:
e.tc().setCustom(SpinTool.class, "shape", FENCE);
phase = 0;
break;
case KeyEvent.VK_SEMICOLON:
e.tc().setCustom(SpinTool.class, "shape", RBROW);
phase = 0;
break;
case KeyEvent.VK_QUOTE:
case KeyEvent.VK_QUOTEDBL:
e.tc().setCustom(SpinTool.class, "shape", LBROW);
phase = 0;
break;
case KeyEvent.VK_PERIOD:
case KeyEvent.VK_DECIMAL:
e.tc().setCustom(SpinTool.class, "shape", BIRD);
phase = 0;
break;
case KeyEvent.VK_SLASH:
case KeyEvent.VK_DIVIDE:
e.tc().setCustom(SpinTool.class, "shape", CRADLE);
phase = 0;
break;
case KeyEvent.VK_COMMA:
case KeyEvent.VK_SEPARATOR:
phase = 0;
cache = null;
break;
}
return false;
}
public Cursor getCursor(ToolEvent e) {
return curs;
}
public int getMouseHeldInterval() {
return dc;
}
public boolean shiftConstrainsCoordinates() {
return true;
}
public boolean doubleClickForOptions() {
return true;
}
public Form getCustomOptionsForm(final ToolContext tc) {
Form f = new Form();
f.add(new IntegerEnumOption() {
public String getName() { return ToolUtilities.messages.getString("spin.options.Shape"); }
public int getValue() {
SpinShape shape = tc.getCustom(SpinTool.class, "shape", SpinShape.class, SPIN);
if (shape == SPIN) return 0;
if (shape == BFLY) return 1;
if (shape == FENCE) return 2;
if (shape == RBROW) return 3;
if (shape == LBROW) return 4;
if (shape == BIRD) return 5;
if (shape == CRADLE) return 6;
if (shape == CLOCK) return 7;
return 0;
}
public void setValue(int v) {
SpinShape shape;
switch (v) {
case 0: shape = SPIN; phase = 0; break;
case 1: shape = BFLY; phase = 0; break;
case 2: shape = FENCE; phase = 0; break;
case 3: shape = RBROW; phase = 0; break;
case 4: shape = LBROW; phase = 0; break;
case 5: shape = BIRD; phase = 0; break;
case 6: shape = CRADLE; phase = 0; break;
case 7: shape = CLOCK; phase = 0; break;
default: shape = SPIN; phase = 0; break;
}
tc.setCustom(SpinTool.class, "shape", shape);
}
public int[] values() {
return new int[]{ 0, 1, 2, 3, 4, 5, 6, 7 };
}
public String getLabel(int v) {
switch (v) {
case 0: return ToolUtilities.messages.getString("spin.options.Shape.Spin");
case 1: return ToolUtilities.messages.getString("spin.options.Shape.Bfly");
case 2: return ToolUtilities.messages.getString("spin.options.Shape.Fence");
case 3: return ToolUtilities.messages.getString("spin.options.Shape.Rbrow");
case 4: return ToolUtilities.messages.getString("spin.options.Shape.Lbrow");
case 5: return ToolUtilities.messages.getString("spin.options.Shape.Bird");
case 6: return ToolUtilities.messages.getString("spin.options.Shape.Cradle");
case 7: return ToolUtilities.messages.getString("spin.options.Shape.Clock");
default: return null;
}
}
});
f.add(new IntegerOption() {
public String getName() { return ToolUtilities.messages.getString("spin.options.LineLength"); }
public int getMaximum() { return 128; }
public int getMinimum() { return 1; }
public int getStep() { return 1; }
public int getValue() { return tc.getCustom(SpinTool.class, "length", Integer.class, 16); }
public void setValue(int v) { tc.setCustom(SpinTool.class, "length", v); }
public boolean useSlider() { return false; }
});
f.add(new IntegerOption() {
public String getName() { return ToolUtilities.messages.getString("spin.options.Delay"); }
public int getMaximum() { return 500; }
public int getMinimum() { return 1; }
public int getStep() { return 1; }
public int getValue() { return tc.getCustom(SpinTool.class, "delay", Integer.class, 4); }
public void setValue(int v) { tc.setCustom(SpinTool.class, "delay", v); }
public boolean useSlider() { return false; }
});
return f;
}
public static interface SpinShape {
public void drawPhase(Graphics g, int cx, int cy, int l, int p);
}
public static final SpinShape SPIN = new SpinShape() {
public void drawPhase(Graphics g, int cx, int cy, int l, int p) {
p &= 63;
int x1 = (p < 32) ? (cx+l*(p-16)/16) : (cx-l*(p-48)/16);
int y1 = (p < 16) ? (cy-l*p/16) : (p < 48) ? (cy+l*(p-32)/16) : (cy-l*(p-64)/16);
int x2 = (p < 32) ? (cx-l*(p-16)/16) : (cx+l*(p-48)/16);
int y2 = (p < 16) ? (cy+l*p/16) : (p < 48) ? (cy-l*(p-32)/16) : (cy+l*(p-64)/16);
g.drawLine(x1, y1, x2, y2);
}
};
public static final SpinShape BFLY = new SpinShape() {
public void drawPhase(Graphics g, int cx, int cy, int l, int p) {
p &= 63;
int x1 = cx-l;
int y1 = (p < 16) ? (cy-l*p/16) : (p < 48) ? (cy+l*(p-32)/16) : (cy-l*(p-64)/16);
int x2 = cx+l;
int y2 = (p < 16) ? (cy+l*p/16) : (p < 48) ? (cy-l*(p-32)/16) : (cy+l*(p-64)/16);
g.drawLine(x1, y1, x2, y2);
}
};
public static final SpinShape FENCE = new SpinShape() {
public void drawPhase(Graphics g, int cx, int cy, int l, int p) {
p &= 63;
int x1 = (p < 16) ? (cx-l*p/16) : (p < 48) ? (cx+l*(p-32)/16) : (cx-l*(p-64)/16);
int y1 = cy-l;
int x2 = (p < 16) ? (cx+l*p/16) : (p < 48) ? (cx-l*(p-32)/16) : (cx+l*(p-64)/16);
int y2 = cy+l;
g.drawLine(x1, y1, x2, y2);
}
};
public static final SpinShape RBROW = new SpinShape() {
public void drawPhase(Graphics g, int cx, int cy, int l, int p) {
p &= 31;
int x1 = (p < 16) ? (cx-l*p/16) : (cx+l*(p-32)/16);
int y1 = (p < 16) ? (cy+l*(p-16)/16) : (cy-l*(p-16)/16);
int x2 = (p < 16) ? (cx+l*p/16) : (cx-l*(p-32)/16);
int y2 = (p < 16) ? (cy-l*(p-16)/16) : (cy+l*(p-16)/16);
g.drawLine(x1, y1, x2, y2);
}
};
public static final SpinShape LBROW = new SpinShape() {
public void drawPhase(Graphics g, int cx, int cy, int l, int p) {
p &= 31;
int x1 = (p < 16) ? (cx+l*(p-16)/16) : (cx-l*(p-16)/16);
int y1 = (p < 16) ? (cy+l*p/16) : (cy-l*(p-32)/16);
int x2 = (p < 16) ? (cx-l*(p-16)/16) : (cx+l*(p-16)/16);
int y2 = (p < 16) ? (cy-l*p/16) : (cy+l*(p-32)/16);
g.drawLine(x1, y1, x2, y2);
}
};
public static final SpinShape BIRD = new SpinShape() {
public void drawPhase(Graphics g, int cx, int cy, int l, int p) {
p &= 63;
int x1 = (p < 32) ? (cx+l*(p-16)/16) : (cx-l*(p-48)/16);
int y1 = (p < 16) ? (cy-l*p/16) : (p < 48) ? (cy+l*(p-32)/16) : (cy-l*(p-64)/16);
int x2 = (p < 32) ? (cx-l*(p-16)/16) : (cx+l*(p-48)/16);
int y2 = (p < 16) ? (cy-l*p/16) : (p < 48) ? (cy+l*(p-32)/16) : (cy-l*(p-64)/16);
g.drawLine(x1, y1, cx, cy);
g.drawLine(cx, cy, x2, y2);
}
};
public static final SpinShape CRADLE = new SpinShape() {
public void drawPhase(Graphics g, int cx, int cy, int l, int p) {
p &= 63;
int x1 = (p < 32) ? (cx+l*(p-16)/16) : (cx-l*(p-48)/16);
int y1 = (p < 16) ? (cy-l*p/16) : (p < 48) ? (cy+l*(p-32)/16) : (cy-l*(p-64)/16);
int x2 = (p < 32) ? (cx+l*(p-16)/16) : (cx-l*(p-48)/16);
int y2 = (p < 16) ? (cy+l*p/16) : (p < 48) ? (cy-l*(p-32)/16) : (cy+l*(p-64)/16);
g.drawLine(x1, y1, cx, cy);
g.drawLine(cx, cy, x2, y2);
}
};
public static final SpinShape CLOCK = new SpinShape() {
public void drawPhase(Graphics g, int cx, int cy, int l, int p) {
p &= 63;
int x1 = (p < 16) ? (cx+l*p/16) : (p < 48) ? (cx-l*(p-32)/16) : (cx+l*(p-64)/16);
int y1 = (p < 32) ? (cy+l*(p-16)/16) : (cy-l*(p-48)/16);
g.drawLine(x1, y1, cx, cy);
}
};
}