package org.geogebra.common.euclidian;
import org.geogebra.common.awt.GGraphics2D;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.main.Feature;
import org.geogebra.common.util.MyMath;
/**
* Helper class for drawing the grid
*
* @author zbynek
*
*/
public class DrawGrid {
private EuclidianView view;
/**
*
* @param euclidianView
* view
*/
public DrawGrid(EuclidianView euclidianView) {
view = euclidianView;
}
/**
*
* @param g2
* graphics
* @param xCrossPix
* x crossing pixel
* @param yCrossPix
* y crossing pixel
*/
protected void drawCartesianGrid(GGraphics2D g2, double xCrossPix,
double yCrossPix) {
if (view.getXaxisLog()) {
drawVerticalGridLog(g2, xCrossPix, yCrossPix);
} else {
drawVerticalGridLinear(g2, xCrossPix, yCrossPix);
}
// horizontal grid lines
if (view.getYaxisLog()) {
drawHorizontalGridLog(g2, xCrossPix, yCrossPix);
} else {
drawHorizontalGridLinear(g2, xCrossPix, yCrossPix);
}
}
private void drawHorizontalGridLinear(GGraphics2D g2, double xCrossPix1,
double yCrossPix1) {
double xCrossPix = xCrossPix1;
double yCrossPix = yCrossPix1;
double tickStepY = view.getYscale() * view.gridDistances[1];
double start = view.getYZero() % tickStepY;
double pix = start;
final double left = view.positiveAxes[0] ? xCrossPix : 0;
if (pix > (view.getHeight() - EuclidianView.SCREEN_BORDER)) {
pix -= tickStepY;
}
if (view.getApplication().has(Feature.TICK_NUMBERS_AT_EDGE)) {
// if xCrossPix less than the width of the view, grid won't be drawn
// to the right border at the case when the yAxis is offscreen on
// the right.
if (xCrossPix1 >= view.getWidth()) {
xCrossPix = view.getWidth() - Kernel.MIN_PRECISION;
} else {
// There will be some space for numbers, where grid won't be
// drawn, labelspace should contain the bigger width.
// See drawLineAvoidingLabelsH function.
double labelspace = (view.yLabelMaxWidthNeg > 0)
? view.yLabelMaxWidthNeg + 10
: view.yLabelMaxWidthPos + 10;
if (xCrossPix - labelspace <= 0) {
xCrossPix = labelspace;
}
}
}
final double yAxisEnd = (view.positiveAxes[1]
&& yCrossPix < view.getHeight()) ? yCrossPix : view.getHeight();
for (int j = 0; pix <= yAxisEnd; j++) {
// don't draw the grid line x=0 if the y-axis is showing
// or if it's too close (eg sticky axes)
if (!view.showAxes[0] || Math.abs(pix - yCrossPix) > 2d) {
if (view.axesLabelsPositionsY.contains(
Integer.valueOf((int) (pix + Kernel.MIN_PRECISION)))) {
// hits axis label, draw in 2 sections
drawLineAvoidingLabelsH(g2, left, pix, view.getWidth(), pix,
xCrossPix);
} else {
// not hitting axis label, just draw it
g2.drawStraightLine(left, pix, view.getWidth(), pix);
}
}
pix = start + (j * tickStepY);
}
}
private void drawHorizontalGridLog(GGraphics2D g2, double xCrossPix,
double yCrossPix) {
double tickStepY = view.getYscale() * view.gridDistances[1];
double start = view.getYZero() % tickStepY;
double pix = 0;
final double left = view.positiveAxes[0] ? xCrossPix : 0;
final double yAxisEnd = (view.positiveAxes[1]
&& yCrossPix < view.getHeight()) ? yCrossPix : view.getHeight();
double pow = MyMath.nextPrettyNumber(view.getYmin(), 1);
for (int j = 0; pix <= yAxisEnd; j++) {
// don't draw the grid line x=0 if the y-axis is showing
// or if it's too close (eg sticky axes)
pix = view.toScreenCoordYd(pow);
if (!view.showAxes[0] || Math.abs(pix - yCrossPix) > 2d) {
if (view.axesLabelsPositionsY.contains(
Integer.valueOf((int) (pix + Kernel.MIN_PRECISION)))) {
// hits axis label, draw in 2 sections
drawLineAvoidingLabelsH(g2, left, pix, view.getWidth(), pix,
xCrossPix);
} else {
// not hitting axis label, just draw it
g2.drawStraightLine(left, pix, view.getWidth(), pix);
}
}
pix = start + (j * tickStepY);
pow = pow * 10;
}
}
private void drawVerticalGridLinear(GGraphics2D g2, double xCrossPix,
double yCrossPix1) {
double yCrossPix = yCrossPix1;
if (view.getApplication().has(Feature.TICK_NUMBERS_AT_EDGE)) {
if (yCrossPix1 >= view.getHeight() - view.xLabelHeights - 5) {
// If the xAxis is offscreen on the bottom, or almost offscreen,
// numbers
// will be fixed at the bottom edge of view, and because of this
// grid won't be drawn there, there will be some space for the
// numbers. The position of this space depends on value of
// yCrossPix.
yCrossPix = view.getHeight() - view.xLabelHeights - 5;
} else if (yCrossPix1 <= 0) {
yCrossPix = 0 + Kernel.MIN_PRECISION;
}
}
// vertical grid lines
double tickStepX = view.getXscale() * view.gridDistances[0];
final double xAxisStart = (view.positiveAxes[0] && xCrossPix > 0)
? xCrossPix + (((view.getXZero() - xCrossPix) % tickStepX)
+ tickStepX) % tickStepX
: (view.getXZero() % tickStepX);
final double yAxisEnd = (view.positiveAxes[1]
&& yCrossPix < view.getHeight()) ? yCrossPix : view.getHeight();
final double bottom = view.positiveAxes[1] ? yAxisEnd
: view.getHeight();
double pix = xAxisStart;
if (pix < EuclidianView.SCREEN_BORDER) {
pix += tickStepX;
}
for (int i = 0; pix <= view.getWidth(); i++) {
// don't draw the grid line x=0 if the y-axis is showing
// or if it's too close (eg sticky axes)
if (!view.showAxes[1] || Math.abs(pix - xCrossPix) > 2d) {
if (view.axesLabelsPositionsX.contains(
Integer.valueOf((int) (pix + Kernel.MIN_PRECISION)))) {
// hits axis label, draw in 2 sections
drawLineAvoidingLabelsV(g2, pix, 0, pix, bottom, yCrossPix);
} else {
// not hitting axis label, just draw it
g2.drawStraightLine(pix, 0, pix, bottom);
}
}
pix = xAxisStart + (i * tickStepX);
}
}
private void drawVerticalGridLog(GGraphics2D g2, double xCrossPix,
double yCrossPix) {
// vertical grid lines
double tickStepX = view.getXscale() * view.gridDistances[0];
final double xAxisStart = (view.positiveAxes[0] && xCrossPix > 0)
? xCrossPix + (((view.getXZero() - xCrossPix) % tickStepX)
+ tickStepX) % tickStepX
: (view.getXZero() % tickStepX);
final double yAxisEnd = (view.positiveAxes[1]
&& yCrossPix < view.getHeight()) ? yCrossPix : view.getHeight();
final double bottom = view.positiveAxes[1] ? yAxisEnd
: view.getHeight();
double pix = 0;
double pow = MyMath.nextPrettyNumber(view.getYmin(), 1);
for (int i = 0; pix <= view.getWidth(); i++) {
// don't draw the grid line x=0 if the y-axis is showing
// or if it's too close (eg sticky axes)
pix = view.toScreenCoordXd(pow);
if (!view.showAxes[1] || Math.abs(pix - xCrossPix) > 2d) {
if (view.axesLabelsPositionsX.contains(
Integer.valueOf((int) (pix + Kernel.MIN_PRECISION)))) {
// hits axis label, draw in 2 sections
drawLineAvoidingLabelsV(g2, pix, 0, pix, bottom, yCrossPix);
} else {
// not hitting axis label, just draw it
g2.drawStraightLine(pix, 0, pix, bottom);
}
}
pow = pow * 10;
pix = xAxisStart + (i * tickStepX);
}
}
private void drawLineAvoidingLabelsH(GGraphics2D g2, double x1, double y1,
double x2, double y2, double xCrossPix) {
if (xCrossPix > x1 && xCrossPix < x2) {
// split in 2
g2.drawStraightLine(x1, y1,
xCrossPix - (view.toRealWorldCoordY(y1) > 0
? view.yLabelMaxWidthPos : view.yLabelMaxWidthNeg)
- 10,
y2);
g2.drawStraightLine(xCrossPix, y1, x2, y2);
} else {
g2.drawStraightLine(x1, y1, x2, y2);
}
}
private void drawLineAvoidingLabelsV(GGraphics2D g2, double x1, double y1,
double x2, double y2, double yCrossPix) {
if (yCrossPix > y1 && yCrossPix < y2) {
// split in 2
g2.drawStraightLine(x1, y1, x2, yCrossPix);
g2.drawStraightLine(x1, yCrossPix + view.xLabelHeights + 5, x2, y2);
} else {
g2.drawStraightLine(x1, y1, x2, y2);
}
}
}