How to calculate the center point of an arc? - math

I have a CustomPieView which is made of several pie slices. I have to draw something in the middle of every pie section.
//Inside Activity's onCreate Method
public void onCreate(Bundle savedInstanceState){
int size = 400;
int bgColor = 0xffa11b1;
ViewPieChart piechart = (ViewPieChart) findViewById(R.id.pieChartView);
piechart.setCallBack(this);
piechart.setLayoutParams(new LayoutParams(size, size));
piechart.setGeometry(size, size, 2, 2, 2, 2, 2130837504);
piechart.setSkinparams(bgColor);
piechart.setData(piedata, maxCount);
piechart.invalidate();
}
//CustomPieView extends View
public void setGeometry(int width, int height, int gapleft, int gapright, int gaptop, int gapbottom, int overlayid) {
mWidth = width;
mHeight = height;
mGapleft = gapleft;
mGapright = gapright;
mGapBottm = gapbottom;
mGapTop = gaptop;
}
protected void onDraw(Canvas canvas){
canvas.drawColor(Color.DKGRAY);
mBagpaints.setAntiAlias(true);
mBagpaints.setStyle(Paint.Style.FILL);
mBagpaints.setColor(0x88FF0000);
mBagpaints.setStrokeWidth(0.0f);
mLinePaints.setAntiAlias(true);
mLinePaints.setColor(Color.WHITE);
mLinePaints.setStrokeWidth(3.0f);
mLinePaints.setStyle(Paint.Style.STROKE);
sLinePaint.setAntiAlias(true);
sLinePaint.setColor(Color.BLACK);
sLinePaint.setStrokeWidth(3.0f);
sLinePaint.setStyle(Paint.Style.STROKE);
RectF mOvals = new RectF(mGapleft, mGapTop, mWidth - mGapright, mHeight
- mGapBottm);
mStart = START_INC;
PieDetailsItem item;
for (int i = 0; i < mdataArray.size(); i++) {
item = (PieDetailsItem) mdataArray.get(i);
mBagpaints.setColor(item.color);
mSweep = (float) 360* ((float) item.count / (float) mMaxConnection);
canvas.drawArc(mOvals, mStart, mSweep, true, mBagpaints);
canvas.drawArc(mOvals, mStart, mSweep, true, mLinePaints);
// The function below is setting the global vars
cSegX, cSegY to the center of the segment. Not Working!!
calculateMidPointOfPieSegment(mSweep);
canvas.drawPoint(cSegX, cSegY, sLinePaint);
mStart = mStart + mSweep;
}
mState = IS_DRAW;
callBack.onDrawFinished(null);
}
private float calculateRadius(){
float width = mWidth/2;
float height = mHeight/2;
if(width < height){
return width;
}else{
return height;
}
}
private void calculateMidPointOfPieSegment(float sweepAngle){
cSegX = (float)((calculateRadius()/2)*Math.cos(Math.toRadians(sweepAngle/2))+(mWidth/2));
cSegY = (float)((calculateRadius()/2)*Math.sin(Math.toRadians(sweepAngle/2))+(mHeight/2));
}
Just see the dots in the image below, it has to be in the center of every arc.
Screenshot
Ref.
Complete Source

Let me know if this clears it up. This is the basic logic you want to implement.
//rimPoint = (rX, rY)
//centerPoint = (cX,cY)
//theta is the angle of the the midpoint measured anticlockwise from the x axis,
//or the average of the two angles making up the slice(measured anticlockwise from
//the x axis)
private void calculateMidPointOfPieSegment(cX,cY, theta)
{
float rX = cX + calculateRadius()*(cos(theta))
float rY = cY + calculateRadius()*(sin(theta))
cSegX = (rX+cX)/2;
cCegY = (rY+cY)/2;
}
If theta isn't in radians, sub it out for theta*Math.PI/180

Related

How to pass a Pointer member inside a class

