/**
* Copyright 2010 Philippe Beaudoin
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.puzzlebazar.client.util;
import com.google.gwt.user.client.ui.Widget;
import com.puzzlebazar.client.ui.SquareGridLayoutPanel;
import com.puzzlebazar.shared.util.Recti;
import com.puzzlebazar.shared.util.Vec2i;
/**
* @author Philippe Beaudoin
*/
public class SquareGridConverterImpl implements SquareGridConverter {
private final SquareGridLayoutPanel gridPanel;
private final Widget uiWidget;
/**
* Contains the position of the square grid in
* the uiWidget coordinates. (Borders are already removed.)
*/
private final Recti gridInUiWidget = new Recti();
/**
* Creates a converter between a specific {@link SquareGridLayoutPanel}
* and another {@link Widget} in which all the ui events will be passed.
*
* @param gridPanel The {@link SquareGridLayoutPanel}.
* @param uiWidget The {@link Widget} that will be receiving the ui events.
*/
public SquareGridConverterImpl(SquareGridLayoutPanel gridPanel, Widget uiWidget) {
this.gridPanel = gridPanel;
this.uiWidget = uiWidget;
}
@Override
public Vec2i pixelToCell(int x, int y) {
updateGridInUiWidget();
int dx = x - gridInUiWidget.x;
int dy = y - gridInUiWidget.y;
Vec2i result = new Vec2i(
dx * gridPanel.getWidth() / gridInUiWidget.w,
dy * gridPanel.getHeight() / gridInUiWidget.h);
if (dx < 0) {
result.x = -1;
}
if (dy < 0) {
result.y = -1;
}
return result;
}
@Override
public VertexHitInfo pixelToVertex(int x, int y) {
updateGridInUiWidget();
double pixelPerCellX = gridInUiWidget.w / (double) gridPanel.getWidth();
double pixelPerCellY = gridInUiWidget.h / (double) gridPanel.getHeight();
// Move grid half a cell to the top-left
double dx = x - gridInUiWidget.x + pixelPerCellX / 2.0;
double dy = y - gridInUiWidget.y + pixelPerCellY / 2.0;
if (dx < 0 || dy < 0) {
return null;
}
Vec2i vertex = new Vec2i();
Vec2i dist = new Vec2i();
vertex.x = (int) (dx * gridPanel.getWidth() / gridInUiWidget.w);
vertex.y = (int) (dy * gridPanel.getHeight() / gridInUiWidget.h);
// The vertex position
dist.x = Math.abs((int) (dx - vertex.x * pixelPerCellX - pixelPerCellX / 2.0));
dist.y = Math.abs((int) (dy - vertex.y * pixelPerCellY - pixelPerCellY / 2.0));
return new VertexHitInfo(vertex, dist);
}
@Override
public EdgeHitInfo pixelToEdge(int x, int y) {
updateGridInUiWidget();
Vec2i cell = pixelToCell(x, y);
VertexHitInfo vertexHitInfo = pixelToVertex(x, y);
if (vertexHitInfo == null || vertexHitInfo.getVertex().x < 0 || vertexHitInfo.getVertex().y < 0) {
return null;
}
boolean vertical;
Vec2i edge = new Vec2i();
int dist;
if (vertexHitInfo.getDist().y < vertexHitInfo.getDist().x) {
// Select horizontal edge
vertical = false;
edge.x = cell.x;
edge.y = vertexHitInfo.getVertex().y;
dist = vertexHitInfo.getDist().y;
} else {
// Select vertical edge
vertical = true;
edge.x = vertexHitInfo.getVertex().x;
edge.y = cell.y;
dist = vertexHitInfo.getDist().x;
}
return new EdgeHitInfo(vertical, edge, dist);
}
/**
* Makes sure the {@link gridInWidget} rectangle is up to date.
* Must be called before any conversion operation.
* Possible optimization:
* We could make the user responsible of calling
* this method when the window is resized.
*/
private void updateGridInUiWidget() {
int border = gridPanel.getBorder();
gridInUiWidget.x = gridPanel.getAbsoluteLeft() + border - uiWidget.getAbsoluteLeft();
gridInUiWidget.y = gridPanel.getAbsoluteTop() + border - uiWidget.getAbsoluteTop();
gridInUiWidget.w = gridPanel.getOffsetWidth() - 2 * border;
gridInUiWidget.h = gridPanel.getOffsetHeight() - 2 * border;
}
}