Package ar.app.display

Source Code of ar.app.display.TransferDisplay$TransferRender

package ar.app.display;

import javax.swing.JFrame;

import java.awt.*;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.concurrent.ExecutorService;

import ar.*;
import ar.aggregates.AggregateUtils;
import ar.app.util.MostRecentOnlyExecutor;
import ar.renderers.ParallelRenderer;
import ar.util.Util;

/**Panel that will draw a set of aggregates on the screen with a given transfer function.**/
public class TransferDisplay extends ARComponent {
  private static final long serialVersionUID = 1L;
 
  /**Transfer function to use in rendering*/
  private Transfer<?,?> transfer;
 
  /**Aggregates to render*/
  private Aggregates<?> aggregates;

  /**What transform was used to produce the base aggregates.**/
  private volatile AffineTransform renderedTransform = new AffineTransform();

 
  /**Aggregates to use for specialization of the transfer function.
   *
   * If null, the full set of aggregates will be used for transfer specialization.
   **/
  private volatile Aggregates<?> refAggregates;

  private volatile Aggregates<?> postTransferAggregates;
  private volatile BufferedImage image;

  /**Transform being used to put geometry on the screen.
   * This is the full view transform, moving from geometry all the way to screen pixels,
   * so some of this action ***may*** be captured in the renderedTransform.
   * Otherwise said, you can use the view transform by itself to figure out where things
   * will be drawn.
   * **/
  private volatile AffineTransform viewTransform = new AffineTransform();
 
 
  private final Renderer renderer;
  private volatile boolean renderError = false;

  protected final ExecutorService renderPool = new MostRecentOnlyExecutor(1, this.getClass().getSimpleName() + " Render Thread");
 
  public TransferDisplay(Renderer renderer) {this(null, null, null, renderer);}
 
  public TransferDisplay(Aggregates<?> aggregates, Transfer<?,?> transfer) {
    this(aggregates, new AffineTransform(), transfer);
  }

  public TransferDisplay(Aggregates<?> aggregates, AffineTransform rendererd, Transfer<?,?> transfer) {
    this(aggregates, rendererd, transfer, new ParallelRenderer());
  }
 
  public TransferDisplay(Aggregates<?> aggregates, AffineTransform rendererdTransform, Transfer<?,?> transfer, Renderer renderer) {
    super();
    this.renderer = renderer;
    this.transfer = transfer;
    this.aggregates = aggregates;
    this.renderedTransform = rendererdTransform != null ? renderedTransform : new AffineTransform();
    this.addComponentListener(new ComponentListener(){
      public void componentResized(ComponentEvent e) {repaint();}
      public void componentMoved(ComponentEvent e) {}
      public void componentShown(ComponentEvent e) {}
      public void componentHidden(ComponentEvent e) {}
    });
  }
 
  protected void finalize() {renderPool.shutdown();}

  /**Set the aggregates set in transfer. 
   * Used as default set of aggregates if refAggregates is null.
   */
  public void aggregates(Aggregates<?> aggregates, AffineTransform renderedTransform) {
    this.aggregates = aggregates;
    this.renderedTransform = renderedTransform;
    renderError = false;
    postTransferAggregates = null;
    //viewTransform(new AffineTransform());
    repaint();
  }
 
  public Aggregates<?> aggregates() {return aggregates;}
  public Aggregates<?> transferAggregates() {return postTransferAggregates;}

 
  public Aggregates<?> refAggregates() {
     return refAggregates == null ? aggregates : refAggregates;
  }
 
  /**Set of aggregates to use in transfer-function specialization**/
  public void refAggregates(Aggregates<?> aggs) {
    if (this.refAggregates != aggs) {
      this.refAggregates = aggs;
      renderError = false;
      postTransferAggregates = null;
    }
    repaint();
  }
 
  public Transfer<?,?> transfer() {return transfer;}
  public void transfer(Transfer<?,?> transfer) {
    this.transfer = transfer;
    postTransferAggregates = null;
    renderError = false;
    repaint();
  }

  public void renderAgain() {
    postTransferAggregates = null;
    renderError=false;
    repaint();
  }
 
