I've working on a depth sorting problem, here's an example case I constructed
There are two planes, one hovering above the other, and they are sectioned into rectangluar polygons. Each polygon is defined by it's 'real-world' coordinates, as well as defined with a camera and view projections to produce a perspective view, as shown. A trace-printout of an example polygon produces
Zn = -0.225, Zf = -0.432
v0 (13, 33, 32.7) (-0.358, -0.065, -0.295, 180.737)
v1 (29, 33, 32.7) (-0.192, -0.142, -0.225, 173.247)
v2 (29, 33, 7.2) ( 0.011, 0.023, -0.375, 190.239)
v3 (13, 33, 7.2) (-0.148, 0.087, -0.432, 197.730)
This sample is one belonging to the green plane y=33, the non-projected vertices are v0 to v4 = (13,33,32.7),(29,33,32,7),(29, 33,7.2) and (13,33,7.2)
The projected coordinates, using a standard camera and view model, are shown following. If p(i) is the projected coordinate then -1 < p(i).x,p(i).z,p(i).z < 1. The fourth value is the homogenous W coordinate when prepared for rendereding divided into each p(i)(x,y,z), it is preserved as a reference value because it gives the distance from the 'camera' to a perpendicular plane of a given vertex.
The third column in the second series of brackets are the projected Z-values, and importantly for this problem The values Zn=depth of nearest vertex, and Zf=depth of farthest vertex
The question
This question is motivated by all of the above but is also independent of any knowledge of graphics rendering. My motivation is to depth-sort the quadrilateral polygons primarily on their Z-values
According to Painter's algorithm the first check on rendering order between two polygons is if the Z-near and Z-far are separate and should imply unambiguous spacial separation.
A simplified extract of the comparison
class Extent implements Comparable<Extent> {
double Zn;
double Zf;
#Override
public int compareTo(Extent ext2) {
if (Zn < ext2.Zf) return 1;
if (ext2.Zn < Zf) return -1;
return 0;
}
}
My Problem
When I run the comparison against a large set of polygons, sometimes I get a
java.lang.IllegalArgumentException: Comparison method violates its general contract exception. While often the rendering looks good as above, sometimes it fails. But why, I haven't been able to find a fail-case, and shouldn't comparing on two max,min in this way always be transient?
Does anyone know of an example set that would fail, subjected to this comparison? And, perhaps, are you able to spot a logical error in my method? (while insight in the internals of the compare method is the primary enquiry, if you are familiar with 3d depth-sorting please feel free to comment)
You should use an external Comparator to compare with instead of trying to implement Comparable which for complex objects is almost always the wrong way to do it.
Comparable works great for objects with a single or very few attributes that need to be the same in all cases. Like a complex object with an ID that is unique which is the only thing that is compared.
It does not work for complex cases where all the attributes are not considered in all cases. For those times you use a Comparator or better yet Ordering from the Guava library so that you can do elegant chaining and compositions.
Also sorting on triangles makes this exercise trivial because they can never be non-planar and have one less point to compare.
Your algorithm won't satisfy the compareTo contract, because of transitivity failure; you're not defining a total ordering. Consider three Extents
p1(0,3) p2(0,1) p3(2,3)
The implementor must ensure that p1.compareTo(p2) == 0 implies that
sgn(p1.compareTo(p3)) == sgn(p2.compareTo(p3)) for all other points.
p1.compareTo(p2) is 0.
p1.compareTo(p3) is 0, but p2.compareTo(p3) is -1.
The "best" way to sort ranges will depend on what exactly you want to do with the sorted list, but you'll most likely end up sorting on either the near, far, or center values.
Related
I'm working on a project and have some doubts about it's design.
How can I design the following problem the best (in JAVA):
Class A with the following attributes:
HashSet of Pixels where each pixel has x,y coordinates and value v between 0-1.
instance of class B.
Class B with the following function:
a function that gets a Pixel and returns its left neighbor.
When I'm in class A I want to use B.function on each pixel in A and add it to the HashSet only if it's not already there. The problem is that I don't want to send the HashSet to the function, how bad is it to return new instance of Pixel from the function if it might already exist (This function going to run on many pixels and will create many unused instances of Pixel).
What other options do I have?
Since you use Set<Pixel> you have to create new Pixel instance to check if it exists in set or not.
If set contains N elements after calling B.function method you will create extra N Pixel nodes. If all elements are new, you just add them to set, in other case Garbage Collection needs to sweep them. One of drawbacks is we need to create m (wheren m <= N - number of Pixel-s which already exists in set) and later we need to collect them by GC. How big is m/N ratio depends from your algorithm and what you are actually doing.
Lets calculate how many memory we need to consume for N = 1_000_000 pixels in set. We know that int is a 4 bytes and double is 8 bytes, lets add extra 8 bytes for an object and 8 bytes for a reference. It gives 32 bytes for every instance of Pixel object. We need to create N objects which gives 32MB. Let's assume that our ratio is 50% so, 16MB we allocated just to check it is not needed.
If this is a cost you can not pay you need to develop algorithm which allows you iterate over Set<Pixel> in an order from left-to-right. So, left neighbour of Pixel X is before X.
Assume that left neighbour of Pixel X(x, y) is pixel X'(x - 1, y). Pixel B(0, y) does not have left neighbour. You need to use TreeSet and implement Comparable<Pixel> interface in Pixel class. Simple implementation could look like this:
#Override
public int compareTo(Pixel o) {
return this.y == o.y ? this.x - o.x : this.y - o.y;
}
This allows you to iterate set in order from left to right: (0, 0), (1, 0), ...., (x - 1, y), (x, y), (x + 1, y), ... , (maxX, maxY). So, when you iterate it you can check whether previous element is a left neighbour of current Pixel. Example implementation could look like below:
void addNeighboursIfNeeded() {
Set<Pixel> neighbours = new HashSet<>(pixels.size());
Pixel last = null;
for (Pixel p : pixels) {
if (p.getX() == 0 || p.isLeftNeighbour(last)) {
// a left border pixel
// or last checked element is a left neighbour of current pixel.
last = p;
continue;
}
// last element was not our left-neighbour so we need to call b method
Pixel left = b.getLeft(p);
neighbours.add(left);
last = p;
}
// add all new neigbours
pixels.addAll(neighbours);
}
This should allow you to save this memory which is allocated for duplicated Pixel objects.
I can see here few concerns regarding object oriented programming.
Encapsulation violation: When you call function of B from A which operates on A's data (which you are avoiding by not sending HashMap), it violates encapsulation (If there is a reason, its acceptable though). Is it possible to move that function (operating on A's HashSet) to A? This will protect A's state from getting exposed.
Proliferation of classes: There is a possibility that there will be large number of objects of Point type. You can think of using Flyweight GOF design pattern, it will externalize the state of each point and will make it reusable. and will reduce number substantially.
Passing large collection of Points to method in B: If you can shift method from B to A, this point gets resolved. Anyway java will pass this collection by reference. But in that case its open for modifications from external classes (need to take care of this aspect).
Abstraction of type Point: If class Point has only state and no behavior, it will lead to violation of encapsulation. Can you shift the method getNeighbour() in to Point? as it will make Point immutable (which is essential). Off course actual algorithm can be delegated to another class (if its independently varying responsibility and has hierarchy of algorithms, think of GOF Strategy pattern here).
Uniqueness of points in collection: which your set will take care with due care about appropriate Hash and logical equality for class Point.
I have a list of Rectangles, created in the usual way with:
List<Rectangle> rects = new ArrayList<>();
Some Rectangles are added (all with non-zero width and height). The number of Rectangles the List contains can be anywhere between 0 and 10,000, and will typically be between 4,000 and 6,000.
The list is sorted by ascending X-coordinate of the Rectangle origin, and then by ascending Y-coordinate for duplicate X-coordinates (though two or more rectangles with the same X-coordinate is rare).
I've verified the sorting is being done correctly (I'm using Collections.sort with a custom comparator).
I need a method that takes as input two ints, x and y, and returns the first Rectangle found containing the point (x,y), or null if no Rectangle in the list contains that point.
public Rectangle findContainingRectangle(int x, int y)
The naive method, which does give the desired functionality, is to just loop through the list and call the contains method on each Rectangle, but that is much too slow.
The List will be modified while the program is running, but at an insignificant rate compared to the rate at which the List needs to be searched, so an algorithm that requires a relatively slow initialization is fine.
I've looked at Collections.binarySearch but couldn't figure out how it might be used. I don't have much experience with Java so if there's another Collection that could be used similarly to a List but better suited to the type of search I need, then that's great (I have read the documentation on things like Maps and Sets but didn't recognize any advantage).
While maintaining a sorted list, you could use a binary search on the 'X' coordinate to find the candidates of the rectangles that contain the wanted 'X', and after which, use binary search on the 'Y' coordinate.
You should implement the binary search yourself, I can't see a way you can use the Collections.binarySearch method.
expected complexity: O(log n) as n the number of rectangles.
(It's a bit more because you might have duplicates)
However ,to do so, you should keep the array sorted while adding other instances, (sort after every insert).
Use HashSet. Map isn't appropriate here since you're not creating key-value pairs, and a Stream doesn't fit in this context either.
Be sure to override equals() and hashCode() in Rectangle, as described here: Why do I need to override the equals and hashCode methods in Java?
You can search your list using parallel stream like this
public Rectangle findContainingRectangle(final int x, final int y) {
List<Rectangle> rectangles = new ArrayList<>();
Rectangle rec = rectangles.parallelStream().filter((r)->{
if(r.getX()==x && r.getY()==y){
return true;
}
return false;
}).findFirst().get();
return rec;
}
Just run binary search a bunch of times - since the probability of same x is low as you say it wont take many times so it will still be logn
a) run binary search
b) remove item if found - and keep index where it was found
c) repeat binary search at a) with the remaining list until null is returned
d) then you have a small array of indexes and you can see which one is the smallest
e) then reinsert the removed elements at the designated spots
You can try and see a performance of a stream. I am not sure it will be fast enough but you can test it.
Rectangle rec = rects.stream().filter((r)->{
return r.contains(x, y);
}).findFirst().get();
You can create a Map.
Map is the best way to associate two values. You can associate the 'x' value and its first position in your List. Then you only have to loop from the first 'x' position to another 'x' in your list.
If you don't find the 'x' on the Map, they don't have the good rectangle on your list.
With this way you don't explore all bad 'x' entry.
In Java SE 7, I'm trying to solve a problem where I have a series of Rectangles. Through some user interaction, I get a Point. What I need to do is find the (first) Rectangle which contains the Point (if any).
Currently, I'm doing this via the very naieve solution of just storing the Rectangles in an ArrayList, and searching for the containing Rectangle by iterating over the list and using contains(). The problem is that, because this needs to be interactive for the user, this technique starts to be too slow for even a relatively small number of Rectangles (say, 200).
My current code looks something like this:
// Given rects is an ArrayList<Rectangle>, and p is a Point:
for(Rectangle r : rects)
{
if(r.contains(p))
{
return r;
}
}
return null;
Is there a more clever way to solve this problem (namely, in O(log n) instead of O(n), and/or with fewer calls to contains() by eliminating obviously bad candidates early)?
Yes, there is. Build 2 interval trees which will tell you if there is a rectangle between x1 to x2 and between y1 and y2. Then, when you have the co-ordinates of the point, perform O(log n) searches in both the trees.
That'll tell you if there are possibly rectangles around the point of interest. You still need to check if there is a common rectangle given by the two trees.
I mainly focused on the Graphics aspects to create a little 2DGame. I've watched/looked at several tutorials but none of them were that pleasing. I already have a player(a square) moving and colliding with other squares on the screen. Gravity etc. Are also done.
If there are only that much objects as seen on the screen (30*20), everything works perfectly fine. But if I increase it to let's say 300*300 the program starts to run very slow since it has to check for so many objects.
I really don't get how games like Minecraft can work with ALL THOSE blocks and my program already gives up on 300*300 blocks.
I already tried to ONLY check for collisions when the objects are visible, but that leads to the program checking every single object for it's visibility leading to the same problem.
What am I doing wrong? Help appreciated.
I'll post some code on how I handle the collisions.
player.collision(player, wall);
public void collision(Tile object1, Tile[] object2){
collisionCheckUp(object1, object2);
collisionCheckDown(object1, object2);
collisionCheckLeft(object1, object2);
collisionCheckRight(object1, object2);
}
public void collisionCheckDown(Tile object1, Tile[] object2){
for (int i = 0; i < Map.tileAmount; i++){
if(object2[i] != null && object2[i].visible)
{
if(object1.isCollidingDown(object2[i])){
object1.collisionDown = true;
return;
}
}
}
object1.collisionDown = false;
}
public void compileHullDown(){
collisionHull = new Rectangle((int)x+3, (int)y+3, width-6, height);
}
int wallCount = 0;
for (int x=0;x<Map.WIDTH;x++) {
for (int y=0;y<Map.HEIGHT;y++) {
if (Map.data[x][y] == Map.BLOCKED) {
wall[wallCount] = new Tile(x * Map.TILE_SIZE, y * Map.TILE_SIZE);
wallCount++;
}
}
}
The usual approach to optimize collision detection is to use a space partition to classify/manage your objects.
The general idea of the approach is that you build a tree representing the space and put your objects into that tree, according to their positions. When you calculate the collisions, you traverse the tree. This way, you will have to perform significantly less calculations than using the brute force approach, because you will be ignoring all objects in branches other than the one you're traversing. Minecraft and similar probably use octrees for collision (and maybe for rendering too).
The most common space partition structures are BSP-Trees, kd-Trees (a special type of BSP-trees). The simpler approach would be to use a uniform space partition for the start - split your space in axis-aligned halves.
The best resource on collision that I have discovered is this book. It should clarify all your questions on the topic.
That's if you wanted to do it right. If you want to do it quick, you could just sample the color buffer around your character, or only in the movement direction to determine if an obstacle is close.
As Kostja mentioned, it will be useful for you to partition your space. However, you will need to use QuadTrees instead of Octrees as you are only in 2D not 3D.
Here are is a nice article to get you started on QuadTrees.
You can cut your overhead by a factor of 4 by, instead of calculating collisions for up/down/left/right, calculating collisions once and using the relative positions of the two objects to find out if you hit a floor, wall, or ceiling. Another good idea is to only pay attention to the objects that are nearby - maybe once every 0.25 seconds make a list of all objects that are probably close enough to collide with in the next 0.25 seconds?
I would like to store the x y and z co-ords for some objects for a game but I can't find a built in class like Point. Is there a nice standard class I could add in and use that would handle distance between points/bearings from one object to another etc?
Having recently done some vector mapping (including z / 3D), and seeing your Android tag, I recommend rolling your own.
The reasons are many:
You can customize to meet your specific precision / memory / performance constraints.
If multi threaded, you can make your class immutable and thread-safe
I.e. If memory constrained you can store all three dimensions in an int or long
If cpu constrained you can use plain-old separate numbers
If GC / Garbage constrained, you can recycle and pool instances (mutable)
In the end, most of these primitives are quite simple to write, test, etc. The main methods you'll need to write (beyond boilerplate constructor/get/set/...)
- Distance
- Dot product
- Unitize (make length == 1 for various math ops)
- And I've used DistanceSquared in the past for comparison functions... This removes the sqrt operator from most distance methods, while computing a relative distance useful enough for comparing point distances etc.
Maybe Point3D is what you need.
There is also a JavaFX class Point3D that meets your requirements.