package org.geogebra.common.geogebra3D.euclidian3D.draw;
import org.geogebra.common.geogebra3D.euclidian3D.EuclidianView3D;
import org.geogebra.common.geogebra3D.euclidian3D.Hitting;
import org.geogebra.common.geogebra3D.euclidian3D.openGL.PlotterBrush;
import org.geogebra.common.geogebra3D.euclidian3D.openGL.PlotterSurface;
import org.geogebra.common.geogebra3D.euclidian3D.openGL.Renderer;
import org.geogebra.common.geogebra3D.euclidian3D.openGL.Renderer.PickingType;
import org.geogebra.common.geogebra3D.kernel3D.algos.AlgoAnglePlanes;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.Matrix.Coords;
import org.geogebra.common.kernel.algos.AlgoAngle;
import org.geogebra.common.kernel.algos.AlgoAnglePointsND;
import org.geogebra.common.kernel.algos.AlgoElement;
import org.geogebra.common.kernel.geos.GeoAngle;
import org.geogebra.common.plugin.EuclidianStyleConstants;
/**
* @author ggb3D
*
* Drawable for GeoConic3D
*
*/
public class DrawAngle3D extends Drawable3DCurves {
private Coords labelCenter = new Coords(4);
private Coords vn2 = new Coords(4);
/**
* @param view3d
* the 3D view where the conic is drawn
* @param geo
* the angle to draw
*/
public DrawAngle3D(EuclidianView3D view3d, GeoAngle geo) {
super(view3d, geo);
}
@Override
protected void updateColors() {
updateAlpha();
setColorsOutlined();
}
@Override
public void drawGeometry(Renderer renderer) {
renderer.getGeometryManager().draw(getGeometryIndex());
}
// method used only if surface is not transparent
@Override
public void drawNotTransparentSurface(Renderer renderer) {
if (!isVisible()) {
return;
}
if (getAlpha() < 255) {
return;
}
setSurfaceHighlightingColor();
drawSurfaceGeometry(renderer);
}
private boolean angleVisible;
@Override
protected boolean isLabelVisible() {
return angleVisible && super.isLabelVisible();
}
private Coords[] drawCoords = null;
private Coords tmpCoords = new Coords(4), tmpCoords2, vn = new Coords(4),
v1 = new Coords(4), v2 = new Coords(4),
center = Coords.createInhomCoorsInD3();
private double size, angleValue, offset;
private boolean show90degrees;
@Override
protected boolean updateForItSelf() {
// update alpha value
updateColors();
initCoords();
Renderer renderer = getView3D().getRenderer();
GeoAngle angle = (GeoAngle) getGeoElement();
angleValue = angle.getDouble();
if (Kernel.isZero(angleValue)) { // nothing to display
setGeometryIndex(-1);
setSurfaceIndex(-1);
angleVisible = false;
return true;
}
size = angle.getArcSize() / getView3D().getScale();
double labelRadius = 1;
angleVisible = true;
AlgoElement algo = angle.getDrawAlgorithm();
if (algo instanceof AlgoAngle) {
if (!((AlgoAngle) algo).getCoordsInD3(drawCoords)) {
setGeometryIndex(-1);
setSurfaceIndex(-1);
angleVisible = false;
return true;
}
vn.setValues(((AlgoAngle) algo).getVn(), 3);
center.setValues(drawCoords[0], 3);
center.setW(1);
if (algo instanceof AlgoAnglePlanes) { // draw angle at center of
// the screen
double[] minmax = getView3D().getIntervalClippedLarge(
new double[] { Double.NEGATIVE_INFINITY,
Double.POSITIVE_INFINITY },
center, vn);
center.setAdd(center,
tmpCoords.setMul(vn, (minmax[0] + minmax[1]) / 2));
}
v1.setValues(drawCoords[1], 3);
v1.calcNorm();
double l1 = v1.getNorm();
v1.mulInside3(1 / l1);
v2.setValues(drawCoords[2], 3);
v2.calcNorm();
double l2 = v2.getNorm();
v2.mulInside3(1 / l2);
switch (angle.getAngleStyle()) {
default:
case ANTICLOCKWISE:
//no adjustment
break;
case NOTREFLEX:
if (angle.getRawAngle() > Math.PI) {
vn.mulInside3(-1);
}
break;
case ISREFLEX:
if (angle.getRawAngle() < Math.PI) {
vn.mulInside3(-1);
}
break;
}
vn2.setCrossProduct(vn, v1);
double a2 = angleValue / 2;
labelCenter.setAdd(tmpCoords.setMul(v1, Math.cos(a2)),
labelCenter.setMul(v2, Math.sin(a2)));
// size < points distances / 2
if (algo instanceof AlgoAnglePointsND) {
double l = Math.min(l1, l2) / 2;
if (size > l) {
size = l;
}
}
labelRadius = size / 1.7;
labelCenter.mulInside3(labelRadius);
labelCenter.addInside(center);
// 90degrees
show90degrees = getView3D()
.getRightAngleStyle() != EuclidianStyleConstants.RIGHT_ANGLE_STYLE_NONE
&& angle.isEmphasizeRightAngle()
&& Kernel.isEqual(angleValue, Kernel.PI_HALF);
// outline
PlotterBrush brush = renderer.getGeometryManager().getBrush();
PlotterSurface surface = renderer.getGeometryManager().getSurface();
brush.start(getReusableGeometryIndex());
brush.setThickness(getGeoElement().getLineThickness(),
(float) getView3D().getScale());
if (show90degrees) {
switch (getView3D().getRightAngleStyle()) {
default:
case EuclidianStyleConstants.RIGHT_ANGLE_STYLE_SQUARE:
size *= 0.7071067811865;
offset = 0;
brush.setAffineTexture(0.5f, 0.25f);
// segments
if (tmpCoords2 == null) {
tmpCoords2 = new Coords(4);
}
brush.segment(center, tmpCoords.setAdd(center,
tmpCoords.setMul(v1, size)));
tmpCoords2.set(tmpCoords);
brush.segment(tmpCoords,
tmpCoords2.addInsideMul(v2, size));
brush.segment(tmpCoords.setAdd(center,
tmpCoords.setMul(v2, size)), tmpCoords2);
brush.segment(center, tmpCoords);
setGeometryIndex(brush.end());
break;
case EuclidianStyleConstants.RIGHT_ANGLE_STYLE_DOT:
// arc
brush.setAffineTexture(0f, 0f);
brush.arc(center, v1, v2, size, 0, angleValue, 60);
brush.setAffineTexture(0.5f, 0.25f);
// segments
brush.segment(center, tmpCoords.setAdd(center,
tmpCoords.setMul(v1, size)));
brush.segment(center, tmpCoords.setAdd(center,
tmpCoords.setMul(v2, size)));
// dot (use surface plotter)
surface.drawSphere(labelCenter, 2.5 * brush.getThickness(),
16);
setGeometryIndex(brush.end());
break;
case EuclidianStyleConstants.RIGHT_ANGLE_STYLE_L:
size *= 0.7071067811865;
offset = size * 0.4;
brush.setAffineTexture(0.5f, 0.25f);
// segments
if (tmpCoords2 == null) {
tmpCoords2 = new Coords(4);
}
tmpCoords2.setAdd(center,
tmpCoords2.setAdd(tmpCoords.setMul(v1, offset),
tmpCoords2.setMul(v2, offset)));
brush.segment(tmpCoords2, tmpCoords.setAdd(tmpCoords2,
tmpCoords.setMul(v1, size)));
brush.segment(tmpCoords2, tmpCoords.setAdd(tmpCoords2,
tmpCoords.setMul(v2, size)));
setGeometryIndex(brush.end());
break;
}
} else {
// arc
brush.setAffineTexture(0f, 0f);
brush.arc(center, v1, vn2, size, 0, angleValue, 60);
brush.setAffineTexture(0.5f, 0.25f);
// segments
brush.segment(center,
tmpCoords.setAdd(center, tmpCoords.setMul(v1, size)));
brush.segment(center,
tmpCoords.setAdd(center, tmpCoords.setMul(v2, size)));
setGeometryIndex(brush.end());
}
// surface
if (show90degrees) {
switch (getView3D().getRightAngleStyle()) {
default:
case EuclidianStyleConstants.RIGHT_ANGLE_STYLE_SQUARE:
surface.start(getReusableSurfaceIndex());
surface.parallelogram(center, v1, v2, size, size);
setSurfaceIndex(surface.end());
break;
case EuclidianStyleConstants.RIGHT_ANGLE_STYLE_DOT:
surface.start(getReusableSurfaceIndex());
surface.ellipsePart(center, v1, v2, size, size, 0,
angleValue);
setSurfaceIndex(surface.end());
break;
case EuclidianStyleConstants.RIGHT_ANGLE_STYLE_L:
setSurfaceIndex(-1);
break;
}
} else {
surface.start(getReusableSurfaceIndex());
surface.ellipsePart(center, v1, vn2, size, size, 0, angleValue);
setSurfaceIndex(surface.end());
}
}
return true;
}
private void initCoords() {
if (drawCoords != null) {
return;
}
drawCoords = new Coords[] { new Coords(4), new Coords(4),
new Coords(4) };
}
@Override
protected void updateForView() {
if (getView3D().viewChangedByZoom() // update only if zoom occurred
|| ((GeoAngle) getGeoElement())
.getParentAlgorithm() instanceof AlgoAnglePlanes) {
updateForItSelf();
setLabelWaitForUpdate();
}
}
@Override
public int getPickOrder() {
return DRAW_PICK_ORDER_PATH;
}
@Override
public void addToDrawable3DLists(Drawable3DLists lists) {
super.addToDrawable3DLists(lists);
addToDrawable3DLists(lists, DRAW_TYPE_SURFACES);
}
@Override
public void removeFromDrawable3DLists(Drawable3DLists lists) {
super.removeFromDrawable3DLists(lists);
removeFromDrawable3DLists(lists, DRAW_TYPE_SURFACES);
}
private void drawSurfaceGeometry(Renderer renderer) {
renderer.setLayer(getLayer() + 1); // +1f to avoid z-fighting with
// planes and polygons
renderer.getGeometryManager().draw(getSurfaceIndex());
renderer.setLayer(0);
}
@Override
public void drawTransp(Renderer renderer) {
if (!isVisible()) {
return;
}
if (!hasTransparentAlpha()) {
return;
}
setSurfaceHighlightingColor();
drawSurfaceGeometry(renderer);
}
@Override
public void drawHiding(Renderer renderer) {
if (!isVisible()) {
return;
}
if (!hasTransparentAlpha()) {
return;
}
drawSurfaceGeometry(renderer);
}
@Override
public Coords getLabelPosition() {
return labelCenter;
}
@Override
protected void updateLabel() {// TODO remove this and implement all angle
// cases
if (labelCenter != null) {
super.updateLabel();
}
}
@Override
protected void updateLabelPosition() {
if (labelCenter != null) {
super.updateLabelPosition();
}
}
@Override
protected float getLabelOffsetX() {
return super.getLabelOffsetX() - 3;
}
@Override
protected float getLabelOffsetY() {
return super.getLabelOffsetY() + 5;
}
@Override
public boolean hit(Hitting hitting) {
if (waitForReset) { // prevent NPE
return false;
}
if (!angleVisible) {
return false;
}
hitting.origin.projectPlaneInPlaneCoords(v1, vn2, hitting.direction,
center, tmpCoords);
if (Kernel.isZero(tmpCoords.getW())) {
return false;
}
double x = tmpCoords.getX();
double y = tmpCoords.getY();
if (show90degrees) {
switch (getView3D().getRightAngleStyle()) {
case EuclidianStyleConstants.RIGHT_ANGLE_STYLE_SQUARE:
case EuclidianStyleConstants.RIGHT_ANGLE_STYLE_L:
if (x < offset || x > size + offset || y < offset
|| y > size + offset) {
return false;
}
double z = tmpCoords.getZ();
setZPick(z, z);
setPickingType(PickingType.POINT_OR_CURVE);
return true;
}
}
double d = Math.sqrt(x * x + y * y);
if (d > size) {
return false;
}
double a = Math.atan2(y, x);
if (a < 0) {
a += Math.PI * 2;
}
if (a > angleValue) {
return false;
}
double z = tmpCoords.getZ();
setZPick(z, z);
setPickingType(PickingType.POINT_OR_CURVE);
return true;
}
}