GWT Query DropEvent: can I get the drop coordinates? - gwt

I'm using GWT and GWT Query plugin for my web application. I have a draggable image and a GWT Canvas wrapped in a Droppable. My code looks like this:
final Canvas canvas = Canvas.createIfSupported();
canvas.setHeight("340px");
DroppableWidget<Canvas> test = new DroppableWidget<Canvas>(canvas);
test.addDropHandler(new DropEventHandler() {
#Override
public void onDrop(DropEvent event) {
Context2d context = canvas.getContext2d();
Image img = new Image("image");
ImageElement i = ImageElement.as(img.getElement());
//get x and y coordinates out of the drop event
context.drawImage(i, x, y);
}
});
The question is... how can I get these x and y coordinates out of the drop event so I can draw image on the canvas on the drop place?

First you should retrieve your image element via the DropEvent
ImageElement i = ImageElement.as(event.getDraggable());
After that if you want to know where the draggable is dropped (related to the droppable), you can calculate the coordinate like this :
Offest droppableOffset = $(event.getDroppable()).offset();
Offset draggableOffset = $(event.getDraggable()).offset();
int x = draggableOffset.left - droppableOffset.left;
int y = draggableOffset.top - droppableOffset.top;

Related

How to change the zoom centerpoint in an ILNumerics scene viewed with a camera

