/* 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.statistics; import java.util.ArrayList; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.algos.AlgoElement; import org.geogebra.common.kernel.commands.Commands; import org.geogebra.common.kernel.geos.GeoBoolean; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoList; import org.geogebra.common.kernel.geos.GeoNumeric; import org.geogebra.common.kernel.geos.GeoPoint; import org.geogebra.common.kernel.geos.GeoPolyLine; import org.geogebra.common.kernel.kernelND.GeoPointND; /** * Creates a frequency polygon. * * Input: inputs are identical to AlgoHistogram * * Output: PolyLine created from class borders and frequency heights of the * histogram generated by the inputs * * @author G.Sturr */ public class AlgoFrequencyPolygon extends AlgoElement { private GeoList list1, list2, list3; // input private GeoBoolean isCumulative, useDensity; // input private GeoNumeric density; private GeoPolyLine outputPolyLine; // output private GeoPointND[] points = null; private AlgoHistogram algoHistogram; private boolean right = false; /** * Creates a frequency polygon from two data lists. * * @param cons * construction * @param label * label for the histogram * @param list1 * list of boundaries * @param list2 * list of heights or raw data */ public AlgoFrequencyPolygon(Construction cons, String label, GeoList list1, GeoList list2) { this(cons, label, null, list1, list2, null, null, null); } /** * /** Creates frequency polygon from two data lists with parameter * specifications. * * @param cons * @param label * @param isCumulative * @param list1 * @param list2 * @param useDensity * @param density */ public AlgoFrequencyPolygon(Construction cons, String label, GeoBoolean isCumulative, GeoList list1, GeoList list2, GeoList list3, GeoBoolean useDensity, GeoNumeric density) { this(cons, isCumulative, list1, list2, list3, useDensity, density); outputPolyLine.setLabel(label); } public AlgoFrequencyPolygon(Construction cons, GeoBoolean isCumulative, GeoList list1, GeoList list2, GeoList list3, GeoBoolean useDensity, GeoNumeric density) { super(cons); this.list1 = list1; this.list2 = list2; this.list3 = list3; // optional frequencies this.isCumulative = isCumulative; this.useDensity = useDensity; this.density = density; outputPolyLine = new GeoPolyLine(cons, points); setInputOutput(); compute(); } @Override public Commands getClassName() { return Commands.FrequencyPolygon; } @Override protected void setInputOutput() { ArrayList<GeoElement> tempList = new ArrayList<GeoElement>(); if (isCumulative != null) { tempList.add(isCumulative); } tempList.add(list1); tempList.add(list2); if (list3 != null) { tempList.add(list3); } if (useDensity != null) { tempList.add(useDensity); } if (density != null) { tempList.add(density); } input = new GeoElement[tempList.size()]; input = tempList.toArray(input); boolean suppressLabelCreation = cons.isSuppressLabelsActive(); cons.setSuppressLabelCreation(true); algoHistogram = new AlgoHistogram(cons, isCumulative, list1, list2, list3, useDensity, density, right); cons.setSuppressLabelCreation(suppressLabelCreation); setOutput(); // parent of output outputPolyLine.setParentAlgorithm(this); cons.addToAlgorithmList(this); setDependencies(); // done by AlgoElement } private void setOutput() { super.setOutputLength(1); super.setOutput(0, outputPolyLine); } public GeoPolyLine getResult() { return outputPolyLine; } @Override public final void compute() { // update our histogram to get class borders and y values algoHistogram.update(); if (!algoHistogram.getOutput()[0].isDefined()) { outputPolyLine.setUndefined(); return; } double[] leftBorder = algoHistogram.getLeftBorder(); if (leftBorder == null || leftBorder.length < 2) { outputPolyLine.setUndefined(); return; } double[] yValue = algoHistogram.getValues(); if (yValue == null || yValue.length < 2) { outputPolyLine.setUndefined(); return; } // if we got this far everything is ok; now define the polyLine outputPolyLine.setDefined(); // remember old number of points int oldPointsLength = points == null ? 0 : points.length; // create a new point array boolean doCumulative = (isCumulative != null && isCumulative.getBoolean()); int size = doCumulative ? yValue.length : yValue.length + 1; points = new GeoPoint[size]; // create points and load the point array boolean suppressLabelCreation = cons.isSuppressLabelsActive(); cons.setSuppressLabelCreation(true); if (doCumulative) { points[0] = new GeoPoint(cons, null, leftBorder[0], 0.0, 1.0); for (int i = 0; i < yValue.length - 1; i++) { points[i + 1] = new GeoPoint(cons, null, leftBorder[i + 1], yValue[i], 1.0); } } else { double midpoint = leftBorder[0] - 0.5 * (leftBorder[1] - leftBorder[0]); points[0] = new GeoPoint(cons, null, midpoint, 0.0, 1.0); for (int i = 0; i < yValue.length - 1; i++) { midpoint = 0.5 * (leftBorder[i + 1] + leftBorder[i]); points[i + 1] = new GeoPoint(cons, null, midpoint, yValue[i], 1.0); } midpoint = 1.5 * leftBorder[yValue.length - 1] - .5 * (leftBorder[yValue.length - 2]); points[yValue.length] = new GeoPoint(cons, null, midpoint, 0.0, 1.0); } cons.setSuppressLabelCreation(suppressLabelCreation); // update the polyLine outputPolyLine.setPoints(points); if (oldPointsLength != points.length) { setOutput(); } } }