package org.chartsy.main.chart; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.BitSet; import java.util.Calendar; import org.chartsy.main.AnnotationPanel; import org.chartsy.main.ChartFrame; import org.chartsy.main.data.ChartData; import org.chartsy.main.intervals.DailyInterval; import org.chartsy.main.intervals.MonthlyInterval; import org.chartsy.main.intervals.WeeklyInterval; import org.chartsy.main.managers.AnnotationManager; import org.chartsy.main.utils.CoordCalc; import org.chartsy.main.utils.Range; import org.chartsy.main.utils.SerialVersion; import org.openide.nodes.AbstractNode; /** * * @author viorel.gheba */ public abstract class Annotation implements Serializable, MouseListener, MouseMotionListener { private static final long serialVersionUID = SerialVersion.APPVERSION; public static final int NO = 0; public static final int YES = 1; protected int intraDay; public static final int NONE = 0; public static final int TOP = 1; public static final int TOP_LEFT = 2; public static final int TOP_RIGHT = 3; public static final int LEFT = 4; public static final int RIGHT = 5; public static final int BOTTOM = 6; public static final int BOTTOM_LEFT = 7; public static final int BOTTOM_RIGHT = 8; public static final int CENTER = 9; protected static final int RESIZE = 0; protected static final int MOVE = 1; protected int state; protected int action; protected BitSet actionSet; protected double x1, x2; protected double y1, y2; protected long t1, t2; protected double v1, v2; protected double x1o, y1o; protected long t1o, t2o; protected double v1o, v2o; protected int index; protected Rectangle[] updateRects = new Rectangle[2]; protected boolean newAnnotation = true; protected boolean justAdded = false; protected boolean active; protected boolean selected; protected transient ChartFrame chartFrame; protected transient AnnotationPanel annotationPanel; public Annotation() { active = false; selected = false; actionSet = new BitSet(9); } public Annotation(ChartFrame frame) { chartFrame = frame; active = false; selected = false; actionSet = new BitSet(9); } public String getName() { return getClass().getName(); } public abstract Annotation newInstance(ChartFrame frame); public ChartFrame getChartFrame() { return chartFrame; } public void setChartFrame(ChartFrame frame) { chartFrame = frame; } public AnnotationPanel getAnnotationPanel() { return annotationPanel; } public void setAnnotationPanel(AnnotationPanel panel) { annotationPanel = panel; } public int isIntraDay() { return intraDay; } public void setIntraDay(int i) { intraDay = i; } public boolean isActive() { return active; } public void setActive(boolean b) { active = b; } public boolean isSelected() { return selected; } public void setSelected(boolean b) { selected = b; } public boolean isNew() { return newAnnotation; } public void setNew(boolean b) { newAnnotation = b; } public int getIndex() { return index; } public void setIndex(int i) { index = i; } public long getT1() { return t1; } public void setT1(long l) { t1 = l; } public long getT2() { return t2; } public void setT2(long l) { t2 = l; } public double getV1() { return v1; } public void setV1(double d) { v1 = d; } public double getV2() { return v2; } public void setV2(double d) { v2 = d; } public double getX1() { return x1; } public void setX1(double d) { x1 = d; } public double getX2() { return x2; } public void setX2(double d) { x2 = d; } public double getY1() { return y1; } public void setY1(double d) { y1 = d; } public double getY2() { return y2; } public void setY2(double d) { y2 = d; } protected double getXFromX(double x) { Integer idx = null; double xc = 0; ChartData cd = chartFrame.getChartData(); Rectangle rect = chartFrame.getSplitPanel().getChartPanel().getBounds(); rect.grow(-2, -2); int items = cd.getPeriod(); int count = cd.getDataset().getItemsCount(); int negCount = count - items - (count - cd.getLast()); int posCount = count - negCount; double w = rect.getWidth() / items; double h = rect.getHeight(); double minX = rect.getMinX(); boolean negative = (x < minX); if (negative) { for (int i = 0; i < negCount; i++) { Rectangle2D r = CoordCalc.rectangle(minX + (-1 * (i * w)), 0, w, h); if (r.contains(x, 0)) { idx = -1 * i; xc = r.getX() + (w/2); break; } } if (idx == null) xc = CoordCalc.rectangle(minX + (-1 * (negCount * w)), 0, w, h).getX() + (w/2); } else { for (int i = 0; i < posCount; i++) { Rectangle2D r = CoordCalc.rectangle(minX + (i * w), 0, w, h); if (r.contains(x, 0)) { idx = i; xc = r.getX() + (w/2); break; } } if (idx == null) xc = CoordCalc.rectangle(minX + ((posCount -1 ) * w), 0, w, h).getX() + (w/2); } return xc; } protected int getTimeIndex(long t) { Integer idx = null; ChartData cd = chartFrame.getChartData(); int count = cd.getDataset().getItemsCount(); if (cd.getInterval() instanceof DailyInterval) { for (int i = 0; i < count; i++) { if (cd.getDataset().getTimeAt(i) == t) { idx = new Integer(i); break; } } } else if (cd.getInterval() instanceof WeeklyInterval) { Calendar c1 = Calendar.getInstance(); Calendar c2 = Calendar.getInstance(); c1.setTimeInMillis(t); for (int i = 0; i < count; i++) { c2.setTimeInMillis(cd.getDataset().getTimeAt(i)); if (c1.get(Calendar.WEEK_OF_YEAR) == c2.get(Calendar.WEEK_OF_YEAR) && c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR)) { idx = new Integer(i); break; } } } else if (cd.getInterval() instanceof MonthlyInterval) { Calendar c1 = Calendar.getInstance(); Calendar c2 = Calendar.getInstance(); c1.setTimeInMillis(t); for (int i = 0; i < count; i++) { c2.setTimeInMillis(cd.getDataset().getTimeAt(i)); if (c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH) && c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR)) { idx = new Integer(i); break; } } } else { for (int i = 0; i < count; i++) { if (t == cd.getDataset().getTimeAt(i)) { idx = new Integer(i); break; } } } if (idx == null) { long min = cd.getDataset().getTimeAt(0); long max = cd.getDataset().getLastTime(); if (t < min) idx = new Integer(0); if (t > max) idx = count - 1; } return idx; } protected double getXFromTime(long t) { double xc = 0; Integer idx = getTimeIndex(t); ChartData cd = chartFrame.getChartData(); Rectangle rect = chartFrame.getSplitPanel().getChartPanel().getBounds(); rect.grow(-2, -2); if (idx != null) { int items = cd.getPeriod(); int end = cd.getLast(); int itemsCount = cd.getDataset().getItemsCount(); int negativeNr = itemsCount - items - (itemsCount - end); double w = rect.getWidth() / items; double h = rect.getHeight(); double minX = rect.getMinX(); boolean negative = (idx < negativeNr); if (negative) xc = CoordCalc.rectangle(minX + ((idx - negativeNr) * w), 0, w, h).getX() + (w/2); else xc = CoordCalc.rectangle(minX + ((idx - negativeNr) * w), 0, w, h).getX() + (w/2); } return xc; } protected long getTimeFromX(double x) { Integer idx = null; ChartData cd = chartFrame.getChartData(); Rectangle rect = chartFrame.getSplitPanel().getChartPanel().getBounds(); rect.grow(-2, -2); int items = cd.getPeriod(); int end = cd.getLast(); int count = cd.getDataset().getItemsCount(); int negativeNr = count - items - (count - end); int positiveNr = count - negativeNr; double w = rect.getWidth() / items; double h = rect.getHeight(); double minX = rect.getMinX(); boolean negative = (x < minX); if (negative) { for (int i = 0; i < negativeNr; i++) { if (CoordCalc.rectangle(minX + (-1 * (i * w)), 0, w, h).contains(x, 0)) { idx = negativeNr - i; break; } } if (idx == null) idx = 0; } else { for (int i = 0; i < positiveNr; i++) { if (CoordCalc.rectangle(minX + (i * w), 0, w, h).contains(x, 0)) { idx = negativeNr + i; break; } } if (idx == null) idx = count - 1; } return chartFrame.getChartData().getDataset().getTimeAt(idx); } protected double getYFromValue(double v) { Rectangle rect = chartFrame.getSplitPanel().getChartPanel().getBounds(); rect.grow(-2, -2); Range range = getAnnotationPanel().getRange(); double dif = range.getUpperBound() - range.getLowerBound(); double percent = ((range.getUpperBound() - v) / dif) * 100; double py = rect.getMinY() + (rect.getHeight() * percent) / 100; if (range.getLowerBound() <= 0.0) { dif = Math.abs(range.getUpperBound()) + Math.abs(range.getLowerBound()); double h1 = (Math.abs(range.getUpperBound()) * rect.getHeight()) / dif; double h2 = (Math.abs(range.getLowerBound()) * rect.getHeight()) / dif; if (v >= 0) { percent = ((range.getUpperBound() - v) / range.getUpperBound()) * 100; py = rect.getMinY() + (h1 * percent) / 100; } else { percent = ((Math.abs(range.getLowerBound()) - Math.abs(v)) / Math.abs(range.getLowerBound())) * 100; py = rect.getMinY() + h1 + (h2 - ((h2 * percent) / 100)); } } return py; } protected double getValueFromY(double y) { Rectangle bounds = chartFrame.getSplitPanel().getChartPanel().getBounds(); bounds.grow(-2, -2); Range range = getAnnotationPanel().getRange(); double dif = range.getUpperBound() - range.getLowerBound(); double percent = (y - bounds.getMinY()) / bounds.getHeight(); double value = range.getUpperBound() - (percent * dif); if (range.getLowerBound() <= 0.0) { dif = Math.abs(range.getUpperBound()) + Math.abs(range.getLowerBound()); double zero = getYFromValue(0); double h1 = (Math.abs(range.getUpperBound()) * bounds.getHeight()) / dif; double h2 = (Math.abs(range.getLowerBound()) * bounds.getHeight()) / dif; if (y < zero) { // positiv percent = 100 * (y - bounds.getMinY()) / h1; value = range.getUpperBound() - ((percent / 100) * range.getUpperBound()); } else { // negative percent = 100 * (h2 - y + bounds.getMinY() + h1) / h2; value = Math.abs(range.getLowerBound()) - (percent * Math.abs(range.getLowerBound()) / 100); value = value > 0 ? value * (-1) : value; } } return value; } protected boolean hasNext(long t) { int items = chartFrame.getChartData().getDataset().getItemsCount(); return (getTimeIndex(t) + 1) < items; } protected boolean hasPrev(long t) { return (getTimeIndex(t) - 0) > 0; } protected long getNext(long t) { int i = getTimeIndex(t); return chartFrame.getChartData().getDataset().getTimeAt(i + 1); } protected long getPrev(long t) { int i = getTimeIndex(t); return chartFrame.getChartData().getDataset().getTimeAt(i - 1); } protected void setArea(long t1, double v1, long t2, double v2) { setP1(t2, v1); setP2(t2, v2); } protected Rectangle getArea() { Rectangle area = new Rectangle(); double X1 = getXFromTime(getT1()), X2 = getXFromTime(getT2()); double Y1 = getYFromValue(getV1()), Y2 = getYFromValue(getV2()); area.setFrameFromDiagonal(X1, Y1, X2, Y2); return area; } protected void setP1(long l, double d) { setT1(l); setV1(d); } protected Point2D getP1() { double x = getXFromTime(getT1()), y = getYFromValue(getV1()); return new Point2D.Double(x, y); } protected void setP2(long l, double d) { setT2(l); setV2(d); } protected Point2D getP2() { double x = getXFromTime(getT2()), y = getYFromValue(getV2()); return new Point2D.Double(x, y); } protected Point2D getPoint(long l, double v) { return new Point2D.Double(getXFromTime(l), getYFromValue(v)); } public void initialize(double x1, double y1, double x2, double y2) { setActive(true); setSelected(true); setArea(getTimeFromX((int)x1), getValueFromY(y1), getTimeFromX((int)x2), getValueFromY(y2)); state = RESIZE; action = BOTTOM_RIGHT; } public void initializeX(double x1, double x2) { setActive(true); setSelected(true); setArea(getTimeFromX((int)x1), getV1(), getTimeFromX((int)x2), getV2()); state = RESIZE; action = BOTTOM_RIGHT; } public void initializeY(double y1, double y2) { setActive(true); setSelected(true); setArea(getT1(), getValueFromY(y1), getT2(), getValueFromY(y2)); state = RESIZE; action = BOTTOM_RIGHT; } public Rectangle[] getUpdateRectangles() { int size = 0; if ((updateRects[1] != null) && (updateRects[0] != null)) { size = 2; } else { if (updateRects[0] != null) { size = 1; } } if (size == 0) return null; Rectangle[] r = new Rectangle[size]; for (int i = 0; i < size; i++) r[i] = updateRects[i]; return r; } protected void updateRectangles(long oldT1, long newT1, long oldT2, long newT2, double oldV1, double newV1, double oldV2, double newV2) { updateRects[0] = null; } protected Rectangle getCurrentRectangle(boolean extraPoints) { Rectangle r = new Rectangle(); r.setFrameFromDiagonal(getP1(), getP2()); if (extraPoints) r.grow(3, 3); return r; } public void moveDown() { double dy1 = getYFromValue(getV1()) + 10D, dy2 = getYFromValue(getV2()) + 10D; setV1(getValueFromY(dy1)); setV2(getValueFromY(dy2)); } public void moveUp() { double Y1 = getYFromValue(getV1()) - 10D, Y2 = getYFromValue(getV2()) - 10D; setV1(getValueFromY(Y1)); setV2(getValueFromY(Y2)); } public void moveLeft() { if (getT1() < getT2() ? hasPrev(getT1()) : hasPrev(getT2())) { setT1(getPrev(getT1())); setT2(getPrev(getT2())); } } public void moveRight() { if (getT1() > getT2() ? hasNext(getT1()) : hasNext(getT2())) { setT1(getNext(getT1())); setT2(getNext(getT2())); } } public boolean updatePosition(double x, double y) { switch (state) { case RESIZE: switch (action) { case TOP: return updateTop(x, y); case TOP_LEFT: return updateTopLeft(x, y); case TOP_RIGHT: return updateTopRight(x, y); case LEFT: return updateLeft(x, y); case RIGHT: return updateRight(x, y); case BOTTOM: return updateBottom(x, y); case BOTTOM_LEFT: return updateBottomLeft(x, y); case BOTTOM_RIGHT: return updateBottomRight(x, y); } break; case MOVE: return updateMove(x, y); } return false; } public boolean updateTop(double x, double y) { double v = getValueFromY(y); if (v != getV1()) { double oldV1 = getV1(); setV1(v); updateRectangles(getT1(), getT1(), getT2(), getT2(), oldV1, getV1(), getV2(), getV2()); return true; } return false; } public boolean updateTopLeft(double x, double y) { long t = getTimeFromX((int)x); double v = getValueFromY(y); if (getT1() != t || getV1() != v) { long oldT1 = getT1(); double oldV1 = getV1(); setT1(t); setV1(v); updateRectangles(oldT1, getT1(), getT2(), getT2(), oldV1, getV1(), getV2(), getV2()); return true; } return false; } public boolean updateTopRight(double x, double y) { long t = getTimeFromX((int)x); double v = getValueFromY(y); if (t != getT2() || v != getV1()) { long oldT2 = getT2(); double oldV1 = getV1(); setT2(t); setV1(v); updateRectangles(getT1(), getT1(), oldT2, getT2(), oldV1, getV1(), getV2(), getV2()); return true; } return false; } public boolean updateLeft(double x, double y) { long t = getTimeFromX((int)x); if (getT1() != t) { long oldT1 = getT1(); setT1(t); updateRectangles(oldT1, getT1(), getT2(), getT2(), getV1(), getV1(), getV2(), getV2()); return true; } return false; } public boolean updateRight(double x, double y) { long t = getTimeFromX((int)x); if (getT2() != t) { long oldT2 = getT2(); setT2(t); updateRectangles(getT1(), getT1(), oldT2, getT2(), getV1(), getV1(), getV2(), getV2()); return true; } return false; } public boolean updateBottom(double x, double y) { double v = getValueFromY(y); if (v != getV2()) { double oldV2 = getV2(); setV2(v); updateRectangles (getT1(), getT1(), getT2(), getT2(), getV1(), getV1(), oldV2, getV2()); return true; } return false; } public boolean updateBottomLeft(double x, double y) { long t = getTimeFromX((int)x); double v = getValueFromY(y); if (t != getT1() || v != getV2()) { long oldT1 = getT1(); double oldV2 = getV2(); setT1(t); setV2(v); updateRectangles (oldT1, getT1(), getT2(), getT2(), getV1(), getV1(), oldV2, getV2()); return true; } return false; } public boolean updateBottomRight(double x, double y) { long t = getTimeFromX((int)x); double v = getValueFromY(y); if (t != getT2() || v != getV2()) { long oldT2 = getT2(); double oldV2 = getV2(); setT2(t); setV2(v); updateRectangles (getT1(), getT1(), oldT2, getT2(), getV1(), getV1(), oldV2, getV2()); return true; } return false; } public boolean updateMove(double x, double y) { boolean ok = false; double X1 = getXFromTime(t1o); double X2 = getXFromTime(t2o); double Y1 = getYFromValue(v1o); double Y2 = getYFromValue(v2o); double dx = x - x1o; double dy = y - y1o; long oldT1 = getT1(); long oldT2 = getT2(); double oldV1 = getV1(); double oldV2 = getV2(); if (dx != 0) { if ((dx > 0) ? (getT1() > getT2() ? hasNext(getT1()) : hasNext(getT2())) : (getT1() > getT2() ? hasPrev(getT2()) : hasPrev(getT1()))) { X1 += dx; X2 += dx; setT1(getTimeFromX((int)X1)); setT2(getTimeFromX((int)X2)); ok = true; } } if (dy != 0) { Y1 += dy; Y2 += dy; setV1(getValueFromY(Y1)); setV2(getValueFromY(Y2)); ok = true; } if (ok) updateRectangles(oldT1, getT1(), oldT2, getT2(), oldV1, getV1(), oldV2, getV2()); return false; } public void initializePress(double x1, double y1) { setSelected(true); x1o = x1; y1o = y1; t1o = getT1(); t2o = getT2(); v1o = getV1(); v2o = getV2(); action = getActionPoint(x1, y1); if (action == NONE) state = MOVE; else state = RESIZE; } protected boolean inRezise() { return (state == RESIZE); } protected int getActionPoint(double x, double y) { double X1 = getXFromTime(getT1()), X2 = getXFromTime(getT2()), Xc = (X1 + X2) / 2; double Y1 = getYFromValue(getV1()), Y2 = getYFromValue(getV2()), Yc = (Y1 + Y2) / 2; if (actionSet.get(TOP) && Math.abs(x - Xc) < 5 && Math.abs(y - Y1) < 5) return TOP; if (actionSet.get(TOP_LEFT) && Math.abs(x - X1) < 5 && Math.abs(y - Y1) < 5) return TOP_LEFT; if (actionSet.get(TOP_RIGHT) && Math.abs(x - X2) < 5 && Math.abs(y - Y1) < 5) return TOP_RIGHT; if (actionSet.get(LEFT) && Math.abs(x - X1) < 5 && Math.abs(y - Yc) < 5) return LEFT; if (actionSet.get(RIGHT) && Math.abs(x - X2) < 5 && Math.abs(y - Yc) < 5) return RIGHT; if (actionSet.get(BOTTOM) && Math.abs(x - Xc) < 5 && Math.abs(y - Y2) < 5) return BOTTOM; if (actionSet.get(BOTTOM_LEFT) && Math.abs(x - X1) < 5 && Math.abs(y - Y2) < 5) return BOTTOM_LEFT; if (actionSet.get(BOTTOM_RIGHT) && Math.abs(x - X2) < 5 && Math.abs(y - Y2) < 5) return BOTTOM_RIGHT; return NONE; } protected void paintActionPoints(Graphics2D g) { double X1 = getXFromTime(getT1()), X2 = getXFromTime(getT2()); double Y1 = getYFromValue(getV1()), Y2 = getYFromValue(getV2()); g.setPaint(Color.BLACK); if (actionSet.get(TOP)) { if (state == RESIZE && action == TOP) g.fill(CoordCalc.rectangle((X1 + X2) / 2 - 2, Y1 - 2, 4, 4)); else g.draw(CoordCalc.rectangle((X1 + X2) / 2 - 2, Y1 - 2, 4, 4)); } if (actionSet.get(TOP_LEFT)) { if (state == RESIZE && action == TOP_LEFT) g.fill(CoordCalc.rectangle(X1 - 2, Y1 - 2, 4, 4)); else g.draw(CoordCalc.rectangle(X1 - 2, Y1 - 2, 4, 4)); } if (actionSet.get(TOP_RIGHT)) { if (state == RESIZE && action == TOP_RIGHT) g.fill(CoordCalc.rectangle(X2 - 2, Y1 - 2, 4, 4)); else g.draw(CoordCalc.rectangle(X2 - 2, Y1 - 2, 4, 4)); } if (actionSet.get(LEFT)) { if (state == RESIZE && action == LEFT) g.fill(CoordCalc.rectangle(X1 - 2, (Y1 + Y2) / 2 - 2, 4, 4)); else g.draw(CoordCalc.rectangle(X1 - 2, (Y1 + Y2) / 2, 4, 4)); } if (actionSet.get(RIGHT)) { if (state == RESIZE && action == RIGHT) g.fill(CoordCalc.rectangle(X2 - 2, (Y1 + Y2) / 2 - 2, 4, 4)); else g.draw(CoordCalc.rectangle(X2 - 2, (Y1 + Y2) / 2, 4, 4)); } if (actionSet.get(BOTTOM)) { if (state == RESIZE && action == BOTTOM) g.fill(CoordCalc.rectangle((X1 + X2) / 2 - 2, Y2 - 2, 4, 4)); else g.draw(CoordCalc.rectangle((X1 + X2) / 2 - 2, Y2 - 2, 4, 4)); } if (actionSet.get(BOTTOM_LEFT)) { if (state == RESIZE && action == BOTTOM_LEFT) g.fill(CoordCalc.rectangle(X1 - 2, Y2 - 2, 4, 4)); else g.draw(CoordCalc.rectangle(X1 - 2, Y2 - 2, 4, 4)); } if (actionSet.get(BOTTOM_RIGHT)) { if (state == RESIZE && action == BOTTOM_RIGHT) g.fill(CoordCalc.rectangle(X2 - 2, Y2 - 2, 4, 4)); else g.draw(CoordCalc.rectangle(X2 - 2, Y2 - 2, 4, 4)); } } public void addUpdateRectangle(Rectangle rect, boolean b) { int i = b ? 1 : 0; if (updateRects[i] == null) updateRects[i] = rect; else updateRects[index] = updateRects[index].union(rect); } public void cumulateUpdateArea() { if (updateRects[0] != null && updateRects[1] != null) { updateRects[0] = updateRects[0].union(updateRects[1]); updateRects[1] = null; } } public void updateArea(AnnotationPanel panel) { if (updateRects[1] != null) updateRects[1] = null; if (updateRects[0] != null) { panel.paintImmediately(updateRects[0]); updateRects[0] = null; } else { panel.repaint(); } } public boolean updateAreaContains(double x, double y, boolean second) { int idx = second ? 1 : 0; if (updateRects[idx] != null) return updateRects[idx].contains(x, y); return false; } public boolean isInBounds(double x, double y) { return annotationPanel.getBounds().contains(x, y); } protected boolean isFlipped() { return (getY1() < getY2()); } protected boolean isMirrored() { return (getX1() > getX2()); } public boolean contains(double x, double y) { return getArea().contains(x, y); } protected Rectangle getRectangle() { return getRectangle(getT1(), getV1(), getT2(), getV2()); } protected Rectangle getRectangle(long t1, double v1, long t2, double v2) { Rectangle r = new Rectangle(); r.setFrameFromDiagonal(getPoint(t1, v1), getPoint(t2, v2)); return r; } public boolean lineContains(double x1, double y1, double x2, double y2, double x, double y, int dx, int dy) { Rectangle r = new Rectangle(); r.setFrameFromDiagonal(x1, y1, x2, y2); r.grow(dx, dy); if (r.contains(x, y)) { if ((x1 == x2) || (y1 == y2)) return true; double dX = x2 - x1; double dY = y2 - y1; double ddx = x - x1; double ddy = y - y1; double D = dX * dX + dY * dY; if (D <= 0) return (ddx * ddx + ddy * ddy < dx * dy); double d = ddx * dY - dX * ddy; d = d * d / D; return (d < dx * dy); } else return false; } public boolean lineContains(double x1, double y1, double x2, double y2, double x, double y, int d) { return lineContains(x1, y1, x2, y2, x, y, d, d); } public boolean lineContains(Point2D p1, Point2D p2, Point2D p, int d) { return lineContains(p1.getX(), p1.getY(), p2.getX(), p2.getY(), p.getX(), p.getY(), d, d); } public boolean lineContains(Point2D p1, Point2D p2, Point2D p, int dx, int dy) { return lineContains(p1.getX(), p1.getY(), p2.getX(), p2.getY(), p.getX(), p.getY(), dx, dy); } public abstract boolean pointIntersects(double x, double y); public abstract void paint(Graphics2D g); @Override public boolean equals(Object obj) { if (obj == this) return true; if (!(obj instanceof Annotation)) return false; Annotation that = (Annotation) obj; if (!getClass().getName().equals(that.getClass().getName())) return false; if (getT1() != that.getT1()) return false; if (getT2() != that.getT2()) return false; if (getV1() != that.getV1()) return false; if (getV2() != that.getV2()) return false; return true; } public abstract AbstractNode getNode(); public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mouseClicked(MouseEvent e) {} public void mouseMoved(MouseEvent e) {} public void mousePressed(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1) { Annotation current = getAnnotationPanel().getCurrent(); getAnnotationPanel().deselectAll(); int x = e.getX(), y = e.getY(); if (current != null && current.isInBounds(x, y)) { getAnnotationPanel().setCurrent(current); switch (getAnnotationPanel().getState()) { case AnnotationPanel.NONE: current.setSelected(true); current.initializePress(getXFromX(x), y); if (state == MOVE) { getAnnotationPanel().setState(AnnotationPanel.MOVE); getAnnotationPanel().mousePressed(e); } if (state == RESIZE) { getAnnotationPanel().setState(AnnotationPanel.RESIZE); getAnnotationPanel().mousePressed(e); } break; case AnnotationPanel.NEWANNOTATION: getAnnotationPanel().addAnnotation(current); current.initialize(x, y, x, y); break; case AnnotationPanel.RESIZE: current.setSelected(true); state = RESIZE; break; case AnnotationPanel.MOVE: current.setSelected(true); state = MOVE; break; } } } } public void mouseDragged(MouseEvent e) { Annotation current = getAnnotationPanel().getCurrent(); if (current != null) { int x = e.getX(), y = e.getY(); if (current.isInBounds(x, y)) { if (current.updatePosition(x, y)) current.updateArea(getAnnotationPanel()); } } getAnnotationPanel().repaint(); } public void mouseReleased(MouseEvent e) { Annotation current = getAnnotationPanel().getCurrent(); if (current != null && current.isNew()) current.setNew(false); AnnotationManager.getDefault().clearNewAnnotation(); getAnnotationPanel().setState(AnnotationPanel.NONE); getAnnotationPanel().repaint(); } }