diff --git a/src/org/oscim/app/LocationHandler.java b/src/org/oscim/app/LocationHandler.java index 82284bd0..1c73e8ce 100644 --- a/src/org/oscim/app/LocationHandler.java +++ b/src/org/oscim/app/LocationHandler.java @@ -15,7 +15,7 @@ package org.oscim.app; import org.oscim.core.GeoPoint; -import org.oscim.core.MapPosition; +import org.oscim.view.MapPosition; import android.content.Context; import android.location.Criteria; diff --git a/src/org/oscim/app/TileMap.java b/src/org/oscim/app/TileMap.java index ebc8a468..48dba1f6 100755 --- a/src/org/oscim/app/TileMap.java +++ b/src/org/oscim/app/TileMap.java @@ -9,12 +9,12 @@ import org.oscim.app.filefilter.ValidRenderTheme; import org.oscim.app.filepicker.FilePicker; import org.oscim.app.preferences.EditPreferences; import org.oscim.core.GeoPoint; -import org.oscim.core.MapPosition; import org.oscim.database.MapDatabases; import org.oscim.theme.InternalRenderTheme; import org.oscim.utils.AndroidUtils; import org.oscim.view.DebugSettings; import org.oscim.view.MapActivity; +import org.oscim.view.MapPosition; import org.oscim.view.MapView; import android.annotation.TargetApi; diff --git a/src/org/oscim/core/MercatorProjection.java b/src/org/oscim/core/MercatorProjection.java index b9e2df14..8220144b 100644 --- a/src/org/oscim/core/MercatorProjection.java +++ b/src/org/oscim/core/MercatorProjection.java @@ -14,6 +14,8 @@ */ package org.oscim.core; +import org.oscim.view.MapPosition; + /** * An implementation of the spherical Mercator projection. */ diff --git a/src/org/oscim/core/Tag.java b/src/org/oscim/core/Tag.java index 2c8c64a9..fca297bc 100644 --- a/src/org/oscim/core/Tag.java +++ b/src/org/oscim/core/Tag.java @@ -17,6 +17,9 @@ package org.oscim.core; /** * A tag represents an immutable key-value pair. */ + +// TODO: use own stringshare method instead of internalized strings + public class Tag { private static final char KEY_VALUE_SEPARATOR = '='; /** diff --git a/src/org/oscim/database/oscimap/MapDatabase.java b/src/org/oscim/database/oscimap/MapDatabase.java index 5d1e2dd8..78257ac4 100644 --- a/src/org/oscim/database/oscimap/MapDatabase.java +++ b/src/org/oscim/database/oscimap/MapDatabase.java @@ -65,8 +65,8 @@ public class MapDatabase implements IMapDatabase { private static final String CACHE_FILE = "%d-%d-%d.tile"; private static final String SERVER_ADDR = "city.informatik.uni-bremen.de"; - // private static final String URL = "/osci/map-live/"; - private static final String URL = "/osci/oscim/"; + private static final String URL = "/osci/map-live/"; + // private static final String URL = "/osci/oscim/"; private final static float REF_TILE_SIZE = 4096.0f; diff --git a/src/org/oscim/utils/GLSurfaceView.java b/src/org/oscim/utils/GLSurfaceView.java index c775af69..5601345e 100644 --- a/src/org/oscim/utils/GLSurfaceView.java +++ b/src/org/oscim/utils/GLSurfaceView.java @@ -1,17 +1,3 @@ -/* - * Copyright 2010, 2011, 2012 mapsforge.org - * - * 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 - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ package org.oscim.utils; /* @@ -53,17 +39,21 @@ import android.view.SurfaceHolder; import android.view.SurfaceView; /** - * An implementation of SurfaceView that uses the dedicated surface for displaying OpenGL rendering. + * An implementation of SurfaceView that uses the dedicated surface for + * displaying OpenGL rendering. * <p> * A GLSurfaceView provides the following features: * <p> * <ul> - * <li>Manages a surface, which is a special piece of memory that can be composited into the Android view system. + * <li>Manages a surface, which is a special piece of memory that can be + * composited into the Android view system. * <li>Manages an EGL display, which enables OpenGL to render into a surface. * <li>Accepts a user-provided Renderer object that does the actual rendering. - * <li>Renders on a dedicated thread to decouple rendering performance from the UI thread. + * <li>Renders on a dedicated thread to decouple rendering performance from the + * UI thread. * <li>Supports both on-demand and continuous rendering. - * <li>Optionally wraps, traces, and/or error-checks the renderer's OpenGL calls. + * <li>Optionally wraps, traces, and/or error-checks the renderer's OpenGL + * calls. * </ul> * <div class="special reference"> * <h3>Developer Guides</h3> @@ -73,15 +63,18 @@ import android.view.SurfaceView; * </p> * </div> <h3>Using GLSurfaceView</h3> * <p> - * Typically you use GLSurfaceView by subclassing it and overriding one or more of the View system input event methods. - * If your application does not need to override event methods then GLSurfaceView can be used as-is. For the most part - * GLSurfaceView behavior is customized by calling "set" methods rather than by subclassing. For example, unlike a - * regular View, drawing is delegated to a separate Renderer object which is registered with the GLSurfaceView using the - * {@link #setRenderer(Renderer)} call. + * Typically you use GLSurfaceView by subclassing it and overriding one or more + * of the View system input event methods. If your application does not need to + * override event methods then GLSurfaceView can be used as-is. For the most + * part GLSurfaceView behavior is customized by calling "set" methods rather + * than by subclassing. For example, unlike a regular View, drawing is delegated + * to a separate Renderer object which is registered with the GLSurfaceView + * using the {@link #setRenderer(Renderer)} call. * <p> - * <h3>Initializing GLSurfaceView</h3> All you have to do to initialize a GLSurfaceView is call - * {@link #setRenderer(Renderer)}. However, if desired, you can modify the default behavior of GLSurfaceView by calling - * one or more of these methods before calling setRenderer: + * <h3>Initializing GLSurfaceView</h3> All you have to do to initialize a + * GLSurfaceView is call {@link #setRenderer(Renderer)}. However, if desired, + * you can modify the default behavior of GLSurfaceView by calling one or more + * of these methods before calling setRenderer: * <ul> * <li>{@link #setDebugFlags(int)} * <li>{@link #setEGLConfigChooser(boolean)} @@ -90,43 +83,54 @@ import android.view.SurfaceView; * <li>{@link #setGLWrapper(GLWrapper)} * </ul> * <p> - * <h4>Specifying the android.view.Surface</h4> By default GLSurfaceView will create a PixelFormat.RGB_565 format - * surface. If a translucent surface is required, call getHolder().setFormat(PixelFormat.TRANSLUCENT). The exact format - * of a TRANSLUCENT surface is device dependent, but it will be a 32-bit-per-pixel surface with 8 bits per component. + * <h4>Specifying the android.view.Surface</h4> By default GLSurfaceView will + * create a PixelFormat.RGB_565 format surface. If a translucent surface is + * required, call getHolder().setFormat(PixelFormat.TRANSLUCENT). The exact + * format of a TRANSLUCENT surface is device dependent, but it will be a + * 32-bit-per-pixel surface with 8 bits per component. * <p> - * <h4>Choosing an EGL Configuration</h4> A given Android device may support multiple EGLConfig rendering - * configurations. The available configurations may differ in how may channels of data are present, as well as how many - * bits are allocated to each channel. Therefore, the first thing GLSurfaceView has to do when starting to render is - * choose what EGLConfig to use. + * <h4>Choosing an EGL Configuration</h4> A given Android device may support + * multiple EGLConfig rendering configurations. The available configurations may + * differ in how may channels of data are present, as well as how many bits are + * allocated to each channel. Therefore, the first thing GLSurfaceView has to do + * when starting to render is choose what EGLConfig to use. * <p> - * By default GLSurfaceView chooses a EGLConfig that has an RGB_565 pixel format, with at least a 16-bit depth buffer - * and no stencil. + * By default GLSurfaceView chooses a EGLConfig that has an RGB_565 pixel + * format, with at least a 16-bit depth buffer and no stencil. * <p> - * If you would prefer a different EGLConfig you can override the default behavior by calling one of the - * setEGLConfigChooser methods. + * If you would prefer a different EGLConfig you can override the default + * behavior by calling one of the setEGLConfigChooser methods. * <p> - * <h4>Debug Behavior</h4> You can optionally modify the behavior of GLSurfaceView by calling one or more of the - * debugging methods {@link #setDebugFlags(int)}, and {@link #setGLWrapper}. These methods may be called before and/or - * after setRenderer, but typically they are called before setRenderer so that they take effect immediately. + * <h4>Debug Behavior</h4> You can optionally modify the behavior of + * GLSurfaceView by calling one or more of the debugging methods + * {@link #setDebugFlags(int)}, and {@link #setGLWrapper}. These methods may be + * called before and/or after setRenderer, but typically they are called before + * setRenderer so that they take effect immediately. * <p> - * <h4>Setting a Renderer</h4> Finally, you must call {@link #setRenderer} to register a {@link Renderer}. The renderer - * is responsible for doing the actual OpenGL rendering. + * <h4>Setting a Renderer</h4> Finally, you must call {@link #setRenderer} to + * register a {@link Renderer}. The renderer is responsible for doing the actual + * OpenGL rendering. * <p> - * <h3>Rendering Mode</h3> Once the renderer is set, you can control whether the renderer draws continuously or - * on-demand by calling {@link #setRenderMode}. The default is continuous rendering. + * <h3>Rendering Mode</h3> Once the renderer is set, you can control whether the + * renderer draws continuously or on-demand by calling {@link #setRenderMode}. + * The default is continuous rendering. * <p> - * <h3>Activity Life-cycle</h3> A GLSurfaceView must be notified when the activity is paused and resumed. GLSurfaceView - * clients are required to call {@link #onPause()} when the activity pauses and {@link #onResume()} when the activity - * resumes. These calls allow GLSurfaceView to pause and resume the rendering thread, and also allow GLSurfaceView to - * release and recreate the OpenGL display. + * <h3>Activity Life-cycle</h3> A GLSurfaceView must be notified when the + * activity is paused and resumed. GLSurfaceView clients are required to call + * {@link #onPause()} when the activity pauses and {@link #onResume()} when the + * activity resumes. These calls allow GLSurfaceView to pause and resume the + * rendering thread, and also allow GLSurfaceView to release and recreate the + * OpenGL display. * <p> * <h3>Handling events</h3> * <p> - * To handle an event you will typically subclass GLSurfaceView and override the appropriate method, just as you would - * with any other View. However, when handling the event, you may need to communicate with the Renderer object that's - * running in the rendering thread. You can do this using any standard Java cross-thread communication mechanism. In - * addition, one relatively easy way to communicate with your renderer is to call {@link #queueEvent(Runnable)}. For - * example: + * To handle an event you will typically subclass GLSurfaceView and override the + * appropriate method, just as you would with any other View. However, when + * handling the event, you may need to communicate with the Renderer object + * that's running in the rendering thread. You can do this using any standard + * Java cross-thread communication mechanism. In addition, one relatively easy + * way to communicate with your renderer is to call + * {@link #queueEvent(Runnable)}. For example: * * <pre class="prettyprint"> * class MyGLSurfaceView extends GLSurfaceView { @@ -154,9 +158,6 @@ import android.view.SurfaceView; * } * * - * - * - * * </pre> */ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback { @@ -169,7 +170,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback private final static boolean LOG_RENDERER_DRAW_FRAME = false; private final static boolean LOG_EGL = false; /** - * The renderer only renders when the surface is created, or when {@link #requestRender} is called. + * The renderer only renders when the surface is created, or when + * {@link #requestRender} is called. * * @see #getRenderMode() * @see #setRenderMode(int) @@ -185,8 +187,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback public final static int RENDERMODE_CONTINUOUSLY = 1; /** - * Check glError() after every GL call and throw an exception if glError indicates that an error has occurred. This - * can be used to help track down which OpenGL ES call is causing an error. + * Check glError() after every GL call and throw an exception if glError + * indicates that an error has occurred. This can be used to help track down + * which OpenGL ES call is causing an error. * * @see #getDebugFlags * @see #setDebugFlags @@ -194,7 +197,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback public final static int DEBUG_CHECK_GL_ERROR = 1; /** - * Log GL calls to the system log at "verbose" level with tag "GLSurfaceView". + * Log GL calls to the system log at "verbose" level with tag + * "GLSurfaceView". * * @see #getDebugFlags * @see #setDebugFlags @@ -202,8 +206,11 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback public final static int DEBUG_LOG_GL_CALLS = 2; /** - * Standard View constructor. In order to render something, you must call {@link #setRenderer} to register a - * renderer. + * Standard View constructor. In order to render something, you must call + * {@link #setRenderer} to register a renderer. + * + * @param context + * ... */ public GLSurfaceView(Context context) { super(context); @@ -211,8 +218,13 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** - * Standard View constructor. In order to render something, you must call {@link #setRenderer} to register a - * renderer. + * Standard View constructor. In order to render something, you must call + * {@link #setRenderer} to register a renderer. + * + * @param context + * .... + * @param attrs + * ... */ public GLSurfaceView(Context context, AttributeSet attrs) { super(context, attrs); @@ -247,9 +259,11 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** - * Set the glWrapper. If the glWrapper is not null, its {@link GLWrapper#wrap(GL)} method is called whenever a - * surface is created. A GLWrapper can be used to wrap the GL object that's passed to the renderer. Wrapping a GL - * object enables examining and modifying the behavior of the GL calls made by the renderer. + * Set the glWrapper. If the glWrapper is not null, its + * {@link GLWrapper#wrap(GL)} method is called whenever a surface is + * created. A GLWrapper can be used to wrap the GL object that's passed to + * the renderer. Wrapping a GL object enables examining and modifying the + * behavior of the GL calls made by the renderer. * <p> * Wrapping is typically used for debugging purposes. * <p> @@ -263,8 +277,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** - * Set the debug flags to a new value. The value is constructed by OR-together zero or more of the DEBUG_CHECK_* - * constants. The debug flags take effect whenever a surface is created. The default value is zero. + * Set the debug flags to a new value. The value is constructed by + * OR-together zero or more of the DEBUG_CHECK_* constants. The debug flags + * take effect whenever a surface is created. The default value is zero. * * @param debugFlags * the new debug flags @@ -285,15 +300,18 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** - * Control whether the EGL context is preserved when the GLSurfaceView is paused and resumed. + * Control whether the EGL context is preserved when the GLSurfaceView is + * paused and resumed. * <p> - * If set to true, then the EGL context may be preserved when the GLSurfaceView is paused. Whether the EGL context - * is actually preserved or not depends upon whether the Android device that the program is running on can support - * an arbitrary number of EGL contexts or not. Devices that can only support a limited number of EGL contexts must - * release the EGL context in order to allow multiple applications to share the GPU. + * If set to true, then the EGL context may be preserved when the + * GLSurfaceView is paused. Whether the EGL context is actually preserved or + * not depends upon whether the Android device that the program is running + * on can support an arbitrary number of EGL contexts or not. Devices that + * can only support a limited number of EGL contexts must release the EGL + * context in order to allow multiple applications to share the GPU. * <p> - * If set to false, the EGL context will be released when the GLSurfaceView is paused, and recreated when the - * GLSurfaceView is resumed. + * If set to false, the EGL context will be released when the GLSurfaceView + * is paused, and recreated when the GLSurfaceView is resumed. * <p> * The default is false. * @@ -312,19 +330,22 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** - * Set the renderer associated with this view. Also starts the thread that will call the renderer, which in turn - * causes the rendering to start. + * Set the renderer associated with this view. Also starts the thread that + * will call the renderer, which in turn causes the rendering to start. * <p> - * This method should be called once and only once in the life-cycle of a GLSurfaceView. + * This method should be called once and only once in the life-cycle of a + * GLSurfaceView. * <p> - * The following GLSurfaceView methods can only be called <em>before</em> setRenderer is called: + * The following GLSurfaceView methods can only be called <em>before</em> + * setRenderer is called: * <ul> * <li>{@link #setEGLConfigChooser(boolean)} * <li>{@link #setEGLConfigChooser(EGLConfigChooser)} * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)} * </ul> * <p> - * The following GLSurfaceView methods can only be called <em>after</em> setRenderer is called: + * The following GLSurfaceView methods can only be called <em>after</em> + * setRenderer is called: * <ul> * <li>{@link #getRenderMode()} * <li>{@link #onPause()} @@ -356,10 +377,11 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback /** * Install a custom EGLContextFactory. * <p> - * If this method is called, it must be called before {@link #setRenderer(Renderer)} is called. + * If this method is called, it must be called before + * {@link #setRenderer(Renderer)} is called. * <p> - * If this method is not called, then by default a context will be created with no shared context and with a null - * attribute list. + * If this method is not called, then by default a context will be created + * with no shared context and with a null attribute list. */ public void setEGLContextFactory(EGLContextFactory factory) { checkRenderThreadState(); @@ -369,9 +391,11 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback /** * Install a custom EGLWindowSurfaceFactory. * <p> - * If this method is called, it must be called before {@link #setRenderer(Renderer)} is called. + * If this method is called, it must be called before + * {@link #setRenderer(Renderer)} is called. * <p> - * If this method is not called, then by default a window surface will be created with a null attribute list. + * If this method is not called, then by default a window surface will be + * created with a null attribute list. */ public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) { checkRenderThreadState(); @@ -381,10 +405,12 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback /** * Install a custom EGLConfigChooser. * <p> - * If this method is called, it must be called before {@link #setRenderer(Renderer)} is called. + * If this method is called, it must be called before + * {@link #setRenderer(Renderer)} is called. * <p> - * If no setEGLConfigChooser method is called, then by default the view will choose an EGLConfig that is compatible - * with the current android.view.Surface, with a depth buffer depth of at least 16 bits. + * If no setEGLConfigChooser method is called, then by default the view will + * choose an EGLConfig that is compatible with the current + * android.view.Surface, with a depth buffer depth of at least 16 bits. * * @param configChooser */ @@ -394,13 +420,15 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** - * Install a config chooser which will choose a config as close to 16-bit RGB as possible, with or without an - * optional depth buffer as close to 16-bits as possible. + * Install a config chooser which will choose a config as close to 16-bit + * RGB as possible, with or without an optional depth buffer as close to + * 16-bits as possible. * <p> - * If this method is called, it must be called before {@link #setRenderer(Renderer)} is called. + * If this method is called, it must be called before + * {@link #setRenderer(Renderer)} is called. * <p> - * If no setEGLConfigChooser method is called, then by default the view will choose an RGB_565 surface with a depth - * buffer depth of at least 16 bits. + * If no setEGLConfigChooser method is called, then by default the view will + * choose an RGB_565 surface with a depth buffer depth of at least 16 bits. * * @param needDepth */ @@ -409,13 +437,15 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** - * Install a config chooser which will choose a config with at least the specified depthSize and stencilSize, and - * exactly the specified redSize, greenSize, blueSize and alphaSize. + * Install a config chooser which will choose a config with at least the + * specified depthSize and stencilSize, and exactly the specified redSize, + * greenSize, blueSize and alphaSize. * <p> - * If this method is called, it must be called before {@link #setRenderer(Renderer)} is called. + * If this method is called, it must be called before + * {@link #setRenderer(Renderer)} is called. * <p> - * If no setEGLConfigChooser method is called, then by default the view will choose an RGB_565 surface with a depth - * buffer depth of at least 16 bits. + * If no setEGLConfigChooser method is called, then by default the view will + * choose an RGB_565 surface with a depth buffer depth of at least 16 bits. */ public void setEGLConfigChooser(int redSize, int greenSize, int blueSize, int alphaSize, int depthSize, int stencilSize) { @@ -424,7 +454,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** - * Inform the default EGLContextFactory and default EGLConfigChooser which EGLContext client version to pick. + * Inform the default EGLContextFactory and default EGLConfigChooser which + * EGLContext client version to pick. * <p> * Use this method to create an OpenGL ES 2.0-compatible context. Example: * @@ -436,19 +467,24 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback * } * </pre> * <p> - * Note: Activities which require OpenGL ES 2.0 should indicate this by setting @lt;uses-feature - * android:glEsVersion="0x00020000" /> in the activity's AndroidManifest.xml file. + * Note: Activities which require OpenGL ES 2.0 should indicate this by + * setting @lt;uses-feature android:glEsVersion="0x00020000" /> in the + * activity's AndroidManifest.xml file. * <p> - * If this method is called, it must be called before {@link #setRenderer(Renderer)} is called. + * If this method is called, it must be called before + * {@link #setRenderer(Renderer)} is called. * <p> - * This method only affects the behavior of the default EGLContexFactory and the default EGLConfigChooser. If - * {@link #setEGLContextFactory(EGLContextFactory)} has been called, then the supplied EGLContextFactory is - * responsible for creating an OpenGL ES 2.0-compatible context. If {@link #setEGLConfigChooser(EGLConfigChooser)} - * has been called, then the supplied EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible - * config. + * This method only affects the behavior of the default EGLContexFactory and + * the default EGLConfigChooser. If + * {@link #setEGLContextFactory(EGLContextFactory)} has been called, then + * the supplied EGLContextFactory is responsible for creating an OpenGL ES + * 2.0-compatible context. If {@link #setEGLConfigChooser(EGLConfigChooser)} + * has been called, then the supplied EGLConfigChooser is responsible for + * choosing an OpenGL ES 2.0-compatible config. * * @param version - * The EGLContext client version to choose. Use 2 for OpenGL ES 2.0 + * The EGLContext client version to choose. Use 2 for OpenGL ES + * 2.0 */ public void setEGLContextClientVersion(int version) { checkRenderThreadState(); @@ -456,12 +492,15 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** - * Set the rendering mode. When renderMode is RENDERMODE_CONTINUOUSLY, the renderer is called repeatedly to - * re-render the scene. When renderMode is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface is - * created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY. + * Set the rendering mode. When renderMode is RENDERMODE_CONTINUOUSLY, the + * renderer is called repeatedly to re-render the scene. When renderMode is + * RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface is + * created, or when {@link #requestRender} is called. Defaults to + * RENDERMODE_CONTINUOUSLY. * <p> - * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance by allowing the GPU and CPU - * to idle when the view does not need to be updated. + * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system + * performance by allowing the GPU and CPU to idle when the view does not + * need to be updated. * <p> * This method can only be called after {@link #setRenderer(Renderer)} * @@ -475,7 +514,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** - * Get the current rendering mode. May be called from any thread. Must not be called before a renderer has been set. + * Get the current rendering mode. May be called from any thread. Must not + * be called before a renderer has been set. * * @return the current rendering mode. * @see #RENDERMODE_CONTINUOUSLY @@ -486,25 +526,26 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** - * Request that the renderer render a frame. This method is typically used when the render mode has been set to - * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand. May be called from any thread. Must - * not be called before a renderer has been set. + * Request that the renderer render a frame. This method is typically used + * when the render mode has been set to {@link #RENDERMODE_WHEN_DIRTY}, so + * that frames are only rendered on demand. May be called from any thread. + * Must not be called before a renderer has been set. */ public void requestRender() { mGLThread.requestRender(); } /** - * This method is part of the SurfaceHolder.Callback interface, and is not normally called or subclassed by clients - * of GLSurfaceView. + * This method is part of the SurfaceHolder.Callback interface, and is not + * normally called or subclassed by clients of GLSurfaceView. */ public void surfaceCreated(SurfaceHolder holder) { mGLThread.surfaceCreated(); } /** - * This method is part of the SurfaceHolder.Callback interface, and is not normally called or subclassed by clients - * of GLSurfaceView. + * This method is part of the SurfaceHolder.Callback interface, and is not + * normally called or subclassed by clients of GLSurfaceView. */ public void surfaceDestroyed(SurfaceHolder holder) { // Surface will be destroyed when we return @@ -512,33 +553,37 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** - * This method is part of the SurfaceHolder.Callback interface, and is not normally called or subclassed by clients - * of GLSurfaceView. + * This method is part of the SurfaceHolder.Callback interface, and is not + * normally called or subclassed by clients of GLSurfaceView. */ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { mGLThread.onWindowResize(w, h); } /** - * Inform the view that the activity is paused. The owner of this view must call this method when the activity is - * paused. Calling this method will pause the rendering thread. Must not be called before a renderer has been set. + * Inform the view that the activity is paused. The owner of this view must + * call this method when the activity is paused. Calling this method will + * pause the rendering thread. Must not be called before a renderer has been + * set. */ public void onPause() { mGLThread.onPause(); } /** - * Inform the view that the activity is resumed. The owner of this view must call this method when the activity is - * resumed. Calling this method will recreate the OpenGL display and resume the rendering thread. Must not be called - * before a renderer has been set. + * Inform the view that the activity is resumed. The owner of this view must + * call this method when the activity is resumed. Calling this method will + * recreate the OpenGL display and resume the rendering thread. Must not be + * called before a renderer has been set. */ public void onResume() { mGLThread.onResume(); } /** - * Queue a runnable to be run on the GL rendering thread. This can be used to communicate with the Renderer on the - * rendering thread. Must not be called before a renderer has been set. + * Queue a runnable to be run on the GL rendering thread. This can be used + * to communicate with the Renderer on the rendering thread. Must not be + * called before a renderer has been set. * * @param r * the runnable to be run on the GL rendering thread. @@ -548,8 +593,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** - * This method is used as part of the View class and is not normally called or subclassed by clients of - * GLSurfaceView. + * This method is used as part of the View class and is not normally called + * or subclassed by clients of GLSurfaceView. */ @Override protected void onAttachedToWindow() { @@ -572,8 +617,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** - * This method is used as part of the View class and is not normally called or subclassed by clients of - * GLSurfaceView. Must not be called before a renderer has been set. + * This method is used as part of the View class and is not normally called + * or subclassed by clients of GLSurfaceView. Must not be called before a + * renderer has been set. */ @Override protected void onDetachedFromWindow() { @@ -592,10 +638,12 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback /** * An interface used to wrap a GL interface. * <p> - * Typically used for implementing debugging and tracing on top of the default GL interface. You would typically use - * this by creating your own class that implemented all the GL methods by delegating to another GL instance. Then - * you could add your own behavior before or after calling the delegate. All the GLWrapper would do was instantiate - * and return the wrapper GL instance: + * Typically used for implementing debugging and tracing on top of the + * default GL interface. You would typically use this by creating your own + * class that implemented all the GL methods by delegating to another GL + * instance. Then you could add your own behavior before or after calling + * the delegate. All the GLWrapper would do was instantiate and return the + * wrapper GL instance: * * <pre class="prettyprint"> * class MyGLWrapper implements GLWrapper { @@ -616,13 +664,15 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback * * @param gl * a GL interface that is to be wrapped. - * @return either the input argument or another GL object that wraps the input argument. + * @return either the input argument or another GL object that wraps the + * input argument. */ GL wrap(GL gl); } /** - * An interface for customizing the eglCreateContext and eglDestroyContext calls. + * An interface for customizing the eglCreateContext and eglDestroyContext + * calls. * <p> * This interface must be implemented by clients wishing to call * {@link GLSurfaceView#setEGLContextFactory(EGLContextFactory)} @@ -633,9 +683,10 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context); } - private class DefaultContextFactory implements EGLContextFactory { + private final class DefaultContextFactory implements EGLContextFactory { private int EGL_CONTEXT_CLIENT_VERSION = 0x3098; + @Override public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) { int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion, EGL10.EGL_NONE }; @@ -644,6 +695,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback mEGLContextClientVersion != 0 ? attrib_list : null); } + @Override public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) { if (!egl.eglDestroyContext(display, context)) { @@ -659,13 +711,22 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** - * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls. + * An interface for customizing the eglCreateWindowSurface and + * eglDestroySurface calls. * <p> * This interface must be implemented by clients wishing to call * {@link GLSurfaceView#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)} */ public interface EGLWindowSurfaceFactory { /** + * @param egl + * ... + * @param display + * ... + * @param config + * ... + * @param nativeWindow + * ... * @return null if the surface cannot be constructed. */ EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, @@ -676,6 +737,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory { + @Override public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, Object nativeWindow) { EGLSurface result = null; @@ -683,7 +745,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback result = egl.eglCreateWindowSurface(display, config, nativeWindow, null); } catch (IllegalArgumentException e) { // This exception indicates that the surface flinger surface - // is not valid. This can happen if the surface flinger surface has + // is not valid. This can happen if the surface flinger surface + // has // been torn down, but the application has not yet been // notified via SurfaceHolder.Callback.surfaceDestroyed. // In theory the application should be notified first, @@ -693,6 +756,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback return result; } + @Override public void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface) { egl.eglDestroySurface(display, surface); @@ -705,6 +769,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback mConfigSpec = filterConfigSpec(configSpec); } + @Override public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { int[] num_config = new int[1]; if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, @@ -741,7 +806,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback return configSpec; } /* - * We know none of the subclasses define EGL_RENDERABLE_TYPE. And we know the configSpec is well formed. + * We know none of the subclasses define EGL_RENDERABLE_TYPE. And we + * know the configSpec is well formed. */ int len = configSpec.length; int[] newConfigSpec = new int[len + 2]; @@ -754,8 +820,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** - * Choose a configuration with exactly the specified r,g,b,a sizes, and at least the specified depth and stencil - * sizes. + * Choose a configuration with exactly the specified r,g,b,a sizes, and at + * least the specified depth and stencil sizes. */ private class ComponentSizeChooser extends BaseConfigChooser { public ComponentSizeChooser(int redSize, int greenSize, int blueSize, @@ -842,8 +908,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback /** * Initialize EGL for a given configuration spec. - * - * @param configSpec */ public void start() { if (LOG_EGL) { @@ -878,8 +942,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay); /* - * Create an EGL context. We want to do this as rarely as we can, because an EGL context is a somewhat - * heavy object. + * Create an EGL context. We want to do this as rarely as we + * can, because an EGL context is a somewhat heavy object. */ mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig); @@ -897,8 +961,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** - * Create an egl surface for the current SurfaceHolder surface. If a surface already exists, destroy it before - * creating the new surface. + * Create an egl surface for the current SurfaceHolder surface. If a + * surface already exists, destroy it before creating the new surface. * * @return true if the surface was created successfully. */ @@ -946,12 +1010,13 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /* - * Before we can issue GL commands, we need to make sure the context is current and bound to a surface. + * Before we can issue GL commands, we need to make sure the context + * is current and bound to a surface. */ if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { /* - * Could not make the context current, probably because the underlying SurfaceView surface has been - * destroyed. + * Could not make the context current, probably because the + * underlying SurfaceView surface has been destroyed. */ logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError()); return false; @@ -963,7 +1028,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback /** * Create a GL object for the current EGL context. * - * @return + * @return ... */ GL createGL() { @@ -1060,7 +1125,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } public static String formatEglError(String function, int error) { - return function + " failed: "; // + EGLLogWrapper.getErrorString(error); + return function + " failed: "; // + + // EGLLogWrapper.getErrorString(error); } private WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef; @@ -1073,9 +1139,11 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** - * A generic GL Thread. Takes care of initializing EGL and GL. Delegates to a Renderer instance to do the actual - * drawing. Can be configured to render continuously or on request. All potentially blocking synchronization is done - * through the sGLThreadManager object. This avoids multiple-lock ordering issues. + * A generic GL Thread. Takes care of initializing EGL and GL. Delegates to + * a Renderer instance to do the actual drawing. Can be configured to render + * continuously or on request. All potentially blocking synchronization is + * done through the sGLThreadManager object. This avoids multiple-lock + * ordering issues. */ static class GLThread extends Thread { GLThread(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) { @@ -1104,7 +1172,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /* - * This private method should only be called inside a synchronized(sGLThreadManager) block. + * This private method should only be called inside a + * synchronized(sGLThreadManager) block. */ private void stopEglSurfaceLocked() { if (mHaveEglSurface) { @@ -1114,7 +1183,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /* - * This private method should only be called inside a synchronized(sGLThreadManager) block. + * This private method should only be called inside a + * synchronized(sGLThreadManager) block. */ private void stopEglContextLocked() { if (mHaveEglContext) { @@ -1265,7 +1335,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback // Ready to draw? if (readyToDraw()) { - // If we don't have an EGL context, try to acquire one. + // If we don't have an EGL context, try to + // acquire one. if (!mHaveEglContext) { if (askedToReleaseEglContext) { askedToReleaseEglContext = false; @@ -1315,7 +1386,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } } - // By design, this is the only place in a GLThread thread where we wait(). + // By design, this is the only place in a GLThread + // thread where we wait(). if (LOG_THREADS) { Log.i("GLThread", "waiting tid=" + getId() + " mHaveEglContext: " + mHaveEglContext @@ -1402,10 +1474,13 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback lostEglContext = true; break; default: - // Other errors typically mean that the current surface is bad, - // probably because the SurfaceView surface has been destroyed, + // Other errors typically mean that the current + // surface is bad, + // probably because the SurfaceView surface has been + // destroyed, // but we haven't been notified yet. - // Log the error to help developers understand why rendering stopped. + // Log the error to help developers understand why + // rendering stopped. EglHelper.logEglErrorAsWarning("GLThread", "eglSwapBuffers", swapError); @@ -1629,8 +1704,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback private EglHelper mEglHelper; /** - * Set once at thread construction time, nulled out when the parent view is garbage called. This weak reference - * allows the GLSurfaceView to be garbage collected while the GLThread is still alive. + * Set once at thread construction time, nulled out when the parent view + * is garbage called. This weak reference allows the GLSurfaceView to be + * garbage collected while the GLThread is still alive. */ private WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef; @@ -1693,8 +1769,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /* - * Tries once to acquire the right to use an EGL context. Does not block. Requires that we are already in the - * sGLThreadManager monitor when this is called. + * Tries once to acquire the right to use an EGL context. Does not + * block. Requires that we are already in the sGLThreadManager monitor + * when this is called. * @return true if the right to use an EGL context was acquired. */ public boolean tryAcquireEglContextLocked(GLThread thread) { @@ -1718,7 +1795,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /* - * Releases the EGL context. Requires that we are already in the sGLThreadManager monitor when this is called. + * Releases the EGL context. Requires that we are already in the + * sGLThreadManager monitor when this is called. */ public void releaseEglContextLocked(GLThread thread) { if (mEglOwner == thread) { @@ -1777,8 +1855,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** - * This check was required for some pre-Android-3.0 hardware. Android 3.0 provides support for - * hardware-accelerated views, therefore multiple EGL contexts are supported on all Android 3.0+ EGL drivers. + * This check was required for some pre-Android-3.0 hardware. Android + * 3.0 provides support for hardware-accelerated views, therefore + * multiple EGL contexts are supported on all Android 3.0+ EGL drivers. */ private boolean mGLESVersionCheckComplete; private int mGLESVersion; @@ -1791,7 +1870,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback private GLThread mEglOwner; } - private static final GLThreadManager sGLThreadManager = new GLThreadManager(); + /* package */static final GLThreadManager sGLThreadManager = new GLThreadManager(); private final WeakReference<GLSurfaceView> mThisWeakRef = new WeakReference<GLSurfaceView>(this); diff --git a/src/org/oscim/view/MapActivity.java b/src/org/oscim/view/MapActivity.java index 7300a176..d4c2753d 100644 --- a/src/org/oscim/view/MapActivity.java +++ b/src/org/oscim/view/MapActivity.java @@ -17,7 +17,6 @@ package org.oscim.view; import java.io.FileNotFoundException; import org.oscim.core.GeoPoint; -import org.oscim.core.MapPosition; import org.oscim.theme.InternalRenderTheme; import android.app.Activity; diff --git a/src/org/oscim/core/MapPosition.java b/src/org/oscim/view/MapPosition.java similarity index 83% rename from src/org/oscim/core/MapPosition.java rename to src/org/oscim/view/MapPosition.java index 7b81bb1a..94d60e7b 100644 --- a/src/org/oscim/core/MapPosition.java +++ b/src/org/oscim/view/MapPosition.java @@ -12,34 +12,29 @@ * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see <http://www.gnu.org/licenses/>. */ -package org.oscim.core; +package org.oscim.view; + +import org.oscim.core.GeoPoint; +import org.oscim.core.MercatorProjection; + +import android.opengl.Matrix; /** - * A MapPosition represents an immutable pair of {@link GeoPoint} and zoom level. + * A MapPosition Container. */ public class MapPosition { - /** - * The map position. - */ - // public final GeoPoint geoPoint; - public final double lon; - public final double lat; + public double lon; + public double lat; - /** - * The zoom level. - */ - public final byte zoomLevel; + public byte zoomLevel; + public float scale; + public float angle; - /** - * 1.0 - 2.0 scale of current zoomlevel - */ - public final float scale; + public double x; + public double y; - public final float angle; - - public final double x; - public final double y; + public float[] rotation; public MapPosition() { this.zoomLevel = (byte) 1; @@ -51,6 +46,11 @@ public class MapPosition { this.y = MercatorProjection.latitudeToPixelY(this.lat, zoomLevel); } + public void init() { + rotation = new float[16]; + Matrix.setIdentityM(rotation, 0); + } + /** * @param geoPoint * the map position. diff --git a/src/org/oscim/view/MapScaleBar.java b/src/org/oscim/view/MapScaleBar.java index 39915c45..45dada8e 100644 --- a/src/org/oscim/view/MapScaleBar.java +++ b/src/org/oscim/view/MapScaleBar.java @@ -17,7 +17,6 @@ package org.oscim.view; import java.util.HashMap; import java.util.Map; -import org.oscim.core.MapPosition; import org.oscim.core.MercatorProjection; import android.graphics.Bitmap; diff --git a/src/org/oscim/view/MapView.java b/src/org/oscim/view/MapView.java index 06cf09ca..b65b5fd4 100644 --- a/src/org/oscim/view/MapView.java +++ b/src/org/oscim/view/MapView.java @@ -23,7 +23,6 @@ import java.util.Map; import javax.xml.parsers.ParserConfigurationException; import org.oscim.core.GeoPoint; -import org.oscim.core.MapPosition; import org.oscim.core.Tile; import org.oscim.database.IMapDatabase; import org.oscim.database.MapDatabaseFactory; @@ -56,9 +55,9 @@ public class MapView extends FrameLayout { final static String TAG = "MapView"; - public final static boolean debugFrameTime = false; - public final static boolean testRegionZoom = false; - private final boolean mDebugDatabase = false; + public static final boolean debugFrameTime = false; + public static final boolean testRegionZoom = false; + private static final boolean debugDatabase = false; RegionLookup mRegionLookup; @@ -146,7 +145,7 @@ public class MapView extends FrameLayout { for (int i = 0; i < mNumMapWorkers; i++) { IMapDatabase mapDatabase; - if (mDebugDatabase) { + if (debugDatabase) { // mapDatabase = MapDatabaseFactory // .createMapDatabase(MapDatabases.TEST_READER); mapDatabase = MapDatabaseFactory @@ -188,7 +187,7 @@ public class MapView extends FrameLayout { mMapZoomControls = new MapZoomControls(mapActivity, this); mMapZoomControls.setShowMapZoomControls(true); - // enableRotation = true; + enableRotation = true; for (MapWorker worker : mMapWorkers) worker.start(); @@ -353,7 +352,7 @@ public class MapView extends FrameLayout { */ public void setMapDatabase(MapDatabases mapDatabaseType) { - if (mDebugDatabase) + if (debugDatabase) return; TileGenerator tileGenerator; @@ -470,6 +469,9 @@ public class MapView extends FrameLayout { Log.d(TAG, "onSizeChanged" + width + " " + height); super.onSizeChanged(width, height, oldWidth, oldHeight); + if (width != 0 && height != 0) + mMapViewPosition.setViewport(width, height); + mapWorkersProceed(); } diff --git a/src/org/oscim/view/MapViewPosition.java b/src/org/oscim/view/MapViewPosition.java index 387b5108..e463dc78 100644 --- a/src/org/oscim/view/MapViewPosition.java +++ b/src/org/oscim/view/MapViewPosition.java @@ -1,5 +1,5 @@ /* - * Copyright 2010, 2011, 2012 mapsforge.org + * Copyright 2010, 2011, 2012 mapsforge.org, 2012 Hannes Janetzek * * 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 @@ -15,36 +15,36 @@ package org.oscim.view; import org.oscim.core.GeoPoint; -import org.oscim.core.MapPosition; import org.oscim.core.MercatorProjection; import org.oscim.utils.FastMath; +import android.opengl.Matrix; +import android.util.FloatMath; +import android.util.Log; + /** * A MapPosition stores the latitude and longitude coordinate of a MapView * together with its zoom level. */ public class MapViewPosition { - private static final String TAG = "MapViewPosition"; + // private static final String TAG = "MapViewPosition"; - public final static int MAX_ZOOMLEVEL = 16; + public final static int MAX_ZOOMLEVEL = 17; - private final static float MAX_SCALE = 2.0f; - private final static float MIN_SCALE = 1.0f; + private final static float MAX_ANGLE = 20; private final MapView mMapView; private double mLatitude; private double mLongitude; private byte mZoomLevel; + // 1.0 - 2.0 scale per level private float mScale; - private float mRotation; - public float mTilt; - // 2^mZoomLevel * mScale; private float mMapScale; - // private final static float MAP_SIZE = 1000000; - // private final static float MAP_SIZE2 = 1000000 >> 1; + private float mRotation; + public float mTilt; MapViewPosition(MapView mapView) { mMapView = mapView; @@ -58,20 +58,158 @@ public class MapViewPosition { mMapScale = 1; } - // private static double latitudeToMapView(double latitude) { - // double sinLatitude = Math.sin(latitude * (Math.PI / 180)); - // return (0.5 - Math.log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * - // Math.PI)) - // * MAP_SIZE; - // } - // - // public static double longitudeToMapView(double longitude) { - // return (longitude + 180) / 360 * MAP_SIZE; - // } - // - // private static double pixelXToLongitude(double pixelX, byte zoomLevel) { - // return 360 * ((pixelX / ((long) Tile.TILE_SIZE << zoomLevel)) - 0.5); - // } + private float[] mProjMatrix = new float[16]; + private float[] mProjMatrixI = new float[16]; + private float[] mUnprojMatrix = new float[16]; + private float[] mRotateMatrix = new float[16]; + private float[] mTmpMatrix = new float[16]; + + private int mHeight, mWidth; + + void setViewport(int width, int height) { + Matrix.frustumM(mProjMatrix, 0, -0.5f * width, 0.5f * width, + -0.5f * height, 0.5f * height, 1, 2); + + Matrix.translateM(mProjMatrix, 0, 0, 0, -1); + + Matrix.invertM(mProjMatrixI, 0, mProjMatrix, 0); + Matrix.invertM(mUnprojMatrix, 0, mProjMatrix, 0); + + Matrix.setIdentityM(mRotateMatrix, 0); + + mHeight = height; + mWidth = width; + } + + public synchronized boolean getMapPosition(final MapPosition mapPosition, + final float[] coords) { + // if (!isValid()) + // return false; + + // if (mapPosition.lat == mLatitude + // && mapPosition.lon == mLongitude + // && mapPosition.zoomLevel == mZoomLevel + // && mapPosition.scale == mScale + // && mapPosition.angle == mRotation) + // return false; + + mapPosition.lat = mLatitude; + mapPosition.lon = mLongitude; + mapPosition.angle = mRotation; + mapPosition.zoomLevel = mZoomLevel; + mapPosition.scale = mScale; + byte z = mZoomLevel; + mapPosition.x = MercatorProjection.longitudeToPixelX(mLongitude, z); + mapPosition.y = MercatorProjection.latitudeToPixelY(mLatitude, z); + + if (mapPosition.rotation != null) { + // updateMatrix(); + System.arraycopy(mRotateMatrix, 0, mapPosition.rotation, 0, 16); + } + + if (coords == null) + return true; + + // if (mapPosition.rotation == null) + // updateMatrix(); + + // not so sure about this, but works... + float tilt = FloatMath.sin((float) Math.toRadians(mTilt)) * 4; + + unproject(-1, 1, tilt, coords, 0); // top-left + unproject(1, 1, tilt, coords, 2); // top-right + unproject(1, -1, -tilt, coords, 4); // bottom-right + unproject(-1, -1, -tilt, coords, 6); // bottom-left + + return true; + } + + private float[] mv = { 0, 0, 0, 1 }; + private float[] mBBoxCoords = new float[8]; + + private void unproject(float x, float y, float z, float[] coords, int position) { + mv[0] = x; + mv[1] = y; + mv[2] = z - 1; + mv[3] = 1; + + Matrix.multiplyMV(mv, 0, mUnprojMatrix, 0, mv, 0); + + if (mv[3] != 0) { + float w = 1 / mv[3]; + float xx = mv[0] * w; + float yy = mv[1] * w; + + coords[position] = xx; + coords[position + 1] = yy; + } + // else what? + } + + private void updateMatrix() { + Matrix.setRotateM(mRotateMatrix, 0, mRotation, 0, 0, 1); + + // tilt map + float tilt = mTilt; + Matrix.setRotateM(mTmpMatrix, 0, -tilt / (mHeight / 2), 1, 0, 0); + + // apply first rotation, then tilt + Matrix.multiplyMM(mRotateMatrix, 0, mTmpMatrix, 0, mRotateMatrix, 0); + + // get unproject matrix: + // (transpose of rotation is its inverse) + Matrix.transposeM(mTmpMatrix, 0, mRotateMatrix, 0); + // (AB)^-1 = B^-1*A^-1 + Matrix.multiplyMM(mUnprojMatrix, 0, mTmpMatrix, 0, mProjMatrixI, 0); + + } + + /** + * sets viewBox to visible bounding box, (left,top,right,bottom) + * + * @param viewBox + * ... + */ + public synchronized void getViewBox(final float[] viewBox) { + + updateMatrix(); + + float tilt = FloatMath.sin((float) Math.toRadians(mTilt)) * 4; + + unproject(-1, 1, tilt, mBBoxCoords, 0); // top-left + unproject(1, 1, tilt, mBBoxCoords, 2); // top-right + unproject(1, -1, -tilt, mBBoxCoords, 4); // bottom-right + unproject(-1, -1, -tilt, mBBoxCoords, 6); // bottom-left + + byte z = mZoomLevel; + double pixelX = MercatorProjection.longitudeToPixelX(mLongitude, z); + double pixelY = MercatorProjection.latitudeToPixelY(mLatitude, z); + + double dx = pixelX - mBBoxCoords[0] / mScale; + double dy = pixelY - mBBoxCoords[1] / mScale; + double lon = MercatorProjection.pixelXToLongitude(dx, z); + double lat = MercatorProjection.pixelYToLatitude(dy, z); + Log.d(">>>", "bl:" + lon + " " + lat); + + dx = pixelX - mBBoxCoords[2] / mScale; + dy = pixelY - mBBoxCoords[3] / mScale; + lon = MercatorProjection.pixelXToLongitude(dx, z); + lat = MercatorProjection.pixelYToLatitude(dy, z); + Log.d("...", "br:" + lon + " " + lat); + + dx = pixelX - mBBoxCoords[4] / mScale; + dy = pixelY - mBBoxCoords[5] / mScale; + lon = MercatorProjection.pixelXToLongitude(dx, z); + lat = MercatorProjection.pixelYToLatitude(dy, z); + Log.d("...", "tl:" + lon + " " + lat); + + dx = pixelX - mBBoxCoords[6] / mScale; + dy = pixelY - mBBoxCoords[7] / mScale; + lon = MercatorProjection.pixelXToLongitude(dx, z); + lat = MercatorProjection.pixelYToLatitude(dy, z); + Log.d("...", "tr:" + lon + " " + lat); + + } /** * @return the current center point of the MapView. @@ -168,6 +306,15 @@ public class MapViewPosition { return new GeoPoint(latitude, longitude); } + // public static double pixelXToLongitude(double pixelX, byte zoomLevel) { + // return 360 * ((pixelX / ((long) Tile.TILE_SIZE << zoomLevel)) - 0.5); + // } + // + // public static double pixelYToLatitude(double pixelY, byte zoomLevel) { + // double y = 0.5 - (pixelY / ((long) Tile.TILE_SIZE << zoomLevel)); + // return 90 - 360 * Math.atan(Math.exp(-y * (2 * Math.PI))) / Math.PI; + // } + /** * Moves this MapViewPosition by the given amount of pixels. * @@ -187,14 +334,13 @@ public class MapViewPosition { double rad = Math.toRadians(mRotation); double x = dx * Math.cos(rad) + dy * -Math.sin(rad); double y = dx * Math.sin(rad) + dy * Math.cos(rad); + dx = x; + dy = y; + } + + dx = pixelX - dx; + dy = pixelY - dy; - dx = pixelX - x; - dy = pixelY - y; - } - else { - dx = pixelX - dx; - dy = pixelY - dy; - } mLatitude = MercatorProjection.pixelYToLatitude(dy, mZoomLevel); mLatitude = MercatorProjection.limitLatitude(mLatitude); @@ -202,6 +348,8 @@ public class MapViewPosition { mLongitude = MercatorProjection.wrapLongitude(mLongitude); // mLongitude = MercatorProjection.limitLongitude(mLongitude); + + // getViewBox(null); } public synchronized void rotateMap(float angle, float cx, float cy) { @@ -209,14 +357,17 @@ public class MapViewPosition { // Log.d("MapViewPosition", "rotate:" + angle + " " + (mRotation - // angle)); mRotation -= angle; + updateMatrix(); } public void setRotation(float f) { mRotation = f; + updateMatrix(); } public void setTilt(float f) { mTilt = f; + updateMatrix(); } synchronized void setMapCenter(GeoPoint geoPoint) { @@ -251,38 +402,41 @@ public class MapViewPosition { * ... * @param pivotY * ... + * @return true if scale was changed */ - public synchronized void scaleMap(float scale, float pivotX, float pivotY) { - if (pivotY != 0 || pivotY != 0) - moveMap(pivotX * (1.0f - scale), - pivotY * (1.0f - scale)); + public synchronized boolean scaleMap(float scale, float pivotX, float pivotY) { float newScale = mMapScale * scale; int z = FastMath.log2((int) newScale); if (z <= 0 || (z >= MAX_ZOOMLEVEL && mScale >= 8)) - return; + return false; if (z > MAX_ZOOMLEVEL) { // z16 shows everything, just increase scaling if (mScale * scale > 8) - return; + return false; mScale *= scale; mMapScale = newScale; - return; + } else { + mZoomLevel = (byte) z; + mScale = newScale / (1 << z); + mMapScale = newScale; } - mZoomLevel = (byte) z; - mScale = newScale / (1 << z); - mMapScale = newScale; + if (pivotY != 0 || pivotY != 0) + moveMap(pivotX * (1.0f - scale), + pivotY * (1.0f - scale)); + + return true; } public boolean tilt(float moveX) { float tilt = mTilt + moveX; - if (tilt > 25) - tilt = 25; + if (tilt > MAX_ANGLE) + tilt = MAX_ANGLE; else if (tilt < 0) tilt = 0; if (mTilt == tilt) diff --git a/src/org/oscim/view/MapZoomControls.java b/src/org/oscim/view/MapZoomControls.java index b4d056ff..9fcee335 100644 --- a/src/org/oscim/view/MapZoomControls.java +++ b/src/org/oscim/view/MapZoomControls.java @@ -123,8 +123,8 @@ public class MapZoomControls { mShowMapZoomControls = true; mZoomLevelMax = DEFAULT_ZOOM_LEVEL_MAX; mZoomLevelMin = DEFAULT_ZOOM_LEVEL_MIN; - if (!MapView.testRegionZoom) - mZoomControls.setVisibility(View.GONE); + // if (!MapView.testRegionZoom) + mZoomControls.setVisibility(View.GONE); mZoomControlsGravity = DEFAULT_ZOOM_CONTROLS_GRAVITY; mZoomControls.setOnZoomInClickListener(new ZoomInClickListener(this)); @@ -203,12 +203,14 @@ public class MapZoomControls { } /** - * Sets the gravity for the placing of the zoom controls. Supported values are {@link Gravity#TOP}, - * {@link Gravity#CENTER_VERTICAL}, {@link Gravity#BOTTOM}, {@link Gravity#LEFT}, {@link Gravity#CENTER_HORIZONTAL} - * and {@link Gravity#RIGHT}. + * Sets the gravity for the placing of the zoom controls. Supported values + * are {@link Gravity#TOP}, {@link Gravity#CENTER_VERTICAL}, + * {@link Gravity#BOTTOM}, {@link Gravity#LEFT}, + * {@link Gravity#CENTER_HORIZONTAL} and {@link Gravity#RIGHT}. * * @param zoomControlsGravity - * a combination of {@link Gravity} constants describing the desired placement. + * a combination of {@link Gravity} constants describing the + * desired placement. */ public void setZoomControlsGravity(int zoomControlsGravity) { if (mZoomControlsGravity != zoomControlsGravity) { @@ -220,14 +222,16 @@ public class MapZoomControls { /** * Sets the maximum zoom level of the map. * <p> - * The maximum possible zoom level of the MapView depends also on the current {@link TileGenerator}. For example, - * downloading map tiles may only be possible up to a certain zoom level. Setting a higher maximum zoom level has no - * effect in this case. + * The maximum possible zoom level of the MapView depends also on the + * current {@link TileGenerator}. For example, downloading map tiles may + * only be possible up to a certain zoom level. Setting a higher maximum + * zoom level has no effect in this case. * * @param zoomLevelMax * the maximum zoom level. * @throws IllegalArgumentException - * if the maximum zoom level is smaller than the current minimum zoom level. + * if the maximum zoom level is smaller than the current minimum + * zoom level. */ public void setZoomLevelMax(byte zoomLevelMax) { if (zoomLevelMax < mZoomLevelMin) { @@ -242,7 +246,8 @@ public class MapZoomControls { * @param zoomLevelMin * the minimum zoom level. * @throws IllegalArgumentException - * if the minimum zoom level is larger than the current maximum zoom level. + * if the minimum zoom level is larger than the current maximum + * zoom level. */ public void setZoomLevelMin(byte zoomLevelMin) { if (zoomLevelMin > mZoomLevelMax) { diff --git a/src/org/oscim/view/RegionLookup.java b/src/org/oscim/view/RegionLookup.java index 64a94283..ad2f84eb 100644 --- a/src/org/oscim/view/RegionLookup.java +++ b/src/org/oscim/view/RegionLookup.java @@ -23,7 +23,6 @@ import java.util.ArrayList; import java.util.Properties; import org.oscim.core.GeoPoint; -import org.oscim.core.MapPosition; import org.oscim.core.MercatorProjection; import android.os.AsyncTask; diff --git a/src/org/oscim/view/TouchHandler.java b/src/org/oscim/view/TouchHandler.java index 275e9867..9cac217b 100644 --- a/src/org/oscim/view/TouchHandler.java +++ b/src/org/oscim/view/TouchHandler.java @@ -32,20 +32,24 @@ import android.widget.Scroller; * Implementation for multi-touch capable devices. TODO write a AnimationTimer * instead of using CountDownTimer */ -public class TouchHandler { +public class TouchHandler + extends SimpleOnGestureListener + implements ScaleGestureDetector.OnScaleGestureListener { + + private static final float SCALE_DURATION = 450; private static final int INVALID_POINTER_ID = -1; - /* package */final MapView mMapView; - /* package */final MapViewPosition mMapPosition; - /* package */final DecelerateInterpolator mInterpolator = new DecelerateInterpolator(); - /* package */boolean mBeginScale; - /* package */float mSumScale; + private final MapView mMapView; + private final MapViewPosition mMapPosition; + private final DecelerateInterpolator mInterpolator = new DecelerateInterpolator(); + private boolean mBeginScale; + private float mSumScale; private final float mMapMoveDelta; private boolean mMoveStart; private boolean mBeginRotate; - /* package */float mPosX; - /* package */float mPosY; + private float mPosX; + private float mPosY; private double mAngle; private int mActivePointerId; @@ -65,9 +69,47 @@ public class TouchHandler { mMapPosition = mapView.getMapPosition(); mMapMoveDelta = viewConfiguration.getScaledTouchSlop(); mActivePointerId = INVALID_POINTER_ID; - mScaleGestureDetector = new ScaleGestureDetector(context, new ScaleListener()); - mGestureDetector = new GestureDetector(context, new MapGestureDetector()); + mScaleGestureDetector = new ScaleGestureDetector(context, this); + mGestureDetector = new GestureDetector(context, this); + mScroller = new Scroller(mMapView.getContext(), + new android.view.animation.LinearInterpolator()); + } + + /** + * @param event + * ... + * @return ... + */ + public boolean handleMotionEvent(MotionEvent event) { + + // workaround for a bug in the ScaleGestureDetector, see Android issue + // #12976 + // if (event.getAction() != MotionEvent.ACTION_MOVE + // || event.getPointerCount() > 1) { + mScaleGestureDetector.onTouchEvent(event); + // } + + if (!mScaling) + mGestureDetector.onTouchEvent(event); + + int action = getAction(event); + boolean ret = false; + if (action == MotionEvent.ACTION_DOWN) { + ret = onActionDown(event); + } else if (action == MotionEvent.ACTION_MOVE) { + ret = onActionMove(event); + } else if (action == MotionEvent.ACTION_UP) { + ret = onActionUp(event); + } else if (action == MotionEvent.ACTION_CANCEL) { + ret = onActionCancel(); + } else if (action == MotionEvent.ACTION_POINTER_DOWN) { + return onActionPointerDown(event); + } else if (action == MotionEvent.ACTION_POINTER_UP) { + ret = onActionPointerUp(event); + } + + return ret; } private static int getAction(MotionEvent motionEvent) { @@ -131,48 +173,46 @@ public class TouchHandler { return true; } - if (mMapView.enableRotation) { - if (multi > 0) { - double x1 = event.getX(0); - double x2 = event.getX(1); - double y1 = event.getY(0); - double y2 = event.getY(1); + if (!mMapView.enableRotation || multi < 1) + return true; - double dx = x1 - x2; - double dy = y1 - y2; + double x1 = event.getX(0); + double x2 = event.getX(1); + double y1 = event.getY(0); + double y2 = event.getY(1); - double rad = Math.atan2(dy, dx); - double r = rad - mAngle; + double dx = x1 - x2; + double dy = y1 - y2; - if (!mBeginRotate && Math.abs(dy) < 80) { - if (mMapPosition.tilt(moveY / 4)) { - mMapView.redrawMap(); - return true; - } - } - - if (!mBeginRotate && !mBeginScale) { - if (r > 0.02 || r < -0.02) - mBeginRotate = true; - } else if (mBeginRotate) { - double rsin = Math.sin(r); - double rcos = Math.cos(r); - - // focus point relative to center - double cx = (mMapView.getWidth() >> 1) - (x1 + x2) / 2; - double cy = (mMapView.getHeight() >> 1) - (y1 + y2) / 2; - - float x = (float) (cx * rcos + cy * -rsin - cx); - float y = (float) (cx * rsin + cy * rcos - cy); - - mMapPosition.rotateMap((float) Math.toDegrees(rad - mAngle), x, y); - mAngle = rad; - mMapView.redrawMap(); - } + double rad = Math.atan2(dy, dx); + double r = rad - mAngle; + if (!mBeginRotate && Math.abs(dy) < 80) { + if (mMapPosition.tilt(moveY / 4)) { + mMapView.redrawMap(); + return true; } } + if (!mBeginRotate && !mBeginScale) { + if (r > 0.02 || r < -0.02) + mBeginRotate = true; + } else if (mBeginRotate) { + double rsin = Math.sin(r); + double rcos = Math.cos(r); + + // focus point relative to center + double cx = (mMapView.getWidth() >> 1) - (x1 + x2) / 2; + double cy = (mMapView.getHeight() >> 1) - (y1 + y2) / 2; + + float x = (float) (cx * rcos + cy * -rsin - cx); + float y = (float) (cx * rsin + cy * rcos - cy); + + mMapPosition.rotateMap((float) Math.toDegrees(rad - mAngle), x, y); + mAngle = rad; + mMapView.redrawMap(); + } + return true; } @@ -225,285 +265,240 @@ public class TouchHandler { return true; } - /** - * @param event - * ... - * @return ... - */ - public boolean handleMotionEvent(MotionEvent event) { + /******************* SimpleOnGestureListener *******************/ - // workaround for a bug in the ScaleGestureDetector, see Android issue - // #12976 - // if (event.getAction() != MotionEvent.ACTION_MOVE - // || event.getPointerCount() > 1) { - mScaleGestureDetector.onTouchEvent(event); - // } + private Scroller mScroller; + private float mScrollX, mScrollY; + private boolean fling = false; - if (!mScaling) - mGestureDetector.onTouchEvent(event); - - int action = getAction(event); - boolean ret = false; - if (action == MotionEvent.ACTION_DOWN) { - ret = onActionDown(event); - } else if (action == MotionEvent.ACTION_MOVE) { - ret = onActionMove(event); - } else if (action == MotionEvent.ACTION_UP) { - ret = onActionUp(event); - } else if (action == MotionEvent.ACTION_CANCEL) { - ret = onActionCancel(); - } else if (action == MotionEvent.ACTION_POINTER_DOWN) { - return onActionPointerDown(event); - } else if (action == MotionEvent.ACTION_POINTER_UP) { - ret = onActionPointerUp(event); - } - - return ret; - } - - class MapGestureDetector extends SimpleOnGestureListener { - private Scroller mScroller; - private float mScrollX, mScrollY, mPrevScale; - private CountDownTimer mTimer = null; - private boolean fling = false; - - public MapGestureDetector() { - mScroller = new Scroller(mMapView.getContext(), - new android.view.animation.LinearInterpolator()); - } - - @Override - public boolean onDown(MotionEvent e) { - if (fling) { - mScroller.forceFinished(true); - - if (mTimer != null) { - mTimer.cancel(); - mTimer = null; - } - fling = false; - } - // Log.d("mapsforge", "onDown"); - - return true; - } - - boolean scroll() { - if (mScroller.isFinished()) { - return false; - } - mScroller.computeScrollOffset(); - - float moveX = mScroller.getCurrX() - mScrollX; - float moveY = mScroller.getCurrY() - mScrollY; - - if (moveX >= 1 || moveY >= 1 || moveX <= -1 || moveY <= -1) { - mMapPosition.moveMap(moveX, moveY); - mMapView.redrawMap(); - mScrollX = mScroller.getCurrX(); - mScrollY = mScroller.getCurrY(); - } - return true; - } - - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, - float velocityY) { - int w = Tile.TILE_SIZE * 20; - int h = Tile.TILE_SIZE * 20; - mScrollX = 0; - mScrollY = 0; + @Override + public boolean onDown(MotionEvent e) { + if (fling) { + mScroller.forceFinished(true); if (mTimer != null) { mTimer.cancel(); mTimer = null; } + fling = false; + } + // Log.d("mapsforge", "onDown"); - mScroller.fling(0, 0, Math.round(velocityX) / 2, Math.round(velocityY) / 2, - -w, w, -h, h); + return true; + } - // animate for two seconds - mTimer = new CountDownTimer(1500, 50) { + boolean scroll() { + if (mScroller.isFinished()) { + return false; + } + mScroller.computeScrollOffset(); + + float moveX = mScroller.getCurrX() - mScrollX; + float moveY = mScroller.getCurrY() - mScrollY; + + if (moveX >= 1 || moveY >= 1 || moveX <= -1 || moveY <= -1) { + mMapPosition.moveMap(moveX, moveY); + mMapView.redrawMap(); + mScrollX = mScroller.getCurrX(); + mScrollY = mScroller.getCurrY(); + } + return true; + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, + float velocityY) { + int w = Tile.TILE_SIZE * 20; + int h = Tile.TILE_SIZE * 20; + mScrollX = 0; + mScrollY = 0; + + if (mTimer != null) { + mTimer.cancel(); + mTimer = null; + } + + mScroller.fling(0, 0, Math.round(velocityX) / 2, Math.round(velocityY) / 2, + -w, w, -h, h); + + // animate for two seconds + mTimer = new CountDownTimer(1500, 50) { + @Override + public void onTick(long tick) { + scroll(); + } + + @Override + public void onFinish() { + // do nothing + } + }.start(); + fling = true; + return true; + } + + @Override + public void onLongPress(MotionEvent e) { + if (MapView.testRegionZoom) { + Log.d("mapsforge", "long press"); + mMapView.mRegionLookup.updateRegion(-1, null); + } + } + + boolean scale2(long tick) { + + fling = true; + if (mPrevScale >= 1) + return false; + float adv = (SCALE_DURATION - tick) / SCALE_DURATION; + adv = mInterpolator.getInterpolation(adv); + + float scale = adv - mPrevScale; + mPrevScale += scale; + scale += 1; + adv += 1; + + if (scale > 1) { + mMapPosition.scaleMap(scale, mScrollX / adv, mScrollY / adv); + mMapView.redrawMap(); + } + + return true; + } + + @Override + public boolean onDoubleTap(MotionEvent e) { + if (MapView.testRegionZoom) { + Log.d("mapsforge", "double tap"); + + mMapView.mRegionLookup.updateRegion(1, + mMapPosition.getOffsetPoint(mPosX, mPosY)); + } else { + mScrollX = (e.getX(0) - (mMapView.getWidth() >> 1)) * 2f; + mScrollY = (e.getY(0) - (mMapView.getHeight() >> 1)) * 2f; + mPrevScale = 0; + + mTimer = new CountDownTimer((int) SCALE_DURATION, 30) { @Override public void onTick(long tick) { - scroll(); + scale2(tick); } @Override public void onFinish() { - // do nothing + scale(0); } }.start(); - fling = true; - return true; - } - - @Override - public void onLongPress(MotionEvent e) { - if (MapView.testRegionZoom) { - Log.d("mapsforge", "long press"); - mMapView.mRegionLookup.updateRegion(-1, null); - } - } - - private final float mScaleDuration = 300; - - boolean scale(long tick) { - - fling = true; - if (mPrevScale >= 1) - return false; - float adv = (mScaleDuration - tick) / mScaleDuration; - adv = mInterpolator.getInterpolation(adv); - - float scale = adv - mPrevScale; - mPrevScale += scale; - scale += 1; - adv += 1; - - if (scale > 1) { - mMapPosition.scaleMap(scale, mScrollX / adv, mScrollY / adv); - mMapView.redrawMap(); - } - - return true; - } - - @Override - public boolean onDoubleTap(MotionEvent e) { - if (MapView.testRegionZoom) { - Log.d("mapsforge", "double tap"); - - mMapView.mRegionLookup.updateRegion(1, - mMapPosition.getOffsetPoint(mPosX, mPosY)); - } else { - mScrollX = (e.getX(0) - (mMapView.getWidth() >> 1)) * 2f; - mScrollY = (e.getY(0) - (mMapView.getHeight() >> 1)) * 2f; - mPrevScale = 0; - - mTimer = new CountDownTimer((int) mScaleDuration, 30) { - @Override - public void onTick(long tick) { - scale(tick); - } - - @Override - public void onFinish() { - scale(0); - } - }.start(); - } - return true; } + return true; } - class ScaleListener implements ScaleGestureDetector.OnScaleGestureListener { - private float mCenterX; - private float mCenterY; - private float mFocusX; - private float mFocusY; - private long mTimeStart; - private long mTimeEnd; + /******************* ScaleListener *******************/ - @Override - public boolean onScale(ScaleGestureDetector gd) { + private float mCenterX; + private float mCenterY; + private float mFocusX; + private float mFocusY; + private long mTimeStart; + private long mTimeEnd; - float scale = gd.getScaleFactor(); - mFocusX = gd.getFocusX() - mCenterX; - mFocusY = gd.getFocusY() - mCenterY; + @Override + public boolean onScale(ScaleGestureDetector gd) { - mSumScale *= scale; + float scale = gd.getScaleFactor(); + mFocusX = gd.getFocusX() - mCenterX; + mFocusY = gd.getFocusY() - mCenterY; - mTimeEnd = SystemClock.elapsedRealtime(); + mSumScale *= scale; - if (!mBeginScale) { - if (mTimeEnd - mTimeStart > 150 || mSumScale > 1.1 || mSumScale < 0.9) { - mBeginScale = true; - scale = mSumScale; + mTimeEnd = SystemClock.elapsedRealtime(); + + if (!mBeginScale) { + if (mTimeEnd - mTimeStart > 150 || mSumScale > 1.1 || mSumScale < 0.9) { + mBeginScale = true; + scale = mSumScale; + } + else + return true; + } + + if (mMapPosition.scaleMap(scale, mFocusX, mFocusY)) + mMapView.redrawMap(); + + return true; + } + + @Override + public boolean onScaleBegin(ScaleGestureDetector gd) { + mTimeEnd = mTimeStart = SystemClock.elapsedRealtime(); + mSumScale = 1; + mBeginScale = false; + mCenterX = mMapView.getWidth() >> 1; + mCenterY = mMapView.getHeight() >> 1; + + if (mTimer != null) { + mTimer.cancel(); + mTimer = null; + } + return true; + } + + @Override + public void onScaleEnd(ScaleGestureDetector gd) { + // Log.d("ScaleListener", "Sum " + mSumScale + " " + (mTimeEnd - + // mTimeStart)); + + if (mTimer == null && mTimeEnd - mTimeStart < 150 + && (mSumScale < 0.99 || mSumScale > 1.01)) { + + mPrevScale = 0; + + mZooutOut = mSumScale < 0.99; + + mTimer = new CountDownTimer((int) SCALE_DURATION, 15) { + @Override + public void onTick(long tick) { + scale(tick); } - else - return true; - } - mMapPosition.scaleMap(scale, mFocusX, mFocusY); - mMapView.redrawMap(); + @Override + public void onFinish() { + scale(0); - return true; + } + }.start(); + } + mBeginScale = false; + } + + private float mPrevScale; + private CountDownTimer mTimer; + boolean mZooutOut; + + boolean scale(long tick) { + + if (mPrevScale >= 1) { + mTimer = null; + return false; } - @Override - public boolean onScaleBegin(ScaleGestureDetector gd) { - mTimeEnd = mTimeStart = SystemClock.elapsedRealtime(); - mSumScale = 1; - mBeginScale = false; - mCenterX = mMapView.getWidth() >> 1; - mCenterY = mMapView.getHeight() >> 1; + float adv = (SCALE_DURATION - tick) / SCALE_DURATION; + adv = mInterpolator.getInterpolation(adv); - if (mTimer != null) { - mTimer.cancel(); - mTimer = null; - } - return true; + float scale = adv - mPrevScale; + mPrevScale += scale; + + if (mZooutOut) { + mMapPosition.scaleMap(1 - scale, 0, 0); + } else { + mMapPosition.scaleMap(1 + scale, mFocusX, mFocusY); } - @Override - public void onScaleEnd(ScaleGestureDetector gd) { - // Log.d("ScaleListener", "Sum " + mSumScale + " " + (mTimeEnd - - // mTimeStart)); + mMapView.redrawMap(); - if (mTimer == null && mTimeEnd - mTimeStart < 150 - && (mSumScale < 0.99 || mSumScale > 1.01)) { + if (tick == 0) + mTimer = null; - mPrevScale = 0; - - mZooutOut = mSumScale < 0.99; - - mTimer = new CountDownTimer((int) mScaleDuration, 15) { - @Override - public void onTick(long tick) { - scale(tick); - } - - @Override - public void onFinish() { - scale(0); - - } - }.start(); - } - mBeginScale = false; - } - - private float mPrevScale; - private CountDownTimer mTimer; - boolean mZooutOut; - private final float mScaleDuration = 450; - - boolean scale(long tick) { - - if (mPrevScale >= 1) { - mTimer = null; - return false; - } - - float adv = (mScaleDuration - tick) / mScaleDuration; - adv = mInterpolator.getInterpolation(adv); - - float scale = adv - mPrevScale; - mPrevScale += scale; - - if (mZooutOut) { - mMapPosition.scaleMap(1 - scale, 0, 0); - } else { - mMapPosition.scaleMap(1 + scale, mFocusX, mFocusY); - } - - mMapView.redrawMap(); - - if (tick == 0) - mTimer = null; - - return true; - } + return true; } } diff --git a/src/org/oscim/view/generator/MapWorker.java b/src/org/oscim/view/generator/MapWorker.java index 1fdbb1d3..d54f2cae 100644 --- a/src/org/oscim/view/generator/MapWorker.java +++ b/src/org/oscim/view/generator/MapWorker.java @@ -15,12 +15,12 @@ package org.oscim.view.generator; import org.oscim.utils.PausableThread; -import org.oscim.view.renderer.TileGenerator; import org.oscim.view.renderer.MapRenderer; +import org.oscim.view.renderer.TileGenerator; /** - * A MapWorker uses a {@link IMapGenerator} to generate map tiles. It runs in a separate thread to avoid blocking the UI - * thread. + * A MapWorker uses a {@link TileGenerator} to generate map tiles. It runs in a + * separate thread to avoid blocking the UI thread. */ public class MapWorker extends PausableThread { private final String THREAD_NAME; @@ -28,11 +28,11 @@ public class MapWorker extends PausableThread { private final TileGenerator mMapGenerator; private final MapRenderer mMapRenderer; - // private final int mPrio; - /** * @param id * thread id + * @param jobQueue + * ... * @param tileGenerator * ... * @param mapRenderer @@ -46,7 +46,6 @@ public class MapWorker extends PausableThread { mMapRenderer = mapRenderer; THREAD_NAME = "MapWorker" + id; - // mPrio = Math.max(Thread.MIN_PRIORITY + id, Thread.NORM_PRIORITY - 1); } public TileGenerator getMapGenerator() { @@ -85,7 +84,6 @@ public class MapWorker extends PausableThread { @Override protected int getThreadPriority() { return (Thread.NORM_PRIORITY + Thread.MIN_PRIORITY) / 3; - // return mPrio; } @Override diff --git a/src/org/oscim/view/renderer/GLRenderer.java b/src/org/oscim/view/renderer/GLRenderer.java index 5f6dea6d..f0888691 100644 --- a/src/org/oscim/view/renderer/GLRenderer.java +++ b/src/org/oscim/view/renderer/GLRenderer.java @@ -32,11 +32,13 @@ import java.util.concurrent.locks.ReentrantLock; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; -import org.oscim.core.MapPosition; import org.oscim.core.Tile; import org.oscim.theme.RenderTheme; import org.oscim.utils.GlUtils; +import org.oscim.view.MapPosition; import org.oscim.view.MapView; +import org.oscim.view.MapViewPosition; +import org.oscim.view.renderer.MapRenderer.TilesData; import android.opengl.GLES20; import android.opengl.GLSurfaceView; @@ -59,6 +61,10 @@ public class GLRenderer implements GLSurfaceView.Renderer { static int CACHE_TILES = CACHE_TILES_MAX; private final MapView mMapView; + private final MapViewPosition mMapViewPosition; + + private static final MapPosition mMapPosition = new MapPosition(); + private static ArrayList<VertexBufferObject> mVBOs; private static int mWidth, mHeight; @@ -71,12 +77,8 @@ public class GLRenderer implements GLSurfaceView.Renderer { private static int mBufferMemoryUsage; private static float[] mMVPMatrix = new float[16]; - private static float[] mRotateMatrix = new float[16]; - private static float[] mTmpMatrix = new float[16]; - private static float[] mTmp2Matrix = new float[16]; - private static float[] mProjMatrix = new float[16]; - private static float[] mProjMatrixI = new float[16]; + private static float[] mTileCoords = new float[8]; // mNextTiles is set by TileLoader and swapped with // mDrawTiles in onDrawFrame in GL thread. @@ -86,8 +88,6 @@ public class GLRenderer implements GLSurfaceView.Renderer { // changed. used in onDrawFrame to flip mNextTiles/mDrawTiles private static boolean mUpdateTiles; - private static MapPosition mCurPosition; - private float[] mClearColor = null; // number of tiles drawn in one frame @@ -98,17 +98,6 @@ public class GLRenderer implements GLSurfaceView.Renderer { // lock to synchronize Main- and GL-Thread static ReentrantLock tilelock = new ReentrantLock(); - // used for passing tiles to be rendered from TileLoader(Main-Thread) to - // GLThread - static class TilesData { - int cnt = 0; - final MapTile[] tiles; - - TilesData(int numTiles) { - tiles = new MapTile[numTiles]; - } - } - /** * @param mapView * the MapView @@ -117,34 +106,27 @@ public class GLRenderer implements GLSurfaceView.Renderer { Log.d(TAG, "init MapRenderer"); mMapView = mapView; + mMapViewPosition = mapView.getMapViewPosition(); + // mMapPosition = new MapPosition(); + + mMapPosition.init(); + Matrix.setIdentityM(mMVPMatrix, 0); mUpdateTiles = false; } - /** - * called by TileLoader when only position changed - * - * @param mapPosition - * current MapPosition - */ - static void updatePosition(MapPosition mapPosition) { - mCurPosition = mapPosition; - } - /** * called by TileLoader when list of active tiles changed * - * @param mapPosition - * current MapPosition * @param tiles * active tiles * @return mNextTiles (the previously active tiles) */ - static TilesData updateTiles(MapPosition mapPosition, TilesData tiles) { + static TilesData updateTiles(TilesData tiles) { GLRenderer.tilelock.lock(); - mCurPosition = mapPosition; + // mCurPosition = mapPosition; // unlock previously active tiles for (int i = 0; i < mNextTiles.cnt; i++) { @@ -178,13 +160,13 @@ public class GLRenderer implements GLSurfaceView.Renderer { // lock tiles (and their proxies) to not be removed from cache for (int i = 0; i < mNextTiles.cnt; i++) { MapTile t = mNextTiles.tiles[i]; - if (!t.isActive) + if (!t.isLocked) t.lock(); } for (int j = 0; j < mDrawTiles.cnt; j++) { MapTile t = mDrawTiles.tiles[j]; - if (!t.isActive) + if (!t.isLocked) t.lock(); } @@ -386,8 +368,9 @@ public class GLRenderer implements GLSurfaceView.Renderer { CACHE_TILES -= 50; } - private static boolean isVisible(MapPosition mapPosition, MapTile tile) { + private static boolean isVisible(MapTile tile) { float dx, dy, scale, div = 1; + MapPosition mapPosition = mMapPosition; int diff = mapPosition.zoomLevel - tile.zoomLevel; if (diff < 0) @@ -407,7 +390,7 @@ public class GLRenderer implements GLSurfaceView.Renderer { // this kindof works for typical screen aspect if (mRotate) { int ssize = mWidth > mHeight ? mWidth : mHeight; - + ssize += Tile.TILE_SIZE; if (sy > ssize / 2 || sx > ssize / 2 || sx + size * scale < -ssize / 2 || sy + size * scale < -ssize / 2) { @@ -429,10 +412,12 @@ public class GLRenderer implements GLSurfaceView.Renderer { private static boolean mRotate = false; - private static void setMatrix(float[] matrix, MapPosition mapPosition, MapTile tile, + private static void setMatrix(float[] matrix, MapTile tile, float div, boolean project) { float x, y, scale; + MapPosition mapPosition = mMapPosition; + scale = mapPosition.scale / (div * COORD_MULTIPLIER); x = (float) (tile.pixelX - mapPosition.x * div); y = (float) (tile.pixelY - mapPosition.y * div); @@ -443,48 +428,30 @@ public class GLRenderer implements GLSurfaceView.Renderer { Matrix.scaleM(matrix, 0, scale, scale, 1); // translate relative to map center - Matrix.translateM(matrix, 0, - x * COORD_MULTIPLIER, - -(y + Tile.TILE_SIZE) * COORD_MULTIPLIER, - -1); // put on near plane + Matrix.translateM(matrix, 0, x * COORD_MULTIPLIER, + -(y + Tile.TILE_SIZE) * COORD_MULTIPLIER, 0); if (mRotate) - Matrix.multiplyMM(matrix, 0, mRotateMatrix, 0, matrix, 0); + Matrix.multiplyMM(matrix, 0, mapPosition.rotation, 0, matrix, 0); if (project) Matrix.multiplyMM(matrix, 0, mProjMatrix, 0, matrix, 0); } - // private float[] mv = new float[4]; - // private float[] mu = { 1, 1, -1, 1 }; - // - // private float[] mUnprojMatrix = new float[16]; - // - // private void unproject(MapPosition pos, float x, float y) { - // mu[0] = x; - // mu[1] = y; - // mu[2] = -1; - // - // // add tilt - // Matrix.multiplyMV(mv, 0, mTmpMatrix, 0, mu, 0); - // // Log.d(TAG, ">> " + mv[0] + " " + mv[1] + " " + mv[2]); - // - // Matrix.multiplyMV(mv, 0, mUnprojMatrix, 0, mv, 0); - // float size = Tile.TILE_SIZE * pos.scale; - // if (mv[3] != 0) { - // float w = 1 / mv[3]; - // float xx = Math.round(((mv[0] * w) / size) * 100) / 100f; - // float yy = Math.round(((mv[1] * w) / size) * 100) / 100f; - // Log.d(TAG, " " + xx + " " + yy); - // } - // } + private static float scaleDiv(MapTile t) { + float div = 1; + int diff = mMapPosition.zoomLevel - t.zoomLevel; + if (diff < 0) + div = (1 << -diff); + else if (diff > 0) + div = (1.0f / (1 << diff)); + return div; + } @Override public void onDrawFrame(GL10 glUnused) { long start = 0; - MapPosition mapPosition; - if (MapView.debugFrameTime) start = SystemClock.uptimeMillis(); @@ -498,66 +465,55 @@ public class GLRenderer implements GLSurfaceView.Renderer { | GLES20.GL_STENCIL_BUFFER_BIT); // get position and current tiles to draw - GLRenderer.tilelock.lock(); - mapPosition = mCurPosition; + // mapPosition = mCurPosition; + if (mUpdateTiles) { + GLRenderer.tilelock.lock(); TilesData tmp = mDrawTiles; mDrawTiles = mNextTiles; mNextTiles = tmp; mUpdateTiles = false; + GLRenderer.tilelock.unlock(); } - GLRenderer.tilelock.unlock(); - if (mDrawTiles == null) + if (mDrawTiles == null || mDrawTiles.cnt == 0) return; - // if (mRotate != (mMapView.enableRotation || mMapView.enableCompass)) { - // Matrix.setIdentityM(mMVPMatrix, 0); - // mRotate = mMapView.enableRotation || mMapView.enableCompass; - // } + // MapPosition mapPosition = + // mMapView.getMapViewPosition().getMapPosition(); - mRotate = mMapView.enableRotation || mMapView.enableCompass; + MapPosition mapPosition = mMapPosition; + boolean changed = mMapViewPosition.getMapPosition(mapPosition, mTileCoords); - if (mRotate) { - // rotate map - Matrix.setRotateM(mRotateMatrix, 0, mapPosition.angle, 0, 0, 1); + int tileCnt = mDrawTiles.cnt; + MapTile[] tiles = mDrawTiles.tiles; - // tilt map - // mMapView.getMapViewPosition().mTilt; + if (changed) { + for (int i = 0; i < tileCnt; i++) + tiles[i].isVisible = false; - float angle = mMapView.getMapViewPosition().mTilt / (mHeight / 2); - Matrix.setRotateM(mTmpMatrix, 0, -angle, 1, 0, 0); + // get relative zoom-level, tiles could not have been updated after + // zoom-level changed. + float div = scaleDiv(tiles[0]); - // move camera center back to map center - Matrix.translateM(mTmpMatrix, 0, - 0, (float) Math.tan(Math.toRadians(angle)), 0); + mRotate = mMapView.enableRotation || mMapView.enableCompass; - // apply first rotation, then tilt - Matrix.multiplyMM(mRotateMatrix, 0, mTmpMatrix, 0, mRotateMatrix, 0); + float s = Tile.TILE_SIZE; + float scale = mapPosition.scale / div; + float px = (float) (mapPosition.x * div); + float py = (float) (mapPosition.y * div); - // // get unproject matrix - // Matrix.setIdentityM(mTmp2Matrix, 0); - // float s = mapPosition.scale; - // Matrix.translateM(mTmp2Matrix, 0, - // (float) (mapPosition.x * s), - // (float) (mapPosition.y * s), 0); - // - // Matrix.multiplyMM(mTmp2Matrix, 0, mRotateMatrix, 0, mTmp2Matrix, - // 0); - // - // // Matrix.invertM(mTmpMatrix, 0, mTmp2Matrix, 0); - // // (AB)^-1 = B^-1*A^-1 - // Matrix.multiplyMM(mUnprojMatrix, 0, mTmp2Matrix, 0, mProjMatrixI, - // 0); - // - // // set tilt of screen coords - // Matrix.setRotateM(mTmpMatrix, 0, -15, 1, 0, 0); - // - // // unproject(mapPosition, 0, 0); - // unproject(mapPosition, -1, -1); // top-left - // unproject(mapPosition, 1, -1); // top-right - // unproject(mapPosition, -1, 1); // bottom-left - // unproject(mapPosition, 1, 1); // bottom-right + mTileCoords[0] = (px + mTileCoords[0] / scale) / s; + mTileCoords[1] = (py - mTileCoords[1] / scale) / s; + mTileCoords[2] = (px + mTileCoords[2] / scale) / s; + mTileCoords[3] = (py - mTileCoords[3] / scale) / s; + mTileCoords[4] = (px + mTileCoords[4] / scale) / s; + mTileCoords[5] = (py - mTileCoords[5] / scale) / s; + mTileCoords[6] = (px + mTileCoords[6] / scale) / s; + mTileCoords[7] = (py - mTileCoords[7] / scale) / s; + + byte z = tiles[0].zoomLevel; + ScanBox.scan(mTileCoords, mDrawTiles, 1 << z); } if (mUpdateColor && mClearColor != null) { @@ -566,9 +522,6 @@ public class GLRenderer implements GLSurfaceView.Renderer { mUpdateColor = false; } - int tileCnt = mDrawTiles.cnt; - MapTile[] tiles = mDrawTiles.tiles; - uploadCnt = 0; int updateTextures = 0; @@ -576,7 +529,9 @@ public class GLRenderer implements GLSurfaceView.Renderer { for (int i = 0; i < tileCnt; i++) { MapTile tile = tiles[i]; - if (!isVisible(mapPosition, tile)) + // if (!isVisible(mapPosition, tile)) + // continue; + if (!tile.isVisible) continue; if (tile.texture == null && TextRenderer.drawToTexture(tile)) @@ -616,14 +571,14 @@ public class GLRenderer implements GLSurfaceView.Renderer { for (int i = 0; i < tileCnt; i++) { if (tiles[i].isVisible && tiles[i].isReady) - drawTile(mapPosition, tiles[i]); + drawTile(tiles[i]); } // proxies are clipped to the region where nothing was drawn to depth // buffer. TODO draw all parent before grandparent for (int i = 0; i < tileCnt; i++) { if (tiles[i].isVisible && !tiles[i].isReady) - drawProxyTile(mapPosition, tiles[i]); + drawProxyTile(tiles[i]); } // GlUtils.checkGlError("end draw"); @@ -647,19 +602,32 @@ public class GLRenderer implements GLSurfaceView.Renderer { else TextRenderer.beginDraw(s, mProjMatrix); - // s = (float) 1.0 / COORD_MULTIPLIER; - // TextRenderer.beginDraw(s, mProjMatrix); - for (int i = 0; i < tileCnt; i++) { if (!tiles[i].isVisible || tiles[i].texture == null) continue; - setMatrix(mMVPMatrix, mapPosition, tiles[i], 1, false); + setMatrix(mMVPMatrix, tiles[i], 1, false); TextRenderer.drawTile(tiles[i], mMVPMatrix); } TextRenderer.endDraw(); - // TODO call overlay renderer + // TODO call overlay renderer here + + // s = 0.5f; + // mTileCoords[0] = -s; + // mTileCoords[1] = s; + // mTileCoords[2] = s; + // mTileCoords[3] = s; + // mTileCoords[4] = -s; + // mTileCoords[5] = -s; + // mTileCoords[6] = s; + // mTileCoords[7] = -s; + // + // Matrix.setIdentityM(mMVPMatrix, 0); + // // Matrix.scaleM(mMVPMatrix, 0, 0.99f, 0.99f, 1); + // // Matrix.multiplyMM(mMVPMatrix, 0, mRotateMatrix, 0, mMVPMatrix, 0); + // // Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0); + // PolygonRenderer.debugDraw(mMVPMatrix, mTileCoords); if (MapView.debugFrameTime) { GLES20.glFinish(); @@ -670,27 +638,26 @@ public class GLRenderer implements GLSurfaceView.Renderer { // used to not draw a tile twice per frame... private static byte mDrawSerial = 0; - private static void drawTile(MapPosition mapPosition, MapTile tile) { + private static void drawTile(MapTile tile) { // draw parents only once if (tile.lastDraw == mDrawSerial) return; - float div = 1; - - int diff = mapPosition.zoomLevel - tile.zoomLevel; - - if (diff < 0) - div = (1 << -diff); - else if (diff > 0) - div = (1.0f / (1 << diff)); + float div = scaleDiv(tile); + // float div = 1; + // int diff = mapPosition.zoomLevel - tile.zoomLevel; + // if (diff < 0) + // div = (1 << -diff); + // else if (diff > 0) + // div = (1.0f / (1 << diff)); tile.lastDraw = mDrawSerial; - int z = mapPosition.zoomLevel; - float s = mapPosition.scale; + int z = mMapPosition.zoomLevel; + float s = mMapPosition.scale; float[] mvp = mMVPMatrix; - setMatrix(mvp, mapPosition, tile, div, true); + setMatrix(mvp, tile, div, true); GLES20.glPolygonOffset(0, mDrawCount++); @@ -734,7 +701,7 @@ public class GLRenderer implements GLSurfaceView.Renderer { } // TODO could use tile.proxies here - private static boolean drawProxyChild(MapPosition mapPosition, MapTile tile) { + private static boolean drawProxyChild(MapTile tile) { int drawn = 0; for (int i = 0; i < 4; i++) { if (tile.rel.child[i] == null) @@ -744,13 +711,13 @@ public class GLRenderer implements GLSurfaceView.Renderer { if (c == null) continue; - if (!isVisible(mapPosition, c)) { + if (!isVisible(c)) { drawn++; continue; } if (c.isReady) { - drawTile(mapPosition, c); + drawTile(c); drawn++; } } @@ -758,17 +725,17 @@ public class GLRenderer implements GLSurfaceView.Renderer { } // TODO could use tile.proxies here - private static void drawProxyTile(MapPosition mapPosition, MapTile tile) { - int diff = mapPosition.zoomLevel - tile.zoomLevel; + private static void drawProxyTile(MapTile tile) { + int diff = mMapPosition.zoomLevel - tile.zoomLevel; boolean drawn = false; - if (mapPosition.scale > 1.5f || diff < 0) { + if (mMapPosition.scale > 1.5f || diff < 0) { // prefer drawing children - if (!drawProxyChild(mapPosition, tile)) { + if (!drawProxyChild(tile)) { if ((tile.proxies & MapTile.PROXY_PARENT) != 0) { MapTile t = tile.rel.parent.tile; if (t.isReady) { - drawTile(mapPosition, t); + drawTile(t); drawn = true; } } @@ -776,7 +743,7 @@ public class GLRenderer implements GLSurfaceView.Renderer { if (!drawn && (tile.proxies & MapTile.PROXY_GRAMPA) != 0) { MapTile t = tile.rel.parent.parent.tile; if (t.isReady) - drawTile(mapPosition, t); + drawTile(t); } } @@ -785,14 +752,14 @@ public class GLRenderer implements GLSurfaceView.Renderer { MapTile t = tile.rel.parent.tile; if (t != null && t.isReady) { - drawTile(mapPosition, t); + drawTile(t); - } else if (!drawProxyChild(mapPosition, tile)) { + } else if (!drawProxyChild(tile)) { if ((tile.proxies & MapTile.PROXY_GRAMPA) != 0) { t = tile.rel.parent.parent.tile; if (t.isReady) - drawTile(mapPosition, t); + drawTile(t); } } } @@ -814,14 +781,18 @@ public class GLRenderer implements GLSurfaceView.Renderer { // Matrix.orthoM(mProjMatrix, 0, -0.5f / mAspect, 0.5f / mAspect, -0.5f, // 0.5f, -1, 1); + float s = 0.5f; + Matrix.frustumM(mProjMatrix, 0, -s * width, s * width, + -s * height, s * height, 1, 2); + Matrix.translateM(mProjMatrix, 0, 0, 0, -1); - Matrix.frustumM(mProjMatrix, 0, - -0.5f * width, - 0.5f * width, - -0.5f * height, - 0.5f * height, 1, 2); + // Matrix.invertM(mProjMatrixI, 0, mProjMatrix, 0); - Matrix.invertM(mProjMatrixI, 0, mProjMatrix, 0); + // use this to scale only the view to see which tiles are rendered + // s = 1.0f; + // Matrix.frustumM(mProjMatrix, 0, -s * width, s * width, + // -s * height, s * height, 1, 2); + // Matrix.translateM(mProjMatrix, 0, 0, 0, -1); // set to zero: we modify the z value with polygon-offset for clipping mProjMatrix[10] = 0; @@ -869,6 +840,7 @@ public class GLRenderer implements GLSurfaceView.Renderer { mMapView.redrawMap(); } + // FIXME this is a bit too spaghetti void clearTiles(int numTiles) { mDrawTiles = new TilesData(numTiles); mNextTiles = new TilesData(numTiles); diff --git a/src/org/oscim/view/renderer/LineLayer.java b/src/org/oscim/view/renderer/LineLayer.java index b935dff4..f5b78595 100644 --- a/src/org/oscim/view/renderer/LineLayer.java +++ b/src/org/oscim/view/renderer/LineLayer.java @@ -40,8 +40,8 @@ class LineLayer { boolean isOutline; int layer; - ShortItem pool; - protected ShortItem curItem; + VertexPoolItem pool; + protected VertexPoolItem curItem; // number of vertices this layer holds int verticesCnt; @@ -85,10 +85,10 @@ class LineLayer { squared = true; if (pool == null) { - pool = curItem = ShortPool.get(); + pool = curItem = VertexPool.get(); } - ShortItem si = curItem; + VertexPoolItem si = curItem; short v[] = si.vertices; int opos = si.used; @@ -137,8 +137,8 @@ class LineLayer { ux = -vy; uy = vx; - if (opos == ShortItem.SIZE) { - si = si.next = ShortPool.get(); + if (opos == VertexPoolItem.SIZE) { + si = si.next = VertexPool.get(); v = si.vertices; opos = 0; } @@ -151,8 +151,8 @@ class LineLayer { boolean outside = (x < tmin || x > tmax || y < tmin || y > tmax); - if (opos == ShortItem.SIZE) { - si = si.next = ShortPool.get(); + if (opos == VertexPoolItem.SIZE) { + si = si.next = VertexPool.get(); v = si.vertices; opos = 0; } @@ -170,8 +170,8 @@ class LineLayer { v[opos++] = dx; v[opos++] = dy; - if (opos == ShortItem.SIZE) { - si = si.next = ShortPool.get(); + if (opos == VertexPoolItem.SIZE) { + si = si.next = VertexPool.get(); v = si.vertices; opos = 0; } @@ -181,8 +181,8 @@ class LineLayer { v[opos++] = dx; v[opos++] = dy; - if (opos == ShortItem.SIZE) { - si = si.next = ShortPool.get(); + if (opos == VertexPoolItem.SIZE) { + si = si.next = VertexPool.get(); v = si.vertices; opos = 0; } @@ -195,8 +195,8 @@ class LineLayer { v[opos++] = (short) (2 | ddx & DIR_MASK); v[opos++] = (short) (2 | ddy & DIR_MASK); - if (opos == ShortItem.SIZE) { - si = si.next = ShortPool.get(); + if (opos == VertexPoolItem.SIZE) { + si = si.next = VertexPool.get(); v = si.vertices; opos = 0; } @@ -210,8 +210,8 @@ class LineLayer { v[opos++] = (short) (0 | ddx & DIR_MASK); v[opos++] = (short) (1 | ddy & DIR_MASK); - if (opos == ShortItem.SIZE) { - si = si.next = ShortPool.get(); + if (opos == VertexPoolItem.SIZE) { + si = si.next = VertexPool.get(); v = si.vertices; opos = 0; } @@ -248,8 +248,8 @@ class LineLayer { v[opos++] = dx; v[opos++] = dy; - if (opos == ShortItem.SIZE) { - si = si.next = ShortPool.get(); + if (opos == VertexPoolItem.SIZE) { + si = si.next = VertexPool.get(); v = si.vertices; opos = 0; } @@ -259,8 +259,8 @@ class LineLayer { v[opos++] = dx; v[opos++] = dy; - if (opos == ShortItem.SIZE) { - si = si.next = ShortPool.get(); + if (opos == VertexPoolItem.SIZE) { + si = si.next = VertexPool.get(); v = si.vertices; opos = 0; } @@ -334,8 +334,8 @@ class LineLayer { ddx = (int) (ux * DIR_SCALE); ddy = (int) (uy * DIR_SCALE); - if (opos == ShortItem.SIZE) { - si = si.next = ShortPool.get(); + if (opos == VertexPoolItem.SIZE) { + si = si.next = VertexPool.get(); v = si.vertices; opos = 0; } @@ -345,8 +345,8 @@ class LineLayer { v[opos++] = (short) (0 | ddx & DIR_MASK); v[opos++] = (short) (1 | ddy & DIR_MASK); - if (opos == ShortItem.SIZE) { - si = si.next = ShortPool.get(); + if (opos == VertexPoolItem.SIZE) { + si = si.next = VertexPool.get(); v = si.vertices; opos = 0; } @@ -375,8 +375,8 @@ class LineLayer { outside = (x < tmin || x > tmax || y < tmin || y > tmax); - if (opos == ShortItem.SIZE) { - si.next = ShortPool.get(); + if (opos == VertexPoolItem.SIZE) { + si.next = VertexPool.get(); si = si.next; opos = 0; v = si.vertices; @@ -394,8 +394,8 @@ class LineLayer { v[opos++] = (short) (0 | ddx & DIR_MASK); v[opos++] = (short) (1 | ddy & DIR_MASK); - if (opos == ShortItem.SIZE) { - si = si.next = ShortPool.get(); + if (opos == VertexPoolItem.SIZE) { + si = si.next = VertexPool.get(); v = si.vertices; opos = 0; } @@ -405,8 +405,8 @@ class LineLayer { v[opos++] = (short) (2 | -ddx & DIR_MASK); v[opos++] = (short) (1 | -ddy & DIR_MASK); - if (opos == ShortItem.SIZE) { - si = si.next = ShortPool.get(); + if (opos == VertexPoolItem.SIZE) { + si = si.next = VertexPool.get(); v = si.vertices; opos = 0; } @@ -422,8 +422,8 @@ class LineLayer { v[opos++] = dx; v[opos++] = dy; - if (opos == ShortItem.SIZE) { - si = si.next = ShortPool.get(); + if (opos == VertexPoolItem.SIZE) { + si = si.next = VertexPool.get(); v = si.vertices; opos = 0; } @@ -439,8 +439,8 @@ class LineLayer { v[opos++] = dx; v[opos++] = dy; - if (opos == ShortItem.SIZE) { - si = si.next = ShortPool.get(); + if (opos == VertexPoolItem.SIZE) { + si = si.next = VertexPool.get(); v = si.vertices; opos = 0; } @@ -470,8 +470,8 @@ class LineLayer { v[opos++] = (short) (0 | ddx & DIR_MASK); v[opos++] = (short) (1 | ddy & DIR_MASK); - if (opos == ShortItem.SIZE) { - si = si.next = ShortPool.get(); + if (opos == VertexPoolItem.SIZE) { + si = si.next = VertexPool.get(); v = si.vertices; opos = 0; } @@ -487,8 +487,8 @@ class LineLayer { v[opos++] = dx; v[opos++] = dy; - if (opos == ShortItem.SIZE) { - si = si.next = ShortPool.get(); + if (opos == VertexPoolItem.SIZE) { + si = si.next = VertexPool.get(); v = si.vertices; opos = 0; } diff --git a/src/org/oscim/view/renderer/LineRenderer.java b/src/org/oscim/view/renderer/LineRenderer.java index e44326cd..33220e36 100644 --- a/src/org/oscim/view/renderer/LineRenderer.java +++ b/src/org/oscim/view/renderer/LineRenderer.java @@ -183,18 +183,18 @@ class LineRenderer { static void compileLayerData(LineLayer layers, ShortBuffer sbuf) { int pos = 0; - ShortItem last = null, items = null; + VertexPoolItem last = null, items = null; for (LineLayer l = layers; l != null; l = l.next) { if (l.isOutline) continue; - for (ShortItem item = l.pool; item != null; item = item.next) { + for (VertexPoolItem item = l.pool; item != null; item = item.next) { if (item.next == null) { sbuf.put(item.vertices, 0, item.used); } else { - // item.used = ShortItem.SIZE; + // item.used = VertexPoolItem.SIZE; sbuf.put(item.vertices); } @@ -213,7 +213,7 @@ class LineRenderer { l.curItem = null; } - ShortPool.add(items); + VertexPool.add(items); } // @SuppressLint("UseValueOf") @@ -310,7 +310,7 @@ class LineRenderer { static void clear(LineLayer layer) { for (LineLayer l = layer; l != null; l = l.next) { if (l.pool != null) { - ShortPool.add(l.pool); + VertexPool.add(l.pool); l.pool = null; l.curItem = null; } diff --git a/src/org/oscim/view/renderer/MapRenderer.java b/src/org/oscim/view/renderer/MapRenderer.java index 98a5d47f..6392bb43 100644 --- a/src/org/oscim/view/renderer/MapRenderer.java +++ b/src/org/oscim/view/renderer/MapRenderer.java @@ -8,7 +8,7 @@ * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see <http://www.gnu.org/licenses/>. */ @@ -17,21 +17,21 @@ package org.oscim.view.renderer; import java.util.ArrayList; import java.util.Collections; -import org.oscim.core.MapPosition; import org.oscim.core.MercatorProjection; import org.oscim.core.Tile; import org.oscim.database.MapInfo; import org.oscim.theme.RenderTheme; import org.oscim.utils.GlConfigChooser; +import org.oscim.view.MapPosition; import org.oscim.view.MapView; import org.oscim.view.generator.JobTile; -import org.oscim.view.renderer.GLRenderer.TilesData; import android.content.Context; import android.opengl.GLSurfaceView; import android.util.FloatMath; import android.util.Log; +// FIXME to many 'Renderer', this one needs a better name.. TileLoader? public class MapRenderer extends GLSurfaceView { private final static String TAG = "MapRenderer"; private GLRenderer mRenderer; @@ -45,7 +45,7 @@ public class MapRenderer extends GLSurfaceView { // all tiles currently referenced private static ArrayList<MapTile> mTiles; - // private static ArrayList<MapTile> mTilesActive; + // tiles that have new data to upload, see passTile() private static ArrayList<MapTile> mTilesLoaded; @@ -59,9 +59,20 @@ public class MapRenderer extends GLSurfaceView { // private static MapPosition mCurPosition, mDrawPosition; private static int mWidth = 0, mHeight = 0; + // used for passing tiles to be rendered from TileLoader(Main-Thread) to + // GLThread + static final class TilesData { + int cnt = 0; + final MapTile[] tiles; + + TilesData(int numTiles) { + tiles = new MapTile[numTiles]; + } + } + private static TilesData mCurrentTiles; - // map zoom-level to available zoom-levels in MapDatabase + // maps zoom-level to available zoom-levels in MapDatabase // e.g. 16->16, 15->16, 14->13, 13->13, 12->13,.... private static int[] mZoomLevels; @@ -85,7 +96,7 @@ public class MapRenderer extends GLSurfaceView { mTiles = new ArrayList<MapTile>(); mTilesLoaded = new ArrayList<MapTile>(30); - ShortPool.init(); + VertexPool.init(); QuadTree.init(); mInitial = true; @@ -97,13 +108,10 @@ public class MapRenderer extends GLSurfaceView { * loading by TileGenerator class * * @param clear - * ... + * whether to clear and reload all tiles */ - public synchronized void updateMap(boolean clear) { - boolean changedPos = false; - boolean changedZoom = false; if (mMapView == null) return; @@ -155,24 +163,20 @@ public class MapRenderer extends GLSurfaceView { int zdir = 0; if (mInitial || mPrevZoom != zoomLevel) { - changedZoom = true; - mPrevScale = scale; - } - else if (tileX != mTileX || tileY != mTileY) { - if (mPrevScale - scale > 0 && scale > 1.2) - zdir = 1; - mPrevScale = scale; changedPos = true; - } - else if (mPrevScale - scale > 0.2 || mPrevScale - scale < -0.2) { + + } else if (tileX != mTileX || tileY != mTileY) { + if (mPrevScale - scale > 0 && scale > 1.2) + zdir = 1; + changedPos = true; + + } else if (mPrevScale - scale > 0.2 || mPrevScale - scale < -0.2) { if (mPrevScale - scale > 0 && scale > 1.2) zdir = 1; - mPrevScale = scale; changedPos = true; } if (mInitial) { - // mCurPosition = mapPosition; mInitial = false; } @@ -180,30 +184,19 @@ public class MapRenderer extends GLSurfaceView { mTileY = tileY; mPrevZoom = zoomLevel; - GLRenderer.updatePosition(mapPosition); + // GLRenderer.updatePosition(mapPosition); + if (!MapView.debugFrameTime) requestRender(); - if (changedZoom || changedPos) { - // need to update visible list first when zoom level changes - // as scaling is relative to the tiles of current zoom-level + if (changedPos) { + mPrevScale = scale; + updateVisibleList(mapPosition, zdir); if (!MapView.debugFrameTime) requestRender(); - } - // else { - // // pass new position to glThread - // GLRenderer.updatePosition(mapPosition); - // } - // if (!MapView.debugFrameTime) - // requestRender(); - - // if (changedPos) - // updateVisibleList(mapPosition, zdir); - - if (changedPos || changedZoom) { int remove = mTiles.size() - GLRenderer.CACHE_TILES; if (remove > 50) limitCache(mapPosition, remove); @@ -256,15 +249,15 @@ public class MapRenderer extends GLSurfaceView { // check MapDatabase zoom-level-mapping if (mZoomLevels[zoomLevel] == 0) { mCurrentTiles.cnt = 0; - mCurrentTiles = GLRenderer.updateTiles(mapPosition, mCurrentTiles); + mCurrentTiles = GLRenderer.updateTiles(mCurrentTiles); return; } if (mZoomLevels[zoomLevel] > zoomLevel) { fetchChildren = true; fetchProxy = true; - } - else if (mZoomLevels[zoomLevel] < zoomLevel) { + + } else if (mZoomLevels[zoomLevel] < zoomLevel) { fetchParent = true; fetchProxy = true; } @@ -285,12 +278,12 @@ public class MapRenderer extends GLSurfaceView { mTiles.add(tile); } - mCurrentTiles.tiles[tiles++] = tile; - - if (!fetchProxy && !(tile.isLoading || tile.newData || tile.isReady)) { + if (!fetchProxy && !tile.isActive()) { mJobList.add(tile); } + mCurrentTiles.tiles[tiles++] = tile; + if (fetchChildren) { byte z = (byte) (zoomLevel + 1); for (int i = 0; i < 4; i++) { @@ -306,7 +299,7 @@ public class MapRenderer extends GLSurfaceView { mTiles.add(c); } - if (!(c.isLoading || c.newData || c.isReady)) { + if (!c.isActive()) { mJobList.add(c); } } @@ -314,18 +307,18 @@ public class MapRenderer extends GLSurfaceView { if (fetchParent || (!fetchProxy && zdir > 0 && zoomLevel > 0)) { // prefetch parent - MapTile parent = tile.rel.parent.tile; + MapTile p = tile.rel.parent.tile; - if (parent == null) { - parent = new MapTile(xx >> 1, yy >> 1, (byte) (zoomLevel - 1)); + if (p == null) { + p = new MapTile(xx >> 1, yy >> 1, (byte) (zoomLevel - 1)); - QuadTree.add(parent); - mTiles.add(parent); - } + QuadTree.add(p); + mTiles.add(p); + mJobList.add(p); - if (!(parent.isLoading || parent.isReady || parent.newData)) { - if (!mJobList.contains(parent)) - mJobList.add(parent); + } else if (!p.isActive()) { + if (!mJobList.contains(p)) + mJobList.add(p); } } } @@ -333,7 +326,7 @@ public class MapRenderer extends GLSurfaceView { // pass new tile list to glThread mCurrentTiles.cnt = tiles; - mCurrentTiles = GLRenderer.updateTiles(mapPosition, mCurrentTiles); + mCurrentTiles = GLRenderer.updateTiles(mCurrentTiles); // note: this sets isLoading == true for all job tiles if (mJobList.size() > 0) { @@ -366,8 +359,7 @@ public class MapRenderer extends GLSurfaceView { QuadTree.remove(t); } - private static void updateTileDistances(ArrayList<?> tiles, - MapPosition mapPosition) { + private static void updateTileDistances(ArrayList<?> tiles, MapPosition mapPosition) { int h = (Tile.TILE_SIZE >> 1); byte zoom = mapPosition.zoomLevel; long x = (long) mapPosition.x; @@ -422,18 +414,17 @@ public class MapRenderer extends GLSurfaceView { // remove orphaned tiles for (int i = 0; i < size;) { - MapTile cur = mTiles.get(i); + MapTile t = mTiles.get(i); // make sure tile cannot be used by GL or MapWorker Thread - if (!cur.isLocked() && !cur.isLoading && !cur.newData && !cur.isReady) { - - clearTile(cur); + if (t.isLocked() || t.isActive()) { + i++; + } else { + // Log.d(TAG, "remove empty tile" + cur); + clearTile(t); mTiles.remove(i); removes--; size--; - // Log.d(TAG, "remove empty tile" + cur); - continue; } - i++; } // Log.d(TAG, "remove tiles: " + removes + " " + size); @@ -451,7 +442,7 @@ public class MapRenderer extends GLSurfaceView { synchronized (t) { if (t.isLocked()) { // dont remove tile used by renderthread - Log.d(TAG, "X not removing " + t + " " + t.isActive + " " + Log.d(TAG, "X not removing " + t + " " + t.isLocked + " " + t.distance); mTiles.add(t); @@ -478,7 +469,7 @@ public class MapRenderer extends GLSurfaceView { synchronized (mTilesLoaded) { - // remove uploaded tiles + // remove tiles uploaded to vbo for (int i = 0; i < size;) { MapTile t = mTilesLoaded.get(i); // rel == null means tile is already removed by limitCache @@ -535,8 +526,7 @@ public class MapRenderer extends GLSurfaceView { if (!tile.isLoading) { // no one should be able to use this tile now, mapgenerator passed - // it, - // glthread does nothing until newdata is set. + // it, glthread does nothing until newdata is set. Log.d(TAG, "passTile: canceled " + tile); synchronized (mTilesLoaded) { mTilesLoaded.add(tile); diff --git a/src/org/oscim/view/renderer/MapTile.java b/src/org/oscim/view/renderer/MapTile.java index cdf104eb..a8845808 100644 --- a/src/org/oscim/view/renderer/MapTile.java +++ b/src/org/oscim/view/renderer/MapTile.java @@ -41,7 +41,7 @@ class MapTile extends JobTile { /** * tile is used by render thread. set by updateVisibleList (main thread). */ - boolean isActive; + boolean isLocked; /** * tile has new data to upload to gl @@ -74,13 +74,16 @@ class MapTile extends JobTile { // counting the tiles that use this tile as proxy byte refs; + boolean isActive() { + return isLoading || newData || isReady; + } + boolean isLocked() { - return isActive || refs > 0; + return isLocked || refs > 0; } void lock() { - - isActive = true; + isLocked = true; if (isReady || newData) return; @@ -110,7 +113,7 @@ class MapTile extends JobTile { } void unlock() { - isActive = false; + isLocked = false; if (proxies == 0) return; diff --git a/src/org/oscim/view/renderer/PolygonLayer.java b/src/org/oscim/view/renderer/PolygonLayer.java index aa310123..c25328b2 100644 --- a/src/org/oscim/view/renderer/PolygonLayer.java +++ b/src/org/oscim/view/renderer/PolygonLayer.java @@ -23,8 +23,8 @@ class PolygonLayer { PolygonLayer next; Area area; - ShortItem pool; - protected ShortItem curItem; + VertexPoolItem pool; + protected VertexPoolItem curItem; int verticesCnt; int offset; @@ -33,14 +33,14 @@ class PolygonLayer { PolygonLayer(int layer, Area area) { this.layer = layer; this.area = area; - curItem = ShortPool.get(); + curItem = VertexPool.get(); pool = curItem; } void addPolygon(float[] points, short[] index) { short center = (short) ((Tile.TILE_SIZE >> 1) * S); - ShortItem si = curItem; + VertexPoolItem si = curItem; short[] v = si.vertices; int outPos = si.used; @@ -59,8 +59,8 @@ class PolygonLayer { int inPos = pos; - if (outPos == ShortItem.SIZE) { - si = si.next = ShortPool.get(); + if (outPos == VertexPoolItem.SIZE) { + si = si.next = VertexPool.get(); v = si.vertices; outPos = 0; } @@ -69,8 +69,8 @@ class PolygonLayer { v[outPos++] = center; for (int j = 0; j < length; j += 2) { - if (outPos == ShortItem.SIZE) { - si = si.next = ShortPool.get(); + if (outPos == VertexPoolItem.SIZE) { + si = si.next = VertexPool.get(); v = si.vertices; outPos = 0; } @@ -78,8 +78,8 @@ class PolygonLayer { v[outPos++] = (short) (points[inPos++] * S); } - if (outPos == ShortItem.SIZE) { - si = si.next = ShortPool.get(); + if (outPos == VertexPoolItem.SIZE) { + si = si.next = VertexPool.get(); v = si.vertices; outPos = 0; } diff --git a/src/org/oscim/view/renderer/PolygonRenderer.java b/src/org/oscim/view/renderer/PolygonRenderer.java index 137e5245..a423e25e 100644 --- a/src/org/oscim/view/renderer/PolygonRenderer.java +++ b/src/org/oscim/view/renderer/PolygonRenderer.java @@ -35,6 +35,9 @@ import static android.opengl.GLES20.glUniformMatrix4fv; import static android.opengl.GLES20.glUseProgram; import static android.opengl.GLES20.glVertexAttribPointer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; import java.nio.ShortBuffer; import org.oscim.utils.GlUtils; @@ -257,7 +260,7 @@ class PolygonRenderer { if (mCount > 0) fillPolygons(zoom, scale); - // + // maybe reset start when only few layers left in stencil buffer // if (mCount > 5){ // mCount = 0; // mStart = 0; @@ -273,6 +276,35 @@ class PolygonRenderer { return l; } + private static float[] debugFillColor = { 0.3f, 0.0f, 0.0f, 0.3f }; + + private static ByteBuffer mDebugFill; + + static void debugDraw(float[] matrix, float[] coords) { + + mDebugFill = ByteBuffer.allocateDirect(32).order(ByteOrder.nativeOrder()); + FloatBuffer buf = mDebugFill.asFloatBuffer(); + buf.put(coords); + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); + + mDebugFill.position(0); + glUseProgram(polygonProgram); + GLES20.glEnableVertexAttribArray(hPolygonVertexPosition); + + glVertexAttribPointer(hPolygonVertexPosition, 2, GLES20.GL_FLOAT, + false, 0, mDebugFill); + + glUniformMatrix4fv(hPolygonMatrix, 1, false, matrix, 0); + + glUniform4fv(hPolygonColor, 1, debugFillColor, 0); + + glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + + GlUtils.checkGlError("draw debug"); + GLES20.glDisableVertexAttribArray(hPolygonVertexPosition); + } + static void drawDepthClip() { glColorMask(false, false, false, false); GLES20.glDepthMask(true); @@ -299,16 +331,16 @@ class PolygonRenderer { static void compileLayerData(PolygonLayer layers, ShortBuffer sbuf) { int pos = 4; - ShortItem last = null, items = null; + VertexPoolItem last = null, items = null; for (PolygonLayer l = layers; l != null; l = l.next) { - for (ShortItem item = l.pool; item != null; item = item.next) { + for (VertexPoolItem item = l.pool; item != null; item = item.next) { if (item.next == null) { sbuf.put(item.vertices, 0, item.used); } else { - // item.used = ShortItem.SIZE; + // item.used = VertexPoolItem.SIZE; sbuf.put(item.vertices); } @@ -326,13 +358,13 @@ class PolygonRenderer { l.pool = null; } - ShortPool.add(items); + VertexPool.add(items); } static void clear(PolygonLayer layers) { for (PolygonLayer l = layers; l != null; l = l.next) { if (l.pool != null) - ShortPool.add(l.pool); + VertexPool.add(l.pool); } } } diff --git a/src/org/oscim/view/renderer/ScanBox.java b/src/org/oscim/view/renderer/ScanBox.java new file mode 100644 index 00000000..b5d7093e --- /dev/null +++ b/src/org/oscim/view/renderer/ScanBox.java @@ -0,0 +1,184 @@ +/* + * Copyright 2012 Hannes Janetzek + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General License for more details. + * + * You should have received a copy of the GNU Lesser General License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* ported from Polymaps: Layer.js */ + +package org.oscim.view.renderer; + +import org.oscim.view.renderer.MapRenderer.TilesData; + +import android.util.FloatMath; + +public class ScanBox { + + interface Callback { + void call(MapTile tile); + } + + class SetVisible implements Callback { + + @Override + public void call(MapTile tile) { + tile.isVisible = true; + } + } + + static class Edge { + float x0, y0, x1, y1, dx, dy; + + void set(float x0, float y0, float x1, float y1) { + if (y0 <= y1) { + this.x0 = x0; + this.y0 = y0; + this.x1 = x1; + this.y1 = y1; + this.dx = x1 - x0; + this.dy = y1 - y0; + } else { + this.x0 = x1; + this.y0 = y1; + this.x1 = x0; + this.y1 = y0; + this.dx = x0 - x1; + this.dy = y0 - y1; + } + } + } + + static Edge ab = new Edge(); + static Edge bc = new Edge(); + static Edge ca = new Edge(); + + static void scanSpans(Edge e0, Edge e1, float ymin, float ymax) { + + // sort edge by x-coordinate + if (e0.x0 == e1.x0 && e0.y0 == e1.y0) { + if (e0.x0 + e1.dy / e0.dy * e0.dx < e1.x1) { + Edge t = e0; + e0 = e1; + e1 = t; + } + } else { + if (e0.x1 - e1.dy / e0.dy * e0.dx < e1.x0) { + Edge t = e0; + e0 = e1; + e1 = t; + } + } + + float m0 = e0.dx / e0.dy; + float m1 = e1.dx / e1.dy; + + int d0 = e0.dx > 0 ? 1 : 0;// use y + 1 to compute x0 + int d1 = e1.dx < 0 ? 1 : 0; // use y + 1 to compute x1 + + float x0, x1; + + int y = (int) Math.max(ymin, FloatMath.floor(e1.y0)); + int bottom = (int) Math.min(ymax, FloatMath.ceil(e1.y1)); + + for (; y < bottom; y++) { + // float x0 = (m0 * Math.min(e0.dy, y + d0 - e0.y0) + e0.x0); + // float x1 = (m1 * Math.min(e1.dy, y + d1 - e1.y0) + e1.x0); + + x0 = y + d0 - e0.y0; + if (e0.dy < x0) + x0 = e0.dy; + + x0 = m0 * x0 + e0.x0; + + if (x0 < 0) + x0 = 0; + else + x0 = FloatMath.ceil(x0); + + x1 = y + d1 - e1.y0; + if (e1.dy < x1) + x1 = e1.dy; + + x1 = m1 * x1 + e1.x0; + + if (x1 < 0) + x1 = 0; + else + x1 = FloatMath.floor(x1); + + setVisible(y, (int) x1, (int) x0); + + // setVisible(y, (int) (x1 - 0.5f), (int) (x0 + 0.5f)); + } + } + + static void scanTriangle(float ymin, float ymax) { + + if (ab.dy > bc.dy) { + Edge t = ab; + ab = bc; + bc = t; + } + if (ab.dy > ca.dy) { + Edge t = ab; + ab = ca; + ca = t; + } + if (bc.dy > ca.dy) { + Edge t = bc; + bc = ca; + ca = t; + } + + if (ab.dy != 0) + scanSpans(ca, ab, ymin, ymax); + if (bc.dy != 0) + scanSpans(ca, bc, ymin, ymax); + } + + public static void scan(float[] coords, TilesData tiles, int max) { + sTiles = tiles; + cntDoubles = 0; + ab.set(coords[0], coords[1], coords[2], coords[3]); + bc.set(coords[2], coords[3], coords[4], coords[5]); + ca.set(coords[4], coords[5], coords[0], coords[1]); + + scanTriangle(0, max); + // Log.d("..", ">doubles " + cntDoubles); + ab.set(coords[4], coords[5], coords[6], coords[7]); + bc.set(coords[6], coords[7], coords[0], coords[1]); + ca.set(coords[0], coords[1], coords[4], coords[5]); + + scanTriangle(0, max); + + // Log.d("..", "<doubles " + cntDoubles); + } + + private static TilesData sTiles; + private static int cntDoubles; + + private static void setVisible(int y, int x1, int x2) { + + MapTile[] tiles = sTiles.tiles; + for (int i = 0, n = sTiles.cnt; i < n; i++) { + if (tiles[i].tileY == y) { + if (tiles[i].tileX >= x1 && tiles[i].tileX < x2) { + // if (tiles[i].isVisible) { + // Log.d("..", ">>>" + y + " " + tiles[i].tileX); + // cntDoubles++; + // } + tiles[i].isVisible = true; + } + } + } + } +} diff --git a/src/org/oscim/view/renderer/TextRenderer.java b/src/org/oscim/view/renderer/TextRenderer.java index 8fd79285..eba3e990 100644 --- a/src/org/oscim/view/renderer/TextRenderer.java +++ b/src/org/oscim/view/renderer/TextRenderer.java @@ -192,7 +192,7 @@ public class TextRenderer { if (tex.tile == null) break; - if (!tex.tile.isActive) + if (!tex.tile.isLocked) break; tex = null; @@ -412,7 +412,7 @@ public class TextRenderer { for (int i = 0; i < mTextures.length; i++) { tex = mTextures[i]; - if (tex.tile == null || !tex.tile.isActive) + if (tex.tile == null || !tex.tile.isLocked) continue; mShortBuffer.put(tex.vertices, 0, tex.length); diff --git a/src/org/oscim/view/renderer/ShortPool.java b/src/org/oscim/view/renderer/VertexPool.java similarity index 75% rename from src/org/oscim/view/renderer/ShortPool.java rename to src/org/oscim/view/renderer/VertexPool.java index ccfae746..e3a52833 100644 --- a/src/org/oscim/view/renderer/ShortPool.java +++ b/src/org/oscim/view/renderer/VertexPool.java @@ -16,10 +16,10 @@ package org.oscim.view.renderer; import android.util.Log; -public class ShortPool { +public class VertexPool { private static final int POOL_LIMIT = 6000; - static private ShortItem pool = null; + static private VertexPoolItem pool = null; static private int count = 0; static private int countAll = 0; @@ -29,14 +29,14 @@ public class ShortPool { pool = null; } - static synchronized ShortItem get() { + static synchronized VertexPoolItem get() { if (pool == null && count > 0) { - Log.d("ShortPool", "XXX wrong count: " + count); + Log.d("VertexPool", "XXX wrong count: " + count); } if (pool == null) { countAll++; - return new ShortItem(); + return new VertexPoolItem(); } count--; @@ -44,14 +44,14 @@ public class ShortPool { if (count < 0) { int c = 0; - for (ShortItem tmp = pool; tmp != null; tmp = tmp.next) + for (VertexPoolItem tmp = pool; tmp != null; tmp = tmp.next) c++; - Log.d("ShortPool", "XXX wrong count: " + count + " left" + c); - return new ShortItem(); + Log.d("VertexPool", "XXX wrong count: " + count + " left" + c); + return new VertexPoolItem(); } - ShortItem it = pool; + VertexPoolItem it = pool; pool = pool.next; it.used = 0; it.next = null; @@ -61,7 +61,7 @@ public class ShortPool { // private static float load = 1.0f; // private static int loadCount = 0; - static synchronized void add(ShortItem items) { + static synchronized void add(VertexPoolItem items) { if (items == null) return; @@ -71,11 +71,11 @@ public class ShortPool { // limit pool items if (countAll < POOL_LIMIT) { - ShortItem last = items; + VertexPoolItem last = items; while (true) { count++; - // load += (float) last.used / ShortItem.SIZE; + // load += (float) last.used / VertexPoolItem.SIZE; // loadCount++; if (last.next == null) @@ -91,14 +91,14 @@ public class ShortPool { } else { // int cleared = 0; - ShortItem prev, tmp = items; + VertexPoolItem prev, tmp = items; while (tmp != null) { prev = tmp; tmp = tmp.next; countAll--; - // load += (float) prev.used / ShortItem.SIZE; + // load += (float) prev.used / VertexPoolItem.SIZE; // loadCount++; prev.next = null; diff --git a/src/org/oscim/view/renderer/ShortItem.java b/src/org/oscim/view/renderer/VertexPoolItem.java similarity index 92% rename from src/org/oscim/view/renderer/ShortItem.java rename to src/org/oscim/view/renderer/VertexPoolItem.java index a94b89d1..0d4cfdbe 100644 --- a/src/org/oscim/view/renderer/ShortItem.java +++ b/src/org/oscim/view/renderer/VertexPoolItem.java @@ -14,12 +14,12 @@ */ package org.oscim.view.renderer; -public class ShortItem { +public class VertexPoolItem { final short[] vertices; int used; - ShortItem next; + VertexPoolItem next; - ShortItem() { + VertexPoolItem() { vertices = new short[SIZE]; used = 0; }