330 lines
10 KiB
Java
330 lines
10 KiB
Java
package btools.routingapp;
|
|
|
|
import android.annotation.SuppressLint;
|
|
import android.content.Context;
|
|
import android.content.res.AssetManager;
|
|
import android.graphics.Bitmap;
|
|
import android.graphics.BitmapFactory;
|
|
import android.graphics.Canvas;
|
|
import android.graphics.Color;
|
|
import android.graphics.Matrix;
|
|
import android.graphics.Paint;
|
|
import android.util.AttributeSet;
|
|
import android.view.GestureDetector;
|
|
import android.view.MotionEvent;
|
|
import android.view.ScaleGestureDetector;
|
|
import android.view.View;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.util.ArrayList;
|
|
|
|
public class BInstallerView extends View {
|
|
public static final int MASK_SELECTED_RD5 = 1;
|
|
public static final int MASK_DELETED_RD5 = 2;
|
|
public static final int MASK_INSTALLED_RD5 = 4;
|
|
public static final int MASK_CURRENT_RD5 = 8;
|
|
private static final float SCALE_GRID_VISIBLE = 3;
|
|
private final Bitmap bmp;
|
|
private final float[] testVector = new float[2];
|
|
private final int[] tileStatus;
|
|
private final Matrix mat;
|
|
private final GestureDetector mGestureDetector;
|
|
private final ScaleGestureDetector mScaleGestureDetector;
|
|
Paint paintGrid = new Paint();
|
|
Paint paintTiles = new Paint();
|
|
private float viewscale;
|
|
private boolean tilesVisible = false;
|
|
private OnSelectListener mOnSelectListener;
|
|
|
|
public BInstallerView(Context context, AttributeSet attrs) {
|
|
super(context, attrs);
|
|
|
|
try {
|
|
AssetManager assetManager = getContext().getAssets();
|
|
InputStream istr = assetManager.open("world.png");
|
|
bmp = BitmapFactory.decodeStream(istr);
|
|
istr.close();
|
|
} catch (IOException io) {
|
|
throw new RuntimeException("cannot read world.png from assets");
|
|
}
|
|
|
|
tileStatus = new int[72 * 36];
|
|
mat = new Matrix();
|
|
mGestureDetector = new GestureDetector(context, new GestureListener());
|
|
mScaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureListener());
|
|
}
|
|
|
|
public void setOnSelectListener(OnSelectListener listener) {
|
|
mOnSelectListener = listener;
|
|
}
|
|
|
|
private void setRatio(float ratio, float focusX, float focusY) {
|
|
if (currentScale() * ratio >= 1) {
|
|
mat.postScale(ratio, ratio, focusX, focusY);
|
|
fitBounds();
|
|
boolean landscape = getWidth() > getHeight();
|
|
tilesVisible = currentScale() >= (landscape ? SCALE_GRID_VISIBLE: SCALE_GRID_VISIBLE-1);
|
|
|
|
invalidate();
|
|
}
|
|
}
|
|
|
|
private void setScale(float scale, float focusX, float focusY) {
|
|
float ratio = scale / currentScale();
|
|
setRatio(ratio, focusX, focusY);
|
|
}
|
|
|
|
public void setTileStatus(int tileIndex, int tileMask) {
|
|
if (mOnSelectListener == null) {
|
|
return;
|
|
}
|
|
tileStatus[tileIndex] |= tileMask;
|
|
mOnSelectListener.onSelect();
|
|
invalidate();
|
|
}
|
|
|
|
public void toggleTileStatus(int tileIndex, int tileMask) {
|
|
if (mOnSelectListener == null) {
|
|
return;
|
|
}
|
|
tileStatus[tileIndex] ^= tileMask;
|
|
mOnSelectListener.onSelect();
|
|
invalidate();
|
|
}
|
|
|
|
public void clearAllTilesStatus(int tileMask) {
|
|
for (int ix = 0; ix < 72; ix++) {
|
|
for (int iy = 0; iy < 36; iy++) {
|
|
int tileIndex = gridPos2Tileindex(ix, iy);
|
|
tileStatus[tileIndex] ^= tileStatus[tileIndex] & tileMask;
|
|
}
|
|
}
|
|
if (mOnSelectListener != null) {
|
|
mOnSelectListener.onSelect();
|
|
}
|
|
invalidate();
|
|
}
|
|
|
|
public ArrayList<Integer> getSelectedTiles(int tileMask) {
|
|
ArrayList<Integer> selectedTiles = new ArrayList<>();
|
|
for (int ix = 0; ix < 72; ix++) {
|
|
for (int iy = 0; iy < 36; iy++) {
|
|
int tileIndex = gridPos2Tileindex(ix, iy);
|
|
if ((tileStatus[tileIndex] & tileMask) != 0 && BInstallerSizes.getRd5Size(tileIndex) > 0) {
|
|
selectedTiles.add(tileIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
return selectedTiles;
|
|
}
|
|
|
|
private int gridPos2Tileindex(int ix, int iy) {
|
|
return (35 - iy) * 72 + (ix >= 70 ? ix - 70 : ix + 2);
|
|
}
|
|
|
|
private int tileIndex(float x, float y) {
|
|
int ix = (int) (72.f * x / bmp.getWidth());
|
|
int iy = (int) (36.f * y / bmp.getHeight());
|
|
if (ix >= 0 && ix < 72 && iy >= 0 && iy < 36) return gridPos2Tileindex(ix, iy);
|
|
return -1;
|
|
}
|
|
|
|
// get back the current image scale
|
|
private float currentScale() {
|
|
testVector[0] = 1.f;
|
|
mat.mapVectors(testVector);
|
|
return testVector[0] / viewscale;
|
|
}
|
|
|
|
@Override
|
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
|
super.onSizeChanged(w, h, oldw, oldh);
|
|
|
|
int imgwOrig = getWidth();
|
|
int imghOrig = getHeight();
|
|
|
|
float scaleX = imgwOrig / ((float) bmp.getWidth());
|
|
float scaleY = imghOrig / ((float) bmp.getHeight());
|
|
|
|
viewscale = Math.max(scaleX, scaleY);
|
|
|
|
mat.preScale(viewscale, viewscale, bmp.getWidth() /2f, 0);
|
|
setRatio(1f, bmp.getWidth() /2f, bmp.getHeight() /2f);
|
|
|
|
tilesVisible = false;
|
|
}
|
|
|
|
@Override
|
|
protected void onDraw(Canvas canvas) {
|
|
canvas.setMatrix(mat);
|
|
canvas.drawBitmap(bmp, 0, 0, null);
|
|
// draw 5*5 lattice starting at scale=3
|
|
|
|
int iw = bmp.getWidth();
|
|
int ih = bmp.getHeight();
|
|
float fw = iw / 72.f;
|
|
float fh = ih / 36.f;
|
|
|
|
if (tilesVisible) {
|
|
paintGrid.setColor(Color.GREEN);
|
|
paintGrid.setStyle(Paint.Style.STROKE);
|
|
for (int ix = 0; ix < 72; ix++) {
|
|
for (int iy = 0; iy < 36; iy++) {
|
|
int tidx = gridPos2Tileindex(ix, iy);
|
|
int tilesize = BInstallerSizes.getRd5Size(tidx);
|
|
if (tilesize > 0) {
|
|
canvas.drawRect(fw * ix, fh * iy, fw * (ix + 1), fh * (iy + 1), paintGrid);
|
|
}
|
|
}
|
|
}
|
|
|
|
int mask2 = MASK_SELECTED_RD5 | MASK_DELETED_RD5 | MASK_INSTALLED_RD5;
|
|
int mask3 = mask2 | MASK_CURRENT_RD5;
|
|
|
|
paintTiles.setStyle(Paint.Style.STROKE);
|
|
paintTiles.setColor(Color.GRAY);
|
|
paintTiles.setStrokeWidth(1);
|
|
drawSelectedTiles(canvas, paintTiles, fw, fh, MASK_INSTALLED_RD5, mask3);
|
|
paintTiles.setColor(Color.BLUE);
|
|
paintTiles.setStrokeWidth(1);
|
|
drawSelectedTiles(canvas, paintTiles, fw, fh, MASK_INSTALLED_RD5 | MASK_CURRENT_RD5, mask3);
|
|
paintTiles.setColor(Color.GREEN);
|
|
paintTiles.setStrokeWidth(2);
|
|
drawSelectedTiles(canvas, paintTiles, fw, fh, MASK_SELECTED_RD5, mask2);
|
|
paintTiles.setColor(Color.YELLOW);
|
|
paintTiles.setStrokeWidth(2);
|
|
drawSelectedTiles(canvas, paintTiles, fw, fh, MASK_SELECTED_RD5 | MASK_INSTALLED_RD5, mask2);
|
|
paintTiles.setColor(Color.RED);
|
|
paintTiles.setStrokeWidth(2);
|
|
drawSelectedTiles(canvas, paintTiles, fw, fh, MASK_DELETED_RD5 | MASK_INSTALLED_RD5, mask2);
|
|
}
|
|
}
|
|
|
|
private void drawSelectedTiles(Canvas canvas, Paint pnt, float fw, float fh, int status, int mask) {
|
|
for (int ix = 0; ix < 72; ix++)
|
|
for (int iy = 0; iy < 36; iy++) {
|
|
int tidx = gridPos2Tileindex(ix, iy);
|
|
if ((tileStatus[tidx] & mask) == status) {
|
|
int tilesize = BInstallerSizes.getRd5Size(tidx);
|
|
if (tilesize > 0) {
|
|
// draw cross
|
|
canvas.drawLine(fw * ix, fh * iy, fw * (ix + 1), fh * (iy + 1), pnt);
|
|
canvas.drawLine(fw * ix, fh * (iy + 1), fw * (ix + 1), fh * iy, pnt);
|
|
|
|
// draw frame
|
|
canvas.drawRect(fw * ix, fh * iy, fw * (ix + 1), fh * (iy + 1), pnt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void fitBounds() {
|
|
float[] srcPoints = new float[]{
|
|
0, 0,
|
|
bmp.getWidth(), bmp.getHeight()
|
|
};
|
|
float[] dstPoints = new float[srcPoints.length];
|
|
float transX = 0;
|
|
float transY = 0;
|
|
mat.mapPoints(dstPoints, srcPoints);
|
|
if (dstPoints[0] > 0) {
|
|
transX = -dstPoints[0];
|
|
} else if (dstPoints[2] < getWidth()) {
|
|
transX = getWidth() - dstPoints[2];
|
|
}
|
|
if (dstPoints[1] > 0) {
|
|
transY = -dstPoints[1];
|
|
} else if (dstPoints[3] < getHeight()) {
|
|
transY = getHeight() - dstPoints[3];
|
|
}
|
|
if (transX != 0 || transY != 0) {
|
|
mat.postTranslate(transX, transY);
|
|
}
|
|
}
|
|
|
|
@SuppressLint("ClickableViewAccessibility")
|
|
@Override
|
|
public boolean onTouchEvent(MotionEvent event) {
|
|
boolean retVal = mScaleGestureDetector.onTouchEvent(event);
|
|
retVal = mGestureDetector.onTouchEvent(event) || retVal;
|
|
return retVal || super.onTouchEvent(event);
|
|
}
|
|
|
|
interface OnSelectListener {
|
|
void onSelect();
|
|
}
|
|
|
|
class GestureListener extends GestureDetector.SimpleOnGestureListener {
|
|
|
|
@Override
|
|
public boolean onSingleTapConfirmed(MotionEvent e) {
|
|
if (tilesVisible) {
|
|
Matrix imat = new Matrix();
|
|
if (mat.invert(imat)) {
|
|
float[] touchPoint = {e.getX(), e.getY()};
|
|
imat.mapPoints(touchPoint);
|
|
|
|
int tidx = tileIndex(touchPoint[0], touchPoint[1]);
|
|
if (tidx != -1) {
|
|
if ((tileStatus[tidx] & MASK_SELECTED_RD5) != 0) {
|
|
toggleTileStatus(tidx, MASK_SELECTED_RD5);
|
|
if ((tileStatus[tidx] & MASK_INSTALLED_RD5) != 0) {
|
|
setTileStatus(tidx, MASK_DELETED_RD5);
|
|
}
|
|
} else if ((tileStatus[tidx] & MASK_DELETED_RD5) != 0) {
|
|
toggleTileStatus(tidx, MASK_DELETED_RD5);
|
|
} else {
|
|
toggleTileStatus(tidx, MASK_SELECTED_RD5);
|
|
}
|
|
}
|
|
}
|
|
invalidate();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean onDoubleTap(MotionEvent e) {
|
|
if (!tilesVisible) {
|
|
setScale(4, e.getX(), e.getY());
|
|
} else {
|
|
setScale(1, e.getX(), e.getY());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
|
mat.postTranslate(-distanceX, -distanceY);
|
|
fitBounds();
|
|
invalidate();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class ScaleGestureListener implements ScaleGestureDetector.OnScaleGestureListener {
|
|
|
|
@Override
|
|
public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
|
|
float focusX = scaleGestureDetector.getFocusX();
|
|
float focusY = scaleGestureDetector.getFocusY();
|
|
float ratio = scaleGestureDetector.getScaleFactor();
|
|
|
|
setRatio(ratio, focusX, focusY);
|
|
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
|
|
}
|
|
}
|
|
|
|
}
|