package org.geogebra.common.euclidian;
import java.util.ArrayList;
import org.geogebra.common.awt.GPoint;
import org.geogebra.common.kernel.algos.AlgoCircleThreePoints;
import org.geogebra.common.kernel.geos.GeoConic;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoPoint;
import org.geogebra.common.kernel.geos.GeoPolygon;
import org.geogebra.common.kernel.geos.Test;
import org.geogebra.common.kernel.kernelND.GeoPointND;
import org.geogebra.common.main.App;
/**
* Freehand processor
*/
public class EuclidianPenFreehand extends EuclidianPen {
/**
* type that is expected to be created
*/
public enum ShapeType {
/** circle */
circleThreePoints,
/** normal polygon */
polygon,
/** polygon with two moveable points */
rigidPolygon,
/** polygon that can be moved by first point */
vectorPolygon;
}
private ShapeType expected = null;
/**
* @param app
* app
* @param view
* euclidian view
*/
public EuclidianPenFreehand(App app, EuclidianView view) {
super(app, view);
super.setFreehand(true);
}
/**
* will be ignored - always freehand
*/
@Override
public void setFreehand(boolean b) {
// don't do anything
}
/**
* @param expectedType
* defines the expected shape
*/
public void setExpected(ShapeType expectedType) {
this.expected = expectedType;
resetParameters();
switch (expected) {
case circleThreePoints:
CIRCLE_MAX_SCORE = 0.15;
CIRCLE_MIN_DET = 0.9;
break;
case polygon:
case rigidPolygon:
case vectorPolygon:
RECTANGLE_LINEAR_TOLERANCE = 0.25;
POLYGON_LINEAR_TOLERANCE = 0.25;
RECTANGLE_ANGLE_TOLERANCE = 17 * Math.PI / 180;
break;
}
}
@Override
public boolean handleMouseReleasedForPenMode(boolean right, int x, int y) {
return checkExpectedShape(x, y);
}
/**
* Creates predicted shape if possible
*
* @param x
* x-coord of new point
* @param y
* y-coord of new point
*/
private boolean checkExpectedShape(int x, int y) {
initShapeRecognition(x, y);
switch (this.expected) {
case polygon:
case rigidPolygon:
case vectorPolygon:
return createPolygon();
case circleThreePoints:
return createCircle();
}
return false;
}
/**
* creates a circle if possible
*/
private boolean createCircle() {
GeoElement geoCircle;
if (tryCircleThroughExistingPoints() != null) {
return true;
} else if ((geoCircle = getCircleThreePoints()) != null) {
boolean recreate = false;
ArrayList<GeoPointND> list = new ArrayList<GeoPointND>();
for (GeoPointND geo : ((GeoConic) geoCircle).getPointsOnConic()) {
if (!geo.isLabelSet()) {
recreate = true;
geo.setLabel(null);
}
list.add(geo);
}
// the circle needs to be recreated to prevent errors in the XML
if (recreate) {
geoCircle.remove();
AlgoCircleThreePoints algo = new AlgoCircleThreePoints(
app.getKernel().getConstruction(), null, list.get(0),
list.get(1), list.get(2));
geoCircle = algo.getCircle();
geoCircle.updateRepaint();
}
return true;
}
resetInitialPoint();
return false;
}
/**
* tries to construct a circle through 3 existing points, null otherwise
*
* @return {@link GeoElement circle}
*/
private GeoElement tryCircleThroughExistingPoints() {
GeoElement circle = null;
ArrayList<GeoPoint> list = new ArrayList<GeoPoint>();
for (GPoint p : this.penPoints) {
this.view.setHits(p,
this.view.getEuclidianController().getDefaultEventType());
if (this.view.getHits().containsGeoPoint()) {
GeoPoint point = (GeoPoint) this.view.getHits()
.getFirstHit(Test.GEOPOINT);
if (!list.contains(point)) {
list.add(point);
}
}
}
if (list.size() >= 3) {
circle = this.app.getKernel().getAlgoDispatcher().Circle(null,
list.get(0), list.get(1), list.get(2));
}
return circle;
}
/**
* creates a polygon if possible
*/
private boolean createPolygon() {
GeoElement polygon = null;
int n = getPolygonal();
if (n > 1) { // if it's not a line
polygon = tryPolygon(n);
}
// Postprocessing
if (polygon != null) {
ArrayList<GeoPoint> list = new ArrayList<GeoPoint>();
for (GeoPointND point : ((GeoPolygon) polygon).getPoints()) {
if (point instanceof GeoPoint) {
list.add((GeoPoint) point);
}
}
if (list.size() == ((GeoPolygon) polygon).getPoints().length
&& expected != ShapeType.polygon) {
// true if all the points are GeoPoints, otherwise the
// original Polygon will not be deleted
polygon.remove();
if (expected == ShapeType.rigidPolygon) {
this.app.getKernel().rigidPolygon(null,
list.toArray(new GeoPoint[0]));
} else {
this.app.getKernel().vectorPolygon(null,
list.toArray(new GeoPoint[0]));
}
return true;
}
return true;
}
resetInitialPoint();
return false;
}
private void resetParameters() {
CIRCLE_MIN_DET = 0.95;
CIRCLE_MAX_SCORE = 0.10;
RECTANGLE_LINEAR_TOLERANCE = 0.20;
POLYGON_LINEAR_TOLERANCE = 0.20;
RECTANGLE_ANGLE_TOLERANCE = 15 * Math.PI / 180;
}
}