/*
GeoGebra - Dynamic Mathematics for Everyone
http://www.geogebra.org
This file is part of GeoGebra.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation.
*/
package org.geogebra.common.kernel.geos;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.TreeSet;
import org.geogebra.common.awt.GColor;
import org.geogebra.common.awt.GFont;
import org.geogebra.common.euclidian.EuclidianViewInterfaceCommon;
import org.geogebra.common.euclidian.EuclidianViewInterfaceSlim;
import org.geogebra.common.kernel.CircularDefinitionException;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Path;
import org.geogebra.common.kernel.PathMover;
import org.geogebra.common.kernel.PathMoverGeneric;
import org.geogebra.common.kernel.PathNormalizer;
import org.geogebra.common.kernel.PathOrPoint;
import org.geogebra.common.kernel.PathParameter;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.Matrix.Coords;
import org.geogebra.common.kernel.algos.AlgoConicPartCircumcircle;
import org.geogebra.common.kernel.algos.AlgoConicPartConicPoints;
import org.geogebra.common.kernel.algos.AlgoDependentList;
import org.geogebra.common.kernel.algos.AlgoElement;
import org.geogebra.common.kernel.algos.AlgoMacroInterface;
import org.geogebra.common.kernel.algos.AlgoSemicircle;
import org.geogebra.common.kernel.arithmetic.ExpressionNode;
import org.geogebra.common.kernel.arithmetic.ExpressionNodeConstants.StringType;
import org.geogebra.common.kernel.arithmetic.ExpressionValue;
import org.geogebra.common.kernel.arithmetic.ListValue;
import org.geogebra.common.kernel.arithmetic.MyList;
import org.geogebra.common.kernel.arithmetic.NumberValue;
import org.geogebra.common.kernel.arithmetic.ValidExpression;
import org.geogebra.common.kernel.arithmetic.ValueType;
import org.geogebra.common.kernel.commands.EvalInfo;
import org.geogebra.common.kernel.geos.GeoAngle.AngleStyle;
import org.geogebra.common.kernel.kernelND.GeoElementND;
import org.geogebra.common.kernel.kernelND.GeoPointND;
import org.geogebra.common.kernel.kernelND.GeoQuadricND;
import org.geogebra.common.plugin.EuclidianStyleConstants;
import org.geogebra.common.plugin.GeoClass;
import org.geogebra.common.util.StringUtil;
import org.geogebra.common.util.debug.Log;
/**
* List of GeoElements
*/
public class GeoList extends GeoElement
implements ListValue, PointProperties, TextProperties, Traceable, Path,
Transformable, SpreadsheetTraceable, AbsoluteScreenLocateable,
Furniture, InequalityProperties,
AngleProperties, HasSymbolicMode, Animatable {
private final static GeoClass ELEMENT_TYPE_MIXED = GeoClass.DEFAULT;
private boolean trace;
// GeoElement list members
private final ArrayList<GeoElement> geoList;
// lists will often grow and shrink dynamically,
// so we keep a cacheList of all old list elements
private final ArrayList<GeoElementND> cacheList;
private boolean isDefined = true;
private boolean isDrawable = true;
private boolean drawAsComboBox = false;
private GeoClass elementType = ELEMENT_TYPE_MIXED;
/**
* Whether this lists show all properties in the properties dialog. This is
* just recommended for the default GeoList in order to show all possible
* properties in the default configuration dialog.
*/
private boolean showAllProperties = false;
private ArrayList<GeoElement> colorFunctionListener; // Michael Borcherds
// 2008-04-02
/**
* Creates new GeoList, size defaults to 20
*
* @param c
* construction
*/
public GeoList(final Construction c) {
this(c, 20);
}
private GeoList(final Construction c, final int size) {
super(c);
// moved from GeoElement's constructor
// must be called from the subclass, see
// http://benpryor.com/blog/2008/01/02/dont-call-subclass-methods-from-a-superclass-constructor/
setConstructionDefaults(); // init visual settings
geoList = new ArrayList<GeoElement>(size);
cacheList = new ArrayList<GeoElementND>(size);
setEuclidianVisible(false);
// don't add here, see GGB-264
// setBackgroundColor(GColor.WHITE);
}
@Override
public void setParentAlgorithm(final AlgoElement algo) {
super.setParentAlgorithm(algo);
setEuclidianVisible(true);
}
/**
* Copy constructor
*
* @param list
* list to copy
*/
public GeoList(final GeoList list) {
this(list.cons, list.size());
set(list);
}
@Override
public GeoClass getGeoClassType() {
return GeoClass.LIST;
}
/**
* Returns the element type of this list.
*
* @return ELEMENT_TYPE_MIXED == GeoClass.DEFAULT or GeoClass.NUMERIC,
* GeoClass.POINT etc constant
*/
public GeoClass getElementType() {
return elementType;
}
@Override
public GeoList copy() {
return new GeoList(this);
}
@Override
public GeoList deepCopyGeo() {
GeoList ret = new GeoList(cons);
for (int i = 0; i < geoList.size(); i++) {
ret.add(geoList.get(i).deepCopyGeo());
}
return ret;
}
@Override
public void set(final GeoElementND geo) {
reuseDefinition(geo);
if (geo.isGeoNumeric()) { // eg SetValue[list, 2]
// 1 -> first element
setSelectedIndex(-1 + (int) ((GeoNumeric) geo).getDouble(), true);
isDefined = true;
return;
}
if (!(geo instanceof GeoList)) {
setUndefined();
return;
}
final GeoList l = (GeoList) geo;
if ((l.cons != cons) && isAlgoMacroOutput()) {
// MACRO CASE
// this object is an output object of AlgoMacro
// we need to check the references to all geos in the list
final AlgoMacroInterface algoMacro = (AlgoMacroInterface) getParentAlgorithm();
algoMacro.initList(l, this);
} else {
// STANDARD CASE
// copy geoList
copyListElements(l);
}
isDefined = l.isDefined;
elementType = l.elementType;
}
/**
* Set if the list should show all properties in the properties dialog. This
* is just recommended for the default list.
*
* @param showAllProperties
* true to show all properties
*/
public void setShowAllProperties(final boolean showAllProperties) {
this.showAllProperties = showAllProperties;
}
private void copyListElements(final GeoList otherList) {
final int otherListSize = otherList.size();
ensureCapacity(otherListSize);
geoList.clear();
for (int i = 0; i < otherListSize; i++) {
final GeoElement otherElement = otherList.get(i);
GeoElementND thisElement = null;
// try to reuse cached GeoElement
if (i < cacheList.size()) {
final GeoElementND cachedGeo = cacheList.get(i);
if (!cachedGeo.isLabelSet() && (cachedGeo
.getGeoClassType() == otherElement.getGeoClassType())) {
// cached geo is unlabeled and has needed object type: use
// it
cachedGeo.set(otherElement);
thisElement = cachedGeo;
}
}
// could not use cached element -> get copy element
if (thisElement == null) {
thisElement = getCopyForList(otherElement);
}
// set list element
add(thisElement);
}
}
private GeoElement getCopyForList(final GeoElement geo) {
if (geo.isLabelSet()) {
// take original element
return geo;
}
// create a copy of geo
final GeoElement ret = geo.copyInternal(cons);
ret.setParentAlgorithm(getParentAlgorithm());
return ret;
}
private void applyVisualStyle(final GeoElement geo) {
if (!geo.isLabelSet()) {
geo.setObjColor(getObjectColor());
geo.setColorSpace(getColorSpace());
// copy color function
if (getColorFunction() != null) {
geo.setColorFunction(getColorFunction());
} else {
geo.removeColorFunction();
}
geo.setLineThickness(getLineThickness());
geo.setLineType(getLineType());
geo.setLineOpacity(getLineOpacity());
if (geo instanceof PointProperties) {
((PointProperties) geo).setPointSize(getPointSize());
((PointProperties) geo).setPointStyle(getPointStyle());
}
if (geo instanceof TextProperties) {
((TextProperties) geo)
.setFontSizeMultiplier(getFontSizeMultiplier());
((TextProperties) geo).setFontStyle(getFontStyle());
((TextProperties) geo).setSerifFont(isSerifFont());
if (useSignificantFigures) {
((TextProperties) geo).setPrintFigures(getPrintFigures(),
false);
} else {
((TextProperties) geo).setPrintDecimals(getPrintDecimals(),
false);
}
}
geo.setFillType(fillType);
geo.setHatchingAngle(hatchingAngle);
geo.setHatchingDistance(hatchingDistance);
if (!geo.getGeoElementForPropertiesDialog().isGeoImage()) {
geo.setImageFileName(getGraphicsAdapter().getImageFileName());
}
geo.setAlphaValue(getAlphaValue());
geo.setLayer(getLayer());
// copy ShowObjectCondition, unless it generates a
// CirclularDefinitionException
try {
geo.setShowObjectCondition(getShowObjectCondition());
} catch (final Exception e) {
// Circular definition -- do nothing
}
setElementEuclidianVisible(geo, isSetEuclidianVisible());
}
}
@Override
public final void removeColorFunction() {
if (getColorFunction() == null) {
return;
}
super.removeColorFunction();
if ((geoList == null) || (geoList.size() == 0)) {
return;
}
final int size = geoList.size();
for (int i = 0; i < size; i++) {
final GeoElement geo = geoList.get(i);
if (!geo.isLabelSet()) {
geo.removeColorFunction();
}
}
}
@Override
public final void setColorFunction(final GeoList col) {
super.setColorFunction(col);
if ((geoList == null) || (geoList.size() == 0)) {
return;
}
final int size = geoList.size();
for (int i = 0; i < size; i++) {
final GeoElement geo = geoList.get(i);
if (!geo.isLabelSet()) {
geo.setColorFunction(col);
}
}
}
@Override
public final void setColorSpace(final int colorSpace) {
super.setColorSpace(colorSpace);
if ((geoList == null) || (geoList.size() == 0)) {
return;
}
final int size = geoList.size();
for (int i = 0; i < size; i++) {
final GeoElement geo = geoList.get(i);
if (!geo.isLabelSet()) {
geo.setColorSpace(colorSpace);
}
}
}
/*
* we DON'T want to do this, as objects without label set eg the point in
* this {(1,1)} are drawn by the list public final void setLayer(int layer)
* { super.setLayer(layer);
*
* if (geoList == null || geoList.size() == 0) return;
*
* int size = geoList.size(); for (int i=0; i < size; i++) { GeoElement geo
* = (GeoElement)geoList.get(i); if (!geo.isLabelSet()) geo.setLayer(layer);
* }
*
* }
*/
@Override
public final void setShowObjectCondition(final GeoBoolean bool)
throws CircularDefinitionException {
super.setShowObjectCondition(bool);
if ((geoList == null) || (geoList.size() == 0)) {
return;
}
final int size = geoList.size();
for (int i = 0; i < size; i++) {
final GeoElement geo = geoList.get(i);
if (!geo.isLabelSet()) {
geo.setShowObjectCondition(bool);
}
}
}
@Override
public void setVisualStyle(final GeoElement style) {
super.setVisualStyle(style);
// set point style
if (style instanceof PointProperties) {
setPointSize(((PointProperties) style).getPointSize());
setPointStyle(((PointProperties) style).getPointStyle());
}
// set visual style
if ((geoList == null) || (geoList.size() == 0)) {
return;
}
final int size = geoList.size();
for (int i = 0; i < size; i++) {
final GeoElement geo = geoList.get(i);
if (!geo.isLabelSet()) {
geo.setVisualStyle(style);
}
}
}
@Override
public void setObjColor(final GColor color) {
super.setObjColor(color);
if ((geoList == null) || (geoList.size() == 0)) {
return;
}
final int size = geoList.size();
for (int i = 0; i < size; i++) {
final GeoElement geo = get(i);
if (!geo.isLabelSet()) {
geo.setObjColor(color);
}
}
}
@Override
public void setBackgroundColor(final GColor color) {
super.setBackgroundColor(color);
if ((geoList == null) || (geoList.size() == 0)) {
return;
}
final int size = geoList.size();
for (int i = 0; i < size; i++) {
final GeoElement geo = get(i);
if (!geo.isLabelSet()) {
geo.setBackgroundColor(color);
}
}
}
@Override
public void setEuclidianVisible(final boolean visible) {
super.setEuclidianVisible(visible);
if ((geoList == null) || (geoList.size() == 0)) {
return;
}
if (visible && drawAsComboBox && labelOffsetX == 0
&& labelOffsetY == 0) {
initScreenLocation();
}
final int size = geoList.size();
for (int i = 0; i < size; i++) {
final GeoElement geo = get(i);
setElementEuclidianVisible(geo, visible);
}
}
private void initScreenLocation() {
int count = countComboBoxes();
labelOffsetX = 5;
EuclidianViewInterfaceSlim ev = kernel.getApplication()
.getActiveEuclidianView();
if (ev != null) {
labelOffsetY = ev.getComboOffsetY() - 45 + 30 * count;
} else {
labelOffsetY = 5 + 30 * count;
}
// make sure combobox is visible on screen
labelOffsetY = labelOffsetY / 400 * 10 + labelOffsetY % 400;
}
private int countComboBoxes() {
int count = 0;
// get all number and angle sliders
TreeSet<GeoElement> lists = cons.getGeoSetLabelOrder(GeoClass.LIST);
if (lists != null) {
Iterator<GeoElement> it = lists.iterator();
while (it.hasNext()) {
GeoList list = (GeoList) it.next();
if (list.isEuclidianVisible() && list.drawAsComboBox()) {
count++;
}
}
}
return count;
}
private static void setElementEuclidianVisible(final GeoElement geo,
final boolean visible) {
if (!geo.isLabelSet() && !geo.isGeoNumeric()) {
geo.setEuclidianVisible(visible);
}
}
@Override
public void setVisibility(int viewId, boolean setVisible) {
super.setVisibility(viewId, setVisible);
if ((geoList == null) || (geoList.size() == 0)) {
return;
}
final int size = geoList.size();
for (int i = 0; i < size; i++) {
final GeoElement geo = get(i);
if (!geo.isLabelSet()) {
geo.setVisibility(viewId, setVisible);
}
}
}
/**
* Returns this GeoList as a MyList object.
*/
@Override
public MyList getMyList() {
final int size = geoList.size();
final MyList myList = new MyList(kernel, size);
for (int i = 0; i < size; i++) {
myList.addListElement(new ExpressionNode(kernel, geoList.get(i)));
}
return myList;
}
@Override
final public boolean isDefined() {
return isDefined;
}
/**
* @param flag
* true to make this list defined
*/
public void setDefined(final boolean flag) {
isDefined = flag;
if (!isDefined) {
final int size = geoList.size();
for (int i = 0; i < size; i++) {
final GeoElement geo = geoList.get(i);
if (!geo.isLabelSet()) {
geo.setUndefined();
}
}
// set also cached geos to undefined (for lists of lists)
for (int i = size; i < cacheList.size(); i++) {
final GeoElementND geo = cacheList.get(i);
if (!geo.isLabelSet()) {
geo.setUndefined();
}
}
}
}
@Override
public void setUndefined() {
setDefined(false);
}
@Override
protected boolean showInEuclidianView() {
return isDefined() && isDrawable();
}
@Override
public boolean isDrawable() {
return isDrawable || drawAsComboBox();
}
@Override
public boolean showInAlgebraView() {
return true;
}
/**
* Clear the list
*/
public final void clear() {
geoList.clear();
}
/**
* free up memory and set undefined
*/
public final void clearCache() {
if (cacheList.size() > 0) {
for (int i = 0; i < cacheList.size(); i++) {
final GeoElementND geo = cacheList.get(i);
if ((geo != null) && !geo.isLabelSet()) {
geo.remove();
}
}
}
cacheList.clear();
clear();
setUndefined();
}
/**
* Adds a geo to this list
*
* @param geo
* geo to be added
*/
public final void add(final GeoElementND geo) {
// add geo to end of list
geoList.add(geo.toGeoElement());
if (geoList.size() == 1) {
setTypeStringForXML(geo.getXMLtypeString());
}
/*
* // needed for Corner[Element[text // together with swapping these
* lines over in MyXMLio: //kernel.updateConstruction();
* //kernel.setNotifyViewsActive(oldVal);
*
* if (geo.isGeoText()) {
* ((GeoText)geo).setNeedsUpdatedBoundingBox(true); }
*/
// add to cache
final int pos = geoList.size() - 1;
if (pos < cacheList.size()) {
cacheList.set(pos, geo);
} else {
cacheList.add(geo);
}
// init element type
if (pos == 0) {
isDrawable = true;
elementType = geo.getGeoClassType();
}
// check element type
else if (elementType != geo.getGeoClassType()) {
if ((elementType == GeoClass.POINT3D
|| elementType == GeoClass.POINT) && geo.isGeoPoint()) {
elementType = GeoClass.POINT3D;
} else {
elementType = ELEMENT_TYPE_MIXED;
}
}
updateDrawableFlag(geo);
// set visual style of this list
applyVisualStyle(geo.toGeoElement());
if (!geo.isLabelSet()) {
geo.setViewFlags(getViewSet());
geo.setVisibleInView3D(this);
geo.setVisibleInViewForPlane(this);
}
}
private void updateDrawableFlag(GeoElementND geo) {
isDrawable = isDrawable && geo.isDrawable() && !geo.isGeoButton()
&& !(geo instanceof GeoBoolean) && !(geo instanceof GeoNumeric
&& ((GeoNumeric) geo).isSlider());
}
/**
* Removes geo from this list. Note: geo is not removed from the
* construction.
*
* @param geo
* element to be removed
*/
public final void remove(final GeoElement geo) {
geoList.remove(geo);
}
/**
* Removes the first geo from the beginning of this list, and adds a new geo
* to its end (useful for indefinite appending)
*
* @param geo
* element to be added
*/
public final void addQueue(final GeoElement geo) {
GeoElement first = get(0);
remove(first);
first.remove();
add(geo);
}
/**
* Removes i-th element from this list. Note: this element is not removed
* from the construction.
*
* @param index
* position of element to be removed
*/
public final void remove(final int index) {
geoList.remove(index);
}
/**
* Returns the element at the specified position in this list.
*
* @param index
* element position
* @return the element at the specified position in this list.
*/
final public GeoElement get(final int index) {
return geoList.get(index);
}
/**
* Returns the element at the specified position in this (2D) list.
*
* @param index
* element position -- row
* @param index2
* element position -- column
* @return the element at the specified position in this (2D) list.
*/
final public GeoElement get(final int index, final int index2) {
return ((GeoList) geoList.get(index)).get(index2);
}
/**
* Tries to return this list as an array of double values
*
* @return array of double values from this list
*/
@Override
public double[] toDouble(int offset) {
int length = geoList.size();
try {
final double[] valueArray = new double[length - offset];
for (int i = offset; i < length; i++) {
valueArray[i - offset] = geoList.get(i).evaluateDouble();
}
return valueArray;
} catch (final Exception e) {
return null;
}
}
/**
* Increases capcity of this list if necessary
*
* @param size
* capcity to ensure
*/
final public void ensureCapacity(final int size) {
geoList.ensureCapacity(size);
cacheList.ensureCapacity(size);
}
@Override
final public int size() {
return geoList.size();
}
/**
* @return number of elements in this list's cache
*/
final public int getCacheSize() {
return cacheList.size();
}
/**
* Returns the cached element at the specified position in this list's
* cache.
*
* @param index
* element position
* @return cached element at given position
*/
final public GeoElement getCached(final int index) {
return cacheList.get(index).toGeoElement();
}
@Override
public String toString(StringTemplate tpl) {
StringBuilder sbToString = new StringBuilder(50);
sbToString.setLength(0);
sbToString.append(label);
sbToString.append(" = ");
sbToString.append(buildValueString(tpl).toString());
return sbToString.toString();
}
@Override
public String toStringMinimal(StringTemplate tpl) {
sbBuildValueString.setLength(0);
if (!isDefined) {
sbBuildValueString.append("?");
return sbBuildValueString.toString();
}
// first (n-1) elements
final int lastIndex = geoList.size() - 1;
if (lastIndex > -1) {
for (int i = 0; i < lastIndex; i++) {
final GeoElement geo = geoList.get(i);
sbBuildValueString
.append(geo.getAlgebraDescriptionRegrOut(tpl));
sbBuildValueString.append(" ");
}
// last element
final GeoElement geo = geoList.get(lastIndex);
sbBuildValueString.append(geo.getAlgebraDescriptionRegrOut(tpl));
}
return sbBuildValueString.toString();
}
@Override
public String toValueString(StringTemplate tpl) {
return buildValueString(tpl).toString();
}
private StringBuilder buildValueString(StringTemplate tpl) {
sbBuildValueString.setLength(0);
if (!isDefined) {
sbBuildValueString.append("?");
return sbBuildValueString;
}
tpl.leftCurlyBracket(sbBuildValueString);
// first (n-1) elements
final int lastIndex = geoList.size() - 1;
if (lastIndex > -1) {
for (int i = 0; i < lastIndex; i++) {
final GeoElement geo = geoList.get(i);
sbBuildValueString.append(geo.toOutputValueString(tpl));
sbBuildValueString.append(getLoc().unicodeComma);
sbBuildValueString.append(" ");
}
// last element
final GeoElement geo = geoList.get(lastIndex);
sbBuildValueString.append(geo.toOutputValueString(tpl));
}
tpl.rightCurlyBracket(sbBuildValueString);
return sbBuildValueString;
}
private final StringBuilder sbBuildValueString = new StringBuilder(50);
@Override
public boolean isGeoList() {
return true;
}
@Override
public boolean evaluatesToList() {
return true;
}
/**
* save object in XML format
*/
@Override
public final void getXML(boolean getListenersToo, final StringBuilder sb) {
// an independent list needs to add
// its expression itself
// e.g. {1,2,3}
if (isIndependent() && (getDefaultGeoType() < 0)) {
sb.append("<expression");
sb.append(" label =\"");
StringUtil.encodeXML(sb, label);
sb.append("\" exp=\"");
if (getDefinition() != null) {
StringUtil.encodeXML(sb,
getDefinition().toString(StringTemplate.xmlTemplate));
} else {
StringUtil.encodeXML(sb,
toValueString(StringTemplate.xmlTemplate));
}
sb.append("\"/>\n");
}
sb.append("<element");
sb.append(" type=\"list\"");
sb.append(" label=\"");
sb.append(label);
if (getDefaultGeoType() >= 0) {
sb.append("\" default=\"");
sb.append(getDefaultGeoType());
}
sb.append("\">\n");
getXMLtags(sb);
if (size() == 0 && getTypeStringForXML() != null) {
sb.append("<listType val=\"");
sb.append(getTypeStringForXML());
sb.append("\"/>\n");
}
if (selectedIndex != 0) {
sb.append("\t<selectedIndex val=\"");
sb.append(selectedIndex);
sb.append("\"/>\n");
}
if (drawAsComboBox) {
sb.append("\t<comboBox val=\"true\"/>\n");
}
// point style
sb.append("\t<pointSize val=\"");
sb.append(pointSize);
sb.append("\"/>\n");
sb.append("\t<pointStyle val=\"");
sb.append(pointStyle);
sb.append("\"/>\n");
GeoText.appendFontTag(sb, serifFont, fontSizeD, fontStyle, false,
kernel.getApplication());
// print decimals
if ((printDecimals >= 0) && !useSignificantFigures) {
sb.append("\t<decimals val=\"");
sb.append(printDecimals);
sb.append("\"/>\n");
}
// print significant figures
if ((printFigures >= 0) && useSignificantFigures) {
sb.append("\t<significantfigures val=\"");
sb.append(printFigures);
sb.append("\"/>\n");
}
// AngleProperties
if (angleStyle != AngleStyle.ANTICLOCKWISE) {
sb.append("\t<allowReflexAngle val=\"");
sb.append(angleStyle != AngleStyle.NOTREFLEX);
sb.append("\"/>\n");
}
if (angleStyle == AngleStyle.ISREFLEX) {
sb.append("\t<forceReflexAngle val=\"");
sb.append(true);
sb.append("\"/>\n");
}
if (!emphasizeRightAngle) {
// only store emphasizeRightAngle if "false"
sb.append("\t<emphasizeRightAngle val=\"");
sb.append(emphasizeRightAngle);
sb.append("\"/>\n");
}
if (isSymbolicMode()) {
sb.append("\t<symbolic val=\"true\" />\n");
}
// AngleProperties end
// for ComboBoxes (and comments)
getCaptionXML(sb);
if (getListenersToo) {
getListenerTagsXML(sb);
}
sb.append("</element>\n");
}
// needed for eg x(Element[list1,1]) when list1 is saved as an empty list
// The Element command needs to know in advance what type of geo the list
// will hold
private String typeStringForXML = null;
/**
* needed for eg x(Element[list1,1]) when list1 is saved as an empty list
* The Element/Cell/Object command needs to know in advance what type of geo
* the list will hold
*
* @return type, eg "point"
*/
public String getTypeStringForXML() {
return typeStringForXML;
}
/**
* needed for eg x(Element[list1,1]) when list1 is saved as an empty list
* The Element/Cell/Object command needs to know in advance what type of geo
* the list will hold
*
* @param type
* eg "point"
*/
public void setTypeStringForXML(String type) {
typeStringForXML = type;
}
/**
* Registers geo as a listener for updates of this boolean object. If this
* object is updated it calls geo.updateConditions()
*
* @param geo
* element which should use this list as dynamic color
*/
public void registerColorFunctionListener(final GeoElement geo) {
if (colorFunctionListener == null) {
colorFunctionListener = new ArrayList<GeoElement>();
}
colorFunctionListener.add(geo);
}
/**
* Unregisters geo as a listener for updates of this boolean object.
*
* @param geo
* element which uses this list as dynamic color
*/
public void unregisterColorFunctionListener(final GeoElement geo) {
if (colorFunctionListener != null) {
colorFunctionListener.remove(geo);
}
}
/**
* Calls super.update() and update() for all registered condition listener
* geos. // Michael Borcherds 2008-04-02
*/
@Override
public void update(boolean drag) {
super.update(drag);
// update information on whether this path is fit for AlgoLocus
// or it can only support AlgoLocusList (call this method here again
// because the GeoList might change - code will run only if locus used
// it)
shouldUseAlgoLocusList(false);
// update all registered locatables (they have this point as start
// point)
if (colorFunctionListener != null) {
// AbstractApplication.debug("GeoList update listeners");
for (int i = 0; i < colorFunctionListener.size(); i++) {
final GeoElement geo = colorFunctionListener.get(i);
// kernel.notifyUpdate(geo);
// geo.toGeoElement().updateCascade();
geo.updateVisualStyle(GProperty.COLOR);
// AbstractApplication.debug(geo);
}
// kernel.notifyRepaint();
}
}
/**
* Tells condition listeners that their condition is removed and calls
* super.remove()
*/
@Override
public void doRemove() {
if (colorFunctionListener != null) {
// copy conditionListeners into array
final Object[] geos = colorFunctionListener.toArray();
colorFunctionListener.clear();
// tell all condition listeners
for (int i = 0; i < geos.length; i++) {
final GeoElement geo = (GeoElement) geos[i];
geo.removeColorFunction();
kernel.notifyUpdate(geo);
}
}
super.doRemove();
}
/**
* return whether this list equals GeoList list
*/
@Override
final public boolean isEqual(final GeoElementND geo) {
if (!geo.isGeoList()) {
return false;
}
final GeoList list = (GeoList) geo;
// check sizes
if (geoList.size() != list.size()) {
return false;
}
// check each element
for (int i = 0; i < list.geoList.size(); i++) {
final GeoElement geoA = geoList.get(i);
final GeoElement geoB = list.get(i);
if (!geoA.isEqual(geoB)) {
return false;
}
}
// all list elements equal
return true;
}
@Override
public void setZero() {
geoList.clear();
}
@Override
public void setLineThickness(final int thickness) {
super.setLineThickness(thickness);
if ((geoList == null) || (geoList.size() == 0)) {
return;
}
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (!geo.isLabelSet()) {
geo.setLineThickness(thickness);
}
}
// Application.debug("GeoList.setLineThickness "+thickness);
}
/**
* @return minimum line thickness (normally 1, but 0 for polygons, integrals
* etc)
*/
@Override
public int getMinimumLineThickness() {
if ((geoList == null) || (geoList.size() == 0)) {
return 1;
}
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (!geo.isLabelSet()) {
if (geo.getMinimumLineThickness() == 1) {
return 1;
}
}
}
return 0;
}
@Override
public void setLineType(final int type) {
super.setLineType(type);
if ((geoList == null) || (geoList.size() == 0)) {
return;
}
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (!geo.isLabelSet()) {
geo.setLineType(type);
}
}
// Application.debug("GeoList.setLineType");
}
private int pointSize = EuclidianStyleConstants.DEFAULT_POINT_SIZE;
private int pointStyle = -1; // use global option if -1
@Override
public void setPointSize(final int size) {
pointSize = size;
if ((geoList == null) || (geoList.size() == 0)) {
return;
}
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (!geo.isLabelSet() && (geo instanceof PointProperties)) {
((PointProperties) geo).setPointSize(size);
}
}
}
@Override
public int getPointSize() {
return pointSize;
}
@Override
public void setPointStyle(final int style) {
pointStyle = style;
if ((geoList == null) || (geoList.size() == 0)) {
return;
}
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (!geo.isLabelSet() && (geo instanceof PointProperties)) {
((PointProperties) geo).setPointStyle(style);
}
}
}
@Override
public double getAlphaValue() {
if (super.getAlphaValue() == -1) {
// no alphaValue set
// so we need to set it to that of the first element, if there is
// one
if ((geoList != null) && (geoList.size() > 0)) {
// get alpha value of first element
final double alpha = geoList.get(0).getAlphaValue();
// Application.debug("setting list alpha to "+alpha);
super.setAlphaValue(alpha);
// set all the other elements in the list
// if appropriate
if (geoList.size() > 1) {
for (int i = 1; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (!geo.isLabelSet()) {
geo.setAlphaValue(alpha);
}
}
}
} else {
return -1.0f;
}
}
return super.getAlphaValue();
}
@Override
public void setAlphaValue(final double alpha) {
if (alpha == -1) {
// wait until we have a GeoElement in the list to use
// see getAlphaValue()
alphaValue = -1;
return;
}
super.setAlphaValue(alpha);
if ((geoList == null) || (geoList.size() == 0)) {
return;
}
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (!geo.isLabelSet()) {
geo.setAlphaValue(alpha);
}
}
}
@Override
public int getPointStyle() {
return pointStyle;
}
@Override
public boolean isFillable() {
if ((geoList == null) || (geoList.size() == 0)) {
return false;
}
boolean someFillable = false;
boolean allLabelsSet = true;
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (geo.isFillable()) {
someFillable = true;
}
if (!geo.isLabelSet()) {
allLabelsSet = false;
}
}
return someFillable && !allLabelsSet;
}
@Override
public GeoElement getGeoElementForPropertiesDialog() {
if ((geoList.size() > 0) && (elementType != ELEMENT_TYPE_MIXED)) {
return get(0).getGeoElementForPropertiesDialog(); // getGeoElementForPropertiesDialog()
// to cope with
// lists of
// lists
}
return this;
}
/**
* @return true iff this list is in the form { {1,2}, {3,4}, {5,6} } etc
*/
@Override
public boolean isMatrix() {
if (!getElementType().equals(GeoClass.LIST) || (size() == 0)) {
return false;
}
final GeoElement geo0 = get(0);
if (geo0.isGeoList()) {
final int length = ((GeoList) geo0).size();
if (length == 0) {
return false;
}
if (size() > 0) {
for (int i = 0; i < size(); i++) {
final GeoElement geoi = get(i);
// Application.debug(((GeoList)geoi).get(0).getGeoClassType()+"");
if (!get(i).isGeoList() || (((GeoList) geoi).size() == 0)
|| (((GeoList) geoi).size() != length)) {
return false;
}
for (int j = 0; j < ((GeoList) geoi).size(); j++) {
final GeoElement geoij = ((GeoList) geoi).get(j);
if (!geoij.getGeoClassType().equals(GeoClass.NUMERIC)
&& !geoij.getGeoClassType()
.equals(GeoClass.FUNCTION)
&& !geoij.getGeoClassType()
.equals(GeoClass.FUNCTION_NVAR)) {
return false;
}
}
}
}
}
return true;
}
// font options
private boolean serifFont = false;
private int fontStyle = GFont.PLAIN;
private double fontSizeD = 1; // size relative to default font size
private int printDecimals = -1;
private int printFigures = -1;
private boolean useSignificantFigures = false;
@Override
public double getFontSizeMultiplier() {
return fontSizeD;
}
@Override
public void setFontSizeMultiplier(final double size) {
fontSizeD = size;
if ((geoList == null) || (geoList.size() == 0)) {
return;
}
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if ((geo instanceof TextProperties) && !geo.isLabelSet()) {
((TextProperties) geo).setFontSizeMultiplier(size);
}
}
}
@Override
public int getFontStyle() {
return fontStyle;
}
@Override
public void setFontStyle(final int fontStyle) {
this.fontStyle = fontStyle;
if ((geoList == null) || (geoList.size() == 0)) {
return;
}
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if ((geo instanceof TextProperties) && !geo.isLabelSet()) {
((TextProperties) geo).setFontStyle(fontStyle);
}
}
}
@Override
final public int getPrintDecimals() {
return printDecimals;
}
@Override
final public int getPrintFigures() {
return printFigures;
}
@Override
public void setPrintDecimals(final int printDecimals,
final boolean update) {
this.printDecimals = printDecimals;
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if ((geo instanceof TextProperties) && !geo.isLabelSet()) {
((TextProperties) geo).setPrintDecimals(printDecimals, update);
}
}
}
@Override
public void setPrintFigures(final int printFigures, final boolean update) {
this.printFigures = printFigures;
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if ((geo instanceof TextProperties) && !geo.isLabelSet()) {
((TextProperties) geo).setPrintFigures(printFigures, update);
}
}
}
@Override
public boolean useSignificantFigures() {
return useSignificantFigures;
}
@Override
public boolean isSerifFont() {
return serifFont;
}
@Override
public void setSerifFont(final boolean serifFont) {
this.serifFont = serifFont;
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if ((geo instanceof TextProperties) && !geo.isLabelSet()) {
((TextProperties) geo).setSerifFont(serifFont);
}
}
}
@Override
public void setHatchingAngle(final int angle) {
super.setHatchingAngle(angle);
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (!geo.isLabelSet()) {
geo.setHatchingAngle(angle);
}
}
}
@Override
public void setHatchingDistance(final int distance) {
super.setHatchingDistance(distance);
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (!geo.isLabelSet()) {
geo.setHatchingDistance(distance);
}
}
}
@Override
public void setFillType(final FillType type) {
super.setFillType(type);
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (!geo.isLabelSet()) {
geo.setFillType(type);
}
}
}
@Override
public void setFillImage(final String filename) {
super.setFillImage(filename);
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (!geo.isLabelSet()) {
geo.setFillImage(filename);
}
}
}
@Override
public void setImageFileName(final String filename) {
super.setImageFileName(filename);
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (!geo.isLabelSet()) {
geo.setImageFileName(filename);
}
}
}
/**
* for a list like this: {Circle[B, A], (x(A), y(A)), "text"} we want to be
* able to set the line properties
*
* @return true if all elements have line properties
*/
@Override
public boolean showLineProperties() {
if (showAllProperties) {
return true;
}
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (geo.showLineProperties() && !geo.isLabelSet()) {
return true;
}
}
return false;
}
/**
* for a list like this: {Circle[B, A], (x(A), y(A)), "text"} we want to be
* able to set the point properties
*
* @return true if all elements have point properties
*/
public boolean showPointProperties() {
if (showAllProperties) {
return true;
}
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if ((geo instanceof PointProperties) && !geo.isLabelSet()) {
return true;
}
}
return false;
}
@Override
public String toLaTeXString(final boolean symbolic, StringTemplate tpl) {
if (isMatrix()) {
// int rows = size();
final int cols = ((GeoList) get(0)).size();
final StringBuilder sb = new StringBuilder();
sb.append("\\left(\\begin{array}{");
// eg rr
for (int i = 0; i < cols; i++) {
sb.append('r');
}
sb.append("}");
for (int i = 0; i < size(); i++) {
final GeoList geo = (GeoList) get(i);
for (int j = 0; j < geo.size(); j++) {
sb.append(geo.get(j).toLaTeXString(symbolic, tpl));
if (j < (geo.size() - 1)) {
sb.append("&");
}
}
sb.append("\\\\");
}
sb.append(" \\end{array}\\right)");
return sb.toString();
// return "\\begin{array}{ll}1&2 \\\\ 3&4 \\\\ \\end{array}";
}
return super.toLaTeXString(symbolic, tpl);
}
@Override
protected void getXMLtags(final StringBuilder sb) {
super.getXMLtags(sb);
getLineStyleXML(sb);
getScriptTags(sb);
}
// Selection index for lists used in comboBoxes
private int selectedIndex = 0;
private int closestPointIndex;
/**
* @return selected index
*/
public int getSelectedIndex() {
if (selectedIndex >= size()) {
selectedIndex = 0;
}
return selectedIndex;
}
/**
* @param selectedIndex0
* new selected index
* @param update
* t
*/
public void setSelectedIndex(final int selectedIndex0, boolean update) {
selectedIndex = selectedIndex0;
if (selectedIndex < 0 || selectedIndex > size() - 1) {
selectedIndex = 0;
}
if (update) {
updateCascade();
getKernel().notifyRepaint();
getKernel().storeUndoInfo();
}
}
/*
* mathieu : for drawing 3D elements of the list
*/
@Override
public boolean hasDrawable3D() {
return true;
}
/**
* when list is visible (as a combobox) this returns the element selected by
* the user or null if there's a problem
*
* @return selected element
*/
public GeoElement getSelectedElement() {
if ((selectedIndex > -1) && (selectedIndex < size())) {
return get(selectedIndex);
}
return null;
}
@Override
public void setTrace(final boolean trace) {
this.trace = trace;
}
@Override
public boolean getTrace() {
return trace;
}
@Override
public boolean isTraceable() {
return true;
}
@Override
public boolean isLimitedPath() {
return false;
}
@Override
public boolean isPath() {
return true;
}
/*
* adapted from GeoLocus
*/
@Override
public void pointChanged(final GeoPointND P) {
// Application.debug("pointChanged",1);
P.updateCoords();
// update closestPointIndex
getNearestPoint(P);
if (geoList.size() == 0) {
if (P.isDefined()) {
P.setUndefined();
}
return;
}
final GeoElement geo = get(closestPointIndex);
if (!(geo instanceof PathOrPoint)) {
Log.debug("TODO: " + geo.getGeoClassType()
+ " should implement PathOrPoint interface");
return;
}
final PathOrPoint path = (PathOrPoint) get(closestPointIndex);
int type = P.getPathParameter().getPathType();
path.pointChanged(P);
final PathParameter pp = P.getPathParameter();
pp.setPathType(type);
// update path param
// 0-1 for first obj
// 1-2 for second
// etc
// Application.debug(pp.t+" "+path.getMinParameter()+"
// "+path.getMaxParameter());
int closestPointIndexBack = closestPointIndex;
if (directionInfoOrdering != null) {
for (int i = 0; i < this.size(); i++) {
if (directionInfoOrdering[i] == closestPointIndex) {
closestPointIndexBack = i;
break;
}
}
}
if ((directionInfoArray == null)
|| directionInfoArray[closestPointIndex]) {
pp.t = closestPointIndexBack
+ PathNormalizer.toNormalizedPathParameter(pp.t,
path.getMinParameter(), path.getMaxParameter());
} else {
pp.t = closestPointIndexBack + 1
- PathNormalizer.toNormalizedPathParameter(pp.t,
path.getMinParameter(), path.getMaxParameter());
}
// Application.debug(pp.t);
}
/**
* Nearest point to p
*
* @param p
* point
*/
public void getNearestPoint(final GeoPointND p) {
// Application.printStacktrace(p.inhomX+" "+p.inhomY);
double distance = Double.POSITIVE_INFINITY;
closestPointIndex = 0; // default - first object
// double closestIndex = -1;
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (geo instanceof PathOrPoint) {
final double d = p.distanceToPath((PathOrPoint) geo);
// Log.debug(i+" "+d+" "+distance+" "+geo);
if (d < distance) {
distance = d;
closestPointIndex = i;
}
}
}
// Application.debug("closestPointIndex="+closestPointIndex);
// return get(closestPointIndex).getNearestPoint(p);
}
@Override
public double distance(final GeoPoint p) {
double distance = Double.POSITIVE_INFINITY;
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
final double d = geo.distance(p);
if (d < distance) {
distance = d;
}
}
return distance;
}
@Override
public double distance(final GeoPointND p) {
double distance = Double.POSITIVE_INFINITY;
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
final double d = geo.distance(p);
if (d < distance) {
distance = d;
}
}
return distance;
}
@Override
public void pathChanged(final GeoPointND PI) {
if (size() == 0) {
PI.setUndefined();
return;
}
// if kernel doesn't use path/region parameters, do as if point changed
// its coords
if (!getKernel().usePathAndRegionParameters(PI)) {
pointChanged(PI);
return;
}
final PathParameter pp = PI.getPathParameter();
double t = pp.getT();
int n0 = (int) Math.floor(t);
int n = n0;
// check n is in a sensible range
if ((n >= size()) || (n < 0)) {
n = (n < 0) ? 0 : size() - 1;
}
int n1 = n;
if (directionInfoOrdering != null) {
n = directionInfoOrdering[n];
}
GeoElement elementN = get(n);
if (!(elementN instanceof PathOrPoint)) {
Log.debug("not path or point");
return;
}
final PathOrPoint path = (PathOrPoint) elementN;
int pt = pp.getPathType();
if (path instanceof GeoQuadricND) {
pp.setPathType(((GeoQuadricND) path).getType());
}
// check direction of the path, as it is not sure that the main list
// path
// has the same direction for minParameter and maxParameter as the
// subpathes
if ((directionInfoArray == null) || directionInfoArray[n]) {
pp.setT(PathNormalizer.toParentPathParameter(t - n1,
path.getMinParameter(), path.getMaxParameter()));
} else {
pp.setT(PathNormalizer.toParentPathParameter(n1 - t + 1,
path.getMinParameter(), path.getMaxParameter()));
}
// Application.debug("pathChanged "+n);
path.pathChanged(PI);
t = pp.getT();
// Application.debug(PathNormalizer.toNormalizedPathParameter(t,
// path.getMinParameter(), path.getMaxParameter()));
if ((directionInfoArray == null) || directionInfoArray[n]) {
pp.setT(PathNormalizer.toNormalizedPathParameter(t,
path.getMinParameter(), path.getMaxParameter()) + n1);
} else {
pp.setT(1
- PathNormalizer.toNormalizedPathParameter(t,
path.getMinParameter(), path.getMaxParameter())
+ n1);
}
pp.setPathType(pt);
}
@Override
public boolean isOnPath(final GeoPointND PI, final double eps) {
// Application.debug("isOnPath",1);
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (((PathOrPoint) geo).isOnPath(PI, eps)) {
return true;
}
}
return false;
}
@Override
public double getMinParameter() {
return 0;
}
@Override
public double getMaxParameter() {
return geoList.size();
}
@Override
public boolean isClosedPath() {
return !shouldUseAlgoLocusList;
}
@Override
public PathMover createPathMover() {
return new PathMoverGeneric(this);
}
@Override
public boolean justFontSize() {
return false;
}
@Override
public boolean hasMoveableInputPoints(
final EuclidianViewInterfaceSlim view) {
// we don't want e.g. DotPlots to be dragged
if (!((getParentAlgorithm() == null)
|| (getParentAlgorithm() instanceof AlgoDependentList))) {
return false;
}
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (geo.isGeoPoint()) {
if (!geo.isMoveable()) {
return false;
}
} else {
// not point
if (!geo.hasMoveableInputPoints(view)) {
return false;
}
}
}
return true;
}
/*
* allow lists like this to be dragged {Segment[A, B], Segment[B, C], (3.92,
* 4)}
*/
@Override
public ArrayList<GeoPointND> getFreeInputPoints(
final EuclidianViewInterfaceSlim view) {
final ArrayList<GeoPointND> al = new ArrayList<GeoPointND>();
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (geo.isGeoPoint()) {
final GeoPoint p = (GeoPoint) geo;
if (p.isMoveable() && !al.contains(p)) {
al.add(p);
}
} else {
final ArrayList<GeoPointND> al2 = geo.getFreeInputPoints(view);
if (al2 != null) {
for (int j = 0; j < al2.size(); j++) {
final GeoPointND p = al2.get(j);
// make sure duplicates aren't added
if (!al.contains(p)) {
al.add(p);
}
}
}
}
}
return al;
}
@Override
final public boolean isCasEvaluableObject() {
return true;
}
@Override
public String getCASString(StringTemplate tpl, final boolean symbolic) {
// isMatrix() is rather expensive, and we only need it
// if we're using Maxima, so test for that first
final StringType casPrinttype = tpl.getStringType();
if ((!casPrinttype.isGiac()) || !isMatrix()) {
return super.getCASString(tpl, symbolic);
}
final StringBuilder sb = new StringBuilder();
if (casPrinttype.isGiac()) {
sb.append("matrix(");
for (int i = 0; i < size(); i++) {
final GeoList geo = (GeoList) get(i);
sb.append('[');
for (int j = 0; j < geo.size(); j++) {
sb.append(geo.get(j).getCASString(tpl, symbolic));
if (j != (geo.size() - 1)) {
sb.append(',');
}
}
sb.append(']');
if (i != (size() - 1)) {
sb.append(',');
}
}
sb.append(')');
} else {
sb.append("mat(");
for (int i = 0; i < size(); i++) {
final GeoList geo = (GeoList) get(i);
sb.append("(");
for (int j = 0; j < geo.size(); j++) {
sb.append(geo.get(j).getCASString(tpl, symbolic));
if (j != (geo.size() - 1)) {
sb.append(',');
}
}
sb.append(')');
if (i != (size() - 1)) {
sb.append(',');
}
}
sb.append(')');
}
return sb.toString();
}
/**
* Returns true if this list contains given geo (check based on ==, not
* value equality)
*
* @param geo
* geo to check
* @return true if the list contains given geo
*/
public boolean listContains(final GeoElement geo) {
if (geoList == null) {
return true;
}
return geoList.contains(geo);
}
@Override
public boolean isLaTeXDrawableGeo() {
if (size() == 0) {
return false;
}
// check for matrix
if (getElementType().equals(GeoClass.LIST)) {
GeoClass geoClass = ((GeoList) get(0)).getElementType();
return geoClass.equals(GeoClass.NUMERIC)
|| geoClass.equals(GeoClass.FUNCTION)
|| geoClass.equals(GeoClass.NUMERIC);
}
// don't check getGeoElementForPropertiesDialog
// as we want matrices to use latex
if (getElementType().equals(GeoClass.NUMERIC)) {
return false;
}
boolean ret = true;
for (int i = 0; i < geoList.size(); i++) {
GeoElement geo1 = geoList.get(i);
if (!geo1.isLaTeXDrawableGeo()) {
return false;
}
}
return ret;
}
@Override
public void updateColumnHeadingsForTraceValues() {
resetSpreadsheetColumnHeadings();
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (geo instanceof SpreadsheetTraceable) {
final ArrayList<GeoText> geoHead = geo.getColumnHeadings();
for (int j = 0; j < geoHead.size(); j++) {
spreadsheetColumnHeadings.add(geoHead.get(j));
}
}
}
}
private TraceModesEnum traceModes = null;
private boolean showOnAxis;
/**
* @param geos
* list of geos
* @return available trace to spreadsheet mode (values/copy) for the geos
* list
*/
final static public TraceModesEnum getTraceModes(
ArrayList<GeoElement> geos) {
TraceModesEnum traceModes = null;
for (GeoElement geo : geos) {
if (!geo.isSpreadsheetTraceable()) {
traceModes = TraceModesEnum.NOT_TRACEABLE;
return traceModes;
}
TraceModesEnum geoMode = geo.getTraceModes();
if (traceModes == null) {
traceModes = geoMode;
if (geoMode == TraceModesEnum.NOT_TRACEABLE) {
return traceModes;
}
} else {
switch (geoMode) {
case NOT_TRACEABLE:
traceModes = TraceModesEnum.NOT_TRACEABLE;
return traceModes;
case ONE_VALUE_ONLY:
case SEVERAL_VALUES_ONLY:
if (traceModes == TraceModesEnum.ONLY_COPY) {
traceModes = TraceModesEnum.NOT_TRACEABLE;
return traceModes;
}
traceModes = TraceModesEnum.SEVERAL_VALUES_ONLY;
break;
case ONE_VALUE_OR_COPY:
case SEVERAL_VALUES_OR_COPY:
if (traceModes == TraceModesEnum.ONE_VALUE_ONLY) {
traceModes = TraceModesEnum.SEVERAL_VALUES_ONLY;
} else if (traceModes == TraceModesEnum.ONE_VALUE_OR_COPY) {
traceModes = TraceModesEnum.SEVERAL_VALUES_OR_COPY;
}
break;
case ONLY_COPY:
if (traceModes == TraceModesEnum.ONE_VALUE_ONLY
|| traceModes == TraceModesEnum.SEVERAL_VALUES_ONLY) {
traceModes = TraceModesEnum.NOT_TRACEABLE;
return traceModes;
}
traceModes = TraceModesEnum.ONLY_COPY;
break;
}
}
}
return traceModes;
}
@Override
public TraceModesEnum getTraceModes() {
if (traceModes != null) {
return traceModes;
}
if (getParentAlgorithm() != null
&& (getParentAlgorithm() instanceof AlgoDependentList)) {
// list = {A, B} : traceModes is computed from A, B
traceModes = getTraceModes(geoList);
} else {
// e.g. Sequence[...] is only copied
traceModes = TraceModesEnum.ONLY_COPY;
}
return traceModes;
}
@Override
public boolean hasSpreadsheetTraceModeTraceable() {
return getTraceModes() != TraceModesEnum.NOT_TRACEABLE;
}
@Override
public String getTraceDialogAsValues() {
StringBuilder sb = new StringBuilder();
if (getParentAlgorithm() != null
&& (getParentAlgorithm() instanceof AlgoDependentList)) {
// list = {A, B} : names for A, B
boolean notFirst = false;
for (GeoElement geo : geoList) {
if (notFirst) {
sb.append(", ");
}
sb.append(geo.getTraceDialogAsValues());
notFirst = true;
}
} else {
// e.g. Sequence[...] : name of the list
sb.append(super.getTraceDialogAsValues());
}
return sb.toString();
}
/*
* default for elements implementing NumberValue interface eg GeoSegment,
* GeoPolygon
*/
@Override
public void addToSpreadsheetTraceList(
ArrayList<GeoNumeric> spreadsheetTraceList) {
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (geo instanceof SpreadsheetTraceable) {
((SpreadsheetTraceable) geo)
.addToSpreadsheetTraceList(spreadsheetTraceList);
}
}
}
/**
* Performs all GeoScriptActions contained in this list
*
* @param info
* evaluation flags
* @return number of actions that were performed
*/
public int performScriptActions(EvalInfo info) {
int actions = 0;
for (int i = 0; i < size(); i++) {
if (get(i) instanceof GeoScriptAction) {
if (info.isScripting()) {
((GeoScriptAction) get(i)).perform();
}
actions++;
}
if (get(i) instanceof GeoList) {
actions += ((GeoList) get(i)).performScriptActions(info);
}
}
return actions;
}
/**
* Returns position of needle in this list or -1 when not found
*
* @param needle
* geo to be found
* @return position of needle in this list or -1 when not found
*/
public int find(GeoElement needle) {
return geoList.indexOf(needle);
}
/**
* @return whether this list should be drawn as combobox
*/
public boolean drawAsComboBox() {
return drawAsComboBox;
}
/**
* @param b
* whether this list should be drawn as combobox
*/
public void setDrawAsComboBox(boolean b) {
drawAsComboBox = b;
}
@Override
public boolean isAbsoluteScreenLocateable() {
return drawAsComboBox();
}
@Override
public boolean isMoveable() {
return drawAsComboBox();
}
@Override
public boolean isAbsoluteScreenLocActive() {
return true;
}
@Override
public void setAbsoluteScreenLoc(int x, int y) {
labelOffsetX = x;
labelOffsetY = y;
}
@Override
public int getAbsoluteScreenLocX() {
return labelOffsetX;
}
@Override
public int getAbsoluteScreenLocY() {
return labelOffsetY;
}
@Override
public void setAbsoluteScreenLocActive(boolean flag) {
// do nothing
}
@Override
public void setRealWorldLoc(double x, double y) {
// do nothing
}
@Override
public double getRealWorldLocX() {
return 0;
}
@Override
public double getRealWorldLocY() {
return 0;
}
@Override
public boolean isFurniture() {
return drawAsComboBox();
}
@Override
public ExpressionValue getListElement(int i) {
return get(i);
}
/**
* attempts to calculate mean of the list if any non-numeric elements are
* found, Double.NAN will be returned
*
* @return mean or Double.NAN
*/
public double mean() {
if (size() == 0) {
return Double.NaN;
}
double sum = 0;
for (int i = 0; i < size(); i++) {
GeoElement geo = get(i);
if (geo instanceof NumberValue) {
sum += ((NumberValue) geo).getDouble();
} else {
return Double.NaN;
}
}
return sum / size();
}
private boolean[] directionInfoArray = null; // true if minParameter is for
// start
private int[] directionInfoOrdering = null; // simple map to the ordered
// indexes
private boolean shouldUseAlgoLocusList = true;// whether AlgoLocus is not
// enough
private boolean locusCalledAlgoLocusList = false;// if a locus ever used
// this list as a path
/**
* Before creating a locus based on this GeoList as a path, this method
* decides whether the locus algo should be AlgoLocus or AlgoLocusList.
*
* @param locusCalling
* whether this method was called by locus
*
* @return boolean true if AlgoLocusList should be used.
*/
public boolean shouldUseAlgoLocusList(boolean locusCalling) {
GeoPoint[] minParArray = new GeoPoint[this.size()];
GeoPoint[] maxParArray = new GeoPoint[this.size()];
GeoPoint[] minParStatic = new GeoPoint[this.size()];
GeoPoint[] maxParStatic = new GeoPoint[this.size()];
// if there is no locus using this, the answer is not important
if (!locusCalledAlgoLocusList && !locusCalling) {
directionInfoArray = null;
directionInfoOrdering = null;
return true;
}
if (size() == 0) {
return false;
}
directionInfoArray = new boolean[this.size()];
directionInfoOrdering = new int[this.size()];
shouldUseAlgoLocusList = true;
locusCalledAlgoLocusList = true;
int i = 0;
for (; i < this.size(); i++) {
directionInfoArray[i] = true;// at first this is used as helper
// array
directionInfoOrdering[i] = i;
if ((Path) get(i) instanceof GeoSegment) {
minParArray[i] = ((GeoSegment) get(i)).getStartPoint();
maxParArray[i] = ((GeoSegment) get(i)).getEndPoint();
} else if ((Path) get(i) instanceof GeoLine) {
minParArray[i] = ((GeoLine) get(i)).getStartPoint();
maxParArray[i] = ((GeoLine) get(i)).getEndPoint();
} else if ((Path) get(i) instanceof GeoConicPart) {
if (((GeoConicPart) get(i))
.getParentAlgorithm() instanceof AlgoConicPartConicPoints) {
minParArray[i] = (GeoPoint) ((AlgoConicPartConicPoints) ((GeoConicPart) get(
i)).getParentAlgorithm()).getStartPoint();
maxParArray[i] = (GeoPoint) ((AlgoConicPartConicPoints) ((GeoConicPart) get(
i)).getParentAlgorithm()).getEndPoint();
} else if (((GeoConicPart) get(i))
.getParentAlgorithm() instanceof AlgoConicPartCircumcircle) {
minParArray[i] = (GeoPoint) ((AlgoConicPartCircumcircle) ((GeoConicPart) get(
i)).getParentAlgorithm()).getInput()[0];
maxParArray[i] = (GeoPoint) ((AlgoConicPartCircumcircle) ((GeoConicPart) get(
i)).getParentAlgorithm()).getInput()[2];
} else if (((GeoConicPart) get(i))
.getParentAlgorithm() instanceof AlgoSemicircle) {
// AlgoSemiCircle's endpoints counted in reverse order in
// GeoConicPart
minParArray[i] = (GeoPoint) ((AlgoSemicircle) ((GeoConicPart) get(
i)).getParentAlgorithm()).getInput()[1];
maxParArray[i] = (GeoPoint) ((AlgoSemicircle) ((GeoConicPart) get(
i)).getParentAlgorithm()).getInput()[0];
} else {
minParArray[i] = ((GeoConicPart) get(i)).getPointParam(0);
maxParArray[i] = ((GeoConicPart) get(i)).getPointParam(1);
}
} else {
minParArray[i] = null;
maxParArray[i] = null;
break;
}
minParStatic[i] = minParArray[i];
maxParStatic[i] = maxParArray[i];
}
if (i < this.size() || minParArray[this.size() - 1] == null) {
directionInfoArray = null;
directionInfoOrdering = null;
return true;
}
// this algorithm is just for deciding if this is a "directed graph
// circle"
for (int j = 0; j < this.size(); j++) {
// to get this data, at first the subpaths should be joined,
// to have compatible directions, and then the joined thing
// should be made compatible with the main path too
for (i = j + 1; i < this.size(); i++) {// search, join
if (GeoPoint.samePosition(minParArray[j], minParArray[i])) {
minParArray[i] = maxParArray[j];
i = 0;
break;
} else if (GeoPoint.samePosition(minParArray[j],
maxParArray[i])) {
maxParArray[i] = maxParArray[j];
i = 0;
break;
} else if (GeoPoint.samePosition(maxParArray[j],
minParArray[i])) {
minParArray[i] = minParArray[j];
i = 0;
break;
} else if (GeoPoint.samePosition(maxParArray[j],
maxParArray[i])) {
maxParArray[i] = minParArray[j];
i = 0;
break;
}
}
if (i != 0 && j < this.size() - 1) {
// there was no match, so this path is not a circle graph
directionInfoArray = null;
directionInfoOrdering = null;
return true;// AlgoLocusList
}
}
// otherwise everything has been reduced to one
if (!GeoPoint.samePosition(minParArray[this.size() - 1],
maxParArray[this.size() - 1])) {
// this path is not a circle graph, but a line graph
directionInfoArray = null;
directionInfoOrdering = null;
return true;// AlgoLocusList
}
// otherwise use AlgoLocus
// now it's time to get the final contents of directionInfoArray
// directionInfoOrdering: which index is the next
// at first search for a minimum index to start from
int ii = 0;
boolean direction = true;// min-max direction is true in theory... (why
// false in testing?)
// starting from ii, determine the ordering
// here we use the information that this is a "directed graph circle"
for (int j = 0; j < this.size(); j++) {
directionInfoOrdering[j] = ii;
directionInfoArray[ii] = direction;// direction of ii
// search for the thing after ii with the help of the ref tree
for (i = 0; i < this.size(); i++) {
if (i == ii)
{
continue;// it is not the same as itself
}
if (j > 0) {
// direction)
if (directionInfoOrdering[j - 1] == i) {
continue;
}
}
if (direction) {
// if direction of ii is true, then use its maxParStatic
// end to match with i
if (GeoPoint.samePosition(maxParStatic[ii],
minParStatic[i])) {
ii = i;
direction = true;
break;
} else if (GeoPoint.samePosition(maxParStatic[ii],
maxParStatic[i])) {
ii = i;
direction = false;
break;
}
} else {
if (GeoPoint.samePosition(minParStatic[ii],
minParStatic[i])) {
ii = i;
direction = true;
break;
} else if (GeoPoint.samePosition(minParStatic[ii],
maxParStatic[i])) {
ii = i;
direction = false;
break;
}
}
}
}
shouldUseAlgoLocusList = false;
return false;
}
@Override
public boolean showOnAxis() {
return showOnAxis;
}
/**
* For inequalities.
*
* @param showOnAxis
* true iff should be drawn on x-Axis only
*/
@Override
public void setShowOnAxis(boolean showOnAxis) {
this.showOnAxis = showOnAxis;
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (!geo.isLabelSet() && (geo instanceof InequalityProperties)) {
((InequalityProperties) geo).setShowOnAxis(showOnAxis);
}
}
}
/**
* @return true if this list contains a 3D geo
*/
public boolean containsGeoElement3D() {
for (GeoElement geo : geoList) {
boolean contains = false;
if (geo.isGeoList()) {
contains = ((GeoList) geo).containsGeoElement3D();
} else {
contains = geo.isGeoElement3D();
}
if (contains) {
return true;
}
}
return false;
}
@Override
final public Coords getMainDirection() {
if (geoList.size() <= closestPointIndex) {
return Coords.VX;
}
return geoList.get(closestPointIndex).getMainDirection();
}
@Override
public boolean isLaTeXTextCommand() {
return false;
}
private AngleStyle angleStyle = AngleStyle.ANTICLOCKWISE;
private boolean emphasizeRightAngle = true;
private int arcSize = EuclidianStyleConstants.DEFAULT_ANGLE_SIZE;
private int totalWidth = 0;
private int totalHeight = 0;
@Override
public void setAngleStyle(int style) {
setAngleStyle(AngleStyle.getStyle(style));
}
/**
* Changes angle style and recomputes the value from raw. See
* GeoAngle.ANGLE_*
*
* @param angleStyle
* clockwise, anticlockwise, (force) reflex or (force) not reflex
*/
@Override
public void setAngleStyle(AngleStyle angleStyle) {
AngleStyle newAngleStyle = angleStyle;
if (newAngleStyle == this.angleStyle) {
return;
}
this.angleStyle = newAngleStyle;
switch (newAngleStyle) {
// case GeoAngle.ANGLE_ISCLOCKWISE:
// newAngleStyle = GeoAngle.ANGLE_ISCLOCKWISE;
// break;
case NOTREFLEX:
newAngleStyle = AngleStyle.NOTREFLEX;
break;
case ISREFLEX:
newAngleStyle = AngleStyle.ISREFLEX;
break;
default:
newAngleStyle = AngleStyle.ANTICLOCKWISE;
}
for (GeoElement geo : geoList) {
if (!geo.isLabelSet() && (geo instanceof AngleProperties)) {
((AngleProperties) geo).setAngleStyle(angleStyle);
}
}
}
@Override
public AngleStyle getAngleStyle() {
return angleStyle;
}
@Override
public boolean hasOrientation() {
return true;
}
/**
* Depending upon angleStyle, some values > pi will be changed to (2pi -
* value). raw_value contains the original value.
*
* @param allowReflexAngle
* If true, angle is allowed to be> 180 degrees
*
*/
@Override
final public void setAllowReflexAngle(boolean allowReflexAngle) {
switch (angleStyle) {
case NOTREFLEX:
if (allowReflexAngle) {
setAngleStyle(AngleStyle.ANTICLOCKWISE);
}
break;
case ISREFLEX:
// do nothing
break;
default: // ANGLE_ISANTICLOCKWISE
if (!allowReflexAngle) {
setAngleStyle(AngleStyle.NOTREFLEX);
}
break;
}
if (allowReflexAngle) {
setAngleStyle(AngleStyle.ANTICLOCKWISE);
} else {
setAngleStyle(AngleStyle.NOTREFLEX);
}
for (GeoElement geo : geoList) {
if (!geo.isLabelSet() && (geo instanceof AngleProperties)) {
((AngleProperties) geo).setAllowReflexAngle(allowReflexAngle);
}
}
}
/**
* Sets this angle shuld be drawn differently when right
*
* @param emphasizeRightAngle
* true iff this angle shuld be drawn differently when right
*/
@Override
public void setEmphasizeRightAngle(boolean emphasizeRightAngle) {
this.emphasizeRightAngle = emphasizeRightAngle;
for (GeoElement geo : geoList) {
if (!geo.isLabelSet() && (geo instanceof AngleProperties)) {
((AngleProperties) geo)
.setEmphasizeRightAngle(emphasizeRightAngle);
}
}
}
/**
* Forces angle to be reflex or switches it to anticlockwise
*
* @param forceReflexAngle
* switch to reflex for true
*/
@Override
final public void setForceReflexAngle(boolean forceReflexAngle) {
if (forceReflexAngle) {
setAngleStyle(AngleStyle.ISREFLEX);
} else if (angleStyle == AngleStyle.ISREFLEX) {
setAngleStyle(AngleStyle.ANTICLOCKWISE);
}
for (GeoElement geo : geoList) {
if (!geo.isLabelSet() && (geo instanceof AngleProperties)) {
((AngleProperties) geo).setForceReflexAngle(forceReflexAngle);
}
}
}
@Override
public void setDecorationType(int type) {
setDecorationType(type, GeoAngle.getDecoTypes().length);
if (geoList != null) {
for (GeoElement geo : geoList) {
if (!geo.isLabelSet() && (geo instanceof AngleProperties)) {
((AngleProperties) geo).setDecorationType(type);
}
}
}
}
/**
* Change the size of the arc in pixels,
*
* @param i
* arc size, should be in <10,100>
*/
@Override
public void setArcSize(int i) {
arcSize = i;
for (GeoElement geo : geoList) {
if (!geo.isLabelSet() && (geo instanceof AngleProperties)) {
((AngleProperties) geo).setArcSize(i);
}
}
}
/**
* returns size of the arc in pixels
*
* @return arc size in pixels
*/
@Override
public int getArcSize() {
return arcSize;
}
/**
*
* @return true iff this angle should be drawn differently when 90 degrees
*/
@Override
public boolean isEmphasizeRightAngle() {
return emphasizeRightAngle;
}
/**
* @param vars
* sequence variable that should be replaced by its free copy
*/
public void replaceChildrenByValues(GeoElement vars) {
if (this.elementType != GeoClass.FUNCTION
&& this.elementType != GeoClass.CURVE_CARTESIAN
&& this.elementType != GeoClass.CURVE_CARTESIAN3D
&& this.elementType != GeoClass.FUNCTION_NVAR
&& this.elementType != GeoClass.SURFACECARTESIAN3D
&& this.elementType != GeoClass.LIST
&& this.elementType != ELEMENT_TYPE_MIXED) {
return;
}
for (GeoElement listElement : this.geoList) {
if (listElement instanceof CasEvaluableFunction) {
CasEvaluableFunction f = (CasEvaluableFunction) listElement;
f.replaceChildrenByValues(vars);
}
else if (listElement.isGeoList()) {
((GeoList) listElement).replaceChildrenByValues(vars);
}
}
}
@Override
public String getLabelDescription() {
if (labelMode == LABEL_CAPTION) {
return getCaption(StringTemplate.defaultTemplate);
}
// return label;
// Mathieu Blossier - 2009-06-30
return getLabel(StringTemplate.defaultTemplate);
}
@Override
final public HitType getLastHitType() {
// TODO check elements
return HitType.ON_FILLING;
}
/**
* Add number to the end, use cache if possible. Assume all cached elements
* are GeoNumerics.
*
* @param value
* value
* @param parent
* parent algo
*/
public void addNumber(double value, AlgoElement parent) {
GeoNumeric listElement;
if (size() < getCacheSize()) {
// use existing list element
listElement = (GeoNumeric) getCached(size());
} else {
// create a new list element
listElement = new GeoNumeric(cons);
listElement.setParentAlgorithm(parent);
listElement.setConstructionDefaults();
listElement.setUseVisualDefaults(false);
}
add(listElement);
listElement.setValue(value);
}
/**
* Add point to the end, use cache if possible. Assumes all cached elements
* are points.
*
* @param x
* x-coord
* @param y
* y-coord
* @param z
* z-coord
* @param parent
* parent algorithm of the new point
*/
public void addPoint(double x, double y, double z, AlgoElement parent) {
GeoPoint listElement;
if (size() < getCacheSize()) {
// use existing list element
listElement = (GeoPoint) getCached(size());
} else {
// create a new list element
listElement = new GeoPoint(cons);
listElement.setParentAlgorithm(parent);
listElement.setConstructionDefaults();
listElement.setUseVisualDefaults(false);
}
add(listElement);
listElement.setCoords(x, y, z);
}
@Override
public int getListDepth() {
return isMatrix() ? 2 : 1;
}
/**
* @return element of the same type as other elements in this list
*/
public GeoElement createTemplateElement() {
if (size() == 0) {
if (getTypeStringForXML() != null) {
return kernel.createGeoElement(cons, getTypeStringForXML());
}
// guess
return new GeoNumeric(cons);
}
// list not zero length
return get(0).copyInternal(cons);
}
@Override
public boolean hasLineOpacity() {
return true;
}
@Override
public int getLineOpacity() {
return lineOpacity;
}
@Override
public void setLineOpacity(int lineOpacity) {
this.lineOpacity = lineOpacity;
if ((geoList == null) || (geoList.size() == 0)) {
return;
}
for (int i = 0; i < geoList.size(); i++) {
final GeoElement geo = geoList.get(i);
if (!geo.isLabelSet()) {
geo.setLineOpacity(lineOpacity);
}
}
}
@Override
public ValueType getValueType() {
return ValueType.LIST;
}
@Override
public boolean hasBackgroundColor() {
if (drawAsComboBox
|| (this.size() > 0 && this.get(0).hasBackgroundColor())) {
return true;
}
if (this.size() > 0 && !this.get(0).hasBackgroundColor()) {
return false;
}
return createTemplateElement().hasBackgroundColor();
}
/**
* @param geo
* element to be added
*/
public void addCopy(GeoElement geo) {
if (!geo.isLabelSet()) {
add(geo.copyInternal(cons));
} else {
add(geo);
}
}
@Override
public ValidExpression toValidExpression() {
return getMyList();
}
@Override
public void setSymbolicMode(boolean mode, boolean updateParent) {
for (int i = 0; i < this.size(); i++) {
if (get(i) instanceof HasSymbolicMode) {
((HasSymbolicMode) get(i)).setSymbolicMode(mode, updateParent);
}
}
}
@Override
public boolean isSymbolicMode() {
return size() > 0 && get(0) instanceof HasSymbolicMode
&& ((HasSymbolicMode) get(0)).isSymbolicMode();
}
@Override
public boolean needToShowBothRowsInAV() {
if (isMatrix() && isIndependent()) {
return false;
}
if (!isIndependent()) {
return true;
}
for (GeoElement geo : geoList) {
if (geo.needToShowBothRowsInAV()) {
return true;
}
}
return false;
}
/**
* Check for matrices for matrices defined per element like {{1,0},{0,1}}
* (also dependent like {{a+1,1}})
*
* @return whether this can be eited as a matrix
*/
public boolean isEditableMatrix() {
if (!isMatrix()) {
return false;
}
if (isIndependent()) {
return true;
}
if (getParentAlgorithm() instanceof AlgoDependentList) {
AlgoElement algo = getParentAlgorithm();
for (int i = 0; i < algo.getInputLength(); i++) {
GeoElementND element = algo.getInput(i);
if (!element.isIndependent() && !(element
.getParentAlgorithm() instanceof AlgoDependentList)) {
return false;
}
}
return true;
}
return false;
}
/**
* Gets LaTeX string including the label for edit
*
* @param substituteNumbers
* whether value should be used
* @param tpl
* template
* @return string for AV editing
*/
@Override
public String getLaTeXAlgebraDescriptionWithFallback(
final boolean substituteNumbers, StringTemplate tpl,
boolean fallback) {
if (isEditableMatrix()) {
return getLabel(tpl) + " = "
+ toLaTeXString(!substituteNumbers, tpl);
}
return super.getLaTeXAlgebraDescriptionWithFallback(substituteNumbers,
tpl, fallback);
}
@Override
public void resetDefinition() {
super.resetDefinition();
for (int i = 0; i < size(); i++) {
this.geoList.get(i).resetDefinition();
}
}
@Override
public GeoElementND doAnimationStep(double frameRate, GeoList parent) {
if (size() > selectedIndex) {
if (get(selectedIndex).isAnimatable()) {
return ((Animatable) get(selectedIndex)).doAnimationStep(
frameRate, this);
}
}
return null;
}
@Override
public boolean isAnimatable() {
return false;
}
/**
* Select next or previous index based on animation props
*/
public void selectNext() {
if (selectedIndex >= size() - 1 && getAnimationDirection() > 0) {
this.changeAnimationDirection();
return;
}
if (selectedIndex == 0 && getAnimationDirection() < 0) {
this.changeAnimationDirection();
return;
}
selectedIndex += this.getAnimationDirection();
}
/**
* May break dependencies, use on free lists only
*
* @param i
* index
* @param element
* new element
*/
public void setListElement(int i, GeoElement element) {
this.geoList.set(i, element);
this.applyVisualStyle(element);
// this.elementType = element.getGeoClassType();
isDrawable = true;
for (int idx = 0; idx < size(); idx++) {
updateDrawableFlag(get(idx));
}
}
@Override
public GColor getBackgroundColor() {
if (drawAsComboBox && bgColor == null) {
return GColor.WHITE;
}
return bgColor;
}
/**
* Sets the total width of the geo.
*
* @param width
* to set.
*/
public void setTotalWidth(int width) {
totalWidth = width;
}
/**
* Sets the total height of the geo.
*
* @param height
* to set.
*/
public void setTotalHeight(int height) {
totalHeight = height;
}
public int getTotalWidth(EuclidianViewInterfaceCommon ev) {
return totalWidth;
}
public int getTotalHeight(EuclidianViewInterfaceCommon ev) {
return totalHeight;
}
}