/*
GeoGebra - Dynamic Mathematics for Everyone
http://www.geogebra.org
This file is part of GeoGebra.
This program 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.
*/
/*
* DrawSlider: draws a slider to change a number continously
*/
package org.geogebra.common.euclidian.draw;
import org.geogebra.common.awt.GBasicStroke;
import org.geogebra.common.awt.GColor;
import org.geogebra.common.awt.GEllipse2DDouble;
import org.geogebra.common.awt.GGraphics2D;
import org.geogebra.common.awt.GLine2D;
import org.geogebra.common.awt.GRectangle;
import org.geogebra.common.euclidian.BoundingBox;
import org.geogebra.common.euclidian.Drawable;
import org.geogebra.common.euclidian.EuclidianStatic;
import org.geogebra.common.euclidian.EuclidianView;
import org.geogebra.common.factories.AwtFactory;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoNumeric;
/**
*
* @author Markus Hohenwarter
*/
public class DrawSlider extends Drawable {
private GeoNumeric number;
private boolean isVisible, labelVisible;
private double[] coordsRW = new double[2];
private double[] coordsScreen = new double[2];
// private GeoPoint geoPoint;
// private DrawPointSlider drawPoint;
// used by getSelectionDiamaterMin()
private static final int SELECTION_RADIUS_MIN = 12;
// private GeoPointND P;
// now hard-coded
final private static int pointSize = 5;
final private static int diameter = 2 * pointSize + 1;
final private static int HIGHLIGHT_OFFSET = pointSize / 2 + 1;
final private static int hightlightDiameter = diameter
+ 2 * HIGHLIGHT_OFFSET;
// for dot and selection
private GEllipse2DDouble circle = AwtFactory.getPrototype()
.newEllipse2DDouble();
private GEllipse2DDouble circleOuter = AwtFactory.getPrototype()
.newEllipse2DDouble();
private GEllipse2DDouble circleHighlight = AwtFactory.getPrototype()
.newEllipse2DDouble();
private static GBasicStroke borderStroke = EuclidianStatic
.getDefaultStroke();
private double[] coords = new double[2];
private GLine2D line = AwtFactory.getPrototype().newLine2D();
/**
* Creates new drawable for slider
*
* @param view
* view
* @param number
* slider
*/
public DrawSlider(EuclidianView view, GeoNumeric number) {
this.view = view;
this.number = number;
geo = number;
update();
}
final private void updateScreenCoords() {
// if (number.isAbsoluteScreenLocActive() && initX >= 0 && initY >= 0) {
// number.fixPositionHorizontal(initX, view.getSettings()
// .getFileWidth(),
// view.getWidth());
// number.fixPositionVertical(initY, view.getSettings()
// .getFileHeight(),
// view.getHeight());
// }
isVisible = geo.isEuclidianVisible();
if (isVisible) {
double widthRW;
double widthScreen;
boolean horizontal = number.isSliderHorizontal();
// start point of horizontal line for slider
if (number.isAbsoluteScreenLocActive()) {
coordsScreen[0] = number.getSliderX();
coordsScreen[1] = number.getSliderY() - 1;
coordsRW[0] = view.toRealWorldCoordX(coordsScreen[0]);
coordsRW[1] = view.toRealWorldCoordY(coordsScreen[1]);
widthScreen = number.getSliderWidth();
widthRW = horizontal ? widthScreen * view.getInvXscale()
: widthScreen * view.getInvYscale();
} else {
coordsRW[0] = number.getSliderX();
coordsRW[1] = number.getSliderY();
coordsScreen[0] = view.toScreenCoordXd(coordsRW[0]);
coordsScreen[1] = view.toScreenCoordYd(coordsRW[1]);
widthRW = number.getSliderWidth();
widthScreen = horizontal ? widthRW * view.getXscale()
: widthRW * view.getYscale();
}
// point on slider that moves
double min = number.getIntervalMin();
double max = number.getIntervalMax();
double param = (number.getValue() - min) / (max - min);
// setPointSize(2 + (number.getLineThickness() + 1) / 3);
// horizontal slider
if (horizontal) {
updatePoint(coordsRW[0] + widthRW * param, coordsRW[1]);
if (labelVisible) {
this.xLabel -= 15;
this.yLabel -= 5;
}
// horizontal line
this.line.setLine(coordsScreen[0], coordsScreen[1],
coordsScreen[0] + widthScreen, coordsScreen[1]);
}
// vertical slider
else {
updatePoint(coordsRW[0], coordsRW[1] + widthRW * param);
if (labelVisible) {
this.xLabel += 5;
this.yLabel += 2 * pointSize + 4;
}
this.line.setLine(coordsScreen[0], coordsScreen[1],
coordsScreen[0], coordsScreen[1] - widthScreen);
// vertical line
}
// now hard-coded
number.setLineThickness(10);
updateStrokes(number, 10);
}
}
@Override
final public void update() {
updateScreenCoords();
// if (needsAdjusted()) {
// // Log.debug(ADJUST + " needed for " + geo.getNameDescription());
// updateScreenCoords();
// }
}
@Override
final public void draw(GGraphics2D g2) {
if (isVisible) {
// horizontal line
g2.setPaint(geo.getSelColor());
g2.setStroke(objStroke);
g2.drawStraightLine(line.getP1().getX(), line.getP1().getY(),
line.getP2().getX(), line.getP2().getY());
if (geo.doHighlighting()) {
g2.fill(circleHighlight);
g2.setStroke(borderStroke);
g2.draw(circleHighlight);
} else {
g2.fill(circleOuter);
g2.setStroke(borderStroke);
g2.draw(circleOuter);
}
// draw a dot
g2.setPaint(geo.getObjectColor());
g2.fill(circle);
// black stroke
g2.setPaint(GColor.BLACK);
g2.setStroke(borderStroke);
g2.draw(circle);
// point
if (labelVisible) {
g2.setFont(view.getFontPoint());
g2.setPaint(geo.getLabelColor());
drawLabel(g2);
}
}
}
@Override
final public boolean hit(int x, int y, int hitThreshold) {
return hitPoint(x, y, hitThreshold) || hitSlider(x, y, hitThreshold);
}
@Override
final public boolean isInside(GRectangle rect) {
return rect.contains(circle.getBounds());
}
/**
* Returns true iff the movable point was hit
*
* @param x
* mouse x-coord
* @param y
* mouse y-coord
* @param hitThreshold
* threshold
* @return true iff the movable point was hit
*/
final public boolean hitPoint(int x, int y, int hitThreshold) {
int r = hitThreshold + SELECTION_RADIUS_MIN;
double dx = coords[0] - x;
double dy = coords[1] - y;
return dx < r && dx > -r && dx * dx + dy * dy <= r * r;
}
@Override
public boolean hitLabel(int x, int y) {
return super.hitLabel(x, y);
}
/**
* Returns true if the slider line was hit, false for fixed sliders
*
* @param x
* mouse x-coord
* @param y
* mouse y-coord
* @param hitThreshold
* threshold
* @return true if the slider line was hit, false for fixed sliders
*/
public boolean hitSlider(int x, int y, int hitThreshold) {
// changed: we want click on fixed slider to increment/decrement the
// slider a bit
// return !number.isSliderFixed() && line.intersects(x-2, y-2, 4,4);
return line.intersects(x - hitThreshold, y - hitThreshold,
2 * hitThreshold, 2 * hitThreshold);
}
@Override
public GeoElement getGeoElement() {
return geo;
}
@Override
public void setGeoElement(GeoElement geo) {
this.geo = geo;
}
/**
* Returns the bounding box of this Drawable in screen coordinates.
*/
@Override
final public GRectangle getBounds() {
if (!geo.isDefined() || ((GeoNumeric) geo).isAbsoluteScreenLocActive()
|| !geo.isEuclidianVisible()) {
return null;
}
return line.getBounds();
}
final public GRectangle getBoundsForStylebarPosition() {
if (!geo.isDefined() || !geo.isEuclidianVisible()) {
return null;
}
return line.getBounds();
}
@Override
public boolean intersectsRectangle(GRectangle rect) {
return circle.intersects(rect) || line.intersects(rect);
}
private void updatePoint(double rwX, double rwY) {
this.coords[0] = rwX;
this.coords[1] = rwY;
labelVisible = geo.isLabelVisible();
// convert to screen
view.toScreenCoords(coords);
double xUL = (coords[0] - pointSize);
double yUL = (coords[1] - pointSize);
// circle might be needed at least for tracing
circle.setFrame(xUL, yUL, diameter, diameter);
if (xUL + diameter < 0 || xUL > view.getWidth() || yUL + diameter < 0
|| yUL > view.getHeight()) {
labelVisible = false;
}
// selection area
circleHighlight.setFrame(xUL - 2 * HIGHLIGHT_OFFSET,
yUL - HIGHLIGHT_OFFSET * 2,
hightlightDiameter + 2 * HIGHLIGHT_OFFSET,
hightlightDiameter + 2 * HIGHLIGHT_OFFSET);
circleOuter.setFrame(xUL - HIGHLIGHT_OFFSET, yUL - HIGHLIGHT_OFFSET,
hightlightDiameter, hightlightDiameter);
// draw trace
if (labelVisible) {
labelDesc = geo.getLabelDescription();
xLabel = (int) Math.round(coords[0] + 4);
yLabel = (int) Math.round(yUL - pointSize);
addLabelOffsetEnsureOnScreen(view.getFontPoint());
}
}
@Override
public BoundingBox getBoundingBox() {
// TODO Auto-generated method stub
return null;
}
@Override
public void updateBoundingBox() {
// TODO Auto-generated method stub
}
// private void setPointSize(int pointSize) {
// if (this.pointSize != pointSize) {
// diameter = 2 * pointSize;
// HIGHLIGHT_OFFSET = pointSize / 2 + 1;
// // HIGHLIGHT_OFFSET = pointSize / 2 + 1;
// hightlightDiameter = diameter + 2 * HIGHLIGHT_OFFSET;
// }
//
// Log.error("pointSize = " + pointSize);
//
// this.pointSize = pointSize;
// }
}