JavaFX 2 Slider for circular knob and snap to value - slider

based on the volume knob from this example, http://fxexperience.com/2012/01/fun-javafx-2-0-audio-player/
How would you make a volume knob that snaps to particular angle value, every 15 or 30 degree for example ? Thanks for any hint.
What i did is redefine rotateKnob in subclass and set the angle in this manner for the slider to snap at values 180, 120, 60, 0, -180, -160, -120, -60 :
Slider s = getSkinnable();
double zeroOneValue = (s.getValue() - s.getMin()) / (s.getMax() - s.getMin());
double angle = minAngle + ((maxAngle - minAngle) * zeroOneValue);
int angleMod = (int) (angle % 60);
double val;
if (angleMod == 0) {
val = (int) angle;
} else if (angleMod > (60 / 2)) {
val = (int) angle - angleMod;
} else {
val = (int) angle - angleMod + 60;
}

You don't need to do anything special. Just make use of the "snapToTicks" property. In the main class, after the volume knob is defined, put this code:
volumeKnob.setMinorTickCount(0);
volumeKnob.setMajorTickUnit(1.0/14.0);
volumeKnob.snapToTicksProperty().set(true);
The numbers here are based on the fact that there are 14 "dots" around the volume knob. This code will make sure that you can only set the volume to one of those dots.

#Enwired has a good answer. you need a different graphical resource to change the number of dots. but that is not a part of what makes the angle between each tick around the rotary control.
just as he divided the circle into 1/14's, you can choose to divide it into 60 degree slices by 60.0/360.0 or 1.0/6.0.

Related

How to Set Wheelnav.js Spin Direction Always the Shorted Distance?

How can wheelnav.js be setup to rotate both clockwise and counterclockwise, whichever is closest? Whenever it goes from 0 to the highest number, it flips around the long way, as seen when you go from 0 to 5 on this demo page after turning on rotation:
http://pmg.softwaretailoring.net/
It needs to always rotate in the closest direction and never flip around the long way.
I am also open to a solution using wheelizate tabs, which uses Wheelnav to accomplish this:
http://wtabs.softwaretailoring.net/
Thank you for your attention on this.
Found the solution by doing a Console.log(rotationAngle), and watching what the rotation value for each particular click. Then I found that I could take the numbers of the situations that went the wrong way, and subtract 360 from it to get it to rotate the opposite direction.
The patch involve adding this to Wheelnav.js line 411:  
if (rotationAngle == 288){
rotationAngle = -72;
}
if (rotationAngle == 216){
rotationAngle = -144;
}
You need to replace the next line (line 422 of Wheelnav.js):
navItem.currentRotateAngle -= rotationAngle;
with the following conditional expression:
if (rotationAngle >= 180) {
rotationAngle = 360 - rotationAngle;
navItem.currentRotateAngle += rotationAngle;
} else if (Math.abs(rotationAngle) > 180) {
rotationAngle = 360 + rotationAngle;
navItem.currentRotateAngle -= rotationAngle;
} else {
navItem.currentRotateAngle -= rotationAngle;
}

Controlling physicsBody's velocities from exceeding certain values

