find tangent line of two adjacent circle - math

those are 2 example cases of what I need to solve, it is just finding the coordinate of D, given position of A, and the direction vector of red and green line
red/green line vector (or direction) is known
point A is an intersection between the red line and red circle tangent point
point B is the center of the red circle with radius = R (known)
point C is an intersection between the green line and the green circle tangent point
point D is unknown and this one that needs to be calculated
point D will always located in green circle (radius of 2R from point B)
both red and green line has the same radius of R
V is the angle of the red line relative to north up
W is the angle of the green line relative to north up
the distance between point B and D is always 2R since the circle adjacent (touching each other)
much help and hint appreciated, preferred in some code instead of math equation

Having coordinates A,B,C, we can write two vector equations using scalar (dot) product:
AC.dot.DC = 0
DB.dot.DB = 4*R^2
The first one refers to perpendicularity between tangent to circle and radius to tangency point, the second one - just squared distance between circle centers.
In coordinates:
(cx-ax)*(cx-dx) + (cy-ay)*(cy-dy) = 0
(bx-dx)*(bx-dx) + (by-dy)*(by-dy) = 4*R^2
Solve this system for unknown dx, dy - two solutions in general case.
If A and C are not known, as #Mike 'Pomax' Kamermans noticed:
Let
cr = sin(v) sr = cos(v)
cg = sin(w) sg = cos(w)
So
ax = bx + R * cr
ay = by + R * sr
and
dx = cx - R * cg
dy = cy + R * sg
Substituting expressions into the system above we have:
(dx+R*cg-bx-R*cr)*cg - (dy-R*sg-by-R*sr)*sg = 0
(bx-dx)*(bx-dx) + (by-dy)*(by-dy) = 4*R^2
Again - solve system for unknowns dx, dy

