Kinect skeleton Scaling strange behaviour - scale

I am trying to scale a skeleton to match to the sizes of another skeleton.
My algoritm do the following:
Find the distance between two joints of the origin skeleton and the destiny skeleton using phytagorean teorem
divide this two distances to find a multiply factor.
Multiply each joint by this factor.
Here is my actual code:
public static Skeleton ScaleToMatch(this Skeleton skToBeScaled, Skeleton skDestiny)
{
Joint newJoint = new Joint();
double distanciaOrigem = 0;
double distanciaDestino = 0;
double fator = 1;
SkeletonPoint pos = new SkeletonPoint();
foreach (BoneOrientation bo in skToBeScaled.BoneOrientations)
{
distanciaOrigem = FisioKinectCalcs.Distance3DBetweenJoint(skToBeScaled.Joints[bo.StartJoint], skToBeScaled.Joints[bo.EndJoint]);
distanciaDestino = FisioKinectCalcs.Distance3DBetweenJoint(skDestiny.Joints[bo.StartJoint], skDestiny.Joints[bo.EndJoint]);
if (distanciaOrigem > 0 && distanciaDestino > 0)
{
fator = (distanciaDestino / distanciaOrigem);
newJoint = skToBeScaled.Joints[bo.EndJoint]; // escaling only the end joint as the BoneOrientatios starts from HipCenter, i am scaling from center to edges.
// applying the new values to the joint
pos = new SkeletonPoint()
{
X = (float)(newJoint.Position.X * fator),
Y = (float)(newJoint.Position.Y * fator),
Z = (float)(newJoint.Position.Z * fator)
};
newJoint.Position = pos;
skToBeScaled.Joints[bo.EndJoint] = newJoint;
}
}
return skToBeScaled;
}
Every seems to work fine except for the hands and foots
Look at this images
I have my own skeleton over me, and my skeleton scaled to the sizes of another person, but the hands and foots still crazy. (but code looks right)
Any suggestion?

It's hard to say without running the code, but it somewhat "looks good".
What I would validate though, is your
if (distanciaOrigem > 0 && distanciaDestino > 0)
If distanciaOrigem is very close to 0, but even just epsilon away from 0, it won't be picked up by the if, and then
fator = (distanciaDestino / distanciaOrigem);
Will result in a very large number!

I would suggest to smooth the factor so it generally fits the proper scale. Try this code:
private static Dictionary<JointType, double> jointFactors = null;
static CalibrationUtils()
{
InitJointFactors();
}
public static class EnumUtil
{
public static IEnumerable<T> GetValues<T>()
{
return Enum.GetValues(typeof(T)).Cast<T>();
}
}
private static void InitJointFactors()
{
var jointTypes = EnumUtil.GetValues<JointType>();
jointFactors = new Dictionary<JointType, double>();
foreach(JointType type in jointTypes)
{
jointFactors.Add(type, 0);
}
}
private static double SmoothenFactor(JointType jointType, double factor, int weight)
{
double currentValue = jointFactors[jointType];
double newValue = 0;
if(currentValue != 0)
newValue = (weight * currentValue + factor) / (weight + 1);
else
newValue = factor;
jointFactors[jointType] = newValue;
return newValue;
}
When it comes to factor usage just use the SmoothenFactor method first:
public static Skeleton ScaleToMatch(this Skeleton skToBeScaled, Skeleton skDestiny, double additionalFactor = 1)
{
Joint newJoint = new Joint();
double distanceToScale = 0;
double distanceDestiny = 0;
double factor = 1;
int weight = 500;
SkeletonPoint pos = new SkeletonPoint();
Skeleton newSkeleton = null;
KinectHelper.CopySkeleton(skToBeScaled, ref newSkeleton);
SkeletonPoint hipCenterPosition = newSkeleton.Joints[JointType.HipCenter].Position;
foreach(BoneOrientation bo in skToBeScaled.BoneOrientations)
{
distanceToScale = Distance3DBetweenJoints(skToBeScaled.Joints[bo.StartJoint], skToBeScaled.Joints[bo.EndJoint]);
distanceDestiny = Distance3DBetweenJoints(skDestiny.Joints[bo.StartJoint], skDestiny.Joints[bo.EndJoint]);
if(distanceToScale > 0 && distanceDestiny > 0)
{
factor = (distanceDestiny / distanceToScale) * additionalFactor;
newJoint = skToBeScaled.Joints[bo.EndJoint]; // escaling only the end joint as the BoneOrientatios starts from HipCenter, i am scaling from center to edges.
factor = SmoothenFactor(newJoint.JointType, factor, weight);
pos = new SkeletonPoint()
{
X = (float)((newJoint.Position.X - hipCenterPosition.X) * factor + hipCenterPosition.X),
Y = (float)((newJoint.Position.Y - hipCenterPosition.Y) * factor + hipCenterPosition.Y),
Z = (float)((newJoint.Position.Z - hipCenterPosition.Z) * factor + hipCenterPosition.Z)
};
newJoint.Position = pos;
newSkeleton.Joints[bo.EndJoint] = newJoint;
}
}
return newSkeleton;
}
I also modified your ScaleToMatch method as you see. There was a need to move joints in relation to HipCenter position. Also new positions are saved to a new Skeleton instance so they are not used in further vector calculations.
Experiment with the weight but since our bones length is constant you can use big numbers like 100 and more to be sure that wrong Kinect readings do not disturb the correct scale.
Here's an example of how it helped with scaling HandRight joint position:
The weight was set to 500. The resulting factor is supposed to be around 2 (because the base skeleton was purposely downscaled by a factor of 2).
I hope it helps!

