Related
I have a set of 3d points S.
I need to find the set X of all sets of points in S which are within manhattan distance d of each other.
i.e. for each set Y in X there exists atleast one point in 3d space that is within distance d of all points in Y
The length of set S will never be >20 but I will have to run this analysis on a stream of sets which are being produced at ~10 new sets per second, so whatever solution I use will have to be fairly efficient.
an example to help visualize the problem, given the following:
the output would be ((A,B), (B,C,E), (B,D,E))
we only care about the largest possible sets so the sets (B,C), (B,D), (B,E), (C,E) and (D,E), while within the given parameters, are not in the output given they are subsets of other sets in X
also this I'm doing this in java but any pointers in terms of algorithms or pseudo code would be greatly appreciated, thanks in advance.
A solution in pseudocode would be:
calculate_intersections(areas):
intersections = calculate every two intersecting areas
combinations = combine_intersections(intersections)
reduced = remove all sets in combinations that are included in bigger sets
combine_intersections(intersections):
do:
combinations = new HashSet
for s1 in intersections:
for s2 in intersections:
diff_1_2 = s1 \ s2
diff_2_1 = s2 \ s1
if diff_1_2.len == 1 && diff_2_1.len == 1:
union = diff_1_2 + diff_2_1
if union in intersections:
union2 = s1 + s2
if !union2 in intersections:
combinations.add(union)
while (combinations not empty)
An implementation in Java could look like this:
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.apache.commons.collections4.SetUtils;
public class IntersectionSetCalculation {
private static class ManhattanDistanceArea {
private String id;
private Vector3D center;
private double distance;
public ManhattanDistanceArea(Vector3D center, double distance, String id) {
this.center = center;
this.distance = distance;
this.id = id;
}
#Override
public String toString() {
return id;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((center == null) ? 0 : center.hashCode());
long temp;
temp = Double.doubleToLongBits(distance);
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ManhattanDistanceArea other = (ManhattanDistanceArea) obj;
if (center == null) {
if (other.center != null)
return false;
}
else if (!center.equals(other.center))
return false;
if (Double.doubleToLongBits(distance) != Double.doubleToLongBits(other.distance))
return false;
if (id == null) {
if (other.id != null)
return false;
}
else if (!id.equals(other.id))
return false;
return true;
}
public boolean intersects(ManhattanDistanceArea other) {
double maxDist = distance + other.distance;
return center.distance(other.center, 1) < maxDist;
}
}
/**
* Calculate the intersection of all areas (maximum of 2 areas in an intersection)
*/
public static Set<Set<ManhattanDistanceArea>> getIntersectingAreas(Set<ManhattanDistanceArea> areas) {
Set<Set<ManhattanDistanceArea>> intersections = new HashSet<Set<ManhattanDistanceArea>>();
for (ManhattanDistanceArea area : areas) {
for (ManhattanDistanceArea area2 : areas) {
if (!area.equals(area2) && area.intersects(area2)) {
HashSet<ManhattanDistanceArea> intersection = new HashSet<ManhattanDistanceArea>();
intersection.add(area);
intersection.add(area2);
intersections.add(intersection);
}
}
}
Set<Set<ManhattanDistanceArea>> combined = combineIntersections(intersections);
Set<Set<ManhattanDistanceArea>> reduced = reduceIntersections(combined);
return reduced;
}
/**
* Combine the small intersections (with a maximum of 2 areas in an intersection) to bigger intersections
*/
public static Set<Set<ManhattanDistanceArea>> combineIntersections(Set<Set<ManhattanDistanceArea>> inters) {
Set<Set<ManhattanDistanceArea>> intersections = new HashSet<Set<ManhattanDistanceArea>>(inters);
Set<Set<ManhattanDistanceArea>> combinations;
do {
combinations = new HashSet<Set<ManhattanDistanceArea>>();
for (Set<ManhattanDistanceArea> intersecting1 : intersections) {
for (Set<ManhattanDistanceArea> intersecting2 : intersections) {
Set<ManhattanDistanceArea> diff_1_2 = SetUtils.difference(intersecting1, intersecting2);
Set<ManhattanDistanceArea> diff_2_1 = SetUtils.difference(intersecting2, intersecting1);
if (diff_1_2.size() == 1 && diff_2_1.size() == 1) {
Set<ManhattanDistanceArea> union_1_2 = SetUtils.union(diff_1_2, diff_2_1);
if (intersections.contains(union_1_2)) {
Set<ManhattanDistanceArea> union = SetUtils.union(intersecting1, intersecting2);
if (!intersections.contains(union)) {
combinations.add(union);
}
}
}
}
}
intersections.addAll(combinations);
} while (!combinations.isEmpty());
return intersections;
}
/**
* Remove the small intersections that are completely covered by bigger intersections
*/
public static Set<Set<ManhattanDistanceArea>> reduceIntersections(Set<Set<ManhattanDistanceArea>> inters) {
Set<Set<ManhattanDistanceArea>> intersections = new HashSet<Set<ManhattanDistanceArea>>(inters);
Iterator<Set<ManhattanDistanceArea>> iter = intersections.iterator();
while (iter.hasNext()) {
Set<ManhattanDistanceArea> intersection = iter.next();
for (Set<ManhattanDistanceArea> intersection2 : inters) {
if (intersection2.size() > intersection.size() && intersection2.containsAll(intersection)) {
iter.remove();
break;
}
}
}
return intersections;
}
public static void main(String[] args) {
final double dist = 2d;//the manhattan distance d
ManhattanDistanceArea A = new ManhattanDistanceArea(new Vector3D(0, -3, 0), dist, "A");
ManhattanDistanceArea B = new ManhattanDistanceArea(new Vector3D(0, 0, 0), dist, "B");
ManhattanDistanceArea C = new ManhattanDistanceArea(new Vector3D(3.5, 0, 0), dist, "C");
ManhattanDistanceArea D = new ManhattanDistanceArea(new Vector3D(0, 3.5, 0), dist, "D");
ManhattanDistanceArea E = new ManhattanDistanceArea(new Vector3D(1, 1, 0), dist, "E");
ManhattanDistanceArea F = new ManhattanDistanceArea(new Vector3D(-1, 1, 0), dist, "F");
//test the example you provided
Set<ManhattanDistanceArea> abcde = new HashSet<ManhattanDistanceArea>();
abcde.addAll(Arrays.asList(new ManhattanDistanceArea[] {A, B, C, D, E}));
//test another example
Set<ManhattanDistanceArea> abcdef = new HashSet<ManhattanDistanceArea>();
abcdef.addAll(abcde);
abcdef.add(F);
Set<Set<ManhattanDistanceArea>> intersectionsABCDE = getIntersectingAreas(abcde);
Set<Set<ManhattanDistanceArea>> intersectionsABCDEF = getIntersectingAreas(abcdef);
System.out.println(intersectionsABCDE);
System.out.println(intersectionsABCDEF);
//test the runntime for 1000 calculation
double startTime = System.currentTimeMillis();
final int calculations = 1000;
for (int i = 0; i < calculations; i++) {
Set<ManhattanDistanceArea> areas = new HashSet<ManhattanDistanceArea>();
for (int j = 0; j < 20; j++) {
areas.add(new ManhattanDistanceArea(new Vector3D(Math.random() * 10 - 5, Math.random() * 10 - 5, Math.random() * 10 - 5), dist,
"A" + j));
}
getIntersectingAreas(areas);
}
System.out.println("\nTime used for " + calculations + " intersection calculations (with sets of size 20): "
+ (System.currentTimeMillis() - startTime) + "ms");
}
}
For the implementation I used this class Vector3D:
public class Vector3D {
public double x;
public double y;
public double z;
public static final Vector3D NAN_VEC = new Vector3D(Double.NaN, Double.NaN, Double.NaN);
public static final Vector3D NULL_VEC = new Vector3D(0, 0, 0);
public enum Axis {
X, Y, Z;
}
public Vector3D() {
}
/**
* Crate a new Vector2D with x and y components.
*/
public Vector3D(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public Vector3D(double... val) {
x = val[0];
y = val[1];
z = val[2];
}
/**
* Create a Vector3D by two angles (in degree).
*
* The first angle is in XY direction. The second angle is the Z direction.
*
* An angle (XY) of 0° results in (x, y) = (1, 0); 90° in (x, y) = (0, 1); ... An angle (Z) of 0° results in (x, y, z) = (x, y, 0); 90° in (x, y,
* z) = (x, y, 1); -90° in (x, y, z) = (x, y, -1)
*
* The resulting vector has a length of 1.
*
* #param angleXY
* The angle of the new vector (in degree) for the XY direction (from 0 to 360).
*
* #param angleZ
* The angle of the new vector (in degree) for the Z direction (from -90 to 90).
*/
public Vector3D(double angleXY, double angleZ) {
x = Math.cos(angleXY * Math.PI / 180) * Math.cos(angleZ * Math.PI / 180);
y = Math.sin(angleXY * Math.PI / 180) * Math.cos(angleZ * Math.PI / 180);
z = Math.sin(angleZ * Math.PI / 180);
double len = length();
x /= len;
y /= len;
z /= len;
}
private Vector3D(Vector3D clone) {
this.x = clone.x;
this.y = clone.y;
}
#Override
public Vector3D clone() {
return new Vector3D(this);
}
#Override
public String toString() {
return "Vector3D[x: " + x + " y: " + y + " z:" + z + "]";
}
#Override
public boolean equals(Object obj) {
if (obj instanceof Vector3D) {
Vector3D v = (Vector3D) obj;
return Math.abs(x - v.x) < 1e-8 && Math.abs(y - v.y) < 1e-8 && Math.abs(z - v.z) < 1e-8;
}
return false;
}
/**
* Get this vector as 3D-Array.
*/
public double[] asArray() {
return new double[] {x, y, z};
}
/**
* The (euclidean) length of the Vector.
*/
public double length() {
return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2));
}
/**
* The length of this vector in a given norm.
*
* #param norm
* The norm of the vector length.
*
* #return The length of this vector in the given norm.
*/
public double length(int norm) {
if (norm == Integer.MAX_VALUE) {
return Math.max(Math.max(x, y), z);
}
return Math.pow(Math.pow(x, norm) + Math.pow(y, norm) + Math.pow(z, norm), 1.0 / norm);
}
/**
* Rotate this vector an angle (in degrees) around an axis resulting in a new Vector that is returned.
*
* #param degrees
* The angle to return the vector.
*
* #param axis
* The axis around which the vector is rotated.
*
* #return The new created vector.
*/
public Vector3D rotate(double degrees, Axis axis) {
double cos = Math.cos(degrees * Math.PI / 180);
double sin = Math.sin(degrees * Math.PI / 180);
switch (axis) {
case X:
return new Vector3D(x, cos * y - sin * z, sin * y + cos * z);
case Y:
return new Vector3D(cos * x + sin * z, y, -sin * x + cos * z);
case Z:
return new Vector3D(cos * x - sin * y, sin * x + cos * y, z);
default:
return null;
}
}
/**
* Project the vector given as parameter on this vector.
*
* #param vec
* The vector that is to be projected on this vector.
*
* #return The projected vector.
*/
public Vector3D project(Vector3D vec) {
return mult(scalar(vec) / Math.pow(length(), 2));
}
/**
* Add another Vector3D to this vector resulting in a new Vector that is returned.
*
* #param vec
* The vector added to this vector.
*
* #return The new created vector.
*/
public Vector3D add(Vector3D vec) {
return new Vector3D(x + vec.x, y + vec.y, z + vec.z);
}
/**
* Subtract another Vector3D from this vector resulting in a new Vector that is returned.
*
* #param vec
* The vector subtracted from this vector.
*
* #return The new created vector.
*/
public Vector3D sub(Vector3D vec) {
return new Vector3D(x - vec.x, y - vec.y, z - vec.z);
}
/**
* Multiply this vector with a scalar resulting in a new Vector that is returned.
*
* #param scalar
* The scalar to multiply this vector with.
*
* #return The new created vector.
*/
public Vector3D mult(double scalar) {
return new Vector3D(x * scalar, y * scalar, z * scalar);
}
/**
* Check whether this vector is linearly dependent to the parameter vector.
*
* #param vec
* The checked vector.
*
* #return True if the vectors are linearly dependent. False otherwise.
*/
public boolean isLinearlyDependent(Vector3D vec) {
double t1 = (x == 0 ? 0 : vec.x / x);
double t2 = (y == 0 ? 0 : vec.y / y);
double t3 = (z == 0 ? 0 : vec.z / z);
return Math.abs(t1 - t2) < 1e-5 && Math.abs(t1 - t3) < 1e-5 && t1 != 0;//all parameters t are equal and != 0
}
/**
* Calculate the scalar product of this vector and the parameter vector.
*
* #param vec
* The vector to calculate the scalar with this vector.
*
* #return The scalar of the vectors.
*/
public double scalar(Vector3D vec) {
return this.x * vec.x + this.y * vec.y + this.z * vec.z;
}
/**
* Calculate the cross product of this vector with another vector (resulting vector = this X parameter vector)
*
* #param vec
* The second vector for the cross product calculation.
*
* #return The cross product vector of the two vectors.
*/
public Vector3D cross(Vector3D vec) {
return new Vector3D(y * vec.z - z * vec.y, z * vec.x - x * vec.z, x * vec.y - y * vec.x);
}
/**
* Create a new vector with the same direction but a different length as this vector.
*
* #param length
* The length of the new vector.
*
* #return The new vector with a new length.
*/
public Vector3D setLength(double length) {
double len = length();
return new Vector3D(x * length / len, y * length / len, z * length / len);
}
/**
* Get the distance of this point's position vector to another point's position vector.
*
* #param p
* The second point's position vector.
*
* #return The distance between the points.
*/
public double distance(Vector3D p) {
return Math.sqrt((this.x - p.x) * (this.x - p.x) + (this.y - p.y) * (this.y - p.y) + (this.z - p.z) * (this.z - p.z));
}
/**
* Get the distance of this point's position vector to another point's position vector in a given norm.
*
* #param p
* The second point's position vector.
*
* #param norm
* The norm in which the distance is calculated (1 -> manhattan, 2 -> euclide, ...)
*
* #return The distance between the points in the given norm.
*/
public double distance(Vector3D p, int norm) {
return Math.pow((Math.pow(Math.abs(this.x - p.x), norm) + Math.pow(Math.abs(this.y - p.y), norm) + Math.pow(Math.abs(this.z - p.z), norm)),
1d / norm);
}
/**
* Change this vector to the new coordinates.
*/
public void move(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
/**
* Move a point's position vector in a direction (by a vector) and a distance.
*
* #param p
* The direction vector.
*
* #param distance
* The distance to move the new vector
*
* #return The new created vector.
*/
public Vector3D moveTo(Vector3D p, double distance) {
double d = distance(p);
double dx = p.x - x;
double dy = p.y - y;
double dz = p.z - z;
double coef = distance / d;
return new Vector3D(x + dx * coef, y + dy * coef, z + dz * coef);
}
/**
* Get the angle difference of this vector to another vector.
*
* #param vec
* The other vector.
*
* #return The angle difference of the two vectors (from 0° to 180°).
*/
public double getAngleTo(Vector3D vec) {
double angle = Math.acos(scalar(vec) / (length() * vec.length())) * 180 / Math.PI;
if (angle > 180) {
angle = 360 - angle;
}
return angle;
}
/**
* Get the vector from this point to another.
*
* #param vec
* The point to which the vector is calculated.
*
* #return The vector from this points position vector to the other point.
*/
public Vector3D vectorTo(Vector3D vec) {
return new Vector3D(vec.x - x, vec.y - y, vec.z - z);
}
/**
* Checks whether a point (by its position vector) is in a given range of this point.
*
* #param p
* The point that is checked.
*
* #param range
* The range used for the check.
*
* #return True if the point is in the range of this point (distance <= range).
*/
public boolean isInRange(Vector3D p, double range) {
return p != this && distance(p) <= range;
}
}
and the class SetUtils from the apache commons lib.
I also added some tests:
the test from your question
another test with a bigger intersection set
a test for the runtime
The results are:
[[A, B], [B, E, C], [B, E, D]]
[[A, B], [B, E, C], [D, E, F, B]]
Time used for 1000 intersection calculations (with sets of size 20):
791.0ms
So the results seem to be correct and you can calculate more than 1000 intersections in a second.
Exhaustive distance computation between 20 points, i.e. 190 distances is nothing for a PC. Time will measure in microseconds. You can draw the desired information from the "close to" relation encoded in a matrix.
I am using this class :
import com.badlogic.gdx.graphics.g2d.TextureRegion;
public class ParallaxLayer {
// the Texture sitting on this layer
public TextureRegion region;
/**
* how much shall this layer (in percent) be moved if the whole background is moved
* 0.5f is half as fast as the speed
* 2.0f is twice the speed
*/
float ratioX, ratioY;
/**
* current position
*/
float positionX, positionY;
/**
*
* #param pRegion
* #param pRatioX
* #param pRatioY
*/
public ParallaxLayer(TextureRegion pRegion, float pRatioX, float pRatioY) {
region = pRegion;
ratioX = pRatioX;
ratioY = pRatioY;
}
/**
* move this layer
* #param pDelta
*/
protected void moveX(float pDelta) {
positionX += pDelta * ratioX;
}
/**
* move this layer
* #param pDelta
*/
protected void moveY(float pDelta) {
positionY += pDelta * ratioY;
}
}
and this class :
import com.badlogic.gdx.graphics.g2d.TextureRegion;
public class ParallaxLayer {
/**
* the Texture sitting on this layer
*/
public TextureRegion region;
/**
* how much shall this layer (in percent) be moved if the whole background is moved
* 0.5f is half as fast as the speed
* 2.0f is twice the speed
*/
float ratioX, ratioY;
/**
* current position
*/
float positionX, positionY;
/**
*
* #param pRegion
* #param pRatioX
* #param pRatioY
*/
public ParallaxLayer(TextureRegion pRegion, float pRatioX, float pRatioY) {
region = pRegion;
ratioX = pRatioX;
ratioY = pRatioY;
}
/**
* move this layer
* #param pDelta
*/
protected void moveX(float pDelta) {
positionX += pDelta * ratioX;
}
/**
* move this layer
* #param pDelta
*/
protected void moveY(float pDelta) {
positionY += pDelta * ratioY;
}
}
And in main class :
camera=new OrthographicCamera(400,240);
camera.position.x=200;
camera.position.y=120;
camera.update();
batch=new SpriteBatch();
layer1=atlas.findRegion("layer1");
layer2=atlas.findRegion("layer2");
layer3=atlas.findRegion("layer3");
ParallaxLayer l1=new ParallaxLayer(layer1,0,0);
ParallaxLayer l2=new ParallaxLayer(layer2,0.5f,0);
ParallaxLayer l3=new ParallaxLayer(layer3,1,0);
ParallaxLayer[] layers={l1,l2,l3};
background=new ParallaxBackground(layers, camera,batch);
// [...] in render
background.moveX(30*delta); // move to the right to show the effect
background.render();
to achieve parallax scrolling effect but i want infinite scrolling but unable to get it. I tried doing this in ParallaxBackground class under for loop but repeats only three time.
posXbg1L1 = layer.positionX;
posXbg2L1 = posXbg1L1 - layer.region.getRegionWidth();
if (camera.position.x <= posXbg2L1 - camera.viewportWidth / 2) {
// Gdx.app.log("TAG", camera.position.x + ":" + posXbg2L1 + camera.viewportWidth / 2);
posXbg1L1 = posXbg2L1;
}
batch.draw(layer.region, -camera.viewportWidth / 2
- posXbg1L1, -camera.viewportHeight / 2
- layer.positionY);
batch.draw(layer.region, -camera.viewportWidth / 2
- posXbg2L1, -camera.viewportHeight / 2
- layer.positionY);
}
Any psuedocode/code will be helpful.
You could try something like this:
TextureRegion[] backgrounds = [...your array of background textures...];
float[] parallax = {...your parallax coefficients...}; //For example {0.2f, 0.1f}
public void drawLayers(Batch batch, OrthographicCamera camera) {
batch.setColor(Color.WHITE);
for(int b = backgrounds.length - 1; b >= 0; b--) {
TextureRegion background = backgrounds[b];
if(background != null) {
float x = (camera.position.x - camera.viewportWidth / 2f * camera.zoom);
float y = camera.position.y - camera.viewportHeight / 2f * camera.zoom + camera.viewportHeight / 15f * camera.zoom;
float rWidth = camera.viewportWidth * 1.5f * camera.zoom;
float rHeight = (rWidth / background.getRegionWidth()) * background.getRegionHeight();
drawParallaxLayer(batch, background, parallax[b], x, y, rWidth, rHeight);
}
}
}
public static void drawParallaxLayer(Batch batch, TextureRegion region, float parallax, float x, float y, float width, float height) {
for(int j = 0; j < 3; j++) {
batch.draw(region, x + (j * width) - ((x * parallax) % width) - (width / 2f), y, width, height);
}
}
You might have to adjust some of the position/width/height values in the drawLayers function, but the real magic happens in drawParallaxLayer - which should be able to stay the same.
I am trying to randmly create set of points with random position of an initial point.
Unfortunatly almost all of my generated points are located in upper left corner. Is there a mistake in my calculations?
private void makePath() {
int r = 25;
Random rand = new Random();
double angle = Math.toRadians(rand.nextInt(361));
int nx = (int) (x + r * Math.cos(angle));
int ny = (int) (y - r * Math.sin(angle));
path.add(new Point2D(nx, ny));
}
For the values, try using:
int nx = (int) (x + r * Math.cos(angle));
int ny = (int) (y + r * Math.sin(angle));
Using y + r * ... instead. This is coming from a method I wrote to do the exact same thing:
/**
* Generates a new point within a circular radius from
* the given <tt>x</tt> and <tt>y</tt> coordinates. This
* is done by generating a random offset along the
* horizontal axis, then applying a rotation.
*
* #param seed The seed to generate the point from.
* #param x The x coordinate of the center of the
* circle.
* #param y The y coordinate of the center of the
* circle.
* #return The newly generated point, within the
* specified radius of the point <tt>(x, y)
* </tt>.
* #see ClusterGenerator#RADIUS
*/
private static Point makePoint(final Random seed, final int x, final int y) {
final int offset = seed.nextInt(RADIUS);
final double rotation = 2 * Math.PI * Math.random();
final int x1 = (int) (Math.sin(rotation) * offset);
final int y1 = (int) (Math.cos(rotation) * offset);
return new Point(x + x1, y + y1);
}
this is page-curl view , i want when nextView() is called my TextView get the next string
but I can't use TextView from xml layout it gives me null ,
how I can but buttons and TextView in customView ?
can any one help me to solve this with code ?
I have searched with many tutorials but I get this understanding
activity class :
public class StandaloneExample extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.standalone_example);
}
#Override
public void onDestroy(){
super.onDestroy();
System.gc();
finish();
}
/**
* Set the current orientation to landscape. This will prevent the OS from changing
* the app's orientation.
*/
public void lockOrientationLandscape() {
lockOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
/**
* Set the current orientation to portrait. This will prevent the OS from changing
* the app's orientation.
*/
public void lockOrientationPortrait() {
lockOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
public void lockOrientation( int orientation ) {
setRequestedOrientation(orientation);
}
}
pageCurlView :
public class PageCurlView extends View {
private class Vector2D {
public float x, y;
public Vector2D(float x, float y) {
this.x = x;
this.y = y;
}
#Override
public String toString() {
// TODO Auto-generated method stub
return "(" + this.x + "," + this.y + ")";
}
public float length() {
return (float) Math.sqrt(x * x + y * y);
}
public float lengthSquared() {
return (x * x) + (y * y);
}
public boolean equals(Object o) {
if (o instanceof Vector2D) {
Vector2D p = (Vector2D) o;
return p.x == x && p.y == y;
}
return false;
}
public Vector2D reverse() {
return new Vector2D(-x, -y);
}
public Vector2D sum(Vector2D b) {
return new Vector2D(x + b.x, y + b.y);
}
public Vector2D sub(Vector2D b) {
return new Vector2D(x - b.x, y - b.y);
}
public float dot(Vector2D vec) {
return (x * vec.x) + (y * vec.y);
}
public float cross(Vector2D a, Vector2D b) {
return a.cross(b);
}
public float cross(Vector2D vec) {
return x * vec.y - y * vec.x;
}
public float distanceSquared(Vector2D other) {
float dx = other.x - x;
float dy = other.y - y;
return (dx * dx) + (dy * dy);
}
public float distance(Vector2D other) {
return (float) Math.sqrt(distanceSquared(other));
}
public float dotProduct(Vector2D other) {
return other.x * x + other.y * y;
}
public Vector2D normalize() {
float magnitude = (float) Math.sqrt(dotProduct(this));
return new Vector2D(x / magnitude, y / magnitude);
}
public Vector2D mult(float scalar) {
return new Vector2D(x * scalar, y * scalar);
}
}
/**
* Inner class used to make a fixed timed animation of the curl effect.
*/
class FlipAnimationHandler extends Handler {
#Override
public void handleMessage(Message msg) {
PageCurlView.this.FlipAnimationStep();
}
public void sleep(long millis) {
this.removeMessages(0);
sendMessageDelayed(obtainMessage(0), millis);
}
}
/**
* Base
*
* #param context
*/
public PageCurlView(Context context) {
super(context);
init(context);
ResetClipEdge();
}
/**
* Construct the object from an XML file. Valid Attributes:
*
* #see android.view.View#View(android.content.Context,
* android.util.AttributeSet)
*/
public PageCurlView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
// Get the data from the XML AttributeSet
{
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.PageCurlView);
// Get data
bEnableDebugMode = a.getBoolean(
R.styleable.PageCurlView_enableDebugMode, bEnableDebugMode);
mCurlSpeed = a.getInt(R.styleable.PageCurlView_curlSpeed,
mCurlSpeed);
mUpdateRate = a.getInt(R.styleable.PageCurlView_updateRate,
mUpdateRate);
mInitialEdgeOffset = a.getInt(
R.styleable.PageCurlView_initialEdgeOffset,
mInitialEdgeOffset);
mCurlMode = a.getInt(R.styleable.PageCurlView_curlMode, mCurlMode);
Log.i(TAG, "mCurlSpeed: " + mCurlSpeed);
Log.i(TAG, "mUpdateRate: " + mUpdateRate);
Log.i(TAG, "mInitialEdgeOffset: " + mInitialEdgeOffset);
Log.i(TAG, "mCurlMode: " + mCurlMode);
// recycle object (so it can be used by others)
a.recycle();
}
ResetClipEdge();
}
/**
* Initialize the view
*/
private final void init(Context context) {
LayoutInflater.from(context).inflate(R.layout.standalone_example, null,
true);
// left text view
textViewContent = (TextView) this.findViewById(R.id.textView);
// Foreground text paint
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(16);
mTextPaint.setColor(0xFF000000);
// The shadow
mTextPaintShadow = new TextPaint();
mTextPaintShadow.setAntiAlias(true);
mTextPaintShadow.setTextSize(16);
mTextPaintShadow.setColor(0x00000000);
// Cache the context
mContext = new WeakReference<Context>(context);
hesham = context;
// Base padding
setPadding(3, 3, 3, 3);
// The focus flags are needed
setFocusable(true);
setFocusableInTouchMode(true);
mMovement = new Vector2D(0, 0);
mFinger = new Vector2D(0, 0);
mOldMovement = new Vector2D(0, 0);
// Create our curl animation handler
mAnimationHandler = new FlipAnimationHandler();
// Create our edge paint
mCurlEdgePaint = new Paint();
mCurlEdgePaint.setColor(Color.WHITE);
mCurlEdgePaint.setAntiAlias(true);
mCurlEdgePaint.setStyle(Paint.Style.FILL);
mCurlEdgePaint.setShadowLayer(10, -5, 5, 0x99000000);
// Set the default props, those come from an XML :D
mCurlSpeed = 120;
mUpdateRate = 66;
mInitialEdgeOffset = 30;
mCurlMode = 2;
// LEGACY PAGE HANDLING!
// Create pages
mPages = new ArrayList<Bitmap>();
mPages.add(BitmapFactory.decodeResource(getResources(),
R.drawable.page1));
mPages.add(BitmapFactory.decodeResource(getResources(),
R.drawable.page2));
// Create some sample images
mForeground = mPages.get(0);
mBackground = mPages.get(1);
}
/**
* Reset points to it's initial clip edge state
*/
public void ResetClipEdge() {
// Set our base movement
mMovement.x = mInitialEdgeOffset;
mMovement.y = mInitialEdgeOffset;
mOldMovement.x = 0;
mOldMovement.y = 0;
// Now set the points
// TODO: OK, those points MUST come from our measures and
// the actual bounds of the view!
mA = new Vector2D(mInitialEdgeOffset, 0);
mB = new Vector2D(this.getWidth(), this.getHeight());
mC = new Vector2D(this.getWidth(), 0);
mD = new Vector2D(0, 0);
mE = new Vector2D(0, 0);
mF = new Vector2D(0, 0);
mOldF = new Vector2D(0, 0);
// The movement origin point
mOrigin = new Vector2D(this.getWidth(), 0);
}
/**
* Return the context which created use. Can return null if the context has
* been erased.
*/
private Context GetContext() {
return mContext.get();
}
/**
* See if the current curl mode is dynamic
*
* #return TRUE if the mode is CURLMODE_DYNAMIC, FALSE otherwise
*/
public boolean IsCurlModeDynamic() {
return mCurlMode == CURLMODE_DYNAMIC;
}
/**
* Set the curl speed.
*
* #param curlSpeed
* - New speed in px/frame
* #throws IllegalArgumentException
* if curlspeed < 1
*/
public void SetCurlSpeed(int curlSpeed) {
if (curlSpeed < 1)
throw new IllegalArgumentException(
"curlSpeed must be greated than 0");
mCurlSpeed = curlSpeed;
}
/**
* Get the current curl speed
*
* #return int - Curl speed in px/frame
*/
public int GetCurlSpeed() {
return mCurlSpeed;
}
/**
* Set the update rate for the curl animation
*
* #param updateRate
* - Fixed animation update rate in fps
* #throws IllegalArgumentException
* if updateRate < 1
*/
public void SetUpdateRate(int updateRate) {
if (updateRate < 1)
throw new IllegalArgumentException(
"updateRate must be greated than 0");
mUpdateRate = updateRate;
}
/**
* Get the current animation update rate
*
* #return int - Fixed animation update rate in fps
*/
public int GetUpdateRate() {
return mUpdateRate;
}
/**
* Set the initial pixel offset for the curl edge
*
* #param initialEdgeOffset
* - px offset for curl edge
* #throws IllegalArgumentException
* if initialEdgeOffset < 0
*/
public void SetInitialEdgeOffset(int initialEdgeOffset) {
if (initialEdgeOffset < 0)
throw new IllegalArgumentException(
"initialEdgeOffset can not negative");
mInitialEdgeOffset = initialEdgeOffset;
}
/**
* Get the initial pixel offset for the curl edge
*
* #return int - px
*/
public int GetInitialEdgeOffset() {
return mInitialEdgeOffset;
}
/**
* Set the curl mode.
* <p>
* Can be one of the following values:
* </p>
* <table>
* <colgroup align="left" /> <colgroup align="left" />
* <tr>
* <th>Value</th>
* <th>Description</th>
* </tr>
* <tr>
* <td>
* <code>{#link #CURLMODE_SIMPLE com.dcg.pagecurl:CURLMODE_SIMPLE}</code></td>
* <td>Curl target will move only in one axis.</td>
* </tr>
* <tr>
* <td>
* <code>{#link #CURLMODE_DYNAMIC com.dcg.pagecurl:CURLMODE_DYNAMIC}</code></td>
* <td>Curl target will move on both X and Y axis.</td>
* </tr>
* </table>
*
* #see #CURLMODE_SIMPLE
* #see #CURLMODE_DYNAMIC
* #param curlMode
* #throws IllegalArgumentException
* if curlMode is invalid
*/
public void SetCurlMode(int curlMode) {
if (curlMode != CURLMODE_SIMPLE && curlMode != CURLMODE_DYNAMIC)
throw new IllegalArgumentException("Invalid curlMode");
mCurlMode = curlMode;
}
/**
* Return an integer that represents the current curl mode.
* <p>
* Can be one of the following values:
* </p>
* <table>
* <colgroup align="left" /> <colgroup align="left" />
* <tr>
* <th>Value</th>
* <th>Description</th>
* </tr>
* <tr>
* <td>
* <code>{#link #CURLMODE_SIMPLE com.dcg.pagecurl:CURLMODE_SIMPLE}</code></td>
* <td>Curl target will move only in one axis.</td>
* </tr>
* <tr>
* <td>
* <code>{#link #CURLMODE_DYNAMIC com.dcg.pagecurl:CURLMODE_DYNAMIC}</code></td>
* <td>Curl target will move on both X and Y axis.</td>
* </tr>
* </table>
*
* #see #CURLMODE_SIMPLE
* #see #CURLMODE_DYNAMIC
* #return int - current curl mode
*/
public int GetCurlMode() {
return mCurlMode;
}
/**
* Enable debug mode. This will draw a lot of data in the view so you can
* track what is happening
*
* #param bFlag
* - boolean flag
*/
public void SetEnableDebugMode(boolean bFlag) {
bEnableDebugMode = bFlag;
}
/**
* Check if we are currently in debug mode.
*
* #return boolean - If TRUE debug mode is on, FALSE otherwise.
*/
public boolean IsDebugModeEnabled() {
return bEnableDebugMode;
}
/**
* #see android.view.View#measure(int, int)
*/
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int finalWidth, finalHeight;
finalWidth = measureWidth(widthMeasureSpec);
finalHeight = measureHeight(heightMeasureSpec);
setMeasuredDimension(finalWidth, finalHeight);
}
/**
* Determines the width of this view
*
* #param measureSpec
* A measureSpec packed into an int
* #return The width of the view, honoring constraints from measureSpec
*/
private int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else {
// Measure the text
result = specSize;
}
return result;
}
/**
* Determines the height of this view
*
* #param measureSpec
* A measureSpec packed into an int
* #return The height of the view, honoring constraints from measureSpec
*/
private int measureHeight(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else {
// Measure the text (beware: ascent is a negative number)
result = specSize;
}
return result;
}
/**
* Render the text
*
* #see android.view.View#onDraw(android.graphics.Canvas)
*/
// #Override
// protected void onDraw(Canvas canvas) {
// super.onDraw(canvas);
// canvas.drawText(mText, getPaddingLeft(), getPaddingTop() - mAscent,
// mTextPaint);
// }
// ---------------------------------------------------------------
// Curling. This handles touch events, the actual curling
// implementations and so on.
// ---------------------------------------------------------------
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!bBlockTouchInput) {
// Get our finger position
mFinger.x = event.getX();
mFinger.y = event.getY();
int width = getWidth();
// Depending on the action do what we need to
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mOldMovement.x = mFinger.x;
mOldMovement.y = mFinger.y;
// If we moved over the half of the display flip to next
if (mOldMovement.x > (width >> 1)) {
mMovement.x = mInitialEdgeOffset;
mMovement.y = mInitialEdgeOffset;
// Set the right movement flag
bFlipRight = true;
} else {
// Set the left movement flag
bFlipRight = false;
// go to next previous page
previousView();
// Set new movement
mMovement.x = IsCurlModeDynamic() ? width << 1 : width;
mMovement.y = mInitialEdgeOffset;
}
break;
case MotionEvent.ACTION_UP:
bUserMoves = false;
bFlipping = true;
FlipAnimationStep();
break;
case MotionEvent.ACTION_MOVE:
bUserMoves = true;
// Get movement
mMovement.x -= mFinger.x - mOldMovement.x;
mMovement.y -= mFinger.y - mOldMovement.y;
mMovement = CapMovement(mMovement, true);
// Make sure the y value get's locked at a nice level
if (mMovement.y <= 1)
mMovement.y = 1;
// Get movement direction
if (mFinger.x < mOldMovement.x) {
bFlipRight = true;
} else {
bFlipRight = false;
}
// Save old movement values
mOldMovement.x = mFinger.x;
mOldMovement.y = mFinger.y;
// Force a new draw call
DoPageCurl();
this.invalidate();
break;
}
}
// TODO: Only consume event if we need to.
return true;
}
/**
* Make sure we never move too much, and make sure that if we move too much
* to add a displacement so that the movement will be still in our radius.
*
* #paramradius - radius form the flip origin
* #param bMaintainMoveDir
* - Cap movement but do not change the current movement
* direction
* #return Corrected point
*/
private Vector2D CapMovement(Vector2D point, boolean bMaintainMoveDir) {
// Make sure we never ever move too much
if (point.distance(mOrigin) > mFlipRadius) {
if (bMaintainMoveDir) {
// Maintain the direction
point = mOrigin.sum(point.sub(mOrigin).normalize()
.mult(mFlipRadius));
} else {
// Change direction
if (point.x > (mOrigin.x + mFlipRadius))
point.x = (mOrigin.x + mFlipRadius);
else if (point.x < (mOrigin.x - mFlipRadius))
point.x = (mOrigin.x - mFlipRadius);
point.y = (float) (Math.sin(Math.acos(Math.abs(point.x
- mOrigin.x)
/ mFlipRadius)) * mFlipRadius);
}
}
return point;
}
/**
* Execute a step of the flip animation
*/
public void FlipAnimationStep() {
if (!bFlipping)
return;
int width = getWidth();
// No input when flipping
bBlockTouchInput = true;
// Handle speed
float curlSpeed = mCurlSpeed;
if (!bFlipRight)
curlSpeed *= -1;
// Move us
mMovement.x += curlSpeed;
mMovement = CapMovement(mMovement, false);
// Create values
DoPageCurl();
// Check for endings :D
if (mA.x < 1 || mA.x > width - 1) {
bFlipping = false;
if (bFlipRight) {
// SwapViews();
nextView();
}
ResetClipEdge();
// Create values
DoPageCurl();
// Enable touch input after the next draw event
bEnableInputAfterDraw = true;
} else {
mAnimationHandler.sleep(mUpdateRate);
}
// Force a new draw call
this.invalidate();
}
/**
* Do the page curl depending on the methods we are using
*/
private void DoPageCurl() {
if (bFlipping) {
if (IsCurlModeDynamic())
doDynamicCurl();
else
doSimpleCurl();
} else {
if (IsCurlModeDynamic())
doDynamicCurl();
else
doSimpleCurl();
}
}
/**
* Do a simple page curl effect
*/
private void doSimpleCurl() {
int width = getWidth();
int height = getHeight();
// Calculate point A
mA.x = width - mMovement.x;
mA.y = height;
// Calculate point D
mD.x = 0;
mD.y = 0;
if (mA.x > width / 2) {
mD.x = width;
mD.y = height - (width - mA.x) * height / mA.x;
} else {
mD.x = 2 * mA.x;
mD.y = 0;
}
// Now calculate E and F taking into account that the line
// AD is perpendicular to FB and EC. B and C are fixed points.
double angle = Math
.atan((height - mD.y) / (mD.x + mMovement.x - width));
double _cos = Math.cos(2 * angle);
double _sin = Math.sin(2 * angle);
// And get F
mF.x = (float) (width - mMovement.x + _cos * mMovement.x);
mF.y = (float) (height - _sin * mMovement.x);
// If the x position of A is above half of the page we are still not
// folding the upper-right edge and so E and D are equal.
if (mA.x > width / 2) {
mE.x = mD.x;
mE.y = mD.y;
} else {
// So get E
mE.x = (float) (mD.x + _cos * (width - mD.x));
mE.y = (float) -(_sin * (width - mD.x));
}
}
/**
* Calculate the dynamic effect, that one that follows the users finger
*/
private void doDynamicCurl() {
int width = getWidth();
int height = getHeight();
// F will follow the finger, we add a small displacement
// So that we can see the edge
mF.x = width - mMovement.x + 0.1f;
mF.y = height - mMovement.y + 0.1f;
// Set min points
if (mA.x == 0) {
mF.x = Math.min(mF.x, mOldF.x);
mF.y = Math.max(mF.y, mOldF.y);
}
// Get diffs
float deltaX = width - mF.x;
float deltaY = height - mF.y;
float BH = (float) (Math.sqrt(deltaX * deltaX + deltaY * deltaY) / 2);
double tangAlpha = deltaY / deltaX;
double alpha = Math.atan(deltaY / deltaX);
double _cos = Math.cos(alpha);
double _sin = Math.sin(alpha);
mA.x = (float) (width - (BH / _cos));
mA.y = height;
mD.y = (float) (height - (BH / _sin));
mD.x = width;
mA.x = Math.max(0, mA.x);
if (mA.x == 0) {
mOldF.x = mF.x;
mOldF.y = mF.y;
}
// Get W
mE.x = mD.x;
mE.y = mD.y;
// Correct
if (mD.y < 0) {
mD.x = width + (float) (tangAlpha * mD.y);
mE.y = 0;
mE.x = width + (float) (Math.tan(2 * alpha) * mD.y);
}
}
/**
* Swap between the fore and back-ground.
*/
#Deprecated
private void SwapViews() {
Bitmap temp = mForeground;
mForeground = mBackground;
mBackground = temp;
}
/**
* Swap to next view
*/
private void nextView() {
MySQLiteHelper SqlLiteInstance = new MySQLiteHelper(hesham);
SqlLiteInstance.insertForTest("تايتل", "لبلب", "ثثث");
SqlLiteInstance.insertForTest("تايتل التاني", "5555", "5555");
SqlLiteInstance.insertForTest("التالت", "66666", "66666");
int foreIndex = mIndex + 1;
Cursor myDataBase = SqlLiteInstance.getCurrentPageData(1);
if (myDataBase.moveToFirst() && myDataBase.getCount() >= 1) {
do {
textViewContent.setText(myDataBase.getString(0));
} while (myDataBase.moveToNext());
}
if (foreIndex >= mPages.size()) {
foreIndex = 0;
}
int backIndex = foreIndex + 1;
if (backIndex >= mPages.size()) {
backIndex = 0;
}
mIndex = foreIndex;
setViews(foreIndex, backIndex);
}
/**
* Swap to previous view
*/
private void previousView() {
int backIndex = mIndex;
int foreIndex = backIndex - 1;
if (foreIndex < 0) {
foreIndex = mPages.size() - 1;
}
mIndex = foreIndex;
setViews(foreIndex, backIndex);
}
/**
* Set current fore and background
*
* #param foreground
* - Foreground view index
* #param background
* - Background view index
*/
private void setViews(int foreground, int background) {
mForeground = mPages.get(foreground);
mBackground = mPages.get(background);
}
// ---------------------------------------------------------------
// Drawing methods
// ---------------------------------------------------------------
#Override
protected void onDraw(Canvas canvas) {
// Always refresh offsets
mCurrentLeft = getLeft();
mCurrentTop = getTop();
// Translate the whole canvas
// canvas.translate(mCurrentLeft, mCurrentTop);
// We need to initialize all size data when we first draw the view
if (!bViewDrawn) {
bViewDrawn = true;
onFirstDrawEvent(canvas);
}
canvas.drawColor(Color.WHITE);
// Curl pages
// DoPageCurl();
// TODO: This just scales the views to the current
// width and height. We should add some logic for:
// 1) Maintain aspect ratio
// 2) Uniform scale
// 3) ...
Rect rect = new Rect();
rect.left = 0;
rect.top = 0;
rect.bottom = getHeight();
rect.right = getWidth();
// First Page render
Paint paint = new Paint();
// Draw our elements
drawForeground(canvas, rect, paint);
drawBackground(canvas, rect, paint);
drawCurlEdge(canvas);
// Draw any debug info once we are done
if (bEnableDebugMode)
drawDebug(canvas);
// Check if we can re-enable input
if (bEnableInputAfterDraw) {
bBlockTouchInput = false;
bEnableInputAfterDraw = false;
}
// Restore canvas
// canvas.restore();
}
/**
* Called on the first draw event of the view
*
* #param canvas
*/
protected void onFirstDrawEvent(Canvas canvas) {
mFlipRadius = getWidth();
ResetClipEdge();
DoPageCurl();
}
/**
* Draw the foreground
*
* #param canvas
* #param rect
* #param paint
*/
private void drawForeground(Canvas canvas, Rect rect, Paint paint) {
canvas.drawBitmap(mForeground, null, rect, paint);
// Draw the page number (first page is 1 in real life :D
// there is no page number 0 hehe)
drawPageNum(canvas, mIndex);
}
/**
* Create a Path used as a mask to draw the background page
*
* #return
*/
private Path createBackgroundPath() {
Path path = new Path();
path.moveTo(mA.x, mA.y);
path.lineTo(mB.x, mB.y);
path.lineTo(mC.x, mC.y);
path.lineTo(mD.x, mD.y);
path.lineTo(mA.x, mA.y);
return path;
}
/**
* Draw the background image.
*
* #param canvas
* #param rect
* #param paint
*/
private void drawBackground(Canvas canvas, Rect rect, Paint paint) {
Path mask = createBackgroundPath();
// Save current canvas so we do not mess it up
canvas.save();
canvas.clipPath(mask);
canvas.drawBitmap(mBackground, null, rect, paint);
// Draw the page number (first page is 1 in real life :D
// there is no page number 0 hehe)
drawPageNum(canvas, mIndex);
canvas.restore();
}
/**
* Creates a path used to draw the curl edge in.
*
* #return
*/
private Path createCurlEdgePath() {
Path path = new Path();
path.moveTo(mA.x, mA.y);
path.lineTo(mD.x, mD.y);
path.lineTo(mE.x, mE.y);
path.lineTo(mF.x, mF.y);
path.lineTo(mA.x, mA.y);
return path;
}
/**
* Draw the curl page edge
*
* #param canvas
*/
private void drawCurlEdge(Canvas canvas) {
Path path = createCurlEdgePath();
canvas.drawPath(path, mCurlEdgePaint);
}
/**
* Draw page num (let this be a bit more custom)
*
* #param canvas
* #param pageNum
*/
private void drawPageNum(Canvas canvas, int pageNum) {
mTextPaint.setColor(Color.WHITE);
String pageNumText = "- " + pageNum + " -";
drawCentered(canvas, pageNumText,
canvas.getHeight() - mTextPaint.getTextSize() - 5, mTextPaint,
mTextPaintShadow);
}
// ---------------------------------------------------------------
// Debug draw methods
// ---------------------------------------------------------------
/**
* Draw a text with a nice shadow
*/
public static void drawTextShadowed(Canvas canvas, String text, float x,
float y, Paint textPain, Paint shadowPaint) {
canvas.drawText(text, x - 1, y, shadowPaint);
canvas.drawText(text, x, y + 1, shadowPaint);
canvas.drawText(text, x + 1, y, shadowPaint);
canvas.drawText(text, x, y - 1, shadowPaint);
canvas.drawText(text, x, y, textPain);
}
/**
* Draw a text with a nice shadow centered in the X axis
*
* #param canvas
* #param text
* #param y
* #param textPain
* #param shadowPaint
*/
public static void drawCentered(Canvas canvas, String text, float y,
Paint textPain, Paint shadowPaint) {
float posx = (canvas.getWidth() - textPain.measureText(text)) / 2;
drawTextShadowed(canvas, text, posx, y, textPain, shadowPaint);
}
/**
* Draw debug info
*
* #param canvas
*/
private void drawDebug(Canvas canvas) {
float posX = 10;
float posY = 20;
Paint paint = new Paint();
paint.setStrokeWidth(5);
paint.setStyle(Style.STROKE);
paint.setColor(Color.BLACK);
canvas.drawCircle(mOrigin.x, mOrigin.y, getWidth(), paint);
paint.setStrokeWidth(3);
paint.setColor(Color.RED);
canvas.drawCircle(mOrigin.x, mOrigin.y, getWidth(), paint);
paint.setStrokeWidth(5);
paint.setColor(Color.BLACK);
canvas.drawLine(mOrigin.x, mOrigin.y, mMovement.x, mMovement.y, paint);
paint.setStrokeWidth(3);
paint.setColor(Color.RED);
canvas.drawLine(mOrigin.x, mOrigin.y, mMovement.x, mMovement.y, paint);
posY = debugDrawPoint(canvas, "A", mA, Color.RED, posX, posY);
posY = debugDrawPoint(canvas, "B", mB, Color.GREEN, posX, posY);
posY = debugDrawPoint(canvas, "C", mC, Color.BLUE, posX, posY);
posY = debugDrawPoint(canvas, "D", mD, Color.CYAN, posX, posY);
posY = debugDrawPoint(canvas, "E", mE, Color.YELLOW, posX, posY);
posY = debugDrawPoint(canvas, "F", mF, Color.LTGRAY, posX, posY);
posY = debugDrawPoint(canvas, "Mov", mMovement, Color.DKGRAY, posX,
posY);
posY = debugDrawPoint(canvas, "Origin", mOrigin, Color.MAGENTA, posX,
posY);
posY = debugDrawPoint(canvas, "Finger", mFinger, Color.GREEN, posX,
posY);
// Draw some curl stuff (Just some test)
/*
* canvas.save(); Vector2D center = new
* Vector2D(getWidth()/2,getHeight()/2);
* //canvas.rotate(315,center.x,center.y);
*
* // Test each lines //float radius = mA.distance(mD)/2.f; //float
* radius = mA.distance(mE)/2.f; float radius = mA.distance(mF)/2.f;
* //float radius = 10; float reduction = 4.f; RectF oval = new RectF();
* oval.top = center.y-radius/reduction; oval.bottom =
* center.y+radius/reduction; oval.left = center.x-radius; oval.right =
* center.x+radius; canvas.drawArc(oval, 0, 360, false, paint);
* canvas.restore(); /*
*/
}
private float debugDrawPoint(Canvas canvas, String name, Vector2D point,
int color, float posX, float posY) {
return debugDrawPoint(canvas, name + " " + point.toString(), point.x,
point.y, color, posX, posY);
}
private float debugDrawPoint(Canvas canvas, String name, float X, float Y,
int color, float posX, float posY) {
mTextPaint.setColor(color);
drawTextShadowed(canvas, name, posX, posY, mTextPaint, mTextPaintShadow);
Paint paint = new Paint();
paint.setStrokeWidth(5);
paint.setColor(color);
canvas.drawPoint(X, Y, paint);
return posY + 15;
}
}
xml code :
<RelativeLayout
android:id="#+id/game_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.mystictreegames.pagecurl.PageCurlView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/dcgpagecurlPageCurlView1"
android:background="#drawable/facebook">
</com.mystictreegames.pagecurl.PageCurlView>
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_centerVertical="true"
android:text="جوهر كون المرء انه انسان لا يسعى الى الكمال"/>
log error:
FATAL EXCEPTION: main
E/AndroidRuntime(2497): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.mystictreegames.pagecurl/com.mystictreegames.pagecurl.StandaloneExample}: android.view.InflateException: Binary XML file line #13: Error inflating class com.mystictreegames.pagecurl.PageCurlView
In the init() of the PageCurlView class, you are inflating R.layout.standalone_example. Objects are added in a circular manner (R.layout.standalone_example has PageCurlView which has R.layout.standalone_example which has ...).
I have a native library which is invoked by the JNI function called from UI. It runs just for a one time, it execute one command and in second run it just exits. I get the following print.
D/Zygote ( 111): Process 921 terminated by signal (2)
What does signal (2) mean? Can I infer something out of this message as to why the process was terminated? The native library works completely fine with a first execute.
My second question is about the accelerometer:
package com.example.android.accelerometerplay;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.BitmapFactory.Options;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
/**
* This is an example of using the accelerometer to integrate the device's
* acceleration to a position using the Verlet method. This is illustrated with
* a very simple particle system comprised of a few iron balls freely moving on
* an inclined wooden table. The inclination of the virtual table is controlled
* by the device's accelerometer.
*
* #see SensorManager
* #see SensorEvent
* #see Sensor
*/
public class AccelerometerPlayActivity extends Activity {
private SimulationView mSimulationView;
private SensorManager mSensorManager;
private PowerManager mPowerManager;
private WindowManager mWindowManager;
private Display mDisplay;
private WakeLock mWakeLock;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Get an instance of the SensorManager
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
// Get an instance of the PowerManager
mPowerManager = (PowerManager) getSystemService(POWER_SERVICE);
// Get an instance of the WindowManager
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
mDisplay = mWindowManager.getDefaultDisplay();
// Create a bright wake lock
mWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, getClass()
.getName());
// instantiate our simulation view and set it as the activity's content
mSimulationView = new SimulationView(this);
setContentView(mSimulationView);
}
#Override
protected void onResume() {
super.onResume();
/*
* when the activity is resumed, we acquire a wake-lock so that the
* screen stays on, since the user will likely not be fiddling with the
* screen or buttons.
*/
mWakeLock.acquire();
// Start the simulation
mSimulationView.startSimulation();
}
#Override
protected void onPause() {
super.onPause();
/*
* When the activity is paused, we make sure to stop the simulation,
* release our sensor resources and wake locks
*/
// Stop the simulation
mSimulationView.stopSimulation();
// and release our wake-lock
mWakeLock.release();
}
class SimulationView extends View implements SensorEventListener {
// diameter of the balls in meters
private static final float sBallDiameter = 0.004f;
private static final float sBallDiameter2 = sBallDiameter * sBallDiameter;
// friction of the virtual table and air
private static final float sFriction = 0.1f;
private Sensor mAccelerometer;
private long mLastT;
private float mLastDeltaT;
private float mXDpi;
private float mYDpi;
private float mMetersToPixelsX;
private float mMetersToPixelsY;
private Bitmap mBitmap;
private Bitmap mWood;
private float mXOrigin;
private float mYOrigin;
private float mSensorX;
private float mSensorY;
private long mSensorTimeStamp;
private long mCpuTimeStamp;
private float mHorizontalBound;
private float mVerticalBound;
private final ParticleSystem mParticleSystem = new ParticleSystem();
/*
* Each of our particle holds its previous and current position, its
* acceleration. for added realism each particle has its own friction
* coefficient.
*/
class Particle {
private float mPosX;
private float mPosY;
private float mAccelX;
private float mAccelY;
private float mLastPosX;
private float mLastPosY;
private float mOneMinusFriction;
Particle() {
// make each particle a bit different by randomizing its
// coefficient of friction
final float r = ((float) Math.random() - 0.5f) * 0.2f;
mOneMinusFriction = 1.0f - sFriction + r;
}
public void computePhysics(float sx, float sy, float dT, float dTC) {
// Force of gravity applied to our virtual object
final float m = 1000.0f; // mass of our virtual object
final float gx = -sx * m;
final float gy = -sy * m;
/*
* ·F = mA <=> A = ·F / m We could simplify the code by
* completely eliminating "m" (the mass) from all the equations,
* but it would hide the concepts from this sample code.
*/
final float invm = 1.0f / m;
final float ax = gx * invm;
final float ay = gy * invm;
/*
* Time-corrected Verlet integration The position Verlet
* integrator is defined as x(t+Æt) = x(t) + x(t) - x(t-Æt) +
* a(t)Ætö2 However, the above equation doesn't handle variable
* Æt very well, a time-corrected version is needed: x(t+Æt) =
* x(t) + (x(t) - x(t-Æt)) * (Æt/Æt_prev) + a(t)Ætö2 We also add
* a simple friction term (f) to the equation: x(t+Æt) = x(t) +
* (1-f) * (x(t) - x(t-Æt)) * (Æt/Æt_prev) + a(t)Ætö2
*/
final float dTdT = dT * dT;
final float x = mPosX + mOneMinusFriction * dTC * (mPosX - mLastPosX) + mAccelX
* dTdT;
final float y = mPosY + mOneMinusFriction * dTC * (mPosY - mLastPosY) + mAccelY
* dTdT;
mLastPosX = mPosX;
mLastPosY = mPosY;
mPosX = x;
mPosY = y;
mAccelX = ax;
mAccelY = ay;
}
/*
* Resolving constraints and collisions with the Verlet integrator
* can be very simple, we simply need to move a colliding or
* constrained particle in such way that the constraint is
* satisfied.
*/
public void resolveCollisionWithBounds() {
final float xmax = mHorizontalBound;
final float ymax = mVerticalBound;
final float x = mPosX;
final float y = mPosY;
if (x > xmax) {
mPosX = xmax;
} else if (x < -xmax) {
mPosX = -xmax;
}
if (y > ymax) {
mPosY = ymax;
} else if (y < -ymax) {
mPosY = -ymax;
}
}
}
/*
* A particle system is just a collection of particles
*/
class ParticleSystem {
static final int NUM_PARTICLES = 15;
private Particle mBalls[] = new Particle[NUM_PARTICLES];
ParticleSystem() {
/*
* Initially our particles have no speed or acceleration
*/
for (int i = 0; i < mBalls.length; i++) {
mBalls[i] = new Particle();
}
}
/*
* Update the position of each particle in the system using the
* Verlet integrator.
*/
private void updatePositions(float sx, float sy, long timestamp) {
final long t = timestamp;
if (mLastT != 0) {
final float dT = (float) (t - mLastT) * (1.0f / 1000000000.0f);
if (mLastDeltaT != 0) {
final float dTC = dT / mLastDeltaT;
final int count = mBalls.length;
for (int i = 0; i < count; i++) {
Particle ball = mBalls[i];
ball.computePhysics(sx, sy, dT, dTC);
}
}
mLastDeltaT = dT;
}
mLastT = t;
}
/*
* Performs one iteration of the simulation. First updating the
* position of all the particles and resolving the constraints and
* collisions.
*/
public void update(float sx, float sy, long now) {
// update the system's positions
updatePositions(sx, sy, now);
// We do no more than a limited number of iterations
final int NUM_MAX_ITERATIONS = 10;
/*
* Resolve collisions, each particle is tested against every
* other particle for collision. If a collision is detected the
* particle is moved away using a virtual spring of infinite
* stiffness.
*/
boolean more = true;
final int count = mBalls.length;
for (int k = 0; k < NUM_MAX_ITERATIONS && more; k++) {
more = false;
for (int i = 0; i < count; i++) {
Particle curr = mBalls[i];
for (int j = i + 1; j < count; j++) {
Particle ball = mBalls[j];
float dx = ball.mPosX - curr.mPosX;
float dy = ball.mPosY - curr.mPosY;
float dd = dx * dx + dy * dy;
// Check for collisions
if (dd <= sBallDiameter2) {
/*
* add a little bit of entropy, after nothing is
* perfect in the universe.
*/
dx += ((float) Math.random() - 0.5f) * 0.0001f;
dy += ((float) Math.random() - 0.5f) * 0.0001f;
dd = dx * dx + dy * dy;
// simulate the spring
final float d = (float) Math.sqrt(dd);
final float c = (0.5f * (sBallDiameter - d)) / d;
curr.mPosX -= dx * c;
curr.mPosY -= dy * c;
ball.mPosX += dx * c;
ball.mPosY += dy * c;
more = true;
}
}
/*
* Finally make sure the particle doesn't intersects
* with the walls.
*/
curr.resolveCollisionWithBounds();
}
}
}
public int getParticleCount() {
return mBalls.length;
}
public float getPosX(int i) {
return mBalls[i].mPosX;
}
public float getPosY(int i) {
return mBalls[i].mPosY;
}
}
public void startSimulation() {
/*
* It is not necessary to get accelerometer events at a very high
* rate, by using a slower rate (SENSOR_DELAY_UI), we get an
* automatic low-pass filter, which "extracts" the gravity component
* of the acceleration. As an added benefit, we use less power and
* CPU resources.
*/
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_UI);
}
public void stopSimulation() {
mSensorManager.unregisterListener(this);
}
public SimulationView(Context context) {
super(context);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
mXDpi = metrics.xdpi;
mYDpi = metrics.ydpi;
mMetersToPixelsX = mXDpi / 0.0254f;
mMetersToPixelsY = mYDpi / 0.0254f;
// rescale the ball so it's about 0.5 cm on screen
Bitmap ball = BitmapFactory.decodeResource(getResources(), R.drawable.ball);
final int dstWidth = (int) (sBallDiameter * mMetersToPixelsX + 0.5f);
final int dstHeight = (int) (sBallDiameter * mMetersToPixelsY + 0.5f);
mBitmap = Bitmap.createScaledBitmap(ball, dstWidth, dstHeight, true);
Options opts = new Options();
opts.inDither = true;
opts.inPreferredConfig = Bitmap.Config.RGB_565;
mWood = BitmapFactory.decodeResource(getResources(), R.drawable.wood, opts);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// compute the origin of the screen relative to the origin of
// the bitmap
mXOrigin = (w - mBitmap.getWidth()) * 0.5f;
mYOrigin = (h - mBitmap.getHeight()) * 0.5f;
mHorizontalBound = ((w / mMetersToPixelsX - sBallDiameter) * 0.5f);
mVerticalBound = ((h / mMetersToPixelsY - sBallDiameter) * 0.5f);
}
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER)
return;
/*
* record the accelerometer data, the event's timestamp as well as
* the current time. The latter is needed so we can calculate the
* "present" time during rendering. In this application, we need to
* take into account how the screen is rotated with respect to the
* sensors (which always return data in a coordinate space aligned
* to with the screen in its native orientation).
*/
switch (mDisplay.getRotation()) {
case Surface.ROTATION_0:
mSensorX = event.values[0];
mSensorY = event.values[1];
break;
case Surface.ROTATION_90:
mSensorX = -event.values[1];
mSensorY = event.values[0];
break;
case Surface.ROTATION_180:
mSensorX = -event.values[0];
mSensorY = -event.values[1];
break;
case Surface.ROTATION_270:
mSensorX = event.values[1];
mSensorY = -event.values[0];
break;
}
mSensorTimeStamp = event.timestamp;
mCpuTimeStamp = System.nanoTime();
}
#Override
protected void onDraw(Canvas canvas) {
/*
* draw the background
*/
canvas.drawBitmap(mWood, 0, 0, null);
/*
* compute the new position of our object, based on accelerometer
* data and present time.
*/
final ParticleSystem particleSystem = mParticleSystem;
final long now = mSensorTimeStamp + (System.nanoTime() - mCpuTimeStamp);
final float sx = mSensorX;
final float sy = mSensorY;
particleSystem.update(sx, sy, now);
final float xc = mXOrigin;
final float yc = mYOrigin;
final float xs = mMetersToPixelsX;
final float ys = mMetersToPixelsY;
final Bitmap bitmap = mBitmap;
final int count = particleSystem.getParticleCount();
for (int i = 0; i < count; i++) {
/*
* We transform the canvas so that the coordinate system matches
* the sensors coordinate system with the origin in the center
* of the screen and the unit is the meter.
*/
final float x = xc + particleSystem.getPosX(i) * xs;
final float y = yc - particleSystem.getPosY(i) * ys;
canvas.drawBitmap(bitmap, x, y, null);
}
// and make sure to redraw asap
invalidate();
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
}
How to move ball?
2 is SIGINT (Interrupt), you can check it in header file within your ndk directory