]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/Trend2DNode.java
Merge commit 'd186091'
[simantics/platform.git] / bundles / org.simantics.scenegraph / src / org / simantics / scenegraph / g2d / nodes / Trend2DNode.java
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
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.scenegraph.g2d.nodes;
13
14 import java.awt.BasicStroke;
15 import java.awt.Color;
16 import java.awt.Font;
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;
28
29 import org.simantics.scenegraph.g2d.G2DNode;
30
31 public class Trend2DNode extends G2DNode {
32
33         /**
34          * 
35          */
36         private static final long serialVersionUID = 8508750881358776559L;
37         
38         protected Rectangle2D bounds = null;
39         protected transient List<TrendPoint> points = null;
40     protected Integer x = 0;
41     protected Integer y = 0;
42
43         @SyncField({"x", "y"})
44         public void setPosition(int x, int y) {
45                 this.x = x;
46                 this.y = y;
47         }
48     
49         @SyncField("bounds")
50         public void setBounds(Rectangle2D bounds) {
51                 this.bounds = bounds;
52         }
53         
54         @SyncField("points")
55         protected void setPoints(List<TrendPoint> points) {
56                 this.points = points;
57         }
58
59         @ClientSide
60         protected void appendPoints(List<TrendPoint> points) {
61                 if(this.points == null) this.points = new ArrayList<TrendPoint>();
62                 /**
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
68                  */
69                 if(location.equals(Location.REMOTE)) {
70                         this.points.addAll(points);
71                 }
72                 this.repaint();
73         }
74         
75         /**
76          * Update trend points. If 
77          * 
78          * @param newpoints
79          */
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--) {
83                         int t = 0;
84                         for(int o = points.size()-1; o >= 0; o--) {
85                                 if(!newpoints.get(n-t).equals(points.get(o))) {
86                                         break;
87                                 }
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));
93                                         }
94                                         points = new ArrayList<TrendPoint>(newpoints);
95                                         if(appendedPoints != null && appendedPoints.size() > 0) {
96                                                 appendPoints(appendedPoints);
97                                         }
98                                         return;
99                                 }
100                                 if((n-t) == 0) {
101                                         setPoints(newpoints);
102                                         return;
103                                 }
104                                 t++;
105                         }
106                 }
107                 setPoints(newpoints);
108         }
109         
110         @Override
111         public void render(Graphics2D g2d) {
112         if(points == null || bounds == null) {
113                 return;
114         }\r
115         AffineTransform ot = g2d.getTransform();\r
116         
117         g2d.translate(x, y);
118         @SuppressWarnings("unused")\r
119         int pointsDrawn = 0;
120         
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--) {
126                 pointsDrawn++;
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;
133         }
134         
135         if(min_y > 0) min_y = 0;
136         if(max_y < 0) max_y = 0;
137
138         double mid_y = 0.5 * (max_y + min_y);
139         
140         GeneralPath path = new GeneralPath();
141
142         double scalex = bounds.getWidth() / (max_x - min_x); // Scale to fit
143         
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();
149                 
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) {
153                         path.moveTo(px, py);
154                 } else {
155                         path.lineTo(px, py);
156                 }
157         }
158         
159         g2d.setColor(Color.WHITE);
160         g2d.fill(bounds);
161         
162                 g2d.setColor(Color.RED);
163                 g2d.setStroke(new BasicStroke(0.2f));
164         g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
165
166                 g2d.draw(path);
167         
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));         
172         g2d.draw(bounds);
173         
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);      
182         g2d.draw(axes);
183
184         // Print scale
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);
189         
190         Font font = Font.decode("Arial 5");
191         g2d.setFont(font);
192         FontMetrics fm = g2d.getFontMetrics();
193         
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);
200         
201         // Print max time
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
205         \r
206         g2d.setTransform(ot);
207         }
208
209         /**
210          * Same as Point2D.Double, but serializable.. (Point2D.Double is not serializable in 1.5)
211          * @author J-P
212          *
213          */
214         public static class TrendPoint implements Serializable {
215                 private static final long serialVersionUID = -3135433049709995399L;
216                 public double x;
217                 public double y;
218                 
219                 public TrendPoint(double x, double y) {
220                         this.x = x;
221                         this.y = y;
222                 }
223                 
224                 public double getX() {
225                         return x;
226                 }
227                 
228                 public double getY() {
229                         return y;
230                 }
231
232                 public boolean equals(TrendPoint b) {
233                         double deltax = Math.abs(b.getX() - this.getX());
234                         double deltay = Math.abs(b.getY() - this.getY());
235                         
236                         return deltax < 0.0000001 && deltay < 0.0000001; // FIXME
237                 }
238         }
239
240         @Override
241         public Rectangle2D getBoundsInLocal() {
242                 return bounds;
243         }
244 }