package org.geogebra.common.kernel; import org.geogebra.common.kernel.algos.AlgoElement; import org.geogebra.common.kernel.algos.AlgoTransformation; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoPoly; import org.geogebra.common.kernel.geos.GeoPolygon; import org.geogebra.common.kernel.geos.LimitedPath; import org.geogebra.common.kernel.kernelND.AlgoTransformable; import org.geogebra.common.kernel.kernelND.GeoConicND; 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.GeoSegmentND; /** * Container for transforms * * @author Zbynek * */ public abstract class Transform { /** * Creates label for transformed geo by appending '. No more than three 's * are appended. For functions we use _1 instead. * * @param geo * source geo * @return label for transformed geo */ public static String transformedGeoLabel(GeoElementND geo) { if (geo.isGeoFunction()) { if (geo.isLabelSet() && !geo.hasIndexLabel()) { return geo.getFreeLabel(geo.getLabelSimple()); } return null; } if (geo.isLabelSet() && !geo.hasIndexLabel() && !geo.getLabelSimple().endsWith("'''")) { return geo.getFreeLabel(geo.getLabelSimple() + "'"); } return null; } /** * Apply the transform to given element and set label for result * * @param geo * input geo * @return transformed geo */ public GeoElement doTransform(GeoElementND geo) { return getTransformAlgo(geo.toGeoElement()).getResult(); } /** construction */ protected Construction cons; /** * @param label * label for transformed polygon * @param poly * input polygon * @return transformed polygon */ final public GeoElement[] transformPoly(String label, GeoPolygon poly) { return transformPoly(label, poly, transformPoints(poly.getPoints())); } /** * Apply the transform to given element and set label for result * * @param transformedLabel * label for transformed geo * @param geo * input geo * @return transformed geo */ public final GeoElement[] transform(GeoElementND geo, String transformedLabel) { String label = transformedLabel; // for geo with parent algorithm that handles the transformation AlgoElement algo = geo.getParentAlgorithm(); if ((algo != null) && (algo instanceof AlgoTransformable)) { return ((AlgoTransformable) algo).getTransformedOutput(this); } // for polygons we transform if (geo instanceof GeoPoly && this.isAffine()) { GeoPoly poly = (GeoPoly) geo; if (poly.isVertexCountFixed() && poly.isAllVertexLabelsSet()) { return transformPoly(label, poly, transformPoints(poly.getPointsND())); } } if (label == null) { label = transformedGeoLabel(geo); } // handle segments, rays and arcs separately // in case these are not e.g. parts of list if (geo.isLimitedPath() && ((LimitedPath) geo).isAllEndpointsLabelsSet()) { GeoElement[] geos = ((LimitedPath) geo) .createTransformedObject(this, label); // TODO: make sure orientation of arcs is OK // if (geos[0] instanceof Orientable && geoMir instanceof // Orientable) // ((Orientable)geos[0]).setOppositeOrientation( // (Orientable)geoMir); return geos; } // standard case GeoElement ret = doTransform(geo); ret.setLabel(label); ret.setVisualStyleForTransformations(geo.toGeoElement()); GeoElement[] geos = { ret }; return geos; } /** * Returns algo that will be used for traansforming given geo * * @param geo * input geo * @return algo that will be used for traansforming given geo */ protected abstract AlgoTransformation getTransformAlgo(GeoElement geo); private GeoElement[] transformPoly(String label, GeoPoly oldPoly, GeoPointND[] transformedPoints) { // get label for polygon String polyLabel = null; if (label == null) { if (((GeoElement) oldPoly).isLabelSet()) { polyLabel = transformedGeoLabel(oldPoly); } } else { polyLabel = label; } // use visibility of points for transformed points GeoPointND[] oldPoints = oldPoly.getPoints(); for (int i = 0; i < oldPoints.length; i++) { setVisualStyleForTransformations((GeoElement) oldPoints[i], (GeoElement) transformedPoints[i]); cons.getKernel().notifyUpdate((GeoElement) transformedPoints[i]); } GeoElement[] ret; // build the polygon from the transformed points if (oldPoly instanceof GeoPolygon) { ret = cons.getKernel().polygonND(wrapLabel(polyLabel), transformedPoints); } else { ret = cons.getKernel().polyLineND(polyLabel, transformedPoints); } for (int i = 0; i < ret.length; i++) { setVisualStyleForTransformations((GeoElement) oldPoly, ret[i]); } if (oldPoly instanceof GeoPolygon) { // set segments' color (needs to be done after setting polygon // color) GeoSegmentND[] transformedSegments = ((GeoPolygon) ret[0]) .getSegments(); GeoSegmentND[] oldSegments = ((GeoPolygon) oldPoly).getSegments(); for (int i = 0; i < oldSegments.length; i++) { setVisualStyleForTransformations((GeoElement) oldSegments[i], (GeoElement) transformedSegments[i]); cons.getKernel() .notifyUpdate((GeoElement) transformedSegments[i]); } } return ret; } private static String[] wrapLabel(String polyLabel) { return polyLabel == null ? null : new String[] { polyLabel }; } /** * Applies the transform to all points * * @param points * input points * @return array of transformed points */ public GeoPointND[] transformPoints(GeoPointND[] points) { // dilate all points GeoPointND[] newPoints = new GeoPointND[points.length]; for (int i = 0; i < points.length; i++) { String pointLabel = transformedGeoLabel(points[i]); newPoints[i] = (GeoPointND) transform(points[i], pointLabel)[0]; ((GeoElement) newPoints[i]) .setVisualStyleForTransformations((GeoElement) points[i]); } return newPoints; } /** * Applies the transform to a conic * * @param conic * input conic * @return transformed conic */ public GeoConicND getTransformedConic(GeoConicND conic) { GeoConicND ret = (GeoConicND) doTransform(conic); ret.setVisualStyleForTransformations(conic); return ret; } /** * Applies the transform to a line * * @param line * input line * @return transformed line */ public GeoElement getTransformedLine(GeoLineND line) { GeoElement ret = doTransform(line); ret.setVisualStyleForTransformations((GeoElement) line); return ret; } /** * True if the transformation is affine * * @return true by default, overriden e.g. for circle inverse */ public boolean isAffine() { return true; } /** * True if the transform preserves angles * * @return true iff similar */ public boolean isSimilar() { return true; } /** * Returns true when orientation of e.g. semicircles is changed * * @return true iff changes orientation of objects */ public boolean changesOrientation() { return false; } /** * set the visual style of transformed geo regarding input * * @param input * input geo * @param transformed * transformed geo */ static final public void setVisualStyleForTransformations(GeoElement input, GeoElement transformed) { transformed.setEuclidianVisible(input.isSetEuclidianVisible()); transformed.setVisualStyleForTransformations(input); } }