package org.geogebra.common.geogebra3D.euclidian3D.draw;
import org.geogebra.common.euclidian.EuclidianController;
import org.geogebra.common.euclidian.Previewable;
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.euclidian3D.printer3D.ExportToPrinter3D;
import org.geogebra.common.geogebra3D.euclidian3D.printer3D.ExportToPrinter3D.Type;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.PathParameter;
import org.geogebra.common.kernel.Matrix.Coords;
import org.geogebra.common.kernel.arithmetic.Functional2Var;
import org.geogebra.common.kernel.geos.FromMeta;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.kernelND.GeoConicND;
import org.geogebra.common.kernel.kernelND.GeoConicNDConstants;
import org.geogebra.common.main.Feature;
/**
* @author ggb3D
*
* Drawable for GeoConic3D
*
*/
public class DrawConic3D extends Drawable3DCurves
implements Functional2Var, Previewable {
/**
* @param view3d
* the 3D view where the conic is drawn
* @param conic
* the conic to draw
*/
public DrawConic3D(EuclidianView3D view3d, GeoConicND conic) {
super(view3d, conic);
setPickingType(PickingType.POINT_OR_CURVE);
}
@Override
public void setGeoElement(GeoElement a_geo) {
super.setGeoElement(a_geo);
conic = (GeoConicND) a_geo;
}
@Override
protected void updateColors() {
updateAlpha();
setColorsOutlined();
}
@Override
public void drawGeometry(Renderer renderer) {
GeoConicND conic1 = (GeoConicND) getGeoElement();
switch (conic1.getType()) {
case GeoConicNDConstants.CONIC_CIRCLE:
case GeoConicNDConstants.CONIC_ELLIPSE:
case GeoConicNDConstants.CONIC_HYPERBOLA:
case GeoConicNDConstants.CONIC_PARABOLA:
case GeoConicNDConstants.CONIC_DOUBLE_LINE:
case GeoConicNDConstants.CONIC_INTERSECTING_LINES:
case GeoConicNDConstants.CONIC_PARALLEL_LINES:
case GeoConicNDConstants.CONIC_SINGLE_POINT:
renderer.getGeometryManager().draw(getGeometryIndex());
break;
default:
break;
}
}
@Override
public void exportToPrinter3D(ExportToPrinter3D exportToPrinter3D) {
if (isVisible()) {
exportToPrinter3D.export(this, Type.CURVE_CLOSED);
}
}
// method used only if surface is not transparent
@Override
public void drawNotTransparentSurface(Renderer renderer) {
if (isVisible() && getAlpha() == 255) {
setSurfaceHighlightingColor();
drawSurfaceGeometry(renderer);
}
drawTracesNotTranspSurface(renderer);
}
/**
*
* @param x
* value
* @return acosh(x) if x>=1; 0 otherwise
*/
public static double acosh(double x) {
if (x <= 1) {
return 0;
}
return Math.log(x + Math.sqrt(x * x - 1));
}
/**
*
* @param x
* value
* @return asinh(x)
*/
public static double asinh(double x) {
return Math.log(x + Math.sqrt(1 + x * x));
}
/* used for update */
protected Coords m;
protected Coords d;
protected Coords[] points = new Coords[4];
protected double[] minmax;
protected GeoConicND conic;
protected Coords ev1, ev2;
protected double e1, e2;
@Override
protected boolean updateForItSelf() {
// update alpha value
updateColors();
Renderer renderer = getView3D().getRenderer();
// check is visible (and update values)
checkVisibleAndSetBoundingBox();
if (visible == Visible.TOTALLY_OUTSIDE) {
setGeometryIndex(-1);
setSurfaceIndex(-1);
return true;
}
if (conic.getType() == GeoConicNDConstants.CONIC_SINGLE_POINT) {
PlotterSurface surface;
surface = renderer.getGeometryManager().getSurface();
updateSinglePoint(surface);
} else {
if (visible != Visible.FRUSTUM_INSIDE) { // no outline when frustum
// inside
PlotterBrush brush = renderer.getGeometryManager().getBrush();
brush.start(getReusableGeometryIndex());
brush.setThickness(getGeoElement().getLineThickness(),
(float) getView3D().getScale());
brush.setAffineTexture(0f, 0f);
switch (conic.getType()) {
case GeoConicNDConstants.CONIC_CIRCLE:
if (getView3D().getApplication()
.has(Feature.DIFFERENT_AXIS_RATIO_3D)) {
updateEllipse(brush);
} else {
updateCircle(brush);
}
break;
case GeoConicNDConstants.CONIC_ELLIPSE:
updateEllipse(brush);
break;
case GeoConicNDConstants.CONIC_HYPERBOLA:
updateHyperbola(brush);
break;
case GeoConicNDConstants.CONIC_PARABOLA:
updateParabola(brush);
break;
case GeoConicNDConstants.CONIC_DOUBLE_LINE:
brush.segment(m.add(d.mul(minmax[0])),
m.add(d.mul(minmax[1])));
break;
case GeoConicNDConstants.CONIC_INTERSECTING_LINES:
case GeoConicNDConstants.CONIC_PARALLEL_LINES:
updateLines(brush);
break;
default:
break;
}
setGeometryIndex(brush.end());
}
// surface
PlotterSurface surface = renderer.getGeometryManager().getSurface();
surface.start(getReusableSurfaceIndex());
switch (conic.getType()) {
case GeoConicNDConstants.CONIC_CIRCLE:
case GeoConicNDConstants.CONIC_ELLIPSE:
updateEllipse(surface);
break;
case GeoConicNDConstants.CONIC_INTERSECTING_LINES:
updateIntersectingLines(surface);
break;
case GeoConicNDConstants.CONIC_PARALLEL_LINES:
updateParallelLines(surface);
break;
case GeoConicNDConstants.CONIC_HYPERBOLA:
updateHyperbola(surface);
break;
case GeoConicNDConstants.CONIC_PARABOLA:
updateParabola(surface);
break;
default:
break;
}
setSurfaceIndex(surface.end());
}
return true;
}
/**
*
* @param i
* index for the line
* @return min, max parameters on the i-th line
*/
protected double[] getLineMinMax(int i) {
return getView3D().getIntervalClippedLarge(new double[] {
Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY }, m, d);
}
/**
* update outline for parallel lines
*
* @param brush
* brush plotter
*/
protected void updateLines(PlotterBrush brush) {
m = conic.getOrigin3D(0);
d = conic.getDirection3D(0);
if (d.isDefined()) {
minmax = getLineMinMax(0);
points[0] = m.add(d.mul(minmax[0]));
points[1] = m.add(d.mul(minmax[1]));
brush.segment(points[0], points[1]);
} else { // tells the surface that second line is infinite
points[0] = null;
}
m = conic.getOrigin3D(1);
d = conic.getDirection3D(1);
if (d.isDefined()) {
minmax = getLineMinMax(1);
points[3] = m.add(d.mul(minmax[0]));
points[2] = m.add(d.mul(minmax[1]));
brush.segment(points[2], points[3]);
} else { // tells the surface that second line is infinite
points[0] = null;
}
}
/**
* update surface drawing for parallel lines case
*
* @param surface
* surface plotter
*/
protected void updateParallelLines(PlotterSurface surface) {
if (points[0] != null) { // in case second line is infinite
surface.drawQuad(points[0], points[1], points[2], points[3]);
}
}
/**
* update surface drawing for intersecting lines case
*
* @param surface
* surface plotter
*/
protected void updateIntersectingLines(PlotterSurface surface) {
surface.drawTriangle(points[0], points[2], conic.getMidpoint3D());
surface.drawTriangle(points[1], points[3], conic.getMidpoint3D());
}
/**
* update outline drawing of hyperbola
*
* @param brush
* brush plotter
*/
protected void updateHyperbola(PlotterBrush brush) {
double[] minmax1 = { Double.POSITIVE_INFINITY,
Double.NEGATIVE_INFINITY };
getView3D().getMinIntervalOutsideClipping(minmax1, m, ev1.mul(e1));
minmax = new double[4];
minmax[1] = acosh(minmax1[1]);
minmax[3] = acosh(-minmax1[0]);
minmax[0] = -minmax[1];
minmax[2] = -minmax[3];
brush.hyperbolaBranch(m, ev1, ev2, e1, e2, minmax[0], minmax[1]);
brush.hyperbolaBranch(m, ev1.mul(-1), ev2, e1, e2, minmax[2],
minmax[3]);
}
/**
* update outline drawing of hyperbola
*
* @param brush
* brush plotter
*/
protected void updateParabola(PlotterBrush brush) {
minmax = getParabolaMinMax();
brush.parabola(m, ev1, ev2, conic.p, minmax[0], minmax[1], points[0],
points[1]);
}
/**
* update surface drawing for hypebola case
*
* @param surface
* surface plotter
*/
protected void updateParabola(PlotterSurface surface) {
surface.parabola(m, ev1, ev2, conic.p, minmax[0], minmax[1]);
}
/**
*
* @return min/max for parabola
*/
protected double[] getParabolaMinMax() {
double[] minmax1 = { Double.POSITIVE_INFINITY,
Double.NEGATIVE_INFINITY };
getView3D().getMinIntervalOutsideClipping(minmax1, m, ev1);
double tMax = Math.sqrt(2 * minmax1[1] / conic.p);
return new double[] { -tMax, tMax };
}
/**
* update surface drawing for hypebola case
*
* @param surface
* surface plotter
*/
protected void updateHyperbola(PlotterSurface surface) {
surface.hyperbolaPart(m, ev1, ev2, e1, e2, minmax[0], minmax[1]);
surface.hyperbolaPart(m, ev1.mul(-1), ev2, e1, e2, minmax[2],
minmax[3]);
}
/**
* update surface drawing for ellipse case
*
* @param surface
* surface plotter
*/
protected void updateEllipse(PlotterSurface surface) {
surface.ellipsePart(m, ev1, ev2, e1, e2, getEllipseSurfaceStart(),
getEllipseSurfaceExtent(), isSector());
}
/**
* update surface drawing for single point case
*
* @param surface
* surface plotter
*/
protected void updateSinglePoint(PlotterSurface surface) {
surface.start(this, getReusableGeometryIndex());
// number of vertices depends on point size
int nb = 2 + conic.getLineThickness();
surface.setU((float) getMinParameter(0), (float) getMaxParameter(0));
surface.setNbU(2 * nb);
surface.setV((float) getMinParameter(1), (float) getMaxParameter(1));
surface.setNbV(nb);
surface.draw();
setGeometryIndex(surface.end());
}
/**
*
* @return true if is a sector (for surface drawing)
*/
protected boolean isSector() {
return true;
}
/**
*
* @return start angle value for drawing ellipse surface
*/
protected double getEllipseSurfaceStart() {
if (visible == Visible.CENTER_OUTSIDE
|| visible == Visible.FRUSTUM_INSIDE) {
return beta - alpha;
}
return 0;
}
/**
*
* @return extent angle value for drawing ellipse surface
*/
protected double getEllipseSurfaceExtent() {
if (visible == Visible.CENTER_OUTSIDE
|| visible == Visible.FRUSTUM_INSIDE) {
return 2 * alpha;
}
return 2 * Math.PI;
}
/**
* draws outline for circle
*
* @param brush
* brush plotter
*/
protected void updateCircle(PlotterBrush brush) {
if (visible == Visible.CENTER_OUTSIDE) {
longitude = brush.calcArcLongitudesNeeded(e1, alpha,
getView3D().getScale());
brush.arc(m, ev1, ev2, e1, beta - alpha, 2 * alpha, longitude);
} else {
longitude = brush.calcArcLongitudesNeeded(e1, Math.PI,
getView3D().getScale());
brush.circle(m, ev1, ev2, e1, longitude);
}
}
/**
* draws outline for ellipse
*
* @param brush
* brush plotter
*/
protected void updateEllipse(PlotterBrush brush) {
if (visible == Visible.CENTER_OUTSIDE) {
brush.arcEllipse(m, ev1, ev2, e1, e2, beta - alpha, 2 * alpha);
} else {
brush.arcEllipse(m, ev1, ev2, e1, e2, 0, 2 * Math.PI);
}
}
@Override
protected void updateForView() {
if (getView3D().viewChanged()) {
switch (((GeoConicND) getGeoElement()).getType()) {
case GeoConicNDConstants.CONIC_DOUBLE_LINE:
case GeoConicNDConstants.CONIC_HYPERBOLA:
case GeoConicNDConstants.CONIC_INTERSECTING_LINES:
case GeoConicNDConstants.CONIC_LINE:
case GeoConicNDConstants.CONIC_PARABOLA:
case GeoConicNDConstants.CONIC_PARALLEL_LINES:
if (getView3D().viewChangedByZoom()
|| getView3D().viewChangedByTranslate()) {
updateForItSelf();
}
break;
case GeoConicNDConstants.CONIC_CIRCLE:
case GeoConicNDConstants.CONIC_ELLIPSE:
if (getView3D().viewChangedByZoom() // update only if zoom
// occurred
|| (visible != Visible.TOTALLY_INSIDE
&& getView3D().viewChangedByTranslate())) {
// if
// translate
// with
// not
// totally
// visible
// ellipse
updateForItSelf();
}
break;
case GeoConicNDConstants.CONIC_SINGLE_POINT:
if (getView3D().viewChangedByZoom()) {
// occurred
updateForItSelf();
}
break;
default:
// do nothing
break;
}
}
}
@Override
public int getPickOrder() {
if (getPickingType() == PickingType.POINT_OR_CURVE) {
return DRAW_PICK_ORDER_PATH;
}
return DRAW_PICK_ORDER_SURFACE;
}
private int drawTypeAdded;
@Override
public void addToDrawable3DLists(Drawable3DLists lists) {
super.addToDrawable3DLists(lists);
if (((GeoConicND) getGeoElement()).isEndOfQuadric()) {
drawTypeAdded = DRAW_TYPE_CLOSED_SURFACES_NOT_CURVED;
} else {
drawTypeAdded = DRAW_TYPE_SURFACES;
}
addToDrawable3DLists(lists, drawTypeAdded);
}
@Override
public void removeFromDrawable3DLists(Drawable3DLists lists) {
super.removeFromDrawable3DLists(lists);
removeFromDrawable3DLists(lists, drawTypeAdded);
}
private void drawSurfaceGeometry(Renderer renderer) {
switch (((GeoConicND) getGeoElement()).getType()) {
default:
// do nothing
break;
case GeoConicNDConstants.CONIC_CIRCLE:
case GeoConicNDConstants.CONIC_ELLIPSE:
case GeoConicNDConstants.CONIC_PARALLEL_LINES:
case GeoConicNDConstants.CONIC_INTERSECTING_LINES:
case GeoConicNDConstants.CONIC_HYPERBOLA:
case GeoConicNDConstants.CONIC_PARABOLA:
// Application.debug(getGeoElement().getLayer());
renderer.setLayer(getLayer()); // +0f to avoid z-fighting with
// planes
renderer.getGeometryManager().draw(getSurfaceIndex());
renderer.setLayer(0);
break;
}
}
@Override
protected void drawGeometryForPicking(Renderer renderer, PickingType type) {
if (type == PickingType.POINT_OR_CURVE) {
drawGeometry(renderer);
} else {
if (getAlpha() > 0) { // surface is pickable only if not totally
// transparent
drawSurfaceGeometry(renderer);
}
}
}
@Override
public void drawTransp(Renderer renderer) {
if (isVisible() && hasTransparentAlpha()) {
setSurfaceHighlightingColor();
drawSurfaceGeometry(renderer);
}
drawTracesTranspSurface(renderer);
}
@Override
public void drawHiding(Renderer renderer) {
if (isVisible() && hasTransparentAlpha()) {
drawSurfaceGeometry(renderer);
}
drawTracesHidingSurface(renderer);
}
// /////////////////////////////////
// FUNCTION2VAR INTERFACE
// /////////////////////////////////
public void evaluatePoint(double u, double v, Coords point) {
GeoConicND conic1 = (GeoConicND) getGeoElement();
double r = conic1.getLineThickness() / getView3D().getScale() * 1.5;
point.set(Math.cos(u) * Math.cos(v) * r, Math.sin(u) * Math.cos(v) * r,
Math.sin(v) * r, 1);
point.setAdd3(point, conic1.getMidpoint3D());
}
@Override
public Coords evaluateNormal(double u, double v) {
return new Coords(new double[] { Math.cos(u) * Math.cos(v),
Math.sin(u) * Math.cos(v), Math.sin(v) });
}
@Override
public double getMinParameter(int index) {
switch (index) {
case 0: // u
default:
return 0;
case 1: // v
return -Math.PI / 2;
}
}
@Override
public double getMaxParameter(int index) {
switch (index) {
case 0: // u
default:
return 2 * Math.PI;
case 1: // v
return Math.PI / 2;
}
}
@Override
public void updatePreview() {
// setWaitForUpdate();
}
@Override
public void updateMousePos(double x, double y) {
// TODO Auto-generated method stub
}
@Override
public boolean isTransparent() {
if (getPickingType() == PickingType.SURFACE) {
return getAlpha() <= EuclidianController.MAX_TRANSPARENT_ALPHA_VALUE_INT;
}
return false;
}
private double alpha, beta;
protected int longitude = 60;
/**
* Visibility flag
*
* @author mathieu
*
*/
private static enum Visible {
/** the conic is totally outside */
TOTALLY_OUTSIDE,
/** the frustum is inside the conic */
FRUSTUM_INSIDE,
/** the conic is totally inside */
TOTALLY_INSIDE,
/** the conic is partly inside, center outside */
CENTER_OUTSIDE,
/** the conic is partly inside, center inside */
CENTER_INSIDE
}
private Visible visible = Visible.TOTALLY_OUTSIDE;
/**
* check if the ellipse is (at least partially) visible
*
* @param center
* ellipse center
* @param rMin
* min ellipse radius
* @param rMax
* max ellipse radius
*/
private void checkEllipseVisible(Coords center, double rMin, double rMax) {
double frustumRadius = getView3D().getFrustumRadius();
Coords origin = getView3D().getCenter();
Coords v = origin.sub(center);
v.calcNorm();
double centersDistance = v.getNorm();
if (centersDistance > rMax + frustumRadius) { // circle totally outside
// the frustum
visible = Visible.TOTALLY_OUTSIDE;
} else if (centersDistance < frustumRadius) { // center inside
visible = Visible.CENTER_INSIDE;
} else if (centersDistance + frustumRadius < rMin) { // frustum totally
// inside the
// circle
visible = Visible.FRUSTUM_INSIDE;
calcVisibleAngles(v, frustumRadius);
} else if (centersDistance + rMax < frustumRadius) { // totally inside
visible = Visible.TOTALLY_INSIDE;
} else {
visible = calcVisibleAngles(v, frustumRadius);
}
}
/**
* calc angles to draw minimum longitudes
*/
private Visible calcVisibleAngles(Coords v, double frustumRadius) {
// calc angles to draw minimum longitudes
double x = v.dotproduct(ev1);
double y = v.dotproduct(ev2);
double horizontalDistance = Math.sqrt(x * x + y * y);
if (horizontalDistance > frustumRadius) {
alpha = Math.asin(frustumRadius / horizontalDistance);
beta = Math.atan2(y * e1, x * e2);
// Log.debug("alpha = "+(alpha*180/Math.PI)+"degrees, beta =
// "+(beta*180/Math.PI)+"degrees");
return Visible.CENTER_OUTSIDE; // center outside
}
return Visible.CENTER_INSIDE; // do as if center inside
}
private Coords boundsMin = new Coords(3), boundsMax = new Coords(3);
@Override
public void enlargeBounds(Coords min, Coords max) {
switch (conic.getType()) {
default:
// do nothing
break;
case GeoConicNDConstants.CONIC_SINGLE_POINT:
case GeoConicNDConstants.CONIC_CIRCLE:
case GeoConicNDConstants.CONIC_ELLIPSE:
enlargeBounds(min, max, boundsMin, boundsMax);
break;
}
}
final private void setBoundsEllipse() {
boundsMin.set(Double.POSITIVE_INFINITY);
boundsMax.set(Double.NEGATIVE_INFINITY);
enlargeBoundsToDiagonal(boundsMin, boundsMax, m, ev1, ev2, e1, e2);
double radius = conic.getLineThickness() * PlotterBrush.LINE3D_THICKNESS
/ getView3D().getScale();
boundsMin.addInside(-radius);
boundsMax.addInside(radius);
}
/**
* check if conic is visible. Note that midpoint, etc. are updated here
*/
protected void checkVisibleAndSetBoundingBox() {
switch (conic.getType()) {
case GeoConicNDConstants.CONIC_SINGLE_POINT:
m = conic.getMidpoint3D();
boundsMin.setValues(m, 3);
boundsMax.setValues(m, 3);
double radius = conic.getLineThickness() / getView3D().getScale()
* DrawPoint3D.DRAW_POINT_FACTOR;
boundsMin.addInside(-radius);
boundsMax.addInside(radius);
double frustumRadius = getView3D().getFrustumRadius();
Coords origin = getView3D().getCenter();
Coords v = origin.sub(m);
v.calcNorm();
double centersDistance = v.getNorm();
if (Kernel.isGreater(centersDistance, frustumRadius)) {
visible = Visible.TOTALLY_OUTSIDE;
} else {
visible = Visible.TOTALLY_INSIDE;
}
break;
case GeoConicNDConstants.CONIC_CIRCLE:
m = conic.getMidpoint3D();
ev1 = conic.getEigenvec3D(0);
ev2 = conic.getEigenvec3D(1);
e1 = conic.getHalfAxis(0);
e2 = e1;
setBoundsEllipse();
checkEllipseVisible(m, e1, e2);
break;
case GeoConicNDConstants.CONIC_ELLIPSE:
m = conic.getMidpoint3D();
ev1 = conic.getEigenvec3D(0);
ev2 = conic.getEigenvec3D(1);
e1 = conic.getHalfAxis(0);
e2 = conic.getHalfAxis(1);
setBoundsEllipse();
double eMin, eMax;
if (e1 > e2) {
eMax = e1;
eMin = e2;
} else {
eMax = e2;
eMin = e1;
}
checkEllipseVisible(m, eMin, eMax);
// dilate angle
if (alpha * eMax >= Math.PI * eMin) {
alpha = Math.PI;
} else {
alpha *= eMax / eMin;
}
break;
case GeoConicNDConstants.CONIC_HYPERBOLA:
m = conic.getMidpoint3D();
ev1 = conic.getEigenvec3D(0);
ev2 = conic.getEigenvec3D(1);
e1 = conic.getHalfAxis(0);
e2 = conic.getHalfAxis(1);
visible = Visible.TOTALLY_INSIDE; // TODO
break;
case GeoConicNDConstants.CONIC_PARABOLA:
m = conic.getMidpoint3D();
ev1 = conic.getEigenvec3D(0);
ev2 = conic.getEigenvec3D(1);
visible = Visible.TOTALLY_INSIDE; // TODO
break;
case GeoConicNDConstants.CONIC_DOUBLE_LINE:
m = conic.getOrigin3D(0);
d = conic.getDirection3D(0);
minmax = getLineMinMax(0);
visible = Visible.TOTALLY_INSIDE; // TODO
break;
case GeoConicNDConstants.CONIC_INTERSECTING_LINES:
case GeoConicNDConstants.CONIC_PARALLEL_LINES:
default:
visible = Visible.TOTALLY_INSIDE; // TODO
break;
}
}
private PathParameter hittingPathParameter = new PathParameter();
@Override
public boolean hit(Hitting hitting) {
return hit(hitting, false);
}
@Override
public boolean hitForList(Hitting hitting) {
if (hasGeoElementVisible() && getGeoElement().isPickable()) {
return hit(hitting, true);
}
return false;
}
/**
*
* @param hitting
* e.g. ray
* @param checkRealPointSize
* true if we check point size (and not threshold)
* @return true if hitted
*/
private boolean hit(Hitting hitting, boolean checkRealPointSize) {
if (waitForReset) { // prevent NPE
return false;
}
switch (((GeoConicND) getGeoElement()).getType()) {
case GeoConicNDConstants.CONIC_EMPTY:
return false;
case GeoConicNDConstants.CONIC_SINGLE_POINT:
if (project == null) {
project = Coords.createInhomCoorsInD3();
}
if (DrawPoint3D.hit(hitting, conic.getMidpoint3D(), this,
conic.getLineThickness(), project, parameters,
checkRealPointSize)) {
setPickingType(PickingType.POINT_OR_CURVE);
return true;
}
return false;
case GeoConicNDConstants.CONIC_CIRCLE:
case GeoConicNDConstants.CONIC_ELLIPSE:
default: // TODO check other cases
boolean ret = false;
// project hitting origin on polygon plane
if (globalCoords == null) {
globalCoords = new Coords(4);
inPlaneCoords = new Coords(4);
}
hitting.origin.projectPlaneThruVIfPossible(
conic.getCoordSys().getMatrixOrthonormal(),
hitting.direction, globalCoords, inPlaneCoords);
// try conic surface
if (getGeoElement()
.getAlphaValue() > EuclidianController.MIN_VISIBLE_ALPHA_VALUE
&& hitting.isInsideClipping(globalCoords)
&& conic.isInRegion(inPlaneCoords.getX(),
inPlaneCoords.getY())) {
double parameterOnHitting = inPlaneCoords.getZ();// TODO use
// other for
// non-parallel
// projection
// :
// -hitting.origin.distance(project[0]);
setZPick(parameterOnHitting, parameterOnHitting);
setPickingType(PickingType.SURFACE);
ret = true;
}
// try outline
inPlaneCoords.setZ(1.0);
conic.pointChanged(inPlaneCoords, hittingPathParameter);
Coords p3d = conic.getCoordSys().getPoint(inPlaneCoords.getX(),
inPlaneCoords.getY()); // get nearest point on conic
// Log.debug("\n"+p2d+"\n3d:\n"+p3d);
if (hitting.isInsideClipping(p3d)) {
if (project == null) {
project = Coords.createInhomCoorsInD3();
}
p3d.projectLine(hitting.origin, hitting.direction, project,
parameters); // check distance to hitting line
double d1 = getView3D().getScaledDistance(p3d, project);
if (d1 <= conic.getLineThickness() + hitting.getThreshold()) {
double z = -parameters[0];
double dz = conic.getLineThickness()
/ getView3D().getScale();
setZPick(z + dz, z - dz);
setPickingType(PickingType.POINT_OR_CURVE);
return true;
}
}
return ret;
}
}
private Coords project, globalCoords, inPlaneCoords;
private double[] parameters = new double[2];
@Override
public boolean doHighlighting() {
// if it depends on a limited quadric, look at the meta' highlighting
if (getGeoElement().getMetasLength() > 0) {
for (GeoElement meta : ((FromMeta) getGeoElement()).getMetas()) {
if (meta != null && meta.doHighlighting()) {
return true;
}
}
}
return super.doHighlighting();
}
}