I have a callback function (for ROS subscriber of type PointCloud2 msg).
I want to be able to pass a different pointer member inside the callback function because I saw that when I call the function twice there is a problem in the values of the variable that use this pointer.
This is part of my code:
class Planner {
public:
char frame_id[10] = "os1_lidar";
int num_area = 1;
int i = 0; // for create the circular path
float right_point_y = 0;
float left_point_y = 0;
float y_distance = 0; //the distance between the trees in the Y axis
float center_point_y = 0; //the center point between the trees in the y axis after average calculation
float right_center_point_x = 0; //the center point of the right tree in the x axis
float left_center_point_x = 0; //the center point of the left tree in the x axis
float max_center_point_y = 1.4; //the max distance in the 7 axis for sending to arduino the steering angle
//setting the two side area of the trees to filter the point cloud
float minX_r = -5, minY_r = 2, minZ_r = -0.4;
float maxX_r = -2, maxY_r = 4, maxZ_r = -0.1;
float minX_l = -5, minY_l = -4, minZ_l = -0.4;
float maxX_l = -2, maxY_l = -2, maxZ_l = 0.1;
int red, green, blue = 0; //for change the boxes' color outside the lines
bool publish_points = true;
bool publish_markers = true;
ros::Publisher point_cloud_pub;
ros::Publisher center_point_marker_pub;
ros::Publisher right_point_marker_pub;
ros::Publisher left_point_marker_pub;
ros::Publisher marker_area_right_pub;
ros::Publisher marker_area_left_pub;
void Point_Filtering(const pcl::PointCloud<pcl::PointXYZ>::ConstPtr &cloud_msg);
}
void Planner::Point_Filtering(const pcl::PointCloud<pcl::PointXYZ>::ConstPtr &cloud_msg) {
ROS_INFO_STREAM_ONCE("Getting Data from Lidar");
ROS_INFO_STREAM_ONCE("size " << cloud_msg->size());
ROS_INFO_STREAM_ONCE("width " << cloud_msg->width);
ROS_INFO_STREAM_ONCE("height " << cloud_msg->height);
// Right Line
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered_r(new pcl::PointCloud<pcl::PointXYZ>);
//Left Line
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered_l(new pcl::PointCloud<pcl::PointXYZ>);
//Right Trees Box Filter
pcl::CropBox<pcl::PointXYZ> boxFilter_r;
boxFilter_r.setInputCloud(cloud_msg);
boxFilter_r.setMin(Eigen::Vector4f(minX_r, minY_r, minZ_r, 1.0));
boxFilter_r.setMax(Eigen::Vector4f(maxX_r, maxY_r, maxZ_r, 1.0));
boxFilter_r.setNegative(false);
boxFilter_r.setTranslation(Eigen::Vector3f(0.0, 0.0, 0.0));
boxFilter_r.filter(*cloud_filtered_r);
//Left Trees Box Filter
pcl::CropBox<pcl::PointXYZ> boxFilter_l;
boxFilter_l.setInputCloud(cloud_msg);
boxFilter_l.setMin(Eigen::Vector4f(minX_l, minY_l, minZ_l, 1.0));
boxFilter_l.setMax(Eigen::Vector4f(maxX_l, maxY_l, maxZ_l, 1.0));
boxFilter_l.setNegative(false);
boxFilter_l.setTranslation(Eigen::Vector3f(0.0, 0.0, 0.0));
boxFilter_l.filter(*cloud_filtered_l);
//Right Trees (Y axis)
Eigen::Vector4f centroid_r_y;
pcl::compute3DCentroid(*cloud_filtered_r, centroid_r_y);
right_point_y = centroid_r_y[1];
//Left Trees (Y axis)
Eigen::Vector4f centroid_l_y;
pcl::compute3DCentroid(*cloud_filtered_l, centroid_l_y);
left_point_y = centroid_l_y[1];
y_distance = abs(centroid_l_y[1]) + abs(centroid_r_y[1]);
center_point_y = (abs(centroid_l_y[1]) - abs(centroid_r_y[1])) / 2;
//Right Trees (X axis)
Eigen::Vector4f centroid_r_x;
pcl::compute3DCentroid(*cloud_filtered_r, centroid_r_x);
right_center_point_x = centroid_r_x[0];
// ROS_INFO_STREAM("right_point_x : "<< num_area << " :" << right_center_point_x);
//Left Trees (X axis)
Eigen::Vector4f centroid_l_x;
pcl::compute3DCentroid(*cloud_filtered_l, centroid_l_x);
left_center_point_x = centroid_l_x[0];
}
int main(int argc, char **argv) {
ROS_INFO_STREAM_ONCE("Running");
// Initialize ROS
ros::init(argc, argv, "pcl_filter_node");
ros::NodeHandle nh;
//region 1st
Planner planner;
ros::Subscriber sub = nh.subscribe<pcl::PointCloud<pcl::PointXYZ> >("/fused_points", 1,
&Planner::Point_Filtering, &planner);
planner.num_area = 1;
planner.minX_r = -3;
planner.minX_l = -3;
planner.maxX_r = 0;
planner.maxX_l = 0;
planner.point_cloud_pub = nh.advertise<pcl::PointCloud<pcl::PointXYZ> >("pcl_filtered", 1);
planner.center_point_marker_pub = nh.advertise<visualization_msgs::Marker>("center_marker",
1);
planner.right_point_marker_pub = nh.advertise<visualization_msgs::Marker>("right_marker",
1);
planner.left_point_marker_pub = nh.advertise<visualization_msgs::Marker>("left_x_marker",
1);
planner.marker_area_right_pub = nh.advertise<visualization_msgs::Marker>("right_box_marker",
1);
planner.marker_area_left_pub = nh.advertise<visualization_msgs::Marker>("left_box_marker",
1);
//endregion
//region 2nd
Planner planner_2;
planner_2.num_area = 2;
planner_2.minX_r = -8 ;
planner_2.minX_l = -8;
planner_2.maxX_r = -5;
planner_2.maxX_l = -5;
planner_2.publish_points = false;
planner_2.point_cloud_pub = nh.advertise<pcl::PointCloud<pcl::PointXYZ> >("pcl_filtered_2",
1);
planner_2.center_point_marker_pub = nh.advertise<visualization_msgs::Marker>
("center_marker_2", 1);
planner_2.right_point_marker_pub = nh.advertise<visualization_msgs::Marker>
("right_marker_2", 1);
planner_2.left_point_marker_pub = nh.advertise<visualization_msgs::Marker>
("left_x_marker_2", 1);
planner_2.marker_area_right_pub = nh.advertise<visualization_msgs::Marker>
("right_box_marker_2", 1);
planner_2.marker_area_left_pub = nh.advertise<visualization_msgs::Marker>
("left_box_marker_2", 1);
ros::Subscriber sub2 = nh.subscribe<pcl::PointCloud<pcl::PointXYZ> >("/fused_points", 1,
&Planner::Point_Filtering, &planner_2);
//endregion
ros::Rate r(15);
ros::Time test_time;
while (nh.ok()) {
ros::spinOnce();
r.sleep();
}
}
This code takes the original point clouds, filters it to relevant areas and then calculate centroid in those filtered point clouds.
I want to take this line :
pcl::PointCloudpcl::PointXYZ::Ptr cloud_filtered_r(new
pcl::PointCloudpcl::PointXYZ);
and to put it in the public part of the class so I will be able to change this name of the filtered points.
When I subscribe to the same msg twice the centroid of the second area is equal to the first and this is wrong for me.
Thanks for your help.

