From fc96e5f6e80d47f25e1e01b8b52290684e93a227 Mon Sep 17 00:00:00 2001
From: Hannes Janetzek <hannes.janetzek@gmail.com>
Date: Mon, 4 Feb 2013 02:01:50 +0100
Subject: [PATCH] impprove labeling a bit by giving priority to labels that
 were labeled previously

---
 src/org/oscim/renderer/layer/TextItem.java    |   5 +-
 .../renderer/overlays/TextOverlayExp.java     | 245 +++++++++++-------
 2 files changed, 154 insertions(+), 96 deletions(-)

diff --git a/src/org/oscim/renderer/layer/TextItem.java b/src/org/oscim/renderer/layer/TextItem.java
index e61d3426..f1ee784b 100644
--- a/src/org/oscim/renderer/layer/TextItem.java
+++ b/src/org/oscim/renderer/layer/TextItem.java
@@ -39,7 +39,7 @@ public class TextItem {
 			pool = pool.next;
 
 			ti.next = null;
-
+			ti.active = 0;
 			return ti;
 		}
 	}
@@ -173,6 +173,5 @@ public class TextItem {
 
 	public byte origin;
 
-	public boolean active;
-	// public byte placement
+	public int active;
 }
diff --git a/src/org/oscim/renderer/overlays/TextOverlayExp.java b/src/org/oscim/renderer/overlays/TextOverlayExp.java
index dc0da072..6b70209b 100644
--- a/src/org/oscim/renderer/overlays/TextOverlayExp.java
+++ b/src/org/oscim/renderer/overlays/TextOverlayExp.java
@@ -20,9 +20,13 @@ import java.util.HashMap;
 import org.oscim.core.MapPosition;
 import org.oscim.core.Tile;
 import org.oscim.generator.JobTile;
+import org.oscim.renderer.BufferObject;
 import org.oscim.renderer.GLRenderer;
+import org.oscim.renderer.GLState;
+import org.oscim.renderer.LineRenderer;
 import org.oscim.renderer.MapTile;
-import org.oscim.renderer.TileManager;
+import org.oscim.renderer.PolygonRenderer;
+import org.oscim.renderer.TextureRenderer;
 import org.oscim.renderer.TileSet;
 import org.oscim.renderer.layer.Layer;
 import org.oscim.renderer.layer.Layers;
@@ -38,6 +42,7 @@ import org.oscim.view.MapView;
 
 import android.graphics.Color;
 import android.graphics.Paint.Cap;
+import android.opengl.GLES20;
 import android.opengl.Matrix;
 import android.os.SystemClock;
 import android.util.Log;
