Package ar.app.display

Source Code of ar.app.display.EnhanceHost$Control

package ar.app.display;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JPanel;

import ar.Aggregates;
import ar.Aggregator;
import ar.Glyphset;
import ar.Renderer;
import ar.Transfer;
import ar.aggregates.wrappers.SubsetWrapper;
import ar.app.util.LabeledItem;
import ar.glyphsets.BoundingWrapper;
import ar.util.Util;

/**Host a panel, add to it a draw-on overlay and enhance-region capability.**/
public class EnhanceHost extends ARComponent.Aggregating {
  private static final long serialVersionUID = -6449887730981205865L;
 
  private static final Color SHOW_ENHANCED  = new Color(100,149,237);
 
  private AggregatingDisplay hosted;
  private SelectionOverlay overlay;
  private EnhancedOverlay enhanced = new EnhancedOverlay();
  private boolean enhanceEnabled;
  private boolean limitEnabled;

  private boolean redoRefAggregates = true;
 
  /**Host the given component in the overlay.**/
  public EnhanceHost(AggregatingDisplay hosted) { 
    this.hosted = hosted;
    this.overlay = new SelectionOverlay(this);
    this.add(overlay);
    this.add(enhanced);
    this.add(hosted);
   
    overlay.setVisible(false);
    enhanced.setVisible(false);
   
    hosted.addAggregatesChangedListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {forceNewRefAggregates();}
    });
  }
 
  public void layout() {
    Rectangle b = this.getBounds();
    hosted.setBounds(b);
    enhanced.setBounds(b);
    overlay.setBounds(b);
  }

  /**Is the overlay currently visible?**/
  public boolean showOverlay() {return overlay.isVisible();}
 
  /**Set the overlay visibility state.**/
  public void showOverlay(boolean show) {overlay.setVisible(show);}

  /**Force a refresh of the ref aggregates and trigger a repaint.**/
  public void forceNewRefAggregates() {
    redoRefAggregates = true;
    this.repaint();
  }
 
  /**Get the subset of aggregates that corresponds to the currently selected region.
   * @return null if no selection; otherwise a subset of aggregates.
   */
  public <A> Aggregates<?> subset() {
    if (overlay.selected == null) {return null;}
   
    @SuppressWarnings("unchecked")
    Aggregates<A> aggs = (Aggregates<A>) hosted.aggregates();
    AffineTransform rt = hosted.renderTransform();
    Area selection = overlay.selectedArea().createTransformedArea(rt);
    Rectangle bounds = selection.getBounds();
   
    int lowX = bounds.x;
    int lowY = bounds.y;
    int highX = (int) bounds.getMaxX();
    int highY = (int) bounds.getMaxY();
   
    return new SubsetWrapper<>(aggs, lowX, lowY, highX, highY);
  }
 
  public void enableEnhance(boolean enable) {
    this.enhanceEnabled = enable;
    redoRefAggregates  = true;
    enhanced.setVisible(enable);
    this.repaint();
   
  }
 
  ///Subset related options ---------------------------------------------------------------------------
  public void updateLimit() {
    Glyphset<?,?> replacement;
    Rectangle2D bounds = limitBounds();
    if (bounds == null || enableLimit()) {
      Glyphset<?,?> original = hosted.dataset();
      if (original instanceof BoundingWrapper) {original = ((BoundingWrapper<?,?>) original).base();}
      replacement = new BoundingWrapper<>(original, bounds);
    } else {
      replacement = hosted.dataset();
      if (replacement instanceof BoundingWrapper) {replacement = ((BoundingWrapper<?,?>) replacement).base();}
     
    }   
    hosted.dataset(replacement, hosted.aggregator, hosted.transfer(), false);
    hosted.zoomFit();
    this.repaint();
  }
 
  public Rectangle2D limitBounds() {
    Area a = overlay.selected;
    if (a == null) {return null;}
    else {return a.getBounds2D();}
  }
 
  public boolean enableLimit() {return limitEnabled;}
  public void enableLimit(boolean limit) {limitEnabled = limit; updateLimit();}
 
  public void paint(Graphics g) {
    if (redoRefAggregates) {
      if (enhanceEnabled) {
        Aggregates<?> subset = subset();
        hosted.refAggregates(subset);
      } else {
        hosted.refAggregates(hosted.aggregates());
      }
      redoRefAggregates = false;
    }
    super.paint(g);
   
    AffineTransform vt = hosted.viewTransform();
    if (enhanceEnabled && vt != null && overlay.selectedArea() != null) {
      Graphics2D g2 = (Graphics2D) g.create();
      g2.setColor(SHOW_ENHANCED);
      g2.setStroke(new BasicStroke(1f));
      g2.draw(overlay.selectedArea().createTransformedArea(vt));
    }
  }

  public Transfer<?, ?> transfer() {return hosted.transfer();}
  public void transfer(Transfer<?, ?> t) {hosted.transfer(t);}
  public Aggregates<?> transferAggregates() {return hosted.transferAggregates();}
  public Aggregates<?> aggregates() {return hosted.aggregates();}
  public void aggregates(Aggregates<?> aggregates, AffineTransform renderTransform) {
    hosted.aggregates(aggregates, renderTransform);
  }
  public Aggregates<?> refAggregates() {return hosted.refAggregates();}
  public void refAggregates(Aggregates<?> aggregates) {hosted.refAggregates(aggregates);}
  public Glyphset<?,?> dataset() {
    if (hosted.dataset() instanceof BoundingWrapper) {
      return ((BoundingWrapper<?,?>) hosted.dataset()).base();
    } else {return hosted.dataset();}
  }
 
  public void renderAgain() {hosted.renderAgain();}
 
  public void dataset(Glyphset<?,?> data, Aggregator<?,?> aggregator, Transfer<?,?> transfer) {
    hosted.dataset(data, aggregator, transfer);
    overlay.clear();
  }
 
  public Aggregator<?, ?> aggregator() {return hosted.aggregator();}
  public Renderer renderer() {return hosted.renderer();}

  public void zoomFit() {hosted.zoomFit();}
  public AffineTransform viewTransform() {return hosted.viewTransform();}
  public AffineTransform renderTransform() {return hosted.renderTransform();}
  public void viewTransform(AffineTransform vt, boolean provisional) {hosted.viewTransform(vt, provisional);}
  public Rectangle2D dataBounds() {return hosted.dataBounds();}

  private static final int borderSize = 5;
  private static BufferedImage makeImage(int size, Color stripes, Color spaces) {
    BufferedImage img = new BufferedImage(size,size, BufferedImage.TYPE_4BYTE_ABGR);
    for (int x=0; x<size; x++) {
      for (int y=0; y<size; y++) {
        if (x ==y || x==y+1 || x==y-1
            || (x==0 && y==size-1)
            || (x==size-1 && y==0)) {img.setRGB(x, y, stripes.getRGB());}
        else {img.setRGB(x, y, spaces.getRGB());}
      }
    }
    return img;
  }

  private static BufferedImage flip(BufferedImage src) {
    BufferedImage img = new BufferedImage(src.getWidth(),src.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
    for (int x=0; x<src.getWidth(); x++) {
      for (int y=0; y<src.getHeight(); y++) {
        img.setRGB(src.getWidth()-x-1,y, src.getRGB(x,y));
      }
    }
    return img;
  }

 
  private static class EnhancedOverlay extends JComponent {
    public EnhancedOverlay() {
      ImageIcon icn = new ImageIcon(flip(makeImage(borderSize+2, SHOW_ENHANCED, Util.CLEAR)));
     
      this.setBorder(BorderFactory.createMatteBorder(borderSize, borderSize, borderSize, borderSize, icn));
    }
  }
 
  /**Component to store and display a selection.**/
  private static class SelectionOverlay extends JComponent implements Selectable {
    private static final long serialVersionUID = 9079768489874376280L;
   
    /**Color to show 'delete' in enhancement.**/
    public Color PROVISIONAL = new Color(200,30,30);
   
    private Area selected;
   
    /**Current selection in dataset coordinates.**/
    private Rectangle2D provisional = null;
    private boolean provisionalRemove = false;
       
    /**Hosting object.**/
    private final EnhanceHost host;
   
   
    public SelectionOverlay(EnhanceHost host) {
      AdjustRange r = new AdjustRange(this);
      this.addMouseListener(r);
      this.addMouseMotionListener(r);
      this.host = host;
      this.setBorder(
          BorderFactory.createMatteBorder(
              borderSize,borderSize,borderSize,borderSize,
              new ImageIcon(makeImage(borderSize+2, new Color(255,140,0), Util.CLEAR))));
    }
   
    public void paintComponent(Graphics g) {
      super.paintComponent(g);
      Graphics2D g2= (Graphics2D) g;
      AffineTransform vt = host.viewTransform();
     

      g2.setColor(SHOW_ENHANCED);
      if (selected != null) {g2.draw(vt.createTransformedShape(selected));}

      if (provisionalRemove) {g2.setColor(PROVISIONAL);}
      if (provisional != null) {g2.draw(vt.createTransformedShape(provisional));}
    }

    public void clear() {
      this.selected = null;
      this.provisional = null;
    }
   
    /**Set the current selection to a new value.
     * To indicate transient state, set the "provisional" flag.
     *
     * @param bounds Selection bounds in screen-space
     * @param provisional Flag passed bounds as provisional selection
     ***/
    public void modSelection(Rectangle2D bounds, boolean provisional, boolean remove) {
     
      try  {
        if (bounds != null) {
          bounds = host.viewTransform().createInverse().createTransformedShape(bounds).getBounds2D();
        }
      } catch (NoninvertibleTransformException e) {/*Ignore...should be impossible...should be.*/}
     
      if (provisional) {
        this.provisional = bounds;
        this.provisionalRemove = remove;
      } else {
        this.provisional = null;
        if (bounds != null) {
          Area a =new Area(bounds);
          if (remove && selected != null) {
            selected.subtract(a);
          } else if (selected != null) {
            selected.add(a);           
          } else {
            selected = a;
          }
        }
        if (selected != null && selected.isEmpty()) {selected = null;}
        host.forceNewRefAggregates();
      }
     
      this.repaint();
    }
   
    public Area selectedArea() {return selected;}
  }
 
  /**Interface indicating a thing has a selection region associated with it.**/
  private interface Selectable {
    public void clear();
    public void modSelection(Rectangle2D bounds, boolean provisional, boolean remove);
  }

  private final static class AdjustRange implements MouseListener, MouseMotionListener {
    Point2D start;
    final Selectable target;
    public AdjustRange(Selectable target) {this.target = target;}

    public void mousePressed(MouseEvent e) {start = e.getPoint();}
    public void mouseReleased(MouseEvent e) {
      if (start != null) {
        Rectangle2D bounds =bounds(e);
        if (bounds.isEmpty() || bounds.getWidth()*bounds.getHeight()<1) {bounds = null;}
        target.modSelection(bounds, false, altPressed(e));
      }
      start = null;
    }
   
    public void mouseClicked(MouseEvent e) {target.clear();}
    public void mouseDragged(MouseEvent e) {
      if (start != null) {
        Rectangle2D bounds =bounds(e);       
        if (bounds.isEmpty() || bounds.getWidth()*bounds.getHeight()<1) {bounds = null;}
        target.modSelection(bounds, true, altPressed(e));
      }
    }

    private static boolean altPressed(MouseEvent e) {
      return (e.getModifiersEx() & MouseEvent.ALT_DOWN_MASK) == MouseEvent.ALT_DOWN_MASK;
    }
   
    private Rectangle2D bounds(MouseEvent e) {
      double w = Math.abs(start.getX()-e.getX());
      double h = Math.abs(start.getY()-e.getY());
      double x = Math.min(start.getX(), e.getX());
      double y = Math.min(start.getY(), e.getY());
         
      return new Rectangle2D.Double(x,y,w,h);
    }
   
    public void mouseMoved(MouseEvent e) {}
    public void mouseEntered(MouseEvent e) {}
    public void mouseExited(MouseEvent e) {}
  }
 
  /**Toggle control for the given overlay.**/
  public static final class Control extends JPanel {
    private static final long serialVersionUID = -4922729009197379804L;
    private final JCheckBox box = new JCheckBox();
    private EnhanceHost host=null;
   
    /**Create a toggle control.  Will have no effect until 'host' is set.**/
    public Control() {
      JPanel item = new LabeledItem("Modify Selection:", box);
      this.add(item);
     
      box.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          boolean show = ((JCheckBox)e.getSource()).isSelected();
          host().showOverlay(show);
          host().repaint();
        }
      });
    }
   
    public void clear() {box.setSelected(false);}
   
    /**Get the target host control.**/
    public EnhanceHost host() {return host;}
    /**Set the target host control.**/
    public void host(EnhanceHost host) {this.host = host;}
  }
}
TOP

Related Classes of ar.app.display.EnhanceHost$Control

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.