]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/RulerNode.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.scenegraph / src / org / simantics / scenegraph / g2d / nodes / RulerNode.java
diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/RulerNode.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/RulerNode.java
new file mode 100644 (file)
index 0000000..ef6f2fc
--- /dev/null
@@ -0,0 +1,241 @@
+/*******************************************************************************\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.AlphaComposite;\r
+import java.awt.BasicStroke;\r
+import java.awt.Color;\r
+import java.awt.Font;\r
+import java.awt.FontMetrics;\r
+import java.awt.Graphics2D;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Rectangle2D;\r
+import java.util.Locale;\r
+\r
+import org.simantics.scenegraph.g2d.G2DNode;\r
+import org.simantics.scenegraph.utils.GridUtils;\r
+
+public class RulerNode extends G2DNode {
+    /**
+     * 
+     */
+    private static final long  serialVersionUID  = 2490944880914577411L;\r
+\r
+    /**\r
+     * FIXME: this is a hack for the map UI that has to be solved some other way.\r
+     */\r
+    private static final boolean MAP_Y_SCALING = false;\r
+\r
+    private static final Color GRAY              = new Color(100, 100, 100);\r
+\r
+    protected Boolean          enabled           = true;\r
+\r
+    protected double           gridSize          = 1.0;\r
+\r
+    @SyncField("enabled")\r
+    public void setEnabled(Boolean enabled) {\r
+        this.enabled = enabled;\r
+    }\r
+\r
+    @SyncField("gridSize")\r
+    public void setGridSize(double gridSize) {\r
+        if (gridSize < 1e-6)\r
+            gridSize = 1e-6;\r
+        this.gridSize = gridSize;\r
+    }\r
+\r
+    @Override
+    public void render(Graphics2D g) {
+        if (!enabled)\r
+            return;\r
+\r
+        AffineTransform tr = g.getTransform();
+        double scaleX = Math.abs(tr.getScaleX());
+        double scaleY = Math.abs(tr.getScaleY());
+        if (scaleX <= 0 || scaleY <= 0) {
+            // Make sure that we don't end up in an eternal loop below.
+            return;
+        }
+        double offsetX = tr.getTranslateX();
+        double offsetY = tr.getTranslateY();
+        g.setTransform(new AffineTransform());
+
+        Font rulerFont = new Font("Tahoma", Font.PLAIN, 9);
+
+        //g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        g.setStroke(new BasicStroke(1));
+        g.setColor(new Color(0.9f, 0.9f, 0.9f, 0.75f));
+
+        Rectangle2D bounds = g.getClipBounds();
+        if(bounds == null) return; // FIXME
+
+        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f));
+
+        Rectangle2D vertical = new Rectangle2D.Double(bounds.getMinX(), bounds.getMinY(), bounds.getMaxX(), bounds.getMinY()+20);
+        g.fill(vertical);
+
+        Rectangle2D horizontal = new Rectangle2D.Double(bounds.getMinX(), bounds.getMinY()+20, bounds.getMinX()+20, bounds.getMaxY());
+        g.fill(horizontal);
+
+        // stepX and stepY should be something between 50 and 100
+        double stepX = 50;
+        double stepY = 50;
+\r
+        stepX = GridUtils.limitedEvenGridSpacing(stepX, scaleX, 100, gridSize, true);\r
+        stepY = GridUtils.limitedEvenGridSpacing(stepY, scaleY, 100, gridSize, true);\r
+        //while(stepX * scaleX > 100) stepX /= 2;
+        //while(stepY * scaleY > 100) stepY /= 2;
+
+        while(stepX * scaleX < 50) stepX *= 2;
+        while(stepY * scaleY < 50) stepY *= 2;
+\r
+        stepX *= scaleX;
+        stepY *= scaleY;
+
+        g.setColor(GRAY);
+        g.setFont(rulerFont);
+
+        double previousText = -100;
+
+        // Vertical ruler
+        for(double x = offsetX%stepX-stepX; x < bounds.getMaxX(); x+=stepX) {\r
+            if(x > 20) {
+                String str = formatValue((x-offsetX)/scaleX);
+                FontMetrics fm = g.getFontMetrics();
+                Rectangle2D r = fm.getStringBounds(str, g);
+                if((x-r.getWidth()/2) > previousText) {
+                    g.setColor(Color.BLACK);
+                    g.drawString(str, (int)(x-r.getWidth()/2), (int)(bounds.getMinY()+1+r.getHeight()));
+                    previousText = x+r.getWidth()/2+stepX/4;
+                }
+
+                g.setColor(GRAY);
+                g.drawLine((int)x, (int)bounds.getMinY()+12, (int)x, (int)bounds.getMinY()+19);
+            }
+            if(stepX/5 > 2) {
+                for(double x2 = x+stepX/5; x2 < x+stepX; x2+=stepX/5) {
+                    if(x2 > 20) {
+                        g.drawLine((int)x2, (int)bounds.getMinY()+15, (int)x2, (int)bounds.getMinY()+19);
+                    }
+                }
+                for(double x2 = x+stepX/10; x2 < x+stepX; x2+=stepX/5) {
+                    if(x2 > 20) {
+                        g.drawLine((int)x2, (int)bounds.getMinY()+17, (int)x2, (int)bounds.getMinY()+19);
+                    }
+                }
+
+            }
+        }
+\r
+        // Horizontal ruler
+        previousText = -100;
+        for(double y = offsetY%stepY-stepY; y < bounds.getMaxY(); y+=stepY) {\r
+            if(y > 20) {\r
+                double val = (y-offsetY)/scaleY;\r
+                if (MAP_Y_SCALING)\r
+                    val = Math.toDegrees(Math.atan(Math.sinh(Math.toRadians(val))));\r
+                String str = formatValue(val);
+                FontMetrics fm = g.getFontMetrics();
+                Rectangle2D r = fm.getStringBounds(str, g);
+                if(y-1+r.getHeight()/2 > previousText) {
+                    g.setColor(Color.BLACK);
+                    AffineTransform origTr = g.getTransform();
+                    g.translate((int)(bounds.getMinX()), (int)(y+r.getWidth()/2));
+                    g.rotate(-Math.PI / 2.0);
+                    g.drawString(str, 0, (int)r.getHeight());
+                    g.setTransform(origTr);
+                    previousText = y-1+r.getHeight();
+                }
+                g.setColor(GRAY);
+                g.drawLine((int)bounds.getMinX()+12, (int)y, (int)bounds.getMinX()+19, (int)y);
+            }
+            if(stepY/5 > 2) {
+                for(double y2 = y+stepY/5; y2 < y+stepY; y2+=stepY/5) {
+                    if(y2 > 20) {
+                        g.drawLine((int)bounds.getMinX()+15, (int)y2, (int)bounds.getMinX()+19, (int)y2);
+                    }
+                }
+                for(double y2 = y+stepY/10; y2 < y+stepY; y2+=stepY/5) {
+                    if(y2 > 20) {
+                        g.drawLine((int)bounds.getMinX()+17, (int)y2, (int)bounds.getMinX()+19, (int)y2);
+                    }
+                }
+            }
+        }
+
+        g.setTransform(tr);
+    }\r
+
+    private static final transient int    MAX_DIGITS = 5;
+    private static final transient double EPSILON    = 0.01;
+    private static final transient double TRIM_THRESHOLD_MAX_VALUE = Math.pow(10, 4);
+    private static final transient String[] SI_UNIT_LARGE_PREFIXES = {
+        "k", "M", "G", "T", "P", "E", "Z", "Y"
+    };
+
+    public static String formatValue(double value) {
+        int magnitude = (int) Math.round(Math.log10(value));
+        //System.out.println("magnitude: " + magnitude + ", " + value);
+        int allowedDecimals = MAX_DIGITS;
+        allowedDecimals -= Math.abs(magnitude);
+        if (allowedDecimals < 0)
+            allowedDecimals = 0;
+
+        String valueStr = String.format(Locale.US, "%." + allowedDecimals + "f", value);
+        if (allowedDecimals > 0) {
+            for (int trunc = valueStr.length() - 1; trunc > 0; --trunc) {
+                char ch = valueStr.charAt(trunc);
+                if (ch == '.') {
+                    valueStr = valueStr.substring(0, trunc);
+                    break;
+                }
+                if (valueStr.charAt(trunc) != '0') {
+                    valueStr = valueStr.substring(0, trunc + 1);
+                    break;
+                }
+            }
+            if (Math.abs(value) + EPSILON > TRIM_THRESHOLD_MAX_VALUE) {
+                // Cut anything beyond a possible decimal dot out since they
+                // should not show anyway. This is a complete hack that tries to
+                // circumvent floating-point inaccuracy problems.
+                int dotIndex = valueStr.lastIndexOf('.');
+                if (dotIndex > -1) {
+                    valueStr = valueStr.substring(0, dotIndex);
+                }
+            }
+        }
+
+        double trimValue = value;
+        if (Math.abs(value)+EPSILON >= TRIM_THRESHOLD_MAX_VALUE) {
+            for (int i = 0; Math.abs(trimValue)+EPSILON >= TRIM_THRESHOLD_MAX_VALUE; ++i) {
+                double trim = trimValue / 1000;
+                if (Math.abs(trim)-EPSILON < TRIM_THRESHOLD_MAX_VALUE) {
+                    valueStr = valueStr.substring(0, valueStr.length() - (i + 1) * 3);
+                    valueStr += SI_UNIT_LARGE_PREFIXES[i];
+                    break;
+                }
+                trimValue = trim;
+            }
+        }
+
+        if (valueStr.equals("-0"))
+            valueStr = "0";
+
+        return valueStr;
+    }
+
+    @Override
+    public Rectangle2D getBoundsInLocal() {
+        return null;
+    }
+
+}