AffineTransform: centered zooming - java

I'm doing a small program as a school assignment. Its rather simple; open an image in a JPanel and then be able to zoom (in or out) and translate using the mouse dragged. I got the translation working so far and the zoom works but its not centered. I would like the zoom to be centered, even after a translation was done. Here is my class so far:
package modele;
import java.awt.geom.AffineTransform;
import java.util.Observable;
import java.io.Serializable;
import vue.JPanelImage;
public class Perspective extends Observable implements Serializable {
private static final long serialVersionUID = 1L;
private JPanelImage panneauSource;
private AffineTransform transformation;
private double zoom = 1;
public Perspective(JPanelImage panneauImage) {
this.panneauSource = panneauImage;
this.transformation = new AffineTransform();
this.addObserver(panneauImage);
}
public AffineTransform getTransformation() {
return transformation;
}
public void setTransformation(AffineTransform transformation) {
this.transformation = transformation;
setChanged();
notifyObservers();
}
public void zoom(double wheelRotation) {
zoom = 1;
zoom += (.1 * -wheelRotation);
System.out.println("Zoom: " + zoom);
transformation.scale(zoom, zoom);
setChanged();
notifyObservers();
}
public void translate(double[] coordSouris) {
double newX = coordSouris[2] - coordSouris[0];
double newY = coordSouris[3] - coordSouris[1];
transformation.translate(newX*3, newY*3);
setChanged();
notifyObservers();
}
}
Any idea how to get this working? This project is made under the constraint of using various programming templates such as Command and Singleton. So the methods zoom and translate will be called at each mousescroll and mousedrag event. And sorry for the french!
Thanks for the help.

Related

JavaFX How can I use one instance of scene in 2 windows? (Mirroring)

