/*******************************************************************************
* This file is part of logisim-evolution.
*
* logisim-evolution is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* logisim-evolution 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 logisim-evolution. If not, see <http://www.gnu.org/licenses/>.
*
* Original code by Carl Burch (http://www.cburch.com), 2011.
* Subsequent modifications by :
* + Haute École Spécialisée Bernoise
* http://www.bfh.ch
* + Haute École du paysage, d'ingénierie et d'architecture de Genève
* http://hepia.hesge.ch/
* + Haute École d'Ingénierie et de Gestion du Canton de Vaud
* http://www.heig-vd.ch/
* The project is currently maintained by :
* + REDS Institute - HEIG-VD
* Yverdon-les-Bains, Switzerland
* http://reds.heig-vd.ch
*******************************************************************************/
package com.cburch.draw.shapes;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.QuadCurve2D;
import java.util.List;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.cburch.draw.model.CanvasObject;
import com.cburch.draw.model.Handle;
import com.cburch.draw.model.HandleGesture;
import com.cburch.logisim.data.Attribute;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Location;
import com.cburch.logisim.util.UnmodifiableList;
public class Curve extends FillableCanvasObject {
private static double[] toArray(Location loc) {
return new double[] { loc.getX(), loc.getY() };
}
private Location p0;
private Location p1;
private Location p2;
private Bounds bounds;
public Curve(Location end0, Location end1, Location ctrl) {
this.p0 = end0;
this.p1 = ctrl;
this.p2 = end1;
bounds = CurveUtil.getBounds(toArray(p0), toArray(p1), toArray(p2));
}
@Override
public boolean canMoveHandle(Handle handle) {
return true;
}
@Override
public boolean contains(Location loc, boolean assumeFilled) {
Object type = getPaintType();
if (assumeFilled && type == DrawAttr.PAINT_STROKE) {
type = DrawAttr.PAINT_STROKE_FILL;
}
if (type != DrawAttr.PAINT_FILL) {
int stroke = getStrokeWidth();
double[] q = toArray(loc);
double[] p0 = toArray(this.p0);
double[] p1 = toArray(this.p1);
double[] p2 = toArray(this.p2);
double[] p = CurveUtil.findNearestPoint(q, p0, p1, p2);
if (p == null)
return false;
int thr;
if (type == DrawAttr.PAINT_STROKE) {
thr = Math.max(Line.ON_LINE_THRESH, stroke / 2);
} else {
thr = stroke / 2;
}
if (LineUtil.distanceSquared(p[0], p[1], q[0], q[1]) < thr * thr) {
return true;
}
}
if (type != DrawAttr.PAINT_STROKE) {
QuadCurve2D curve = getCurve(null);
if (curve.contains(loc.getX(), loc.getY())) {
return true;
}
}
return false;
}
@Override
public List<Attribute<?>> getAttributes() {
return DrawAttr.getFillAttributes(getPaintType());
}
@Override
public Bounds getBounds() {
return bounds;
}
public Location getControl() {
return p1;
}
private QuadCurve2D getCurve(HandleGesture gesture) {
Handle[] p = getHandleArray(gesture);
return new QuadCurve2D.Double(p[0].getX(), p[0].getY(), p[1].getX(),
p[1].getY(), p[2].getX(), p[2].getY());
}
public QuadCurve2D getCurve2D() {
return new QuadCurve2D.Double(p0.getX(), p0.getY(), p1.getX(),
p1.getY(), p2.getX(), p2.getY());
}
@Override
public String getDisplayName() {
return Strings.get("shapeCurve");
}
public Location getEnd0() {
return p0;
}
public Location getEnd1() {
return p2;
}
private Handle[] getHandleArray(HandleGesture gesture) {
if (gesture == null) {
return new Handle[] { new Handle(this, p0), new Handle(this, p1),
new Handle(this, p2) };
} else {
Handle g = gesture.getHandle();
int gx = g.getX() + gesture.getDeltaX();
int gy = g.getY() + gesture.getDeltaY();
Handle[] ret = { new Handle(this, p0), new Handle(this, p1),
new Handle(this, p2) };
if (g.isAt(p0)) {
if (gesture.isShiftDown()) {
Location p = LineUtil.snapTo8Cardinals(p2, gx, gy);
ret[0] = new Handle(this, p);
} else {
ret[0] = new Handle(this, gx, gy);
}
} else if (g.isAt(p2)) {
if (gesture.isShiftDown()) {
Location p = LineUtil.snapTo8Cardinals(p0, gx, gy);
ret[2] = new Handle(this, p);
} else {
ret[2] = new Handle(this, gx, gy);
}
} else if (g.isAt(p1)) {
if (gesture.isShiftDown()) {
double x0 = p0.getX();
double y0 = p0.getY();
double x1 = p2.getX();
double y1 = p2.getY();
double midx = (x0 + x1) / 2;
double midy = (y0 + y1) / 2;
double dx = x1 - x0;
double dy = y1 - y0;
double[] p = LineUtil.nearestPointInfinite(gx, gy, midx,
midy, midx - dy, midy + dx);
gx = (int) Math.round(p[0]);
gy = (int) Math.round(p[1]);
}
if (gesture.isAltDown()) {
double[] e0 = { p0.getX(), p0.getY() };
double[] e1 = { p2.getX(), p2.getY() };
double[] mid = { gx, gy };
double[] ct = CurveUtil.interpolate(e0, e1, mid);
gx = (int) Math.round(ct[0]);
gy = (int) Math.round(ct[1]);
}
ret[1] = new Handle(this, gx, gy);
}
return ret;
}
}
public List<Handle> getHandles() {
return UnmodifiableList.create(getHandleArray(null));
}
@Override
public List<Handle> getHandles(HandleGesture gesture) {
return UnmodifiableList.create(getHandleArray(gesture));
}
@Override
public boolean matches(CanvasObject other) {
if (other instanceof Curve) {
Curve that = (Curve) other;
return this.p0.equals(that.p0) && this.p1.equals(that.p1)
&& this.p2.equals(that.p2) && super.matches(that);
} else {
return false;
}
}
@Override
public int matchesHashCode() {
int ret = p0.hashCode();
ret = ret * 31 * 31 + p1.hashCode();
ret = ret * 31 * 31 + p2.hashCode();
ret = ret * 31 + super.matchesHashCode();
return ret;
}
@Override
public Handle moveHandle(HandleGesture gesture) {
Handle[] hs = getHandleArray(gesture);
Handle ret = null;
if (!hs[0].equals(p0)) {
p0 = hs[0].getLocation();
ret = hs[0];
}
if (!hs[1].equals(p1)) {
p1 = hs[1].getLocation();
ret = hs[1];
}
if (!hs[2].equals(p2)) {
p2 = hs[2].getLocation();
ret = hs[2];
}
bounds = CurveUtil.getBounds(toArray(p0), toArray(p1), toArray(p2));
return ret;
}
@Override
public void paint(Graphics g, HandleGesture gesture) {
QuadCurve2D curve = getCurve(gesture);
if (setForFill(g)) {
((Graphics2D) g).fill(curve);
}
if (setForStroke(g)) {
((Graphics2D) g).draw(curve);
}
}
@Override
public Element toSvgElement(Document doc) {
return SvgCreator.createCurve(doc, this);
}
@Override
public void translate(int dx, int dy) {
p0 = p0.translate(dx, dy);
p1 = p1.translate(dx, dy);
p2 = p2.translate(dx, dy);
bounds = bounds.translate(dx, dy);
}
}