#Override
public Shape getShape() {
final Rectangle2D.Double result = new Rectangle2D.Double();
result.setFrameFromDiagonal(getStart(), getEnd());
//FIX this is causing square to move when going opposite direction
result.setRect(result.getX(), result.getY(),
result.getHeight(), result.getHeight());
return result;
}
So here is my code that is drawing a square using Rectangle2D.Double. The getStart() and getEnd() are points that are being returned from mouseDrag events. When I drag to the right or up, it works as intended and creates a square. When I drag left or down the square moves with the drag as it draws. I am fairly new to Java swing and paint components. Wondering if anyone know what is causing this and why?
You need to consider a few specialities:
Save the first coordinate on mouse click: x,y
Save the last coordinate on mouse drag x2,y2
Set min x and y coordinates as the startpoint for setRect: Math.min(x,x2);
Use the absolute value of the coordinate difference to calculate the height and width of rectangle: Math.abs(x-x2);
px = Math.min(x,x2);
int py = Math.min(y,y2);
int pw=Math.abs(x-x2);
int ph=Math.abs(y-y2);
result.setRect(px, py, pw, ph);
Related
I'm working on something that involves clicking specific points on a buffered image in a JPanel. I had issues with this earlier in the project (affine transform translation not working properly), but nothing I found fixed it so I decided I would come back to it later.
I'm not entirely sure how to trouble shoot it since I'm a novice, but I think it's reading my y coordinates too low. I made a mouse input listener that tracks the number of times the user has clicked and gets the mouse pointer's location for functions I haven't made yet. For testing I have it output the coordinates and number of clicks then make a circle centered where the mouse clicks.
#Override
public void mouseClicked(MouseEvent e) {
Point mouseCursor = MouseInfo.getPointerInfo().getLocation();
panel.drawCenteredCircle(mouseCursor.getX(), mouseCursor.getY(), 100);
System.out.println(String.valueOf(mouseCursor));
System.out.println(String.valueOf(clickCount));
clickCount++;
}
Here is drawCenteredCircle in my custom panel class:
public void drawCenteredCircle(double x, double y, int r) {
imgG2 = image.createGraphics();
imgG2.setPaint(Color.RED);
x = (x-r/2.0);
y = (y-r/2.0);
imgG2.fillOval((int)Math.round(x), (int)Math.round(y), r, r);
this.repaint();
imgG2.dispose();
}
I tried taking a screenshot to show what happens, but the circle properly centers on the x coordinate, but not the y coordinate. Instead it draws the circle with the pointer at the top center edge.
I overrided the paintComponent of my JPanel to implement a zoom feature:
#Override
protected void paintComponent(Graphics g) {
//Implimenting zoom
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g.create();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
/*Supposed to counter the movement from the scale, not working properly
int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
double x = (w - scale * imageWidth)/2;
double y = (h - scale * imageHeight)/2;*/
AffineTransform at = new AffineTransform()/*.getTranslateInstance(x, y) */;
at.scale(scale, scale);
g2.drawRenderedImage(image, at);
//g2.dispose(); I was told to put this, but I'm not sure if it's necessary or what it does entirely
}
My confused notes are because I got this code from an example someone made and, as I said earlier, the affine translation wasn't working (I took the actual translation out). They're irrelevant to the question.
The reason I put this is because I initially had code that was meant to fit the image to the screen/frame depending if it was fullscreen or not:
int x = image.getWidth();
int y = image.getHeight();
double frameW = frame.getBounds().getWidth();
double frameH = frame.getBounds().getHeight();
//Rectangle winSize = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
double screenW = Toolkit.getDefaultToolkit().getScreenSize().getWidth();
double screenH = Toolkit.getDefaultToolkit().getScreenSize().getHeight();
if (!isFullScreen) {
if (x/y > frameW/frameH) {
scale = frameW/x;
} else {
scale = frameH/y;
}
} else {
if (x/y > screenW/screenH) {
scale = screenW/x;
} else {
scale = screenH/y;
}
}
It uses my zoom function which scales the image with the double "scale." I noticed that when I zoomed in or out, it would change where the dots would appear relative to the pointer. It wasn't until I removed the code for the image to start fitted to the window and had it start at 100% that I received the result of the pointer being at the top center of the circle.
I also tried removing the part that's supposed to center the circle and the result was the pointer being on the left side and having a gap between it and the top of the circle.
Sorry if this is too much stuff. I'm pretty novice and learned just as much about java (the only coding language I know) working on this project as I knew when I first started it. I'm not sure what information I have that could be helpful in this, so I just threw in everything I thought could help. I appreciate any help, even irrelevant to my question.
I'm trying to build a Drawing Program using Processing. I am currently stuck on using PGrapchics.
When the user draws a rectangle, it shows the shape being drawn. When the user releases their mouse, it then creates a PGraphic of the final shape. I would then like the user to draw on top of that. Here is my problem:
I had to reset the background of the canvas when drawing a rectangle because otherwise, it shows a trail of rectangles. The result is that while the user draws a new rectangle the old ones disappear and come back once the mouse has been releasd
Some thoughts:
I would also like to add features where the user can select on a previously drawn rectangle and change it's colour, stroke, send to back, bring to front etc..
To achieve this, I'm storing all drawn rectangles (PGraphics) into an ArrayList which will be drawn via a for loop. This will allow me to adjust the behaviour by moving the PGraphics elements up and down the ArrayList.
PS: Instead of creating my own class of Shape am I better off using PShape?
int startX;
int startY;
int endX;
int endY;
boolean drawing;
int strokeW = 3;
Shape shape;
PGraphics shapeLayer;
ArrayList<PGraphics> layersList = new ArrayList();
void setup() {
size(500, 500);
cursor(CROSS);
background(255);
smooth();
}
void draw() {
strokeWeight(strokeW);
if (key >= '0' && key <= '9') {
strokeW = key - '0';
}
for(int i = 0; i < layersList.size(); i++) {
image(layersList.get(i), 0, 0);
}
if (drawing) {
shape.createRectangle();
}
}
void mousePressed() {
startX = mouseX;
startY = mouseY;
shapeLayer = createGraphics(width, height);
shapeLayer.beginDraw();
}
void mouseDragged() {
drawing = true;
endX = constrain(mouseX, 0, 500);
endY = constrain(mouseY, 0, 500);
shape = new Shape(startX, startY, endX, endY);
shapeLayer.clear();
}
void mouseReleased() {
drawing = false;
shapeLayer.endDraw();
layersList.add(shapeLayer);
}
Here is the Shape Class:
class Shape {
int startX;
int startY;
int endX;
int endY;
Shape(int x1, int y1, int x2, int y2) {
startX = x1;
startY = y1;
endX = x2;
endY = y2;
}
void createRectangle() {
background(255, 0);
shapeLayer.strokeWeight(strokeW);
shapeLayer.rectMode(CORNERS);
shapeLayer.rect(startX, startY, endX, endY);
rectMode(CORNERS);
rect(startX, startY, endX, endY);
}
}
In the future, please try to narrow your problem down to a MCVE before you post. For example you could have hard-coded it to draw a rectangle when the user drags instead of including all the code for every shape.
But your problem is caused by drawing to the screen and never clearing it out. You need to break your problem down into smaller pieces and then approach those pieces one at a time.
Step 1: Can you create a sketch that just shows a rectangle as you drag, but has the rectangle go away when you let go of the mouse? Start over with a basic sketch that does just this one thing, and get it working perfectly before you move on to the next step.
Step 2: Can you draw shapes to an off-screen buffer? It looks like you've tried this in your current code, but note that you never actually draw any shapes to your buffer, and you never actually draw your buffer to the screen. Again, start with a basic sketch that just does this. Don't even worry about user input or anything yet, just get a hard-coded rectangle drawn to an off-screen buffer, then draw that off-screen buffer to the screen.
Step 3: Can you combine those two to show the rectangle when you're drawing it, then draw it to the off-screen buffer when the user lets go?
Step 4: Only when you have the rectangle working perfectly, then move on to other shapes.
This is how programming works: you have to break your problem down into small steps like this, and then you have to approach each step in isolation. If you get stuck, you can come back with an MCVE showing just one of these steps, and we'll go from there. Good luck.
In addition to Kevin's answer: it does look like you are using another PGraphics buffer to draw into, but the whole sketch could be simpler.
Unless you need an undo/redo mode, where remembering the drawing commands and their order is needed, you can get away with something slightly simpler.
You can find a detailed answer with commented code showing something very similar. You simply need to add the pencil and line modes.
I'm working in a game with some friends in which we have a large horizontal world and a OrthographicCamera that shows only 1/3 of it. This camera it's moved when the horizontal position of the player change so the camera only move to the left and to the right.
Some of the objects showed in the game are near the player point-of-view but others are far away (for example, islands). With this in consideration, we cannot set fixed positions for elements and move only the camera. We need to achieve a parallax effect taking in consideration the distance of the elements.
Here is a simple image to explain it better:
The viewport to the left shows 3 objects of the game. The green one is near the player, the red ellipse is far and the yellow one is in the middle. In the viewport to the right the camera has been moved to the right so all the objects disappear to the left. The thing is that the relative movement of the green rectangle is greater than the movement of the yellow. In the same way, movement of yellow object is greater than red object movement.
I created all my assets scaled taking in consideration how far they are but now, how can I simulate this perspective using libGDX? Is there any class to do it? If I have to set elements position in each iteration, how could I calculate the right position?
Note that the example below is not tested as I am just recalling how I did it. The idea is simple - create layers with an extra layer for each with initial positions and velocity and move them. If a layer goes off the edge, put another one (that is why we create an extra layer) at the opposite edge.
Say you have a parallax object that takes initial positions, size, and velocity-
public class Parallax extends DynamicGameObject {
public float width, height; // Use setter/getter if you prefer
public Parallax(float x, float y, float width, float height, float velocityX, float velocityY) {
super(x, y, width, height);
velocity.set(velocityX, velocityY);
this.width = width;
this.height = height;
}
public void update(float deltaTime) {
position.add(velocity.x * deltaTime, velocity.y * deltaTime);
}
public void setPosition(float x, float y) {
position.set(x, y);
}
}
DynamicGameObject is taken from SuperJumper demo-
public class DynamicGameObject extends GameObject {
public final Vector2 velocity;
public final Vector2 accel;
public DynamicGameObject(float x, float y, float width, float height) {
super(x, y, width, height);
velocity = new Vector2();
accel = new Vector2();
}
}
GameObject as well-
public class GameObject {
public final Vector2 position;
public final Rectangle bounds;
public GameObject(float x, float y, float width, float height) {
this.position = new Vector2(x,y);
this.bounds = new Rectangle(x - width/2f, y - height/2f, width, height);
}
}
Say we have two layers - one in front and the other goes at back. We have one texture for each. Each texture fills the entire screen. We create two instances for each layer so that when one texture starts going off the screen, the other shows up at the edge to fill the gap. If you have smaller textures, you need to determine first how many textures you need to fill the screen and then create layers with one extra to fill the gap in between.
We can create an array of parallax layers during world creation-
Array<Parallax> parallaxList = new Array<Parallax>(4);
We can create the layers like this-
// Back
/* First parallax for back layer is at 0 x-axis. If you want to move the texture from right to left, the value of BACK_VELOCITY_X should be negative. You can experiment with velocity value for desire pace of movement. We do not want our layer to move on y-axis. Hence, it is set to 0. */
parallaxList.add(new Parallax(0, BACK_TEXTURE_HEIGHT, BACK_TEXTURE_WIDTH, BACK_TEXTURE_HEIGHT, BACK_VELOCITY_X, 0));
/* This one is also for back layer but it is positioned at the right edge of the layer above*/
parallaxList.add(new Parallax(BACK_TEXTURE_WIDTH, BACK_TEXTURE_HEIGHT, BACK_TEXTURE_WIDTH, BACK_TEXTURE_HEIGHT, SOME_VELOCITY_X, 0));
// Front
parallaxList.add(new Parallax(0, 0, FRONT_TEXTURE_WIDTH, FRONT_TEXTURE_HEIGHT, FRONT_VELOCITY_X, 0));
parallaxList.add(new Parallax(FRONT_TEXTURE_WIDTH, 0, FRONT_TEXTURE_WIDTH, FRONT_TEXTURE_HEIGHT, FRONT_VELOCITY_X, 0));
We update the layers on an update call in each frame-
// In our example, TOTAL_LAYERS is 4
for (int i = 0; i < TOTAL_LAYERS; i++) {
int tmpInt;
Parallax parallax = parallaxList.get(i);
parallax.update(deltaTime);
// If one layer is off the edge, put it at the right of the next one
// In this example, layers are moving from right to left
if (parallax.position.x <= -parallax.width) {
// We know that parallaxList's indexes 0 and 1 hold the back layers
// and indexes 2 and 3 have the front layers. You can add additional
// parameters in Parallax class to indicate a group so that you do not
// have to determine the group in dirty way like this
if(i == 0){
tmpInt = 1;
} else if(i == 1) {
tmpInt = 0;
} else if(i == 2) {
tmpInt = 3;
} else {
tmpInt = 2;
}
parallax.setPosition(parallaxList.get(tmpInt).position.x + parallax.width, parallax.position.y);
}
}
You can use an OrthographicCamera and a SpriteBatch to draw the parallax layers. You can actually use the game camera you have but I think using a separate camera is much cleaner. Anyways, parallax textures are usually big enough to be batched in a separate call so using the game camera most probably will not save you a draw call.
I want to make a game where you can build stuff by dragging and dropping objects into place. I think LibGDX only supports DragNDrop on Actors, but I need physics on bricks in order to make them fall down if the construction is not stable.
So far, my approach to drag and drop is:
for(Brick b : map.getList()){
final Image im = new Image(b.ar);
stage.addActor(im);
im.setPosition(b.posX, b.posY);
im.setOrigin(b.posX, b.posY);
im.addListener((new DragListener() {
public void touchDragged (InputEvent event, float x, float y, int pointer) {
im.setOrigin(x, y);
im.setPosition(x, y);
//System.out.println("touchdragged ---> X=" + x + " , Y=" + y);
}
}));
}
where the map.getLists contains all bricks to be painted. b.ar is the texture to be painted.
With this aproach [this] is what happens. I don't know what may be causing it.
#Override
public void render(float delta) {
spritebatch.begin();
map.getWorld().step(1/60f, 6, 2);
renderer.render(map.getWorld(), camera.combined);
if(Gdx.input.justTouched()){
Vector3 touchPoint = new Vector3(Gdx.input.getX(), Gdx.input.getY(),0);
camera.unproject(touchPoint.set(Gdx.input.getX(), Gdx.input.getY(), 0));
System.out.println(touchPoint);
}
stage.draw();
spritebatch.end();
}
Of course i'd like to make the body fell (with the box 2d engine from libgdx) if you drop the object and it has nothing under it.
Thanks in advance
You're setting the origin in your listener callback to a screen coordinate. That is not going to work.
The origin is used to define the "center" of your object, so when you reposition it, Libgdx knows which part of the actor to put where. Generally the origin is either the bottom left corner of the object (I think this is the default) or its the center of the object.
I guess you may want to reset the origin so if someone taps on the left edge of a brick and then you reposition the object you'll reposition that point on the brick (and not reposition the bottom left corner of the brick). To do that you'll need to convert the screen coordinates into coordinates in the actor's space.
That's all somewhat icky though. I think you'd be better off just doing relative repositioning. Instead of trying to position the brick absolutely with setPosition just reposition it relatively:
im.setPosition(im.getX() + dx, im.getY() + dy);
Then it doesn't matter where the "origin" is.
You'll have to compute dx and dy in your listener based on the previous touch point.
It appears that the drag listener gives coordinates relative to the origin of the actor that is raising the event. That is a bit strange when you are moving that actor in response to the drag events, because the origin keeps changing. Essentially, I found that if I just move the actor by the x and y values of the event, it will follow the mouse or finger.
One improvement is to record the position that the drag started at and use it as an offset, so the pointer stays the same distance from the actor's origin.
Another option might be to add the listener to the stage instead of the button. I expect the coordinates would then be relative to the stage's origin, which is not changing. I haven't tried that technique.
Here's the code I used to drag a button horizontally:
DragListener dragListener = new DragListener() {
private float startDragX;
#Override
public void dragStart(
InputEvent event,
float x,
float y,
int pointer) {
startDragX = x;
}
#Override
public void drag(InputEvent event, float x, float y, int pointer) {
insertButton.translate(x - startDragX, 0);
}
};
dragListener.setTapSquareSize(2);
insertButton.addListener(dragListener);
If you want to drag something in two dimensions, just copy the x code for the y position.
I'm having problem trying to point my "arrow_sprite" to the touch position.
The result I want is that the arrow(the sprite that I want to rotate)will point to the touch position.
How can I acheive this?
You need to calculate the vector between the touch position and the sprite position. To do so you have to unproject the touch position received through your InputProcessor.
public class MyInputProcessor implements InputProcessor {
...
#Override
public boolean touchDown (int x, int y, int pointer, int button) {
Vector3 touchDown = new Vector3(x, y, 0);
camera.unproject(touchDown);
Vector3 spritePosition = new Vector3(yourSpriteX, yourSpriteY, 0);
spritePosition.sub(touchDown);
return false;
}
...
}
Now we have a vector pointing from your sprite to the touch position. From there you just need to calculate the rotation angle. One way would be to use Vector2.angle(), as such creating a Vector2 from the spritePosition Vector3.
If you want to draw in 2D, then you have to set your SpriteBatch to Camera.combined,
something like this : yourBatch.setProjectionMatrix(yourCamera.combined);
Then you must unproject the camera like "batch" said.
After, you must choose if you want to use a vector or 2d coordinate. Basically identycal ending for angle calculation :
First define a float degrees then make it like so in the touchDown method :
degrees = (float) ((Math.atan2 (touchPoint.x - arrowPosition.x, -(touchPoint.y - arrowPosition.y))*180.0d/Math.PI)+90.0f);
assuming you use a Vector3 or 2 for both input and arrow.
then when rendering the Arrow sprite :
Batch.draw(Arrow, x, y, originX, originY, width, height, scaleX, scaleY, degrees);
Hope this will be helpfull.
Just like Psilopat said, we define the degree by :
degrees = (float) ((Math.atan2 (touchPoint.x - arrowPosition.x, -(touchPoint.y - arrowPosition.y))*180.0d/Math.PI)+90.0f);
but if the rotation seems wrong, change the "+90.0f" at the end of line to something else. My arrow is pointing up, so I change it to "-180.0f"