I have 3 NPC's that each have their own circular physicsBody meant to mimic bouncy balls in zero gravity - more precisely, bouncy balls that never stop moving due to restitution = 1.0; (bounciness 100%) friction = 0.0; (No friction) linearDampening = 0.0; (Collisions don't impact the NPC). If either of these NPC's collide/contact with another one, then their velocity increases. They start moving so fast that it begins to force the levels boundaries/physicsBodies (which are supposedly non-dynamic) actually shift/move from the impact. Below are the if statements I wrote in the update method to keep these 3 NPC's physicsBody.velocities under control. Could someone take a look and tell me if there's a better way of doing this? I basically want to have limited maximum velocities/moving speeds for all 3 NPC's at all times that they can't exceed.
-(void)update:(CFTimeInterval)currentTime
{
/* Called before each frame is rendered */
//Track ball velocities.
if (_npcRed.physicsBody.velocity.dx > 1000 || _npcRed.physicsBody.velocity.dy > 1000)
{
NSLog(#"RED's moving too fast; lowering velocity");
_npcRed.physicsBody.velocity = CGVectorMake(500, 500);
}
else
{
NSLog(#"RED's at stable speed");
}
if (_npcBlue.physicsBody.velocity.dx > 1000 || _npcBlue.physicsBody.velocity.dy > 1000)
{
NSLog(#"BLUE's moving too fast; lowering velocity");
_npcBlue.physicsBody.velocity = CGVectorMake(500, 500);
}
else
{
NSLog(#"BLUE's at stable speed");
}
if (_npcGreen.physicsBody.velocity.dx > 1000 || _npcGreen.physicsBody.velocity.dy > 1000)
{
NSLog(#"GREEN's moving too fast; lowering velocity");
_npcGreen.physicsBody.velocity = CGVectorMake(500, 500);
}
else
{
NSLog(#"GREEN's at stable speed");
}
}
What you are doing works fine except for two things. Your if statement
if (_npcBlue.physicsBody.velocity.dx > 1000 || _npcBlue.physicsBody.velocity.dy > 1000)
_npcBlue.physicsBody.velocity = CGVectorMake(500, 500);
is setup to fire if either the dx or dy velocity is greater than 1000. What if npcBlue has a dx velocity of 100 and a dy velocity of 1010. npcBlue's dx velocity jumps from 100 to 500. Not so good.
Second, why throttle at 1000 for dx and dy and then set to 500? That would make movement very jerky. Why not check if dx or dy is greater than 500 and if yes, set to 500?

Center coordinate of a coordinate region

I have 2 coordinates, a top left and a bottom right. I would like to find the center point of the region. Right now I have the following method to calculate it. The center point is way off. When I call the method with
[self.map setRegionTopLeft: CLLocationCoordinate2DMake(21.57524, -157.984514)
bottomRight: CLLocationCoordinate2DMake(21.309766, -157.80766)
animated:YES];
It should center on the island of Oahu in the State of Hawaii, USA. I found this math here so I'm not sure whats going on.
Code A - This is way off. It's not putting me anywhere near the island.
- (CLLocationCoordinate2D)centerPointFromRegionTopLeft:(CLLocationCoordinate2D)topLeft
bottomRight:(CLLocationCoordinate2D)bottomRight
{
CLLocationCoordinate2D centerPoint;
centerPoint.longitude = (topLeft.longitude + bottomRight.longitude) / 2;
if (fabs(bottomRight.longitude - topLeft.longitude) > 180)
{
if (centerPoint.longitude > 0)
{
centerPoint.longitude = centerPoint.longitude + 180;
} else {
centerPoint.longitude = centerPoint.longitude - 180;
}
}
centerPoint.latitude = asin((sin(bottomRight.latitude) + sin(topLeft.latitude))/2);
return centerPoint;
}
I've also, originally, tried this method. Its just what popped in my head when I thought center of a rectangle. If gets me a lot closer to what the center should be - I can see the island - but its still off.
Code B - Original code I tried. This is much closer to what I expected but still off.
- (CLLocationCoordinate2D)centerPointFromRegionTopLeft:(CLLocationCoordinate2D)topLeft
bottomRight:(CLLocationCoordinate2D)bottomRight
{
CLLocationCoordinate2D centerPoint;
centerPoint.latitude = ((topLeft.latitude + bottomRight.latitude) / 2);
centerPoint.longitude = ((topLeft.longitude + bottomRight.longitude) / 2);
return centerPoint;
}
So given a coordinate region (topLeft, bottomRight) how to I get the center coordinate? The idea is I should be able to give any 2 coordinates and get the center coordinate.
Update* Code B works. I had my topLeft and bottomRight wrong. Code A puts me very south and a little east of where it should.
You need the middle of L(longitude) and B(latitude). For B the problem is around the pole, but as you set it, you simply can't "put the cap on the pole", so, really no problems here.
Middle(B1,B2)=(B1+B2)/2.
But L is much worse. L can jump from -179 to -179. And another problem : the middle of (-179,+179) should be 180, and middle(-1,+1) should be 0. I.e., we should always choose middle along shorter way between opposite points, not around the whole Earth.
We should move the zero meridian so, that the difference between L1,L2 will be smaller, than 180, make normal middle of them and then return the zero meridian back.
Let L1
if L2-L1>180, let's choose L2 for the new zero meridian.
shift=L2
L2=L2-shift, L1=L1+360-shift. Now, notice, L1-L2<180!
LmShifted=(L1+L2)/2
Lm=LmShifted+shift.
If we'll take these formulas together, we'll have:
Lm=(L1-L2+360)/2+L2
if L2-L1<180, Lm=(L1+L2)/2
The problem is when L2-L1=180. In this case you have two opposite meridians, dividing the Earth in two, and for the role of the middle both "quarter" meridian, to the right and to the left, fit. It's up to you, what to choose.

Finding point is close to line and between the endpoints of the line

To find if the point is on a specified line containing two points i do the following checks:
-(Boolean)isOnLine:(Line*) line point:(CGPoint) point{
//If between two dots:
if (((line.first.x <= point.x && point.x <= line.last.x)||(line.first.x >= point.x && point.x >= line.last.x))&&((line.first.y<=point.y && point.y<= line.last.y)||(line.first.y>=point.y && point.y>=line.last.y)) ) {
//Calculate distance:
double dist = (((double)point.y - line.first.y)) / (0.00001+((double)(point.x - line.first.x)))- ((double)(line.last.y - line.first.y)) / (0.00001+((double)(line.last.x - line.first.x)));
NSLog(#"Dist to line: %f", fabs(dist));
return fabs(dist) <0.5;
}else
return NO;
}
}
Somehow, however, the function is not working with vertical lines. My guess is the if clause is invalid in some sense.
I haven't read your code carefully so I'm not entirely sure what you're doing, but fyi the easiest way to do this operation is find the distance of one end of the line to the point, find the distance of the other end of the line to the point, and then add those distances and compare to the length of the line.
Something like:
Boolean isOnLine(line, point) {
var dist1 = dist(line.first, point)
var dist2 = dist(line.last, point)
return abs(line.length - (dist1 + dist2)) < .5
}
For the dist() function I'm guessing CoreGraphics provides that, but if not it's just basic trigonometry.
Here's my implementation of jhockings' solution
return abs([line length] -
(sqrt((line.first.x - point.x)*(line.first.x - point.x)
+ (line.first.y - point.y)*(line.first.y - point.y))
+ sqrt((line.last.x - point.x)*(line.last.x - point.x)
+ (line.last.y - point.y)*(line.last.y - point.y)))) < .5;
Another(my) implementation of #jhocking solution:
- (BOOL)isPoint:(CGPoint)origin nearToLineSegmentPointA:(CGPoint)pointA pointB:(CGPoint)pointB withMarginOfError:(CGFloat)marginOfError {
CGFloat distanceAP = [self distanceBetweenPointA:origin pointB:pointA];
CGFloat distanceBP = [self distanceBetweenPointA:origin pointB:pointB];
CGFloat distanceAB = [self distanceBetweenPointA:pointA pointB:pointB];
if (fabsf(distanceAB - distanceAP - distanceBP) < marginOfError) {
return YES;
} else {
return NO;
}
}
- (CGFloat)distanceBetweenPointA:(CGPoint)pointA pointB:(CGPoint)pointB {
return sqrtf(powf((pointA.x - pointB.x), 2.f) + powf((pointA.y - pointB.y), 2.f));
}
The explanation of why it is not working is you are comparing the tangent of the angles of two triangles - you are not calculating distance at all despite the comments and variable name.
Now as the angle approaches 90 deg the magnitude of the tangent increases rapidly until it reaches infinity at 90 degrees itself. At 90 degrees the difference of the x coordinates is zero and you would end up with a divide-by-zero error where it not for adding in the 0.00001 constant to avoid it. While the relative difference between two tangents near 90 might be small the absolute difference can be huge even for very close angles, so your < 0.5 test fails.
So you need another approach. One is to calculate the distances from the point to the two end points, and the length of the line itself, and compare - if the sum of the two distances from the point is larger than the length of the line the three points form a triangle, if it isn't they are co-linear. (And if the sum is less you've slipped into an alternate dimension...).
You can calculate the length of the lines using Pythagorus: sqrt((x1 - x2)^2 + (y1 - y2)^2).

How to program smooth movement with the accelerometer like a labyrinth game on iPhone OS?

I want to be able to make image move realistically with the accelerometer controlling it, like any labyrinth game. Below shows what I have so far but it seems very jittery and isnt realistic at all. The ball images seems to never be able to stop and does lots of jittery movements around everywhere.
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
deviceTilt.x = 0.01 * deviceTilt.x + (1.0 - 0.01) * acceleration.x;
deviceTilt.y = 0.01 * deviceTilt.y + (1.0 - 0.01) * acceleration.y;
}
-(void)onTimer {
ballImage.center = CGPointMake(ballImage.center.x + (deviceTilt.x * 50), ballImage.center.y + (deviceTilt.y * 50));
if (ballImage.center.x > 279) {
ballImage.center = CGPointMake(279, ballImage.center.y);
}
if (ballImage.center.x < 42) {
ballImage.center = CGPointMake(42, ballImage.center.y);
}
if (ballImage.center.y > 419) {
ballImage.center = CGPointMake(ballImage.center.x, 419);
}
if (ballImage.center.y < 181) {
ballImage.center = CGPointMake(ballImage.center.x, 181);
}
Is there some reason why you can not use the smoothing filter provided in response to your previous question: How do you use a moving average to filter out accelerometer values in iPhone OS ?
You need to calculate the running average of the values. To do this you need to store the last n values in an array, and then push and pop values off the array when ever you read the accelerometer data. Here is some pseudocode:
const SIZE = 10;
float[] xVals = new float[SIZE];
float xAvg = 0;
function runAverage(float newX){
xAvg += newX/SIZE;
xVals.push(newX);
if(xVals.length > SIZE){
xAvg -= xVals.pop()/SIZE;
}
}
You need to do this for all three axis. Play around with the value of SIZE; the larger it is, the smoother the value, but the slower things will seem to respond. It really depends on how often you read the accelerometer value. If it is read 10 times per second, then SIZE = 10 might be too large.