Android Open Source - android-plotter Plot Renderer






From Project

Back to project page android-plotter.

License

The source code is released under:

Apache License

If you think the Android project android-plotter listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright (C) 2011 The Android Open Source Project
 */*from w  ww .  ja  va 2  s  .co m*/
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.solovyev.android.plotter;

import android.content.Context;
import android.graphics.Rect;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
import com.android.texample.GLText;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;

@SuppressWarnings("SynchronizeOnNonFinalField")
final class PlotRenderer implements GLSurfaceView.Renderer {

  @Nonnull
  private final Spf spf = new Spf();

  // lock for synchronization GL objects
  @Nonnull
  private final Object lock = new Object();

  @Nonnull
  private final Context context;

  @Nonnull
  private final PlottingView view;

  @Nonnull
  private GLText glText;

  @GuardedBy("lock")
  @Nullable
  private Plotter plotter;

  @GuardedBy("lock")
  private boolean glInitialized;

  @Nonnull
  private final RotationHolder rotation = new RotationHolder();

  @Nonnull
  private final ZoomerHolder zoomer = new ZoomerHolder();

  @Nonnull
  private Rect viewDimensions = new Rect();

  @Nonnull
  private final Frustum frustum = Frustum.empty();

  private volatile boolean looping = rotation.shouldRotate();

  public PlotRenderer(@Nonnull Context context, @Nonnull PlottingView view) {
    this.context = context;
    this.view = view;
  }

  public void setPlotter(@Nonnull Plotter plotter) {
    float zoomLevel;
    synchronized (zoomer) {
      zoomLevel = zoomer.zoomer.getLevel();
    }
    synchronized (lock) {
      Check.isNull(this.plotter);
      this.plotter = plotter;
      if (!viewDimensions.isEmpty()) {
        this.plotter.updateDimensions(zoomLevel, viewDimensions.width(), viewDimensions.height());
      }
    }
  }

  @Nullable
  private Plotter getPlotter() {
    // plotter might changed only from NULL to some object => if change is already visible from GL thread just use
    // the object. Otherwise, do the whole sync block thing.
    Plotter localPlotter = plotter;
    if (localPlotter != null) {
      return localPlotter;
    }
    synchronized (lock) {
      localPlotter = plotter;
    }
    return localPlotter;
  }

  @Override
  public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    gl.glDisable(GL10.GL_DITHER);
    gl.glDisable(GL10.GL_LIGHTING);

    gl.glClearDepthf(1.0f);
    gl.glEnable(GL10.GL_DEPTH_TEST);
    gl.glDepthFunc(GL10.GL_LEQUAL);