As a hint: draw it out some more:
We can construct D by constructing the line segment AG, for which we know both the angle and length, because AC⟂AG, and the segment has length R.
We can then construct a line perpendicular to AG, through G, which gives us a chord on the blue circle, one endpoint of which is D. We know the distance from B to GD (because we know trigonometry) and we know that the distance BD is 2R (because that's a given). Pythagoras then trivially gives us D.

You know where A is and the angle θ it makes from vertical.
So specify the line though C called line(C) above and the offset the line by R in order to get line(D) above that goes through point D.
In C# code this is
Line line_C = Line.ThroughPointAtAngle(A, theta);
Line line_D = line_C.Offset(radius);
Now find the intersection of this line to the greater circle
Circle circle = new Circle(B, 2 * radius);
if (circle.Intersect(line_D, out Point D, alternate: false))
{
Console.WriteLine(D);
float d_BD = B.DistanceTo(D);
Console.WriteLine(d_BD);
}
else
{
Console.WriteLine("Does not intersect.");
}
This produces point D either above line(C) or below line(C) depending on the bool argument alternate.
The code example below produces the following output:
D=Point(-0.4846499,-1.94039)
|BD|=2
The source code is
Program.cs
using static Float;
static class Program
{
static void Main(string[] args)
{
float radius = 1;
Point A = new Point(-radius, 0);
Point B = new Point(0, 0);
float theta = deg(15);
Line line_C = Line.ThroughPointAtAngle(A, theta);
Line line_D = line_C.Offset(radius);
Circle circle = new Circle(B, 2 * radius);
if (circle.Intersect(line_D, out Point D, alternate: false))
{
Console.WriteLine($"D={D}");
float d_BD = B.DistanceTo(D);
Console.WriteLine($"|BD|={d_BD}");
}
else
{
Console.WriteLine("Does not intersect.");
}
}
}
Point.cs
Describes a point in cartesian space using two coordinates (x,y)
using static Float;
public readonly struct Point
{
readonly (float x, float y) data;
public Point(float x, float y)
{
this.data = (x, y);
}
public static Point Origin { get; } = new Point(0, 0);
public static Point FromTwoLines(Line line1, Line line2)
{
float x = line1.B * line2.C - line1.C * line2.B;
float y = line1.C * line2.A - line1.A * line2.C;
float w = line1.A * line2.B - line1.B * line2.A;
return new Point(x / w, y / w);
}
public float X => data.x;
public float Y => data.y;
public float SumSquares => data.x * data.x + data.y * data.y;
#region Algebra
public static Point Negate(Point a)
=> new Point(
-a.data.x,
-a.data.y);
public static Point Scale(float factor, Point a)
=> new Point(
factor * a.data.x,
factor * a.data.y);
public static Point Add(Point a, Point b)
=> new Point(
a.data.x + b.data.x,
a.data.y + b.data.y);
public static Point Subtract(Point a, Point b)
=> new Point(
a.data.x - b.data.x,
a.data.y - b.data.y);
public static float Dot(Point point, Line line)
=> line.A * point.data.x + line.B * point.data.y + line.C;
public static Point operator +(Point a, Point b) => Add(a, b);
public static Point operator -(Point a) => Negate(a);
public static Point operator -(Point a, Point b) => Subtract(a, b);
public static Point operator *(float f, Point a) => Scale(f, a);
public static Point operator *(Point a, float f) => Scale(f, a);
public static Point operator /(Point a, float d) => Scale(1 / d, a);
#endregion
#region Geometry
public Point Offset(float dx, float dy)
=> new Point(data.x + dx, data.y + dy);
public Point Offset(Vector2 delta) => Offset(delta.X, delta.Y);
public float DistanceTo(Point point)
=> sqrt(sqr(data.x - point.data.x) + sqr(data.y - point.data.y));
#endregion
#region Formatting
public string ToString(string formatting, IFormatProvider provider)
{
return $"Point({data.x.ToString(formatting, provider)},{data.y.ToString(formatting, provider)})";
}
public string ToString(string formatting)
=> ToString(formatting, null);
public override string ToString()
=> ToString("g");
#endregion
}
Line.cs
Describes a line in cartesian space using the coefficients (a,b,c) such that the equation of the line is a x + b y + c = 0
using static Float;
public readonly struct Line
{
readonly (float a, float b, float c) data;
public Line(float a, float b, float c) : this()
{
data = (a, b, c);
}
public static Line AlongX { get; } = new Line(0, 1, 0);
public static Line AlongY { get; } = new Line(-1, 0, 0);
public static Line ThroughPointAtAngle(Point point, float angle)
{
return new Line(cos(angle), -sin(angle), point.Y * sin(angle) - point.X * cos(angle));
}
public static Line ThroughTwoPoints(Point point1, Point point2)
=> new Line(
point1.Y - point2.Y,
point2.X - point1.X,
point1.X * point2.Y - point1.Y * point2.X);
public float A => data.a;
public float B => data.b;
public float C => data.c;
#region Algebra
public static float Dot(Line line, Point point)
=> line.data.a * point.X + line.data.b * point.Y + line.data.c;
#endregion
#region Geometry
public Line ParallelThrough(Point point)
{
return new Line(data.a, data.b, -data.a * point.X - data.b * point.Y);
}
public Line PerpendicularThrough(Point point)
{
return new Line(data.b, -data.a, -data.b * point.X + data.a * point.Y);
}
public Line Offset(float amount)
=> new Line(data.a, data.b, data.c - amount * sqrt(sqr(data.a) + sqr(data.b)));
public Line Offset(float dx, float dy)
=> new Line(data.a, data.b, data.c + data.a * dx + data.b * dy);
public Line Offset(Vector2 delta) => Offset(delta.X, delta.Y);
public float DistanceTo(Point point)
=> Dot(this, point) / (data.a * data.a + data.b * data.b);
#endregion
#region Formatting
public string ToString(string formatting, IFormatProvider provider)
{
return $"Line({data.a.ToString(formatting, provider)}x+{data.b.ToString(formatting, provider)}y+{data.c.ToString(formatting, provider)}=0)";
}
public string ToString(string formatting)
=> ToString(formatting, null);
public override string ToString()
=> ToString("g");
#endregion
}
Circle.cs
Describes a circle using the center and radius.
using static Float;
public readonly struct Circle
{
readonly (Point center, float radius) data;
public Circle(Point center, float radius)
{
this.data = (center, radius);
}
public static Circle FromTwoPoints(Point point1, Point point2)
{
float radius = point1.DistanceTo(point2) / 2;
Point center = (point1 + point2) / 2;
return new Circle(center, radius);
}
public static Circle FromThreePoints(Point point1, Point point2, Point point3)
{
float k_1 = point1.SumSquares / 2;
float k_2 = point2.SumSquares / 2;
float k_3 = point3.SumSquares / 2;
float dx_12 = point2.X - point1.X;
float dy_12 = point2.Y - point1.Y;
float dx_23 = point3.X - point2.X;
float dy_23 = point3.Y - point2.Y;
float det = dx_12 * dy_23 - dx_23 * dy_12;
Point center = new Point(
(dy_12 * (k_2 - k_3) + dy_23 * (k_2 - k_1)) / det,
(dx_12 * (k_3 - k_2) + dx_23 * (k_1 - k_2)) / det);
float radius = center.DistanceTo(point1);
return new Circle(center, radius);
}
public Point Center => data.center;
public float Radius => data.radius;
#region Geometry
public float DistanceTo(Point point)
=> data.center.DistanceTo(point) - data.radius;
public float DistanceTo(Line line)
{
float d = line.DistanceTo(Center);
if (d > 0)
{
return d - data.radius;
}
else
{
return d + data.radius;
}
}
public bool Intersect(Line line, out Point point, bool alternate = false)
{
line = line.Offset(-Center.X, -Center.Y);
int sign = alternate ? -1 : 1;
float discr = sqr(line.A * data.radius) + sqr(line.B * data.radius) - sqr(line.C);
if (discr >= 0)
{
float d = sign * sqrt(discr);
float ab = line.A * line.A + line.B * line.B;
point = new Point((line.B * d - line.A * line.C) / ab, -(line.A * d + line.B * line.C) / ab);
point += Center;
return true;
}
else
{
float ab = line.A * line.A + line.B * line.B;
point = new Point((-line.A * line.C) / ab, -(+line.B * line.C) / ab);
point += Center;
return false;
}
}
#endregion
#region Formatting
public string ToString(string formatting, IFormatProvider provider)
{
return $"Circle({data.center.ToString(formatting, provider)},{data.radius.ToString(formatting, provider)})";
}
public string ToString(string formatting)
=> ToString(formatting, null);
public override string ToString()
=> ToString("g");
#endregion
}
Float.cs
Helper functions dealing with float math which is lacking from System.Math.
public static class Float
{
/// <summary>
/// A factor of π.
/// </summary>
/// <param name="x">The factor.</param>
public static float pi(float x) => (float)(Math.PI * x);
/// <summary>
/// Degree to Radian conversion
/// </summary>
/// <param name="x">The angle in degrees.</param>
/// <returns>Angle in radians</returns>
public static float deg(float x) => pi(x) / 180;
/// <summary>
/// Radian to Degree conversion
/// </summary>
/// <param name="x">The angle in radians.</param>
/// <returns>Angle in degrees</returns>
public static float rad(float x) => x * 180 / pi(1);
public static float sqr(float x) => x * x;
public static float sqrt(float x) => (float)Math.Sqrt(x);
public static float sin(float x) => (float)Math.Sin(x);
public static float cos(float x) => (float)Math.Cos(x);
}

thx for all the answer (will upvote them), I did the work on it myself, I will share my result :
and here is length of d, to solve the equation :

Related

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);
}
}

