/*
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.
*/
package org.geogebra.common.kernel.geos;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.PathMover;
import org.geogebra.common.kernel.PathMoverGeneric;
import org.geogebra.common.kernel.PathParameter;
import org.geogebra.common.kernel.Transform;
import org.geogebra.common.kernel.Matrix.Coords;
import org.geogebra.common.kernel.algos.AlgoConicPartCircumcircle;
import org.geogebra.common.kernel.algos.AlgoElement;
import org.geogebra.common.kernel.algos.AlgoJoinPointsRay;
import org.geogebra.common.kernel.algos.AlgoRayPointVector;
import org.geogebra.common.kernel.algos.AlgoTranslate;
import org.geogebra.common.kernel.algos.AlgoUnitVectorLine;
import org.geogebra.common.kernel.kernelND.GeoConicNDConstants;
import org.geogebra.common.kernel.kernelND.GeoElementND;
import org.geogebra.common.kernel.kernelND.GeoPointND;
import org.geogebra.common.kernel.kernelND.GeoRayND;
import org.geogebra.common.kernel.kernelND.GeoVectorND;
import org.geogebra.common.plugin.GeoClass;
/**
* @author Markus Hohenwarter
*/
final public class GeoRay extends GeoLine implements LimitedPath, GeoRayND {
private boolean allowOutlyingIntersections = false;
private boolean keepTypeOnGeometricTransform = true;
/**
* Creates ray with start point A.
*
* @param c
* construction
* @param A
* start point
*/
public GeoRay(Construction c, GeoPoint A) {
this(c);
setStartPoint(A);
}
/**
* Creates new ray
*
* @param c
* construction
*/
public GeoRay(Construction c) {
super(c);
setConstructionDefaults();
}
/**
* Copy constructor
*
* @param ray
* template ray
*/
public GeoRay(GeoRay ray) {
this(ray.cons);
set(ray);
}
@Override
public GeoClass getGeoClassType() {
return GeoClass.RAY;
}
@Override
public GeoLine copy() {
return new GeoRay(this);
}
@Override
public GeoElement copyInternal(Construction cons1) {
GeoRay ray = new GeoRay(cons1,
(GeoPoint) startPoint.copyInternal(cons1));
ray.set(this);
return ray;
}
@Override
public void set(GeoElementND geo) {
super.set(geo);
if (!geo.isGeoRay()) {
return;
}
GeoRay ray = (GeoRay) geo;
keepTypeOnGeometricTransform = ray.keepTypeOnGeometricTransform;
startPoint = (GeoPoint) GeoLine.updatePoint(cons, startPoint,
ray.startPoint);
// Need to adjust the second defining object too, see #3770
if (getParentAlgorithm() instanceof AlgoJoinPointsRay
&& geo.getParentAlgorithm() instanceof AlgoJoinPointsRay) {
((AlgoJoinPointsRay) getParentAlgorithm()).getQ()
.set(((AlgoJoinPointsRay) geo.getParentAlgorithm()).getQ());
} else if (getParentAlgorithm() instanceof AlgoRayPointVector
&& geo.getParentAlgorithm() instanceof AlgoRayPointVector) {
((AlgoRayPointVector) getParentAlgorithm()).getv().set(
((AlgoRayPointVector) geo.getParentAlgorithm()).getv());
}
}
/**
* Sets this ray using direction line and start point
*
* @param s
* start point
* @param direction
* line
*/
public void set(GeoPoint s, GeoVec3D direction) {
super.set(direction);
setStartPoint(s);
}
@Override
public void setVisualStyle(GeoElement geo) {
super.setVisualStyle(geo);
if (geo.isGeoRay()) {
GeoRay ray = (GeoRay) geo;
allowOutlyingIntersections = ray.allowOutlyingIntersections;
}
}
/*
* Path interface
*/
@Override
public void pointChanged(GeoPointND P) {
super.pointChanged(P);
// ensure that the point doesn't get outside the ray
// i.e. ensure 0 <= t
PathParameter pp = P.getPathParameter();
if (pp.t < 0.0) {
P.setCoords2D(startPoint.x, startPoint.y, startPoint.z);
P.updateCoordsFrom2D(false, null);
pp.t = 0.0;
}
}
@Override
public void pathChanged(GeoPointND PI) {
// if kernel doesn't use path/region parameters, do as if point changed
// its coords
if (!getKernel().usePathAndRegionParameters(PI)) {
pointChanged(PI);
return;
}
GeoPoint P = (GeoPoint) PI;
PathParameter pp = P.getPathParameter();
if (pp.t < 0.0) {
pp.t = 0;
}
// calc point for given parameter
P.x = startPoint.inhomX + pp.t * y;
P.y = startPoint.inhomY - pp.t * x;
P.z = 1.0;
}
@Override
public boolean allowOutlyingIntersections() {
return allowOutlyingIntersections;
}
@Override
public void setAllowOutlyingIntersections(boolean flag) {
allowOutlyingIntersections = flag;
}
@Override
public boolean keepsTypeOnGeometricTransform() {
return keepTypeOnGeometricTransform;
}
@Override
public void setKeepTypeOnGeometricTransform(boolean flag) {
keepTypeOnGeometricTransform = flag;
}
@Override
final public boolean isLimitedPath() {
return true;
}
@Override
public boolean isIntersectionPointIncident(GeoPoint p, double eps) {
if (allowOutlyingIntersections) {
return isOnFullLine(p, eps);
}
return isOnPath(p, eps);
}
/**
* Returns the smallest possible parameter value for this path (may be
* Double.NEGATIVE_INFINITY)
*
* @return smallest possible parameter
*/
@Override
public double getMinParameter() {
return 0;
}
/**
* Returns the largest possible parameter value for this path (may be
* Double.POSITIVE_INFINITY)
*
* @return largest possible parameter
*/
@Override
public double getMaxParameter() {
return Double.POSITIVE_INFINITY;
}
@Override
public PathMover createPathMover() {
return new PathMoverGeneric(this);
}
/**
* returns all class-specific xml tags for saveXML
*/
@Override
protected void getXMLtags(StringBuilder sb) {
super.getXMLtags(sb);
// allowOutlyingIntersections
sb.append("\t<outlyingIntersections val=\"");
sb.append(allowOutlyingIntersections);
sb.append("\"/>\n");
// keepTypeOnGeometricTransform
sb.append("\t<keepTypeOnTransform val=\"");
sb.append(keepTypeOnGeometricTransform);
sb.append("\"/>\n");
}
/**
* Creates a new ray using a geometric transform.
*
* @param t
* transform
*/
@Override
public GeoElement[] createTransformedObject(Transform t,
String transformedLabel) {
AlgoElement parent = keepTypeOnGeometricTransform ? getParentAlgorithm()
: null;
// CREATE RAY
if (parent instanceof AlgoJoinPointsRay) {
// transform points
AlgoJoinPointsRay algo = (AlgoJoinPointsRay) parent;
GeoPointND[] points = { algo.getP(), algo.getQ() };
points = t.transformPoints(points);
if (t.isAffine()) {
GeoElement ray = (GeoElement) kernel.rayND(transformedLabel,
points[0], points[1]);
ray.setVisualStyleForTransformations(this);
GeoElement[] geos = { ray, (GeoElement) points[0],
(GeoElement) points[1] };
return geos;
}
GeoPoint inf = new GeoPoint(cons);
inf.setCoords(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY,
1);
inf = (GeoPoint) t.doTransform(inf);
AlgoConicPartCircumcircle ae = new AlgoConicPartCircumcircle(cons,
Transform.transformedGeoLabel(this), (GeoPoint) points[0],
(GeoPoint) points[1], inf,
GeoConicNDConstants.CONIC_PART_ARC);
cons.removeFromAlgorithmList(ae);
GeoElement arc = ae.getConicPart();// GeoConicPart
arc.setVisualStyleForTransformations(this);
GeoElement[] geos = { arc, (GeoElement) points[0],
(GeoElement) points[1] };
return geos;
} else if (parent instanceof AlgoRayPointVector) {
// transform startpoint
GeoPointND[] points = { getStartPoint() };
points = t.transformPoints(points);
boolean oldSuppressLabelCreation = cons.isSuppressLabelsActive();
cons.setSuppressLabelCreation(true);
AlgoUnitVectorLine ad = new AlgoUnitVectorLine(cons, this, false);
cons.removeFromAlgorithmList(ad);
GeoVectorND direction = ad.getVector();
if (t.isAffine()) {
direction = (GeoVector) t.doTransform(direction);
cons.setSuppressLabelCreation(oldSuppressLabelCreation);
// ray through transformed point with direction of transformed
// line
GeoElement ray = kernel.getAlgoDispatcher().ray(
transformedLabel, (GeoPoint) points[0],
(GeoVector) direction);
ray.setVisualStyleForTransformations(this);
GeoElement[] geos = new GeoElement[] { ray,
(GeoElement) points[0] };
return geos;
}
AlgoTranslate at = new AlgoTranslate(cons, getStartPoint(),
(GeoVector) direction);
cons.removeFromAlgorithmList(at);
GeoPoint thirdPoint = (GeoPoint) at.getResult();
GeoPoint inf = new GeoPoint(cons);
inf.setCoords(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY,
1);
GeoPointND[] points2 = new GeoPointND[] { thirdPoint, inf };
points2 = t.transformPoints(points2);
cons.setSuppressLabelCreation(oldSuppressLabelCreation);
AlgoConicPartCircumcircle ae = new AlgoConicPartCircumcircle(cons,
Transform.transformedGeoLabel(this), (GeoPoint) points[0],
(GeoPoint) points2[0], (GeoPoint) points2[1],
GeoConicNDConstants.CONIC_PART_ARC);
GeoElement arc = ae.getConicPart();// GeoConicPart
arc.setVisualStyleForTransformations(this);
GeoElement[] geos = { arc, (GeoElement) points[0] };
return geos;
} else {
// create LINE
GeoElement transformedLine = t.getTransformedLine(this);
transformedLine.setLabel(transformedLabel);
GeoElement[] ret = { transformedLine };
return ret;
}
}
@Override
public boolean isGeoRay() {
return true;
}
// Michael Borcherds 2008-04-30
@Override
final public boolean isEqual(GeoElementND geo) {
// return false if it's a different type, otherwise check direction and
// start point
if (!geo.isGeoRay()) {
return false;
}
return isSameDirection((GeoLine) geo)
&& ((GeoRay) geo).getStartPoint().isEqual(getStartPoint());
}
private Coords pnt2D;
@Override
public boolean isOnPath(Coords Pnd, double eps) {
if (pnt2D == null) {
pnt2D = new Coords(3);
}
pnt2D.setCoordsIn2DView(Pnd);
if (!isOnFullLine2D(pnt2D, eps)) {
return false;
}
return respectLimitedPath(pnt2D, eps);
}
@Override
public boolean respectLimitedPath(Coords Pnd, double eps) {
if (pnt2D == null) {
pnt2D = new Coords(3);
}
pnt2D.setCoordsIn2DView(Pnd);
PathParameter pp = getTempPathParameter();
doPointChanged(pnt2D, pp);
double t = pp.getT();
return t >= -eps;
}
@Override
public boolean isAllEndpointsLabelsSet() {
return startPoint.isLabelSet();
}
@Override
public boolean respectLimitedPath(double parameter) {
return Kernel.isGreaterEqual(parameter, 0);
}
@Override
public GeoElement copyFreeRay() {
GeoPoint startPoint1 = (GeoPoint) getStartPoint().copyInternal(cons);
double[] direction = new double[3];
getDirection(direction);
GeoVector directionVec = new GeoVector(cons);
directionVec.setCoords(direction);
AlgoRayPointVector algo = new AlgoRayPointVector(cons, null,
startPoint1, directionVec);
return algo.getRay();
}
}