I would like to be able to zoom into an ILNumerics scene viewed by a camera (as in scene.Camera) with the center point of the zoom determined by where the mouse pointer is located when I start spinning the mouse scroll wheel. The default zoom behavior is for the zoom center to be at the scene.Camera.LookAt point. So I guess this would require the mouse to be tracked in (X,Y) continuously and for that point to be used as the new LookAt point? This seems to be like this post on getting the 3D coordinates from a mouse click, but in my case there's no click to indicate the location of the mouse.
Tips would be greatly appreciated!
BTW, this kind of zoom method is standard operating procedure in CAD software to zoom in and out on an assembly of parts. It's super convenient for the user.
One approach is to overload the MouseWheel event handler. The current coordinates of the mouse are available here, too.
Use the mouse screen coordinates to acquire (to "pick") the world
coordinate corresponding to the primitive under the mouse.
Adjust the Camera.Position and Camera.ZoomFactor to 'move' the camera closer to the point under the mouse and to achieve the required 'directional zoom' effect.
Here is a complete example from the ILNumerics website:
using System;
using System.Windows.Forms;
using ILNumerics;
using ILNumerics.Drawing;
using ILNumerics.Drawing.Plotting;
using static ILNumerics.Globals;
using static ILNumerics.ILMath;
namespace ILNumerics.Examples.DirectionalZoom {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private void panel2_Load(object sender, EventArgs e) {
Array<float> X = 0, Y = 0, Z = CreateData(X, Y);
var surface = new Surface(Z, X, Y, colormap: Colormaps.Winter);
surface.UseLighting = true;
surface.Wireframe.Visible = false;
panel2.Scene.Camera.Add(surface);
// setup mouse handlers
panel2.Scene.Camera.Projection = Projection.Orthographic;
panel2.Scene.Camera.MouseDoubleClick += Camera_MouseDoubleClick;
panel2.Scene.Camera.MouseWheel += Camera_MouseWheel;
// initial zoom all
ShowAll(panel2.Scene.Camera);
}
private void Camera_MouseWheel(object sender, Drawing.MouseEventArgs e) {
// Update: added comments.
// the next conditionals help to sort out some calls not needed. Helpful for performance.
if (!e.DirectionUp) return;
if (!(e.Target is Triangles)) return;
// make sure to start with the SceneSyncRoot - the copy of the scene which receives
// user interaction and is eventually used for rendering. See: https://ilnumerics.net/scene-management.html
var cam = panel2.SceneSyncRoot.First<Camera>();
if (Equals(cam, null)) return; // TODO: error handling. (Should not happen in regular setup, though.)
// in case the user has configured limited interaction
if (!cam.AllowZoom) return;
if (!cam.AllowPan) return; // this kind of directional zoom "comprises" a pan operation, to some extent.
// find mouse coordinates. Works only if mouse is over a Triangles shape (surfaces, but not wireframes):
using (var pick = panel2.PickPrimitiveAt(e.Target as Drawable, e.Location)) {
if (pick.NextVertex.IsEmpty) return;
// acquire the target vertex coordinates (world coordinates) of the mouse
Array<float> vert = pick.VerticesWorld[pick.NextVertex[0], r(0, 2), 0];
// and transform them into a Vector3 for easier computations
var vertVec = new Vector3(vert.GetValue(0), vert.GetValue(1), vert.GetValue(2));
// perform zoom: we move the camera closer to the target
float scale = Math.Sign(e.Delta) * (e.ShiftPressed ? 0.01f : 0.2f); // adjust for faster / slower zoom
var offs = (cam.Position - vertVec) * scale; // direction on the line cam.Position -> target vertex
cam.Position += offs; // move the camera on that line
cam.LookAt += offs; // keep the camera orientation
cam.ZoomFactor *= (1 + scale);
// TODO: consider adding: the lookat point now moved away from the center / the surface due to our zoom.
// In order for better rotations it makes sense to place the lookat point back to the surface,
// by adjusting cam.LookAt appropriately. Otherwise, one could use cam.RotationCenter.
e.Cancel = true; // don't execute common mouse wheel handlers
e.Refresh = true; // immediate redraw at the end of event handling
}
}
private void Camera_MouseDoubleClick(object sender, Drawing.MouseEventArgs e) {
var cam = panel2.Scene.Camera;
ShowAll(cam);
e.Cancel = true;
e.Refresh = true;
}
// Some sample data. Replace this with your own data!
private static RetArray<float> CreateData(OutArray<float> Xout, OutArray<float> Yout) {
using (Scope.Enter()) {
Array<float> x_ = linspace<float>(0, 20, 100);
Array<float> y_ = linspace<float>(0, 18, 80);
Array<float> Y = 1, X = meshgrid(x_, y_, Y);
Array<float> Z = abs(sin(sin(X) + cos(Y))) + .01f * abs(sin(X * Y));
if (!isnull(Xout)) {
Xout.a = X;
}
if (!isnull(Yout)) {
Yout.a = Y;
}
return -Z;
}
}
// See: https://ilnumerics.net/examples.php?exid=7b0b4173d8f0125186aaa19ee8e09d2d
public static double ShowAll(Camera cam) {
// Update: adjusts the camera Position too.
// this example works only with orthographic projection. You will need to take the view frustum
// into account, if you want to make this method work with perspective projection also. however,
// the general functioning would be similar....
if (cam.Projection != Projection.Orthographic) {
throw new NotImplementedException();
}
// get the overall extend of the cameras scene content
var limits = cam.GetLimits();
// take the maximum of width/ height
var maxExt = limits.HeightF > limits.WidthF ? limits.HeightF : limits.WidthF;
// make sure the camera looks at the unrotated bounding box
cam.Reset();
// center the camera view
cam.LookAt = limits.CenterF;
cam.Position = cam.LookAt + Vector3.UnitZ * 10;
// apply the zoom factor: the zoom factor will scale the 'left', 'top', 'bottom', 'right' limits
// of the view. In order to fit exactly, we must take the "radius"
cam.ZoomFactor = maxExt * .50;
return cam.ZoomFactor;
}
}
}
Note, that the new handler performs the directional zoom only when the mouse is located over an object hold by this Camera! If, instead, the mouse is placed on the background of the scene or over some other Camera / plot cube object no effect will be visible and the common zoom feature is performed (zooming in/out to the look-at point).

WPF Cropping a portion of an image

