package org.geogebra.common.geogebra3D.euclidianForPlane;
import java.util.ArrayList;
import org.geogebra.common.awt.GColor;
import org.geogebra.common.awt.GGraphics2D;
import org.geogebra.common.euclidian.DrawableND;
import org.geogebra.common.euclidian.EuclidianView;
import org.geogebra.common.euclidian.draw.DrawAngle;
import org.geogebra.common.euclidian.draw.DrawParametricCurve;
import org.geogebra.common.euclidian3D.EuclidianView3DInterface;
import org.geogebra.common.euclidianForPlane.EuclidianViewForPlaneCompanionInterface;
import org.geogebra.common.geogebra3D.euclidian3D.EuclidianView3D;
import org.geogebra.common.geogebra3D.euclidianFor3D.CurveEvaluableForPlane;
import org.geogebra.common.geogebra3D.euclidianFor3D.DrawAngleFor3D;
import org.geogebra.common.geogebra3D.euclidianFor3D.EuclidianViewFor3DCompanion;
import org.geogebra.common.geogebra3D.kernel3D.geos.GeoCurveCartesian3D;
import org.geogebra.common.geogebra3D.main.App3DCompanion;
import org.geogebra.common.geogebra3D.main.settings.EuclidianSettingsForPlane;
import org.geogebra.common.gui.layout.DockPanel;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.Matrix.CoordMatrix;
import org.geogebra.common.kernel.Matrix.CoordMatrix4x4;
import org.geogebra.common.kernel.Matrix.CoordSys;
import org.geogebra.common.kernel.Matrix.Coords;
import org.geogebra.common.kernel.algos.AlgoElement;
import org.geogebra.common.kernel.geos.GeoAngle;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.kernelND.GeoDirectionND;
import org.geogebra.common.kernel.kernelND.GeoElementND;
import org.geogebra.common.kernel.kernelND.GeoPlaneND;
import org.geogebra.common.kernel.kernelND.GeoPointND;
import org.geogebra.common.kernel.kernelND.ViewCreator;
import org.geogebra.common.main.settings.AbstractSettings;
import org.geogebra.common.main.settings.EuclidianSettings;
import org.geogebra.common.util.debug.Log;
/**
* Companion for view for plane specific stuff
*
* @author mathieu
*
*/
public class EuclidianViewForPlaneCompanion extends EuclidianViewFor3DCompanion
implements EuclidianViewForPlaneCompanionInterface {
private ViewCreator plane;
/**
* constructor
*
* @param view
* view attached
*/
public EuclidianViewForPlaneCompanion(EuclidianView view) {
super(view);
}
private CoordMatrix4x4 transform;
private boolean initViewJustCreated = false;
/**
* @param defPlane
* planar object
*/
public void initView(ViewCreator defPlane) {
setPlane(defPlane);
if (settingsFromLoadFile) {
initViewJustCreated = false;
// view is created from file, only update matrices
updateOtherMatrices();
} else {
initViewJustCreated = true;
// view is created from scratch
updateMatrix();
// set coord system to fit 3D view
updateCenterAndOrientationRegardingView();
updateScaleRegardingView();
}
}
/**
* set the plane creator
*
* @param plane
* plane creator
*/
public void setPlane(ViewCreator plane) {
this.plane = plane;
}
/**
* @return defining plane
*/
public ViewCreator getPlane() {
return plane;
}
/**
* update orientation of the view regarding 3D view
*/
public void updateScaleRegardingView() {
double newScale = view.getApplication().getEuclidianView3D()
.getXscale();
double w = view.getWidth() / 2.0;
double h = view.getHeight() / 2.0;
double dx = (w - view.getXZero()) * newScale / view.getXscale();
double dy = (h - view.getYZero()) * newScale / view.getYscale();
setCoordSystem(w - dx, h - dy, newScale, newScale);
}
private void setCoordSystem(double xZero, double yZero, double xscale,
double yscale) {
view.setCoordSystem(xZero, yZero, xscale, yscale);
}
private Coords tmpCoords = new Coords(4);
@Override
protected void updateSizeKeepDrawables() {
if (initViewJustCreated) {
// set coord system to fit 3D view
// previous set may fail due to width = height = 0
updateCenterAndOrientationRegardingView();
updateScaleRegardingView();
initViewJustCreated = false;
}
super.updateSizeKeepDrawables();
}
/**
* update center and orientation of the view regarding 3D view
*/
public void updateCenterAndOrientationRegardingView() {
setTransformRegardingView();
updateMatrix();
EuclidianView3DInterface view3D = view.getApplication()
.getEuclidianView3D();
// coords of the bounding box center in the 3D view
Coords c = new Coords(-view3D.getXZero(), -view3D.getYZero(),
-view3D.getZZero(), 1);
// project it in this view coord sys
c.projectPlaneInPlaneCoords(getMatrix(), tmpCoords);
// take this projection for center
int x = view.toScreenCoordX(tmpCoords.getX());
int y = view.toScreenCoordY(tmpCoords.getY());
setCoordSystem(view.getWidth() / 2 - x + view.getXZero(),
view.getHeight() / 2 - y + view.getYZero(), view.getXscale(),
view.getYscale());
}
private CoordMatrix4x4 planeMatrix, transformedMatrix;
private CoordMatrix inverseTransformedMatrix;
@Override
public CoordMatrix getMatrix() {
return transformedMatrix;
}
@Override
public CoordMatrix getInverseMatrix() {
return inverseTransformedMatrix;
}
@Override
public void updateMatrix() {
if (!plane.isDefined()) {
// force plane matrix for Drawables creation
planeMatrix = CoordMatrix4x4.IDENTITY;
transformedMatrix = CoordMatrix4x4.IDENTITY;
inverseTransformedMatrix = CoordMatrix4x4.IDENTITY;
return;
}
if (transform == null) {
transform = CoordMatrix4x4.IDENTITY;
}
updateOtherMatrices();
}
private final void updateOtherMatrices() {
// // use continuity
// Coords vx1 = Coords.UNDEFINED;
// if (transformedMatrix!=null){
// vx1 = transformedMatrix.getVx();
// }
planeMatrix = plane.getCoordSys().getDrawingMatrix();
transformedMatrix = planeMatrix.mul(transform);// transform.mul(planeMatrix);
// Coords vx2 = transformedMatrix.getVx();
// Log.debug("\nvx1=\n"+vx1+"\nvx2=\n"+vx2);
// double dxx = vx1.dotproduct(vx2);
// if (dxx < 0){
// transformRotate += 180;
// if (transformRotate > 180){
// transformRotate -= 360;
// }
// setTransform();
// transformedMatrix = planeMatrix.mul(transform);
// }
inverseTransformedMatrix = transformedMatrix.inverse();
}
private int transformMirror;
private int transformRotate;
@Override
public void setTransformRegardingView() {
Coords directionView3D = ((EuclidianView3D) view.getApplication()
.getEuclidianView3D()).getViewDirection();
CoordMatrix toScreenMatrix = ((EuclidianView3D) view.getApplication()
.getEuclidianView3D()).getToScreenMatrix();
// front or back view
double p = plane.getCoordSys().getNormal().dotproduct(directionView3D);
if (p <= 0) {
transform = CoordMatrix4x4.IDENTITY;
transformMirror = 1;
} else {
transform = CoordMatrix4x4.MIRROR_Y;
transformMirror = -1;
}
// Application.debug("transform=\n"+transform);
// CoordMatrix m = toScreenMatrix.mul(planeMatrix.mul(transform));
CoordMatrix m = toScreenMatrix.mul(planeMatrix);
// Log.debug("m=\n"+m);
double vXx = m.get(1, 1);
double vXy = m.get(2, 1);
double vYx = m.get(1, 2);
double vYy = m.get(2, 2);
transformRotate = 0;
// is vX vertical and vY horizontal ?
if (Math.abs(vXy) > Math.abs(vXx) && Math.abs(vYx) > Math.abs(vYy)) {
if (vYx * transformMirror >= 0) {
transform = CoordMatrix4x4.ROTATION_OZ_90.mul(transform);
transformRotate = 90;
} else {
transform = CoordMatrix4x4.ROTATION_OZ_M90.mul(transform);
transformRotate = -90;
}
// check vX direction
} else if (vXx * transformMirror < 0) {
transform = CoordMatrix4x4.MIRROR_O.mul(transform);
transformRotate = 180;
}
updateMatrix();
// TODO only if new matrix != old matrix
view.updateAllDrawables(true);
}
/**
* set transform from values
*/
public void setTransform() {
if (transformMirror == 1) {
transform = CoordMatrix4x4.IDENTITY;
} else {
transform = CoordMatrix4x4.MIRROR_Y;
}
if (transformRotate == 90) {
transform = CoordMatrix4x4.ROTATION_OZ_90.mul(transform);
} else if (transformRotate == -90) {
transform = CoordMatrix4x4.ROTATION_OZ_M90.mul(transform);
} else if (transformRotate == 180) {
transform = CoordMatrix4x4.MIRROR_O.mul(transform);
}
}
@Override
public void getXML(StringBuilder sbxml, boolean asPreference) {
if (!view.isShowing()) {
// we don't want to store view for plane that is not showing
Log.debug("view is not showing");
return;
}
view.startXML(sbxml, asPreference);
// transform
sbxml.append("\t<transformForPlane mirror=\"");
sbxml.append(transformMirror == -1);
sbxml.append("\" rotate=\"");
sbxml.append(transformRotate);
sbxml.append("\"/>\n");
view.endXML(sbxml);
}
private boolean settingsFromLoadFile = false;
@Override
public void settingsChanged(AbstractSettings settings) {
super.settingsChanged(settings);
EuclidianSettingsForPlane evs = (EuclidianSettingsForPlane) settings;
// transform
transformMirror = 1;
if (evs.getMirror()) {
transformMirror = -1;
}
transformRotate = evs.getRotate();
setTransform();
settingsFromLoadFile = evs.isFromLoadFile();
evs.setFromLoadFile(false);
}
@Override
protected DrawAngle newDrawAngle(GeoAngle geo) {
return new DrawAngleFor3D(view, geo);
}
@Override
public boolean isDefault2D() {
return false;
}
@Override
public void updateForPlane() {
updateMatrix();
view.updateAllDrawables(true);
}
@Override
public boolean isVisibleInThisView(GeoElementND geo) {
// prevent not implemented type to be displayed (TODO remove)
switch (geo.getGeoClassType()) {
case POINT:
case POINT3D:
case SEGMENT:
case SEGMENT3D:
case LINE:
case LINE3D:
case RAY:
case RAY3D:
case VECTOR:
case VECTOR3D:
case POLYGON:
case POLYGON3D:
case POLYLINE:
case POLYLINE3D:
case CONIC:
case CONIC3D:
case CONICSECTION:
case CONICPART:
case ANGLE3D:
case TEXT:
case LOCUS:
case IMPLICIT_POLY:
case CURVE_CARTESIAN3D:
case LIST:
return geo.isVisibleInViewForPlane();
case ANGLE:
if (geo.isIndependent()) { // no slider in view for plane (for now)
return false;
}
return geo.isVisibleInViewForPlane();
default:
return false;
}
}
@Override
public Coords getCoordsForView(Coords coords) {
return coords.projectPlaneWithInverseMatrix(getInverseMatrix());
}
/**
* @param x
* x coord in view plane
* @param y
* y coord in view plane
*/
@Override
public void getCoordsFromView(double x, double y, Coords c) {
c.setMul(getMatrix(), new Coords(x, y, 0, 1));
}
@Override
public String getFromPlaneString() {
if (plane == null) {
return "";
}
return plane.toGeoElement().getLabel(StringTemplate.defaultTemplate);
}
@Override
public String getTranslatedFromPlaneString() {
if (plane == null) {
return "";
}
if (plane instanceof GeoPlaneND) {
return view.getApplication().getLocalization().getPlain("PlaneA",
((GeoElement) plane)
.getLabel(StringTemplate.defaultTemplate));
}
return view.getApplication().getLocalization().getPlain("PlaneFromA",
((GeoElement) plane).getLabel(StringTemplate.defaultTemplate));
}
@Override
public GeoPlaneND getPlaneContaining() {
if (plane instanceof GeoPlaneND) {
return (GeoPlaneND) plane;
}
return view.getKernel().getManager3D().Plane3D(plane);
}
@Override
public GeoDirectionND getDirection() {
return plane;
}
@Override
public boolean goToZPlus(Coords v) {
double dot = v.dotproduct(getDirection().getDirectionInD3());
return (dot > 0) ^ (transformMirror == -1);
}
/**
*
* @param clockwise
* input orientation
* @return clockwise (resp. not(clockwise)) if clockwise is displayed as it
* in the view
*/
public boolean viewOrientationForClockwise(boolean clockwise) {
if (transformMirror == 1) {
return clockwise;
}
return !clockwise;
}
@Override
public boolean isMoveable(GeoElement geo) {
if (hasForParent(geo)) {
return false;
}
return super.isMoveable(geo);
}
/**
* @param geo
* geo
* @return true if the geo is parent of the view
*/
public boolean hasForParent(GeoElement geo) {
return geo.isParentOf(plane);
}
@Override
public ArrayList<GeoPointND> getFreeInputPoints(AlgoElement algoParent) {
ArrayList<GeoPointND> list = algoParent.getFreeInputPoints();
ArrayList<GeoPointND> ret = new ArrayList<GeoPointND>();
for (GeoPointND p : list) {
if (!hasForParent((GeoElement) p)) {
ret.add(p);
}
}
return ret;
}
@Override
public void getXMLid(StringBuilder sbxml) {
sbxml.append("\t<viewId ");
sbxml.append("plane=\"");
sbxml.append(((GeoElement) plane).getLabelSimple());
sbxml.append("\"");
sbxml.append("/>\n");
}
@Override
public void paint(GGraphics2D g2) {
if (!plane.isDefined()) {
// draws the view in gray
g2.setColor(GColor.LIGHT_GRAY);
g2.fillRect(0, 0, view.getWidth(), view.getHeight());
return;
}
super.paint(g2);
}
/**
* add all existing geos to this view
*/
public void addExistingGeos() {
view.getKernel().notifyAddAll(view);
}
@Override
public void attachView() {
view.getKernel().attach(view);
}
@Override
public boolean showGrid(boolean show) {
EuclidianSettings settings = view.getApplication().getSettings()
.getEuclidianForPlane(getFromPlaneString());
if (settings != null) {
settings.setShowGridSetting(show);
}
return super.showGrid(show);
}
private DockPanel panel;
/**
* set the dock panel of the view
*
* @param panel
* dock panel containing
*/
public void setDockPanel(DockPanel panel) {
this.panel = panel;
this.id = panel.getViewId();
}
/**
*
* @return true if the panel is visible
*/
public boolean isPanelVisible() {
return panel.isVisible();
}
private int id;
@Override
public int getId() {
return id;
}
/**
* remove the view when the creator doens't exist anymore
*/
@Override
public void doRemove() {
removeFromGuiAndKernel();
((App3DCompanion) view.getApplication().getCompanion())
.removeEuclidianViewForPlaneFromList(this);
}
/**
* remove panel from gui and view from kernel
*/
public void removeFromGuiAndKernel() {
panel.closePanel();
view.getApplication().getGuiManager().getLayout().getDockManager()
.unRegisterPanel(panel);
view.getKernel().detach(view);
}
/**
* update all drawables
*
* @param repaint
* says if repaint is needed
*/
@Override
public void updateAllDrawables(boolean repaint) {
view.updateAllDrawables(repaint);
}
@Override
protected DrawableND newDrawParametricCurve(GeoCurveCartesian3D geo) {
return new DrawParametricCurve(view,
new CurveEvaluableForPlane(geo, this));
}
@Override
public boolean isInPlane(CoordSys sys) {
return sys == null || sys.getEquationVector()
.isEqual(plane.getCoordSys().getEquationVector());
}
}