1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.scenegraph.g2d.nodes;
14 import java.awt.BasicStroke;
15 import java.awt.Color;
17 import java.awt.FontMetrics;
18 import java.awt.Graphics2D;
19 import java.awt.RenderingHints;
20 import java.awt.geom.AffineTransform;
\r
21 import java.awt.geom.GeneralPath;
22 import java.awt.geom.Rectangle2D;
23 import java.io.Serializable;
24 import java.text.DecimalFormat;
25 import java.text.NumberFormat;
26 import java.util.ArrayList;
27 import java.util.List;
29 import org.simantics.scenegraph.g2d.G2DNode;
31 public class Trend2DNode extends G2DNode {
36 private static final long serialVersionUID = 8508750881358776559L;
38 protected Rectangle2D bounds = null;
39 protected transient List<TrendPoint> points = null;
40 protected Integer x = 0;
41 protected Integer y = 0;
43 @SyncField({"x", "y"})
44 public void setPosition(int x, int y) {
50 public void setBounds(Rectangle2D bounds) {
55 protected void setPoints(List<TrendPoint> points) {
60 protected void appendPoints(List<TrendPoint> points) {
61 if(this.points == null) this.points = new ArrayList<TrendPoint>();
63 * We need to have the same set of points on the both sides, so locally the points are just updated,
64 * but on remote side we send only the new points.
65 * In case we are running on local workbench, the point list is updated and in addition these points
66 * would be added to the list without this check.
67 * FIXME: find out some way to implement this without this check
69 if(location.equals(Location.REMOTE)) {
70 this.points.addAll(points);
76 * Update trend points. If
80 public void updatePoints(List<TrendPoint> newpoints) {
81 if(points == null) points = new ArrayList<TrendPoint>();
82 for(int n = newpoints.size()-1; n >= 0; n--) {
84 for(int o = points.size()-1; o >= 0; o--) {
85 if(!newpoints.get(n-t).equals(points.get(o))) {
88 if(o == 0 || o < points.size()-10) {
89 // Now we have 10 matching values, so n tells where the old points ends
90 ArrayList<TrendPoint> appendedPoints = new ArrayList<TrendPoint>();
91 for(int i = n; i < newpoints.size()-1; i++) {
92 appendedPoints.add(newpoints.get(i));
94 points = new ArrayList<TrendPoint>(newpoints);
95 if(appendedPoints != null && appendedPoints.size() > 0) {
96 appendPoints(appendedPoints);
101 setPoints(newpoints);
107 setPoints(newpoints);
111 public void render(Graphics2D g2d) {
112 if(points == null || bounds == null) {
115 AffineTransform ot = g2d.getTransform();
\r
118 @SuppressWarnings("unused")
\r
121 double max_y = Double.MIN_VALUE; // for scaling
122 double min_y = Double.MAX_VALUE;
123 double max_x = Double.MIN_VALUE; // for scaling
124 double min_x = Double.MAX_VALUE;
125 for(int index = points.size()-1; index >= 0; index--) {
127 double y = points.get(index).getY();
128 double x = points.get(index).getX();
129 if(y > max_y) max_y = y;
130 if(y < min_y) min_y = y;
131 if(x > max_x) max_x = x;
132 if(x < min_x) min_x = x;
135 if(min_y > 0) min_y = 0;
136 if(max_y < 0) max_y = 0;
138 double mid_y = 0.5 * (max_y + min_y);
140 GeneralPath path = new GeneralPath();
142 double scalex = bounds.getWidth() / (max_x - min_x); // Scale to fit
144 double scalefactor = 0;
145 double height = bounds.getHeight()-2;
146 if(max_y - min_y > 0) scalefactor = height / (max_y - min_y);
147 for(int index = points.size()-1; index >= 0; index--) {
148 double t = bounds.getMinX();
150 float px = (float)((points.get(index).getX()-min_x) * scalex + t);
151 float py = (float)((mid_y-points.get(index).getY())*scalefactor + (bounds.getY() + 0.5 * height) + 1);
152 if(path.getCurrentPoint() == null) {
159 g2d.setColor(Color.WHITE);
162 g2d.setColor(Color.RED);
163 g2d.setStroke(new BasicStroke(0.2f));
164 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
168 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
169 float w = (float)(0.2*g2d.getTransform().getScaleX() < 1 ? 1/g2d.getTransform().getScaleX() : 0.2);
170 g2d.setStroke(new BasicStroke(w));
171 g2d.setColor(new Color(200, 200, 200));
174 GeneralPath axes = new GeneralPath();
175 axes.moveTo((float)bounds.getMinX()-1, (float)bounds.getMinY()); // Scale line
176 axes.lineTo((float)bounds.getMinX(), (float)bounds.getMinY());
177 axes.lineTo((float)bounds.getMinX(), (float)bounds.getMaxY());
178 axes.moveTo((float)bounds.getMinX()-1, (float)bounds.getMaxY()); // Scale line
179 axes.lineTo((float)bounds.getMaxX(), (float)bounds.getMaxY());
180 axes.lineTo((float)bounds.getMaxX(), (float)bounds.getMaxY()+1);
181 g2d.setColor(Color.BLACK);
185 NumberFormat eformat = new DecimalFormat("###E0");
186 NumberFormat format = new DecimalFormat("0.0#");
187 String max = Math.abs(max_y) > 1000 ? eformat.format(max_y) : format.format(max_y);
188 String min = Math.abs(min_y) > 1000 ? eformat.format(min_y) : format.format(min_y);
190 Font font = Font.decode("Arial 5");
192 FontMetrics fm = g2d.getFontMetrics();
194 double x1 = bounds.getMinX()-fm.getStringBounds(max, g2d).getWidth()-0.3;
195 double x2 = bounds.getMinX()-fm.getStringBounds(min, g2d).getWidth()-0.3;
196 double y1 = bounds.getMinY()+fm.getStringBounds(max, g2d).getHeight()/2;
197 double y2 = bounds.getMaxY()+fm.getStringBounds(max, g2d).getHeight()/2;
198 g2d.drawString(max, (float)x1, (float)y1);
199 g2d.drawString(min, (float)x2, (float)y2);
202 NumberFormat tformat = new DecimalFormat("#.0");
203 String time = tformat.format(max_x);
204 g2d.drawString(time, (float)(bounds.getMaxX()-fm.getStringBounds(time, g2d).getWidth()/2), (float)(bounds.getMaxY()+fm.getStringBounds(time, g2d).getHeight()));
\r
206 g2d.setTransform(ot);
210 * Same as Point2D.Double, but serializable.. (Point2D.Double is not serializable in 1.5)
214 public static class TrendPoint implements Serializable {
215 private static final long serialVersionUID = -3135433049709995399L;
219 public TrendPoint(double x, double y) {
224 public double getX() {
228 public double getY() {
232 public boolean equals(TrendPoint b) {
233 double deltax = Math.abs(b.getX() - this.getX());
234 double deltay = Math.abs(b.getY() - this.getY());
236 return deltax < 0.0000001 && deltay < 0.0000001; // FIXME
241 public Rectangle2D getBoundsInLocal() {