Skip to content

Commit

Permalink
* performance update
Browse files Browse the repository at this point in the history
* replace Double wrapper object with double and nullable with another
method
* improve sort algorithm
* reduce object allocations
* point now takes doubles instead of the Double wrapper object
* add 2 new scale methods instead of nullable parameters
  • Loading branch information
FabianTerhorst committed Apr 6, 2017
1 parent f31774b commit 0b0c6ab
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,10 @@ public void sampleThree(double angle, IsometricView isometricView) {
isometricView.add(new Prism(new Point(3, 0, 2), 2, 4, 1), new Color(33, 150, 243));
isometricView.add(new Prism(new Point(2, 1, 2), 1, 3, 1), new Color(33, 150, 243));
isometricView.add(new Stairs(new Point(2, 0, 2), 10).rotateZ(new Point(2.5, 0.5, 0), -Math.PI / 2), new Color(33, 150, 243));
isometricView.add(new Pyramid(new Point(2, 3, 3)).scale(new Point(2, 4, 3), 0.5, null, null), new Color(180, 180, 0));
isometricView.add(new Pyramid(new Point(4, 3, 3)).scale(new Point(5, 4, 3), 0.5, null, null), new Color(180, 0, 180));
isometricView.add(new Pyramid(new Point(4, 1, 3)).scale(new Point(5, 1, 3), 0.5, null, null), new Color(0, 180, 180));
isometricView.add(new Pyramid(new Point(2, 1, 3)).scale(new Point(2, 1, 3), 0.5, null, null), new Color(40, 180, 40));
isometricView.add(new Pyramid(new Point(2, 3, 3)).scale(new Point(2, 4, 3), 0.5), new Color(180, 180, 0));
isometricView.add(new Pyramid(new Point(4, 3, 3)).scale(new Point(5, 4, 3), 0.5), new Color(180, 0, 180));
isometricView.add(new Pyramid(new Point(4, 1, 3)).scale(new Point(5, 1, 3), 0.5), new Color(0, 180, 180));
isometricView.add(new Pyramid(new Point(2, 1, 3)).scale(new Point(2, 1, 3), 0.5), new Color(40, 180, 40));
isometricView.add(new Prism(new Point(3, 2, 3), 1, 1, 0.2), new Color(50, 50, 50));
isometricView.add(new Octahedron(new Point(3, 2, 3.2)).rotateZ(new Point(3.5, 2.5, 0), angle), new Color(0, 180, 180));
}
Expand Down
196 changes: 111 additions & 85 deletions lib/src/main/java/io/fabianterhorst/isometric/Isometric.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,9 @@ public Isometric() {
* Y rides perpendicular to this angle (in isometric view: PI - angle)
* Z affects the y coordinate of the drawn point
*/
//Todo: use less object creation and maybe reuse the point
public Point translatePoint(Point point) {
Point xMap = new Point(point.x * this.transformation[0][0],
point.x * this.transformation[0][1]);

Point yMap = new Point(point.y * this.transformation[1][0],
point.y * this.transformation[1][1]);

double x = this.originX + xMap.x + yMap.x;
double y = this.originY - xMap.y - yMap.y - (point.z * this.scale);
return new Point(x, y);
return new Point(this.originX + point.x * this.transformation[0][0] + point.y * this.transformation[1][0],
this.originY - point.x * this.transformation[0][1] - point.y * this.transformation[1][1] - (point.z * this.scale));
}

public void add(Path path, Color color) {
Expand Down Expand Up @@ -121,13 +113,12 @@ public void measure(int width, int height, boolean sort) {
item.transformedPoints.add(translatePoint(point));
}

item.drawPath.moveTo(item.transformedPoints.get(0).x.floatValue(), item.transformedPoints.get(0).y.floatValue());
item.drawPath.moveTo((float) item.transformedPoints.get(0).x, (float) item.transformedPoints.get(0).y);

for (int i = 1, length = item.transformedPoints.size(); i < length; i++) {
item.drawPath.lineTo(item.transformedPoints.get(i).x.floatValue(), item.transformedPoints.get(i).y.floatValue());
item.drawPath.lineTo((float) item.transformedPoints.get(i).x, (float) item.transformedPoints.get(i).y);
}

//Todo: check if needed
item.drawPath.close();
}

Expand All @@ -136,7 +127,6 @@ public void measure(int width, int height, boolean sort) {
}
}