javafx 3d without scene graph

I have written my own routines for manipulating vectors and matrices including a number of math utilities.
I'd far rather use JavaFX without its scene graph and use my own routines, is it possible to manipulate just a single (4x4) matrix for each Shape3D you wish to render?
In order to Transform a Node directly rather than making it a child of a scene graph of transforms (for example and X,Y,Z axis rotation and a translation etc) you can use a single Affine matrix to do all the transformations (orientation/translation)
For example parent a Shape3D to this class then parent the Pivot to the root of the scene
package uk.co.bedroomcoders.jfx3dedit;
import javafx.scene.Group;
import javafx.scene.transform.Affine;
import javafx.scene.transform.MatrixType;
import com.sun.javafx.geom.Vec3d;
public class Pivot extends Group {
private final double[] idt={1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1};
public double rx,ry,rz;
public Affine matrix=new Affine(idt,MatrixType.MT_3D_4x4,0);
public Pivot() {
super();
getTransforms().setAll(matrix);
}
public Pivot(double x, double y, double z) {
this();
matrix.setTx(x);
matrix.setTy(y);
matrix.setTz(z);
}
public void setPosition(double x, double y, double z) {
matrix.setTx(x);
matrix.setTy(y);
matrix.setTz(z);
}
public Vec3d getPosition() {
return new Vec3d(matrix.getTx(),matrix.getTy(),matrix.getTz());
}
// set to eular rotation retaining translation (TODO reimplement fromQuat)
public void updateFromEular(double rx, double ry, double rz) {
double cx = Math.cos(rx);
double cy = Math.cos(ry);
double cz = Math.cos(rz);
double sx = Math.sin(rx);
double sy = Math.sin(ry);
double sz = Math.sin(rz);
matrix.setMxx(cy*cz); matrix.setMxy((sx * sy * cz) + (cx * sz)); matrix.setMxz(-(cx * sy * cz) + (sx * sz));
matrix.setMyx(-cy*sz); matrix.setMyy(-(sx * sy * sz) + (cx * cz)); matrix.setMyz((cx * sy * sz) + (sx * cz));
matrix.setMzx(sy); matrix.setMzy(-sx*cy); matrix.setMzz(cx*cy);
}
// make this pivot face the a point
public void lookAt(Vec3d centre, Vec3d up) {
final Vec3d f = new Vec3d(), s = new Vec3d(), u = new Vec3d();
final Vec3d t = new Vec3d(), eye = new Vec3d();
eye.set(matrix.getTx(),matrix.getTy(),matrix.getTz());
f.set(centre);
f.sub(eye);
f.normalize();
up.normalize();
t.set(f);
s.cross(t,up);
s.normalize();
t.set(s);
u.cross(t,f);
u.normalize();
matrix.setMxx( s.x); matrix.setMxy( u.x); matrix.setMxz( f.x);
matrix.setMyx( s.y); matrix.setMyy( u.y); matrix.setMyz( f.y);
matrix.setMzx( s.z); matrix.setMzy( u.z); matrix.setMzz( f.z);
}
}

