I'm relatively new to TDD design and am working on a project to gain experience with it. My project is a tower defense style game - I'm starting out with a basic Creep (monster) and designing it based off of tested behavior. However, how the creep moves is a rather complicated method. It currently looks something along the lines of:
public void moveToward(Point2D destination) {
if (canMove()) {
if (speedGreaterThanDistance(destination) {
leftoverDistance = calculateLevtoverDistance(destination);
currentLocation.setLocation(destination);
} else {
// do math to calculate x distance and y distance to move
Point2D newLocation = new Point2D.Double(oldX + xTrans, oldY + yTrans);
currentLocation.setLocation(newLocation);
}
}
}
This really doesn't feel like the right design and structure, but I'm unsure how to go about segmenting it up. Does anyone have any suggestions or links to point me to on how to go about doing this? My design has a Creep object as part of my business logic, so I know it's going to have to be at least somewhat complex, but it just doesn't seem like I have it right at the moment. Could part of my problem be that I don't have appropriate value objects in place?
Thanks,
If I understand your problem correctly, you have a Creep with a CurrentPosition (a Point2D), a Direction (a Vector xTrans, yTrans) and a Destination (a Point2D).
The Creep behavior is, as far as I understand:
When the Creep cannot move, its CurrentPosition after a turn should remain the same,
When the Creep can move and the distance between CurrentPosition and Destination is less than the norm of Direction, the after a turn CurrentPosition should be Destination,
When the Creep can move and the distance between CurrentPosition and Destination is strictly greater than the norm of Direction, the after a turn CurrentPosition should be CurrentPosition + Direction.
In that perspective, I would tend to extract the calculation/geometry part into a Geometry class, and state the Creep behavior as
public void moveToward(Point2D destination)
{
if (CanMove())
{
var distanceToDestination = Geometry.Distance(CurrentPosition, destination);
var distancePerTurn = Geometry.Length(Direction);
if (distanceToDestination <= distancePerTurn)
{
CurrentPosition = destination;
}
else
{
CurrentPosition = Geometry.Add(CurrentPosition, Direction);
}
}
}
You can now test the methods on the Geometry class, and the 3 behaviors of the Creep should be testable as well.
Related
I´m currently programming my first chess game and I have a small problem with my pieces. I´ll add some of my code below, but generally, I would like to check whether the tile a player moves a certain piece (in this example the king) to, is already occupied by another piece because in that case the other piece would either be killed (if the piece belongs to the other player) or the move would be false (tile occupied by own piece).
If you have any ideas how it could be done, I´d appreciate it greatly.
Best regards
public abstract class Piece
{
protected int x;
protected int y;
protected Tile finalTile;
protected Type type;
protected boolean killed;
protected boolean white;
public Piece(boolean white, int x, int y)
{
this.setWhite(white);
this.x = x;
this.y = y;
this.finalTile = finalTile;
}
public boolean isWhite() {
return this.white;
}
public void setWhite (boolean white) {
this.white = white;
}
public boolean isKilled() {
return this.killed;
}
public void setKilled (boolean killed) {
this.killed = killed;
}
// which type of Piece is being used
public abstract Type getType();
//is the chosen move valid?
public abstract boolean canMovePiece (Chessboard chessboard, int finalX, int finalY);
//The validation of the move does not show in which direction the piece is being moved. To set
the piece at its new spot (tile), a determination of the direction is needed for the
// calculation of the new spot on the chessboard
public abstract void setNewPieceLocation();
}
public class King extends Piece
{
public King(boolean white, int x, int y)
{
super(white, x, y);
type = Type.KING;
}
public Type getType() {
return Type.KING;
}
#Override
public boolean canMovePiece(Chessboard chessboard, int finalX, int finalY) {
int x_diff = Math.abs(finalX - this.x);
int y_diff = Math.abs(finalY - this.y);
//the king can only move one tile per round. The direction does not matter. Therefore, the
movements can be either one on the x-axis, the y-axis or one on both.
if ( x_diff == 1 && y_diff == 0 || x_diff == 0 && y_diff == 1 || x_diff == 1 && y_diff ==1) {
return true;
}
}
#Override
public void setNewPieceLocation() {
int finalX = this.x;
int finalY = this.y;
}
}
In tile-based computer games, you always have to make a very important initial design decision about what you choose as your central data structure:
The field itself, e.g. as a two-dimensional array, where the state of each tile in the field represents which piece is on it or
A collection of pieces with coordinates.
Option 1 is great if you have a small field and the pieces can only be directly on a tile.
Option 2 should only be done if option 2 is not possible, i.e.:
your game world is very large and sparse, e.g. 1 million x 1 million, then you would have an array with one trillion entries or (inclusive or)
your pieces can be halfway or otherwise fractionally located respectively with the tiles
Option 1 is easiest to implement if there is at most one piece at any one tile at the same time but you can work around that and it would depend on the specific circumstances, which one is best.
Now which case do we have in chess? As an exercise you can think about this yourself but I will give the solution below:
The first option is the perfect fit for chess, because you have a very small field (8x8), the pieces are exactly on the tiles (in theory, even if they may be a few millimeters off in practice) and there can be at most one piece per tile.
Option 1 has the advantage that it becomes extremely easy to check, which piece is on a certain tile: just check the field data structure at that point!
Another very important point for beginners is however how to map your coordinate system to your data structure especially in the case of a two dimensional array. Which dimension of the array is which axis of your game world and in which direction? There is however no right or wrong here, just clearly document it and be consistent!
For example, I personally like to have y going from top to bottom as the first dimension of the array and x going from left to right as field[y][x] because computer graphics memory is aligned that way (at least it was this way in the old 10h 320x200 8 bit palette video mode) but a mathematics teacher may very well prefer x first and y going from bottom to top as field[x][y] because that's how you usually draw 2-dimensional plots and write points with x first in mathematical notation.
So in my mental model, checking the top left would be checking the value field[0][0], top right would be field[0][7], bottom left field[7][0] and bottom right would be field[7][7].
Our hypothetical mathematics teacher could instead use field[0][7] for top left, top right would be field[7][7], bottom left field[0][0] and bottom right would be field[7][0].
Just use what works for you (and your team if you have one).
In order to prevent problems with object equality vs object identity I would not use a class for the type but an enum as that is the perfect fit for this case. As an empty field, you can either use null, but then you may need a bunch of checks to prevent NullPointerException, or you create a special value for an empty field.
Checking, which piece is in a certain spot with option 2 is not hard either, but as option 1 is so much easier here and also faster in case that matters, I will not get into detail about that here as that is not the best solution to your problem.
So, my problem is that I'm currently working on a path-finding technique in an open world with tiles of various sizes. The object needs to find an optimal path to a destination inside an infinite world (it generates on the fly), which is filled with tiles of various sizes (which are not located on a set grid - they can have any location and size - and neither have to be integers). (The object has access to the data of all the tiles via and ArrayList). Now some factors that make this problem more difficult:
The objects itself has a size and cannot move through tiles. Therefore, it is possible for a path to exist that is too narrow for the object to move through.
The target destination may itself be a moving object.
It is possible for there to be dozens of such objects at the same time - so it is necessary for the algorithm to either be light on the system or for the path to be calculated in a few separate ticks of the program.
I tried implementing solutions for maze-solving techniques, but the main problem is the in most mazes, the tiles can only have very specific coordinates (such as whole numbers) and are always the same size.
I also tried rendering the scene as a giant conventional maze where tiles are actually pixels of tiles (so if i have a 20x40 tile it becomes a 20x40 block of 1x1 tiles), but ran into performance issues and the still didn't solve the issue with a path potentially being to narrow for the object to fit through.
EDIT:
Terribly sorry for my poor wording before, that happens when I'm trying to rush to a solution without fully understanding the question. So what I'm using the algorithm for at the moment is for NPC enemies to find their way to the player around obstacles. Here is an example of a scene:
The black circle with an arrow is the player, the black bush-like blobs are the NPC enemies. So this my my current algorithm I'm using for the enemy AI:
void move() { //part of the Enemy class, this function is called once each tick for every enemy
PVector velocity = new PVector(speed*game.dt, 0); //speed is a pre-set float denoting the enemy's speed, game.dt is deltatim
velocity.rotate(atan2(game.player.location.y-location.y, game.player.location.x-location.x)); //game.player.location is a PVector of the player's position, location is a PVector of this enemy's position
boolean init_collision = getTileCollision(); //getTileCollision is a boolean of whether this enemy is colliding with any tile
location.add(velocity);
boolean collision = getTileCollision();
if (!init_collision && collision) { //if the enemy happens to spawn inside a tile, let is move out of it before checking for collision
location.sub(velocity);
if (desired_heading != -1) { //desired heading is the angle, in radians, of which 90-degree angle the enemy wants to move in, by default set to -1 (see my descrition of this algorithm below)
velocity = new PVector(speed*game.dt, 0);
velocity.rotate(desired_heading);
location.add(velocity);
if (getTileCollision()) {
location.sub(velocity);
velocity = new PVector(speed*game.dt, 0);
velocity.rotate(current_heading); //current heading the an angle, in radians, of which 90-degree angle the enemy is currently moving in. set to -1 by default but can not equal -1 if desired_heading is not -1
location.add(velocity);
if (getTileCollision()) {
location.sub(velocity);
desired_heading = -1;
current_heading = -1;
}
} else {
desired_heading = -1;
current_heading = -1;
}
} else {
float original_heading = velocity.heading();
desired_heading = radians(round(degrees(velocity.heading())/90.0)*90.0); //round to the nearest 90 degrees
velocity = new PVector(speed*game.dt, 0);
velocity.rotate(desired_heading);
location.add(velocity);
if (getTileCollision()) {
location.sub(velocity);
}
float turn = radians(90);
while (true) { //if it cant move, try rotating 90 degrees and moving
velocity.rotate(turn);
location.add(velocity);
if (!getTileCollision() && abs(round(degrees(current_heading)) - round(degrees(velocity.heading()))) != 180) {
current_heading = velocity.heading();
break;
} else {
location.sub(velocity);
}
}
}
} else {
desired_heading = -1;
current_heading = -1;
}
}
So what my terrible code hopes to accomplish is the the enemy first tries to move directly at the player. If it encounters an obstacle, it will round its angle to the nearest 90 degrees, set desired_heading to this and try to move through. If it cant, it will rotate another 90 degrees and so forth, always keeping the original rounded angle in mind.
This doesn't work remotely well as first of all, rotating 90 degrees has a 50% chance to go in the exact wrong diretion, so I tried adding
if (abs(original_heading - velocity.heading()+turn) < abs(original_heading - velocity.heading()-turn)) {
turn = radians(-90);
}
right before the while (true) but that broke the algorithm completely (sometimes the enemy will freeze in deep thought and not move ever again).
What am I doing terribly wrong? Should I try a different algorithm or does this one have potential?
I hope this is a better question now...
I'm learning a lot more about Java 8 and its functional capabilities, and I wanted to do some more practice with it. Say, for example, I have the following imperative code which is for wrapping a circle around the bounds of the screen:
if (circle.getPosition().getX() > width + circle.getRadius()){
circle.getPosition().setX(-circle.getRadius());
}else if (circle.getPosition().getX() < -circle.getRadius()){
circle.getPosition().setX(width + circle.getRadius());
}
if (circle.getPosition().getY() > height + circle.getRadius()){
circle.getPosition().setY(-circle.getRadius());
}else if (circle.getPosition().getY() < -circle.getRadius()){
circle.getPosition().setY(height + circle.getRadius());
}
How could I go about trying to "Functionalize" it? Maybe some pseudo-code? It seems to me that mutability and state seem inherent in this example.
Is functional programming not a good fit for game development? I love the both, so I'm trying to combine them.
There is nothing inherent about the requirement for mutability in this example. The imperative approach is to modify an existing circles by applying side-effects which alter the state of an existing circle.
The functional approach is to have an immutable data structure and create a function that takes data from the first structure and creates a new structure. In your example, a functional approach would have the circle being immutable, i.e. no setX() or setY() methods.
private Circle wrapCircleAroundBounds(Circle circle, double width, double height) {
double newx = (circle.getPosition().getX() > width + circle.getRadius()) ? -circle.getRadius() : width + circle.getRadius()
double newy = (circle.getPosition().getY() > height + circle.getRadius()) ? -circle.getRadius() : height + circle.getRadius()
return new Circle(newx, newy)
}
Using Java8's functional features, you could then imagine mapping a list of circles to wrapped circles:
circles.stream().map(circ -> wrapCircleAroundBounds(circ, width, height))
The imperative and functional approaches have different advantages, the functional approach, for example, is intrisicaly threadsafe because of the immutability so you should be able to more readily parallelise this kind of code. For instance, one could equally safely write:
circles.parallelStream().map(circ -> wrapCircleAroundBounds(circ, width, height))
I don't think that functional programming is necessarily badly suited to game development but, although it has be done, it's certainly not a standard approach so you won't get the same level of library support if you're using a functional language.
As dfeuer states in his answer, Java's functional features are pretty primitive - you don't have support for algebraic data types, pattern matching, etc which will make it much easier to express problems in a functional style (at least once you get used to those idioms). I agree that at least reading a bit about Haskell, which has an excellent tutorial: http://learnyouahaskell.com/chapters would be a good way to get started. Unlike Scala, which is very much a multiparadigm language, you won't have OOP features to fall back on while you're learning the new style.
For your first point: You "functionalize" your example by thinking about what the code ought to achieve. And this is, you have a circle, and want to compute another circle based on some conditions. But for some reason your imperative upbringing makes you assume that the input circle and the output circle should be stored in the same memory locations!
For being functional, the first thing is to forget memory locations and embrace values. Think of every type the same way you think of int or java.lang.Integer or the other numeric types.
For an example, assume some newbie shows you some code like this:
double x = 3.765;
sin(x);
System.out.println("The square root of x is " + x);
and complains that sin doesn't seem to work. What would you think then?
Now consider this:
Circle myCircle = ....;
wrapAroundBoundsOfScreen(myCircle);
System.out.println("The wrapped-around circle is now " + myCircle);
You will have climbed the first step to functional programming when the latter code seems as absurd to you as the former. And yes, this does mean not to use certain features of the imperative language you are using, or use them extremely sparingly.
Here not much 'functionalization' applicable. But at least we can fight with mutability.
First of all pure functions. This will help to separate logic. Make it clear and easy to test.
Answer the question: what is your code do? It accepts some params and returns two params new x and y.
Next samples will be written with pseudo scala.
So you need a function that will be invoked two times for both x and y calculation.
def (xOrY: Int, widthOrHeight: Int, radius: Int): Int = {
if (x > widthOrHeight + radius) -1*radius else widthOrHeight + radius
// do your calculation here - return x or y values.
}
P.S> so far no matter where you want to apply functional style: as you need to do some business logic it's good to go with functional approach.
But do not try overcomplicate it as it does not help.
So what I would not do for this sample is next (pseudo scala goes next):
def tryToMakeMove(conditions: => Boolean, move: => Unit) = if (conditions) move()
/// DO NOT DO IT AT HOME :)
tryToMakeMove(circle.getPosition().getX() > width + circle.getRadius(), circle.getPosition().setX(-circle.getRadius())).andThen()
tryToMakeMove(circle.getPosition().getX() < -circle.getRadius()), circle.getPosition().setX(width + circle.getRadius()))
).andThen ... so on.
That how functional programs can looks like. I've created the higher-order function (that accepts other functions as an arguments and invoke it inside).
With this functions, i've invoked one be one operations you have to do...
But such functional style does not really help. At all. You should apply it properly only in a places where it's simplify the code.
You can write functional code in just about any programming language, but you can't easily learn functional programming in any language. Java in particular makes functional programming sufficiently painful that people who wanted to do functional programming in the JVM came up with Clojure and Scalaz. If you want to learn the functional way of thinking (what problems it deals with naturally and how, what problems are more awkward and how it manages them, etc.), I strongly recommend that you spend some time with a functional or mostly-functional language. Based on a combination of language quality, ease of sticking to functional idioms, learning resources, and community, my top pick would be Haskell and my next would be Racket. Others will of course have other opinions.
How could I go about trying to "Functionalize" it? Maybe some
pseudo-code? It seems to me that mutability and state seem inherent in
this example.
You could try to limit the mutability to a few functions, and also use final variables inside the functions (which forces you to use expressions rather than statements). Here's one possible way:
Position wrapCircle(Circle circle, int width, int height) {
final int radius = circle.getRadius();
final Position pos = circle.getPosition();
final int oldX = pos.getX();
final int x = (oldX > width + radius) ? -radius : (
(oldX < -radius) ? (width + radius) : oldX);
final int y = // similar
return new Position(x, y);
}
circle.setPosition(wrapCircle(circle, width, height));
Aside, I would make wrapCircle a method of the Circle class, to get:
circle.wrapCircle(width, height);
Or I could go one step further and define a getWrappedCircle method, that returns me a new circle instance:
Circle getWrappedCircle(width, height) {
newCircle = this.clone();
newCircle.wrapCircle(width, height);
return newCircle();
}
.. depending on how you intend to structure the rest of the code.
Tip: Use final keyword as often as you can in Java. It automatically lends to a more functional style.
Is functional programming not a good fit for game development? I love the both, so I'm trying to combine them.
Pure functional programming is slower, because it requires lots of copying / cloning of data. If performance is important, then you could definitely try a mixed approach, as shown above.
I would suggest using as much immutability as possible, followed by benchmarking, and then converting to mutability in only the performance critical sections.
Functional programming fits game development (why would not it?). The question is usually more about performance and memory consumption or even if any functional game engine can beat an existing non-functional one in those metrics. You are not the only person who loves functional programming and game development. Seems like John Carmack does too, watch his keynotes about the topics at Quakecon 2013 starting from 02:05. His notes here and here even give insight on how a functional game engine can be structured.
Setting theoretical foundation aside, there are usually two concepts perceived inherent in functional programming by a newcomer and from a practical prospect. They are data immutability and state absence. The former means that data never changes and the latter means every task is performed as if for the first time with no prior knowledge.
Considering that, you imperative code has two problems: the setters mutate the circle position and the code relies on outside values (a global state) of width and height. To fix them make your function return a new circle on each update and take the screen resolutions as arguments. Let's apply the first clue from the video and pass a reference to the static snapshot of the world and a reference to an entity being "updated" (it is simply this here) to an update function:
class Circle extends ImmutableEntity {
private int radius;
public Circle(State state, Position position, int radius) {
super(state, position);
this.radius = radius;
}
public int getRadius() {
return radius;
}
#Override
public ImmutableEntity update(World world) {
int updatedX = getPosition().getX();
if (getPosition().getX() > world.getWidth() + radius){
updatedX = -radius;
} else if (getPosition().getX() < -radius){
updatedX = world.getWidth() + radius;
}
int updatedY = getPosition().getX();
if (getPosition().getY() > world.getHeight() + radius){
updatedY = -radius;
} else if (getPosition().getY() < -radius) {
updatedY = world.getHeight() + radius;
}
return new Circle(getState(), new Position(updatedX, updatedY), radius);
}
}
class Position {
private int x;
private int y;
//here can be other quantities like speed, velocity etc.
public Position(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
class State { /*...*/ }
abstract class ImmutableEntity {
private State state;
private Position position;
public ImmutableEntity(State state, Position position) {
this.state = state;
this.position = position;
}
public State getState() {
return state;
}
public Position getPosition() {
return position;
}
public abstract ImmutableEntity update(World world);
}
class World {
private int width;
private int height;
public World(int width, int height) {
this.width = width;
this.height = height;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
}
Now the tricky part is how to affect the state of the world and other entities. You can follow the second clue from the video and use event passing mechanism to pass such changes to and fro so the rest of the game knows about all the effects.
Obviously, you can keep only events and rely completely on them even when changing your circle positions. So, if you introduce sort of an id to your entities you will be able to pass MoveEntity(id, newPosition).
OK, it's time for us all to get over how new and shiny Java 8's functional features look. "Functionalizing" something is really not a valid goal to have.
However, the original code here has a good ol' object-oriented problem:
When you say circle.getPosition().setX(...), you are messing with the internal state of the circle (its position) without involving the object itself. That breaks encapsulation. If the circle class were properly designed, then the getPosition() method would return a copy of the position or an immutable position so that you couldn't do this.
That is the problem you really need to fix with this code...
How, then, should you do that?
Well, you could certainly come up with some functional interface in Circle, but honestly your code will be more readable if you just have circle.move(double x, double y);
I have the problem with processing digital signals. I am trying to detect fingertips, similar to the solution that is presented here: Hand and finger detection using JavaCV.
However, I am not using JavaCV but OpenCV for android which is slightly different.
I have managed to do all the steps presented in the tutorial, but filtering of convex hulls and convexity defects. This is how my image looks like:
Here is an image in another resolution:
As you can clearly see, There is to many yellow points (convex hulls) and also to many red points (convexity deffects). Sometimes between 2 yellow points there is no red point, which is quite strange (how are convex hulls calculated?)
What I need is to create simillar filtering function like in the link provided before, but using data structures of OpenCV.
Convex Hulls are type of MatOfInt ...
Convexity defects are type of MatOfInt4 ...
I created also some additional data structures, because stupid OpenCV uses different types of data containing same data, in different methods...
convexHullMatOfInt = new MatOfInt();
convexHullPointArrayList = new ArrayList<Point>();
convexHullMatOfPoint = new MatOfPoint();
convexHullMatOfPointArrayList = new ArrayList<MatOfPoint>();
Here is what I did so far but it is not working good. The problem is probably with converting data in a wrong way:
Creating convex hulls and convexity defects:
public void calculateConvexHulls()
{
convexHullMatOfInt = new MatOfInt();
convexHullPointArrayList = new ArrayList<Point>();
convexHullMatOfPoint = new MatOfPoint();
convexHullMatOfPointArrayList = new ArrayList<MatOfPoint>();
try {
//Calculate convex hulls
if(aproximatedContours.size() > 0)
{
Imgproc.convexHull( aproximatedContours.get(0), convexHullMatOfInt, false);
for(int j=0; j < convexHullMatOfInt.toList().size(); j++)
convexHullPointArrayList.add(aproximatedContours.get(0).toList().get(convexHullMatOfInt.toList().get(j)));
convexHullMatOfPoint.fromList(convexHullPointArrayList);
convexHullMatOfPointArrayList.add(convexHullMatOfPoint);
}
} catch (Exception e) {
// TODO Auto-generated catch block
Log.e("Calculate convex hulls failed.", "Details below");
e.printStackTrace();
}
}
public void calculateConvexityDefects()
{
mConvexityDefectsMatOfInt4 = new MatOfInt4();
try {
Imgproc.convexityDefects(aproximatedContours.get(0), convexHullMatOfInt, mConvexityDefectsMatOfInt4);
if(!mConvexityDefectsMatOfInt4.empty())
{
mConvexityDefectsIntArrayList = new int[mConvexityDefectsMatOfInt4.toArray().length];
mConvexityDefectsIntArrayList = mConvexityDefectsMatOfInt4.toArray();
}
} catch (Exception e) {
Log.e("Calculate convex hulls failed.", "Details below");
e.printStackTrace();
}
}
Filtering:
public void filterCalculatedPoints()
{
ArrayList<Point> tipPts = new ArrayList<Point>();
ArrayList<Point> foldPts = new ArrayList<Point>();
ArrayList<Integer> depths = new ArrayList<Integer>();
fingerTips = new ArrayList<Point>();
for (int i = 0; i < mConvexityDefectsIntArrayList.length/4; i++)
{
tipPts.add(contours.get(0).toList().get(mConvexityDefectsIntArrayList[4*i]));
tipPts.add(contours.get(0).toList().get(mConvexityDefectsIntArrayList[4*i+1]));
foldPts.add(contours.get(0).toList().get(mConvexityDefectsIntArrayList[4*i+2]));
depths.add(mConvexityDefectsIntArrayList[4*i+3]);
}
int numPoints = foldPts.size();
for (int i=0; i < numPoints; i++) {
if ((depths.get(i).intValue()) < MIN_FINGER_DEPTH)
continue;
// look at fold points on either side of a tip
int pdx = (i == 0) ? (numPoints-1) : (i - 1);
int sdx = (i == numPoints-1) ? 0 : (i + 1);
int angle = angleBetween(tipPts.get(i), foldPts.get(pdx), foldPts.get(sdx));
if (angle >= MAX_FINGER_ANGLE) // angle between finger and folds too wide
continue;
// this point is probably a fingertip, so add to list
fingerTips.add(tipPts.get(i));
}
}
Results (white points - fingertips after filtering):
Could you help me to write proper function for filtering?
UPDATE 14.08.2013
I use standard openCV function for contour approximation. I have to change approximation value with resolution change, and hand-to-camera distance, which is quite hard to do. If the resolution is smaller, then finger consist of less pixel, thus approximation value should be lover. Same with the distance. Keeping it high will result in completely losing the finger. So I think approximation is not good approach to resolving the problem, however small value could be useful to speed up calculations:
Imgproc.approxPolyDP(frame, frame, 2 , true);
If I use high values, then the result is like on the image below, which would be good only if distance and resolution wouldn't change. Also, I am quite surprised that default methods for hulls points and defects points doesn't have useful arguments to pass (min angle, distance etc)...
Image below presents the effect that I would like to achieve always, independently from resolution or hand-to-camera distance. Also I don't want to see any yellow points when I close my palm...
To sum everything up, I would like to know:
how to filter the points
how can I make resolution and distance independent approximation which will always work
if someone knows or have some materials (graphical representation, explanation) about those data structures used in OpenCV, I would be happy to read it. (Mat, MatOfInt, MatOfPoint, MatOfPoint2, MatOfPoint4 etc.)
The convex hull at low res can be used to identify the position of the hand as a whole, it is not useful for fingers but does provide a region of interest and appropriate scale.
The higher resolution analysis should then be applied to your approximated contour, it is easy to skip any points that do not pass the "length and angle" criteria from the last two, though you may wish to "average in" instead of "skip entirely".
Your code example is a single pass of calculating convexity defects and then removing them .. that is a logic error .. you need to remove points as you go .. (a) it is faster and simpler to do everything in one-pass (b) it avoids removing points at a first pass and having to add them back later because any removal changes previous calcs.
This basic technique is very simple and so works for a basic open palm. It doesn't intrinsically understand a hand or a gesture though, so tuning the scale, angle and length parameters is only ever going to get you "so far".
References to Techniques:
filter length and angle "Convexity defect"
Simen Andresen blog http://simena86.github.io/blog/2013/08/12/hand-tracking-and-recognition-with-opencv/
Kinect SDK based C# Library with added finger direction detection
http://candescentnui.codeplex.com/
http://blog.candescent.ch/2011/11/improving-finger-detection.html
"Self-growing and organized neural gas" (SGONG)
Prof Nikos Papamarkos http://www.papamarkos.gr/uploaded-files/Hand%20gesture%20recognition%20using%20a%20neural%20network%20shape%20fitting%20technique.pdf
Commercial product
David Holz & Michael Buckwald founders of "Leap Motion" http://www.engadget.com/2013/03/11/leap-motion-michael-buckwald-interview/
I think you missed that point:
Hull creation and defect analysis are speeded up by utilizing a low-polygon approximation of the contour rather than the original.
I'm trying to test out removing an image from the screen by converting its x and y values into negative values (my screen is a 500 by 500 and cannot be resized) and then returning it to the screen in the next slide by converting the x and y values back into positives so I can then convert them back to negative to remove them again.
In one class (which shows a red pointer on the screen that I can move to the boundaries of other items) I have this:
import java.awt.Rectangle;
public void checkBoundariesTouching() {
NextSlide next = MainFrame.getSlide();
for (int i = 0; i < 1; i++) {
if (getBounds().intersects(next.getBounds())) {
MainFrame.slide += 1;
In my MainFrame, this slide increase changes the background to make my slides interesting and changes the writing shown.
I have another class that produces black rectangles that I remove one by one to uncover a bullet point of writing that I then explain. I remove the rectangles like this:
import java.awt.Rectangle;
public class BlackRectangle extends Entity {
public BlackRectangle(int x, int y) {
super(x, y);
}
public void update() {
int slide = MainFrame.getSlide();
checkBoundariesTouching();
if (slide >= 2) {
if (x < 0) {
x = -x;
}
if (y < 0) {
y = -y;
}
checkBoundariesTouching();
}
}
public void checkBoundariesTouching() {
Pointer pointer = MainFrame.getPointer();
NextSlide next = MainFrame.getSlide();
if (getBounds().intersects(pointer.getBounds())) {
x = -x;
y = -y;
}
When the slide is equal to 1, I can remove the black squares without problem. However, when I have increased to the next slide (from slide 2 onwards) when the pointer is touching a black rectangle, it is moved off screeen but once I move the pointer away from it, the black rectangle returns and covers the information. Is there a way I can fix this code as it stands only changing the update method?
I admit I do not know very much Java at all and started two days ago but I am trying to use Java code for my presentation to prove that I can grasp basic concepts of a program in a short space of time and build on it as I go along (I need to prove this to get into my course at uni that has nothing to do with Computer Science.)
I would seriously counsel you NOT to do this.
If the prerequisites for the course are that you have a basic grasp of programming ... and you don't have that basic grasp ... then you are going to get into real problems down the track. Especially, since if this is not a CS course, they are UNLIKELY to help you out if you run into problems due to poor programming skills.
OTOH, if the problem is that you are competent at (say) C# and not at Java, then the right solution is to fork out the money for a developer license. 'Cos you are going to need it in order to do the development work as part of your course ... or in the future.
(But surely you can find a cheaper way of getting a Visual Studio license. I can get "Visual Studio Professional 2012 with MSDN" from the MS Store for less than $1.2k AUD ... with no student discount.)
I acknowledge your point that they are not specifically testing your Java competence. They are, however testing, you on your general competence as a programmer. And surely that includes your ability to choose the right tool for the job ... which includes not trying to do some programming to a tight deadline in a programming language that you are not familiar with!!
If the code works fine when slide = 1, I would suggest removing this:
if (slide >= 2) {
if (x < 0) {
x = -x;
}
if (y < 0) {
y = -y;
}
checkBoundariesTouching();
}
If you need that code to move the boxes back on the screen, you can put the if statements inside a separate method and call that method when you change slides.
Check for an additional condition within the update method. The method must look like this one below:-
public void update() {
int slide = MainFrame.getSlide();
checkBoundariesTouching();
if (slide >= 2) {
if (x < 0) {
x = x*-1;
}
if (y < 0) {
y = y*-1;
}
}
}
public void checkBoundariesTouching() {
Pointer pointer = MainFrame.getPointer();
NextSlide next = MainFrame.getSlide();
if (getBounds().intersects(pointer.getBounds())) {
if(x>0){
x = -x;
}
if(y>0){
y = -y;
}
}