Well, lets say I have the Shape of an O and I want to render it.
Now my current rendering code is this:
public void fill(Shape shape, float xOffset, float yOffset) {
AffineTransform transform = new AffineTransform();
transform.translate(xOffset, yOffset);
PathIterator pi = shape.getPathIterator(transform, 1);
int winding = pi.getWindingRule();
ShapeRenderer r = getInstance();
float[] coords = new float[6];
boolean drawing = false;
while (!pi.isDone()) {
int segment = pi.currentSegment(coords);
switch (segment) {
case PathIterator.SEG_CLOSE:
if (drawing) {
r.endPoly();
}
break;
case PathIterator.SEG_LINETO:
r.lineTo(coords);
break;
case PathIterator.SEG_MOVETO:
if (drawing) {
r.endPoly();
}
drawing = true;
r.beginPoly(winding);
r.moveTo(coords);
break;
default:
throw new IllegalArgumentException("Unexpected value: " + segment);
}
pi.next();
}
}
ShapeRenderer:
private final Deque<Queue<Float>> vertices = new ConcurrentLinkedDeque<>();
#Override
public void beginPoly(int windingRule) {
}
#Override
public void endPoly() {
Queue<Float> q;
GL11.glBegin(GL11.GL_LINE_LOOP);
while ((q = vertices.poll()) != null) {
Float x, y;
while ((x = q.poll()) != null && (y = q.poll()) != null) {
GL11.glVertex2f(x, y);
}
}
GL11.glEnd();
}
#Override
public void moveTo(float[] vertex) {
Queue<Float> q = new ConcurrentLinkedQueue<>();
vertices.offer(q);
lineTo(vertex);
}
#Override
public void lineTo(float[] vertex) {
Queue<Float> q = vertices.peekLast();
q.offer(vertex[0]);
q.offer(vertex[1]);
}
So lets say I want to fill that Shape, just like java awt does, successfully... How would I do that?
(Already tried using GL_POLYGON, but it just fills the entire O and I have a filled circle, not an O. Also tried using parts of jogamp glu but it just rendered nothing, no clue why)
Already tried using GL_POLYGON, but it just fills the entire O and I have a filled circle, "
Yes of course. GL_POLYGON is for Convex shapes and fills the entire area enclosed by the polygon.
You have 2 options:
Use GL_POLYGON twice to draw a white shape and then a red shape inside the white shape.
Use GL_TRIANGLE_STRIP and form a shape that just draws the outline. The primitive can be formed by alternately specifying a point on the outer outline and on the inner outline.
Related
This is my second post on Mandelbrot fractal conversion from Java to C#.
As per my assignment, I need to Draw a mandelbrot fractal on a form, and once it is drawn, allow the user to Zoom in using the mouse, while also drawing a rectangle from the initial click point to the point where the click is released. This is the part of code which i believe is responsible for the rectangle.
private static void Swap<T>(ref T t1, ref T t2)
{
T temp = t1;
t1 = t2;
t2 = t1;
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g1 = e.Graphics;
g1.DrawImage(bitmap, 0, 0, x1, y1);
if (action)
{
//g.setColor(Color.White);
if (xe < xs)
{
Swap(ref xs, ref xe);
}
if (ye < ys)
{
Swap(ref ys, ref ye);
}
g1.DrawRectangle(Pens.White, xs, ys, (xe - xs), (ye - ys));
//g1.Dispose();
}
}
//load method here
private void Form1_Load(object sender, EventArgs e)
//while loading
{
init();
start();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (action)
{
xe = e.X;
ye = e.Y;
}
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
action = true;
// e.consume();
if (action)
{
xs = xe = e.X;
ys = ye = e.Y;
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
using (Graphics g = this.CreateGraphics())
{
Pen pen = new Pen(Color.White);
g.DrawRectangle(pen, xs, ys, Math.Abs(xs - xe), Math.Abs(ys - ye));
}
int z, w;
if (xs > xe)
{
z = xs;
xs = xe;
xe = z;
}
if (ys > ye)
{
z = ys;
ys = ye;
ye = z;
}
w = (xe - xs);
z = (ye - ys);
if ((w < 2) && (z < 2)) initvalues();
else
{
if (((float)w > (float)z * xy)) ye = (int)((float)ys + (float)w / xy);
else xe = (int)((float)xs + (float)z * xy);
xende = xstart + xzoom * (double)xe;
yende = ystart + yzoom * (double)ye;
xstart += xzoom * (double)xs;
ystart += yzoom * (double)ys;
}
xzoom = (xende - xstart) / (double)x1;
yzoom = (yende - ystart) / (double)y1;
mandelbrot();
this.Invalidate();
}
What the code does is, draw a rectangle AFTER the dragging is done, and then zoom in with the drawn rectangle still being displayed. What I needed is the rectangle to draw as the mouse is being dragged.
I referred to this question, and solution mentioned there did not help.
Java to C# conversion. How do i draw a rectangle on my bitmap?
Any help would be appreciated.
Drawing the Rectangle
First of all, it appears that the Graphics.DrawRectangle method is unable to draw a rectangle with negative widths or heights. You will therefore have to write a method that will take two points and produce a rectangle meeting the requirements (positive width and height).
private Rectangle CreateRectangle(Point pt1, Point pt2)
{
// we use this method to create the rectangle with positive width and height
int x1 = Math.Min(pt1.X, pt2.X);
int y1 = Math.Min(pt1.Y, pt2.Y);
return new Rectangle(x1, y1, Math.Abs(pt1.X - pt2.X), Math.Abs(pt1.Y - pt2.Y));
}
Second, in your event handler for the MouseDown event, record the position at which the mouse was held down.
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
this.startPoint = e.Location;// record the start position
}
Next, modify your mouse move method to update the variable that holds current location of the mouse. Additionally, make it invalidate the form so that the image is redrawn (along with the rectangle).
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
// record the current position as the end point if the left button is down
this.endPoint = e.Location;
// force a redraw
this.Invalidate();
}
}
In the form's Paint event handler, make your code call the CreateRectangle method with the start and end points of the rectangle in order to draw the rectangle on the form.
private void Form1_Paint(object sender, PaintEventArgs e)
{
// draw the cached Mandelbrot image
e.Graphics.DrawImage(mandelbrotCache, new Point(0, 0));
// draw the current rectangle
e.Graphics.DrawRectangle(rectPen, CreateRectangle(startPoint, endPoint));
}
Finally, in order to remove the rectangle when the mouse button is no longer pressed, set startPoint and endPoint to a value that gets drawn outside the image. This should be done in the MouseUp event handler.
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
// setting the point to -1,-1 makes them get drawn off the screen
startPoint = new Point(-1, -1);
endPoint = new Point(-1, -1);
// force an update so that the rectangle disappears
this.Invalidate();
}
}
Addressing the Flickering Issue
In order to stop the form from flickering while you're drawing to it, you will need to enable double buffering on the form. This is done by setting the DoubleBuffered property of the form to true. You can do this anywhere, but I prefer to do it right after the form is created, as below:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// this reduces the flickering
this.DoubleBuffered = true;
}
}
Complete Code:
Here is the complete code for all the steps I detailed above. You can plug in your methods in order to have a working solution.
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private Point startPoint;
private Point endPoint;
private Image mandelbrotCache;
private Pen rectPen;
public Form1()
{
InitializeComponent();
// this reduces the flickering
this.DoubleBuffered = true;
// initialize a dummy image. Cache a copy of your Mandelbrot fractal here
mandelbrotCache = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
using (var g = Graphics.FromImage(mandelbrotCache))
{
var imgRect = new Rectangle(0, 0,
mandelbrotCache.Width,
mandelbrotCache.Height);
g.FillRectangle(new HatchBrush(HatchStyle.Cross, Color.DarkBlue,
Color.LightBlue), imgRect);
}
// this is the pen to draw the rectangle with
rectPen = new Pen(Color.Red, 3);
}
private Rectangle CreateRectangle(Point pt1, Point pt2)
{
// we use this method to create a rectangle with positive width and height
int x1 = Math.Min(pt1.X, pt2.X);
int y1 = Math.Min(pt1.Y, pt2.Y);
return new Rectangle(x1, y1, Math.Abs(pt1.X - pt2.X), Math.Abs(pt1.Y - pt2.Y));
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
this.startPoint = e.Location;// record the start position
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
// record the current position as the end point if the left button is down
this.endPoint = e.Location;
// force a redraw
this.Invalidate();
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
// setting the point to -1,-1 makes them get drawn off the screen
startPoint = new Point(-1, -1);
endPoint = new Point(-1, -1);
// force an update so that the rectangle disappears
this.Invalidate();
}
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
// draw the cached Mandelbrot image
e.Graphics.DrawImage(mandelbrotCache, new Point(0, 0));
// draw the current rectangle
e.Graphics.DrawRectangle(rectPen, CreateRectangle(startPoint, endPoint));
}
}
}
Here is a screenshot of a rectangle being drawn.
Note: The left mouse button is still held down. The rectangle disappears immediately the button is released.
Helo everyone,
I have a homework which involves drawing and manipulating shapes in a Swing GUI.
I got into a problem that I dont get the results I want when I try to mirror my shapes.
The drawallnodes method is called in Jpanels paintComponent.
public void drawallnodes(ArrayList<DevicesEditor> nodes, Graphics2D g2)
{
int arraysize = nodes.size();
ArrayList<DevicesEditor> temparray;
AffineTransform at = new AffineTransform();
if (nodes.size() != 0)
{
System.out.println("nodes.size " + nodes.size());
if (currentarrayindex >= 0)
{
AffineTransform afx = new AffineTransform();// for rotate
for (int i = 0; i <= currentarrayindex; i++)
{
if (nodes.get(i).getWasAngleChanged())
{
afx.rotate(
Math.toRadians(nodes.get(i).getAngleInDegrees()),
nodes.get(i).getCenter().x,
nodes.get(i).getCenter().y);
nodes.get(i).setShape(
afx.createTransformedShape(nodes.get(i).getShape()));
nodes.get(i).setWasAngleChanged(false);
nodes.get(i).setokrajRectangle();
}
try
{
Rectangle r = nodes.get(i).getShape().getBounds();
}
catch (Exception e)
{
System.out.println(
"Exception found at getbounds, no shape with getbounds found");
}
AffineTransform saveXform = g2.getTransform();
g2.setColor(nodes.get(i).getColor());
int w = getWidth();
// it gets the JPanels width, which is set to 758px
at = AffineTransform.getTranslateInstance(w, 0);
System.out.println("*********Get width of shape: " + w);
at.scale(-1, 1); // mirror -x, y
g2.setPaint(Color.red);
g2.draw(at.createTransformedShape(nodes.get(i).getShape()));
try
{
g2.drawString(nodes.get(i).getText(),
(int) nodes.get(i).getCenter().getX(),
(int) nodes.get(i).getCenter().getY());
}
catch (Exception e)
{
System.err.println("No text found at node");
}
try
{
g2.draw((Shape) nodes.get(i).getShape());
}
catch (Exception e)
{
System.err.println("No shape found at node");
}
// g2.transform(AffineTransform.getRotateInstance(0, 1));
g2.setTransform(saveXform);
}
}
}
}
When I mirror the Shape,for example I draw on the right side,but the mirrorred image appears on the left side...I want to mirror the shape and get the mirrorred shape at the same place not accros my jpanel....
Thank you for your help
When you transform the shape with a simple affine transform like scale(-1,1) then it will be mirrored horizontally at x=0. That is, when you have an object at (300,100), it will afterwards be at (-300,100).
In order to mirror the object "in place" (that is, at the x-coordinate of its center point) you have to
Move the object so that its center is at x=0
Mirror the object
Move the object back to its original position
This sequence of transforms can (and should) be represented by a single AffineTransform that can be obtained by concatenating AffineTransforms that represent the individual steps. For example, you could create a method like
private static Shape mirrorAlongX(double x, Shape shape)
{
AffineTransform at = new AffineTransform();
at.translate(x, 0);
at.scale(-1, 1);
at.translate(-x, 0);
return at.createTransformedShape(shape);
}
In order to paint a shape that is flipped horizontally (i.e. mirrored) at its center, you can then call
g.fill(mirrorAlongX(shape.getBounds2D().getCenterX(), shape));
BTW: Get rid of all these Exceptions being caught there! That's a horrible style. E.g. replace the exception check that prints "No text found at node" with something like
String text = nodes.get(i).getText();
if (text != null)
{
g2.drawString(text, ...);
}
else
{
System.err.println("No text found at node"); // May not even be necessary...
}
I am trying to rotate a custom shape around its center, but can not get the result as expected.
what i want is
*shape should be rotated around its center without moving itself.*
what my solution is currently doing is
rotating a whole shape around its center , by every rotation its changing its position.
I have multiple shapes so i have created a class to encapsulate a shape with its transform in following class
public abstract class Shoe implements Shape, ShoeShape {
// variable declaration
/**
*
*/
public Shoe() {
position = new Point();
lastPosition = new Point();
}
public void draw(Graphics2D g2, AffineTransform transform, boolean firstTime) {
AffineTransform af = firstTime ? getInitTransform()
: getCompositeTransform();
if (af != null) {
Shape s = af.createTransformedShape(this);
if (getFillColor() != null) {
g2.setColor(getFillColor());
g2.fill(s);
} else {
g2.draw(s);
}
}
}
}
public AffineTransform getCompositeTransform() {
AffineTransform af = new AffineTransform();
af.setToIdentity();
af.translate(position.getX(), position.getY());
Point2D centerP = calculateShapeCenter();
af.rotate(orientation, centerP.getX(), centerP.getY());
return af;
}
public void onMouseDrag(MouseEvent me, Rectangle2D canvasBoundary,
int selectionOperation) {
// shape operation can be either resize , rotate , translate ,
switch (selectionOperation) {
case MmgShoeViewer.SHAPE_OPERATION_MOVE:
// MOVEMENT
break;
case MmgShoeViewer.SHAPE_OPERATION_ROTATE:
Point2D origin = calculateShapeCenter();
Point2D.Double starting = new Point2D.Double(me.getX(), me.getY());
currentAngle = RotationHelper.getAngle(origin, starting);
rotationAngle = currentAngle - startingAngle;
rotate(rotationAngle);
break;
case MmgShoeViewer.SHAPE_OPERATION_RESIZE:
break;
default:
System.out.println(" invalid select operation");
}
}
public void onMousePress(MouseEvent me, Rectangle2D canvasBoundary,
int selectionOperation) {
// shape operation can be either resize , rotate , translate ,
switch (selectionOperation) {
case MmgShoeViewer.SHAPE_OPERATION_MOVE:
break;
case MmgShoeViewer.SHAPE_OPERATION_ROTATE:
Point2D origin = calculateShapeCenter();
Point2D.Double starting = new Point2D.Double(me.getX(), me.getY());
startingAngle = RotationHelper.getAngle(origin, starting);
setShapeOperation(selectionOperation);
break;
case MmgShoeViewer.SHAPE_OPERATION_RESIZE:
break;
default:
System.out.println(" invalid select operation");
}
}
public void onMouseRelease(MouseEvent me, Rectangle2D canvasBoundary,
int selectionOperation) {
// shape operation can be either resize , rotate , translate ,
switch (selectionOperation) {
case MmgShoeViewer.SHAPE_OPERATION_MOVE:
break;
case MmgShoeViewer.SHAPE_OPERATION_ROTATE:
// FIXME rotation angle computation
setShapeOperation(-1);
break;
case MmgShoeViewer.SHAPE_OPERATION_RESIZE:
break;
default:
System.out.println(" invalid select operation");
}
}
public void rotate(double angle) {
orientation = (float) angle;
}
public void translate(double deltaX, double deltaY) {
position.setLocation(deltaX, deltaY);
lastPosition.setLocation(deltaX, deltaY);
}
// another getter and setter
I am calculating angle of rotation using following method
public static double getAngle(Point2D origin, Point2D other) {
double dy = other.getY() - origin.getY();
double dx = other.getX() - origin.getX();
double angle;
if (dx == 0) {// special case
angle = dy >= 0 ? Math.PI / 2 : -Math.PI / 2;
} else {
angle = Math.atan(dy / dx);
if (dx < 0) // hemisphere correction
angle += Math.PI;
}
// all between 0 and 2PI
if (angle < 0) // between -PI/2 and 0
angle += 2 * Math.PI;
return angle;
}
in mouse press event of the canvas mouse listener
selectedShape.onMousePress(me, canvasBoundary, shoeViewer
.getShapeOperation());
i am just calling selected shape's onMousePress method
and in my mouse drag method of the canvas mouse listener , i am just calling the selected shape's onMouseDrag method which updates the rotation angle as you can see from the very first class
selectedShape.onMouseDrag(me, canvasBoundary, shoeViewer
.getShapeOperation());
and you can see the draw method of the individual shape , to draw the shape according to current transform , i am calling from paintComponent like
Iterator<Shoe> shoeIter = shoeShapeMap.values().iterator();
while (shoeIter.hasNext()) {
Shoe shoe = shoeIter.next();
shoe.draw(g2, firstTime);
}
where shoeShapeMap contains all of the custom shapes currently on the canvas.
is i am doing mistake in calculating angle or determining anchor point ? my current solution rotates shape 360 degree by checking all the conditions[90 degree etc.] as you can see in the above mentioned method.
i want the shape should be rotated around its center without resizing its positions ?
in the word it is difficult to explain , so please suggest me any better way to show here what i want to accomplish ?
i think i have mentioned all the things related to this issue. if you have any doubts please feel free to ask me.
i found 2 related posts here but i could not find much information from them.
I think that the solution may be to (either/and):
invert the order of operations on your AffineTransform, put translate after rotate
use -x and -y for your translation values
The city in my game is randomly generated but is a graph of roads and intersections that can only form rectangles:
As can be seen, my terrain is pretty empty. What I want to do is find each empty rectangle and store in in a list of rectangles, forming Lots.
As you can see in this illustration, I filled in 3 'lots' and in 1 I showed the 3 rectangles it is made of.
My data structures are:
package com.jkgames.gta;
import android.graphics.Bitmap;
import android.graphics.RectF;
public class Intersection extends Entity
{
Road topRoad;
Road leftRoad;
Road bottomRoad;
Road rightRoad;
Bitmap image;
public Bitmap getImage()
{
return image;
}
public void setImage(Bitmap image)
{
this.image = image;
}
public Intersection(RectF rect, Bitmap image)
{
setRect(rect);
setImage(image);
}
public Road getTopRoad()
{
return topRoad;
}
public void setTopRoad(Road topRoad)
{
this.topRoad = topRoad;
}
public Road getLeftRoad()
{
return leftRoad;
}
public void setLeftRoad(Road leftRoad)
{
this.leftRoad = leftRoad;
}
public Road getBottomRoad()
{
return bottomRoad;
}
public void setBottomRoad(Road bottomRoad)
{
this.bottomRoad = bottomRoad;
}
public Road getRightRoad()
{
return rightRoad;
}
public void setRightRoad(Road rightRoad)
{
this.rightRoad = rightRoad;
}
#Override
public void draw(GraphicsContext c)
{
c.drawRotatedScaledBitmap(image, getCenterX(), getCenterY(),
getWidth(), getHeight(), getAngle());
}
}
public class Road extends Entity
{
private Bitmap image = null;
private Intersection startIntersection;
private Intersection endIntersection;
private boolean topBottom;
public Road(RectF rect, Intersection start, Intersection end,
Bitmap image, boolean topBottom)
{
setRect(rect);
setStartIntersection(start);
setEndIntersection(end);
setImage(image);
setTopBottom(topBottom);
}
#Override
public void draw(GraphicsContext c)
{
//Rect clipRect = c.getCanvas().getClipBounds();
//c.getCanvas().clipRect(getRect());
float sizeW;
float sizeH;
if(isTopBottom())
{
sizeW = getWidth();
sizeH = (sizeW / image.getWidth()) * image.getHeight();
}
else
{
sizeW = getHeight();
sizeH = (sizeW / image.getWidth()) * image.getHeight();
}
int numTiles = isTopBottom() ? (int)Math.ceil(getHeight() / sizeH) :
(int)Math.ceil(getWidth() / sizeW);
for(int i = 0; i < numTiles; ++i)
{
if(isTopBottom())
{
c.drawRotatedScaledBitmap(
image,
getRect().left + (sizeW / 2.0f),
(getRect().top + (sizeH / 2.0f)) + (sizeH * i),
sizeW, sizeH, 0.0f);
}
else
{
c.drawRotatedScaledBitmap(
image,
getRect().left + (sizeH / 2.0f) + (sizeH * i),
getRect().top + (sizeH / 2.0f),
sizeW, sizeH, (float)Math.PI / 2.0f);
}
}
// c.getCanvas().clipRect(clipRect);
}
public Bitmap getImage()
{
return image;
}
public void setImage(Bitmap image)
{
this.image = image;
}
public Intersection getStartIntersection()
{
return startIntersection;
}
public void setStartIntersection(Intersection startIntersection)
{
this.startIntersection = startIntersection;
}
public Intersection getEndIntersection()
{
return endIntersection;
}
public void setEndIntersection(Intersection endIntersection)
{
this.endIntersection = endIntersection;
}
public boolean isTopBottom()
{
return topBottom;
}
public void setTopBottom(boolean topBottom)
{
this.topBottom = topBottom;
}
}
The city is a list of roads and intersections.
Is there some sort of algorithm that could generate these lots and their rectangles?
Thanks
The easiest method that comes to my mind is to use a flood-fill algorithm to build up your list of regions. So basically
foreach square:
if the square isn't part of a region:
create a new empty region list
add the square to it
recursivly add all neighboring squares to the region
The end result will be that you'll have a list of regions which you can then do whatever you want with (look and see if any of the contained squares have buildings on, color in for the user, etc..).
Note: to determine whether a square is part of a region or not, I'd add a marked flag or something to the square data structure, that way when you start, you go through and clear all those flags, then as you add a square to a region you set that flag, and when you want to check to see if a square is in a region, all you need to do is check to see if that flag is set or not. That way you end up with a linear time algorithm to construct your list of regions.
As Markus pointed out in the comments here, this "flag" could be in fact a pointer/reference to a Lot object that holds the list of your squares, which would probably be convenient to have handy anyways.
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++) {
if (x > 0 && squares[y][x].isConnectedTo(squares[y][x-1]) {
// Connected from the left
squares[y][x-1].getLot().addSquare(squares[y][x]);
if (y > 0 && squares[y][x].isConnectedTo(squares[y-1][x]) {
// Connected from both the left and above
squares[y-1][x].getLot().mergeWith(squares[y][x].getLot());
}
}
else if (y > 0 && squares[y][x].isConnectedTo(squares[y-1][x]) {
// Connected from above
squares[y-1][x].getLot().addSquare(squares[y][x]);
}
else {
// Not connected from either
createNewLot().addSquare(squares[y][x]);
}
}
Lot.addSquare(…) adds a square to the lot, and calls setLot(…) on the square.
Lot.mergeWith(…) merges two lots, and reassigns the squares assigned to them, if they are not the same lot.
Square.isConnectedTo(…) checks if they are neighbors, and that there is no road in between.
You could optimize this by using the Disjoint-set data structure
Wonder if anyone could point me in the right direction.
I want to be able to animate an object like a bullet from a static position to any area on the screen.
I have no problem with simple horizontal and vertical movements. I.e. x +/- 1 or y +/- 1.
But when it comes to an object like a bullet could move/animate at any degree I'm not quite sure how to do this by making the animation look smooth. For example at 18, 45, or 33 degree angle an algorithm like y+1, x+1, y+1..... Is not going to make the animation very smooth.
Thanks in advance
P.s maybe there is some documentation out there already?
Update
Thanks to everyone who has replied.
This is the code I have so far based on your comments.
package com.bullet;
//imports go here
public class canvas extends SurfaceView implements OnTouchListener, SurfaceHolder.Callback{
private Bitmap bullet;
private int bulletStartX, bulletStartY;
private int bulletX, bulletY;
private int bulletEndX = -1;
private int bulletEndY = -1;
private int incX = 0;
private int incY = 0;
private SurfaceHolder holder;
private Thread t;
public canvas(Context context) {
super(context);
this.setBackgroundColor(Color.WHITE);
setFocusable(true);
setFocusableInTouchMode(true);
setOnTouchListener(this);
bulletStartX = 0;
bulletStartY = 0;
bulletX = bulletStartX;
bulletY = bulletStartY;
bullet = BitmapFactory.decodeResource(getResources(), R.drawable.bullet);
holder = getHolder();
holder.addCallback(this);
}
public void onDraw(Canvas canvas){
if(bulletEndX != -1 && bulletEndY != -1){
Log.e("here", "drawing bullet");
Log.e("here", "x: " + bulletX + ", y: " + bulletY);
canvas.drawBitmap(bullet, bulletX, bulletY, null);
}
}
public void updateBullet(){
Log.e("here", "inc bullet");
bulletX += incX;
bulletY += incY;
if(bulletX > bulletEndX){
bulletEndX = -1;
}
if(bulletY > bulletEndY){
bulletEndY = -1;
}
}
#Override
public boolean onTouch(View v, MotionEvent event) {
int[] coordinates = {(int) event.getX(), (int) event.getY()};
int motion = event.getAction();
switch(motion){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
bulletX = bulletStartX;
bulletY = bulletStartY;
bulletEndX = (int) event.getX();
bulletEndY = (int) event.getY();
incX = (int) bulletEndX / 50;
incY = (int) bulletEndY / 50;
Log.e("here", "touch up");
break;
}
return true;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
t = new GameThread(this, holder);
t.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
//Thread class
class GameThread extends Thread{
private canvas canvas;
private SurfaceHolder holder;
private boolean run = false;
long delay = 70;
public GameThread(canvas canvas, SurfaceHolder holder){
this.holder = holder;
this.canvas = canvas;
startThread(true);
}
public boolean isRunning(){
return this.run;
}
private void startThread(boolean run){
this.run = run;
}
public void stopThread(){
this.run = false;
}
#Override
public void run(){
while(run){
Canvas c = null;
try {
//lock canvas so nothing else can use it
c = holder.lockCanvas(null);
synchronized (holder) {
//clear the screen with the black painter.
//This is where we draw the game engine.
//Log.e("drawthread", "running");
if(bulletEndX != -1 && bulletEndY != -1){
updateBullet();
canvas.postInvalidate();
}
canvas.onDraw(c);
//Log.e("drawthread", "ran");
try {
sleep(32);
//Log.e("slept", "sleeping");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
holder.unlockCanvasAndPost(c);
}
}
}
}
}
}
The drawing works pretty well, however there are two problems.
1. the drawing is fairly slow and jumpy... compared to something like this java example http://www.youtube.com/watch?v=-g5CyPQlIo4
2. If the click is on a position to close to Y = 0 then due to the value of ENDY / FRAME being less that 1, means that the round up is to 0 and the bullet travels across the top of the screen, rather than the ocassionaly increment in Y.
#SyntaxT3rr0r your right, that is prob the best way to go about it. But do you know of any documentation for implementing something like this?
Thanks again to everyone who replied
My understanding is that you're asking how to determine how many x&y pixels to move the bullet per frame, as opposed to asking about implementation details specific to animating on Android.
The short answer: Attack it with Math :P
With the example of the bullet:
-You can either chop up the animation as "divide up the animation into 100 frames, play them as fast as we can" or "Play the animation in about 2 seconds, smash as many frames in those 2 seconds as you can." I'm going to explain the former, because that sounds like what you're trying to do.
Start out with a starting X & Y, and an ending X & Y: Let's pretend you want to move from 0,0 to 200,400, and you want to do it in about 100 frames of animation.
Divide up the total distance travelled along the X axis by the number of frames. Do the same with total distance along Y axis. Now you have the distance to travel x & y for each frame. For this example, you want the bullet to move 2 pixels per frame (200 pixels / 100 frames) sideways, and 4 pixels per frame (400 pixels / 100 frames) vertically. So every frame, add x +=2, y+=4.
I suggest you to read the following articles:
View Animation and Property Animation
I don't think this can be answered in it's current form. First how are you animating? are you using the graphics API? GL? AndEngine?
If is graphics API I would rotate the canvas the appropriate degree and move the bullet up the y axis.
For GL you can do the same thing.
For and engine, refer to the tutorials.