/* * 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.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Cursor; import java.awt.Graphics2D; import java.awt.Image; import java.awt.event.KeyEvent; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import com.kreative.paint.draw.PencilStrokeDrawObject; import com.kreative.paint.util.CursorUtils; public class CurlPencilTool extends AbstractPaintDrawTool implements ToolOptions.Curl { 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,K,0,0,0,0,0,0,0,0,K,K,K,K,0, 0,K,0,0,0,0,0,0,0,0,0,K,0,0,0,K, 0,K,0,K,K,0,0,0,0,0,K,0,0,0,0,K, 0,0,K,0,0,K,0,0,0,0,K,K,0,0,K,0, 0,K,0,K,K,0,0,0,0,K,0,0,K,K,K,0, 0,K,0,0,0,0,0,0,0,K,0,0,0,K,0,0, 0,K,0,K,K,0,0,0,K,0,0,0,0,K,0,0, 0,0,K,0,0,K,0,0,K,0,0,0,K,0,0,0, 0,K,0,K,K,0,0,K,0,0,0,0,K,0,0,0, 0,K,0,0,0,0,0,K,0,0,0,K,0,0,0,0, 0,K,0,K,K,0,0,K,K,0,0,K,0,0,0,0, 0,0,K,0,0,K,0,K,K,K,K,0,0,0,0,0, 0,K,0,K,K,0,0,K,K,K,0,0,0,0,0,0, 0,K,0,0,0,0,0,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, } ); private static final Cursor curs = CursorUtils.makeCursor( 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,K,K,K,K,0,0,0,0, 0,0,0,0,0,0,0,0,K,W,W,W,K,0,0,0, 0,0,0,0,0,0,0,K,W,W,W,W,K,0,0,0, 0,0,0,0,0,0,0,K,K,W,W,K,0,0,0,0, 0,0,0,0,0,0,K,W,W,K,K,K,0,0,0,0, 0,0,0,0,0,0,K,W,W,W,K,0,0,0,0,0, 0,0,0,0,0,K,W,W,W,W,K,0,0,0,0,0, 0,0,0,0,0,K,W,W,W,K,0,0,0,0,0,0, 0,0,0,0,K,W,W,W,W,K,0,0,0,0,0,0, 0,0,0,0,K,W,W,W,K,0,0,0,0,0,0,0, 0,0,0,0,K,K,W,W,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,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,0,0,0,0,0,0,0,0,0,0,0, }, 4, 15, "Pencil" ); private static final double TP = 2.0 * Math.PI; private static final double FP = 4.0 * Math.PI; public double angle = 0.0; private GeneralPath currentPath; public ToolCategory getCategory() { return ToolCategory.MISC; } protected Image getBWIcon() { return icon; } public boolean toolSelected(ToolEvent e) { currentPath = null; return false; } public boolean toolDeselected(ToolEvent e) { currentPath = null; return false; } public boolean mousePressed(ToolEvent e) { // Opening e.beginTransaction(getName()); currentPath = new GeneralPath(); // First Plot double radius = e.tc().getRawCurlRadius(); double x = e.getX() + radius * Math.cos(angle); double y = e.getY() + radius * Math.sin(angle); currentPath.moveTo((float)x, (float)y); // Return return false; } public boolean mouseDragged(ToolEvent e) { double radius = e.tc().getRawCurlRadius(); double spacing = e.tc().getRawCurlSpacing(); double sx = e.getPreviousX(); double sy = e.getPreviousY(); double dx = e.getX(); double dy = e.getY(); double sa = angle; double da = angle + TP * Math.hypot(dx-sx, dy-sy) / spacing; int m = (int)Math.ceil(Math.abs(da-sa)*radius); for (int s = 0; s < m; s++) { double dx2 = sx + ((dx-sx)*(s+1))/m; double dy2 = sy + ((dy-sy)*(s+1))/m; double da2 = sa + ((da-sa)*(s+1))/m; dx2 += radius * Math.cos(da2); dy2 += radius * Math.sin(da2); currentPath.lineTo((float)dx2, (float)dy2); } angle = da; if (angle > FP) angle -= FP; if (angle < -FP) angle += FP; return false; } public boolean mouseReleased(ToolEvent e) { // Last Plot double radius = e.tc().getRawCurlRadius(); double x = e.getX() + radius * Math.cos(angle); double y = e.getY() + radius * Math.sin(angle); currentPath.lineTo((float)x, (float)y); // Closing PencilStrokeDrawObject o = new PencilStrokeDrawObject(e.getPaintSettings(), currentPath); if (e.isInDrawMode()) e.getDrawSurface().add(o); else o.paint(e.getPaintGraphics()); currentPath = null; e.commitTransaction(); // Return return true; } public boolean paintIntermediate(ToolEvent e, Graphics2D g) { if (currentPath != null) { PencilStrokeDrawObject o = new PencilStrokeDrawObject(e.getPaintSettings(), currentPath); o.paint(g); } if (e.isMouseOnCanvas()) { double radius = e.tc().getRawCurlRadius(); g.setComposite(AlphaComposite.SrcOver); g.setStroke(new BasicStroke(1)); g.setColor(new Color(0x80000000,true)); g.draw(new Ellipse2D.Double(e.getX()-radius, e.getY()-radius, radius*2.0, radius*2.0)); g.setColor(new Color(0xC0000000,true)); GeneralPath p = new GeneralPath(); float dx = (float)(e.getX() + radius*Math.cos(angle)); float dy = (float)(e.getY() + radius*Math.sin(angle)); p.moveTo(dx-2.0f, dy); p.lineTo(dx+2.0f, dy); p.moveTo(dx, dy-2.0f); p.lineTo(dx, dy+2.0f); g.draw(p); } return true; } public boolean keyPressed(ToolEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_BACK_QUOTE: e.tc().toggleCurlCCW(); break; case KeyEvent.VK_EQUALS: case KeyEvent.VK_PLUS: case KeyEvent.VK_ADD: if (e.isShiftDown()) { e.tc().incrementCurlSpacing(); } else { e.tc().incrementCurlRadius(); } break; case KeyEvent.VK_MINUS: case KeyEvent.VK_SUBTRACT: if (e.isShiftDown()) { e.tc().decrementCurlSpacing(); } else { e.tc().decrementCurlRadius(); } break; case KeyEvent.VK_1: if (e.isShiftDown()) e.tc().setCurlSpacing(5); else e.tc().setCurlRadius(5); break; case KeyEvent.VK_2: if (e.isShiftDown()) e.tc().setCurlSpacing(10); else e.tc().setCurlRadius(10); break; case KeyEvent.VK_3: if (e.isShiftDown()) e.tc().setCurlSpacing(15); else e.tc().setCurlRadius(15); break; case KeyEvent.VK_4: if (e.isShiftDown()) e.tc().setCurlSpacing(20); else e.tc().setCurlRadius(20); break; case KeyEvent.VK_5: if (e.isShiftDown()) e.tc().setCurlSpacing(25); else e.tc().setCurlRadius(25); break; case KeyEvent.VK_6: if (e.isShiftDown()) e.tc().setCurlSpacing(30); else e.tc().setCurlRadius(30); break; case KeyEvent.VK_7: if (e.isShiftDown()) e.tc().setCurlSpacing(35); else e.tc().setCurlRadius(35); break; case KeyEvent.VK_8: if (e.isShiftDown()) e.tc().setCurlSpacing(40); else e.tc().setCurlRadius(40); break; case KeyEvent.VK_9: if (e.isShiftDown()) e.tc().setCurlSpacing(45); else e.tc().setCurlRadius(45); break; case KeyEvent.VK_0: if (e.isShiftDown()) e.tc().setCurlSpacing(50); else e.tc().setCurlRadius(50); break; } return false; } public Cursor getCursor(ToolEvent e) { return curs; } public boolean shiftConstrainsCoordinates() { return true; } public boolean doubleClickForOptions() { return true; } }