@@ -45,18 +50,17 @@ import android.util.Log;
 public class TextOverlayExp extends BasicOverlay {
 	private final static String TAG = TextOverlayExp.class.getName();
 
-	private TileSet mTiles;
+	private TileSet mTileSet;
 	private LabelThread mThread;
 
-	private MapPosition mWorkPos;
+	private MapPosition mTmpPos;
 
 	// TextLayer that is updating
-	private TextLayer mWorkLayer;
+	private TextLayer mTmpLayer;
 	// TextLayer that is ready to be added to 'layers'
-	private TextLayer mCurLayer;
+	private TextLayer mNextLayer;
 
 	/* package */boolean mRun;
-	/* package */boolean mRerun;
 
 	class LabelThread extends PausableThread {
 
@@ -67,10 +71,12 @@ public class TextOverlayExp extends BasicOverlay {
 				return;
 
 			mRun = false;
-			if (updateLabels())
-				mMapView.redrawMap(false);
-			else
+
+			if (updateLabels()) {
+				mMapView.redrawMap(true);
+			} else {
 				mRun = true;
+			}
 		}
 
 		@Override
@@ -80,16 +86,22 @@ public class TextOverlayExp extends BasicOverlay {
 
 		@Override
 		protected boolean hasWork() {
-			return mRun || mRerun;
+			return mRun;
 		}
 	}
 
+	private int mSerial;
+
 	public TextOverlayExp(MapView mapView) {
 		super(mapView);
 
-		mWorkPos = new MapPosition();
+		layers.textureLayers = new TextLayer();
+		mTmpLayer = new TextLayer();
+
+		mTmpPos = new MapPosition();
 		mThread = new LabelThread();
 		mThread.start();
+		mSerial = 0;
 	}
 
 	private HashMap<TextItem, PlacementItem> mItemMap;
@@ -111,6 +123,7 @@ public class TextOverlayExp extends BasicOverlay {
 		}
 	}
 
+	// local pool, avoids synchronized TextItem.get()/release()
 	private TextItem mPool;
 
 	private byte checkOverlap(TextLayer tl, TextItem ti) {
@@ -122,7 +135,7 @@ public class TextOverlayExp extends BasicOverlay {
 			}
 
 			// check bounding box
-			if (!TextItem.bboxOverlaps(ti, lp, 50)) {
+			if (!TextItem.bboxOverlaps(ti, lp, 100)) {
 				lp = lp.next;
 				continue;
 			}
@@ -131,7 +144,9 @@ public class TextOverlayExp extends BasicOverlay {
 				// make strings unique
 				ti.string = lp.string;
 
-				if (lp.length > ti.length) {
+				if (lp.active < ti.active) {
+
+					//if (lp.length > ti.length) {
 					//Log.d(TAG, "drop " + lp.string);
 					TextItem tmp = lp;
 					lp = lp.next;
@@ -159,24 +174,11 @@ public class TextOverlayExp extends BasicOverlay {
 					lp.x1, lp.y1, lp.x2, lp.y2);
 
 			if (intersect != 0) {
-				//Log.d(TAG, "overlap " + lp.string + " <> " + ti.string 
-				//+ " at " + ti.x + ":" + ti.y);
+				//Log.d(TAG, "intersection " + lp.string + " <> " + ti.string
+				//	+ " at " + ti.x + ":" + ti.y);
 
-				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;
-				}
-
-				if (lp.length > ti.length) {
+				if (lp.active < ti.active) {
+					//if (lp.length > ti.length) {
 					//Log.d(TAG, "drop " + lp.string);
 					TextItem tmp = lp;
 					lp = lp.next;
@@ -188,7 +190,24 @@ public class TextOverlayExp extends BasicOverlay {
 
 					continue;
 				}
+
 				return intersect;
+
+				//				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 = lp.next;
@@ -196,55 +215,42 @@ public class TextOverlayExp extends BasicOverlay {
 		return 0;
 	}
 
-	private final Layers mDebugLayer = null; //new Layers();
+	private Layers mDebugLayer;
 
 	boolean updateLabels() {
-		mTiles = TileManager.getActiveTiles(mTiles);
-
-		// Log.d("...", "relabel " + mRerun + " " + x + " " + y);
-		if (mTiles.cnt == 0)
+		if (mTmpLayer == null)
 			return false;
 
-		mMapView.getMapViewPosition().getMapPosition(mWorkPos, null);
+		mTileSet = GLRenderer.getVisibleTiles(mTileSet);
 
-		TextLayer tl = mWorkLayer;
+		if (mTileSet.cnt == 0)
+			return false;
 
-		if (tl == null)
-			tl = new TextLayer();
+		mSerial++;
+
+		TextLayer tl = mTmpLayer;
+		mTmpLayer = null;
+
+		//mDebugLayer = new Layers();
 
 		// mTiles might be from another zoomlevel than the current:
 		// this scales MapPosition to the zoomlevel of mTiles...
 		// TODO create a helper function in MapPosition
-		int diff = mTiles.tiles[0].zoomLevel - mWorkPos.zoomLevel;
+		mMapView.getMapViewPosition().getMapPosition(mTmpPos, null);
+		// capture current state
 
-		// only relabel when tiles belong to the current zoomlevel or its parent
-		if (diff > 1 || diff < -2) {
-			// pass back the current layer
-			synchronized (this) {
-				Log.d(TAG, "drop labels: diff " + diff);
-				//mCurLayer = tl;
-			}
-			return false;
-		}
+		MapTile[] tiles = mTileSet.tiles;
 
-		MapTile[] tiles = mTiles.tiles;
-		//		for (int i = 0, n = mTiles.cnt; i < n; i++) {
-		//			MapTile t = tiles[i];
-		//			if (t.isVisible && (t.state == JobTile.STATE_NONE || t.state == JobTile.STATE_LOADING)) {
-		//				synchronized (this) {
-		//					Log.d(TAG, "tiles not ready");
-		//					//mCurLayer = tl;
-		//				}
-		//				return false;
-		//			}
-		//		}
+		int diff = tiles[0].zoomLevel - mTmpPos.zoomLevel;
 
-		float scale = mWorkPos.scale;
-		double angle = Math.toRadians(mWorkPos.angle);
+		float div = FastMath.pow(diff);
+
+		float scale = mTmpPos.scale * div;
+		double angle = Math.toRadians(mTmpPos.angle);
 		float cos = (float) Math.cos(angle);
 		float sin = (float) Math.sin(angle);
 
-		int maxx = Tile.TILE_SIZE << (mWorkPos.zoomLevel - 1);
+		int maxx = Tile.TILE_SIZE << (mTmpPos.zoomLevel - 1);
 
 		TextItem ti2 = null;
 
@@ -263,12 +269,11 @@ public class TextOverlayExp extends BasicOverlay {
 			ll.line = new Line((Color.GREEN & 0xaaffffff), 1, Cap.BUTT);
 			ll.width = 2;
 		}
+		int added = 0;
+
+		for (int i = 0, n = mTileSet.cnt; i < n; i++) {
 
-		// TODO more sophisticated placement :)
-		for (int i = 0, n = mTiles.cnt; i < n; i++) {
 			MapTile t = tiles[i];
-			if (!t.isVisible)
-				continue;
 
 			if (t.state == JobTile.STATE_NONE || t.state == JobTile.STATE_LOADING)
 				continue;
@@ -276,8 +281,8 @@ public class TextOverlayExp extends BasicOverlay {
 			//if (t.joined != MapTile.JOINED)
 			//	joinTile()
 
-			float dx = (float) (t.pixelX - mWorkPos.x);
-			float dy = (float) (t.pixelY - mWorkPos.y);
+			float dx = (float) (t.pixelX - mTmpPos.x);
+			float dy = (float) (t.pixelY - mTmpPos.y);
 
 			// flip around date-line
 			if (dx > maxx) {
@@ -378,14 +383,20 @@ public class TextOverlayExp extends BasicOverlay {
 					ll.addLine(points, indices, false);
 				}
 				if (overlaps == 0) {
+					ti.active++;
+
 					tl.addText(ti2);
+					ti2.active = ti.active;
 					ti2 = null;
 				}
 			}
 		}
-		//int count = 0;
+		int count = 0;
 		for (TextItem ti = tl.labels; ti != null; ti = ti.next) {
-			//count++;
+			count++;
+
+			//ti.active = mSerial;
+
 			// scale back to fixed zoom-level. could be done in setMatrix
 			ti.x /= scale;
 			ti.y /= scale;
@@ -419,15 +430,15 @@ public class TextOverlayExp extends BasicOverlay {
 		tl.setScale(scale);
 		tl.prepare();
 
+		if (count == 0)
+			Log.d(TAG, "> no labels: " + count + " " + added);
+
 		//TextItem.printPool();
-
-		//		if (count == 0)
-		//			Log.d(TAG, "> no labels: " + count);
-
 		//Log.d(TAG, "new labels: " + count);
-		// everything synchronized?
+
+		// pass new labels for rendering
 		synchronized (this) {
-			mCurLayer = tl;
+			mNextLayer = tl;
 		}
 		return true;
 	}
@@ -435,11 +446,10 @@ public class TextOverlayExp extends BasicOverlay {
 	@Override
 	public synchronized void update(MapPosition curPos, boolean positionChanged,
 			boolean tilesChanged) {
-		// Log.d("...", "update " + tilesChanged + " " + positionChanged);
 
-		if (mCurLayer != null) {
+		if (mNextLayer != null) {
 			// keep text layer, not recrating its canvas each time
-			mWorkLayer = (TextLayer) layers.textureLayers;
+			mTmpLayer = (TextLayer) layers.textureLayers;
 
 			// clear textures and text items from previous layer
 			layers.clear();
@@ -449,32 +459,81 @@ public class TextOverlayExp extends BasicOverlay {
 				mDebugLayer.layers = null;
 			}
 
-			// set new TextLayer to be uploaded and used
-			layers.textureLayers = mCurLayer;
-
-			mCurLayer = null;
+			// set new TextLayer to be uploaded and rendered
+			layers.textureLayers = mNextLayer;
 
 			// make the 'labeled' MapPosition current
 			MapPosition tmp = mMapPosition;
-			mMapPosition = mWorkPos;
-			mWorkPos = tmp;
+			mMapPosition = mTmpPos;
+			mTmpPos = tmp;
 
-			// TODO should return true instead
 			newData = true;
+			mNextLayer = null;
+			if (!(positionChanged || tilesChanged))
+				return;
 		}
+
 		if (mHolding)
 			return;
 
-		if (tilesChanged || positionChanged) {
-			if (!mRun) {
-				mRun = true;
-				synchronized (mThread) {
-					mThread.notify();
-				}
+		if (!mRun) {
+			mRun = true;
+			synchronized (mThread) {
+				mThread.notify();
 			}
 		}
 	}
 
+	@Override
+	public void compile() {
+		int newSize = layers.getSize();
+		if (newSize == 0)
+			Log.d(TAG, "text layer size " + newSize);
+
+		if (newSize == 0) {
+			BufferObject.release(vbo);
+			vbo = null;
+			isReady = false;
+			return;
+		}
+
+		if (vbo == null) {
+			vbo = BufferObject.get(0);
+
+			if (vbo == null)
+				return;
+		}
+
+		if (newSize > 0) {
+			if (GLRenderer.uploadLayers(layers, vbo, newSize, true))
+				isReady = true;
+		}
+	}
+
+	@Override
+	public synchronized void render(MapPosition pos, float[] mv, float[] proj) {
+		setMatrix(pos, mv);
+		float div = FastMath.pow(mMapPosition.zoomLevel - pos.zoomLevel);
+
+		Matrix.multiplyMM(mvp, 0, proj, 0, mv, 0);
+
+		GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo.id);
+		GLState.test(false, false);
+
+		for (Layer l = layers.layers; l != null;) {
+			if (l.type == Layer.POLYGON) {
+				l = PolygonRenderer.draw(pos, l, mvp, true, false);
+			} else {
+				l = LineRenderer.draw(pos, l, mvp, div, 0, layers.lineOffset);
+			}
+		}
+
+		for (Layer l = layers.textureLayers; l != null;) {
+			l = TextureRenderer.draw(l, (mMapPosition.scale / pos.scale) * div
+					, proj, mv);
+		}
+	}
+
 	@Override
 	protected void setMatrix(MapPosition curPos, float[] matrix) {
 		MapPosition oPos = mMapPosition;