XNA - Bounding Box Rotation Nightmare

I'm currently going throught kind of a nightmare right now by trying to find the right formula to obtain a bounding box that correspond to my sprite orientation.
I KNOW ! There is a bunch of examples, solution, explanations on the internet, including here, on this site. But trust me, I've tried them all. I tried to just apply solutions, I tried to understand explanations, but every post gives a different solution and none of them work.
I'm obviously missing something important here...
So, basically, I have a sprite which texture is natively (20 width * 40 height) and located at (200,200) when starting the app. The sprite origin is a classic
_origin = new Vector2((float)_texture.Width / 2, (float)_texture.Height / 2);
So origin would return a (5.5;8) vector 2
By keyboard input, I can rotate this sprite. Default rotation is 0 or Key.Up. Then rotation 90 corresponds to Key.Right, 180 to Key.Down, and so on...
For the moment, there is no move involved, just rotation.
So here is my code to calculate the bounding rectangle:
public partial class Character : ICollide
{
private const int InternalRunSpeedBonus = 80;
private const int InternalSpeed = 80;
private Vector2 _origin;
private Texture2D _texture;
private Texture2D _axisBase;
private Texture2D _axisOrig;
public Character()
{
MoveData = new MoveWrapper { Rotation = 0f, Position = new Vector2(200, 200), Speed = new Vector2(InternalSpeed) };
}
public MoveWrapper MoveData { get; set; }
#region ICollide Members
public Rectangle Bounds
{
get { return MoveData.Bounds; }
}
public Texture2D Texture
{
get { return _texture; }
}
#endregion ICollide Members
public void Draw(SpriteBatch theSpriteBatch)
{
theSpriteBatch.Draw(_texture, MoveData.Position, null, Color.White, MoveData.Rotation, _origin, 1f, SpriteEffects.None, 0);//main sprite
theSpriteBatch.Draw(_axisOrig, MoveData.Position, null, Color.White, 0f, _origin, 1f, SpriteEffects.None, 0);//green
theSpriteBatch.Draw(_axisBase, MoveData.Position, null, Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0);//red
}
public void Load(ContentManager theContentManager)
{
_texture = theContentManager.Load<Texture2D>("man");
_axisBase = theContentManager.Load<Texture2D>("axis");
_axisOrig = theContentManager.Load<Texture2D>("axisOrig");
_origin = new Vector2((float)_texture.Width / 2, (float)_texture.Height / 2);
}
public void MoveForward(GameTime theGameTime, KeyboardState aCurrentKeyboardState)
{
InternalMove(theGameTime, aCurrentKeyboardState);
}
private void InternalMove(GameTime theGameTime, KeyboardState aCurrentKeyboardState, bool forward = true)
{
//stuff to get the move wrapper data valorized (new position, speed, rotation, etc.)
MoveWrapper pm = MovementsHelper.Move(MoveData.Position, MoveData.Rotation, aCurrentKeyboardState, InternalSpeed,
InternalRunSpeedBonus, theGameTime, forward);
pm.Bounds = GetBounds(pm);
MoveData = pm;
}
public void MoveBackward(GameTime theGameTime, KeyboardState aCurrentKeyboardState)
{
InternalMove(theGameTime, aCurrentKeyboardState, false);
}
private Rectangle GetBounds(MoveWrapper pm)
{
return GetBoundingBox(pm, _texture.Width, _texture.Height);
}
public Rectangle GetBoundingBox(MoveWrapper w, int tWidth, int tHeight)
{
//1) get original bounding vectors
//upper left => same as position
Vector2 p1 = w.Position;
//upper right x = x0+width, y = same as position
Vector2 p2 = new Vector2(w.Position.X + tWidth, w.Position.Y);
//lower right x = x0+width, y = y0+height
Vector2 p3 = new Vector2(w.Position.X + tWidth, w.Position.Y + tHeight);
//lower left x = same as position,y = y0+height
Vector2 p4 = new Vector2(w.Position.X, w.Position.Y + tHeight);
//2) rotate all points given rotation and origin
Vector2 p1r = RotatePoint(p1, w);
Vector2 p2r = RotatePoint(p2, w);
Vector2 p3r = RotatePoint(p3, w);
Vector2 p4r = RotatePoint(p4, w);
//3) get vector2 bouding rectancle location
var minX = Math.Min(p1r.X, Math.Min(p2r.X, Math.Min(p3r.X, p4r.X)));
var maxX = Math.Max(p1r.X, Math.Max(p2r.X, Math.Max(p3r.X, p4r.X)));
//4) get bounding rectangle width and height
var minY = Math.Min(p1r.Y, Math.Min(p2r.Y, Math.Min(p3r.Y, p4r.Y)));
var maxY = Math.Max(p1r.Y, Math.Max(p2r.Y, Math.Max(p3r.Y, p4r.Y)));
var width = maxX - minX;
var height = maxY - minY;
// --> begin hack to get it work for 0,90,180,270 degrees
var origMod = new Vector2((float)tWidth / 2, (float)tHeight / 2);
var degree = (int)MathHelper.ToDegrees(w.Rotation);
if (degree == 0)
{
minX -= origMod.X;
minY -= origMod.Y;
}
else if (degree == 90)
{
minX += origMod.Y;
minY -= origMod.X;
}
else if (degree == 180)
{
minX += origMod.X;
minY += origMod.Y;
}
else if (degree == 270)
{
minX -= origMod.Y;
minY += origMod.X;
}
// end hack <--
return new Rectangle((int)minX, (int)minY, (int)width, (int)height);
}
public Vector2 RotatePoint(Vector2 p, MoveWrapper a)
{
var m = Matrix.CreateRotationZ(a.Rotation);
var refToWorldOrig = p - a.Position;
Vector2 rotatedVector = Vector2.Transform(refToWorldOrig, m);
var backToSpriteOrig = rotatedVector + a.Position;
return backToSpriteOrig;
//does not work
//var Origin = new Vector3(_origin, 0);
//var Position = new Vector3(p, 0);
//var m = Matrix.CreateTranslation(-Origin)
// * Matrix.CreateRotationZ(a.Rotation)
// * Matrix.CreateTranslation(Position);
//return Vector2.Transform(p, m);
}
}
The rotation paramter is MathHelper degree to radians result.
I have a function to draw a rectangle corresponding to the bounding box and I expect that bounding box to overlap exactly with my sprite, at least for 0,90,180 and 270 degrees angle rotations.
Instead I have strange coordinates after rotation calculation:
- when rotation to 90°, bounding box X is negative (so the box is not visible)
- when rotation to 180°, bounding box X and Y are negative (so the box is not visible)
- when rotation to 270°, bounding box Y is negative (so the box is not visible)
Can someone explain to me what I'm doing wrong, and do it like is was explaining to 3 year old child, because regarding Maths, this is what I am !!!
:)
EDIT : I have found a hack to make it work for 0, 90, 180, 270 degrees but now i'm stuck for intermediate positions (45,135,215, 325 degrees) which make me thinks that THERE MUST BE a way to compute all that stuff in one single formula that would work for any angle...
Finally found the way to make it work without the hack !!!!!!!!!!!!!!!!
public Vector2 RotatePoint(Vector2 p, MoveWrapper a)
{
var wm = Matrix.CreateTranslation(-a.Position.X - _origin.X, -a.Position.Y - _origin.Y, 0)//set the reference point to world reference taking origin into account
* Matrix.CreateRotationZ(a.Rotation) //rotate
* Matrix.CreateTranslation(a.Position.X, a.Position.Y, 0); //translate back
var rp = Vector2.Transform(p, wm);
return rp;
}
Bonus effect, this is even more precise (as my drawn guides seems to show) than my previous "hacky" method
I juste realized that this is pretty close as what Blau proposed except that my first translation set the reference back to world 0,0,0 minus the sprite origin. I Guess id did not understand the hint at that time...
You can rotate positions using the matrix struct.
Vector2 p1 = MoveData.Position;
var m = Matrix.CreateRotationZ(angleInRadians);
p1 = Vector2.Transform(p1, m);
if you want to rotate about an origin it should be:
var Origin = new Vector3(Origin2D, 0);
var Position = new Vector3(Position2D, 0);
var m = Matrix.CreateTranslation(-Origin)
* Matrix.CreateRotationZ(angleInRadians)
* Matrix.CreateTranslation(Position);