Related

PixelWriten canvas won't display on screen

I have an intArray of ints corresponding to pixel RGB values and am trying to write them to an image that I then add to a canvas displayed in a view.
I have followed the JavaFX docs guideline for using a pixelWriter and I know my syntax for adding a canvas to the view is correct cause I have done the same elsewhere I my app and tested putting a different canvas here correctly.
I've tried the context way:
View class:
vbox(20.0, Pos.BOTTOM_CENTER) {
if (controller.t > 100) { //same results with or without this
label("IR camera view title")
children.add(controller.rPiSensors.canvasIR)
}
}
Model class:
val canvasIR = Canvas(lIR_WIDTH_RES.toDouble(), lIR_HEIGHT_RES.toDouble())
val gc: GraphicsContext = canvasIR.graphicsContext2D
var lIRPixelWriter = gc.pixelWriter
for (y in 0 until 32) {
for (x in 0 until 24) {
lIRPixelWriter.setPixels(0,0,lIR_WIDTH_RES,lIR_HEIGHT_RES, PixelFormat.getIntArgbInstance(), lIRHeatMapPixelInts, 0 , lIR_WIDTH_RES)
}
}
I've also tried the WritableImage way as mentioned in this SO post Creating a Javafx image from a int array:
View class:
vbox(20.0, Pos.BOTTOM_CENTER) {
if (controller.t > 100) { //same results with or without this
label("IR camera view title")
//tested this too controller.rPiSensors.imageView.show()
children.add(controller.rPiSensors.imageView)
}
}
Model class:
var lIRHeatMapPixelInts = IntArray(768) //24*32
//write to this in calculateIRPixelHelper below
var lIRHeatImage = WritableImage(lIR_WIDTH_RES, lIR_HEIGHT_RES)
var lIRPixelWriter = lIRHeatImage.pixelWriter
val imageView = ImageView(lIRHeatImage)
for (y in 0 until 32) {
for (x in 0 until 24) {
lIRPixelWriter.setPixels(0,0,lIR_WIDTH_RES,lIR_HEIGHT_RES, PixelFormat.getIntArgbInstance(), lIRHeatMapPixelInts, 0 , lIR_WIDTH_RES)
}
}
function to get IR RGBInt from float value of temperature (shown to be correct on RasPi that acquires data, and I have compared my received data vs snet RasPi data)
fun calculateIRPixelHelper(x : Int, y : Int, v : Float) {
//float color[NUM_COLORS][3] = { {0;0;0}; {0;0;1}; {0;1;0}; {1,1,0},{1,0,0}, {1,0,1}, {1,1,1} }
val color : Array<FloatArray> = arrayOf(floatArrayOf(0.0f,0.0f,0.0f), floatArrayOf(0.0f,0.0f,1.0f), floatArrayOf(0.0f,1.0f,0.0f), floatArrayOf(1.0f,1.0f,0.0f), floatArrayOf(1.0f,0.0f,0.0f), floatArrayOf(1.0f,0.0f,1.0f), floatArrayOf(1.0f,1.0f,1.0f))
var outputV = v
val idx1 : Int
val idx2 : Int
var fractBetween = 0.0f
val vmin = 5.0f
val vmax = 50.0f
val vrange = vmax-vmin
outputV -= vmin
outputV /= vrange
val ir : Int
val ig : Int
val ib : Int
val pixelRGBInt : Int
if (outputV <= 0) {
idx1= 0
idx2= 0
} else if (outputV >= 1) {
idx1 = (NUM_COLORS-1)
idx2 = (NUM_COLORS-1)
} else {
outputV *= (NUM_COLORS-1)
idx1 = truncate(outputV).toInt()
idx2 = idx1+1
fractBetween = outputV - idx1
}
ir = ((((color[idx2][0] - color[idx1][0]) * fractBetween) + color[idx1][0]) * 255.0).toInt()
ig = ((((color[idx2][1] - color[idx1][1]) * fractBetween) + color[idx1][1]) * 255.0).toInt()
ib = ((((color[idx2][2] - color[idx1][2]) * fractBetween) + color[idx1] [2]) * 255.0).toInt()
pixelRGBInt = (ir shl 16) or (ig shl 8) or ib
for(py in 0 until IMAGE_SCALE) {
for(px in 0 until IMAGE_SCALE) {
lIRHeatMapPixelInts[x + px + IMAGE_SCALE*(y + py)] = pixelRGBInt
}
}
}
Here are the float values of the temperature
the pixel ints, which are all 0 beyond the first ~50
And part of the list of values saved in the canvas
For the first method, there are 133K dirty bits written in the canvas and when I use the debugger it seems that PixelWriter.setPixels is writing over bits on almost every call but slowly progressing in increasing values (the trend is like this:
write val pos = 12, 30, 62, 12, 30, 62, 78, 99, 10 ...
I can copy down the exact numbers if this is suspected to be the issue)
In both cases I thought the issue was in updating the canvas once it's populated but I have eliminated this theory by only adding the canvas once a flag is set in the models--still nothing appears besides the title of the canvas.
I have spent about a full workday on trying to display this, any help would be GREATLY appreciated :)
Alpha bits were set to zero, must set them in the pixelRGBint high bits to non-zero for a non transparent pixel!