    // let's use fastest perspective calculations
    gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);

    gl.glShadeModel(GL10.GL_SMOOTH);

    // Create the GLText
    glText = new GLText(gl, context.getAssets());

    // Load the font from file (set size + padding), creates the texture
    // NOTE: after a successful call to this the font is ready for rendering!
    glText.load("Roboto-Regular.ttf", 14, 2, 2);  // Create Font (Height: 14 Pixels / X+Y Padding 2 Pixels)

    tryInitGl(gl, false);
  }

  private void tryInitGl(@Nonnull GL10 gl, boolean fromFrameDraw) {
    Check.isGlThread();

    // state might be changed only from false to true. If change is already visible from GL thread there is no
    // point in obtaining the lock. If change is not visible from GL thread - let's obtain the lock and check again
    if (glInitialized) {
      return;
    }

    synchronized (lock) {
      if (glInitialized) {
        return;
      }
      if (plotter != null) {
        initGl((GL11) gl, plotter, fromFrameDraw);
      }
    }
  }

  private void initGl(@Nonnull GL11 gl, @Nonnull Plotter plotter, boolean fromFrameDraw) {
    Check.isTrue(Thread.holdsLock(lock), "Should be called from synchronized block");
    Check.isTrue(!glInitialized, "Should be not initialized");
    Check.isGlThread();

    // surface was created, let's reinitialize all meshes if needed (just to avoid reinitialization in onDrawFrame)
    plotter.initGl(gl, true);

    if (!fromFrameDraw) {
      // we must to request rendering
      view.requestRender();
    }

    glInitialized = true;
  }

  @Override
  public void onDrawFrame(GL10 gl10) {
    spf.logFrameStart();
    tryInitGl(gl10, true);

    final Plotter plotter = getPlotter();
    if (plotter != null) {
      final GL11 gl = (GL11) gl10;

      final float zoomLevel = zoomer.onFrame(gl, plotter);

      gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

      gl.glMatrixMode(GL10.GL_MODELVIEW);
      gl.glLoadIdentity();

      gl.glTranslatef(0, 0, -Dimensions.DISTANCE * zoomLevel);

      rotation.onFrame(gl10);

      plotter.initGl(gl, false);

      gl.glEnable(GL10.GL_BLEND);
      gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);

      gl.glEnable(GL10.GL_TEXTURE_2D);
      glText.begin(1.0f, 1.0f, 1.0f, 1.0f);         // Begin Text Rendering (Set Color WHITE)
      glText.draw("Test String :)", 0, 0, 0);          // Draw Test String
      glText.draw("Line 1", 50, 50, 0);                // Draw Test String
      glText.draw("Line 2", 100, 100, 0);              // Draw Test String
      glText.end();

      glText.begin(1.0f, 0.0f, 0.0f, 1.0f);         // Begin Text Rendering (Set Color RED)
      glText.draw("zoom out !", -200, 0, -800);        // Draw Test String
      glText.draw("zoom in !", -50, -50, 600);        // Draw Test String
      glText.end();
      gl.glDisable(GL10.GL_TEXTURE_2D);

      plotter.draw(gl);

      gl.glDisable(GL10.GL_BLEND);
    }

    if (looping) {
      view.requestRender();
    }
    spf.logFrameEnd();
  }

  private void initFrustum(@Nonnull GL10 gl, float distance) {
    Check.isGlThread();

    if (viewDimensions.isEmpty()) {
      return;
    }

    if (frustum.update(distance, (float) viewDimensions.height() / (float) viewDimensions.width())) {
      gl.glMatrixMode(GL10.GL_PROJECTION);
      gl.glLoadIdentity();
      gl.glFrustumf(-frustum.width / 2, frustum.width / 2, -frustum.height / 2, frustum.height / 2, frustum.near, frustum.far);
    }
  }

  @Override
  public void onSurfaceChanged(GL10 gl, final int width, final int height) {
    viewDimensions.set(0, 0, width, height);
    gl.glViewport(0, 0, width, height);
    zoomer.onSurfaceChanged(gl);
    final float zoomLevel = zoomer.zoomer.getLevel();

    view.post(new Runnable() {
      @Override
      public void run() {
        final Plotter plotter = getPlotter();
        if (plotter != null) {
          plotter.updateDimensions(zoomLevel, width, height);
        }
      }
    });
  }

  public void rotate(float dx, float dy) {
    rotation.setRotationSpeed(dx, dy);
  }

  public void rotateTo(float x, float y) {
    rotation.setRotationTo(x, y);
  }

  public void setRotationSpeed(float speedX, float speedY) {
    rotation.setRotationSpeed(speedX, speedY);
  }

  public void stopRotating() {
    looping = false;
  }

  public void startRotating() {
    synchronized (rotation) {
      final boolean shouldRotate = rotation.shouldRotate();
      loop(shouldRotate);
    }
  }

  private void loop(boolean loop) {
    if (looping != loop) {
      looping = loop;
      if (looping) {
        view.requestRender();
      }
    }
  }

  public void saveState(@Nonnull Bundle bundle) {
    Check.isMainThread();
    rotation.saveState(bundle);
    zoomer.saveState(bundle);
  }

  public void restoreState(@Nonnull Bundle bundle) {
    Check.isMainThread();
    rotation.restoreState(bundle);
    zoomer.restoreState(bundle);
    view.requestRender();
  }

  public void zoom(boolean in) {
    zoomer.zoom(in);
  }

  public void resetZoom() {
    zoomer.reset();
  }

  public void zoomBy(@Nonnull ZoomLevels level) {
    zoomer.zoomBy(level);
  }

  public void setPinchZoom(boolean pinchZoom) {
    zoomer.setPinchZoom(pinchZoom);
  }

  private static final class Rotation {

    private static final float MIN_ROTATION = 0.5f;
    private static final Angle DEFAULT_ANGLE = new Angle(0, 0);
    private static final Angle DEFAULT_SPEED = new Angle(0f, 0.5f);

    @Nonnull
    final Angle angle;

    @Nonnull
    final Angle speed;

    @Nonnull
    final float[] matrix;

    private Rotation() {
      angle = DEFAULT_ANGLE;
      speed = DEFAULT_SPEED;
      matrix = angle.getMatrix();
    }

    private Rotation(@Nonnull Bundle bundle) {
      angle = restoreAngle(bundle, "rotation.angle", DEFAULT_ANGLE);
      speed = restoreAngle(bundle, "rotation.speed", DEFAULT_SPEED);
      final float[] array = bundle.getFloatArray("rotation.matrix");
      matrix = array != null ? array : angle.getMatrix();
    }

    public void saveState(@Nonnull Bundle bundle) {
      bundle.putParcelable("rotation.angle", angle);
      bundle.putParcelable("rotation.speed", speed);
      bundle.putFloatArray("rotation.matrix", matrix);
    }

    @Nonnull
    private static Angle restoreAngle(@Nonnull Bundle bundle, @Nonnull String name, @Nonnull Angle def) {
      final Parcelable angle = bundle.getParcelable(name);
      if (angle instanceof Angle) {
        return (Angle) angle;
      }
      return def;
    }

    public void onFrame() {
      angle.add(speed);
      speed.rotateBy(matrix);
    }

    public boolean shouldRotate() {
      return Math.abs(speed.x) >= MIN_ROTATION || Math.abs(speed.y) >= MIN_ROTATION;
    }
  }

  private final class RotationHolder {

    /**
     * Synchronization is done on the current instance of the field, so it's always in a good state on GL thread
     */
    @GuardedBy("rotation")
    @Nonnull
    Rotation rotation = new Rotation();

    boolean shouldRotate() {
      synchronized (rotation) {
        return rotation.shouldRotate();
      }
    }

    void onFrame(@Nonnull GL10 gl) {
      synchronized (rotation) {
        rotation.onFrame();
        gl.glMultMatrixf(rotation.matrix, 0);
      }
    }

    void setRotationSpeed(float angleX, float angleY) {
      synchronized (rotation) {
        rotation.speed.x = angleX;
        rotation.speed.y = angleY;
      }
      view.requestRender();
    }

    void saveState(@Nonnull Bundle bundle) {
      synchronized (rotation) {
        rotation.saveState(bundle);
      }
    }

    void restoreState(@Nonnull Bundle bundle) {
      synchronized (rotation) {
        rotation = new Rotation(bundle);
        looping = rotation.shouldRotate();
      }
    }

    public void setRotationTo(float x, float y) {
      synchronized (rotation) {
        rotation.angle.x = x;
        rotation.angle.y = y;
        if (x == 0 && y == 0) {
          Matrix.setIdentityM(rotation.matrix, 0);
        } else {
          final float[] newMatrix = rotation.angle.getMatrix();
          System.arraycopy(newMatrix, 0, rotation.matrix, 0, newMatrix.length);
        }
      }
      view.requestRender();
    }
  }

  private final class ZoomerHolder {

    @Nonnull
    private final String TAG = Plot.getTag("Zoomer");

    @GuardedBy("PlotRenderer.this.lock")
    Object frustumZoomer;

    @GuardedBy("zoomer")
    @Nonnull
    volatile Zoomer zoomer = new Zoomer();

    @GuardedBy("zoomer")
    volatile boolean pinchZoom;

    float onFrame(@Nonnull GL11 gl, @Nonnull Plotter plotter) {
      final float zoomLevel;
      synchronized (zoomer) {
        if (zoomer.onFrame()) {
          synchronized (lock) {
            frustumZoomer = zoomer;
          }
          initFrustum(gl, Dimensions.DISTANCE * zoomer.getLevel());

          // if we were running and now we are stopped it's time to update the dimensions
          if (!zoomer.isZooming()) {
            if (!pinchZoom) {
              plotter.updateDimensions(zoomer.getLevel(), viewDimensions.width(), viewDimensions.height());
            }
            startRotating();
          } else {
            // we must loop while zoom is zooming
            looping = true;
          }
        } else {
          synchronized (lock) {
            // frustum is not initialized yet => let's do it
            if (frustumZoomer != zoomer) {
              frustumZoomer = zoomer;
              initFrustum(gl, Dimensions.DISTANCE * zoomer.getLevel());
            }
          }
        }
        zoomLevel = zoomer.getLevel();
      }
      return zoomLevel;
    }

    void onSurfaceChanged(GL10 gl) {
      synchronized (zoomer) {
        initFrustum(gl, Dimensions.DISTANCE * zoomer.getLevel());
      }
    }

    void saveState(@Nonnull Bundle bundle) {
      synchronized (zoomer) {
        Log.d(TAG, "Saving state: " + zoomer);
        zoomer.saveState(bundle);
      }
    }

    void restoreState(@Nonnull Bundle bundle) {
      final float zoomLevel;
      synchronized (zoomer) {
        zoomer = new Zoomer(bundle);
        Log.d(TAG, "Restoring state: " + zoomer);
        zoomLevel = zoomer.getLevel();
      }

      if (!viewDimensions.isEmpty()) {
        final Plotter plotter = getPlotter();
        if (plotter != null) {
          plotter.updateDimensions(zoomLevel, viewDimensions.width(), viewDimensions.height());
        }
      }
    }

    void zoom(boolean in) {
      synchronized (zoomer) {
        if (zoomer.zoom(in)) {
          loop(true);
        }
        if (in) {
          Log.d(TAG, "Zooming in: " + zoomer);
        } else {
          Log.d(TAG, "Zooming out: " + zoomer);
        }
      }
    }

    void reset() {
      synchronized (zoomer) {
        if (zoomer.reset()) {
          loop(true);
        }
        Log.d(TAG, "Resetting: " + zoomer);
      }
    }

    public void zoomBy(@Nonnull ZoomLevels levels) {
      if(!levels.isChanged()) {
        return;
      }

      //if (levels.x != 1f && levels.y != 1f) {
      if (true) {
        final float level = levels.getLevel();

        synchronized (zoomer) {
          if (zoomer.zoomBy(level)) {
            view.requestRender();
          }
          Log.d(TAG, "Zooming by level=" + levels + ". " + zoomer);
        }
      } else {
        final Plotter plotter = getPlotter();
        if (plotter != null) {
          final Dimensions dimensions = plotter.getDimensions();
          dimensions.graph.multiplyBy(levels.x, levels.y);
          plotter.setDimensions(dimensions);
        }
      }
    }

    public void setPinchZoom(boolean pinchZoom) {
      synchronized (zoomer) {
        if (this.pinchZoom != pinchZoom) {
          this.pinchZoom = pinchZoom;
          if (this.pinchZoom) {
            Log.d(TAG, "Starting pinch zoom");
          } else {
            final Plotter plotter = getPlotter();
            if (plotter != null) {
              plotter.updateDimensions(zoomer.getLevel(), viewDimensions.width(), viewDimensions.height());
            }
            Log.d(TAG, "Ending pinch zoom");
          }
        }
      }
    }
  }
}




