/** * Copyright (c) 2009, iPlant Collaborative, Texas Advanced Computing Center This software is licensed * under the CC-GNU GPL version 2.0 or later. License: http://creativecommons.org/licenses/GPL/2.0/ */ package org.iplantc.phyloviewer.client.tree.viewer; import java.util.logging.Level; import java.util.logging.Logger; import org.iplantc.phyloviewer.client.math.ConvertMathTypes; import org.iplantc.phyloviewer.client.services.TreeImage; import org.iplantc.phyloviewer.client.services.TreeImageAsync; import org.iplantc.phyloviewer.client.services.TreeIntersectService; import org.iplantc.phyloviewer.client.services.TreeIntersectServiceAsync; import org.iplantc.phyloviewer.client.tree.viewer.canvas.Canvas; import org.iplantc.phyloviewer.client.tree.viewer.canvas.Image; import org.iplantc.phyloviewer.client.tree.viewer.canvas.ImageListener; import org.iplantc.phyloviewer.shared.math.Box2D; import org.iplantc.phyloviewer.shared.math.Matrix33; import org.iplantc.phyloviewer.shared.math.Vector2; import org.iplantc.phyloviewer.shared.model.IDocument; import org.iplantc.phyloviewer.shared.model.ITree; import org.iplantc.phyloviewer.shared.render.Camera; import org.iplantc.phyloviewer.shared.render.Defaults; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JsonUtils; import com.google.gwt.event.dom.client.MouseDownEvent; import com.google.gwt.event.dom.client.MouseDownHandler; import com.google.gwt.event.dom.client.MouseMoveEvent; import com.google.gwt.event.dom.client.MouseMoveHandler; import com.google.gwt.user.client.rpc.AsyncCallback; public class OverviewView extends AnimatedView { private static final String MESSAGE_ERROR_LOADING_IMAGE = "Error loading image."; private static final String MESSAGE_LOADING_IMAGE = "Loading image..."; private final class ImageListenerImpl implements ImageListener { private OverviewView view = null; public ImageListenerImpl(OverviewView view) { this.view = view; } public void onLoadingComplete(Image image) { view.image = image; view.downloadingImage = null; view.imageStatus = ImageStatus.IMAGE_STATUS_IMAGE_LOADED; view.requestRender(); } } enum ImageStatus { IMAGE_STATUS_NO_TREE, IMAGE_STATUS_IMAGE_LOADED, IMAGE_STATUS_LOADING_IMAGE, IMAGE_STATUS_ERROR } private Canvas canvas = null; private Image image = null; @SuppressWarnings("unused") private Image downloadingImage = null; private int width; private int height; private ImageStatus imageStatus = ImageStatus.IMAGE_STATUS_NO_TREE; private JsHit hit; private TreeImageAsync treeImageService = GWT.create(TreeImage.class); private TreeIntersectServiceAsync treeIntersectService = GWT.create(TreeIntersectService.class); public OverviewView(int width, int height) { this.width = width; this.height = height; canvas = new Canvas(width, height); this.add(canvas); this.addMouseMoveHandler(new MouseMoveHandler() { @Override public void onMouseMove(MouseMoveEvent arg0) { int x = arg0.getX(); int y = arg0.getY(); // Project the point in screen space to object space. Vector2 position = new Vector2((double)x / OverviewView.this.width, (double)y / OverviewView.this.height); ITree tree = getTree(); if(tree != null) { treeIntersectService.intersectTree(tree.getId(), position.getX(), position.getY(), new AsyncCallback<String>() { @Override public void onFailure(Throwable arg0) { hit = null; OverviewView.this.requestRender(); } @Override public void onSuccess(String arg0) { JsHitResult result = (JsHitResult)JsonUtils.safeEval(arg0); hit = result.getHit(); OverviewView.this.requestRender(); } }); } } }); this.addMouseDownHandler(new MouseDownHandler() { @Override public void onMouseDown(MouseDownEvent arg0) { if(hit != null) { Box2D bbox = ConvertMathTypes.convertToBox2(hit.boundingBox()); zoomToBoundingBox(bbox); } } }); } @Override public void setDocument(IDocument document) { super.setDocument(document); this.image = null; this.downloadingImage = null; this.requestRender(); } public void updateImage() { this.retrieveOverviewImage(); } private void retrieveOverviewImage() { this.downloadingImage = null; if(this.getTree() == null) { this.image = null; return; } this.imageStatus = ImageStatus.IMAGE_STATUS_LOADING_IMAGE; final AsyncCallback<String> callback = new AsyncCallback<String>() { final OverviewView caller = OverviewView.this; @Override public void onFailure(Throwable arg0) { image = null; downloadingImage = null; caller.imageStatus = ImageStatus.IMAGE_STATUS_ERROR; Logger.getLogger("").log(Level.INFO, "Failure retrieving overview image.", arg0); } @Override public void onSuccess(String result) { downloadingImage = new Image(result, new ImageListenerImpl(caller)); caller.requestRender(); } }; if(this.getTree() != null && this.getLayout() != null) { final ITree tree = this.getTree(); String layoutID = this.getLayoutType().toString(); treeImageService.getTreeImageURL(tree.getId(), layoutID, width, height, callback); } else { imageStatus = ImageStatus.IMAGE_STATUS_ERROR; } } public void render() { try { canvas.clear(); if(image != null && image.isLoaded()) { canvas.save(); canvas.scale((double)width / image.getWidth(), (double)height / image.getHeight()); canvas.drawImage(image, 0, 0); canvas.restore(); } else { switch (imageStatus) { case IMAGE_STATUS_LOADING_IMAGE: showStatusMessage(OverviewView.MESSAGE_LOADING_IMAGE); break; case IMAGE_STATUS_ERROR: showStatusMessage(OverviewView.MESSAGE_ERROR_LOADING_IMAGE); } } Camera camera = this.getCamera(); if(camera != null) { Matrix33 V = camera.getViewMatrix(); Matrix33 IV = V.inverse(); Vector2 min = new Vector2(0, 0); Vector2 max = new Vector2(1, 1); min = IV.transform(min); max = IV.transform(max); Matrix33 S = Matrix33.makeScale(canvas.getWidth(), canvas.getHeight()); min = S.transform(min); max = S.transform(max); double x = min.getX(); double y = min.getY(); double width = max.getX() - x; double height = max.getY() - y; canvas.setStrokeStyle(Defaults.OVERVIEW_OUTLINE_COLOR); canvas.setFillStyle(Defaults.OVERVIEW_FILL_COLOR); canvas.beginPath(); canvas.rect(x, y, width, height); canvas.fill(); canvas.stroke(); } if(hit != null) { canvas.setFillStyle("red"); canvas.beginPath(); double x = hit.position().getX(); double y = hit.position().getY(); canvas.arc(x * this.width, y * this.height, Defaults.POINT_SIZE / 2.0, 0, Math.PI * 2, true); canvas.closePath(); canvas.fill(); } } catch(Exception e) { Logger.getLogger("").log(Level.WARNING, "An exception was caught in OverviewView.render: " + e.getMessage()); } } private void showStatusMessage(String message) { canvas.setStrokeStyle(Defaults.TEXT_COLOR); canvas.setFillStyle(Defaults.TEXT_COLOR); canvas.fillText(message, 0, canvas.getHeight() / 2); } public void resize(int width, int height) { this.width = width; this.height = height; canvas.setWidth(width); canvas.setHeight(height); retrieveOverviewImage(); // FIXME: limit the rate of these requests. Most browsers are going to // have many resize events as the user drags the window border // around. } @Override public boolean isReady() { return true; } @Override public String exportImageURL() { return image.getElement().getSrc(); } }