I have created a WPF application where I need to allow a user to draw a rectangle on an existing loaded image(tif image) and have it save the coordinates/the portion of the rectangle as a separate image.
I am using the Leadtools.Windows.Controls reference and using the RasterImageViewer
Below is the code for the event handler when the user has completed drawing the rectangle.
private void ImageViewer_InteractiveUserRectangle(object sender, RectangleInteractiveEventArgs e)
{
if (e.Status == InteractiveModeStatus.End)
{
var img = ImageViewer.Image;
var top =Convert.ToInt32(e.Bounds.Top);
var left = Convert.ToInt32(e.Bounds.Left);
var width = Convert.ToInt32(e.Bounds.Width);
var height = Convert.ToInt32(e.Bounds.Height);
var rect = new Leadtools.LeadRect(left, top, width, height);
var cmd = new Leadtools.ImageProcessing.CropCommand(rect);
cmd.Run(img);
_codecs.Save(img, #"c:\temp\test.tif",
RasterImageFormat.CcittGroup4, 1, 1, 1, -1, CodecsSavePageMode.Append);
}
}
I am getting a separate cropped image, but it does not match the area drawn with the rectangle. I have tried various methods from the examples but they were all for Windows Forms applications and not WPF. Any help with what I am missing would be greatly appreciated.
The issue is that the ImageViewer UserRectangle bounds returns the coordinates in Control coordiates and you need to convert these to Image Coordinates which the Crop Command is looking for.
According to the Documentation here:
https://www.leadtools.com/help/leadtools/v19/dh/wl/rectangleinteractiveeventargs-bounds.html
The coordinates are always in control (display) to image coordinates.
You can use PointToImageCoordinates and BoundsToImageCoordinates to
map a value in control or display coordinates (what is on screen) to
image coordinates (actual x and y location in the image pixels). You
can use PointFromImageCoordinates and BoundsFromImageCoordinates to
map a value in image coordinates (actual x and y location in t he
image pixels) to control or display coordinates (what is on screen).
Here is the updated code to make it work for you project:
if (e.Status == Leadtools.Windows.Controls.InteractiveModeStatus.End)
{
var img = imageViewer.Image;
var imgRect = imageViewer.BoundsToImageCoordinates(e.Bounds);
var top = Convert.ToInt32(imgRect.Top);
var left = Convert.ToInt32(imgRect.Left);
var width = Convert.ToInt32(imgRect.Width);
var height = Convert.ToInt32(imgRect.Height);
var rect = new Leadtools.LeadRect(left, top, width, height);
var cmd = new Leadtools.ImageProcessing.CropCommand(rect);
cmd.Run(img);
_codecs.Save(img, #"c:\temp\test.tif",
RasterImageFormat.CcittGroup4, 1, 1, 1, -1, CodecsSavePageMode.Append);
}

How do I convert a RectTransform (UI Panel) to screen coordinates?

I want to determine the screen coordinates of a RectTransform in Unity3D. How is this done, taking into consideration that the element may be anchored, or even scaled?
I am trying to use RectTransform.GetWorldCorners and then converting each Vector3 to screen coordinates, but the values are wrong.
public Rect GetScreenCoordinates(RectTransform uiElement)
{
var worldCorners = new Vector3[4];
uiElement.GetWorldCorners(worldCorners);
var result = new Rect(
worldCorners[0].x,
worldCorners[0].y,
worldCorners[2].x - worldCorners[0].x,
worldCorners[2].y - worldCorners[0].y);
for (int index = 0; index < 4; index++)
result[index] = Camera.main.WorldToScreenPoint(result[index]);
return result;
}
Although the help says that it returns coordinates in world space, they are actually in screen space (at least when the UI is not in world space). So the solution is simple:
public Rect GetScreenCoordinates(RectTransform uiElement)
{
var worldCorners = new Vector3[4];
uiElement.GetWorldCorners(worldCorners);
var result = new Rect(
worldCorners[0].x,
worldCorners[0].y,
worldCorners[2].x - worldCorners[0].x,
worldCorners[2].y - worldCorners[0].y);
return result;
}

Resizeable quadratic grid

