package org.geogebra.common.geogebra3D.kernel3D.geos;
import org.geogebra.common.awt.GColor;
import org.geogebra.common.geogebra3D.kernel3D.transform.MirrorableAtPlane;
import org.geogebra.common.kernel.CircularDefinitionException;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.Matrix.CoordMatrix;
import org.geogebra.common.kernel.Matrix.Coords;
import org.geogebra.common.kernel.arithmetic.MyDouble;
import org.geogebra.common.kernel.arithmetic.NumberValue;
import org.geogebra.common.kernel.arithmetic.ValueType;
import org.geogebra.common.kernel.geos.Dilateable;
import org.geogebra.common.kernel.geos.GProperty;
import org.geogebra.common.kernel.geos.GeoBoolean;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoNumberValue;
import org.geogebra.common.kernel.geos.Transformable;
import org.geogebra.common.kernel.geos.Translateable;
import org.geogebra.common.kernel.kernelND.GeoConicND;
import org.geogebra.common.kernel.kernelND.GeoCoordSys2D;
import org.geogebra.common.kernel.kernelND.GeoDirectionND;
import org.geogebra.common.kernel.kernelND.GeoElementND;
import org.geogebra.common.kernel.kernelND.GeoLineND;
import org.geogebra.common.kernel.kernelND.GeoPointND;
import org.geogebra.common.kernel.kernelND.GeoQuadric3DLimitedInterface;
import org.geogebra.common.kernel.kernelND.GeoQuadricND;
import org.geogebra.common.kernel.kernelND.GeoQuadricNDConstants;
import org.geogebra.common.kernel.kernelND.GeoSegmentND;
import org.geogebra.common.kernel.kernelND.HasHeight;
import org.geogebra.common.kernel.kernelND.HasVolume;
import org.geogebra.common.kernel.kernelND.RotateableND;
import org.geogebra.common.plugin.GeoClass;
import org.geogebra.common.util.debug.Log;
/**
* Class for limited quadrics (e.g. limited cones, cylinders, ...)
*
* @author mathieu
*
*/
public class GeoQuadric3DLimited extends GeoQuadricND
implements GeoNumberValue, HasVolume, HasHeight, RotateableND,
Translateable, MirrorableAtPlane, Transformable, Dilateable,
GeoQuadric3DLimitedInterface, GeoQuadric3DLimitedOrPart {
/** side of the quadric */
private GeoQuadric3DPart side;
/** bottom and top of the quadric */
private GeoConicND bottom;
private GeoConic3D top;
// private GeoPointND bottomPoint, topPoint;
private double bottomParameter, topParameter;
/*
* constructor
*
* @param c
*
* public GeoQuadric3DLimited(Construction c) { this(c, null, null); }
*/
/**
*
* @param c
* construction
*/
public GeoQuadric3DLimited(Construction c) {// , GeoPointND bottomPoint,
// GeoPointND topPoint) {
super(c);
setConstructionDefaults();
}
/*
* public void setPoints(GeoPointND bottomPoint, GeoPointND topPoint) {
* this.bottomPoint = bottomPoint; this.topPoint = topPoint; }
*/
public void setBottomTop(GeoConicND bottom2, GeoConic3D top) {
this.bottom = bottom2;
this.top = top;
bottom.addMeta(this);
top.addMeta(this);
}
public void setSide(GeoQuadric3DPart side) {
this.side = side;
side.setFromMeta(this);
}
@Override
public void remove() {
bottom.removeMeta(this);
// no need to remove meta for side and top: theses parts will be removed
// too
super.remove();
}
public GeoQuadric3DLimited(GeoQuadric3DLimited quadric) {
this(quadric.getConstruction());
this.bottom = new GeoConic3D(quadric.getConstruction());
this.top = new GeoConic3D(quadric.getConstruction());
this.side = new GeoQuadric3DPart(quadric.getConstruction());
set(quadric);
}
public GeoConicND getBottom() {
return bottom;
}
public GeoConic3D getTop() {
return top;
}
public GeoQuadric3DPart getSide() {
return side;
}
public void updatePartsVisualStyle() {
setObjColor(getObjectColor());
setLineThickness(getLineThickness());
setAlphaValue(getAlphaValue());
setEuclidianVisible(isEuclidianVisible());
}
/**
* init the labels
*
* @param labels
* labels for self, bottom, top, side
*/
public void initLabelsIncludingBottom(String[] labels) {
if (cons.isSuppressLabelsActive()) { // for redefine
return;
}
if (labels == null || labels.length == 0) {
initLabelsIncludingBottom(new String[1]);
return;
}
setLabel(labels[0]);
if (labels.length < 3) {
bottom.setLabel(null);
if (!silentTop) {
top.setLabel(null);
}
side.setLabel(null);
return;
} else if (labels.length == 3) {
bottom.setLabel(labels[1]);
side.setLabel(labels[2]);
} else {
bottom.setLabel(labels[1]);
top.setLabel(labels[2]);
side.setLabel(labels[3]);
}
}
/**
* init the labels
*
* @param labels
* labels for self, top, side
*/
public void initLabelsNoBottom(String[] labels) {
if (cons.isSuppressLabelsActive()) { // for redefine
return;
}
if (labels == null || labels.length == 0) {
initLabelsNoBottom(new String[1]);
return;
}
setLabel(labels[0]);
if (labels.length < 3) {
if (!silentTop) {
top.setLabel(null);
}
side.setLabel(null);
return;
}
// else
top.setLabel(labels[1]);
side.setLabel(labels[2]);
}
@Override
public double getBottomParameter() {
return bottomParameter;
}
@Override
public double getTopParameter() {
return topParameter;
}
private Coords direction;
public Coords getDirection() {
return direction;
}
private Coords origin;
public Coords getOrigin() {
return origin;
}
/**
* @param origin
* @param direction
* @param r
* @param bottomParameter
* @param topParameter
*
*/
public void setCylinder(Coords origin, Coords direction, double r,
double bottomParameter, double topParameter) {
// limits
setLimits(bottomParameter, topParameter);
// set center
this.origin = origin;
// set direction
this.direction = direction;
// set bottom radius
this.radius = r;
// set type
setType(QUADRIC_CYLINDER);
}
public void setHyperbolicCylinder(Coords origin, Coords direction, double r,
double bottomParameter, double topParameter) {
// limits
setLimits(bottomParameter, topParameter);
// set center
this.origin = origin;
// set direction
this.direction = direction;
// set bottom radius
this.radius = r;
// set type
setType(QUADRIC_HYPERBOLIC_CYLINDER);
}
public void setParabolicCylinder(Coords origin, Coords direction, double r,
double bottomParameter, double topParameter) {
// limits
setLimits(bottomParameter, topParameter);
// set center
this.origin = origin;
// set direction
this.direction = direction;
// set bottom radius
this.radius = r;
// set type
setType(QUADRIC_PARABOLIC_CYLINDER);
}
/**
* sets the bottom and top values for limits
*
* @param bottomParameter
* @param topParameter
*/
public void setLimits(double bottomParameter, double topParameter) {
this.bottomParameter = bottomParameter;
this.topParameter = topParameter;
}
private double radius;
public double getRadius() {
return radius;
}
public void setCone(Coords origin, Coords direction, double r,
double bottomParameter, double topParameter) {
// limits
setLimits(bottomParameter, topParameter);
// set center
this.origin = origin;
// set direction
this.direction = direction;
// set bottom radius
this.radius = r;
// set type
type = QUADRIC_CONE;
}
// ///////////////////////
// GEOELEMENT
// ///////////////////////
@Override
public void setObjColor(GColor color) {
super.setObjColor(color);
if (bottom == null) {
return;
}
bottom.setObjColor(color);
if (!silentTop) {
top.setObjColor(color);
}
side.setObjColor(color);
}
@Override
public void setTrace(boolean trace) {
super.setTrace(trace);
if (bottom == null) {
return;
}
bottom.setTrace(trace);
if (!silentTop) {
top.setTrace(trace);
}
side.setTrace(trace);
}
/** to be able to fill it with an alpha value */
@Override
public boolean isFillable() {
return true;
}
@Override
public void setEuclidianVisible(boolean visible) {
super.setEuclidianVisible(visible);
bottom.setEuclidianVisible(visible);
if (!silentTop) {
top.setEuclidianVisible(visible);
}
side.setEuclidianVisible(visible);
}
@Override
public void setShowObjectCondition(final GeoBoolean cond)
throws CircularDefinitionException {
super.setShowObjectCondition(cond);
if (bottom == null) {
return;
}
bottom.setShowObjectCondition(cond);
if (!silentTop) {
top.setShowObjectCondition(cond);
}
side.setShowObjectCondition(cond);
}
@Override
public void updateVisualStyle(GProperty prop) {
super.updateVisualStyle(prop);
if (bottom == null) {
return;
}
bottom.updateVisualStyle(prop);
if (!silentTop) {
top.updateVisualStyle(prop);
}
side.updateVisualStyle(prop);
}
@Override
public void setLineType(int type) {
super.setLineType(type);
if (bottom == null) {
return;
}
bottom.setLineType(type);
bottom.update();
if (!silentTop) {
top.setLineType(type);
top.update();
}
}
@Override
public void setLineTypeHidden(int type) {
super.setLineTypeHidden(type);
if (bottom == null) {
return;
}
bottom.setLineTypeHidden(type);
bottom.update();
if (!silentTop) {
top.setLineTypeHidden(type);
top.update();
}
}
@Override
public void setLineThickness(int th) {
super.setLineThickness(th);
if (bottom == null) {
return;
}
bottom.setLineThickness(th);
bottom.update();
if (!silentTop) {
top.setLineThickness(th);
top.update();
}
}
@Override
public void setAlphaValue(double alpha) {
super.setAlphaValue(alpha);
if (bottom == null) {
return;
}
bottom.setAlphaValue(alpha);
bottom.updateVisualStyle(GProperty.COLOR);
if (!silentTop) {
top.setAlphaValue(alpha);
top.updateVisualStyle(GProperty.COLOR);
}
side.setAlphaValue(alpha);
side.updateVisualStyle(GProperty.COLOR);
getKernel().notifyRepaint();
}
@Override
public GeoElement copy() {
return new GeoQuadric3DLimited(this);
}
@Override
public GeoClass getGeoClassType() {
return GeoClass.QUADRIC_LIMITED;
}
@Override
public String getTypeString() {
return side.getQuadricTypeString();
}
@Override
public boolean isEqual(GeoElementND Geo) {
// TODO Auto-generated method stub
return false;
}
@Override
public void set(GeoElementND geo) {
if (geo instanceof GeoQuadric3DLimited) {
GeoQuadric3DLimited quadric = (GeoQuadric3DLimited) geo;
bottomParameter = quadric.bottomParameter;
topParameter = quadric.topParameter;
quadric.calcVolume();
volume = quadric.volume;
bottom.set(quadric.bottom);
top.set(quadric.top);
silentTop = quadric.silentTop;
side.set(quadric.side);
// TODO merge with GeoQuadric3D
// copy everything
toStringMode = quadric.toStringMode;
type = quadric.type;
radius = quadric.getRadius();
// set from side
origin = side.getMidpoint3D();
direction = side.getEigenvec3D(2);
defined = quadric.defined;
super.set(geo);
}
}
@Override
public boolean showInAlgebraView() {
return true;
}
@Override
protected boolean showInEuclidianView() {
return true;
}
// ///////////////////////////////////
// GEOQUADRICND
// ///////////////////////////////////
private double volume;
/**
* Compute volume from radius, halfAxes and parameters
*/
public void calcVolume() {
// Application.debug("ici");
if (bottom == null) {
volume = Double.NaN;
return;
}
double pih = Math.PI * Math.abs(topParameter - bottomParameter);
switch (type) {
default:
case QUADRIC_CYLINDER:
if (bottom.halfAxes == null) {
volume = radius * radius * pih;
} else {
volume = bottom.getHalfAxis(0) * bottom.getHalfAxis(1) * pih;
}
break;
case QUADRIC_CONE:
double h = Math.abs(topParameter - bottomParameter);
if (bottom.halfAxes == null) {
double r = radius * h; // "radius" is the radius value for h = 1
volume = r * r * pih / 3;
} else {
volume = bottom.getHalfAxis(0) * bottom.getHalfAxis(1) * pih
/ 3;
}
break;
// default:
// volume=Double.NaN;
}
}
@Override
public double getVolume() {
if (defined) {
return volume;
}
return Double.NaN;
}
@Override
public boolean hasFiniteVolume() {
return true;
}
@Override
public String toValueString(StringTemplate tpl) {
switch (type) {
case QUADRIC_CYLINDER:
case QUADRIC_CONE:
return kernel.format(volume, tpl);
case QUADRIC_EMPTY:
return kernel.format(0, tpl);
default:
Log.debug("todo-GeoQuadric3DLimited");
return "?";
}
}
@Override
protected StringBuilder buildValueString(StringTemplate tpl) {
return new StringBuilder(toValueString(tpl));
}
@Override
public void setSphereND(GeoPointND M, GeoSegmentND segment) {
// TODO Auto-generated method stub
}
@Override
public void setSphereND(GeoPointND M, GeoPointND P) {
// TODO Auto-generated method stub
}
// ////////////////////////////////
// NumberValue
// ////////////////////////////////
@Override
public MyDouble getNumber() {
return new MyDouble(kernel, getDouble());
}
@Override
public double getDouble() {
return getVolume();
}
@Override
public boolean isNumberValue() {
return true;
}
private boolean silentTop = false;
/**
* set top as silent part (not in construction)
*/
public void setSilentTop() {
silentTop = true;
}
@Override
public double getOrientedHeight() {
return topParameter - bottomParameter;
}
@Override
public boolean isGeoElement3D() {
return true;
}
@Override
public boolean hasFillType() {
return false;
}
@Override
protected void setColorVisualStyle(final GeoElement geo) {
setObjColor(geo.getObjectColor());
setAlphaValue(geo.getAlphaValue());
}
@Override
public void rotate(NumberValue r, GeoPointND S) {
bottom.rotate(r, S);
top.rotate(r, S);
side.rotate(r, S);
// get infos from side
origin = side.getMidpoint3D();
direction = side.getEigenvec3D(2);
}
@Override
public void rotate(NumberValue r) {
bottom.rotate(r);
top.rotate(r);
side.rotate(r);
// get infos from side
origin = side.getMidpoint3D();
direction = side.getEigenvec3D(2);
}
@Override
public void rotate(NumberValue r, GeoPointND S,
GeoDirectionND orientation) {
((GeoConic3D) bottom).rotate(r, S, orientation);
top.rotate(r, S, orientation);
side.rotate(r, S, orientation);
// get infos from side
origin = side.getMidpoint3D();
direction = side.getEigenvec3D(2);
}
@Override
public void rotate(NumberValue r, GeoLineND line) {
((GeoConic3D) bottom).rotate(r, line);
top.rotate(r, line);
side.rotate(r, line);
// get infos from side
origin = side.getMidpoint3D();
direction = side.getEigenvec3D(2);
}
@Override
final public boolean isTranslateable() {
return true;
}
@Override
public void translate(Coords v) {
bottom.translate(v);
top.translate(v);
side.translate(v);
// get infos from side
origin = side.getMidpoint3D();
}
// //////////////////////
// MIRROR
// //////////////////////
@Override
public void mirror(Coords Q) {
bottom.mirror(Q);
top.mirror(Q);
side.mirror(Q);
// get infos from side
origin = side.getMidpoint3D();
direction = side.getEigenvec3D(2);
}
@Override
public void mirror(GeoLineND g) {
bottom.mirror(g);
top.mirror(g);
side.mirror(g);
// get infos from side
origin = side.getMidpoint3D();
direction = side.getEigenvec3D(2);
}
@Override
public void mirror(GeoCoordSys2D plane) {
((MirrorableAtPlane) bottom).mirror(plane);
top.mirror(plane);
side.mirror(plane);
// get infos from side
origin = side.getMidpoint3D();
direction = side.getEigenvec3D(2);
}
// //////////////////////
// DILATE
// //////////////////////
@Override
public void dilate(NumberValue rval, Coords S) {
bottom.dilate(rval, S);
top.dilate(rval, S);
side.dilate(rval, S);
double r = Math.abs(rval.getDouble());
volume *= r * r * r;
bottomParameter *= r;
topParameter *= r;
// get infos from side
origin = side.getMidpoint3D();
direction = side.getEigenvec3D(2);
}
@Override
final protected void singlePoint() {
type = GeoQuadricNDConstants.QUADRIC_SINGLE_POINT;
}
@Override
public Coords getMidpoint2D() {
return side.getMidpoint2D();
}
@Override
public Coords getMidpoint() {
return side.getMidpoint();
}
@Override
public Coords getMidpoint3D() {
return side.getMidpoint3D();
}
@Override
public CoordMatrix getSymetricMatrix() {
return side.getSymetricMatrix();
}
@Override
public double getHalfAxis(int i) {
return side.getHalfAxis(i);
}
@Override
public int getDimension() {
return side.getDimension();
}
@Override
public Coords getEigenvec3D(int i) {
return side.getEigenvec3D(i);
}
@Override
public double[] getFlatMatrix() {
return side.getFlatMatrix();
}
@Override
final public HitType getLastHitType() {
return HitType.ON_FILLING;
}
@Override
public ValueType getValueType() {
return ValueType.NUMBER;
}
}