/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2013, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotoolkit.gui.swing.render3d.control; import org.geotoolkit.math.XMath; import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.geom.Ellipse2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.IOException; import java.util.logging.Level; import org.geotoolkit.display3d.Map3D; import org.geotoolkit.display3d.scene.ContextContainer3D; import org.geotoolkit.display3d.scene.Terrain; import org.geotoolkit.display3d.scene.camera.TrackBallCamera; /** * * @author Thomas Rouby (Geomatys) */ public class JZoomControlDecoration extends JComponent implements PropertyChangeListener { private static final int[] MARGIN = new int[]{50,5,50,5}; private static final int ANCHOR_RADIUS = 6; private static final int PADDING = ANCHOR_RADIUS/2; private static final int BUTTON_HEIGHT = ANCHOR_RADIUS*2+2; private static final int HEIGHT = 80; private static final int[] CONTENT_SIZE = new int[]{ANCHOR_RADIUS*2+2, BUTTON_HEIGHT*2 + PADDING*2 + HEIGHT}; private final Map3D map; private final TrackBallCamera camera; private final Color teinteDark; private final Color teinteLight; private final Color text1; private final Color text2; private Shape content; private Shape anchor; private BufferedImage imgPlus; private BufferedImage imgMinus; private double zoom = 0.0; // 1.0 = zoomMax (top bar) && 0.0 = zoomMin (bottom bar) private final BufferedImage buffer = new BufferedImage(MARGIN[0]+MARGIN[2]+CONTENT_SIZE[0], MARGIN[3]+MARGIN[1]+CONTENT_SIZE[1], BufferedImage.TYPE_INT_ARGB); private boolean mustUpdate = true; /** * -1 = no button * 0 = top * 1 = bottom * 2 = anchor */ private short overButton = -1; /** * -1 = no action * 1 = zoomAction */ private int actionFlag; public JZoomControlDecoration(Map3D map) { this.map = map; this.camera = map.getCamera(); this.camera.addPropertyChangeListener(this); teinteDark = Color.LIGHT_GRAY; teinteLight = Color.WHITE; text1 = Color.GRAY; text2 = Color.DARK_GRAY; try { imgPlus = ImageIO.read(this.getClass().getClassLoader().getResourceAsStream("img/icon/plus.png")); imgMinus = ImageIO.read(this.getClass().getClassLoader().getResourceAsStream("img/icon/minus.png")); } catch (IOException ex) { Map3D.LOGGER.log(Level.WARNING, "icon not found", ex); imgPlus = null; imgMinus = null; } addMouseListener(mouseListener); addMouseMotionListener(mouseMotionListener); setOpaque(false); content = new java.awt.geom.Rectangle2D.Float(MARGIN[0], MARGIN[3], CONTENT_SIZE[0], CONTENT_SIZE[1]); } @Override public void propertyChange(PropertyChangeEvent evt) { final Terrain terrain = ((ContextContainer3D)map.getContainer()).getTerrain(); if (terrain!= null){ float minLength = camera.getMinLength(); if (minLength < 0.0f){ minLength = (float) map.getDistForScale(terrain.getMinScale()); } float maxLength = camera.getMaxLength(); if (maxLength < 0.0f){ maxLength = (float) map.getDistForScale(terrain.getMaxScale()); } final float length = camera.getLength(); setZoom((length-minLength) / (maxLength-minLength)); } } @Override protected void paintComponent(final Graphics g) { super.paintComponent(g); final Graphics2D g2d = (Graphics2D) g; final Rectangle clip = g2d.getClipBounds(); if(!clip.intersects(0,0,buffer.getWidth(),buffer.getHeight())) return; if(mustUpdate){ final Graphics2D g2 = buffer.createGraphics(); g2.setStroke(new BasicStroke(1)); g2.setFont(g2d.getFont().deriveFont(Font.BOLD)); g2.setBackground(new Color(0f,0f,0f,0f)); g2.clearRect(0, 0, buffer.getWidth(), buffer.getHeight()); g2.setRenderingHints(g2d.getRenderingHints()); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setPaint(teinteLight); g2.fillRoundRect(MARGIN[0], MARGIN[3], CONTENT_SIZE[0], CONTENT_SIZE[1], CONTENT_SIZE[0]/2, CONTENT_SIZE[0]/2); g2.setPaint(teinteDark); g2.drawRoundRect(MARGIN[0], MARGIN[3], CONTENT_SIZE[0], CONTENT_SIZE[1], CONTENT_SIZE[0]/2, CONTENT_SIZE[0]/2); final int iconSize = BUTTON_HEIGHT-2; if (imgPlus != null){ Composite defaultComp = g2.getComposite(); if (overButton==0) { Composite comp= AlphaComposite.getInstance(AlphaComposite.XOR, 0.5f); g2.setComposite(comp); } g2.drawImage(imgPlus, MARGIN[0]+CONTENT_SIZE[0]/2-iconSize/2, MARGIN[3]+BUTTON_HEIGHT/2-iconSize/2, iconSize, iconSize, null); g2.setComposite(defaultComp); } if (imgMinus != null){ Composite defaultComp = g2.getComposite(); if (overButton==1) { Composite comp= AlphaComposite.getInstance(AlphaComposite.XOR, 0.5f); g2.setComposite(comp); } g2.drawImage(imgMinus, MARGIN[0]+CONTENT_SIZE[0]/2-iconSize/2, MARGIN[3]+CONTENT_SIZE[1]-BUTTON_HEIGHT/2-iconSize/2, iconSize, iconSize, null); g2.setComposite(defaultComp); } final int paddingTop = MARGIN[3]+BUTTON_HEIGHT; final int paddingHeight = CONTENT_SIZE[1]-BUTTON_HEIGHT*2; g2.setPaint(teinteLight); g2.fillRect(MARGIN[0], paddingTop, CONTENT_SIZE[0], paddingHeight); g2.setPaint(teinteDark); g2.drawRect(MARGIN[0], paddingTop, CONTENT_SIZE[0], paddingHeight); final int barTop = paddingTop+PADDING; final int barHeight = paddingHeight-PADDING*2; g2.setPaint(teinteDark); g2.drawArc(MARGIN[0], barTop, CONTENT_SIZE[0], CONTENT_SIZE[0], 0, 180); g2.setPaint(teinteDark); g2.drawArc(MARGIN[0], barTop+barHeight-CONTENT_SIZE[0], CONTENT_SIZE[0], CONTENT_SIZE[0], 0, -180); final int barInnerTop = barTop+1; final int barInnerHeight = barHeight-2; final int centerAnchorX = MARGIN[0]+1+ANCHOR_RADIUS; final int centerAnchorY = barInnerTop+ANCHOR_RADIUS + (int)((1.0-zoom)*(barInnerHeight-ANCHOR_RADIUS*2)); final float[] dist = {0.3f, 1.0f}; final Color[] colors; if (overButton == 2){ colors = new Color[]{text1, text2}; } else { colors = new Color[]{teinteLight, teinteDark}; } final RadialGradientPaint radialPaint = new RadialGradientPaint(new Point2D.Float(centerAnchorX, centerAnchorY), ANCHOR_RADIUS, dist, colors); g2.setPaint(radialPaint); anchor = new Ellipse2D.Float(centerAnchorX-ANCHOR_RADIUS, centerAnchorY-ANCHOR_RADIUS, ANCHOR_RADIUS*2+1, ANCHOR_RADIUS*2+1); g2.fill(anchor); mustUpdate = false; } g2d.drawImage(buffer, 0, 0, this); g2d.dispose(); } private void zoomMap(){ final Terrain terrain = ((ContextContainer3D)map.getContainer()).getTerrain(); float minLength = camera.getMinLength(); if (minLength < 0.0f){ minLength = (float) this.map.getDistForScale(terrain.getMinScale()); } float maxLength = camera.getMaxLength(); if (maxLength < 0.0f){ maxLength = (float) this.map.getDistForScale(terrain.getMaxScale()); } camera.zoomTo((float)(minLength + (maxLength-minLength)*zoom)); } private void setZoom(double zoom){ this.zoom = XMath.clamp(zoom, 0.0, 1.0); mustUpdate = true; repaint(content.getBounds()); } private void zoomMore(){ this.setZoom(XMath.clamp(zoom+0.001, 0.0, 1.0)); zoomMap(); } private void zoomLess(){ this.setZoom(XMath.clamp(zoom-0.001, 0.0, 1.0)); zoomMap(); } private double calculateZoom(int X, int Y){ int top = MARGIN[3]+BUTTON_HEIGHT+PADDING+1+ANCHOR_RADIUS; int bottom = top + HEIGHT - ANCHOR_RADIUS*2; int height = bottom - top; int h = XMath.clamp(Y, top, bottom) - top; return 1.0 - (double)(h)/(double)(height); } @Override public boolean contains(final int x, final int y) { return content.contains(x,y); } private final MouseListener mouseListener = new MouseListener() { boolean mousePressed = false; @Override public void mouseClicked(final MouseEvent e) { } @Override public void mousePressed(final MouseEvent e) { final Point mouse = e.getPoint(); if(!content.contains(mouse)) return; Thread actionThread = new Thread(new Runnable() { @Override public void run() { mousePressed = true; while (mousePressed) { if (anchor.contains(mouse.x, mouse.y)){ actionFlag = 1; mousePressed = false; } else { Shape buttonNorth = new Rectangle2D.Float(MARGIN[0], MARGIN[3], CONTENT_SIZE[0], BUTTON_HEIGHT); Shape buttonSouth = new Rectangle2D.Float(MARGIN[0], MARGIN[3]+CONTENT_SIZE[1]-BUTTON_HEIGHT, CONTENT_SIZE[0], BUTTON_HEIGHT); if (buttonNorth.contains(mouse.x, mouse.y)){ zoomMore(); } else if (buttonSouth.contains(mouse.x, mouse.y)){ zoomLess(); } } if (mousePressed){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } }); actionThread.setDaemon(true); actionThread.start(); } @Override public void mouseReleased(MouseEvent e) { mousePressed = false; actionFlag = -1; } @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseExited(MouseEvent e) { mousePressed = false; if(overButton !=-1){ overButton = -1; mustUpdate = true; repaint(content.getBounds()); } } }; private final MouseMotionListener mouseMotionListener = new MouseMotionListener() { @Override public void mouseDragged(final MouseEvent e) { if (actionFlag == 1) { setZoom(calculateZoom(e.getX(),e.getY())); zoomMap(); } } @Override public void mouseMoved(final MouseEvent e) { final Point mouse = e.getPoint(); if(content.contains(mouse)){ if (anchor.contains(mouse.x, mouse.y)){ overButton = 2; } else { Shape buttonNorth = new Rectangle2D.Float(MARGIN[0], MARGIN[3], CONTENT_SIZE[0], BUTTON_HEIGHT); Shape buttonSouth = new Rectangle2D.Float(MARGIN[0], MARGIN[3]+CONTENT_SIZE[1]-BUTTON_HEIGHT, CONTENT_SIZE[0], BUTTON_HEIGHT); if (buttonNorth.contains(mouse.x, mouse.y)){ overButton = 0; } else if (buttonSouth.contains(mouse.x, mouse.y)){ overButton = 1; } else { overButton = -1; } } mustUpdate = true; repaint(content.getBounds()); } else if (overButton != -1){ overButton = -1; mustUpdate = true; repaint(content.getBounds()); } } }; @Override public Dimension getPreferredSize() { return new Dimension(buffer.getWidth(),buffer.getHeight()); } @Override public Dimension getSize() { return getPreferredSize(); } @Override public Dimension getSize(final Dimension rv) { if(rv != null){ rv.height = buffer.getHeight(); rv.width = buffer.getWidth(); return rv; }else{ return getSize(); } } @Override public int getWidth() { return this.getSize().width; } @Override public int getHeight() { return this.getSize().height; } }