I would like to display a quadratic GridPane inside a window. The window can have every possible size and normally width and height are not equal. Anyway I would like to display the GridPane as square centered like this:
respectively
Ideally there is a configurable padding around the square. The pictures are taken from a canvas approach, but I want to switch to standard controls. Can anyone give me some hints how to achieve this?
One way to achieve an squared layout for your grid for any resolution of your window is rescaling it to use the maximum size of the window (after some padding), while keeping the squared size.
Instead of a GridPane, a simple Group is more flexible for moving its children, though it requires manual layouting of them.
This simple snippet is based on the JavaFX implementation of the 2048 game you can find here. It uses styleable rectangles to create a grid, and over them the 'tiles' are added. To find out more about styling, layouting or tile movement, go to the refered link.
private static final int NUM_CELLS = 15;
private static final int CELL_SIZE = 50;
private static final int TILE_SIZE = CELL_SIZE-15;
private final static int MARGIN = 20;
#Override
public void start(Stage primaryStage) {
// create background square grid
Group gridGroup = new Group();
IntStream.range(0,NUM_CELLS).boxed().forEach(i->
IntStream.range(0,NUM_CELLS).boxed().forEach(j->{
Rectangle cell = new Rectangle(i * CELL_SIZE, j * CELL_SIZE, CELL_SIZE, CELL_SIZE);
cell.setFill(Color.WHEAT);
cell.setStroke(Color.GREY);
gridGroup.getChildren().add(cell);
})
);
// Add grid to board
Group board = new Group(gridGroup);
// add random tiles to board
IntStream.range(0,NUM_CELLS).boxed().forEach(i->
IntStream.range(0,NUM_CELLS).boxed().forEach(j->{
if(i==j || NUM_CELLS-i-1==j){
Label label = new Label();
label.setMinSize(TILE_SIZE, TILE_SIZE);
label.setPrefSize(TILE_SIZE, TILE_SIZE);
label.setMaxSize(TILE_SIZE, TILE_SIZE);
label.setStyle("-fx-background-color: red; -fx-background-radius: 50");
label.setLayoutX((i+0.5)*CELL_SIZE-TILE_SIZE/2);
label.setLayoutY((j+0.5)*CELL_SIZE-TILE_SIZE/2);
board.getChildren().add(label);
}
})
);
Bounds gameBounds = board.getLayoutBounds();
StackPane root = new StackPane(board);
// Listener to rescale and center the board
ChangeListener<Number> resize = (ov, v, v1) -> {
double scale = Math.min((root.getWidth() - MARGIN) / gameBounds.getWidth(),
(root.getHeight() - MARGIN) / gameBounds.getHeight());
board.setScaleX(scale);
board.setScaleY(scale);
board.setLayoutX((root.getWidth() - gameBounds.getWidth()) / 2d);
board.setLayoutY((root.getHeight() - gameBounds.getHeight()) / 2d);
};
root.widthProperty().addListener(resize);
root.heightProperty().addListener(resize);
Scene scene = new Scene(root);
// Maximum size of window
Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
double factor = Math.min(visualBounds.getWidth() / (gameBounds.getWidth() + MARGIN),
visualBounds.getHeight() / (gameBounds.getHeight() + MARGIN));
primaryStage.setScene(scene);
primaryStage.setWidth((gameBounds.getWidth() + MARGIN) * factor);
primaryStage.setHeight((gameBounds.getHeight() + MARGIN) * factor);
primaryStage.show();
}
And this is how it will look like after some window resizing:

How to give hyperllink in an image using Titanium

I am looking to give a hyperlink on particular point in an image using Titanium, like Facebook does in showing images on focus of faces it is showing names.
So is there any possibility i can do this on titanium. If possible please provide some sample code of it.
Just use the click event for a view, then detect if its inside a certain area, I use a circular area as an example since its the most straightforward to code, you can use this as a guide for rectangular areas
var clickPoint = {x : 100, y : 100};
var clickRadiusSquared = 25;
// View user clicks on
var view = Ti.UI.createView({
width : 200,
height : 200,
});
view.addEventListener('click', function(e) {
// Get the X and Y coordinates of the click inside the view
var x = e.x;
var y = e.y;
// Now see if it is inside the area
var distanceSquared = Math.pow(clickPoint.x - x, 2) + Math.pow(clickPoint.y - y, 2);
if(distanceSquared < clickRadiusSquared) {
// Open the link or do whatever
Titanium.Platform.openURL('http://www.yoururl.com');
}
});