I made a chess game that is supposed to run in 2 windows (stages). When a player moves a piece, in the second window the same piece moves accordingly. But the second window is rotated by 180 degrees to simulate a 2 player experience.
To realize this I thought it would be the easiest to use the 1 scene in 2 Windows. Basically a mirror of the first scene.
Problem: Figure doesn't move in the second window but the game knows that it has been moved in the first window because the player can't move the same figures again but the other color.
There is also a Main Menu, which has a play button, that starts the 2 windows.
#FXML
public void play_game(ActionEvent event) throws IOException {
Parent root = FXMLLoader.load(Objects.requireNonNull(getClass().getResource("game_board.fxml")));
Parent second_screen = FXMLLoader.load(Objects.requireNonNull(getClass().getResource("game_board.fxml")));
stage = (Stage)((Node)event.getSource()).getScene().getWindow();
scene = new Scene(root);
stage.setScene(scene);
stage.show();
Stage second_stage = new Stage();
second_stage.setScene(new Scene(second_screen));
second_screen.rotateProperty().set(180);
second_stage.show();
}
The **controller class ** for the first window:
public class board_controller {
#FXML
GridPane chess_board;
#FXML
GridPane second_board;
#FXML
public void initialize(){
game_logic game_logic = new game_logic(chess_board);
}
The controller class for the second window:
public class second_board_controller extends board_controller {
#FXML
public void initialize(){
game_logic new_round = new game_logic(chess_board);
}
}
My question is: How can I use the same exact instance of the scene but only rotated in the second window (Basically a Mirror of the first window)?
Game Example
Thank you!
I tried making the gridpane in the first_board controller static using it in the second board controller with the hopes of them updating automatically but with no results. Setting the main scene in first and second stages but my IDE said it's not allowed. I'm out of ideas...
Share the Model
Since you want to mirror between two windows in the same process, the general idea is to create two instances of your view but share only one instance of the model. The model should be observable in some way so that the view/controller can react to changes in the model by updating the view. With the model being shared and observed, updating it from one window will be seen by the other window.
Models
Note a model should not know about the view. In your code, you do:
game_logic game_logic = new game_logic(chess_board);
From the name of the class, this indicates you're passing a GridPane (the view) to your model. It would be better if your model only modelled a chess game. The controller/view is responsible for translating that state into a visual representation.
Rotating the Second View
The simplest approach to this would be to add state/a method to your controller, and then only on the second instance of the controller configure it to rotate the view. It is at least somewhat justifiable to put this logic in the controller/view because it is only a view thing (it does not affect the game state).
Though instead of rotating the board, you might want to consider "inverting" the location of the pieces (vertically). In other words, for the second view, have it so that a white piece in the bottom-left corner of the board is actually displayed in the top-left corner (and the opposite for black pieces). That way the chess piece images are not rotated along with the rest of the board.
Example
Here's a proof-of-concept for mirroring a draggable rectangle (much simpler than a chess game). Note it only demonstrates the mirroring, it does not show how to e.g., rotate the view in one window but not the other.
RectangleModel.java:
package sample;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;
public class RectangleModel {
private final List<Consumer<? super Dimensions>> listeners = new CopyOnWriteArrayList<>();
private Dimensions dimensions;
public RectangleModel(Dimensions dimensions) {
this.dimensions = Objects.requireNonNull(dimensions);
}
public RectangleModel(double x, double y, double width, double height) {
this(new Dimensions(x, y, width, height));
}
public void move(double deltaX, double deltaY) {
if (deltaX != 0.0 || deltaY != 0.0) {
double x = dimensions.x() + deltaX;
double y = dimensions.y() + deltaY;
double w = dimensions.width();
double h = dimensions.height();
dimensions = new Dimensions(x, y, w, h);
notifyListeners();
}
}
public Dimensions getDimensions() {
return dimensions;
}
public void addDimensionsListener(Consumer<? super Dimensions> listener) {
listeners.add(Objects.requireNonNull(listener));
}
public void removeDimensionsListener(Consumer<? super Dimensions> listener) {
listeners.remove(Objects.requireNonNull(listener));
}
private void notifyListeners() {
for (var listener : listeners) {
listener.accept(dimensions);
}
}
public record Dimensions(double x, double y, double width, double height) {}
}
RectangleController.java:
package sample;
import java.util.function.Consumer;
import javafx.fxml.FXML;
import javafx.geometry.Point2D;
import javafx.scene.input.MouseEvent;
import javafx.scene.shape.Rectangle;
public class RectangleController {
private final Consumer<RectangleModel.Dimensions> listener = this::updateRectangle;
#FXML
private Rectangle rectangle;
private Point2D offset;
private RectangleModel model;
public void setModel(RectangleModel model) {
if (this.model != null) {
this.model.removeDimensionsListener(listener);
}
this.model = model;
if (model != null) {
model.addDimensionsListener(listener);
updateRectangle(model.getDimensions());
} else {
updateRectangle(null);
}
}
private void updateRectangle(RectangleModel.Dimensions dims) {
if (dims != null) {
rectangle.setX(dims.x());
rectangle.setY(dims.y());
rectangle.setWidth(dims.width());
rectangle.setHeight(dims.height());
} else {
rectangle.setX(0);
rectangle.setY(0);
rectangle.setWidth(0);
rectangle.setHeight(0);
}
}
#FXML
private void handleMousePressed(MouseEvent event) {
event.consume();
offset = new Point2D(event.getX(), event.getY());
}
#FXML
private void handleMouseDragged(MouseEvent event) {
event.consume();
double deltaX = event.getX() - offset.getX();
double deltaY = event.getY() - offset.getY();
model.move(deltaX, deltaY);
offset = new Point2D(event.getX(), event.getY());
}
#FXML
private void handleMouseReleased(MouseEvent event) {
event.consume();
offset = null;
}
}
RectangleView.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.shape.Rectangle?>
<Pane xmlns="http://javafx.com/javafx/" xmlns:fx="http://javafx.com/fxml/"
fx:controller="sample.RectangleController">
<Rectangle fx:id="rectangle" onMousePressed="#handleMousePressed" onMouseDragged="#handleMouseDragged"
onMouseReleased="#handleMouseReleased"/>
</Pane>
Main.java:
package sample;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
var model = new RectangleModel(0, 0, 100, 50);
primaryStage.setScene(createScene(model));
primaryStage.setTitle("Primary Stage");
primaryStage.show();
var secondStage = new Stage();
secondStage.setScene(createScene(model));
secondStage.setTitle("Second Stage");
secondStage.show();
primaryStage.setX(primaryStage.getX() - primaryStage.getWidth() / 2);
secondStage.setX(primaryStage.getX() + primaryStage.getWidth());
secondStage.setY(primaryStage.getY());
primaryStage.setOnCloseRequest(e -> secondStage.close());
secondStage.setOnCloseRequest(e -> primaryStage.close());
}
private Scene createScene(RectangleModel model) throws IOException {
var loader = new FXMLLoader(Main.class.getResource("RectangleView.fxml"));
var root = loader.<Parent>load();
var controller = loader.<RectangleController>getController();
var scene = new Scene(root, 600, 400);
controller.setModel(model);
return scene;
}
}
Naming Conventions
You should follow the standard naming conventions of Java (or whatever language you're using) when posting on a public forum.
Classes and interfaces use PascalCase.
Methods, fields, parameters, and local variables use camelCase.
Static constants use UPPER_SNAKE_CASE.

Moving a circle on a map using Observer pattern

I have a (probably an easy) problem with my java program. I'm trying to create a circle that will move from one place to another. The map is a part of an easy dialogue game that says "go here" and the map should react to it. It has to be using an Observer design pattern.
So far I have the map in the game implemented but I just can't fathom how to make the circle function as it should, while using Observer, too. Thanks for any help
package GUI;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import logika.Game;
import logika.IGame;
import main.Main;
import utils.Observer;
public class Mapa extends AnchorPane implements Observer{
private IGame game;
private Circle dot;
//private double posTop = 0.0;
//private double posLeft = 0.0;
public Mapa(IGame game){
this.game = game;
game.getGamePlan().registerObserver(this);
init();
}
private void init(){
ImageView obrazekImageView = new ImageView(new Image(Main.class.getResourceAsStream("/zdroje/mapa.gif"),255,255,false,true));
tecka = new Circle(10, Paint.valueOf("red"));
//this.setTopAnchor(tecka, 15.0);
//this.setLeftAnchor(tecka, 20.0);
this.getChildren().addAll(obrazekImageView, dot);
update();
}
public void newGame(IGame newGame){
game.getGamePlan().removeObserver(this);
game= newGame;
game.getGamePlan().registerObserver(this);
update();
}
#Override
public void update(){
this.setTopAnchor(this, game.getGamePlan().getCurrentPlace().getPosTop());
this.setLeftAnchor(this, game.getGamePlan().getCurrentPlace().getPosLeft());
}
}
You should implement the pattern like seen in this UML diagram from Wikipedia:
That is you will have an interface like CommandObserver with a method like notify that is called on each observer every time the event occurs. The event contains the exact command and all stuff that is neccessary to move the circle around. I'll assume you can move it by only having the reference to the circle. All in all it may look like
public interface CommandObserver {
void notify(String command, Circle circle);
}
Next the invoker (let's call it CommandReceiver) of the event must have some register method where all observers can be registered. And it must have some kind of notifyObservers that it calls if the event occurs:
public class CommandReceiver {
// The reference to the circle
private Circle circle = ...
private Set<CommandObserver> observers = new HashSet<>();
public void registerObserver(CommandObserver observer) {
observers.add(observer);
}
public void unregisterObserver(CommandObserver observer) {
observers.remove(observer);
}
private void notifyObserver(String command, Circle circle) {
for (CommandObserver observer : observers) {
observer.notify(command, circle);
}
}
// If a command was entered
public void commandEntered(String command) {
notifyObserver(command, circle);
}
}
And last you will implement observers for all possible commands like:
public PositionMoveObserver implements CommandObserver {
private int x;
private int y;
private String command;
public PositionMoveObserver(int x, int y, String command) {
this.x = x;
this.y = y;
this.command = command;
}
#Override
public void notify(String command, Circle circle) {
// Not interested in, reject
if (!this.command.equals(command)) {
return;
}
// Move the circle to our destination
circle.moveTo(x, y);
}
}
And then create and register it for every location:
private void createObservers(CommandReceiver invoker) {
invoker.registerObserver(new PositionMoveObserver(0, 5, "bottomLeft"));
invoker.registerObserver(new PositionMoveObserver(100, 5, "bottomRight"));
invoker.registerObserver(new PositionMoveObserver(0, 50, "middleLeft"));
...
}
Note that for a specific command only one observer should listen to it, otherwise multiple instances will move the circle around and the result will depend on the iteration order of invokers HashSet.

Creating a java method that can continuously update variables

I've been taking AP Computer Science this year as a sophomore in high school and we mainly cover material like loops, classes, methods, general CS logic, and some math stuff. I am missing what I really loved about coding in the first place, making games. Now every game I have made had some sort of way to manage it whether it was using timers in visual basic or a XNA plugin for c# that setup a update method for me. The problem is I have not learned how to do this for java in my course. I've read up a little on threads and implements runnable but i'm not really sure where I'm going with it.
Class 1
import java.awt.FlowLayout;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class GFXScreen extends JFrame
{
/**
* #param screenHeigth
* #param screenHeigth
* #param The file name of the image. Make sure to include the extension type also
* #param The title at the top of the running screen
* #param The height of the screen
* #param The width of the screen
*/
public GFXScreen(String fileName, String screenTitle, int screenHeight, int screenWidth)
{
setLayout(new FlowLayout());
image1 = new ImageIcon(getClass().getResource(fileName));
label1 = new JLabel(image1);
this.add(label1);
//Set up JFrame
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setVisible(true);
this.setTitle(screenTitle);
this.setSize(screenWidth, screenHeight);
}
/**
* #param desired amount to move picture
*/
public void updatePic(int increment)
{
//update pos
label1.setBounds(label1.bounds().x, label1.bounds().y - increment,
label1.bounds().width, label1.bounds().height);
}
private ImageIcon image1;
private JLabel label1;
}
Class 2
public class MainClass implements Runnable {
public static void main(String[] args)
{
(new Thread(new MainClass())).start();
GFXScreen gfx = new GFXScreen("pixel_man.png", "pixel_Man", 1000, 1000);
}
public void run()
{
gfx.updatePic(1);
}
}
In this instance what I want to happen is, I want a picture that starts in the top to slowly move down smoothly to the bottom. How would i do this?
Suggestions:
Again, a Swing Timer works well for simple Swing animations or simple game loops. It may not be the greatest choice for complex or rigorous tame loops as its timing is not precise.
Most game loops will not be absolutely precise with time slices
And so your game model should take this into consideration and should note absolute time slices and use that information in its physics engine or animation.
If you must use background threading, do take care that most all Swing calls are made on the Swing event thread. To do otherwise will invite pernicious infrequent and difficult to debug program-ending exceptions. For more details on this, please read Concurrency in Swing.
I avoid using null layouts, except when animating components, as this will allow my animation engine to place the component absolutely.
When posting code here for us to test, it's best to avoid code that uses local images. Either have the code use an image easily available to all as a URL or create your own image in your code (see below for a simple example).
Your compiler should be complaining to you about your using deprecated methods, such as bounds(...), and more importantly, you should heed those complaints as they're there for a reason and suggest increased risk and danger if you use them. So don't use those methods, but instead check the Java API for better substitutes.
Just my own personal pet peeve -- please indicate that you've at least read our comments. No one likes putting effort and consideration into trying to help, only to be ignored. I almost didn't post this answer because of this.
For example:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
#SuppressWarnings("serial")
public class GfxPanel extends JPanel {
private static final int BI_WIDTH = 26;
private static final int BI_HEIGHT = BI_WIDTH;
private static final int GAP = 6;
private static final Point INITIAL_LOCATION = new Point(0, 0);
private static final int TIMER_DELAY = 40;
public static final int STEP = 1;
private ImageIcon image1;
private JLabel label1;
private Point labelLocation = INITIAL_LOCATION;
private int prefW;
private int prefH;
private Timer timer;
public GfxPanel(int width, int height) {
// the only time I use null layouts is for component animation.
setLayout(null);
this.prefW = width;
this.prefH = height;
// My program creates its image so you can run it without an image file
image1 = new ImageIcon(createMyImage());
label1 = new JLabel(image1);
label1.setSize(label1.getPreferredSize());
label1.setLocation(labelLocation);
this.add(label1);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(prefW, prefH);
}
public void startAnimation() {
if (timer != null && timer.isRunning()) {
timer.stop();
}
labelLocation = INITIAL_LOCATION;
timer = new Timer(TIMER_DELAY, new TimerListener());
timer.start();
}
// My program creates its image so you can run it without an image file
private Image createMyImage() {
BufferedImage bi = new BufferedImage(BI_WIDTH, BI_HEIGHT,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = bi.createGraphics();
g2.setColor(Color.red);
g2.fillRect(0, 0, BI_WIDTH, BI_HEIGHT);
g2.setColor(Color.blue);
int x = GAP;
int y = x;
int width = BI_WIDTH - 2 * GAP;
int height = BI_HEIGHT - 2 * GAP;
g2.fillRect(x, y, width, height);
g2.dispose();
return bi;
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
int x = labelLocation.x + STEP;
int y = labelLocation.y + STEP;
labelLocation = new Point(x, y);
label1.setLocation(labelLocation);
repaint();
if (x + BI_WIDTH > getWidth() || y + BI_HEIGHT > getHeight()) {
System.out.println("Stopping Timer");
((Timer) e.getSource()).stop();
}
}
}
private static void createAndShowGui() {
final GfxPanel gfxPanel = new GfxPanel(900, 750);
JButton button = new JButton(new AbstractAction("Animate") {
#Override
public void actionPerformed(ActionEvent arg0) {
gfxPanel.startAnimation();
}
});
JPanel buttonPanel = new JPanel();
buttonPanel.add(button);
JFrame frame = new JFrame("GFXScreen");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(gfxPanel);
frame.getContentPane().add(buttonPanel, BorderLayout.PAGE_END);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
What I always use is an infinite loop that calls an update method each iteration, in that method, you would do whatever was required to update the state of the game or render a GUI.
Example
public static void main(String[] args){
// Initialise game
while(true){
updateGame();
}
}
public static void updateGame(){
// Update things here.
}
What I also do ,which is a little more complex, is create and interface called IUpdateListener and have certain classes that are specialised for a certain element of the game. I would example have an InputListener, an AIListener, each handling a certain element of game updating.
public interface IUpdateListener{
public void update();
}
public class Main{
public static ArrayList<IUpdateListener> listeners = new ArrayList<IUpdateListener>();
public static void main(String[] args){
listeners.add(new InputListener());
while(true){
for(IUpdateListener listener : listeners){
listener.update();
}
}
}
}
public class InputListener implements IUpdateListener{
public void update(){
// Handle user input here etc
}
}

Java Physics loop (repaint on interval) is choppy unless mouse is moving

I have a simple physics loop that does a calculation for a time interval, waits for the interval to pass, and then renders the results on the screen. It's very simple code (even though the timing is probably wrong, but that's exactly what I'm trying to learn about) and works well when I am moving the mouse around the screen.
package physicssim;
import java.awt.Graphics;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class PhysicsSim extends JFrame {
private static class PhysicsObject {
public PhysicsObject(double x, double y, double v_x, double v_y) {
this.x = x;
this.y = y;
this.v_x = v_x;
this.v_y = v_y;
}
public double x;
public double y;
public double v_x;
public double v_y;
}
PhysicsObject particle;
boolean running = true;
DrawPane drawPane;
public PhysicsSim() {
particle = new PhysicsObject(10,10, .1, .2);
drawPane = new DrawPane(particle);
this.setSize(800,600);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setContentPane(drawPane);
this.setVisible(true);
}
private static class DrawPane extends JPanel {
PhysicsObject p;
public DrawPane(PhysicsObject p) {
this.p = p;
}
#Override
public void paint(Graphics g) {
super.paint(g); //To change body of generated methods, choose Tools | Templates.
g.fillOval((int)p.x, (int) p.y, 10, 10);
}
}
public void start() {
int FPS = 60;
long TIME_BETWEEN_FRAMES_NS = 1000000000/FPS;
// Initial draw
drawPane.repaint();
long lastDrawTime = System.nanoTime();
while(running) {
// Update physics
particle.x+=particle.v_x*(TIME_BETWEEN_FRAMES_NS*.0000001);
particle.y+=particle.v_y*(TIME_BETWEEN_FRAMES_NS*.0000001);
// While there is time until the next draw wait
while(TIME_BETWEEN_FRAMES_NS > (System.nanoTime()-lastDrawTime)) {
try {
Thread.sleep(1);
} catch (InterruptedException ex) {
Logger.getLogger(PhysicsSim.class.getName()).log(Level.SEVERE, null, ex);
}
}
drawPane.repaint();
long currentTime = System.nanoTime();
System.out.println(currentTime - lastDrawTime);
lastDrawTime = currentTime;
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
PhysicsSim sim = new PhysicsSim();
sim.start();
}
}
The last bit about printing the time difference was just a sanity check to make sure that it was in fact calling around the requested interval. The results are fairly consistent so I don't see why there should be any choppiness.
As I mentioned above, this code works great if I a moving the mouse around the screen, everything is smooth.
If I am not moving the mouse it becomes very choppy until I start moving the mouse over the application.
I assume this is something simple, but I hope that you guys can help me. Thank you.
Alright, it looks like my problem was I was drawing directly to g in paint(). After replacing with the following everything worked correctly.
#Override
public void paint(Graphics g) {
BufferedImage img = new BufferedImage(800, 600, BufferedImage.TYPE_3BYTE_BGR);
img.getGraphics().fillOval((int) p.x, (int) p.y, 10, 10);
g.drawImage(img, 0, 0, null);
}
I was considering deleting this code snippet because it's rough and shameful, but maybe it will help someone else. Happy coding.

Cannot remove shapes in Java using mouseEvents

I am trying to make an original game in Eclipse and I am having some trouble writing code that will remove GObjects and change the properties of others, using the mouseClicked method.
The problem seems to be that the private instant variables are not being recognised in the mousePressed method.
Can someone please advise on what I'm getting wrong here? I have spent a whole day on this and some help will be greatly appreciated.
Kind regards,
Mehul
*/*
* File: Linesv1.java
* 07/08/2012
*
*/
import acm.graphics.*;
import acm.program.*;
import acm.util.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.event.MenuEvent;
public class Linesv1 extends GraphicsProgram {
/** Width and height of application window in pixels */
public static final int BACKGROUND_WIDTH = 400;
public static final int BACKGROUND_HEIGHT = 600;
/** Dimensions of game board (usually the same) */
private static final int WIDTH = BACKGROUND_WIDTH;
private static final int HEIGHT = BACKGROUND_HEIGHT;
/** Dimensions of triangle*/
private static final int LENGTH_OF_TRIANGLE_SIDE = 100;
/** Dimensions of arc height*/
private static final int ARC_HEIGHT = 100;
/** Dimensions of radius of switches*/
private static final int SWITCH_RADII = 5;
// private instant variables
private GObject switchA;
private GOval switchB;
private GObject triangle;
private GObject bottomArc;
public void run() {
addMouseListeners();
setUpGame();
}
public void setUpGame(){
//add central triangle
GPolygon triangle = new GPolygon (WIDTH/2,HEIGHT/2);
triangle.addVertex(0,-LENGTH_OF_TRIANGLE_SIDE*2/3);
triangle.addVertex(LENGTH_OF_TRIANGLE_SIDE/2,+LENGTH_OF_TRIANGLE_SIDE*1/3);
triangle.addVertex(-LENGTH_OF_TRIANGLE_SIDE/2,+LENGTH_OF_TRIANGLE_SIDE*1/3);
triangle.setFilled(true);
triangle.setFillColor(Color.green);
add(triangle);
//add topArc
GArc bottomArc = new GArc (WIDTH/2-LENGTH_OF_TRIANGLE_SIDE/2, HEIGHT/2+LENGTH_OF_TRIANGLE_SIDE*1/3-ARC_HEIGHT/2, ARC_HEIGHT,ARC_HEIGHT,0,-180);
bottomArc.setFilled(true);
bottomArc.setFillColor(Color.green);
add(bottomArc);
//add switches to the bottom of the triangle
GOval switchA = new GOval (WIDTH/2-LENGTH_OF_TRIANGLE_SIDE/2-SWITCH_RADII, HEIGHT/2+LENGTH_OF_TRIANGLE_SIDE*1/3-SWITCH_RADII,SWITCH_RADII*2,SWITCH_RADII*2);
switchA.setFilled(true);
switchA.setFillColor(Color.black);
add(switchA);
//add switches to the bottom of the triangle
GOval switchB = new GOval (WIDTH/2+LENGTH_OF_TRIANGLE_SIDE/2-SWITCH_RADII, HEIGHT/2+LENGTH_OF_TRIANGLE_SIDE*1/3-SWITCH_RADII,SWITCH_RADII*2,SWITCH_RADII*2);
switchB.setFilled(true);
switchB.setFillColor(Color.black);
add(switchB);
}
public void mousePressed(MouseEvent e) {
findObject(e.getX(),e.getY());
GObject check = findObject(e.getX(),e.getY());
if (check!=null){
remove(triangle);
switchA.setColor(Color.cyan);
}
}
private GObject findObject(int a, int b){
if(getElementAt(a,b) != null) {
return getElementAt(a,b);
} else {
return null;
}
}
}*
The triangle you are trying to remove is not the one you added. You created a local GPolygon called triangle in setupGame() and added that, not the private member.
private GObject triangle; // this is uninitialized
public void setUpGame(){
GPolygon triangle = new GPolygon (WIDTH/2,HEIGHT/2); // this is hiding your member variable
// THIS IS LOCAL COPY, NOT MEMBER VARIABLE
add(triangle);
And then later you try to remove the uninitialized member variable, so nothing happens. You probably don't want a separate local copy of triangle. You probably want
public void setUpGame() {
triangle = new GPolygon (WIDTH/2, HEIGHT/2);
// ...
add(triangle); // Now, you are adding the member
}

Categories