private List<Item> sortPaths() {
ArrayList<Item> sortedItems = new ArrayList<>();
Point observer = new Point(-10, -10, 20);
Expand All @@ -145,10 +135,14 @@ private List<Item> sortPaths() {
for (int i = 0; i < length; i++) {
drawBefore.add(i, new ArrayList<Integer>());
}
Item itemA;
Item itemB;
for (int i = 0; i < length; i++) {
itemA = items.get(i);
for (int j = 0; j < i; j++) {
if (hasIntersection(items.get(i).transformedPoints, items.get(j).transformedPoints)) {
int cmpPath = items.get(i).path.closerThan(items.get(j).path, observer);
itemB = items.get(j);
if (hasIntersection(itemA.transformedPoints, itemB.transformedPoints)) {
int cmpPath = itemA.path.closerThan(itemB.path, observer);
if (cmpPath < 0) {
drawBefore.get(i).add(drawBefore.get(i).size(), j);
}
Expand All @@ -158,6 +152,7 @@ private List<Item> sortPaths() {
}
}
}
Item currItem;
int drawThisTurn = 1;
while (drawThisTurn == 1) {
drawThisTurn = 0;
Expand All @@ -170,24 +165,21 @@ private List<Item> sortPaths() {
}
}
if (canDraw == 1) {
Item item = new Item(items.get(i).path, items.get(i).baseColor);
item.drawPath = items.get(i).drawPath;
item.transformedPoints = items.get(i).transformedPoints;
currItem = items.get(i);
Item item = new Item(currItem);
sortedItems.add(item);
Item currItem = items.get(i);
currItem.drawn = 1;
items.set(i, currItem);
drawThisTurn = 1;
}
}
}
}

for (int i = 0; i < length; i++) {
if (items.get(i).drawn == 0) {
Item item = new Item(items.get(i).path, items.get(i).baseColor);
item.drawPath = items.get(i).drawPath;
item.transformedPoints = items.get(i).transformedPoints;
sortedItems.add(item);
currItem = items.get(i);
if (currItem.drawn == 0) {
sortedItems.add(new Item(currItem));
}
}
return sortedItems;
Expand All @@ -204,6 +196,8 @@ public void draw(Canvas canvas) {
}
}

//Todo: use android.grphics region object to check if point is inside region
//Todo: use path.op to check if the path intersects with another path
@Nullable
public Item findItemForPosition(Point position) {
//Todo: reverse sorting for click detection, because hidden object is getting drawed first und will be returned as the first as well
Expand Down Expand Up @@ -256,29 +250,29 @@ public Item findItemForPosition(Point position) {

//search for equal points that are above or below for left and right or left and right for bottom and top
for (Point point : item.transformedPoints) {
if (point.x.equals(left.x)) {
if (!point.y.equals(left.y)) {
if (point.x == left.x) {
if (point.y != left.y) {
items.add(point);
}
}
if (point.x.equals(right.x)) {
if (!point.y.equals(right.y)) {
if (point.x == right.x) {
if (point.y != right.y) {
items.add(point);
}
}
if (point.y.equals(top.y)) {
if (!point.y.equals(top.y)) {
if (point.y == top.y) {
if (point.y != top.y) {
items.add(point);
}
}
if (point.y.equals(bottom.y)) {
if (!point.y.equals(bottom.y)) {
if (point.y == bottom.y) {
if (point.y != bottom.y) {
items.add(point);
}
}
}

if (isPointInPoly(items, position)) {
if (isPointInPoly(items, position.x, position.y)) {
return item;
}
}
Expand All @@ -289,11 +283,23 @@ static class Item {
Path path;
Color baseColor;
Paint paint;
int drawn = 0;
List<Point> transformedPoints = new ArrayList<>();
android.graphics.Path drawPath = new android.graphics.Path();
int drawn;
List<Point> transformedPoints;
android.graphics.Path drawPath;

Item(Item item) {
transformedPoints = item.transformedPoints;
drawPath = item.drawPath;
drawn = item.drawn;
this.paint = item.paint;
this.path = item.path;
this.baseColor = item.baseColor;
}

Item(Path path, Color baseColor) {
transformedPoints = new ArrayList<>();
drawPath = new android.graphics.Path();
drawn = 0;
this.paint = new Paint(Paint.ANTI_ALIAS_FLAG);
this.paint.setStyle(Paint.Style.FILL_AND_STROKE);
this.paint.setStrokeWidth(1);
Expand All @@ -303,11 +309,22 @@ static class Item {
}
}

private boolean isPointInPoly(List<Point> poly, Point pt) {
private boolean isPointInPoly(List<Point> poly, double x, double y) {
boolean c = false;
for (int i = -1, l = poly.size(), j = l - 1; ++i < l; j = i) {
if (((poly.get(i).y <= pt.y && pt.y < poly.get(j).y) || (poly.get(j).y <= pt.y && pt.y < poly.get(i).y))
&& (pt.x < (poly.get(j).x - poly.get(i).x) * (pt.y - poly.get(i).y) / (poly.get(j).y - poly.get(i).y) + poly.get(i).x)) {
if (((poly.get(i).y <= y && y < poly.get(j).y) || (poly.get(j).y <= y && y < poly.get(i).y))
&& (x < (poly.get(j).x - poly.get(i).x) * (y - poly.get(i).y) / (poly.get(j).y - poly.get(i).y) + poly.get(i).x)) {
c = !c;
}
}
return c;
}

private boolean isPointInPoly(Point[] poly, double x, double y) {
boolean c = false;
for (int i = -1, l = poly.length, j = l - 1; ++i < l; j = i) {
if (((poly[i].y <= y && y < poly[j].y) || (poly[j].y <= y && y < poly[i].y))
&& (x < (poly[j].x - poly[i].x) * (y - poly[i].y) / (poly[j].y - poly[i].y) + poly[i].x)) {
c = !c;
}
}
Expand All @@ -324,68 +341,79 @@ private boolean hasIntersection(List<Point> pointsA, List<Point> pointsB) {
double BminY = pointsB.get(0).y;
double BmaxX = BminX;
double BmaxY = BminY;

Point point;

for (i = 0; i < lengthA; i++) {
AminX = Math.min(AminX, pointsA.get(i).x);
AminY = Math.min(AminY, pointsA.get(i).y);
AmaxX = Math.max(AmaxX, pointsA.get(i).x);
AmaxY = Math.max(AmaxY, pointsA.get(i).y);
point = pointsA.get(i);
AminX = Math.min(AminX, point.x);
AminY = Math.min(AminY, point.y);
AmaxX = Math.max(AmaxX, point.x);
AmaxY = Math.max(AmaxY, point.y);
}
for (i = 0; i < lengthB; i++) {
BminX = Math.min(BminX, pointsB.get(i).x);
BminY = Math.min(BminY, pointsB.get(i).y);
BmaxX = Math.max(BmaxX, pointsB.get(i).x);
BmaxY = Math.max(BmaxY, pointsB.get(i).y);
point = pointsB.get(i);
BminX = Math.min(BminX, point.x);
BminY = Math.min(BminY, point.y);
BmaxX = Math.max(BmaxX, point.x);
BmaxY = Math.max(BmaxY, point.y);
}

if (((AminX <= BminX && BminX <= AmaxX) || (BminX <= AminX && AminX <= BmaxX)) &&
((AminY <= BminY && BminY <= AmaxY) || (BminY <= AminY && AminY <= BmaxY))) {
// now let's be more specific
List<Point> polyA = cloneList(pointsA);
List<Point> polyB = cloneList(pointsB);
polyA.add(pointsA.get(0));
polyB.add(pointsB.get(0));
Point[] polyA = cloneListAndInsert(pointsA, pointsA.get(0));
Point[] polyB = cloneListAndInsert(pointsB, pointsB.get(0));

// see if edges cross, or one contained in the other
List<Double> deltaAX = new ArrayList<>();
List<Double> deltaAY = new ArrayList<>();
List<Double> deltaBX = new ArrayList<>();
List<Double> deltaBY = new ArrayList<>();
List<Double> rA = new ArrayList<>();
List<Double> rB = new ArrayList<>();
lengthPolyA = polyA.size();
lengthPolyA = polyA.length;
lengthPolyB = polyB.length;

double[] deltaAX = new double[lengthPolyA];
double[] deltaAY = new double[lengthPolyA];
double[] deltaBX = new double[lengthPolyB];
double[] deltaBY = new double[lengthPolyB];

double[] rA = new double[lengthPolyA];
double[] rB = new double[lengthPolyB];

for (i = 0; i <= lengthPolyA - 2; i++) {
deltaAX.add(i, polyA.get(i + 1).x - polyA.get(i).x);
deltaAY.add(i, polyA.get(i + 1).y - polyA.get(i).y);
point = polyA[i];
deltaAX[i] = polyA[i + 1].x - point.x;
deltaAY[i] = polyA[i + 1].y - point.y;
//equation written as deltaY.x - deltaX.y + r = 0
rA.add(i, deltaAX.get(i) * polyA.get(i).y - deltaAY.get(i) * polyA.get(i).x);
rA[i] = deltaAX[i] * point.y - deltaAY[i] * point.x;
}
lengthPolyB = polyB.size();

for (i = 0; i <= lengthPolyB - 2; i++) {
deltaBX.add(i, polyB.get(i + 1).x - polyB.get(i).x);
deltaBY.add(i, polyB.get(i + 1).y - polyB.get(i).y);
rB.add(i, deltaBX.get(i) * polyB.get(i).y - deltaBY.get(i) * polyB.get(i).x);
point = polyB[i];
deltaBX[i] = polyB[i + 1].x - point.x;
deltaBY[i] = polyB[i + 1].y - point.y;
rB[i] = deltaBX[i] * point.y - deltaBY[i] * point.x;
}

for (i = 0; i <= lengthPolyA - 2; i++) {
for (j = 0; j <= lengthPolyB - 2; j++) {
if (deltaAX.get(i) * deltaBY.get(j) != deltaAY.get(i) * deltaBX.get(j)) {
if (deltaAX[i] * deltaBY[j] != deltaAY[i] * deltaBX[j]) {
//case when vectors are colinear, or one polygon included in the other, is covered after
//two segments cross each other if and only if the points of the first are on each side of the line defined by the second and vice-versa
if ((deltaAY.get(i) * polyB.get(j).x - deltaAX.get(i) * polyB.get(j).y + rA.get(i)) * (deltaAY.get(i) * polyB.get(j + 1).x - deltaAX.get(i) * polyB.get(j + 1).y + rA.get(i)) < -0.000000001 &&
(deltaBY.get(j) * polyA.get(i).x - deltaBX.get(j) * polyA.get(i).y + rB.get(j)) * (deltaBY.get(j) * polyA.get(i + 1).x - deltaBX.get(j) * polyA.get(i + 1).y + rB.get(j)) < -0.000000001) {
if ((deltaAY[i] * polyB[j].x - deltaAX[i] * polyB[j].y + rA[i]) * (deltaAY[i] * polyB[j + 1].x - deltaAX[i] * polyB[j + 1].y + rA[i]) < -0.000000001 &&
(deltaBY[j] * polyA[i].x - deltaBX[j] * polyA[i].y + rB[j]) * (deltaBY[j] * polyA[i + 1].x - deltaBX[j] * polyA[i + 1].y + rB[j]) < -0.000000001) {
return true;
}
}
}
}

for (i = 0; i <= lengthPolyA - 2; i++) {
if (isPointInPoly(polyB, new Point(polyA.get(i).x, polyA.get(i).y))) {
point = polyA[i];
if (isPointInPoly(polyB, point.x, point.y)) {
return true;
}
}
for (i = 0; i <= lengthPolyB - 2; i++) {
if (isPointInPoly(polyA, new Point(polyB.get(i).x, polyB.get(i).y))) {
point = polyB[i];
if (isPointInPoly(polyA, point.x, point.y)) {
return true;
}
}
Expand All @@ -396,21 +424,19 @@ private boolean hasIntersection(List<Point> pointsA, List<Point> pointsB) {
}
}

private List<Point> cloneList(List<Point> points) {
List<Point> clonedList = new ArrayList<>(points.size());
for (Point point : points) {
Point newPoint = new Point();
if (point.x != null) {
newPoint.x = point.x;
}
if (point.y != null) {
newPoint.y = point.y;
}
if (point.z != null) {
newPoint.z = point.z;
}
clonedList.add(newPoint);
private Point[] cloneListAndInsert(List<Point> points, Point insertPoint) {
int size = points.size();
Point[] clonedList = new Point[size + 1];
Point point, newPoint;
for (int i = 0; i < size; i++) {
point = points.get(i);
newPoint = new Point();
newPoint.x = point.x;
newPoint.y = point.y;
newPoint.z = point.z;
clonedList[i] = newPoint;
}
clonedList[size] = insertPoint;
return clonedList;
}
}
Loading

0 comments on commit 0b0c6ab

Please sign in to comment.