I'm using a HorizontalGroup in LibGDX which works fine. I want to create a row of tiles. But when I try to position the group using setPosition() method, it doesn't position as I hope, that is to say, it doesn't render using my "y" value. Here is the code:
a = new Tile(missingwords.getAM().get("a2.png", Texture.class), "a", 3);
b = new Tile(missingwords.getAM().get("a2.png", Texture.class), "a", 3);
c = new Tile(missingwords.getAM().get("a2.png", Texture.class), "a", 3);
a.setSize(50, 50);
b.setSize(50, 50);
c.setSize(50, 50);
submitGroup = new HorizontalGroup();
submitGroup.addActor(a);
submitGroup.addActor(b);
submitGroup.addActor(c);
submitGroup.setPosition((Gdx.graphics.getWidth() - submitGroup.getMinWidth()) / 2, 0);
stage.addActor(submitGroup);
Here is my Tile code:
public class Tile extends Actor {
private String letter;
private int points;
private Texture texture;
public Tile(Texture tileTexture, String letter, int points) {
this.letter = letter;
this.points = points;
texture = tileTexture;
}
#Override
public void draw(Batch batch, float parentAlpha) {
batch.draw(texture, getX(), getY(), getWidth(), getHeight());
}
As you can see, I use:
submitGroup.setPosition((Gdx.graphics.getWidth() - submitGroup.getMinWidth()) / 2, 0);
This means that it should render at y = 0. However, take a look at this screenshot:
The tiles are out of the screen and I don't know why. What am I missing?
Thanks in advance.
Looks like the reason is the default alignment in the HorizontalGroup: Align.center. Because of this the Tile y positions are -25.0 (which is the Tile actor's height / 2). So for example if you would create a Tile with height of 100 then the y position of the tile in your HorizontalGroup would be -50.
I see two ways of positioning the tiles like you want it:
Fixing the Alignment of the HorizontalGroup to bottom: submitGroup.align(Align.bottom);
by just setting the y position of the HorizontalGroup to 25 instead of 0;
Related
I want to create a shape Object(in my case the object is a rectangle). Each time I click a button. Currently, I'm able to make it appear just once. The idea would be that each time I click the button, a new rectangle Object is created, additional to the old one. Therefore, if I click the button 5 times, I should have 5 rectangles.
I tried to do it with an ArrayList, but still, there is just one rectangle appearing. Does someone know how to do it!
Thank you very much in advance!
This is the main class, FYI there is also a rectangle Class(not attached)
import controlP5.*;
ControlP5 cp5;
Rectangle rect; // rect begins as null
Button rc;
ArrayList<Rectangle> rectList;
void setup(){
size(1000, 1000);
rectList = new ArrayList<Rectangle>();
cp5 = new ControlP5(this);
rc = cp5.addButton("Rectangle").
setPosition(5, 4).
setColorBackground(color(52, 55, 76));
rc.onRelease(new CallbackListener() {
public void controlEvent(CallbackEvent theEvent) {
// only create the rectangle when the button is clicked
rect = new Rectangle(100, 100, 100, 100);
}
});
}
void draw(){
background(255);
// if the rect exists, draw it on the screen
if(rect != null) {
rect.displayRect();
showRect();
}
for(int i = 0; i < rectList.size(); i++){
//((Rectangle)rectList.get(i)).update();
((Rectangle)rectList.get(i)).displayRect();
}
}
public void showRect(){
for(Rectangle r: rectList){
r.displayRect();
rect(r.getXvalue(), r.getYvalue(), r.getWvalue(), r.getHvalue());
}
}
You have a list, but you never add anything to that list. The list remains empty.
Drop the member field rect, delete this line:
Rectangle rect; // rect begins as null
When you instantiate a new Rectangle, immediately add it to the list.
rc.onRelease( new CallbackListener() {
public void controlEvent(CallbackEvent theEvent) {
// When the button is clicked, instantiate a new rectangle and remember it by adding to our list of rectangles.
rectList.add(
new Rectangle( 100, 100, 100, 100 )
);
}
});
Some chiding: This was not a good Question for Stack Overflow. You could easily have found this bug by using a debugger to step through the code. You would have seen that the list remains empty. Before posting, do your own debugging exhaustively.
You should post your Rectangle class to make it easier for others to test and help out.
As Basil pointed out (+1) you're only rendering a new rectangle for one frame when there's a click event.
The idea would be that each time I click the button, a new rectangle Object is created, additional to the old one. Therefore, if I click the button 5 times, I should have 5 rectangles.
This statement is a bit ambiguous. I get you'd like to render a rectangle per click, however in the click handler the rect has the exact same dimensions and coordinates. Even if you would make minor fixes, rendering 5 identical rectangles on top of each other will likely appear as if it's a single rectangle.
Regarding the code you posted, this stands out to me:
Rectangle rect; // rect begins as null: what is the purpose of the rect if you have this underneath: ArrayList<Rectangle> rectList; ?
showRect(); is called in draw(): it loops over rectList and not only calls displayRect() which I'd assume would render the current rect, but also re-renders the same data on the following line (rect(r.getXvalue(), r.getYvalue(), r.getWvalue(), r.getHvalue());)
underneath, there's a for loop over the same list calling displayRect() yet again. My guess is 2 out 3 calls to render rectangles are redundant. (Also the array list is typed therefore, no need to cast like this: (Rectangle)rectList.get(i)), rectList.get(i) should suffice)
The only other minor caveat I have is around naming: ideally you would want to stick to Java naming conventions in Processing. (For example getXValue() instead of getXvalue(), etc.)
Regarding the ControlP5 button you could use controlEvent() which is a bit simpler than setting a callback. Even simpler is to use this automatic variable plugging functionality. In short, if a function has the same name as a button's name it will be called automatically:
Automatic controller-event detection
ControlP5 offers a range of controllers that allow you to easily change and adjust values while your sketch is running. Each controller is identified by a unique name assigned when creating a controller. ControlP5 locates variables and functions inside your sketch and will link controllers to matching variables or functions automatically
(from controlP5 reference)
Here's a basic example that prints a message to console each time the button is clicked:
import controlP5.*;
ControlP5 cp5;
void setup() {
size(1000, 1000);
cp5 = new ControlP5(this);
cp5.addButton("rectangle").
setPosition(5, 4).
setColorBackground(color(52, 55, 76));
}
void draw(){
background(255);
}
void rectangle(){
println("rectangle button clicked");
}
(I've kept the name rectangle instead of Rectangle to keep in line with Java naming conventions. The text label is displayed in uppercase anyway)
Back to your main question, if you want to add new rectangle per button press and render them the code be as simple as:
import controlP5.*;
ControlP5 cp5;
ArrayList<Rectangle> rectList = new ArrayList<Rectangle>();
int x = 100;
int y = 100;
void setup() {
size(1000, 1000);
rectList = new ArrayList<Rectangle>();
cp5 = new ControlP5(this);
cp5.addButton("rectangle").
setPosition(5, 4).
setColorBackground(color(52, 55, 76));
}
void draw() {
background(255);
for (int i = 0; i < rectList.size(); i++) {
rectList.get(i).display();
}
}
void rectangle(){
rectList.add(new Rectangle(x, y, 100, 100));
// increment x, y to avoid superimposed rectangles
x += 50;
y += 50;
}
class Rectangle{
private int x, y, w, h;
Rectangle(int x, int y, int w, int h){
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
void display(){
rect(x, y, w, h);
}
// currently not used
public int getX(){
return x;
}
public int getY(){
return x;
}
public int getWidth(){
return x;
}
public int getHeight(){
return x;
}
}
Rectangle rect; stands out. If this was an OOP homework assignment or exercise perhaps the intention was to have a basic drawing app functionality where the user could have a rectangle drawing tool ?
If that's the case rect could be selection rectangle which could cloned into rectList so it persists.
You could implement rectangle selection like so:
when the mouse is pressed remember the coordinates: these are the starting point of the selection
as the mouse is dragged the end point coordinates are the current mouse coordinates, hence the selection rectangle dimensions are the difference between the current mouse coordinates and the previously stored mouse coordinates
as the mouse is released, reset the selection rectangle (so it no longer displays)
Here's a basic example sketch:
Rectangle selection = new Rectangle(0, 0, 0, 0);
void setup(){
size(1000, 1000);
}
void draw(){
background(255);
selection.display();
}
void mousePressed(){
// store selection start
selection.x = mouseX;
selection.y = mouseY;
}
void mouseDragged(){
// update selection dimension as the difference between the current mouse coordinates and the previous ones (selection x, y)
selection.w = mouseX - selection.x;
selection.h = mouseY - selection.y;
}
void mouseReleased(){
selection.w = selection.h = selection.x = selection.y = 0;
}
class Rectangle{
private int x, y, w, h;
Rectangle(int x, int y, int w, int h){
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
void display(){
rect(x, y, w, h);
}
}
Resetting the Rectangle properties to 0 could be nicely encapsulated into a method:
void reset(){
x = y = w = h = 0;
}
The release handler would also be useful to add a new rectangle to rectList which has the same properties (x, y, w, h) as the selection, but before the selection is reset. something like:
void mouseReleased(){
// add a copy of the selection to rectList
rectList.add(new Rectangle(selection.x, selection.y, selection.w, selection.h));
// reset selection
selection.reset();
}
Again, the copy functionality is something that could be nicely encapsulated as a method as well:
Rectangle copy(){
return new Rectangle(x, y, w, h);
}
Putting it all together would look like this:
import controlP5.*;
ControlP5 cp5;
ArrayList<Rectangle> rectList = new ArrayList<Rectangle>();
Rectangle selection = new Rectangle(0, 0, 0, 0);
void setup() {
size(1000, 1000);
rectList = new ArrayList<Rectangle>();
cp5 = new ControlP5(this);
cp5.addButton("rectangle").
setPosition(5, 4).
setColorBackground(color(52, 55, 76));
}
void draw() {
background(255);
// draw previous rectangles (black)
stroke(0);
for (int i = 0; i < rectList.size(); i++) {
rectList.get(i).display();
}
// draw current selection (green)
stroke(0, 192, 0);
selection.display();
}
void rectangle(){
println("selected drawing tool is rectangle");
}
void mousePressed(){
// store selection start
selection.x = mouseX;
selection.y = mouseY;
}
void mouseDragged(){
// update selection dimension as the difference between the current mouse coordinates and the previous ones (selection x, y)
selection.w = mouseX - selection.x;
selection.h = mouseY - selection.y;
}
void mouseReleased(){
// add a new rectangle to the list: a copy of the selection
rectList.add(selection.copy());
// reset selection
selection.reset();
}
class Rectangle{
private int x, y, w, h;
Rectangle(int x, int y, int w, int h){
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
void display(){
rect(x, y, w, h);
}
Rectangle copy(){
return new Rectangle(x, y, w, h);
}
void reset(){
x = y = w = h = 0;
}
}
At this stage the button is a bit redundant, however it could useful in the future if other shapes are required (e.g. an Ellipse would be an obviously simple one to implement since ellipse() has the same parameters as rect(), just need to ensure ellipseMode(CORNER) is set to use the same selection x,y coordinates the rectangle does)
Hopefully this is useful. The initial code, as mentioned before looks a bit messy, as if it's put together in a haste before a deadline.
(I assume this because it reminds me of my code as a student :))
I'd recommend taking a short walk away from the computer, remembering what the task is and pen on paper breaking the task into small, clear, easy to implement subtasks. Once that is as clear as it can be, implement one subtask at a time in isolation. Initially the code may break or get messy, but eventually it will work (and it will be easier to write compared to the whole drawing program). Once that works, cleanup the code so that all unnecessary code is removed and it's easy to move the code to another sketch and run it without errors. Repeat the process for each subtask which should result in multiple minimal sketches solving one single problem. Once all parts are solved in isolation you can start putting the code together into one main sketch, however I recommend adding one subtask code as a time and testing first. When mixing code from multiple sketches conflicts/errors may arise and it will far easier to tackle merging two sketches a time than all of the subtask sketches in one go. Best of luck !
I followed this tutorial on Youtube, and I have successfully added colors to the black and white picture. However, my intention was to create a multi-color or gradient effect (like here or here) instead of switching colors when I move the cursor.
I very new at processing, and I have tried to play with the variable, with no success.
Here is the code snippet of the sketch:
`
PImage img;
void setup() {
size(598,336);
colorMode(HSB);
img = loadImage("picture-in-data-folder.jpg");
img.resize(598,336);
//ellipseMode(RADIUS);
frameRate(30);
}
void draw() {
background(255);
noStroke();
// fill(0);
float tiles = mouseX/10;
float tileSize = width/tiles;
// color section
fill(color(tiles, 255, 255));
tileSize++;
if (tiles > width / 2) {
tileSize = 0;
}
// end color section
translate(tileSize/2, tileSize/2);
for (int x = 0; x < tiles; x++) {
for (int y = 0; y < tiles; y++) {
color c = img.get(int(x*tileSize),int(y*tileSize));
float size = map(brightness(c), 0, 255, tileSize, 0);
ellipse(x*tileSize, y*tileSize, size, size);
// image(img, mouseX, mouseY);
}
}
}
I would be grateful if you had any hints, or if you could provide an advice.
Thanks.
Short answer: you need to put a fill() command inside the for loop.
Long answer:
Right now, your code is doing the following:
Define tiles based on mouseX
Set the fill color to (tiles, 255, 255)
Draw all the circles
I think what you want it to do is something like this:
Set the fill color to (21, 255, 255) (or whatever you want the first color to be)
draw the first circle
set the fill color to the next color in the gradient
draw the second circle
etc.
In order to do this, you need to put a command into the for loop which changes the fill color. Here is one way to do that:
for (int x = 0; x < tiles; x++) {
for (int y = 0; y < tiles; y++) {
color c = img.get(int(x*tileSize),int(y*tileSize));
float size = map(brightness(c), 0, 255, tileSize, 0);
fill(map(x, 0, tiles, 0, 255), 255, 255);
ellipse(x*tileSize, y*tileSize, size, size);
}
}
I just added that fill command as a function of x, but you can make it whatever you want. In order for it to be a gradient, it needs to vary somewhat with x or y.
I am using a ShapeRenderer to draw circles at specific X,Y coordinates, starting at 0,0 and filling out to 50,50 with a gap between.
Am i missing something obvious here?
I have created an Array of points like this:
[[0,0],[0,2]...[50,48],[50,50]]
Here is the relevant code:
public void create() {
camera = new OrthographicCamera();
viewport = new FitViewport(100, 100, camera);
renderer = new ShapeRenderer();
grid = new PointGrid(50, 50);
}
public void render() {
Gdx.gl.glClearColor(0,0,0,0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
renderer.begin(ShapeRenderer.ShapeType.Filled);
renderer.setProjectionMatrix(camera.combined);
renderer.setColor(Color.WHITE);
for (int i = 0; i < grid.getPoints().size; i++) {
Vector2 point = grid.getPoints().get(i);
renderer.circle(point.x, point.y, 1, 32);
}
renderer.end();
camera.update();
}
When you use a Viewport to manage your camera, you need to call viewport.update(...) in your resize method.
public void resize (int width, int height) {
viewport.update(width, height, true);
}
Passing true to the update method will put 0,0 at the bottom left of the screen instead of the center.
If you want gaps between your circles, you need a smaller radius or a bigger step between them. Right now you have a 2-unit step between them and they have a 2-unit diameter so they are touching each other.
Pretty sure camera is by default looking at 0,0 which means it will put 0,0 at the center.
So you want to set the position of your camera at - SCREEN_WIDTH / 2, - SCREEN_HEIGHT / 2
You can get Screen size with Gdx.graphics.getWidth() / getHeight()
I successfully added an oval shape to the canvas, however now i'd like to add two more rectangles but for some reason they do not get added to the canvas. The oval shape is a ball which moves and rectangle shapes are static elements which are for "background". One rectangle should be as a floor and another one as an obstacle for the moving object, ball.
I tried to visualize it in the image:
This is the code, mBack and mObs are the rectangles i'm trying to add.
AnimatedView animatedView = null;
ShapeDrawable mDrawable = new ShapeDrawable();
ShapeDrawable mBack = new ShapeDrawable();
ShapeDrawable mJump = new ShapeDrawable();
public static int x;
public static int y;
public class AnimatedView extends ImageView {
static final int width = 50;
static final int height = 50;
public AnimatedView(Context context) {
super(context);
// TODO Auto-generated constructor stub
mDrawable = new ShapeDrawable(new OvalShape());
mBack = new ShapeDrawable(new RectShape());
mObs = new ShapeDrawable(new RectShape());
mDrawable.getPaint().setColor(0xffffAC23);
//mDrawable.setBounds(x, y, x + width, y + height);
mDrawable.setBounds(y, x, y + width, x + height);
mBack.setBounds(100, 100, 100, 100);
mObs.setBounds(120,120,120,120);
}
#Override
protected void onDraw(Canvas canvas) {
mDrawable.setBounds(y, x, y + width, x + height);
mBack.draw(canvas);
mDrawable.draw(canvas);
invalidate();
}
}
mDrawable will be added however mBack or mObs not. Adding setBounds to onDraw won't change a thing also.
The way you are setting Bounds is wrong. The definition of the setBounds method is here:
setBounds(int left, int top, int right, int bottom)
for the two rectangles you are setting it as
mBack.setBounds(100, 100, 100, 100);
mObs.setBounds(120,120,120,120);
This means you are left and right corners are same and top and bottom are same so you are not seeing your rectangle.
Set it something like this then you will see your rectangles
mBack.setBounds(100, 100, 300, 400);
And call draw method on both rectangle shapes in onDraw method.
It seems the problem is that for mBack you defined the bounds to zero pixel (start end ends in (100,100)), same for mObs, but for that you didn't call draw either.
I need to know in which way I can color the following image (PNG) by using JavaFX. This image is currently included in a ImageView of JavaFX:
I want to color region 1 blue, the second one red, and the last two purple. How can I do this in JavaFX? Isn't there some kind of function as in Windows Paint? (You know, the painting bucket that fills a certain area with a color between borders).
Suggested Approach
You can use a flood fill algorithm.
Sample Code
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.image.*;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.util.Stack;
public class UnleashTheKraken extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(final Stage stage) {
Image original = new Image(
"http://s12.postimg.org/wofhjvy2h/image_2.jpg"
);
WritableImage updateable = new WritableImage(
original.getPixelReader(),
(int) original.getWidth(),
(int) original.getHeight()
);
Kraken kraken = new Kraken(updateable, Color.WHITE);
kraken.unleash(new Point2D(40, 40), Color.BLUE);
kraken.unleash(new Point2D(40, 100), Color.RED);
kraken.unleash(new Point2D(100, 100), Color.GREEN);
kraken.unleash(new Point2D(120, 40), Color.YELLOW);
ImageView originalView = new ImageView(original);
ImageView filledView = new ImageView(updateable);
HBox layout = new HBox(10, originalView, filledView);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
class Kraken {
private final WritableImage image;
private final Color colorToFill;
// tolerance for color matching (on a scale of 0 to 1);
private final double E = 0.3;
public Kraken(WritableImage image, Color colorToFill) {
this.image = image;
this.colorToFill = colorToFill;
}
public void unleash(Point2D start, Color color) {
PixelReader reader = image.getPixelReader();
PixelWriter writer = image.getPixelWriter();
Stack<Point2D> stack = new Stack<>();
stack.push(start);
while (!stack.isEmpty()) {
Point2D point = stack.pop();
int x = (int) point.getX();
int y = (int) point.getY();
if (filled(reader, x, y)) {
continue;
}
writer.setColor(x, y, color);
push(stack, x - 1, y - 1);
push(stack, x - 1, y );
push(stack, x - 1, y + 1);
push(stack, x , y + 1);
push(stack, x + 1, y + 1);
push(stack, x + 1, y );
push(stack, x + 1, y - 1);
push(stack, x, y - 1);
}
}
private void push(Stack<Point2D> stack, int x, int y) {
if (x < 0 || x > image.getWidth() ||
y < 0 || y > image.getHeight()) {
return;
}
stack.push(new Point2D(x, y));
}
private boolean filled(PixelReader reader, int x, int y) {
Color color = reader.getColor(x, y);
return !withinTolerance(color, colorToFill, E);
}
private boolean withinTolerance(Color a, Color b, double epsilon) {
return
withinTolerance(a.getRed(), b.getRed(), epsilon) &&
withinTolerance(a.getGreen(), b.getGreen(), epsilon) &&
withinTolerance(a.getBlue(), b.getBlue(), epsilon);
}
private boolean withinTolerance(double a, double b, double epsilon) {
return Math.abs(a - b) < epsilon;
}
}
}
Answers to additional questions
But wouldn't the image be colored pixel by pixel?
Yes, that's the point, you need to shade the pixels. Everything in computer graphics with bitmapped displays eventually comes down to coloring pixels.
Is this an efficient way in coloring?
It's instantaneous (as far as I can tell) on the sample image you provided. Space-wise it takes up some memory, but all such algorithms will use memory. The sample code I provided is not the most efficient flood fill shading algorithm which could be devised (time or space wise). The wikipedia page I linked has alternate more efficient (and more complicated) algorithms you could apply if you needed to.
Alternate Approach
If you have a cut-out stencil shape for each area, you could stack the stencils and apply ColorAdjust effects to them (such as in: How to change color of image in JavaFX). The ColorAdjust is (likely) a hardware accelerated effect. This alternate is not a general approach though as it requires you to know the stencil shapes.
Shape circle = new Circle(x,y,r);
Shape rect = new Rectangle(x,y,w,h);
Shape region1 = Shape.subtract(circle, rect);// to "cut" the rect away from a circle.
// You'll need to do this twice for each piece.
region1 = Shape.subtract(region1,anotherRect);
region1.setFill(Color.BLUE);
// Then simply add your shape to a node and set it's translation.
The way this works is that where the rectangle overlaps the circle, that part of the circle will be removed.