labeling testing

This commit is contained in:
Hannes Janetzek 2013-02-18 09:48:32 +01:00
parent d25d967332
commit 34bffcc15f
16 changed files with 628 additions and 348 deletions

View File

@ -35,6 +35,7 @@ import org.oscim.theme.renderinstruction.Area;
import org.oscim.theme.renderinstruction.Line; import org.oscim.theme.renderinstruction.Line;
import org.oscim.theme.renderinstruction.RenderInstruction; import org.oscim.theme.renderinstruction.RenderInstruction;
import org.oscim.theme.renderinstruction.Text; import org.oscim.theme.renderinstruction.Text;
import org.oscim.utils.LineClipper;
import org.oscim.view.DebugSettings; import org.oscim.view.DebugSettings;
import org.oscim.view.MapView; import org.oscim.view.MapView;
@ -114,6 +115,8 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
private boolean mDebugDrawPolygons; private boolean mDebugDrawPolygons;
boolean mDebugDrawUnmatched; boolean mDebugDrawUnmatched;
private final LineClipper mClipper;
public static void setRenderTheme(RenderTheme theme) { public static void setRenderTheme(RenderTheme theme) {
renderTheme = theme; renderTheme = theme;
renderLevels = theme.getLevels(); renderLevels = theme.getLevels();
@ -129,6 +132,7 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
*/ */
public TileGenerator(MapView mapView) { public TileGenerator(MapView mapView) {
// mMapView = mapView; // mMapView = mapView;
mClipper = new LineClipper(0,0,Tile.TILE_SIZE, Tile.TILE_SIZE, true);
} }
public void cleanup() { public void cleanup() {
@ -172,10 +176,10 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
} }
if (debug.drawTileFrames) { if (debug.drawTileFrames) {
//mTagName = new Tag("name", tile.toString(), false); mTagName = new Tag("name", tile.toString(), false);
//mPoiX = Tile.TILE_SIZE >> 1; mPoiX = Tile.TILE_SIZE >> 1;
//mPoiY = 10; mPoiY = 10;
//TileGenerator.renderTheme.matchNode(this, debugTagWay, (byte) 0); TileGenerator.renderTheme.matchNode(this, debugTagWay, (byte) 0);
mIndices = debugBoxIndex; mIndices = debugBoxIndex;
if (MapView.enableClosePolygons) if (MapView.enableClosePolygons)
@ -476,7 +480,7 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
int length = mIndices[i]; int length = mIndices[i];
if (length < 4) if (length < 4)
break; break;
mLabels = WayDecorator.renderText(mCoords, mTagName.value, text, mLabels = WayDecorator.renderText(mClipper,mCoords, mTagName.value, text,
offset, length, mLabels); offset, length, mLabels);
offset += length; offset += length;
} }

View File

@ -18,99 +18,155 @@ package org.oscim.generator;
import org.oscim.core.Tile; import org.oscim.core.Tile;
import org.oscim.renderer.layer.TextItem; import org.oscim.renderer.layer.TextItem;
import org.oscim.theme.renderinstruction.Text; import org.oscim.theme.renderinstruction.Text;
import org.oscim.utils.GeometryUtils;
import org.oscim.utils.LineClipper;
public final class WayDecorator { public final class WayDecorator {
public static TextItem renderText(float[] coordinates, String string, Text text, public static TextItem renderText(LineClipper clipper, float[] coordinates, String string,
Text text,
int pos, int len, TextItem textItems) { int pos, int len, TextItem textItems) {
TextItem items = textItems; TextItem items = textItems;
TextItem t = null; TextItem t = null;
// calculate the way name length plus some margin of safety // calculate the way name length plus some margin of safety
float wayNameWidth = -1; float wayNameWidth = -1;
float minWidth = Tile.TILE_SIZE / 10; float minWidth = Tile.TILE_SIZE / 10;
//int skipPixels = 0;
// get the first way point coordinates final int min = 0;
int prevX = (int) coordinates[pos + 0]; final int max = Tile.TILE_SIZE;
int prevY = (int) coordinates[pos + 1];
// find way segments long enough to draw the way name on them // find way segments long enough to draw the way name on them
for (int i = pos + 2; i < pos + len; i += 2) { for (int i = pos; i < pos + len - 2; i += 2) {
// get the first way point coordinates
int prevX = (int) coordinates[i + 0];
int prevY = (int) coordinates[i + 1];
byte edge = 0;
clipper.clipStart(prevX, prevY);
// get the current way point coordinates // get the current way point coordinates
int curX = (int) coordinates[i]; int curX = (int) coordinates[i + 2];
int curY = (int) coordinates[i + 1]; int curY = (int) coordinates[i + 3];
int clip;
if ((clip = clipper.clipNext(curX, curY)) != 0) {
if (clip < 0) {
prevX = clipper.out[0];
prevY = clipper.out[1];
curX = clipper.out[2];
curY = clipper.out[3];
if (prevX == min)
edge |= 1 << 0;
else if (prevX == max)
edge |= 1 << 1;
if (prevY == min)
edge |= 1 << 2;
else if (prevY == max)
edge |= 1 << 3;
if (curX == min)
edge |= 1 << 4;
else if (curX == max)
edge |= 1 << 5;
if (curY == min)
edge |= 1 << 5;
else if (curY == max)
edge |= 1 << 6;
}
}
int last = i;
// calculate the length of the current segment (Euclidian distance) // calculate the length of the current segment (Euclidian distance)
float vx = prevX - curX; float vx = prevX - curX;
float vy = prevY - curY; float vy = prevY - curY;
if (vx == 0 && vy == 0)
continue;
float a = (float) Math.sqrt(vx * vx + vy * vy); float a = (float) Math.sqrt(vx * vx + vy * vy);
// only if not cur segment crosses edge
if (edge < (1 << 4)) {
vx /= a; vx /= a;
vy /= a; vy /= a;
int last = i;
int nextX = 0, nextY = 0;
// add additional segments if possible // add additional segments if possible
for (int j = last + 2; j < pos + len; j += 2) { for (int j = i + 4; j < pos + len; j += 2) {
nextX = (int) coordinates[j]; int nextX = (int) coordinates[j + 0];
nextY = (int) coordinates[j + 1]; int nextY = (int) coordinates[j + 1];
float wx = curX - nextX; if ((clip = clipper.clipNext(nextX, nextY)) != 0) {
float wy = curY - nextY; if (clip < 0) {
curX = clipper.out[0];
curY = clipper.out[1];
// TODO break when cur has changed
nextX = clipper.out[2];
nextY = clipper.out[3];
}
}
float wx = nextX - curX;
float wy = nextY - curY;
if (wx == 0 && wy == 0)
continue;
float area = GeometryUtils.area(prevX, prevY, curX, curY, nextX, nextY);
if (area > 1000) {
//Log.d(">>>", "b: " + string + " " + area );
break;
}
a = (float) Math.sqrt(wx * wx + wy * wy); a = (float) Math.sqrt(wx * wx + wy * wy);
wx /= a; wx /= a;
wy /= a; wy /= a;
// avoid adding short segments that add much area
if (area / 2 > a * a) {
//Log.d(">>>", "a: " +string + " " + area + " " + a*a);
break;
}
float ux = vx + wx; float ux = vx + wx;
float uy = vy + wy; float uy = vy + wy;
float diff = wx * uy - wy * ux; float diff = wx * uy - wy * ux;
if (diff > 0.1 || diff < -0.1) // maximum angle between segments
if (diff > 0.1 || diff < -0.1) {
//Log.d(">>>", "c: " + string + " " + area );
break; break;
}
last = j;
curX = nextX; curX = nextX;
curY = nextY; curY = nextY;
continue; last = j - 2;
if (clip < 0) {
if (nextX == min)
edge |= 1 << 4;
else if (nextX == max)
edge |= 1 << 5;
if (nextY == min)
edge |= 1 << 6;
else if (nextY == max)
edge |= 1 << 7;
}
} }
vx = curX - prevX; vx = curX - prevX;
vy = curY - prevY; vy = curY - prevY;
a = (float) Math.sqrt(vx * vx + vy * vy);
if (vx < 0)
vx = -vx;
if (vy < 0)
vy = -vy;
// minimum segment to label
if (vx + vy < minWidth) {
// restart from next node
prevX = (int) coordinates[i];
prevY = (int) coordinates[i + 1];
continue;
} }
// compare against max segment length float segmentLength = a;
if (wayNameWidth > 0 && vx + vy < wayNameWidth) {
// restart from next node
prevX = (int) coordinates[i];
prevY = (int) coordinates[i + 1];
continue;
}
float segmentLength = (float) Math.sqrt(vx * vx + vy * vy);
//if (skipPixels > 0) {
// skipPixels -= segmentLength;
//
//} else
if (edge == 0) {
if (segmentLength < minWidth) { if (segmentLength < minWidth) {
// restart from next node
prevX = (int) coordinates[i];
prevY = (int) coordinates[i + 1];
continue; continue;
} }
@ -119,22 +175,13 @@ public final class WayDecorator {
} }
if (segmentLength < wayNameWidth * 0.50) { if (segmentLength < wayNameWidth * 0.50) {
// restart from next node
prevX = (int) coordinates[i];
prevY = (int) coordinates[i + 1];
continue; continue;
} }
} else if (wayNameWidth < 0) {
//float s = (wayNameWidth + 20) / segmentLength; wayNameWidth = text.paint.measureText(string);
//float s; }
//if (wayNameWidth < segmentLength)
// s = (segmentLength - 10) / segmentLength;
//else
//s = (wayNameWidth + 20) / segmentLength;
//float width, height;
float x1, y1, x2, y2; float x1, y1, x2, y2;
if (prevX < curX) { if (prevX < curX) {
x1 = prevX; x1 = prevX;
y1 = prevY; y1 = prevY;
@ -147,17 +194,6 @@ public final class WayDecorator {
y2 = prevY; y2 = prevY;
} }
//// estimate position of text on path
//width = (x2 - x1) / 2f;
////width += 4 * (width / wayNameWidth);
//x2 = x2 - (width - s * width);
//x1 = x1 + (width - s * width);
//
//height = (y2 - y1) / 2f;
////height += 4 * (height / wayNameWidth);
//y2 = y2 - (height - s * height);
//y1 = y1 + (height - s * height);
TextItem n = TextItem.get(); TextItem n = TextItem.get();
// link items together // link items together
@ -177,15 +213,11 @@ public final class WayDecorator {
t.x2 = x2; t.x2 = x2;
t.y2 = y2; t.y2 = y2;
t.length = (short) segmentLength; t.length = (short) segmentLength;
t.edges = edge;
t.next = items; t.next = items;
items = t; items = t;
// skip to last
i = last; i = last;
// store the previous way point coordinates
prevX = curX;
prevY = curY;
} }
return items; return items;
} }

View File

@ -544,7 +544,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
td.cnt = 0; td.cnt = 0;
for (int i = 0; i < cnt; i++) { for (int i = 0; i < cnt; i++) {
MapTile t = newTiles[i]; MapTile t = newTiles[i];
if (t.isVisible) { if (t.isVisible && t.state == STATE_READY) {
t.lock(); t.lock();
td.tiles[td.cnt++] = t; td.tiles[td.cnt++] = t;
} }

View File

@ -91,6 +91,8 @@ public class TextureObject {
* @param to the TextureObjet to compile and upload * @param to the TextureObjet to compile and upload
*/ */
public static synchronized void uploadTexture(TextureObject to) { public static synchronized void uploadTexture(TextureObject to) {
// FIXME what needs synchronized ?
if (TextureRenderer.debug) if (TextureRenderer.debug)
Log.d(TAG, "upload texture " + to.id); Log.d(TAG, "upload texture " + to.id);

View File

@ -158,11 +158,12 @@ public final class TextureRenderer {
+ "const float coord_scale = 0.125;" + "const float coord_scale = 0.125;"
+ "void main() {" + "void main() {"
+ " vec4 pos;" + " vec4 pos;"
+ " vec2 dir = vertex.zw;"
+ " if (mod(vertex.x, 2.0) == 0.0){" + " if (mod(vertex.x, 2.0) == 0.0){"
+ " pos = u_proj * (u_mv * vec4(vertex.xy + vertex.zw * u_scale, 0.0, 1.0));" + " pos = u_proj * (u_mv * vec4(vertex.xy + dir * u_scale, 0.0, 1.0));"
+ " } else {" // place as billboard + " } else {" // place as billboard
+ " vec4 dir = u_mv * vec4(vertex.xy, 0.0, 1.0);" + " vec4 center = u_mv * vec4(vertex.xy, 0.0, 1.0);"
+ " pos = u_proj * (dir + vec4(vertex.zw * (coord_scale * u_swidth), 0.1, 0.0));" + " pos = u_proj * (center + vec4(dir * (coord_scale * u_swidth), 0.1, 0.0));"
+ " }" + " }"
+ " gl_Position = pos;" + " gl_Position = pos;"
+ " tex_c = tex_coord * div;" + " tex_c = tex_coord * div;"

View File

@ -290,7 +290,7 @@ public class ExtrusionLayer extends Layer {
} }
/* check if face is within tile */ /* check if face is within tile */
if (!mClipper.clipNext((int) nx, (int) ny)) { if (mClipper.clipNext((int) nx, (int) ny) == 0) {
even = (even == 0 ? 1 : 0); even = (even == 0 ? 1 : 0);
continue; continue;
} }

View File

@ -15,7 +15,6 @@
package org.oscim.renderer.layer; package org.oscim.renderer.layer;
import org.oscim.theme.renderinstruction.Text; import org.oscim.theme.renderinstruction.Text;
import org.oscim.utils.OBB2D;
import android.util.Log; import android.util.Log;
@ -40,7 +39,33 @@ public class TextItem {
pool = pool.next; pool = pool.next;
ti.next = null; ti.next = null;
ti.active = 0; //ti.active = 0;
return ti;
}
}
public static TextItem copy(TextItem orig) {
synchronized (lock) {
TextItem ti = pool;
if (ti == null) {
count++;
ti = new TextItem();
} else {
inPool--;
pool = pool.next;
}
ti.next = null;
ti.x = orig.x;
ti.y = orig.y;
ti.x1 = orig.x1;
ti.y1 = orig.y1;
ti.x2 = orig.x2;
ti.y2 = orig.y2;
return ti; return ti;
} }
} }
@ -115,28 +140,24 @@ public class TextItem {
public TextItem move(TextItem ti, float dx, float dy) { public TextItem move(TextItem ti, float dx, float dy) {
this.x = dx + ti.x; this.x = dx + ti.x;
this.y = dy + ti.y; this.y = dy + ti.y;
this.string = ti.string;
this.text = ti.text;
this.width = ti.width;
this.length = ti.length;
return this; return this;
} }
/* copy properties from 'ti' and add offset
*
* */
public TextItem move(TextItem ti, float dx, float dy, float scale) { public TextItem move(TextItem ti, float dx, float dy, float scale) {
this.x = dx + (ti.x * scale); this.x = (dx + ti.x) * scale;
this.y = dy + (ti.y * scale); this.y = (dy + ti.y) * scale;
return this;
}
public void clone(TextItem ti){
this.string = ti.string; this.string = ti.string;
this.text = ti.text; this.text = ti.text;
this.width = ti.width; this.width = ti.width;
this.length = ti.length; this.length = ti.length;
return this;
} }
public void setAxisAlignedBBox(){ public void setAxisAlignedBBox() {
this.x1 = x - width / 2; this.x1 = x - width / 2;
this.y1 = y - text.fontHeight / 2; this.y1 = y - text.fontHeight / 2;
this.x2 = x + width / 2; this.x2 = x + width / 2;
@ -197,8 +218,10 @@ public class TextItem {
public TextItem n1; public TextItem n1;
public TextItem n2; public TextItem n2;
public byte origin; public byte edges;
public int active; @Override
public OBB2D bbox; public String toString() {
return x + " " + y + " " + string;
}
} }

View File

@ -34,7 +34,6 @@ public final class TextLayer extends TextureLayer {
public TextItem labels; public TextItem labels;
private final Canvas mCanvas; private final Canvas mCanvas;
private float mScale;
public TextItem getLabels() { public TextItem getLabels() {
return labels; return labels;
@ -44,11 +43,6 @@ public final class TextLayer extends TextureLayer {
type = Layer.SYMBOL; type = Layer.SYMBOL;
mCanvas = new Canvas(); mCanvas = new Canvas();
fixed = true; fixed = true;
mScale = 1;
}
public void setScale(float scale) {
mScale = scale;
} }
public boolean removeText(TextItem item) { public boolean removeText(TextItem item) {
@ -132,7 +126,7 @@ public final class TextLayer extends TextureLayer {
for (TextItem it = labels; it != null;) { for (TextItem it = labels; it != null;) {
float width = it.width + 2 * mFontPadX; float width = it.width + 2 * mFontPadX;
float height = (int) (it.text.fontHeight) + 2 * mFontPadY + 0.5f; float height = (int) (it.text.fontHeight) + 0.5f;
if (height > advanceY) if (height > advanceY)
advanceY = (int) height; advanceY = (int) height;
@ -158,7 +152,8 @@ public final class TextLayer extends TextureLayer {
} }
} }
yy = y + (height - 1) - it.text.fontDescent - mFontPadY; //yy = y + (height - 1) - it.text.fontDescent - mFontPadY;
yy = y + height - it.text.fontDescent; // - mFontPadY;
if (it.text.stroke != null) if (it.text.stroke != null)
mCanvas.drawText(it.string, x + it.width / 2, yy, it.text.stroke); mCanvas.drawText(it.string, x + it.width / 2, yy, it.text.stroke);
@ -174,11 +169,10 @@ public final class TextLayer extends TextureLayer {
float hh2 = 0; float hh2 = 0;
if (!it.text.caption) { if (!it.text.caption) {
hw /= mScale; // displace by baseline
hh2 = hh + it.text.fontDescent / 2; float desc = it.text.fontDescent / 2;
hh -= it.text.fontDescent / 2; hh2 = hh + desc;
hh /= mScale; hh = hh - desc;
hh2 /= mScale;
} }
// texture coordinates // texture coordinates
@ -192,17 +186,17 @@ public final class TextLayer extends TextureLayer {
short x1, x2, x3, x4, y1, y3, y2, y4; short x1, x2, x3, x4, y1, y3, y2, y4;
if (it.text.caption) { if (it.text.caption) {
if (it.origin == 0) { //if (it.origin == 0) {
x1 = x3 = (short) (SCALE * -hw); x1 = x3 = (short) (SCALE * -hw);
x2 = x4 = (short) (SCALE * hw); x2 = x4 = (short) (SCALE * hw);
y1 = y2 = (short) (SCALE * hh); y1 = y2 = (short) (SCALE * hh);
y3 = y4 = (short) (SCALE * -hh); y3 = y4 = (short) (SCALE * -hh);
} else { //} else {
x1 = x3 = (short) (SCALE * 0); // x1 = x3 = (short) (SCALE * 0);
x2 = x4 = (short) (SCALE * width); // x2 = x4 = (short) (SCALE * width);
y1 = y2 = (short) (SCALE * 0); // y1 = y2 = (short) (SCALE * 0);
y3 = y4 = (short) (SCALE * -height); // y3 = y4 = (short) (SCALE * -height);
} //}
} else { } else {
float vx = it.x1 - it.x2; float vx = it.x1 - it.x2;
float vy = it.y1 - it.y2; float vy = it.y1 - it.y2;
@ -219,12 +213,16 @@ public final class TextLayer extends TextureLayer {
vx *= hw; vx *= hw;
vy *= hw; vy *= hw;
// top-left
x1 = (short) (SCALE * (vx - ux)); x1 = (short) (SCALE * (vx - ux));
y1 = (short) (SCALE * (vy - uy)); y1 = (short) (SCALE * (vy - uy));
// top-right
x2 = (short) (SCALE * (-vx - ux)); x2 = (short) (SCALE * (-vx - ux));
y2 = (short) (SCALE * (-vy - uy)); y2 = (short) (SCALE * (-vy - uy));
// bot-right
x4 = (short) (SCALE * (-vx + ux2)); x4 = (short) (SCALE * (-vx + ux2));
y4 = (short) (SCALE * (-vy + uy2)); y4 = (short) (SCALE * (-vy + uy2));
// bot-left
x3 = (short) (SCALE * (vx + ux2)); x3 = (short) (SCALE * (vx + ux2));
y3 = (short) (SCALE * (vy + uy2)); y3 = (short) (SCALE * (vy + uy2));
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012 Hannes Janetzek * Copyright 2012, 2013 OpenScienceMap
* *
* This program is free software: you can redistribute it and/or modify it under the * This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software * terms of the GNU Lesser General Public License as published by the Free Software
@ -17,12 +17,15 @@ package org.oscim.renderer.layer;
import java.nio.ShortBuffer; import java.nio.ShortBuffer;
import org.oscim.renderer.TextureObject; import org.oscim.renderer.TextureObject;
import org.oscim.renderer.TextureRenderer;
import android.util.Log;
/**
* @author Hannes Janetzek
*/
public abstract class TextureLayer extends Layer { public abstract class TextureLayer extends Layer {
// holds textures and offset in vbo
public TextureObject textures; public TextureObject textures;
// scale mode
public boolean fixed; public boolean fixed;
/** /**
@ -30,12 +33,11 @@ public abstract class TextureLayer extends Layer {
* buffer to add vertices * buffer to add vertices
*/ */
void compile(ShortBuffer sbuf) { void compile(ShortBuffer sbuf) {
if (TextureRenderer.debug)
Log.d("...", "compile");
for (TextureObject to = textures; to != null; to = to.next) for (TextureObject to = textures; to != null; to = to.next)
TextureObject.uploadTexture(to); TextureObject.uploadTexture(to);
// add vertices to vbo
Layers.addPoolItems(this, sbuf); Layers.addPoolItems(this, sbuf);
} }

View File

@ -363,7 +363,6 @@ public class TextOverlay extends BasicOverlay {
} }
// draw text to bitmaps and create vertices // draw text to bitmaps and create vertices
tl.setScale(scale);
tl.prepare(); tl.prepare();
// everything synchronized? // everything synchronized?

View File

@ -30,7 +30,6 @@ import java.util.HashMap;
import org.oscim.core.MapPosition; import org.oscim.core.MapPosition;
import org.oscim.core.Tile; import org.oscim.core.Tile;
import org.oscim.generator.JobTile;
import org.oscim.renderer.BufferObject; import org.oscim.renderer.BufferObject;
import org.oscim.renderer.GLRenderer; import org.oscim.renderer.GLRenderer;
import org.oscim.renderer.GLRenderer.Matrices; import org.oscim.renderer.GLRenderer.Matrices;
@ -82,7 +81,7 @@ public class TextOverlayExp extends BasicOverlay {
@Override @Override
protected void doWork() { protected void doWork() {
SystemClock.sleep(300); SystemClock.sleep(150);
if (!mRun) if (!mRun)
return; return;
@ -106,35 +105,45 @@ public class TextOverlayExp extends BasicOverlay {
} }
} }
private float mSquareRadius;
private int mRelabelCnt;
public TextOverlayExp(MapView mapView) { public TextOverlayExp(MapView mapView) {
super(mapView); super(mapView);
mMapViewPosition = mapView.getMapViewPosition(); mMapViewPosition = mapView.getMapViewPosition();
layers.textureLayers = new TextLayer(); layers.textureLayers = new TextLayer();
mTmpLayer = new TextLayer(); mTmpLayer = new TextLayer();
mActiveTiles = new HashMap<MapTile, Link>();
mTmpPos = new MapPosition(); mTmpPos = new MapPosition();
mThread = new LabelThread(); mThread = new LabelThread();
mThread.start(); mThread.start();
mRelabelCnt = 0;
} }
private HashMap<MapTile, PlacementItem> mItemMap; private HashMap<MapTile, Label> mItemMap;
class PlacementItem extends TextItem { class Label extends TextItem {
int tileX; TextItem item;
int tileY;
boolean isTileNeighbour(PlacementItem other) { Link blocking;
int dx = other.tileX - tileX; Link blockedBy;
if (dx > 1 || dx < -1)
return false;
int dy = other.tileY - tileY; // shared list of all label for a tile
if (dy > 1 || dy < -1) Link siblings;
return false;
return true; MapTile tile;
public byte origin;
public int active;
public OBB2D bbox;
} }
class Link {
Link next;
Link prev;
Label it;
} }
//private static void setOBB(TextItem ti){ //private static void setOBB(TextItem ti){
@ -147,35 +156,35 @@ public class TextOverlayExp extends BasicOverlay {
//} //}
// local pool, avoids synchronized TextItem.get()/release() // local pool, avoids synchronized TextItem.get()/release()
private TextItem mPool; private Label mPool;
private byte checkOverlap(TextLayer tl, TextItem ti) { private Label mNewLabels;
private Label mPrevLabels;
private final HashMap<MapTile, Link> mActiveTiles;
for (TextItem lp = tl.labels; lp != null;) { private byte checkOverlap(TextLayer tl, Label ti) {
for (Label lp = (Label) tl.labels; lp != null;) {
// check bounding box // check bounding box
if (!TextItem.bboxOverlaps(ti, lp, 100)) { if (!TextItem.bboxOverlaps(ti, lp, 150)) {
lp = lp.next; lp = (Label) lp.next;
continue; continue;
} }
if (lp.text == ti.text && (lp.string == ti.string || lp.string.equals(ti.string))) { if (lp.text == ti.text && (lp.string == ti.string || lp.string.equals(ti.string))) {
if (lp.active <= ti.active)
return 1;
// make strings unique // make strings unique
ti.string = lp.string; ti.string = lp.string;
//p.active < ti.active ||
//Log.d(TAG, "overlap, same label in bbox " + lp.string
// + " at " + ti.x + ":" + ti.y + ", " + lp.x + ":"
//+ lp.y + " " + ti.length + "/" + lp.length);
if (lp.length < ti.length) { if (lp.length < ti.length) {
Label tmp = lp;
lp = (Label) lp.next;
//if (lp.length > ti.length) { TextItem.release(tmp.item);
//Log.d(TAG, "drop " + lp.string);
TextItem tmp = lp;
lp = lp.next;
tl.removeText(tmp); tl.removeText(tmp);
tmp.next = mPool; tmp.next = mPool;
@ -199,19 +208,20 @@ public class TextOverlayExp extends BasicOverlay {
boolean intersect = ti.bbox.overlaps(lp.bbox); boolean intersect = ti.bbox.overlaps(lp.bbox);
// byte intersect = GeometryUtils.linesIntersect(
// ti.x1, ti.y1, ti.x2, ti.y2,
// lp.x1, lp.y1, lp.x2, lp.y2);
if (intersect) { if (intersect) {
if (lp.active <= ti.active)
return 1;
//Log.d(TAG, "intersection " + lp.string + " <> " + ti.string //Log.d(TAG, "intersection " + lp.string + " <> " + ti.string
// + " at " + ti.x + ":" + ti.y); // + " at " + ti.x + ":" + ti.y);
if (!lp.text.caption if (!lp.text.caption
&& (lp.text.priority > ti.text.priority || lp.length < ti.length)) { && (lp.text.priority > ti.text.priority || lp.length < ti.length)) {
TextItem tmp = lp; Label tmp = lp;
lp = lp.next; lp = (Label) lp.next;
TextItem.release(tmp.item);
tl.removeText(tmp); tl.removeText(tmp);
tmp.next = mPool; tmp.next = mPool;
@ -221,42 +231,66 @@ public class TextOverlayExp extends BasicOverlay {
} }
return 1; return 1;
// if ((lp.n1 != null && lp.n1 == ti.n2) ||
// (lp.n2 != null && lp.n2 == ti.n1)) {
// Log.d(TAG, "overlap with adjacent label " + lp.string
// + " at " + ti.x + ":" + ti.y + ", " + lp.x + ":" + lp.y);
//
// return intersect;
// }
//
// if ((ti.n1 != null || ti.n2 != null) && (lp.n1 == null && lp.n2 == null)) {
// Log.d(TAG, "overlap, other is unique " + lp.string + " " + ti.string
// + " at " + ti.x + ":" + ti.y + ", " + lp.x + ":" + lp.y);
// return intersect;
// }
//
// return intersect;
} }
lp = (Label) lp.next;
lp = lp.next;
} }
return 0; return 0;
} }
private int mMinX; private boolean nodeIsVisible(TextItem ti) {
private int mMinY; // rough filter
private int mMaxX; float dist = ti.x * ti.x + ti.y * ti.y;
private int mMaxY; if (dist > mSquareRadius)
return false;
private boolean isVisible(TextItem ti) {
return true; return true;
} }
private boolean wayIsVisible(TextItem ti) {
// rough filter
float dist = ti.x * ti.x + ti.y * ti.y;
if (dist < mSquareRadius)
return true;
dist = ti.x1 * ti.x1 + ti.y1 * ti.y1;
if (dist < mSquareRadius)
return true;
dist = ti.x2 * ti.x2 + ti.y2 * ti.y2;
if (dist < mSquareRadius)
return true;
return false;
}
private Layers mDebugLayer; private Layers mDebugLayer;
private final float[] mMVP = new float[16]; private final float[] mMVP = new float[16];
void addTile(MapTile t) {
}
private static void addDebugLayers(Layers dbg){
dbg.clear();
LineLayer ll = (LineLayer) dbg.getLayer(0, Layer.LINE);
ll.line = new Line((Color.BLUE & 0xaaffffff), 1, Cap.BUTT);
ll.width = 2;
ll = (LineLayer) dbg.getLayer(3, Layer.LINE);
ll.line = new Line((Color.YELLOW & 0xaaffffff), 1, Cap.BUTT);
ll.width = 2;
ll = (LineLayer) dbg.getLayer(1, Layer.LINE);
ll.line = new Line((Color.RED & 0xaaffffff), 1, Cap.BUTT);
ll.width = 2;
ll = (LineLayer) dbg.getLayer(2, Layer.LINE);
ll.line = new Line((Color.GREEN & 0xaaffffff), 1, Cap.BUTT);
ll.width = 2;
ll = (LineLayer) dbg.getLayer(4, Layer.LINE);
ll.line = new Line((Color.CYAN & 0xaaffffff), 1, Cap.BUTT);
ll.width = 2;
ll = (LineLayer) dbg.getLayer(5, Layer.LINE);
ll.line = new Line((Color.MAGENTA & 0xaaffffff), 1, Cap.BUTT);
ll.width = 2;
}
boolean updateLabels() { boolean updateLabels() {
if (mTmpLayer == null) if (mTmpLayer == null)
return false; return false;
@ -271,106 +305,133 @@ public class TextOverlayExp extends BasicOverlay {
TextLayer tl = mTmpLayer; TextLayer tl = mTmpLayer;
mTmpLayer = null; mTmpLayer = null;
mNewLabels = null;
Layers dbg = null; //new Layers(); Layers dbg = null; //new Layers();
float[] coords = mTmpCoords; float[] coords = mTmpCoords;
MapPosition pos = mTmpPos;
synchronized (mMapViewPosition) { synchronized (mMapViewPosition) {
mMapViewPosition.getMapPosition(mTmpPos); mMapViewPosition.getMapPosition(pos);
mMapViewPosition.getMapViewProjection(coords); mMapViewPosition.getMapViewProjection(coords);
mMapViewPosition.getMatrix(null, null, mMVP); mMapViewPosition.getMatrix(null, null, mMVP);
} }
int mw = (mMapView.getWidth() + Tile.TILE_SIZE) / 2;
int mh = (mMapView.getHeight() + Tile.TILE_SIZE) / 2;
mSquareRadius = mw * mw + mh * mh;
// mTiles might be from another zoomlevel than the current: // mTiles might be from another zoomlevel than the current:
// this scales MapPosition to the zoomlevel of mTiles... // this scales MapPosition to the zoomlevel of mTiles...
// TODO create a helper function in MapPosition // TODO create a helper function in MapPosition
MapTile[] tiles = mTileSet.tiles; MapTile[] tiles = mTileSet.tiles;
int diff = tiles[0].zoomLevel - mTmpPos.zoomLevel; int diff = tiles[0].zoomLevel - pos.zoomLevel;
float div = FastMath.pow(diff); float div = FastMath.pow(diff);
float scale = mTmpPos.scale * div; float scale = pos.scale * div;
double angle = Math.toRadians(mTmpPos.angle); double angle = Math.toRadians(pos.angle);
float cos = (float) Math.cos(angle); float cos = (float) Math.cos(angle);
float sin = (float) Math.sin(angle); float sin = (float) Math.sin(angle);
int maxx = Tile.TILE_SIZE << (mTmpPos.zoomLevel - 1); int maxx = Tile.TILE_SIZE << (pos.zoomLevel - 1);
TextItem ti2 = null; Label l = null;
if (dbg != null) { if (dbg != null)
dbg.clear(); addDebugLayers(dbg);
LineLayer ll = (LineLayer) dbg.getLayer(0, Layer.LINE);
ll.line = new Line((Color.BLUE & 0xaaffffff), 1, Cap.BUTT); mRelabelCnt++;
ll.width = 2;
ll = (LineLayer) dbg.getLayer(3, Layer.LINE); for (Label lp = mPrevLabels; lp != null; ) {
ll.line = new Line((Color.YELLOW & 0xaaffffff), 1, Cap.BUTT); //l.active = mRelabelCnt;
ll.width = 2;
ll = (LineLayer) dbg.getLayer(1, Layer.LINE); // transform screen coordinates to tile coordinates
ll.line = new Line((Color.RED & 0xaaffffff), 1, Cap.BUTT); float s = FastMath.pow(lp.tile.zoomLevel - pos.zoomLevel);
ll.width = 2; float sscale = pos.scale / s;
ll = (LineLayer) dbg.getLayer(2, Layer.LINE);
ll.line = new Line((Color.GREEN & 0xaaffffff), 1, Cap.BUTT); if (lp.width > lp.length * sscale){
ll.width = 2; Log.d(TAG, "- scale " + lp + " " + s + " " + sscale + " " + lp.length + " " + lp.width);
TextItem.release(lp.item);
lp = (Label) lp.next;
continue;
} }
for (int i = 0, n = mTileSet.cnt; i < n; i++) { float dx = (float) (lp.tile.pixelX - pos.x * s);
float dy = (float) (lp.tile.pixelY - pos.y * s);
MapTile t = tiles[i];
if (t.state == JobTile.STATE_NONE || t.state == JobTile.STATE_LOADING)
continue;
float dx = (float) (t.pixelX - mTmpPos.x);
float dy = (float) (t.pixelY - mTmpPos.y);
// flip around date-line // flip around date-line
if (dx > maxx) { if (dx > maxx)
dx = dx - maxx * 2; dx = dx - maxx * 2;
} else if (dx < -maxx) { else if (dx < -maxx)
dx = dx + maxx * 2; dx = dx + maxx * 2;
}
dx *= scale;
dy *= scale;
for (TextItem ti = t.labels; ti != null; ti = ti.next) { lp.move(lp.item, dx, dy, sscale);
// acquire a TextItem to add to TextLayer if (!lp.text.caption) {
if (ti2 == null) { // set line endpoints relative to view to be able to
if (mPool == null) // check intersections with label from other tiles
ti2 = TextItem.get(); float width = (lp.x2 - lp.x1) / 2f;
else { float height = (lp.y2 - lp.y1) / 2f;
ti2 = mPool;
mPool = mPool.next;
ti2.next = null;
}
}
if (!ti.text.caption) lp.x2 = (lp.x + width);
lp.x1 = (lp.x - width);
lp.y2 = (lp.y + height);
lp.y1 = (lp.y - height);
if (!wayIsVisible(lp)){
Log.d(TAG, "- visible " + lp);
TextItem.release(lp.item);
lp = (Label) lp.next;
continue; continue;
}
ti2.move(ti, dx, dy, scale); byte overlaps = -1;
ti2.setAxisAlignedBBox(); if (lp.bbox != null)
lp.bbox.set(lp.x, lp.y, lp.x1, lp.y1,
lp.width + 5, lp.text.fontHeight + 5);
else lp.bbox= new OBB2D(lp.x, lp.y, lp.x1, lp.y1,
lp.width + 5, lp.text.fontHeight + 5);
ti2.bbox = new OBB2D(ti2.x, ti2.y, cos, -sin, ti2.width + 6, if (dbg != null) {
ti2.text.fontHeight + 6, true);
boolean overlaps = false; LineLayer ll;
for (TextItem lp = tl.labels; lp != null; lp = lp.next) { if (overlaps == 1)
if (!lp.text.caption) ll = (LineLayer) dbg.getLayer(4, Layer.LINE);
else
ll = (LineLayer) dbg.getLayer(5, Layer.LINE);
{
float[] points = new float[4];
short[] indices = { 4 };
points[0] = (lp.x - width * sscale);
points[1] = (lp.y - height * sscale);
points[2] = (lp.x + width * sscale);
points[3] = (lp.y + height * sscale);
ll.addLine(points, indices, false);
}
if (lp.bbox != null) {
short[] indices = { 8 };
ll.addLine(lp.bbox.corner, indices, true);
}
}
overlaps = checkOverlap(tl, lp);
if (overlaps == 0) {
if (s != 1)
Log.d(TAG, s + "add prev label " + lp);
Label tmp = lp;
lp = (Label) lp.next;
tmp.next = null;
tl.addText(tmp);
continue; continue;
if (ti2.bbox.overlaps(lp.bbox)) {
Log.d(TAG, "overlap > " + ti2.string + " " + lp.string);
//if (TextItem.bboxOverlaps(ti2, lp, 4)) {
overlaps = true;
break;
} }
} }
if (!overlaps) {
tl.addText(ti2);
ti2 = null;
}
} TextItem.release(lp.item);
lp = (Label) lp.next;
} }
/* add way labels */ /* add way labels */
@ -378,20 +439,14 @@ public class TextOverlayExp extends BasicOverlay {
MapTile t = tiles[i]; MapTile t = tiles[i];
if (t.state == JobTile.STATE_NONE || t.state == JobTile.STATE_LOADING) float dx = (float) (t.pixelX - pos.x);
continue; float dy = (float) (t.pixelY - pos.y);
float dx = (float) (t.pixelX - mTmpPos.x);
float dy = (float) (t.pixelY - mTmpPos.y);
// flip around date-line // flip around date-line
if (dx > maxx) { if (dx > maxx)
dx = dx - maxx * 2; dx = dx - maxx * 2;
} else if (dx < -maxx) { else if (dx < -maxx)
dx = dx + maxx * 2; dx = dx + maxx * 2;
}
dx *= scale;
dy *= scale;
for (TextItem ti = t.labels; ti != null; ti = ti.next) { for (TextItem ti = t.labels; ti != null; ti = ti.next) {
@ -399,36 +454,41 @@ public class TextOverlayExp extends BasicOverlay {
continue; continue;
// acquire a TextItem to add to TextLayer // acquire a TextItem to add to TextLayer
if (ti2 == null) { if (l == null) {
if (mPool == null) if (mPool == null)
ti2 = TextItem.get(); l = new Label();
else { else {
ti2 = mPool; l = mPool;
mPool = mPool.next; mPool = (Label) mPool.next;
ti2.next = null; l.next = null;
} }
l.active = Integer.MAX_VALUE;
} }
// check if path at current scale is long enough for text // check if path at current scale is long enough for text
if (dbg == null && ti.width > ti.length * scale) if (dbg == null && ti.width > ti.length * scale)
continue; continue;
l.clone(ti);
l.move(ti, dx, dy, scale);
// set line endpoints relative to view to be able to // set line endpoints relative to view to be able to
// check intersections with label from other tiles // check intersections with label from other tiles
float width = (ti.x2 - ti.x1) / 2f; float width = (ti.x2 - ti.x1) / 2f;
float height = (ti.y2 - ti.y1) / 2f; float height = (ti.y2 - ti.y1) / 2f;
ti2.bbox = null; l.bbox = null;
l.x2 = (l.x + width);
l.x1 = (l.x - width);
l.y2 = (l.y + height);
l.y1 = (l.y - height);
ti2.move(ti, dx, dy, scale); if (!wayIsVisible(l))
ti2.x2 = (ti2.x + width); continue;
ti2.x1 = (ti2.x - width);
ti2.y2 = (ti2.y + height);
ti2.y1 = (ti2.y - height);
byte overlaps = -1; byte overlaps = -1;
if (dbg == null || ti.width < ti.length * scale) if (dbg == null || ti.width < ti.length * scale)
overlaps = checkOverlap(tl, ti2); overlaps = checkOverlap(tl, l);
if (dbg != null) { if (dbg != null) {
@ -451,35 +511,93 @@ public class TextOverlayExp extends BasicOverlay {
{ {
float[] points = new float[4]; float[] points = new float[4];
short[] indices = { 4 }; short[] indices = { 4 };
points[0] = (ti2.x - width * scale) / scale; points[0] = (l.x - width * scale);
points[1] = (ti2.y - height * scale) / scale; points[1] = (l.y - height * scale);
points[2] = (ti2.x + width * scale) / scale; points[2] = (l.x + width * scale);
points[3] = (ti2.y + height * scale) / scale; points[3] = (l.y + height * scale);
ll.addLine(points, indices, false); ll.addLine(points, indices, false);
} }
if (ti2.bbox != null) { if (l.bbox != null) {
short[] indices = { 8 }; short[] indices = { 8 };
float[] points = new float[8]; ll.addLine(l.bbox.corner, indices, true);
for (int p = 0; p < 8; p++)
points[p] = ti2.bbox.corner[p] / scale;
ll.addLine(points, indices, true);
} }
} }
if (overlaps == 0) { if (overlaps == 0) {
ti.active++; tl.addText(l);
l.item = TextItem.copy(ti);
tl.addText(ti2); l.tile = t;
ti2.active = ti.active; l.active = mRelabelCnt;
ti2 = null; l = null;
} }
} }
} }
for (TextItem ti = tl.labels; ti != null; ti = ti.next) { for (int i = 0, n = mTileSet.cnt; i < n; i++) {
// scale back to fixed zoom-level. could be done in setMatrix
ti.x /= scale; MapTile t = tiles[i];
ti.y /= scale;
float dx = (float) (t.pixelX - pos.x);
float dy = (float) (t.pixelY - pos.y);
// flip around date-line
if (dx > maxx)
dx = dx - maxx * 2;
else if (dx < -maxx)
dx = dx + maxx * 2;
for (TextItem ti = t.labels; ti != null; ti = ti.next) {
if (!ti.text.caption)
continue;
// acquire a TextItem to add to TextLayer
if (l == null) {
if (mPool == null)
l = new Label();
else {
l = mPool;
mPool = (Label) mPool.next;
l.next = null;
}
l.active = Integer.MAX_VALUE;
}
l.clone(ti);
l.move(ti, dx, dy, scale);
if (!nodeIsVisible(l))
continue;
l.setAxisAlignedBBox();
l.bbox = new OBB2D(l.x, l.y, cos, -sin, l.width + 6,
l.text.fontHeight + 6, true);
boolean overlaps = false;
for (Label lp = (Label) tl.labels; lp != null; lp = (Label) lp.next) {
//if (!lp.text.caption)
// continue;
if (lp.bbox == null) {
lp.bbox = new OBB2D(lp.x, lp.y, lp.x1, lp.y1,
lp.width + 5, lp.text.fontHeight + 5);
}
if (l.bbox.overlaps(lp.bbox)) {
//Log.d(TAG, "overlap > " + ti2.string + " " + lp.string);
//if (TextItem.bboxOverlaps(ti2, lp, 4)) {
overlaps = true;
break;
}
}
if (!overlaps) {
tl.addText(l);
l.item = TextItem.copy(ti);
l.tile = t;
l.active = mRelabelCnt;
l = null;
}
}
}
for (Label ti = (Label)tl.labels; ti != null; ti = (Label)ti.next) {
if (ti.text.caption) if (ti.text.caption)
continue; continue;
@ -497,19 +615,18 @@ public class TextOverlayExp extends BasicOverlay {
} }
// release temporarily used TextItems // release temporarily used TextItems
if (ti2 != null) { if (l != null) {
ti2.next = mPool; l.next = mPool;
mPool = ti2; mPool = l;
}
if (mPool != null) {
TextItem.release(mPool);
mPool = null;
} }
// draw text to bitmaps and create vertices // draw text to bitmaps and create vertices
tl.setScale(scale);
tl.prepare(); tl.prepare();
// after 'prepare' TextLayer does not need TextItems any longer
mPrevLabels = (Label) tl.labels;
tl.labels = null;
// remove tile locks // remove tile locks
GLRenderer.releaseTiles(mTileSet); GLRenderer.releaseTiles(mTileSet);
@ -530,6 +647,7 @@ public class TextOverlayExp extends BasicOverlay {
mTmpLayer = (TextLayer) layers.textureLayers; mTmpLayer = (TextLayer) layers.textureLayers;
// clear textures and text items from previous layer // clear textures and text items from previous layer
layers.textureLayers = null;
layers.clear(); layers.clear();
if (mDebugLayer != null) { if (mDebugLayer != null) {
@ -589,13 +707,15 @@ public class TextOverlayExp extends BasicOverlay {
GLState.test(false, false); GLState.test(false, false);
if (layers.layers != null) { if (layers.layers != null) {
setMatrix(pos, m); setMatrix(pos, m, true);
Matrix.multiplyMM(m.mvp, 0, m.proj, 0, m.mvp,0);
//Matrix.multiplyMM(m.mvp, 0, m.proj, 0, m.mvp,0);
for (Layer l = layers.layers; l != null;) { for (Layer l = layers.layers; l != null;) {
if (l.type == Layer.POLYGON) { if (l.type == Layer.POLYGON) {
l = PolygonRenderer.draw(pos, l, m.mvp, true, false); l = PolygonRenderer.draw(pos, l, m.mvp, true, false);
} else { } else {
l = LineRenderer.draw(pos, l, m.mvp, div, 0, layers.lineOffset); float scale = pos.scale * div;
l = LineRenderer.draw(pos, l, m.mvp, scale, 0, layers.lineOffset);
} }
} }
} }
@ -603,6 +723,7 @@ public class TextOverlayExp extends BasicOverlay {
setMatrix(pos, m); setMatrix(pos, m);
for (Layer l = layers.textureLayers; l != null;) { for (Layer l = layers.textureLayers; l != null;) {
float scale = (mMapPosition.scale / pos.scale) * div; float scale = (mMapPosition.scale / pos.scale) * div;
l = TextureRenderer.draw(l, scale, m.proj, m.mvp); l = TextureRenderer.draw(l, scale, m.proj, m.mvp);
} }
@ -616,9 +737,9 @@ public class TextOverlayExp extends BasicOverlay {
float x = (float) (oPos.x - curPos.x * div); float x = (float) (oPos.x - curPos.x * div);
float y = (float) (oPos.y - curPos.y * div); float y = (float) (oPos.y - curPos.y * div);
float scale = curPos.scale / div; float scale = (curPos.scale / mMapPosition.scale) / div;
float s = curPos.scale / div;
GlUtils.setMatrix(m.mvp, x * scale, y * scale, GlUtils.setMatrix(m.mvp, x * s, y * s,
scale / GLRenderer.COORD_MULTIPLIER); scale / GLRenderer.COORD_MULTIPLIER);
Matrix.multiplyMM(m.mvp, 0, m.view, 0, m.mvp, 0); Matrix.multiplyMM(m.mvp, 0, m.view, 0, m.mvp, 0);

View File

@ -167,7 +167,8 @@ public final class Text extends RenderInstruction {
FontMetrics fm = paint.getFontMetrics(); FontMetrics fm = paint.getFontMetrics();
fontHeight = (float) Math.ceil(Math.abs(fm.bottom) + Math.abs(fm.top)); fontHeight = (float) Math.ceil(Math.abs(fm.bottom) + Math.abs(fm.top));
fontDescent = (float) Math.ceil(Math.abs(fm.descent)); //fontDescent = (float) Math.ceil(Math.abs(fm.descent));
fontDescent = Math.abs(fm.bottom);
} }
@Override @Override

View File

@ -991,14 +991,22 @@
<!-- place --> <!-- place -->
<rule e="node" k="place" v="*"> <rule e="node" k="place" v="*">
<rule e="node" k="*" v="suburb|town|village"> <rule e="node" k="*" v="suburb" zoom-max="14">
<caption k="name" font-size="17" fill="#2020bb" <caption k="name" font-size="17" fill="#bb2020"
stroke="#ffffff" stroke-width="2.0" />
</rule>
<rule e="node" k="*" v="village" zoom-max="14">
<caption k="name" font-size="17" fill="#2020cc"
stroke="#ffffff" stroke-width="2.0" /> stroke="#ffffff" stroke-width="2.0" />
</rule> </rule>
<rule e="node" k="*" v="island" zoom-min="10"> <rule e="node" k="*" v="island" zoom-min="10">
<caption k="name" font-style="bold" font-size="20" fill="#000000" <caption k="name" font-style="bold" font-size="20" fill="#000000"
stroke="#ffffff" stroke-width="2.0" /> stroke="#ffffff" stroke-width="2.0" />
</rule> </rule>
<rule e="node" k="*" v="town">
<caption k="name" font-size="19" fill="#000000"
stroke="#ffffff" stroke-width="2.0" />
</rule>
<rule e="node" k="*" v="city"> <rule e="node" k="*" v="city">
<caption k="name" font-size="20" fill="#000000" <caption k="name" font-size="20" fill="#000000"
stroke="#ffffff" stroke-width="2.0" /> stroke="#ffffff" stroke-width="2.0" />

View File

@ -329,5 +329,37 @@ public final class GeometryUtils {
return inside; return inside;
} }
public static float areaSigned(Point p1, Point p2, Point p3) {
return ((p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y))*0.5f;
}
public static float areaSigned(float ax, float ay, float bx, float by, float cx, float cy) {
return ((ax - cx) * (by - cy) - (bx - cx) * (ay - cy)) * 0.5f;
}
public static float area(float ax, float ay, float bx, float by, float cx, float cy) {
float area = ((ax - cx) * (by - cy) - (bx - cx) * (ay - cy)) * 0.5f;
return area < 0 ? -area : area;
}
public static boolean pointInTri(Point pt, Point p1, Point p2, Point p3) {
boolean inside = false;
boolean p1s = p1.y > pt.y;
boolean p2s = p2.y > pt.y;
boolean p3s = p3.y > pt.y;
if ((p1s != p3s)
&& (pt.x < (p3.x - p1.x) * (pt.y - p1.y) / (p3.y - p1.y) + p1.x))
inside = !inside;
if ((p2s != p1s)
&& (pt.x < (p1.x - p2.x) * (pt.y - p2.y) / (p1.y - p2.y) + p2.x))
inside = !inside;
if ((p3s != p2s)
&& (pt.x < (p2.x - p3.x) * (pt.y - p3.y) / (p2.y - p3.y) + p3.x))
inside = !inside;
return inside;
}
} }

View File

@ -15,9 +15,10 @@
package org.oscim.utils; package org.oscim.utils;
/** /**
* @author Hannes Janetzek * from http://en.wikipedia.org/wiki/Cohen%E2%80%93
* taken from http://en.wikipedia.org/wiki/Cohen%E2%80%93
* Sutherland_algorithm * Sutherland_algorithm
*
* @adapted by Hannes Janetzek
*/ */
public class LineClipper { public class LineClipper {
@ -28,19 +29,35 @@ public class LineClipper {
private static final int BOTTOM = 4; // 0100 private static final int BOTTOM = 4; // 0100
private static final int TOP = 8; // 1000 private static final int TOP = 8; // 1000
private int xmin, xmax, ymin, ymax; private final int xmin, xmax, ymin, ymax;
public final int[] out;
public LineClipper(int minx, int miny, int maxx, int maxy) { public LineClipper(int minx, int miny, int maxx, int maxy) {
this.xmin = minx; this.xmin = minx;
this.ymin = miny; this.ymin = miny;
this.xmax = maxx; this.xmax = maxx;
this.ymax = maxy; this.ymax = maxy;
this.out = null;
}
public LineClipper(int minx, int miny, int maxx, int maxy, boolean keepResult) {
this.xmin = minx;
this.ymin = miny;
this.xmax = maxx;
this.ymax = maxy;
if (keepResult)
this.out = new int[4];
else
this.out = null;
} }
private int mPrevOutcode; private int mPrevOutcode;
private int mPrevX; private int mPrevX;
private int mPrevY; private int mPrevY;
public int outX;
public int outY;
public void clipStart(int x0, int y0) { public void clipStart(int x0, int y0) {
mPrevX = x0; mPrevX = x0;
mPrevY = y0; mPrevY = y0;
@ -58,8 +75,13 @@ public class LineClipper {
mPrevOutcode = outcode; mPrevOutcode = outcode;
} }
public boolean clipNext(int x1, int y1) { /**
boolean accept; * @param x1 ...
* @param y1 ...
* @return 0 if not intersection, 1 fully within, -1 clipped (and 'out' set to new points)
*/
public int clipNext(int x1, int y1) {
int accept;
int outcode = INSIDE; int outcode = INSIDE;
if (x1 < xmin) if (x1 < xmin)
@ -73,12 +95,13 @@ public class LineClipper {
if ((mPrevOutcode | outcode) == 0) { if ((mPrevOutcode | outcode) == 0) {
// Bitwise OR is 0. Trivially accept // Bitwise OR is 0. Trivially accept
accept = true; accept = 1;
} else if ((mPrevOutcode & outcode) != 0) { } else if ((mPrevOutcode & outcode) != 0) {
// Bitwise AND is not 0. Trivially reject // Bitwise AND is not 0. Trivially reject
accept = false; accept = 0;
} else { } else {
accept = clip(mPrevX, mPrevY, x1, y1, xmin, ymin, xmax, ymax, mPrevOutcode, outcode); accept = clip(mPrevX, mPrevY, x1, y1, xmin, ymin, xmax, ymax, mPrevOutcode, outcode,
this.out) ? -1 : 0;
} }
mPrevOutcode = outcode; mPrevOutcode = outcode;
mPrevX = x1; mPrevX = x1;
@ -91,7 +114,7 @@ public class LineClipper {
// P0 = (x0, y0) to P1 = (x1, y1) against a rectangle with // P0 = (x0, y0) to P1 = (x1, y1) against a rectangle with
// diagonal from (xmin, ymin) to (xmax, ymax). // diagonal from (xmin, ymin) to (xmax, ymax).
private static boolean clip(int x0, int y0, int x1, int y1, private static boolean clip(int x0, int y0, int x1, int y1,
int xmin, int ymin, int xmax, int ymax, int outcode0, int outcode1) { int xmin, int ymin, int xmax, int ymax, int outcode0, int outcode1, int[] out) {
boolean accept = false; boolean accept = false;
@ -154,9 +177,12 @@ public class LineClipper {
} }
} }
} }
if (accept && out != null) {
// TODO could do sth with the result x0... out[0] = x0;
out[1] = y0;
out[2] = x1;
out[3] = y1;
}
return accept; return accept;
} }
} }

View File

@ -158,6 +158,37 @@ public class OBB2D {
computeAxes(); computeAxes();
} }
public void set(float cx, float cy, float dx, float dy, float width, float height){
float vx = cx - dx;
float vy = cy - dy;
float a = (float) Math.sqrt(vx * vx + vy * vy);
vx /= a;
vy /= a;
float hw = width / 2;
float hh = height / 2;
float ux = vy * hh;
float uy = -vx * hh;
vx *= hw;
vy *= hw;
corner[0] = cx - vx - ux;
corner[1] = cy - vy - uy;
corner[2] = cx + vx - ux;
corner[3] = cy + vy - uy;
corner[4] = cx + vx + ux;
corner[5] = cy + vy + uy;
corner[6] = cx - vx + ux;
corner[7] = cy - vy + uy;
computeAxes();
}
public OBB2D(float cx, float cy, float dx, float dy, float width, float height) { public OBB2D(float cx, float cy, float dx, float dy, float width, float height) {
float vx = cx - dx; float vx = cx - dx;