  public Renderer renderer() {return renderer;}
 
 
  @Override
  public void paintComponent(Graphics g) {
   
    if (postTransferAggregates == null
        && transfer !=null && aggregates !=null
        && !renderError) {
      renderPool.execute(new TransferRender());
    }

    g.setColor(Color.WHITE);
    g.fillRect(0, 0, this.getWidth(), this.getHeight());
    if (image != null) {
      Graphics2D g2 = (Graphics2D) g;
     
      g2.drawRenderedImage(image,offsetTransform(viewTransform, renderedTransform));
    }
  }
 
 
  /**In some cases, some of the view transform is reflected in the rendered transform,
   * the display needs a modified view transform to properly position/scale post-transfer results.
   * This calculates that new view transform.
   *
   * If the rendered transform is non-invertible/null or other exception occurs,
   * then the identity transform is returned.
   * 
   * @param vt The view transform required
   * @param rt The rendered transform assumed to have been already performed
   * @return
   */
  private static AffineTransform offsetTransform(AffineTransform vt, AffineTransform rt) {
    AffineTransform nvt;
    try {
      nvt = rt.createInverse();
      nvt.preConcatenate(vt);
      return nvt;
    } catch (Exception e) {
      return new AffineTransform();
    }
  }
 
  //TODO: Fix race condition between "aggregates" and "refAggregates".  May require that whenever you set "aggregates" then "refAggregates" gets cleared off and related shenanagans elsewhere
  protected final class TransferRender implements Runnable {
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public void run() {
      try {
        Aggregates<?> aggs = aggregates;
        if (aggs == null) {return;}
       
        long start = System.currentTimeMillis();
       
        Transfer.Specialized ts = transfer.specialize((Aggregates) refAggregates());
        postTransferAggregates = renderer.transfer(aggs, ts);
       
        int width = TransferDisplay.this.getWidth();
        int height = TransferDisplay.this.getHeight();
        if (postTransferAggregates.defaultValue() instanceof Color ) {
          image = AggregateUtils.asImage((Aggregates<Color>) postTransferAggregates, width, height, Util.CLEAR);
        } else {
          image = null;
        }
       
        long end = System.currentTimeMillis();
        if (PERFORMANCE_REPORTING) {
          Rectangle r = AggregateUtils.bounds(postTransferAggregates);
          System.out.printf("%d ms (transfer on %d x %d grid)\n",
              (end-start),
              Math.max(r.width, TransferDisplay.this.getWidth()),
              Math.max(r.height, TransferDisplay.this.getHeight()));
        }
      } catch (ClassCastException e) {
        renderError = true;
        if (PERFORMANCE_REPORTING) {e.printStackTrace();}
      }
     
      TransferDisplay.this.repaint();
    }
  }
   
  @Override public AffineTransform viewTransform() {return new AffineTransform(viewTransform);}

  @Override
  public void viewTransform(AffineTransform vt, boolean provisional) {
    if (vt == null) {vt = new AffineTransform();}
    this.viewTransform = new AffineTransform(vt);
    repaint();
  }

  @Override
  public void zoomFit() {
    Rectangle2D content = dataBounds();
    if (dataBounds() ==null || content.isEmpty()) {return;}
    viewTransform(Util.zoomFit(content, this.getWidth(), this.getHeight()), false);
  }

  @Override
  public Rectangle2D dataBounds() {
    Aggregates<?> aggs = aggregates;
    Rectangle2D content = (aggs == null ? null : AggregateUtils.bounds(aggs));
    return content;
  }

  /**Utility method to show a set of aggregates w.r.t. a transfer function in its own window.**/
  public static <A> void show(String title, int width, int height, Aggregates<? extends A> aggregates, Transfer<A,Color> transfer) {
    JFrame frame = new JFrame(title);
    frame.setLayout(new BorderLayout());
    frame.setSize(width,height);
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    frame.add(new TransferDisplay(aggregates, transfer), BorderLayout.CENTER);
    frame.setVisible(true);
    frame.revalidate();
    frame.validate();
  }

}
TOP

Related Classes of ar.app.display.TransferDisplay$TransferRender

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.