How to set QChart Aspect Ratio

I am trying to implement an algorithm to maintain a QChart aspect ratio. It is kind of working but i was wondering if anyone had a simpler solution.
below is the code that occurs on a resize event
void TrainChartView::maintainAspectRatio(QSizeF eventSize) {
int aspect = 8;
QSizeF chartSize = m_chart->size();
QValueAxis* axisX = qobject_cast<QValueAxis*>(m_chart->axisX());
QValueAxis* axisY = qobject_cast<QValueAxis*>(m_chart->axisY());
// get Min Max X-axis Value
double minAxisX = axisX->min();
double maxAxisX = axisX->max();
double minAxisY = axisY->min();
double maxAxisY = axisY->max();
// Get Coordinates in scene of min and max X-axis value
QPointF minAxisXPosition = m_chart->mapToPosition(QPointF(minAxisX, 0));
QPointF maxAxisXPosition = m_chart->mapToPosition(QPointF(maxAxisX, 0));
QPointF minAxisYPosition = m_chart->mapToPosition(QPointF(0, minAxisY));
QPointF maxAxisYPosition = m_chart->mapToPosition(QPointF(0, maxAxisY));
double axisXSize = abs(maxAxisXPosition.x() - minAxisXPosition.x());
double axisYSize = abs(maxAxisXPosition.y() - minAxisYPosition.y());
// get the size of axis x in the coordinate system
double deltaAxisXSize = maxAxisXPosition.x() - minAxisXPosition.x();
if (chartSize.width() != eventSize.width()) {
QPointF maxAxisValue = m_chart->mapToValue(QPointF(0, (minAxisYPosition.y() - deltaAxisXSize)));
axisY->setRange(minAxisY, maxAxisValue.y() / aspect);
}
if (chartSize.height() != eventSize.height() && m_chart->minimumSize().height() >= eventSize.height()) {
double deltaHeight = eventSize.height() - chartSize.height();
maxAxisYPosition.setY(maxAxisYPosition.y() - deltaHeight);
QPointF maxAxisValue = m_chart->mapToValue(QPointF(maxAxisYPosition));
axisY->setRange(minAxisY, maxAxisValue.y());
}
I modified a bit the piece of code you did, and here is what I ended up with :
in the .h
//! \brief The QChartView_scaledAxis class extends the QChartView class but force the graph to be normalized
//! (i.e.) 1 pixel on the x-axis represent the same amount as 1 pixel on the y-axis.
class QChartView_scaledAxis : public QChartView {
public:
void resizeEvent(QResizeEvent *event) override;
};
in the .cpp
void QChartView_scaledAxis::resizeEvent(QResizeEvent *event) {
QChartView::resizeEvent(event);
// Get the axis of the graph
QValueAxis* axisX = qobject_cast<QValueAxis*>(this->chart()->axes(Qt::Horizontal)[0]);
QValueAxis* axisY = qobject_cast<QValueAxis*>(this->chart()->axes(Qt::Vertical)[0]);
// Get the series displayed on the graph
const QList<QAbstractSeries*> series = this->chart()->series();
// get Min Max values (on X and Y) of all the points plotted on the graph
float minX = std::numeric_limits<double>::max();
float maxX = std::numeric_limits<double>::min();
float minY = std::numeric_limits<double>::max();
float maxY = std::numeric_limits<double>::min();
for(QAbstractSeries *p_serie : series) { //iterate on all the series in the graph
//Assuming all the series in the graph are QXYSeries...
for(QPointF p : qobject_cast<QXYSeries*>(p_serie)->points()) { //iterate on each point of each serie
minX = fmin(minX, p.x());
maxX = fmax(maxX, p.x());
minY = fmin(minY, p.y());
maxY = fmax(maxY, p.y());
}
}
// Get the points at both ends of the axis (will help to determine the plottable area in pixel)
const QPointF minPosition = this->chart()->mapToPosition(QPointF(axisX->min(), axisY->min()));
const QPointF maxPosition = this->chart()->mapToPosition(QPointF(axisX->max(), axisY->max()));
// Ration between the size of the axis in pixel and in term of represented value
const double axisX_PixToValue = (maxX - minX) / (maxPosition.x() - minPosition.x());
const double axisY_PixToValue = (maxY - minY) / (maxPosition.y() - minPosition.y());
// The smallest ratio must be 'kept' and applied to the other axis
if(abs(axisX_PixToValue) > abs(axisY_PixToValue)) {
axisY->setMin(minY);
axisY->setMax(minY + (maxPosition.y() - minPosition.y()) * std::copysign(axisX_PixToValue, axisY_PixToValue));
} else {
axisX->setMin(minX);
axisX->setMax(minX + (maxPosition.x() - minPosition.x()) * std::copysign(axisY_PixToValue, axisX_PixToValue));
}
}
This code is for a 1:1 ratio, but I'm sure it can be easily modified for any other ratio...

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

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

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