Java Source Code List

com.android.texample.GLText.java
com.android.texample.SpriteBatch.java
com.android.texample.TexampleRenderer.java
com.android.texample.TextureRegion.java
com.android.texample.Vertices.java
org.solovyev.android.plotter.Angle.java
org.solovyev.android.plotter.AxisStyle.java
org.solovyev.android.plotter.Check.java
org.solovyev.android.plotter.Color.java
org.solovyev.android.plotter.DefaultPlotter.java
org.solovyev.android.plotter.Dimensions.java
org.solovyev.android.plotter.Frustum.java
org.solovyev.android.plotter.Function0.java
org.solovyev.android.plotter.Function1.java
org.solovyev.android.plotter.Function2.java
org.solovyev.android.plotter.Function.java
org.solovyev.android.plotter.LineStyle.java
org.solovyev.android.plotter.MeshConfig.java
org.solovyev.android.plotter.MultisampleConfigChooser.java
org.solovyev.android.plotter.PinchZoomTracker.java
org.solovyev.android.plotter.PlotData.java
org.solovyev.android.plotter.PlotFunction.java
org.solovyev.android.plotter.PlotRenderer.java
org.solovyev.android.plotter.PlotView.java
org.solovyev.android.plotter.Plot.java
org.solovyev.android.plotter.Plotter.java
org.solovyev.android.plotter.PlottingView.java
org.solovyev.android.plotter.Spf.java
org.solovyev.android.plotter.SuperFunction.java
org.solovyev.android.plotter.TouchHandler.java
org.solovyev.android.plotter.ZoomLevels.java
org.solovyev.android.plotter.Zoomer.java
org.solovyev.android.plotter.app.MainActivity.java
org.solovyev.android.plotter.app.PlotterApplication.java
org.solovyev.android.plotter.meshes.Arrays.java
org.solovyev.android.plotter.meshes.AxisGrid.java
org.solovyev.android.plotter.meshes.Axis.java
org.solovyev.android.plotter.meshes.BaseCube.java
org.solovyev.android.plotter.meshes.BaseCurve.java
org.solovyev.android.plotter.meshes.BaseMesh.java
org.solovyev.android.plotter.meshes.BaseSurface.java
org.solovyev.android.plotter.meshes.DimensionsAwareSwapper.java
org.solovyev.android.plotter.meshes.DimensionsAware.java
org.solovyev.android.plotter.meshes.DoubleBufferGroup.java
org.solovyev.android.plotter.meshes.DoubleBufferMesh.java
org.solovyev.android.plotter.meshes.FunctionGraph2d.java
org.solovyev.android.plotter.meshes.FunctionGraph3d.java
org.solovyev.android.plotter.meshes.FunctionGraphSwapper.java
org.solovyev.android.plotter.meshes.FunctionGraph.java
org.solovyev.android.plotter.meshes.Graph.java
org.solovyev.android.plotter.meshes.Group.java
org.solovyev.android.plotter.meshes.IndicesOrder.java
org.solovyev.android.plotter.meshes.ListGroup.java
org.solovyev.android.plotter.meshes.ListPool.java
org.solovyev.android.plotter.meshes.Mesh.java
org.solovyev.android.plotter.meshes.Meshes.java
org.solovyev.android.plotter.meshes.Pool.java
org.solovyev.android.plotter.meshes.Scene.java
org.solovyev.android.plotter.meshes.SolidCube.java
org.solovyev.android.plotter.meshes.SurfaceInitializer.java
org.solovyev.android.plotter.meshes.WireFrameCube.java