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
Related
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.
im trying to make the bouncing ball bounce on the arrays of rectangles. I've looked at various other codes but cant seem to find a solution. Would appreciate any help!!!
Basically, i want the bouncing ball to recognise that theres the rectangles there and for it to be able to jump onto the rectangles.
PVector location; // Location of shape
PVector velocity; // Velocity of shape
PVector gravity; // Gravity acts at the shape's acceleration
PVector upwardForce;
PImage bg;
int radius = 10, directionX = 1, directionY = 0;
float x=20, y=20, speed=0.5;
int xarray[] = new int[20];
int yarray[] = new int[20];
// =========================================================
void setup() {
size(380,750);
location = new PVector(100,50);
velocity = new PVector(0.0,2.1);
upwardForce = new PVector(0.0,-10.0);
gravity = new PVector(0,0.4);
bg = loadImage("bg.png");
bg.resize(1600,1600);
background(0);
for(int i =0; i< 20;i++){
xarray[i]= i*100;
yarray[i] = 750-int(random(10))*50;
}
}
int xd =0, yd=0;
void draw() {
background(0);
noStroke();
xd--;
yd++;
// display image twice:
image(bg, y, 0);
image(bg, y+bg.height, 0);
// pos
y--;
if (y<-bg.height)
y=0;
for (int i = 0;i< 20;i++){
if (xarray[i] <100 && xarray[i]+100 >100){
fill(255,0,0);
}
else {
fill(255);
}
rect(xarray[i],yarray[i],100,1200);
fill(255);
xarray[i]=xarray[i]-4;
//yarray[i]=yarray[i]+1;
if (xarray[i] + 100 < 0){
xarray[i]+=2000;
// yarray[i]-=850;
}
}
// changing Position
x=x+speed*directionX;
y=y+speed*directionY;
// check boundaries
if ((x>width-radius) || (x<radius))
{
directionX=-directionX;
}
if ((y>height-radius) || (y<radius))
{
directionY=-directionY;
}
// draw
// if(direction==1)
// Add velocity to the location.
location.add(velocity);
// Add gravity to velocity
velocity.add(gravity);
// Bounce off edges
if ((location.x > width) || (location.x < 0)) {
velocity.x = velocity.x * -1;
}
if ((location.y > height) || (location.y < 0)){
// We're reducing velocity ever so slightly
// when it hits the bottom of the window
velocity.y = velocity.y * -0.95;
location.y = height;
}
// Display circle at location vector
stroke(255);
strokeWeight(0);
fill(255);
ellipse(location.x,location.y,30,30);
}
void keyPressed()
{
velocity.add(upwardForce);
}
The best advice we can give you is to break your problem down into smaller steps and to take those steps on one at a time.
For example, can you create a simple sketch that just shows a single hard-coded circle and a single hard-coded rectangle? Now add some code that prints a message to the console if they're colliding. You're going to have to do some research into collision detection, but here's a hint: a common technique is to treat the ball as a rectangle, so you can do rectangle-rectangle collision detection.
Get that working perfectly by itself, and then work your way forward in small steps. Can you add a second rectangle to your sketch? How about a third?
Then if you get stuck, you can post a MCVE (not your whole project, just a small example) along with a more specific question. Good luck.
Here's a few suggestions:
You're best off using a Rectangle class. That way, you don't have to store the locations in an array, and the collide function can be a method of the class. It's easier to just call the positions of the rectangles "x" and "y", but this would obviously conflict with the x and y global variables which you declared at the top of the code. Assuming that you would want to make the ball bounce if it collided, you would need to have a "ballLastx" and a "ballLasty" in order to keep track of which direction the ball came from. You would also need to store the Rectangles in an array or arrayList. It would be something like this:
PVector lastLocation;
Rectangle[] rects;
As for the rectangle class, here's how it would probably look like this:
class Rectangle {
float x, y;
Rectangle(float x_, float y_) {
x = x_;
y = y_;
}
void show() {
//Displays rectangle
if (x < 100 && x+100 > 100) fill(255,0,0);
else fill(255);
rect(x,y,100,1200);
fill(255);
x=x-4;
if (x + 100 < 0) x+=2000;
}
private boolean insideX(PVector pos) {
return (pos.x + 15 >= x && pos.x - 15 <= x+100);
}
private boolean insideY(PVector pos) {
return (pos.y + 15 >= y && pos.y - 15 <= x + 1200);
}
boolean collidedX() {
//Detects if the ball has collided along the x-axis
return ((insideX(location) && !insideX(lastLocation)) && insideY(location))
}
boolean collidedY() {
//Detects if the ball has collided along the y-axis
return ((insideY(location) && !insideY(lastLocation)) && insideX(location))
}
}
And then, in your setup function, you could declare the Rectangle classes in a for-loop:
//declare the rects array
rects = new Rectangle[20];
//declare each item of the rects array to be a Rectangle
for(int i = 0; i < rects.length; i++) {
rects[i] = new Rectangle(i*100, 750-int(random(0,10))*50;
}
In order to detect the collision and to bounce the ball, you would need to loop through all of the Rectangles and see if the ball should bounce off any of them:
boolean bouncex = false;
boolean bouncey = false;
//see if any of the rects are colliding with the ball
for(Rectangle r : rects) {
if(r.collidedX()) bouncex = true;
if(r.collidedY()) bouncey = true;
}
//if any are colliding, bounce the ball
if(bouncex) velocity.x = -velocity.x;
if(bouncey) velocity.y = -velocity.y;
Finally, don't forget to set the lastLocation PVector to the current location, just before moving the current location:
lastLocation = location.copy();
//move the ball...
Hope this was helpful!
I have custom view control, which looks like this:
Inside my activity I want to be able to move this view around the screen by dragging it on green Arcs (left or right does not matter).
Also want to be able to detect if yellow arc at top is tapped, middle circle or bottom arc.
I'm having trouble to detect where the tap is in which area. This is the code that I'm using inside of my activity:
float dX, dY;
final MyCustomView myCustomView = (MyCustomView)findViewById(R.id.test);
final Boolean[] movable = {false};
myCustomView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
movable[0] = false;
dX = view.getX() - event.getRawX();
dY = view.getY() - event.getRawY();
int x = (int) event.getX();
int y = (int) event.getY();
if (myCustomView.leftArcRegion.contains(x,y) || myCustomView.rightArcRegion.contains(x,y)){
movable[0] = true;
} else if (myCustomView.topArcRegion.contains(x,y)){
//todo: do something if top arc area is selected
} else if (myCustomView.midRoundedBitmapRegion.contains(x,y)){
//todo: do something if mid bitmap area is selected
} else if (myCustomView.bottomArcRegion.contains(x,y)){
//todo: do something if bottom arc area is selected
}
break;
case MotionEvent.ACTION_MOVE:
if (movable[0]) {
view.animate()
.x(event.getRawX() + dX)
.y(event.getRawY() + dY)
.setDuration(0)
.start();
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
break;
default:
return false;
}
return true;
}
});
And these are public fields from my custom view control:
public Region topArcRegion;
private Path topArc;
//topArc is my top arc path
RectF rectFTop = new RectF();
topArc.computeBounds(rectFTop, true);
topArcRegion = new Region();
topArcRegion.setPath(topArc, new Region((int) rectFTop.left, (int) rectFTop.top,
(int) rectFTop.right, (int) rectFTop.bottom));
But it looks like it uses rectangular shapes for these regions, not arcs when checking with this "contains" method. And because of that I'm not getting expected results.
So, how can I detect where is initial tap (top arc, bottom arc, side arcs or middle bitmap) in order to apply my app logic?
Since you're only looking to detect touches inside an arc segment, it should't be too complicated.
Each of your arc segments is defined as the space between two concentric circles and between start and end angles. So all you really want to do is do a little trig to determine the distance from the center of the circles to your touch point and the angle from the center to your touch point.
float x = touchevent.getX();
float y = touchevent.getY();
// Transform relative to arc centers
x -= circle1.x;
y -= circle1.y;
double dist = Math.sqrt(x*x + y*y);
double angle = Math.atan2(y,x) * 180 / Math.PI;
// Given an arc segment defined by circle1, circle2, angle1, angle2:
boolean touch = dist > circle1.radius && dist < circle2.radius &&
angle > angle1 && angle < angle2;
You'll probably have to play around a bit depending on whether angle1 > angle2 or vice versa. If there's any chance that any of the angles cross the zero-degree angle, it gets a little trickier.
Meta: For clarity, I used sqrt() to compute distance, but you can optimize this code by skipping the sqrt() and compare distance² instead:
double dist2 = x*x + y*y;
if (dist2 > circle1.radius * circle1.radius &&
dist2 < circle2.radius * circle2.radius &&
...
One more edit: computing trig functions can be expensive; certainly a lot more expensive than computing distance².
In the interest of optimization, you should check the distance against the circle radii before bothering with the trig:
boolean touch = dist > circle1.radius && dist < circle2.radius;
if (touch) {
// This is only a *possible* touch, check the angles now
double angle = Math.atan2(y,x) * 180 / Math.PI;
touch = angle > angle1 && angle < angle2;
}
Is there a way (a library) with which one can give the (contour) lines of AWT-2D-objects a "hand-drawn" look (somehow shaky: not exactly following the "official" path, not having perfect line contours)
(The official path should be followed almost perfectly, with just some "random" noise. The line contours should be almost perfect, with just some "random" noise.)
Use the SloppyStroke from here: http://www.java2s.com/Code/Java/2D-Graphics-GUI/CustomStrokes.htm
/**
* This Stroke implementation randomly perturbs the line and curve segments that
* make up a Shape, and then strokes that perturbed shape. It uses PathIterator
* to loop through the Shape and GeneralPath to build up the modified shape.
* Finally, it uses a BasicStroke to stroke the modified shape. The result is a
* "sloppy" looking shape.
*/
class SloppyStroke implements Stroke {
BasicStroke stroke;
float sloppiness;
public SloppyStroke(float width, float sloppiness) {
this.stroke = new BasicStroke(width); // Used to stroke modified shape
this.sloppiness = sloppiness; // How sloppy should we be?
}
public Shape createStrokedShape(Shape shape) {
GeneralPath newshape = new GeneralPath(); // Start with an empty shape
// Iterate through the specified shape, perturb its coordinates, and
// use them to build up the new shape.
float[] coords = new float[6];
for (PathIterator i = shape.getPathIterator(null); !i.isDone(); i
.next()) {
int type = i.currentSegment(coords);
switch (type) {
case PathIterator.SEG_MOVETO:
perturb(coords, 2);
newshape.moveTo(coords[0], coords[1]);
break;
case PathIterator.SEG_LINETO:
perturb(coords, 2);
newshape.lineTo(coords[0], coords[1]);
break;
case PathIterator.SEG_QUADTO:
perturb(coords, 4);
newshape.quadTo(coords[0], coords[1], coords[2], coords[3]);
break;
case PathIterator.SEG_CUBICTO:
perturb(coords, 6);
newshape.curveTo(coords[0], coords[1], coords[2], coords[3],
coords[4], coords[5]);
break;
case PathIterator.SEG_CLOSE:
newshape.closePath();
break;
}
}
// Finally, stroke the perturbed shape and return the result
return stroke.createStrokedShape(newshape);
}
// Randomly modify the specified number of coordinates, by an amount
// specified by the sloppiness field.
void perturb(float[] coords, int numCoords) {
for (int i = 0; i < numCoords; i++)
coords[i] += (float) ((Math.random() * 2 - 1.0) * sloppiness);
}
}
Once I tried to do something similar with jhlabs. Please take a look at the WobbleStroke sample at: http://www.jhlabs.com/java/java2d/strokes/
In general - you have to implement the java.awt.Stroke class. So better search for appropriate implementations.
Here is a good sample of a brush stroke as well:
http://javagraphics.blogspot.com/2007/04/strokes-brush-stroke.html
In order to build a tic-tac-toe game for testing, I have following routine. But problem is that I am getting too many events for just one touch. I suspect isTouched() returns all of down, up, and move. Is there any way to just get up event?
UPDATE: Resolved the issue by employing justTouched() instead.
#Override
public void render() {
// we update the game state so things move.
updateGame();
// First we clear the screen
GL10 gl = Gdx.graphics.getGL10();
gl.glViewport(0, 0, width, height);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
// Next we update the camera and set the camera matrix
camera.update();
camera.apply(Gdx.gl10);
...
}
private void updateGame() {
// the delta time so we can do frame independant time based movement
float deltaTime = Gdx.graphics.getDeltaTime();
// Has the user touched the screen? then position the paddle
if (Gdx.input.isTouched() && !isProcess) {
// get the touch coordinates and translate them
// to the game coordinate system.
isProcess=true;
int width = Gdx.graphics.getWidth();
int height = Gdx.graphics.getHeight();
int offx=-width/2;
int offy=-height/2;
float x = Gdx.input.getX();
float y = Gdx.input.getY();
float touchX = 480 * (x
/ (float) width - 0.5f);
float touchY = 320 * (0.5f - y
/ (float) height);
for(int i=0;i<3;i++) {
for(int j=0;j<3;j++)
{
if(touchX >= offx+i*width/3 && touchX < offx+(i+1)*width/3 &&
touchY >= offy+j*height/3 && touchY < offy+(j+1)*height/3)
{
if(isCurrentO)
data[i][j]=CellStatus.O;
else
data[i][j]=CellStatus.X;
isCurrentO=!isCurrentO;
break;
}
}
}
isProcess=false;
}
}
An alternative to using justTouched is to implement the InputProcessor interface, as it has a touchUp(x,y,pointer,button) which gives you greater control over the input. There are several classes that implement this or you can have your class implement it.
You can create a board for example (with hash map) and each object in your game wants to be clickable add itself to that board if an object was touched and was in board it will catch the event. If not it will not catch the event. So easy! :)