25package umontreal.ssj.charts;
27import umontreal.ssj.functions.MathFunction;
28import umontreal.ssj.functionfit.SmoothingCubicSpline;
29import umontreal.ssj.util.RootFinder;
31import org.jfree.data.xy.*;
32import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
34import cern.colt.list.DoubleArrayList;
36import java.util.Locale;
37import java.util.Formatter;
53 protected String[] marksType;
54 protected String[] dashPattern;
56 protected String[] plotStyle;
57 private boolean autoCompletion =
false;
63 renderer =
new XYLineAndShapeRenderer(
true,
false);
65 seriesCollection =
new XYSeriesCollection();
99 renderer =
new XYLineAndShapeRenderer(
true,
false);
101 seriesCollection =
new XYSeriesCollection();
103 XYSeriesCollection tempSeriesCollection = (XYSeriesCollection) seriesCollection;
104 for (
int i = 0; i < data.length; i++) {
106 if (data[i].length < 2)
107 throw new IllegalArgumentException(
108 "Unable to render the plot. data[" + i +
"] contains less than two rows");
110 for (
int j = 0; j < data[i].length - 1; j++)
111 if (data[i][j].length != data[i][j + 1].length)
112 throw new IllegalArgumentException(
113 "data[" + i +
"][" + j +
"] and data[" + i +
"][" + (j + 1) +
"] must share the same length");
115 for (
int j = 1; j < data[i].length; j++) {
116 XYSeries serie =
new XYSeries(
" ");
117 for (
int k = 0; k < data[i][0].length; k++)
118 serie.add(data[i][0][k], data[i][j][k]);
119 tempSeriesCollection.addSeries(serie);
124 for (
int i = 0; i < tempSeriesCollection.getSeriesCount(); i++)
125 renderer.setSeriesPaint(i, getDefaultColor(i));
128 plotStyle =
new String[tempSeriesCollection.getSeriesCount()];
129 marksType =
new String[tempSeriesCollection.getSeriesCount()];
130 dashPattern =
new String[tempSeriesCollection.getSeriesCount()];
131 for (
int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
133 plotStyle[i] =
"smooth";
134 dashPattern[i] =
"solid";
155 renderer =
new XYLineAndShapeRenderer(
true,
false);
157 seriesCollection =
new XYSeriesCollection();
159 XYSeriesCollection tempSeriesCollection = (XYSeriesCollection) seriesCollection;
161 throw new IllegalArgumentException(
"Unable to render the plot. data contains less than two rows");
164 for (
int j = 1; j < data.length; j++) {
165 XYSeries serie =
new XYSeries(
" ");
166 for (
int k = 0; k < numPoints; k++)
167 serie.add(data[0][k], data[j][k]);
168 tempSeriesCollection.addSeries(serie);
172 for (
int i = 0; i < tempSeriesCollection.getSeriesCount(); i++)
173 renderer.setSeriesPaint(i, getDefaultColor(i));
176 plotStyle =
new String[tempSeriesCollection.getSeriesCount()];
177 marksType =
new String[tempSeriesCollection.getSeriesCount()];
178 dashPattern =
new String[tempSeriesCollection.getSeriesCount()];
179 for (
int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
181 plotStyle[i] =
"smooth";
182 dashPattern[i] =
"solid";
196 renderer =
new XYLineAndShapeRenderer(
true,
false);
198 seriesCollection =
new XYSeriesCollection();
199 XYSeriesCollection tempSeriesCollection = (XYSeriesCollection) seriesCollection;
204 DoubleArrayList temp;
205 for (
int i = 0; i < data.length; i++) {
206 serie =
new XYSeries(
" ");
208 temp = data[i].copy();
210 temp.quickSortFromTo(0, temp.size() - 1);
211 elements = temp.elements();
215 while (j < elements.length) {
216 while (j < elements.length && elements[j] == elements[l]) {
220 serie.add(elements[l], count);
224 tempSeriesCollection.addSeries(serie);
228 for (
int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
229 renderer.setSeriesPaint(i, getDefaultColor(i));
233 plotStyle =
new String[tempSeriesCollection.getSeriesCount()];
234 marksType =
new String[tempSeriesCollection.getSeriesCount()];
235 dashPattern =
new String[tempSeriesCollection.getSeriesCount()];
236 for (
int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
238 plotStyle[i] =
"smooth";
239 dashPattern[i] =
"solid";
251 renderer =
new XYLineAndShapeRenderer(
true,
false);
253 seriesCollection = data;
254 for (
int i = 0; i < data.getSeriesCount(); i++) {
255 XYSeries serie = data.getSeries(i);
259 for (
int i = 0; i < data.getSeriesCount(); i++) {
260 renderer.setSeriesPaint(i, getDefaultColor(i));
264 plotStyle =
new String[data.getSeriesCount()];
265 marksType =
new String[data.getSeriesCount()];
266 dashPattern =
new String[data.getSeriesCount()];
267 for (
int i = 0; i < data.getSeriesCount(); i++) {
269 plotStyle[i] =
"smooth";
270 dashPattern[i] =
"solid";
288 public int add(
double[] x,
double[] y) {
289 if (x.length != y.length)
290 throw new IllegalArgumentException(
"x and y must have the same length");
291 return add(x, y, x.length);
306 public int add(
double[] x,
double[] y,
int numPoints) {
307 XYSeries serie =
new XYSeries(
" ");
308 XYSeriesCollection tempSeriesCollection = (XYSeriesCollection) seriesCollection;
309 serie.setNotify(
true);
310 if ((x.length < numPoints) || (y.length < numPoints))
311 throw new IllegalArgumentException(
"numPoints > length of x or y");
312 for (
int i = 0; i < numPoints; i++)
313 serie.add(x[i], y[i]);
314 tempSeriesCollection.addSeries(serie);
317 int j = tempSeriesCollection.getSeriesCount() - 1;
318 renderer.setSeriesPaint(j, getDefaultColor(j));
320 int co = tempSeriesCollection.getSeriesCount();
321 String[] newPlotStyle =
new String[co];
322 String[] newMarksType =
new String[co];
323 String[] newDashPattern =
new String[co];
324 for (j = 0; j < co - 1; j++) {
325 newPlotStyle[j] = plotStyle[j];
326 newMarksType[j] = marksType[j];
327 newDashPattern[j] = dashPattern[j];
330 newPlotStyle[j] =
"smooth";
331 newMarksType[j] =
" ";
332 newDashPattern[j] =
"solid";
333 plotStyle = newPlotStyle;
334 marksType = newMarksType;
335 dashPattern = newDashPattern;
337 return tempSeriesCollection.getSeriesCount() - 1;
348 public int add(
double[][] data) {
349 return add(data, data[0].length);
363 public int add(
double[][] data,
int numPoints) {
364 XYSeriesCollection tempSeriesCollection = (XYSeriesCollection) seriesCollection;
365 int n = tempSeriesCollection.getSeriesCount();
368 throw new IllegalArgumentException(
"Unable to render the plot. data contains less than two rows");
370 for (
int j = 0; j < data.length; j++)
371 if (data[j].length < numPoints)
372 throw new IllegalArgumentException(
"data[" + j +
"] has not enough points");
374 for (
int j = 1; j < data.length; j++) {
375 XYSeries serie =
new XYSeries(
" ");
376 serie.setNotify(
true);
377 for (
int k = 0; k < numPoints; k++)
378 serie.add(data[0][k], data[j][k]);
379 tempSeriesCollection.addSeries(serie);
383 for (
int j = n; j < tempSeriesCollection.getSeriesCount(); j++)
384 renderer.setSeriesPaint(j, getDefaultColor(j));
386 String[] newPlotStyle =
new String[tempSeriesCollection.getSeriesCount()];
387 String[] newMarksType =
new String[tempSeriesCollection.getSeriesCount()];
388 String[] newDashPattern =
new String[tempSeriesCollection.getSeriesCount()];
389 for (
int j = 0; j < n; j++) {
390 newPlotStyle[j] = plotStyle[j];
391 newMarksType[j] = marksType[j];
392 newDashPattern[j] = dashPattern[j];
395 for (
int j = n; j < tempSeriesCollection.getSeriesCount(); j++) {
396 newPlotStyle[j] =
"smooth";
397 newMarksType[j] =
" ";
398 newDashPattern[j] =
"solid";
400 plotStyle = newPlotStyle;
401 marksType = newMarksType;
402 dashPattern = newDashPattern;
404 return (tempSeriesCollection.getSeriesCount() - n);
415 public int add(DoubleArrayList data) {
416 XYSeries serie =
new XYSeries(
" ");
417 DoubleArrayList temp = data.copy();
418 XYSeriesCollection tempSeriesCollection = (XYSeriesCollection) seriesCollection;
421 temp.quickSortFromTo(0, temp.size() - 1);
422 double[] elements = temp.elements();
427 while (j < elements.length) {
428 while (j < elements.length && elements[j] == elements[l]) {
432 serie.add(elements[l], count);
436 tempSeriesCollection.addSeries(serie);
439 j = tempSeriesCollection.getSeriesCount() - 1;
440 renderer.setSeriesPaint(j, getDefaultColor(j));
442 String[] newPlotStyle =
new String[tempSeriesCollection.getSeriesCount()];
443 String[] newMarksType =
new String[tempSeriesCollection.getSeriesCount()];
444 String[] newDashPattern =
new String[tempSeriesCollection.getSeriesCount()];
445 for (j = 0; j < tempSeriesCollection.getSeriesCount() - 1; j++) {
446 newPlotStyle[j] = plotStyle[j];
447 newMarksType[j] = marksType[j];
448 newDashPattern[j] = dashPattern[j];
451 newPlotStyle[j] =
"smooth";
452 newMarksType[j] =
" ";
453 newDashPattern[j] =
"solid";
454 plotStyle = newPlotStyle;
455 marksType = newMarksType;
456 dashPattern = newDashPattern;
458 return tempSeriesCollection.getSeriesCount() - 1;
468 return (String) ((XYSeriesCollection) seriesCollection).getSeries(series).getKey();
477 public void setName(
int series, String name) {
480 ((XYSeriesCollection) seriesCollection).getSeries(series).setKey(name);
500 this.autoCompletion =
true;
507 this.autoCompletion =
false;
517 return marksType[series];
531 this.marksType[series] = marksType;
541 return dashPattern[series];
557 this.dashPattern[series] = dashPattern;
558 if (dashPattern.equals(
"only marks")) {
559 ((XYLineAndShapeRenderer) renderer).setSeriesLinesVisible(series,
false);
560 ((XYLineAndShapeRenderer) renderer).setSeriesShapesVisible(series,
true);
562 ((XYLineAndShapeRenderer) renderer).setSeriesLinesVisible(series,
true);
563 ((XYLineAndShapeRenderer) renderer).setSeriesShapesVisible(series,
false);
574 return plotStyle[series];
590 this.plotStyle[series] = plotStyle;
593 public String toLatex(
double XScale,
double YScale,
double XShift,
double YShift,
double xmin,
double xmax,
594 double ymin,
double ymax) {
598 xmin = Math.min(XShift, xmin);
599 xmax = Math.max(XShift, xmax);
600 ymin = Math.min(YShift, ymin);
601 ymax = Math.max(YShift, ymax);
603 Formatter formatter =
new Formatter(Locale.US);
604 XYSeriesCollection tempSeriesCollection = (XYSeriesCollection) seriesCollection;
605 double XEPSILON = (1.0E-4 / XScale) + XShift;
606 double YEPSILON = (1.0E-4 / YScale) + YShift;
607 boolean outOfBounds =
false;
608 MathFunction[] spline =
null;
609 double[] xBounds = getRangeBounds();
610 double[] yBounds = getDomainBounds();
626 spline =
new SmoothingCubicSpline[tempSeriesCollection.getSeriesCount()];
627 for (
int i = 0; i < tempSeriesCollection.getSeriesCount(); i++)
628 spline[i] =
new SmoothingCubicSpline((tempSeriesCollection.getSeries(i).toArray())[0],
629 (tempSeriesCollection.getSeries(i).toArray())[1], 1);
631 spline =
new AffineFit[tempSeriesCollection.getSeriesCount()];
632 for (
int i = 0; i < tempSeriesCollection.getSeriesCount(); i++)
633 spline[i] =
new AffineFit((tempSeriesCollection.getSeries(i).toArray())[0],
634 (tempSeriesCollection.getSeries(i).toArray())[1]);
637 for (
int i = tempSeriesCollection.getSeriesCount() - 1; i >= 0; i--) {
638 XYSeries temp = tempSeriesCollection.getSeries(i);
640 if (temp.getItemCount() < 2)
641 throw new IllegalArgumentException(
642 "Unable to plot series " + i +
": this series must have two points at least");
644 Color color = (Color) renderer.getSeriesPaint(i);
645 String colorString = detectXColorClassic(color);
646 if (colorString ==
null) {
647 colorString =
"color" + i;
648 formatter.format(
"\\definecolor{%s}{rgb}{%.2f, %.2f, %.2f}%n", colorString, color.getRed() / 255.0,
649 color.getGreen() / 255.0, color.getBlue() / 255.0);
654 if (temp.getX(0).doubleValue() >= xmin && temp.getX(0).doubleValue() <= xmax
655 && temp.getY(0).doubleValue() >= ymin && temp.getY(0).doubleValue() <= ymax) {
657 formatter.format(
"\\draw [%s, color=%s, mark=%s, style=%s] plot coordinates {%%%n", plotStyle[i],
658 colorString, marksType[i], dashPattern[i]);
661 formatter.format(
"%% ");
663 formatter.format(
"(%.2f,%.4f)", (temp.getX(0).doubleValue() - XShift) * XScale,
664 (temp.getY(0).doubleValue() - YShift) * YScale);
665 formatter.format(
" %% (%f, %f)%n", temp.getX(0).doubleValue(), temp.getY(0).doubleValue());
668 for (
int j = 1; j < temp.getItemCount(); j++) {
671 result = evalLimitValues(xmin, xmax, ymin, ymax, XEPSILON, YEPSILON, spline[i], temp, j,
false);
674 if (result !=
null) {
677 formatter.format(
"(%.2f,%.4f) %%%n", (result[0] - XShift) * XScale, (result[1] - YShift) * YScale);
678 formatter.format(
"}%%%n%% ");
681 if (temp.getX(j).doubleValue() >= xmin && temp.getX(j).doubleValue() <= xmax
682 && temp.getY(j).doubleValue() >= ymin && temp.getY(j).doubleValue() <= ymax) {
685 result = evalLimitValues(xmin, xmax, ymin, ymax, XEPSILON, YEPSILON, spline[i], temp, j,
true);
687 formatter.format(
";%%%n\\draw [%s, color=%s, mark=%s, style=%s] plot coordinates {%%%n", plotStyle[i],
688 colorString, marksType[i], dashPattern[i]);
690 formatter.format(
"(%.2f,%.4f) %%%n ", (result[0] - XShift) * XScale,
691 (result[1] - YShift) * YScale);
692 formatter.format(
"%% ");
695 formatter.format(
"%% ");
703 formatter.format(
"(%.2f,%.4f)", (temp.getX(j).doubleValue() - XShift) * XScale,
704 (temp.getY(j).doubleValue() - YShift) * YScale);
705 if (j == temp.getItemCount() - 1)
706 formatter.format(
"}");
707 formatter.format(
" %% (%f, %f)%n", temp.getX(j).doubleValue(), temp.getY(j).doubleValue());
710 formatter.format(
" node[right] {%s};%n", (String) temp.getKey());
712 return formatter.toString();
732 private static double[] evalLimitValues(
double xmin,
double xmax,
double ymin,
double ymax,
double XEPSILON,
733 double YEPSILON, MathFunction spline, XYSeries temp,
int numPoint,
boolean sens) {
741 if (temp.getX(j).doubleValue() < xmin) {
743 y = spline.evaluate(xmin);
746 y = spline.evaluate(x);
751 y = spline.evaluate(x);
753 }
else if (temp.getX(j).doubleValue() > xmax) {
755 y = spline.evaluate(xmax);
758 y = spline.evaluate(x);
762 y = spline.evaluate(x);
764 }
else if (temp.getY(j).doubleValue() < ymin) {
766 x = evaluateX(spline, y, temp.getX(j).doubleValue(), temp.getX(k).doubleValue());
772 x = evaluateX(spline, y, x, temp.getX(k).doubleValue());
776 x = evaluateX(spline, y, x, temp.getX(k).doubleValue());
778 }
else if (temp.getY(j).doubleValue() > ymax) {
780 x = evaluateX(spline, y, temp.getX(j).doubleValue(), temp.getX(k).doubleValue());
786 x = evaluateX(spline, y, x, temp.getX(k).doubleValue());
790 x = evaluateX(spline, y, x, temp.getX(k).doubleValue());
794 double[] retour =
new double[2];
800 private static double evaluateX(
final MathFunction spline,
final double y,
double xPrincipal,
double xAnnexe) {
801 final MathFunction xFunction =
new MathFunction() {
802 public double evaluate(
double t) {
803 return spline.evaluate(t) - y;
806 return RootFinder.brentDekker(xPrincipal, xAnnexe - 1.0E-6, xFunction, 1e-6);
809 private class AffineFit
implements MathFunction {
814 public AffineFit(
double[] x,
double[] y) {
819 public double evaluate(
double t) {
823 while (i < x.length && t > x[i])
827 return x[x.length - 1];
829 return y[i] + ((t - x[i]) / (x[i + 1] - x[i])) * (y[i + 1] - y[i]);
Stores data used in a XYChart.
void setPlotStyle(int series, String plotStyle)
Selects the plot style for a given series.
int add(double[] x, double[] y)
Adds a data series into the series collection.
void setMarksType(int series, String marksType)
Adds marks on the points of a data series.
String getName(int series)
Gets the current name of the selected series.
void setName(int series, String name)
Sets the name of the selected series.
String getMarksType(int series)
Returns the mark type associated with the seriesth data series.
String getDashPattern(int series)
Returns the dash pattern associated with the seriesth data series.
XYListSeriesCollection()
Stores data used in a XYLineChart or in other related charts, and provides complementary tools to dra...
void setDashPattern(int series, String dashPattern)
Selects dash pattern for a data series.
void enableAutoCompletion()
Enables the auto completion option.
String getPlotStyle(int series)
Gets the current plot style for the selected series.
void disableAutoCompletion()
Disables auto completion option.