Is a point between two angles from another point?

I am trying to determine if a point is between two angles coming from an original point (in order to determine whether or not to draw it with OpenGL, although that is irrelevant). What is the easiest way of doing this?
If absolute value of angles CAB + BAD = 45, then point is inside. If CAB + BAD > 45, then point is outside.
The 2-dimensional cross-product of 2 vectors u = (ux, uy), v = (vx, vy), is
u x v = ux * vy - uy * vx = |u| * |v| * sin(phi)
where phi is the angle between u to v (measured from u to v). The cross-product is positive if the angle is between 0 and 180 degrees.
Therefore
(B - A) x (D - A) > 0
if B lies in the half plane "left of" the vector from A to D, and thus
(B - A) x (D - A) > 0 and (B - A) x (C - A) < 0
exactly if B lies in the sector. If you want also to catch the case that B lies on the boundary of the sector, use >= resp. <=.
(Note: This works as long as the angle of the sector at A is less than 180 degrees, and can probably generalized for greater angles. Since your angle is 45 degrees, these formulas can be used.)
If you have point coordinates and no angles then use polar coordinates to convert [X,Y] -> [R,Theta] (Radius and Angle) relative to center (A in your figure) and then compare angles (thetas).
This code converts Point to PolarPoint relative to center point:
/// <summary>
/// Converts Point to polar coordinate point
/// </summary>
public static PolarPoint PointToPolarPoint(Point center, Point point)
{
double dist = Distance(center, point);
double theta = Math.Atan2(point.Y - center.Y, point.X - center.X);
if (theta < 0) // provide 0 - 2Pi "experience"
theta = 2 * Math.PI + theta;
return new PolarPoint(dist, theta);
}
/// <summary>
/// Calculates distance between two points
/// </summary>
public static int Distance(Point p1, Point p2)
{
return (int) Math.Sqrt
(
Math.Pow(p1.X - p2.X, 2) +
Math.Pow(p1.Y - p2.Y, 2)
);
}
The Polar Point Class in C# (That includes conversion back to Point):
/* NFX by ITAdapter
* Originated: 2006.01
* Revision: NFX 0.2 2009.02.10
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
namespace NFX.Geometry
{
/// <summary>
/// Represents a point with polar coordinates
/// </summary>
public struct PolarPoint
{
#region .ctor
/// <summary>
/// Initializes polar coordinates
/// </summary>
public PolarPoint(double r, double theta)
{
m_R = r;
m_Theta = 0;
Theta = theta;
}
/// <summary>
/// Initializes polar coordinates from 2-d cartesian coordinates
/// </summary>
public PolarPoint(Point center, Point point)
{
this = CartesianUtils.PointToPolarPoint(center, point);
}
#endregion
#region Private Fields
private double m_R;
private double m_Theta;
#endregion
#region Properties
/// <summary>
/// R coordinate component which is coordinate distance from point of coordinates origin
/// </summary>
public double R
{
get { return m_R; }
set { m_R = value; }
}
/// <summary>
/// Angular azimuth coordinate component. An angle must be between 0 and 2Pi.
/// Note: Due to screen Y coordinate going from top to bottom (in usual orientation)
/// Theta angle may be reversed, that is - be positive in the lower half coordinate plane.
/// Please refer to:
/// http://en.wikipedia.org/wiki/Polar_coordinates
/// </summary>
public double Theta
{
get { return m_Theta; }
set
{
if ((value < 0) || (value > Math.PI * 2))
throw new NFXException("Invalid polar coordinates angle");
m_Theta = value;
}
}
/// <summary>
/// Returns polar coordinate converted to 2-d cartesian coordinates.
/// Coordinates are relative to 0,0 of the angle base vertex
/// </summary>
public Point Point
{
get
{
int x = (int)(m_R * Math.Cos(m_Theta));
int y = (int)(m_R * Math.Sin(m_Theta));
return new Point(x, y);
}
}
#endregion
#region Operators
public static bool operator ==(PolarPoint left, PolarPoint right)
{
return (left.m_R == right.m_R) && (left.m_Theta == right.m_Theta);
}
public static bool operator !=(PolarPoint left, PolarPoint right)
{
return (left.m_R != right.m_R) || (left.m_Theta != right.m_Theta);
}
#endregion
#region Object overrides
public override bool Equals(object obj)
{
if (obj is PolarPoint)
return this==((PolarPoint)obj);
else
return false;
}
public override int GetHashCode()
{
return m_R.GetHashCode() + m_Theta.GetHashCode();
}
public override string ToString()
{
return string.Format("Distance: {0}; Angle: {1} rad.", m_R, m_Theta);
}
#endregion
}
}
I eventually got it down to this function (where cameraYR is the angle that the point A is rotated at, cameraX is A.x, cameraY is A.y, x is B.x and y is B.y):
float cameraAngle = PI + cameraYR;
float angle = PI / 2 + atan2f(cameraY - y, cameraX - x);
float anglediff = fmodf(angle - cameraAngle + PI, PI * 2) - PI;
return (anglediff <= visibleAngle && anglediff >= -visibleAngle) || (anglediff <= -PI * 2 + visibleAngle && angleDiff >= -PI * 2 - visibleAngle);
I just answered a similar question. Find if an angle is between 2 angles (which also solves your problem). Check below
Is angle in between two angles
Hope it helps

Android - trouble in implementing this user interface

I am trying to implement a UI like this one..
http://www.shrenikvikam.com/wp-content/uploads/2011/04/214e422a43E11S3.png-150x134.png
But i am having some trouble implementing this.. Could someone tell me mistakes in this...
public class Meter extends View{
static final int ORBIT_COLOR = Color.argb(255, 66, 66, 66);
static final double RAD_CIRCLE = 2*Math.PI; // Number radians in a circle
private Paint paint; // Paint object controlling format of screen draws
private ShapeDrawable planet; // Planet symbol
private int planetRadius = 7; // Radius of spherical planet (pixels)
private int sunRadius = 12; // Radius of Sun (pixels)
private float X0 = 0; // X offset from center (pixels)
private float Y0 = 0; // Y offset from center (pixels)
private float X; // Current X position of planet (pixels)
private float Y; // Current Y position of planet (pixels)
private float centerX; // X for center of display (pixels)
private float centerY; // Y for center of display (pixels)
private float R0; // Radius of circular orbit (pixels)
private int nsteps = 120; // Number animation steps around circle
private double theta; // Angle around orbit (radians)
private double dTheta; // Angular increment each step (radians)
private double direction = -1; // Direction: counter-clockwise -1; clockwise +1
private float lastTouchX; // x coordinate of symbol i at last touch
private float lastTouchY; // x coordinate of symbol i at last touch
private int divisions = 120; // Since it requires temperature change from 0 -120
private double oneSegmentLength = (2 * Math.PI * R0)/(double)120;
public Meter(Context context) {
super(context);
// Initialize angle and angle step (in radians)
theta = 30;
//dTheta = RAD_CIRCLE/((double) nsteps); // Angle increment in radians
dTheta = ((360-60)/(double)divisions);
planet = new ShapeDrawable(new OvalShape());
planet.getPaint().setColor(Color.WHITE);
planet.setBounds(0, 0, 2*planetRadius, 2*planetRadius);
paint = new Paint();
paint.setAntiAlias(true);
paint.setTextSize(14);
paint.setStrokeWidth(1);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
// MotionEvent class constant signifying a finger-down event
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
lastTouchX = x;
lastTouchY = y;
newXY();
break;
}
// MotionEvent class constant signifying a finger-drag event
case MotionEvent.ACTION_MOVE: {
float newX = ev.getX();
float newY = ev.getY();
float dx = newX - lastTouchX;
float dy = newY - lastTouchY;
int diff = (int) (Math.abs(ev.getX()) % Math.abs(oneSegmentLength));
if(diff == 0){
if(Math.abs(dx) > Math.abs(dy)) {
if(dx>0) direction = 1;
else direction = -1;
newXY();
} else {
newXY();
}
Log.d("MOVE", "dx ->" + dx + " one seg->" + oneSegmentLength);
invalidate();
}
break;
}
// MotionEvent class constant signifying a finger-up event
case MotionEvent.ACTION_UP: {
Log.d("ACTION MOVE","Value ->");
final float x = ev.getX();
final float y = ev.getY();
lastTouchX = x;
lastTouchY = y;
newXY();
break;
}
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawBackground(paint, canvas);
canvas.save();
canvas.translate(X + X0, Y + Y0);
planet.draw(canvas);
canvas.restore();
}
// Called by onDraw to draw the background
private void drawBackground(Paint paint, Canvas canvas){
paint.setColor(Color.YELLOW);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(centerX + X0, centerY + Y0, sunRadius, paint);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(ORBIT_COLOR);
canvas.drawCircle(centerX + X0, centerY + Y0, R0, paint);
}
//Method to increment angle theta and compute the new X and Y .
private void newXY(){
theta += dTheta;
Log.d("THETA VAL", "->" + theta);
//if(theta > RAD_CIRCLE) theta -= RAD_CIRCLE; // For convenience, keep angle 0-2pi
if(theta > 150)theta = 30;
if(theta > 30 && theta <120){
X = (float)(R0*Math.sin(direction*theta)) + centerX - planetRadius;
Y = centerY - (float)(R0*Math.cos(direction*theta)) - planetRadius;
}
//Log.i("ANIMATOR", "X="+X+" Y="+Y);
}
#Override
protected void onSizeChanged (int w, int h, int oldw, int oldh){
// Coordinates for center of screen
centerX = w/2;
centerY = h/2;
// Make orbital radius a fraction of minimum of width and height of display
R0 = (float) (0.90*Math.min(centerX, centerY));
oneSegmentLength = (2 * Math.PI * R0)/(double)120;
// Set the initial position of the planet (translate by planetRadius so center of planet
// is at this position)
X = centerX - planetRadius ;
Y = centerY - R0 - planetRadius;
}
}
I am referring this code to do this implementation...
http://eagle.phys.utk.edu/guidry/android/animatorDemo.html
I am just drawing a circle and trying to implement the same motion between 0 -120 degrees..

Resources