/* 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.algos; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.arithmetic.Inspecting; import org.geogebra.common.kernel.commands.Commands; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoList; import org.geogebra.common.kernel.geos.GeoNumberValue; import org.geogebra.common.kernel.geos.GeoPolygon; import org.geogebra.common.kernel.geos.Test; import org.geogebra.common.plugin.GeoClass; import org.geogebra.common.util.debug.Log; /** * n-th element of a GeoList object. * * Note: the type of the returned GeoElement object is determined by the type of * the first list element. If the list is initially empty, a GeoNumeric object * is created for element. * * @author Markus Hohenwarter * @version 15-07-2007 */ public class AlgoListElement extends AlgoElement { private GeoList geoList; // input private GeoNumberValue num; private GeoNumberValue[] num2 = null; // input private GeoElement numGeo; private GeoElement element; // output /** * Creates new labeled element algo * * @param cons * @param label * @param geoList * @param num */ public AlgoListElement(Construction cons, String label, GeoList geoList, GeoNumberValue num) { this(cons, geoList, num); element.setLabel(label); } public AlgoListElement(Construction cons, GeoList geoList, GeoNumberValue num) { super(cons); this.geoList = geoList; this.num = num; numGeo = num.toGeoElement(); int initIndex = Math.max(0, (int) Math.round(num.getDouble()) - 1); // init return element as copy of initIndex list element if (geoList.size() > initIndex) { // create copy of initIndex GeoElement in list element = getGenericElement(geoList, initIndex).copyInternal(cons); } // if not enough elements in list: // init return element as copy of first list element else if (geoList.size() > 0) { // create copy of first GeoElement in list element = getGenericElement(geoList, 0).copyInternal(cons); } // desperate case: empty list else if (geoList.getTypeStringForXML() != null) { // if the list was non-empty at some point before saving, get the // same type of geo // saved in XML from 4.1.131.0 element = kernel.createGeoElement(cons, geoList.getTypeStringForXML()); } // desperate case: empty list else { // saved in XML from 4.0.18.0 element = cons.getOutputGeo(); } if (element.isGeoPolygon()) { // ensure type will not be sticked to e.g. // "triangle" ((GeoPolygon) element).setNotFixedPointsLength(true); } setInputOutput(); compute(); } private static GeoElement getGenericElement(GeoList geoList, int index) { GeoElement toCopy = geoList.get(index); if (geoList.getElementType() == GeoClass.DEFAULT && // we have list {2,x}, not eg Factors[2x] (geoList.getParentAlgorithm() == null || geoList .getParentAlgorithm() instanceof AlgoDependentList) // for {a,x} also return number a, not function && !Inspecting.dynamicGeosFinder.check(toCopy)) { for (int i = 0; i < geoList.size(); i++) { if (Test.canSet(geoList.get(i), toCopy)) { toCopy = geoList.get(i); } } } return toCopy; } /** * Creates new unlabeled element algo */ public AlgoListElement(Construction cons, String label, GeoList geoList, GeoNumberValue[] num2) { this(cons, geoList, num2); element.setLabel(label); } public AlgoListElement(Construction cons, GeoList geoList, GeoNumberValue[] num2) { super(cons); this.geoList = geoList; this.num2 = num2; element = null; GeoElement current = geoList; int k = 0; try { do { int initIndex = Math.max(0, (int) Math.round(num2[k].getDouble()) - 1); // init return element as copy of initIndex list element if (((GeoList) current).size() > initIndex) { // create copy of initIndex GeoElement in list current = k == num2.length - 1 ? getGenericElement((GeoList) current, initIndex) : ((GeoList) current).get(initIndex); } // if not enough elements in list: // init return element as copy of first list element else if (geoList.size() > 0) { // create copy of first GeoElement in list current = k == num2.length - 1 ? getGenericElement((GeoList) current, 0) : ((GeoList) current).get(0); } k++; } while (current.isGeoList() && k < num2.length); element = current.copyInternal(cons); } catch (Exception e) { Log.debug("error initialising list"); } // desperate case: empty list, or malformed 2D array if (element == null) { // saved in XML from 4.0.18.0 element = cons.getOutputGeo(); } setInputOutput(); compute(); } @Override public Commands getClassName() { return Commands.Element; } @Override protected void setInputOutput() { if (num2 == null) { input = new GeoElement[2]; input[0] = geoList; input[1] = numGeo; } else { input = new GeoElement[num2.length + 1]; input[0] = geoList; for (int i = 0; i < num2.length; i++) { input[i + 1] = num2[i].toGeoElement(); } } setOutputLength(1); setOutput(0, element); setDependencies(); // done by AlgoElement } /** * Returns chosen element * * @return chosen element */ public GeoElement getElement() { return element; } @Override public final void compute() { if ((numGeo != null && !numGeo.isDefined()) || !geoList.isDefined()) { element.setUndefined(); return; } if (num2 == null) { // index of wanted element int n = (int) Math.round(num.getDouble()) - 1; if (n >= 0 && n < geoList.size()) { GeoElement nth = geoList.get(n); setElement(nth); } else { element.setUndefined(); } } else { for (int k = 0; k < num2.length; k++) { if (!num2[k].toGeoElement().isDefined()) { element.setUndefined(); return; } } int m = (int) Math.round(num2[num2.length - 1].getDouble()) - 1; GeoElement current = geoList; for (int k = 0; k < num2.length - 1; k++) { int index = (int) Math.round(num2[k].getDouble() - 1); if (index >= 0 && current.isGeoList() && index < ((GeoList) current).size()) { current = ((GeoList) current).get(index); } else { element.setUndefined(); return; } } GeoList list = ((GeoList) current); if (m >= 0 && m < list.size()) { current = list.get(m); } else { element.setUndefined(); return; } setElement(current); } } private void setElement(GeoElement nth) { // check type: if (nth.getGeoClassType() == element.getGeoClassType() || Test.canSet(element, nth)) { element.set(nth); if (nth.getDrawAlgorithm() instanceof DrawInformationAlgo) { element.setDrawAlgorithm( ((DrawInformationAlgo) nth.getDrawAlgorithm()).copy()); } } else { element.setUndefined(); } } /* * @Override public String getCommandDescription(StringTemplate tpl,boolean * real) { * * return super.getCommandDescription(tpl,real); * * TODO re enable this for shortSyntax flag true for 5.0 sb.setLength(0); * * * int length = input.length; * * sb.append(geoList.getLabel()+"("); // input * sb.append(real?input[1].getRealLabel():input[1].getLabel()); // Michael * Borcherds 2008-05-15 added input.length>0 for Step[] for (int i = 2; i < * length; ++i) { sb.append(", "); sb.append(real? * input[i].getRealLabel():input[i].getLabel()); } sb.append(")"); return * sb.toString(); * * } */ }