]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/Trend2DNode.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.scenegraph / src / org / simantics / scenegraph / g2d / nodes / Trend2DNode.java
diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/Trend2DNode.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/Trend2DNode.java
new file mode 100644 (file)
index 0000000..a2c2e5d
--- /dev/null
@@ -0,0 +1,244 @@
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.scenegraph.g2d.nodes;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Rectangle2D;
+import java.io.Serializable;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.simantics.scenegraph.g2d.G2DNode;
+
+public class Trend2DNode extends G2DNode {
+
+       /**
+        * 
+        */
+       private static final long serialVersionUID = 8508750881358776559L;
+       
+       protected Rectangle2D bounds = null;
+       protected transient List<TrendPoint> points = null;
+    protected Integer x = 0;
+    protected Integer y = 0;
+
+       @SyncField({"x", "y"})
+       public void setPosition(int x, int y) {
+               this.x = x;
+               this.y = y;
+       }
+    
+       @SyncField("bounds")
+       public void setBounds(Rectangle2D bounds) {
+               this.bounds = bounds;
+       }
+       
+       @SyncField("points")
+       protected void setPoints(List<TrendPoint> points) {
+               this.points = points;
+       }
+
+       @ClientSide
+       protected void appendPoints(List<TrendPoint> points) {
+               if(this.points == null) this.points = new ArrayList<TrendPoint>();
+               /**
+                * We need to have the same set of points on the both sides, so locally the points are just updated, 
+                * but on remote side we send only the new points. 
+                * In case we are running on local workbench, the point list is updated and in addition these points
+                * would be added to the list without this check.
+                * FIXME: find out some way to implement this without this check
+                */
+               if(location.equals(Location.REMOTE)) {
+                       this.points.addAll(points);
+               }
+               this.repaint();
+       }
+       
+       /**
+        * Update trend points. If 
+        * 
+        * @param newpoints
+        */
+       public void updatePoints(List<TrendPoint> newpoints) {
+               if(points == null) points = new ArrayList<TrendPoint>();
+               for(int n = newpoints.size()-1; n >= 0; n--) {
+                       int t = 0;
+                       for(int o = points.size()-1; o >= 0; o--) {
+                               if(!newpoints.get(n-t).equals(points.get(o))) {
+                                       break;
+                               }
+                               if(o == 0 || o < points.size()-10) {
+                                       // Now we have 10 matching values, so n tells where the old points ends
+                                       ArrayList<TrendPoint> appendedPoints = new ArrayList<TrendPoint>();
+                                       for(int i = n; i < newpoints.size()-1; i++) {
+                                               appendedPoints.add(newpoints.get(i));
+                                       }
+                                       points = new ArrayList<TrendPoint>(newpoints);
+                                       if(appendedPoints != null && appendedPoints.size() > 0) {
+                                               appendPoints(appendedPoints);
+                                       }
+                                       return;
+                               }
+                               if((n-t) == 0) {
+                                       setPoints(newpoints);
+                                       return;
+                               }
+                               t++;
+                       }
+               }
+               setPoints(newpoints);
+       }
+       
+       @Override
+       public void render(Graphics2D g2d) {
+       if(points == null || bounds == null) {
+               return;
+       }\r
+       AffineTransform ot = g2d.getTransform();\r
+       
+       g2d.translate(x, y);
+       @SuppressWarnings("unused")\r
+        int pointsDrawn = 0;
+       
+       double max_y = Double.MIN_VALUE; // for scaling
+       double min_y = Double.MAX_VALUE;
+       double max_x = Double.MIN_VALUE; // for scaling
+       double min_x = Double.MAX_VALUE;        
+       for(int index = points.size()-1; index >= 0; index--) {
+               pointsDrawn++;
+               double y = points.get(index).getY();
+               double x = points.get(index).getX();
+               if(y > max_y) max_y = y;
+               if(y < min_y) min_y = y;
+               if(x > max_x) max_x = x;
+               if(x < min_x) min_x = x;
+       }
+       
+       if(min_y > 0) min_y = 0;
+       if(max_y < 0) max_y = 0;
+
+       double mid_y = 0.5 * (max_y + min_y);
+       
+        GeneralPath path = new GeneralPath();
+
+        double scalex = bounds.getWidth() / (max_x - min_x); // Scale to fit
+        
+       double scalefactor = 0;
+       double height = bounds.getHeight()-2;
+       if(max_y - min_y > 0) scalefactor = height / (max_y - min_y);
+       for(int index = points.size()-1; index >= 0; index--) {
+               double t = bounds.getMinX();
+               
+               float px = (float)((points.get(index).getX()-min_x) * scalex + t);
+               float py = (float)((mid_y-points.get(index).getY())*scalefactor + (bounds.getY() + 0.5 * height) + 1);
+               if(path.getCurrentPoint() == null) {
+                       path.moveTo(px, py);
+               } else {
+                       path.lineTo(px, py);
+               }
+       }
+       
+       g2d.setColor(Color.WHITE);
+       g2d.fill(bounds);
+       
+               g2d.setColor(Color.RED);
+               g2d.setStroke(new BasicStroke(0.2f));
+        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+
+               g2d.draw(path);
+       
+        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
+       float w = (float)(0.2*g2d.getTransform().getScaleX() < 1 ? 1/g2d.getTransform().getScaleX() : 0.2);
+               g2d.setStroke(new BasicStroke(w));
+       g2d.setColor(new Color(200, 200, 200));         
+       g2d.draw(bounds);
+       
+       GeneralPath axes = new GeneralPath();
+       axes.moveTo((float)bounds.getMinX()-1, (float)bounds.getMinY()); // Scale line
+       axes.lineTo((float)bounds.getMinX(), (float)bounds.getMinY());
+       axes.lineTo((float)bounds.getMinX(), (float)bounds.getMaxY());
+       axes.moveTo((float)bounds.getMinX()-1, (float)bounds.getMaxY()); // Scale line
+       axes.lineTo((float)bounds.getMaxX(), (float)bounds.getMaxY());
+       axes.lineTo((float)bounds.getMaxX(), (float)bounds.getMaxY()+1);
+       g2d.setColor(Color.BLACK);      
+       g2d.draw(axes);
+
+       // Print scale
+       NumberFormat eformat = new DecimalFormat("###E0");
+       NumberFormat format = new DecimalFormat("0.0#");
+       String max = Math.abs(max_y) > 1000 ? eformat.format(max_y) : format.format(max_y);
+       String min = Math.abs(min_y) > 1000 ? eformat.format(min_y) : format.format(min_y);
+       
+       Font font = Font.decode("Arial 5");
+       g2d.setFont(font);
+       FontMetrics fm = g2d.getFontMetrics();
+       
+       double x1 = bounds.getMinX()-fm.getStringBounds(max, g2d).getWidth()-0.3;
+       double x2 = bounds.getMinX()-fm.getStringBounds(min, g2d).getWidth()-0.3;
+       double y1 = bounds.getMinY()+fm.getStringBounds(max, g2d).getHeight()/2;
+       double y2 = bounds.getMaxY()+fm.getStringBounds(max, g2d).getHeight()/2;
+       g2d.drawString(max, (float)x1, (float)y1);
+       g2d.drawString(min, (float)x2, (float)y2);
+       
+       // Print max time
+       NumberFormat tformat = new DecimalFormat("#.0");
+       String time = tformat.format(max_x);
+       g2d.drawString(time, (float)(bounds.getMaxX()-fm.getStringBounds(time, g2d).getWidth()/2), (float)(bounds.getMaxY()+fm.getStringBounds(time, g2d).getHeight()));\r
+       \r
+       g2d.setTransform(ot);
+       }
+
+       /**
+        * Same as Point2D.Double, but serializable.. (Point2D.Double is not serializable in 1.5)
+        * @author J-P
+        *
+        */
+       public static class TrendPoint implements Serializable {
+               private static final long serialVersionUID = -3135433049709995399L;
+               public double x;
+               public double y;
+               
+               public TrendPoint(double x, double y) {
+                       this.x = x;
+                       this.y = y;
+               }
+               
+               public double getX() {
+                       return x;
+               }
+               
+               public double getY() {
+                       return y;
+               }
+
+               public boolean equals(TrendPoint b) {
+                       double deltax = Math.abs(b.getX() - this.getX());
+                       double deltay = Math.abs(b.getY() - this.getY());
+                       
+                       return deltax < 0.0000001 && deltay < 0.0000001; // FIXME
+               }
+       }
+
+       @Override
+       public Rectangle2D getBoundsInLocal() {
+               return bounds;
+       }
+}