Calculate sound value with distance

I have a more mathematical than programming question, sorry if I'm not in the right section. In my 2D game, we can move the camera on a map where there are objects that can emit sound, and this sound volume (defined by a float from 0 to 1) must increase when the screen center is near this object. For example, when the object is at the screen center, the sound volume is 1, and when we move away, the volume must decrease. Each object has its own scope value. (for example 1000 pixels).
I don't know how to write a method that can calculate it.
Here is some of my code (which is not the right calculation) :
private function setVolumeWithDistance():Void
{
sound.volume = getDistanceFromScreenCenter() / range;
// So the volume is a 0 to 1 float, the range is the scope in pixels and
// and the getDistanceFromScreenCenter() is the distance in pixels
}
I already have the method which calculates the distance of the object from the center screen :
public function getDistanceFromScreenCenter():Float
{
return Math.sqrt(Math.pow((Cameraman.getInstance().getFocusPosition().x - position.x), 2) +
Math.pow((Cameraman.getInstance().getFocusPosition().y - position.y), 2));
Simple acoustics can help.
Here is the formula for sound intensity from a point source. It follows an inverse square of distance rule. Build that into your code.
You need to consider the mapping between global and screen coordinates. You have to map pixel location on the screen to physical coordinates and back.
Your distance code is flawed. No one should use pow() to square numbers. Yours is susceptible to round off errors.
This code combines the distance calculation, done properly, and attempts to solve the inverse square intensity calculation. Note: Inverse square is singular for zero distance.
package physics;
/**
* Simple model for an acoustic point source
* Created by Michael
* Creation date 1/16/2016.
* #link https://stackoverflow.com/questions/34827629/calculate-sound-value-with-distance/34828300?noredirect=1#comment57399595_34828300
*/
public class AcousticPointSource {
// Units matter here....
private static final double DEFAULT_REFERENCE_INTENSITY = 0.01;
private static final double DEFAULT_REFERENCE_DISTANCE = 1.0;
// Units matter here...
private double referenceDistance;
private double referenceIntensity;
public static void main(String[] args) {
int numPoints = 20;
double x = 0.0;
double dx = 0.05;
AcousticPointSource source = new AcousticPointSource();
for (int i = 0; i < numPoints; ++i) {
x += dx;
Point p = new Point(x);
System.out.println(String.format("point %s intensity %-10.6f", p, source.intensity(p)));
}
}
public AcousticPointSource() {
this(DEFAULT_REFERENCE_DISTANCE, DEFAULT_REFERENCE_INTENSITY);
}
public AcousticPointSource(double referenceDistance, double referenceIntensity) {
if (referenceDistance <= 0.0) throw new IllegalArgumentException("distance must be positive");
if (referenceIntensity <= 0.0) throw new IllegalArgumentException("intensity must be positive");
this.referenceDistance = referenceDistance;
this.referenceIntensity = referenceIntensity;
}
public double distance2D(Point p1) {
return distance2D(p1, Point.ZERO);
}
public double distance2D(Point p1, Point p2) {
double distance = 0.0;
if ((p1 != null) && (p2 != null)) {
double dx = Math.abs(p1.x - p2.x);
double dy = Math.abs(p1.y - p2.y);
double ratio;
if (dx > dy) {
ratio = dy/dx;
distance = dx;
} else {
ratio = dx/dy;
distance = dy;
}
distance *= Math.sqrt(1.0 + ratio*ratio);
if (Double.isNaN(distance)) {
distance = 0.0;
}
}
return distance;
}
public double intensity(Point p) {
double intensity = 0.0;
if (p != null) {
double distance = distance2D(p);
if (distance != 0.0) {
double ratio = this.referenceDistance/distance;
intensity = this.referenceIntensity*ratio*ratio;
}
}
return intensity;
}
}
class Point {
public static final Point ZERO = new Point(0.0, 0.0, 0.0);
public final double x;
public final double y;
public final double z;
public Point(double x) {
this(x, 0.0, 0.0);
}
public Point(double x, double y) {
this(x, y, 0.0);
}
public Point(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
#Override
public String toString() {
return String.format("(%-10.4f,%-10.4f,%-10.4f)", x, y, z);
}
}

Processing2 2D Physics Collision at 90 degree corners

so this is my first post ever on asking a question about programming, so please be patient :)
For a little project in school I made a little physics class, handling collision. Although it worked out fine I still have a bug I couldn't figure out after some hours of searching and I still don't really know where the problem lies in.
For the implementation we used the on Java based language Processing which is used for an introduction to programming and prototyping.
With the a left mouseclick I can spawn some balls which collide pixel-wise with a certain color on the screen. When colliding with a 90 degree corner they just fall through the obstacle. Sadly I can't post a screenshot because of my lack in reputation.
So my question is about what the problem is. Someone I asked said it could be a problem with the dot product I use for calculating the new mirrored velocity, but I couldn't find anything in that direction. I suspect the error lies somewhere in the part where the new velocity is calculated, in the update method of the PhysicsEntity class.
So thanks to everyone who is answering, I am grateful for every useful hint :)
Here is my code, it consists of three classes. I am going to post everything so you can run the code yourself. If you don't have processing you'll need to download it from http://processing.org/ in order to run the code sample below.
Main.pde NOTE: This part is only an example for using my physics class.
ArrayList<PhysicsEntity> entities = new ArrayList<PhysicsEntity>();
boolean mouseClicked = false;
boolean paused = false;
void setup()
{
size(800, 600);
background(0);
frameRate(60);
}
void draw()
{
if (!paused)
{
clear();
float gameTime = 1 / frameRate;
loadPixels();
for (int x = 0; x < width; ++x)
{
for (int y = height - 100; y < height; ++y)
{
pixels[x + y * width] = color(0, 200, 0, 128);
}
}
for (int x = 0; x < width; ++x)
{
for (int y = 0; y < 20; ++y)
{
pixels[x + y * width] = color(0, 200, 0, 128);
}
}
for (int x = 0; x < 100; ++x)
{
for (int y = 0; y < height; ++y)
{
pixels[x + y * width] = color(0, 200, 0, 128);
}
}
for (int x = width - 100; x < width; ++x)
{
for (int y = 0; y < height; ++y)
{
pixels[x + y * width] = color(0, 200, 0, 128);
}
}
updatePixels();
if (mousePressed)
{
entities.add(new PhysicsEntity(new Vector2(width / 2, height / 2), new Vector2(random(-100, 100), random(-100, 100)), new Vector2(0.0f, 250.0f)));
}
for (int i = 0; i < entities.size(); ++i)
{
entities.get(i).update(gameTime);
entities.get(i).show();
}
}
}
Vector2.pde NOTE: This class is just necessary for calculting things in the physics class.
class Vector2
{
float a;
float b;
Vector2()
{
a = 0.0f;
b = 0.0f;
}
Vector2(float _a, float _b)
{
a = _a;
b = _b;
}
/* Return exact copy of the vector */
Vector2 Copy()
{
return new Vector2(a, b);
}
Vector2 Add(Vector2 vecB)
{
return new Vector2(a + vecB.a, b + vecB.b);
}
Vector2 Substract(Vector2 vecB)
{
return new Vector2(a - vecB.a, b - vecB.b);
}
/* Scale the vector by a scalar x */
Vector2 Scale(float x)
{
return new Vector2(a * x, b * x);
}
Vector2 Divide(float x)
{
return new Vector2(a / x, b / x);
}
float Dot(Vector2 vecB)
{
return (a * vecB.a + b * vecB.b);
}
float SqrLength()
{
return (pow(a, 2) + pow(b, 2));
}
float Length()
{
return sqrt(SqrLength());
}
boolean Equals(Vector2 vecB)
{
return (a != vecB.a || b != vecB.b) ? false : true;
}
}
Vector2 ZeroVector()
{
return new Vector2(0.0f, 0.0f);
}
PhysicsEntity.pde NOTE: That's the class where actually failed.
class PhysicsEntity
{
Vector2 m_Pos;
Vector2 m_PrevPos;
Vector2 m_Vel;
Vector2 m_Acc;
/* bouncyness in case of collision; gets multiplied with the velocity */
float m_fBouncyness = 1.0f;
color collisionKey = color(0, 200, 0, 128);
public PhysicsEntity(Vector2 _pos, Vector2 _vel, Vector2 _acc)
{
if (_vel == null)
_vel = new Vector2(0.0f, 0.0f);
m_Pos = new Vector2(_pos.a, _pos.b);
m_PrevPos = m_Pos;
m_Vel = _vel;
m_Acc = _acc;
}
public void update(float dt)
{
/* Euler Integration more accurate Version */
/* x = x + vt + 0.5*at^2 */
m_Pos = m_Pos.Add(m_Vel.Scale(dt)).Add(m_Acc.Scale(pow(dt, 2)).Scale(0.5));
/* v = v + at */
m_Vel = m_Vel.Add(m_Acc.Scale(dt));
/* Collision based on color key */
if (isCollidable(m_Pos.a, m_Pos.b, collisionKey))
{
float speed = m_Vel.Length();
if (speed > 0.0f)
{
/* normalized vector of velocity */
Vector2 velNorm = m_Vel.Divide(speed);
/* getting the floor normal */
Vector2 floorNorm = interp(m_Pos, m_PrevPos);
if (!floorNorm.Equals(ZeroVector()))
{
/* mirror velocity on floor normal vector */
/* C = A - (2 * B * (A dot B)) where A is original vector, B the mirror, C result. */
Vector2 mirVel = velNorm.Substract(floorNorm.Scale(2.0f).Scale(velNorm.Dot(floorNorm)));
/* caculate new velocity */
m_Vel = mirVel.Scale(speed).Scale(m_fBouncyness);
/* add to position to move out of collision */
m_Pos = m_Pos.Add(m_Vel.Scale(dt));
}
}
}
m_PrevPos = m_Pos;
}
public void show()
{
ellipse(m_Pos.a, m_Pos.b, 10, 10);
}
public Vector2 interp(Vector2 pos, Vector2 PrevPos)
{
/* Vector from previous position to current position */
Vector2 line = pos.Substract(PrevPos);
float iLength = line.Length();
Vector2 lineFraction = ZeroVector();
/* checks if there the is vectorlength greater zero that connects the current and the previous position */
if (iLength > 0.0f)
lineFraction = line.Divide(iLength);
/* loop from through positions between previous position and current position */
for (int i = 0; i <= iLength; ++i)
{
Vector2 normVec = getNormal(PrevPos.Add(lineFraction.Scale(i)), collisionKey);
if (!normVec.Equals(ZeroVector()))
return normVec;
}
return ZeroVector();
}
}
/* returns normal vector of a 2d landscape in a certain area */
public Vector2 getNormal(Vector2 pos, color col)
{
int area = 10;
/* prevent coordinates from being out of the window */
if (pos.a <= area || pos.a >= width - area || pos.b <= area || pos.b >= height - area)
return ZeroVector();
Vector2 avg = new Vector2();
float loops = 0;
/* loop through an area of pixels */
for (int x = -area; x <= area; ++x)
{
for (int y = -area; y <= area; ++y)
{
if (x*x + y*y <= area*area)
{
float sumX = pos.a + float(x);
float sumY = pos.b + float(y);
/* count collidable pixels in area */
if (isCollidable(sumX, sumY, col))
{
/* add up positions of these pixels */
avg.a += sumX;
avg.b += sumY;
++loops;
}
}
}
}
if (loops == 0)
return ZeroVector();
/* calculate average position */
avg = avg.Divide(loops);
/* calculate length of the vector from initial position to average position */
float avgLength = dist(avg.a, avg.b, pos.a, pos.b);
/* check if avgLenth is zero or in other words: if avg is equals to pos */
if (avgLength == 0.0f)
return ZeroVector();
/* calculate vector(connection vector) from initial position to average position */
Vector2 conVec = pos.Substract(avg);
/* return normalized connection vector */
return conVec.Divide(avgLength);
}
/* method to check if pixel on a certain position is collidable */
public boolean isCollidable(float pixelX, float pixelY, color col)
{
if (pixelX >= width || pixelX < 0 || pixelY >= height || pixelY < 0)
return false;
return pixels[int(pixelX) + int(pixelY) * width] == col;
}
Edit1:
So thanks to the friendly first replay I stripped my code by a few lines :) If there is still a problem with my post let me know!
I cant analyze correctness of your whole physic calculation but in my opinion problem is with calculation of new velocity and :
/* caculate new velocity */
m_Vel = mirVel.Scale(speed).Scale(m_fBouncyness);
/* add to position to move out of collision */
m_Pos = m_Pos.Add(m_Vel.Scale(dt));
Because if you change m_fBouncyness to real value simulating some gravitation (0.8f or less) your problem will never occur but if you change it to some unreal value like 2.0f you will lose all your balls after few bounces.
This indicate problem in algorithm. Your approach consist (in simple) of this steps in loop:
update position of ball
calculate new position
correct position depending on bounce
draw ball
Here can be problem because you calculate new position of ball - this position is out of black box so you calculate average position then new velocity and correct new position. Then draw ball and repeat but what if this new position is also out of the black box? This ball will bounce out of border ... this happens in corner because of calculation of average position (in corner you got far away from black box then at classic border (when you set m_fBouncyness to some bigger value this will happen even on normal border not only in corner!))
Hope this could help you to find your problem.
So finally I've got a solution.
It appears that the answer of Majlik was very helpful. According to his answer I did a few changes which I will explain now.
First of all I put the if-statement if (speed > 0.0f) way up, over the whole movement code so nothing happens anymore if the speed is too low. Of course you can define a certain treshold which works for you.
In addition to that I introduced an else-case, for the if(colliding) statement, in which the movement code is handled, so if the ball is currently colliding it doesn't move at all apart from the collision handling code.
Finally I thought of a new way to move the ball out of the collision. The suggestion of Maljik proved to be right. My previous method didn't move the ball out of the collision at all.
For that I made a while loop which loops as long as the ball is still in collision. In every runthrough the ball gets moved by a normalized vector with the same direction as my mirrored velocity vector. For safety reasons I still got an iterator incrementing every time, so it doesn't end in an infinite loop.
After all the solution was very obvious. But thanks to those who answered.
Below the new changed code:
public void update(float dt)
{
float speed = m_Vel.Length();
if (speed > 0.0f)
{
/* Collision based on color key */
if (isCollidable(m_Pos.a, m_Pos.b, collisionKey))
{
/* normalized vector of velocity */
Vector2 velNorm = m_Vel.Divide(speed);
/* getting the floor normal */
Vector2 floorNorm = interp(m_Pos, m_PrevPos);
if (!floorNorm.Equals(ZeroVector()))
{
/* mirror velocity on floor normal vector */
/* C = A - (2 * B * (A dot B)) where A is original vector, B the mirror, C result. */
Vector2 mirVel = velNorm.Substract(floorNorm.Scale(2.0f).Scale(velNorm.Dot(floorNorm)));
/* caculate new velocity */
m_Vel = mirVel.Scale(speed).Scale(m_fBouncyness);
int it = 0;
Vector2 normMirVel = mirVel.Divide(mirVel.Length());
while (isCollidable(m_Pos.a, m_Pos.b, collisionKey) && it < 100)
{
/* add to position to move out of collision */
m_Pos = m_Pos.Add(normMirVel);
++it;
}
}
}
else
{
/* Euler Integration more accurate Version */
/* x = x + vt + 0.5*at^2 */
m_Pos = m_Pos.Add(m_Vel.Scale(dt)).Add(m_Acc.Scale(pow(dt, 2)).Scale(0.5));
/* v = v + at */
m_Vel = m_Vel.Add(m_Acc.Scale(dt));
}
}
m_PrevPos = m_Pos;
}
Edit: I might that this is not an ideal soluation since the ball gets moved further than it should in this frame. Maybe you should only calculate the necessary distance to move out of collision and add the actual velocity step by step. Also you could compare the current velocity direction to the direction where it should go. If it's already moving in the right direction there is no interference needed.

