/* Copyright (C) 2006 Christian Schneider * * This file is part of Nomad. * * Nomad 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 2 of the License, or * (at your option) any later version. * * Nomad 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 Nomad; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Created on Feb 10, 2006 */ package net.sf.nmedit.jtheme.cable; import java.awt.Point; import java.awt.Rectangle; import java.awt.Shape; import java.awt.geom.CubicCurve2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; public class Pseudo3DCableGeometrie extends CubicCurve2D.Float implements CableGeometrie { /** * */ private static final long serialVersionUID = 2686151817733494025L; private boolean boundaryChanged = true; private transient Rectangle2D cachedBounds; private final static int maxCX = 60; private final static int maxCY = 10; private final static int dyTreshold = 40; private final static int dxTreshold = 300; // in pixel private final static int gravityLimit = 100; // in pixel private final static int hrzGravityTreshold = dyTreshold/2; // in pixel //private ShapeGrid shapeGrid = new ShapeGrid() ; public Pseudo3DCableGeometrie() { this(0, 0, 0, 0); } public Pseudo3DCableGeometrie(float x1, float y1, float x2, float y2) { setCurve(x1, y1, x2, y2); } public Pseudo3DCableGeometrie(Point2D p1, Point2D p2) { setCurve((float)p1.getX(), (float)p1.getY(), (float)p2.getX(), (float)p2.getY()); } public Point getP1() { return new Point(getX1i(), getY1i()); } public Point getP2() { return new Point(getX2i(), getY2i()); } public Point getCtrlP1() { return new Point(getCtrlX1i(), getCtrlY1i()); } public Point getCtrlP2() { return new Point(getCtrlX2i(), getCtrlY2i()); } public int getX1i() { return (int) getX1(); } public int getY1i() { return (int) getY1(); } public int getX2i() { return (int) getX2(); } public int getY2i() { return (int) getY2(); } public int getCtrlX1i() { return (int) getCtrlX1(); } public int getCtrlY1i() { return (int) getCtrlY1(); } public int getCtrlX2i() { return (int) getCtrlX2(); } public int getCtrlY2i() { return (int) getCtrlY2(); } public void setP1(float x, float y) { setCurve(x, y, x2, y2); } public void setP1(Point2D p) { setP1((float)p.getX(), (float)p.getY()); } public void setP2(float x, float y) { setCurve(x1, y1, x, y); } public void setP2(Point2D p) { setP2((float)p.getX(), (float)p.getY()); } public void setCurve(CubicCurve2D curve) { setCurve((float)curve.getCtrlX1(), (float)curve.getCtrlY1(), (float)curve.getCtrlX2(), (float)curve.getCtrlY2()); } public void setCurve(Point p1, Point p2) { setCurve(p1.x, p1.y, p2.x, p2.y); } public void setCurve(float x1, float y1, float ctrlx1, float ctrly1, float ctrlx2, float ctrly2, float x2, float y2) { setCurve(x1, y1, x2, y2); } protected void swapPoints() { float tmp; tmp = x1; x1=x2; x2=tmp; tmp = y1; y1=y2; y2=tmp; tmp = ctrlx1; ctrlx1=ctrlx2; ctrlx2=tmp; tmp = ctrly1; ctrly1=ctrly2; ctrly2=tmp; } public void setCurve(float x1, float y1, float x2, float y2) { if (this.x1!=x1||this.y1!=y1||this.x2!=x2||this.y2!=y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; updateControlPoints(); } } private void updateControlPoints() { final float dx = Math.abs(x1-x2); // distance x : p1, p2 final float dy = Math.abs(y1-y2); // distance y : p1, p2 // control point offset from p1/p2 (without gravity) float cx = Math.min(maxCX, dx); float cy = Math.min(maxCY, dy); // if p1 is at the left of p2 then -1, otherwise +1 int isLeft = x1<x2 ? -1 : +1; // is p1 is above p2 then +1, otherwise -1 int isAbove = y1<y2 ? +1 : -1; // if p1 and p2 are below dy treshold, the higher control point is lowered // so that the cable hangs down rather then it becomes a straight line float gravity1 = Math.max(0, dyTreshold-dy); // if distanceX(p1, p2) passes dx treshold the gravity has higher impact float infiniteGravity = Math.max(0, dx-dxTreshold); // => the cable will hang down more than before (limited by gravityLimit) float gravity2 = Math.min(infiniteGravity, gravityLimit); // assures that if p1 is exactly above p2 the curve is not a straight lines float hrzGravity = Math.min(Math.max(0, hrzGravityTreshold-dx), hrzGravityTreshold); // offset with gravity float offsetX = ((cx-gravity1)*isLeft); float offsetY = (cy*isAbove); float globalGravity = gravity1+gravity2; ctrly1 = y1-offsetY+globalGravity; ctrlx1 = x1-offsetX+hrzGravity; ctrly2 = y2+offsetY+globalGravity; ctrlx2 = x2+offsetX+hrzGravity; boundaryChanged = true; } public Rectangle getBounds() { return getBounds(null); } public Rectangle getBounds(Rectangle r) { if (r == null) r = new Rectangle(); r.setFrame(getCachedBounds()); return r; } public boolean intersects(int x, int y, int width, int height) { return super.intersects(x, y, width, height); } public boolean intersects(Rectangle r) { return super.intersects(r.x, r.y, r.width, r.height); } private final Rectangle2D getCachedBounds() { // cachedBounds==null => boundaryChanged = true if (boundaryChanged || (cachedBounds == null)) { cachedBounds = super.getBounds2D(); boundaryChanged = false; } return cachedBounds; } public void setEndPoints(int x1, int y1, int x2, int y2) { setCurve(x1, y1, x2, y2); } public void setEndPoints(Point p1, Point p2) { setCurve(p1, p2); } public Shape getShape() { return this; } public Point getStart() { return new Point((int)getX1(), (int)getY1()); } public Point getStop() { return new Point((int)getX2(), (int)getY2()); } public double getShake() { return 0; } public void setShake(double shake) { // not supported } public void shake() { // not supported } }