How to draw a continuous curved line from 3 given points at a time

I am trying to draw a continuous curved line in flash. There are many methods but none of the ones I have found so far quite fit my requirements. First of all, I want to use the flash graphic api's curveTo() method. I DO NOT want to simulate a curve with hundreds of calls to lineTo() per curved line segment. It is my experience and understanding that line segments are processor heavy. Flash's quadratic bezier curve should take less CPU power. Please challenge this assumption if you think I am wrong.
I also do not want to use a pre-made method that takes the entire line as an argument (eg mx.charts.chartClasses.GraphicsUtilities.drawPolyline()).
The reason is that I will need to modify the logic eventually to add decorations to the line I am drawing, so I need something I understand at its lowest level.
I have currently created a method that will draw a curve given 3 points, using the mid-point method found here.
Here is a picture:
The problem is that the lines do not actually curve through the "real" points of the line (the gray circles). Is there a way using the power of math that I can adjust the control point so that the curve will actually pass through the "real" point? Given only the current point and its prev/next point as arguments? The code to duplicate the above picture follows. It would be great if I could modify it to meet this requirement (note the exception for first and last point).
package {
import flash.display.Shape;
import flash.display.Sprite;
import flash.display.Stage;
import flash.geom.Point;
[SWF(width="200",height="200")]
public class TestCurves extends Sprite {
public function TestCurves() {
stage.scaleMode = "noScale";
var points:Array = [
new Point(10, 10),
new Point(80, 80),
new Point(80, 160),
new Point(20, 160),
new Point(20, 200),
new Point(200, 100)
];
graphics.lineStyle(2, 0xFF0000);
var point:Point = points[0];
var nextPoint:Point = points[1];
SplineMethod.drawSpline(graphics, point, null, nextPoint);
var prevPoint:Point = point;
var n:int = points.length;
var i:int;
for (i = 2; i < n + 1; i++) {
point = nextPoint;
nextPoint = points[i]; //will eval to null when i == n
SplineMethod.drawSpline(graphics, point, prevPoint, nextPoint);
prevPoint = point;
}
//straight lines and vertices for comparison
graphics.lineStyle(2, 0xC0C0C0, 0.5);
graphics.drawCircle(points[0].x, points[0].y, 4);
for (i = 1; i < n; i++) {
graphics.moveTo(points[i - 1].x, points[i - 1].y);
graphics.lineTo(points[i].x, points[i].y);
graphics.drawCircle(points[i].x, points[i].y, 4);
}
}
}
}
import flash.display.Graphics;
import flash.geom.Point;
internal class SplineMethod {
public static function drawSpline(target:Graphics, p:Point, prev:Point=null, next:Point=null):void {
if (!prev && !next) {
return; //cannot draw a 1-dimensional line, ie a line requires at least two points
}
var mPrev:Point; //mid-point of the previous point and the target point
var mNext:Point; //mid-point of the next point and the target point
if (prev) {
mPrev = new Point((p.x + prev.x) / 2, (p.y + prev.y) / 2);
}
if (next) {
mNext = new Point((p.x + next.x) / 2, (p.y + next.y) / 2);
if (!prev) {
//This is the first line point, only draw to the next point's mid-point
target.moveTo(p.x, p.y);
target.lineTo(mNext.x, mNext.y);
return;
}
} else {
//This is the last line point, finish drawing from the previous mid-point
target.moveTo(mPrev.x, mPrev.y);
target.lineTo(p.x, p.y);
return;
}
//draw from mid-point to mid-point with the target point being the control point.
//Note, the line will unfortunately not pass through the actual vertex... I want to solve this
target.moveTo(mPrev.x, mPrev.y);
target.curveTo(p.x, p.y, mNext.x, mNext.y);
}
}
Later I will be adding arrows and things to the draw method.
I think you're looking for a Catmull-Rom spline. I've googled an AS3 implementation for you but haven't tried it so use at your own discretion:
http://actionsnippet.com/?p=1031
Ok, the Catmull-Rom spline suggestion is a good one but not exactly what I am looking for.
The example from the link provided was a good starting point, but a bit inflexible. I have taken it and modified my original source code to use it. I am posting this as an answer because I think it is more modular and easier to understand than Zevan's blog post (no offense Zevan!). The following code will display the following image:
Here is the code:
package {
import flash.display.Shape;
import flash.display.Sprite;
import flash.display.Stage;
import flash.geom.Point;
[SWF(width="300",height="300")]
public class TestCurves extends Sprite {
public function TestCurves() {
stage.scaleMode = "noScale";
//draw a helpful grid
graphics.lineStyle(1, 0xC0C0C0, 0.5);
for (var x:int = 0; x <= 300; x += 10) {
graphics.moveTo(x, 0);
graphics.lineTo(x, 300);
graphics.moveTo(0, x);
graphics.lineTo(300, x);
}
var points:Array = [
new Point(40, 20),
new Point(120, 80),
new Point(120, 160),
new Point(60, 160),
new Point(60, 200),
new Point(240, 150),
new Point(230, 220),
new Point(230, 280)
];
SplineMethod.setResolution(5);
graphics.lineStyle(2, 0xF00000);
graphics.moveTo(points[0].x, points[0].y);
var n:int = points.length;
var i:int;
for (i = 0; i < n - 1; i++) {
SplineMethod.drawSpline(
graphics,
points[i], //segment start
points[i + 1], //segment end
points[i - 1], //previous point (may be null)
points[i + 2] //next point (may be null)
);
}
//straight lines and vertices for comparison
graphics.lineStyle(2, 0x808080, 0.5);
graphics.drawCircle(points[0].x, points[0].y, 4);
for (i = 1; i < n; i++) {
graphics.moveTo(points[i - 1].x, points[i - 1].y);
graphics.lineTo(points[i].x, points[i].y);
graphics.drawCircle(points[i].x, points[i].y, 4);
}
}
}
}
import flash.display.Graphics;
import flash.geom.Point;
internal class SplineMethod {
//default setting will just draw a straight line
private static var hermiteValues:Array = [0, 0, 1, 0];
public static function setResolution(value:int):void {
var resolution:Number = 1 / value;
hermiteValues = [];
for (var t:Number = resolution; t <= 1; t += resolution) {
var h00:Number = (1 + 2 * t) * (1 - t) * (1 - t);
var h10:Number = t * (1 - t) * (1 - t);
var h01:Number = t * t * (3 - 2 * t);
var h11:Number = t * t * (t - 1);
hermiteValues.push(h00, h10, h01, h11);
}
}
public static function drawSpline(target:Graphics, segmentStart:Point, segmentEnd:Point, prevSegmentEnd:Point=null, nextSegmentStart:Point=null):void {
if (!prevSegmentEnd) {
prevSegmentEnd = segmentStart;
}
if (!nextSegmentStart) {
nextSegmentStart = segmentEnd;
}
var m1:Point = new Point((segmentEnd.x - prevSegmentEnd.x) / 2, (segmentEnd.y - prevSegmentEnd.y) / 2);
var m2:Point = new Point((nextSegmentStart.x - segmentStart.x) / 2, (nextSegmentStart.y - segmentStart.y) / 2);
var n:int = hermiteValues.length;
for (var i:int = 0; i < n; i += 4) {
var h00:Number = hermiteValues[i];
var h10:Number = hermiteValues[i + 1];
var h01:Number = hermiteValues[i + 2];
var h11:Number = hermiteValues[i + 3];
var px:Number = h00 * segmentStart.x + h10 * m1.x + h01 * segmentEnd.x + h11 * m2.x;
var py:Number = h00 * segmentStart.y + h10 * m1.y + h01 * segmentEnd.y + h11 * m2.y;
target.lineTo(px, py);
}
}
}
This is not a perfect solution. But unfortunately, I cannot piece together how to accomplish what I want using curveTo(). Note that GraphicsUtilities.drawPolyLine() does accomplish what I am attempting to do--the problem there is that it is inflexible and I cannot parse the code (more importantly, it doesn't appear to properly draw acute angles--correct me if I am wrong). If anyone can provide any insight, please post. For now, the above is my answer.
I code this, I think it may help:
SWF: http://dl.dropbox.com/u/2283327/stackoverflow/SplineTest.swf
Code: http://dl.dropbox.com/u/2283327/stackoverflow/SplineTest.as
I left a lot of comments on the code. I wish it helps!
Here is the theory behind the code:
A and C are the first and last point, B is the "control point" in AS3 you can draw the curve like this:
graphics.moveTo(A.x, A.y);
graphics.curveTo(B.x, B.y, C.x, C.y);
Now, D is the mid-point of the vector AC. And the mid-point of DB is the mid-point of the curve. Now what I did in the code was to move B exactly to D+DB*2 so, if you draw the curve using that point as control point, the mid-point of the curve will be B.
PS: Sorry for my poor Enlgish

Formula for the max number of paths through a grid?

Given a grid of open spots, and a certain number of tiles to place in those spots, what function f(openSpots, tilesToPlace) will give you the number of continuous paths you can form?
Continuous paths are placements of the tiles such that each tile shares an edge with another. (Only corners touching is not good enough. So (0, 1) and (0, 0) are legal, but (1, 1) and (2, 2) is not.)
I already have a function that will find all these paths. However, it only works for small numbers. For larger values, all I need is a count of how many could possibly exist. Here is some data:
For 1 tiles, there are 1 paths.
For 2 tiles, there are 4 paths.
For 3 tiles, there are 22 paths.
For 4 tiles, there are 89 paths.
For 5 tiles, there are 390 paths.
For 6 tiles, there are 1476 paths.
For 7 tiles, there are 5616 paths.
For 8 tiles, there are 19734 paths.
For 9 tiles, there are 69555 paths.
This gets really slow to calculate as the puzzle size increases. I think the asymptotic complexity of my path finding solution is pretty bad.
If there are n tiles, the grid is at most n spots long and wide.
Your problem seems to be at least as difficult as enumerating polyominoes. There are no known fast algorithms for doing this, and the best known algorithms struggle after n=50. I doubt there is a fast way to solve this problem.
I'm not even going to pretend that this is an optimal solution but it might be useful as a reference solution. I think it at least gives the correct answer, although it takes some time. It solves the problem recursively by finding all paths of length n-1, then checking for all possible places it can add one more tile and removing duplicate solutions. It has a particularly ugly part where it checks for duplicate by converting the path to a string and comparing the strings, but it was fast to write.
Here's the output it generates:
n = 1, number of paths found = 1
n = 2, number of paths found = 4
n = 3, number of paths found = 22
n = 4, number of paths found = 113
n = 5, number of paths found = 571
n = 6, number of paths found = 2816
n = 7, number of paths found = 13616
n = 8, number of paths found = 64678
n = 9, number of paths found = 302574
And here's the code:
using System;
using System.Collections.Generic;
using System.Linq;
public struct Tile
{
public Tile(int x, int y) { X = x; Y = y; }
public readonly int X;
public readonly int Y;
public IEnumerable<Tile> GetNeighbours(int gridSize)
{
if (X > 0)
yield return new Tile(X - 1, Y);
if (X < gridSize - 1)
yield return new Tile(X + 1, Y);
if (Y > 0)
yield return new Tile(X, Y - 1);
if (Y < gridSize - 1)
yield return new Tile(X, Y + 1);
}
public override string ToString()
{
return string.Format("({0},{1})", X, Y);
}
}
public class Path
{
public Path(Tile[] tiles) { Tiles = tiles; }
public Tile[] Tiles { get; private set; }
public override string ToString()
{
return string.Join("", Tiles.Select(tile => tile.ToString()).ToArray());
}
}
public class PathFinder
{
public IEnumerable<Path> FindPaths(int n, int gridSize)
{
if (n == 1)
{
for (int x = 0; x < gridSize; ++x)
for (int y = 0; y < gridSize; ++y)
yield return new Path(new Tile[] { new Tile(x, y) });
}
else
{
Dictionary<string, object> pathsSeen = new Dictionary<string, object>();
foreach (Path shortPath in FindPaths(n - 1, gridSize))
{
foreach (Tile tile in shortPath.Tiles)
{
foreach (Tile neighbour in tile.GetNeighbours(gridSize))
{
// Ignore tiles that are already included in the path.
if (shortPath.Tiles.Contains(neighbour))
continue;
Path newPath = new Path(shortPath.Tiles
.Concat(new Tile[] { neighbour })
.OrderBy(t => t.X)
.ThenBy(t => t.Y)
.ToArray());
string pathKey = newPath.ToString();
if (!pathsSeen.ContainsKey(pathKey))
{
pathsSeen[pathKey] = null;
yield return newPath;
}
}
}
}
}
}
static void Main()
{
PathFinder pathFinder = new PathFinder();
for (int n = 1; n <= 9; ++n)
{
List<Path> paths = pathFinder.FindPaths(n, n).ToList();
Console.WriteLine("n = {0}, number of paths found = {1}", n, paths.Count);
//foreach (Path path in paths)
// Console.WriteLine(path.ToString());
}
}
}

Resources