Edit:Code is available on
github(https://github.com/goxr3plus/DragAdjustableLabel)
Question
I made a custom control in javaFX which I don't know how to name it. It actually does not exist in the javaFX packages but I saw it in other programs like LMMS where it's been used to control the volume.
The problem
When the mouse is reaching the top or bottom of the monitor screen nothing can be done. Have a look at the code to see what I'm talking about. How to solve the problem? Feel free to edit the title to a more appropriate ones.
import java.awt.Robot;
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class FX3 extends Application {
#Override
public void start(Stage stage) throws Exception {
// Root-BorderPane
BorderPane borderPane = new BorderPane(new VolumeLabel(0, 100));
borderPane.setStyle("-fx-background-color:black;");
// Scene
stage.setScene(new Scene(borderPane, 200, 200, Color.BLACK));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
// TODO SpecialLabel
class VolumeLabel extends Label {
int screenX, screenY, previousY, minimumValue, maximumValue;
IntegerProperty currentValue = new SimpleIntegerProperty(15);
/**
* Constructor
*/
public VolumeLabel(int minimumValue, int maximumValue) {
this.minimumValue = minimumValue;
this.maximumValue = maximumValue;
textProperty().bind(currentValue.asString().concat(" %"));
setStyle(
"-fx-background-color:white; -fx-text-fill:black; -fx-padding:-2 8 -2 8; -fx-background-radius: 15; -fx-font-size:20; -fx-cursor:open-hand;");
// MouseListeners
setOnMousePressed(m -> {
screenX = (int) m.getScreenX();
screenY = (int) m.getScreenY();
//setCursor(Cursor.NONE); //Uncommend this line to make the cursor invisible
});
setOnMouseDragged(m -> {
setCurrentValue(
getCurrentValue() + (m.getScreenY() == previousY ? 0 : m.getScreenY() > previousY ? -1 : 1));
previousY = (int) m.getScreenY();
});
setOnMouseReleased(m -> {
// When the mouse is released -> move it to the initial position
try {
new Robot().mouseMove(screenX, screenY);
} catch (Exception e) {
e.printStackTrace();
}
setCursor(Cursor.OPEN_HAND);
});
}
/**
* Returns the Current Value of VolumeLabel
*
* #return
*/
public int getCurrentValue() {
return currentValue.get();
}
/**
* Setting the Current Value of VolumeLabel
*
* #param value
*/
public void setCurrentValue(int value) {
//System.out.println("Value:" + value + ", CurrentValue:" + currentValue.get());
if (value >= minimumValue && value <= maximumValue)
currentValue.set(value);
}
}
}
Here is the answer(the code also in on github):
import java.awt.Robot;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Cursor;
import javafx.scene.control.Label;
import javafx.stage.Screen;
/**
* A custom Node which is working like a Slider. <br>
* <b>!</b> When you hold the mouse on it and drag upside the value is
* increased. <br>
* <b>!</b> When you hold the mouse on it and drag down side the value is
* decreased.
*
* <br>
* Usage:
*
* <pre>
* <code>
* //initialize
* DragAdjustableLabel dragAdjustableLabel = new DragAdjustableLabel(10, 0, 100);
*
* //add it for example to a BorderPane
* primaryStage.setScene(new Scene(new BorderPane(dragAdjustableLabel)));
*</code>
* </pre>
*
* #author GOXR3PLUS
* #version 1.0
*/
public class DragAdjustableLabel extends Label {
// Variables
private int screenX;
private int screenY;
private int previousY;
private int minimumValue;
private int maximumValue;
/**
* The current value of the DragAdjustableLabel
*/
private IntegerProperty currentValue;
/**
* Constructor
*
* #param minimumValue
* Minimum Value that the slider can have
* #param maximumValue
* Maximum Value that the slider can have
*/
public DragAdjustableLabel(int currentValue, int minimumValue, int maximumValue) {
this.currentValue = new SimpleIntegerProperty(currentValue);
this.minimumValue = minimumValue;
this.maximumValue = maximumValue;
// Add a costume style class
this.getStyleClass().add("drag-adjustable-label");
setCursor(Cursor.OPEN_HAND);
textProperty().bind(this.currentValue.asString().concat(" %"));
// when the mouse is pressed
setOnMousePressed(m -> {
screenX = (int) m.getScreenX();
screenY = (int) m.getScreenY();
setCursor(Cursor.NONE); // comment this line to make the cursor
// visible
});
// when the mouse is dragged
setOnMouseDragged(m -> {
// calculate the monitor height
double screenHeight = Screen.getPrimary().getBounds().getHeight();
// !if the mouse has reached the the top of the monitor
// or
// ! if the mouse has reached the bottom of the monitor
if (m.getScreenY() <= 0 || m.getScreenY() + 10 >= screenHeight) {
resetMouse();
return;
}
// Calculate the current value
setCurrentValue(
getCurrentValue() + (m.getScreenY() == previousY ? 0 : m.getScreenY() > previousY ? -1 : 1));
previousY = (int) m.getScreenY();
});
// when the mouse is released
setOnMouseReleased(m -> {
resetMouse();
setCursor(Cursor.OPEN_HAND);
});
}
/**
* Reset the mouse to the default position(which is the center of the
* element)
*
* #param x
* #param y
*/
private void resetMouse() {
try {
new Robot().mouseMove(screenX, screenY);
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* Set the current value
*
* #param value
*/
public void setCurrentValue(int value) {
// if the value is between the limits
if (value >= minimumValue && value <= maximumValue)
currentValue.set(value);
}
public int getCurrentValue() {
return currentValue.get();
}
public IntegerProperty currentValueProperty() {
return currentValue;
}
}
Related
I am trying to bounce my text in a window, inserted from a text file without modifying the inputs from the text. I have the code to create and bounce the text, but some of it is lost when trying to bounce on the wall. The code consists of 3 classes.
This creates the text:
public class MyShape extends ClosedShape {
//The width and height of the text (major and minor axis)
private int width, height;
private String bouncingText;
private double textFont;
/**
* Creates an oval.
* #param x The display component's x position.
* #param y The display component's y position.
* #param vx The display component's x velocity.
* #param vy The display component's y velocity.
* #param width The width of the text (in pixels).
* #param height The height of the text (in pixels).
* #param colour The line colour or fill colour.
* #param isFilled True if the oval is filled with colour, false if opaque.
*/
public MyShape (int insertionTime, int x, int y, int vx, int vy, String bouncingText, int width, int height, Color colour, boolean isFilled, double textFont) {
super (insertionTime, x, y, vx, vy, colour, isFilled);
this.width = width;
this.height = height;
this.bouncingText = bouncingText;
this.textFont = textFont;
}
/**
* Method to convert a text to a string.
*/
public String toString () {
String result = "This is a text shape\n";
result += super.toString ();
result += "Its width is " + this.width + " and its height is " + this.height + "\n";
return result;
}
/**
* #param width Resets the width.
*/
public void setWidth (int width) {
this.width = width;
}
/**
* #param height Resets the height.
*/
public void setHeight (int height) {
this.height = height;
}
public void setBouncingText (String bouncingText) {
this.bouncingText = bouncingText;
}
public void setTextFont (double textFont) {
this.textFont = textFont;
}
/**
* #return The width of the text.
*/
public int getWidth() {
return width;
}
/**
* #return The height of the text.
*/
public int getHeight() {
return height;
}
public String getBouncingText() {
return bouncingText;
}
public double getTextFont() {
return textFont;
}
public void pulsing() {
if (textFont > 20) {
textFont = 10;
}else {
textFont = 50;
}
}
/**
* Draw the text.
* #param g The graphics object of the drawable component.
*/
public void draw (GraphicsContext g) {
Font bouncingTextFont = new Font ("TimesNew", textFont);
g.setFill (colour);
g.setStroke( colour );
if (isFilled) {
g.fillText(bouncingText, xPos, yPos );
g.setFont(bouncingTextFont);
}
else {
g.strokeText(bouncingText, xPos, yPos );
g.setFont(bouncingTextFont);
}
}
}
This is the bouncing code:
import java.util.ArrayList;
import javafx.animation.Animation;
import javafx.animation.AnimationTimer;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.scene.shape.ArcType;
import javafx.stage.Stage;
import javafx.util.Duration;
public class BouncingShapesWindow {
private static final int ANIMATION_DELAY = 10;
private static final String FRAME_TITLE = "Shape Booooiiinggg Frame";
private GraphicsContext gc;
private Queue shapesToAdd;
private ArrayList<ClosedShape> activeShapes;
private int currentTime = 0;
private boolean flag=true;
private String filename;
public BouncingShapesWindow(GraphicsContext gc,String filename) {
this.gc=gc;
activeShapes=new ArrayList<ClosedShape>();
this.initShapes(filename);
this.insertShapes ();
drawShapes();
actionPerformed();
}
private void drawShapes () {
for (ClosedShape s : activeShapes)
{
s.draw(gc);
}
}
private void initShapes (String filename) {
shapesToAdd = ReadShapeFile.readFile(filename);
}
private void insertShapes() {
//no more shapes to add, we are done
if (shapesToAdd.isEmpty ()) {
return;
}
//add shapes if needed
ClosedShape current = (ClosedShape) shapesToAdd.peek ();
while (!shapesToAdd.isEmpty () && (current.getInsertionTime() <= currentTime*ANIMATION_DELAY)) {
activeShapes.add(current);
shapesToAdd.dequeue();
if (!shapesToAdd.isEmpty ()) {
current = (ClosedShape) shapesToAdd.peek();
}
}
}
public void actionPerformed()
{
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(5), ae -> onTime()));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
}
private void onTime() {
currentTime++;
double h =gc.getCanvas().getHeight();
double w = gc.getCanvas().getWidth();
gc.clearRect(0, 0, w, h);
moveShapes();
insertShapes ();
drawShapes();
}
public void moveShapes()
{
double dimsY = gc.getCanvas().getHeight() ;
double dimsX = gc.getCanvas().getWidth() ;
for (ClosedShape s : activeShapes)
{
s.move();
// Move us back in and bounce if we went outside the drawing area.
if (s.outOfBoundsX(dimsX))
{
s.putInBoundsX(dimsX);
s.bounceX();
}
if (s.outOfBoundsY(dimsY))
{
s.putInBoundsY(dimsY);
s.bounceY();
}
}
}
}
and this is the super class:
import javafx.scene.paint.Color;
import javafx.scene.canvas.GraphicsContext;
/**
* A ClosedShape is any shape that can be drawn without
* taking a pencil off a piece of paper.
* It's representation on computer has a line colour
* and a position on the drawable screen component.
* It can be filled in with colour or opaque.
* This class is a super class for all shapes.
*/
public class ClosedShape {
/**
* The x position of the Shape.
*/
protected int xPos;
/**
* The y position of the Shape.
*/
protected int yPos = 20;
/**
* The x position of the Shape.
*/
protected int xVec;
/**
* The y position of the Shape.
*/
protected int yVec;
/**
* The line colour of the shape, or the filled in
* colour if the Shape has fill.
*/
protected Color colour;
/**
* Determines if the Shape has a fill colour or not.
*/
protected boolean isFilled;
/**
* Encodes the insertion time into the scene
*/
private int insertionTime;
/**
* Creates a closed shape object.
* #param x The x position.
* #param y the y position.
* #param colour The line or fill colour.
* #param isFilled True if the shape is filled, false if not.
*/
protected ClosedShape (int insertionTime, int x, int y, int vx, int vy, Color colour, boolean isFilled) {
this.xPos = x;
this.yPos = y;
this.xVec = vx;
this.yVec = vy;
this.colour = colour;
this.isFilled = isFilled;
this.insertionTime = insertionTime;
}
/**
* The method returns a string suitable for printing.
* #return string to print out shape.
*/
public String toString () {
String result = "";
result += "Its position is " + xPos + " " + yPos + "\n";
result += "Its velocity is " + xVec + " " + yVec + "\n";
result += "Its colour is " + colour + "\n";
if (isFilled)
result += "It is filled" + "\n";
else
result += "It is not filled" + "\n";
result += "It should be inserted at " + insertionTime + "\n";
return result;
}
/**
* Resets the x position.
*/
public void setX (int x) {
this.xPos = x;
}
/**
* Resets the y position.
*/
public void setY (int y) {
this.yPos = y;
}
/**
* Resets the x vector
*/
public void setVecX (int x) {
this.xVec = x;
}//end setVecX
/**
* Resets the y position.
*/
public void setVecY (int y) {
this.yVec = y;
}//end setVecY
/**
* Resets the colour.
*/
public void setColour (Color colour) {
this.colour = colour;
}
/**
* Sets the shape to filled.
*/
public void setFilled () {
isFilled = true;
}
/**
* Sets the shape to unfilled.
*/
public void unsetFilled () {
isFilled = false;
}
/**
* Sets the insertion time.
*/
public void setInsertionTime (int time) {
insertionTime = time;
}
/**
* #return The x position value.
*/
public int getX() {
return xPos;
}
/**
* #return The y position value.
*/
public int getY() {
return yPos;
}
/**
* #return The colour.
*/
public Color getColour() {
return colour;
}
/**
* #return True if the shape is filled, false if not.
*/
public boolean isFilled() {
return isFilled;
}
/**
* #return the insertion time.
*/
public int getInsertionTime () {
return insertionTime;
}
/**
* Puts the shape back in bounds in X
*/
public void putInBoundsX (double winX) {
if (xPos < 0) xPos = 0;
if (xPos + this.getWidth() > winX) {
xPos = (int) (winX - Math.ceil (this.getWidth ()));
}
}//end inBoundsX;
/**
* Puts the shape back in bounds
*/
public void putInBoundsY (double winY) {
if (yPos < 0) yPos = 0;
if (yPos + this.getHeight() > winY) {
yPos = (int) (winY - Math.ceil (this.getHeight ()));
}
}//end inBoundsY;
/**
* Bounces the shape off a vertical wall
*/
public void bounceX () {
xVec = -xVec;
}
/**
* Bounces the shape off a horizontal wall
*/
public void bounceY () {
yVec = -yVec;
}
/**
* Returns true if the shapes have gone out of bounds in X
*/
public boolean outOfBoundsX (double winX) {
return (xPos + this.getWidth()> winX) || (xPos < 0);
}
/**
* Returns true if the shapes have gone out of bounds in Y
*/
public boolean outOfBoundsY (double winY) {
return (yPos + this.getHeight() > winY) || (yPos < 0);
}
/**
* Takes in a direction and a velocity and moves the shape
* in that direction on unit
*/
public void move () {
xPos += xVec;
yPos += yVec;
}
/**
* Draws the object to the current component.
* #param g The graphics object associated with the drawing component.
*/
public void draw (GraphicsContext g) {
System.out.println ("You forgot to override this method! (draw)");
System.out.println ("Don't modify this method.");
}
/**
* Get the width of the current component
*/
public int getWidth () {
System.out.println ("You forgot to override this method! (getWidth)");
System.out.println ("Don't modify this method.");
return 1;
}
/**
* Get the width of the current component
*/
public int getHeight () {
System.out.println ("You forgot to override a method! (getHeight)");
System.out.println ("Don't modify this method.");
return 1;
}
public int getSide () {
System.out.println ("You forgot to override this method! (getSide)");
System.out.println ("Don't modify this method.");
return 1;
}
}
I tried reversing it, but nothing works. Using other shapes like circle or rect is OK, but with text it bounces under the head of the window and if I change the font size it loses its boundaries. I tried searching for solutions and it seems that everything uses JFrame and JPanel. I hope the code I provided leads to a useful solution.
Can any of you FX Gurus tell me why this code appears to freeze FX screen updates? The loop is continuing to run in the animation thread, but after a while the screen stops updating.
I know, by the way, that using Thread.sleep in this way might upset some people, but this is code for students in an intro course to allow them to create animations without doing any event handling.
The exercise for students is to convert the animation to make it bounce an array of 100's of balls moving in random directions. After the conversion, the freeze tends to come much earlier than with a single ball.
Thanks in advance!
Here's the main class...
package week6code;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
/**
* Bouncing Balls exercise starter
*
* #author Sam Scott
*/
public class BouncingBalls extends Application {
/**
* Sets up the stage and starts the main thread. Your drawing code should
* NOT go here.
*
* #param stage The first stage
*/
#Override
public void start(Stage stage) {
stage.setTitle("Bouncing Balls!"); // window title here
Canvas canvas = new Canvas(400, 300); // canvas size here
Group root = new Group();
Scene scene = new Scene(root);
root.getChildren().add(canvas);
stage.setScene(scene);
stage.show();
GraphicsContext gc = canvas.getGraphicsContext2D();
// This code starts a "thread" which will run your animation
Thread t = new Thread(() -> animate(gc));
t.start();
}
/**
* Animation thread. This is where you put your animation code.
*
* #param gc The drawing surface
*/
public void animate(GraphicsContext gc) {
// YOUR CODE HERE!
// intial positions and speeds
Ball ball = new Ball(100, 50, -1, -1, 10, Color.RED);
while (true) // loop forever
{
// draw screen
gc.setFill(Color.YELLOW);
gc.fillRect(0, 0, 400, 300);
ball.draw(gc);
// moving
ball.moveOneStep();
// bouncing
if (ball.getX() <= 0 || ball.getX() >= 400 - (ball.getSize() - 1)) {
ball.bounceX();
}
if (ball.getY() <= 0 || ball.getY() >= 300 - (ball.getSize() - 1)) {
ball.bounceY();
}
// pause
pause(1000 / 60);
}
}
/**
* Use this method instead of Thread.sleep(). It handles the possible
* exception by catching it, because re-throwing it is not an option in this
* case.
*
* #param duration Pause time in milliseconds.
*/
public static void pause(int duration) {
try {
Thread.sleep(duration);
} catch (InterruptedException ex) {
}
}
/**
* Exits the app completely when the window is closed. This is necessary to
* kill the animation thread.
*/
#Override
public void stop() {
System.exit(0);
}
/**
* Launches the app
*
* #param args unused
*/
public static void main(String[] args) {
launch(args);
}
}
And here's the Ball class.
package week6solutions;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
/**
* An example of an object that can draw and move itself.
*
* #author Sam Scott
*/
public class Ball {
private double x, y, xSpeed, ySpeed;
private final int size;
private final Color c;
/**
* Creates a Ball instance.
*
* #param x Initial x position (left)
* #param y Initial y position (top)
* #param xSpeed Number of pixels to move horizontally in each step
* (negative for left, positive for right)
* #param ySpeed Number of pixels to move vertically in each step (negative
* for up, positive for down)
* #param size Diameter of ball
* #param c Color of ball
*/
public Ball(double x, double y, double xSpeed, double ySpeed, int size, Color c) {
this.x = x;
this.y = y;
this.xSpeed = xSpeed;
this.ySpeed = ySpeed;
this.size = size;
this.c = c;
}
/**
* Increment x and y using the values of xSpeed and ySpeed
*/
public void moveOneStep() {
x += xSpeed;
y += ySpeed;
}
/**
* Reverses the x direction by multiplying it by -1
*/
public void bounceX() {
xSpeed *= -1;
}
/**
* Reverses the y direction by multiplying it by -1
*/
public void bounceY() {
ySpeed *= -1;
}
/**
* Draw the ball in its current location on a Graphics object
*
* #param g The GraphicsContext object to draw on
*/
public void draw(GraphicsContext g) {
g.setFill(c);
g.fillOval((int) Math.round(x), (int) Math.round(y), size, size);
}
/**
* #return the current x location
*/
public double getX() {
return x;
}
/**
* #return the current y location
*/
public double getY() {
return y;
}
/**
* #return the size of the ball
*/
public int getSize() {
return size;
}
}
I still don't understand why you insist on teaching to do this the wrong way if the right way is so trivial and in fact very close to your code.
Just replace all the threading stuff with an AnimationTimer. Here is your updated main code. The rest just remains as before.
package week6code;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
/**
* Bouncing Balls exercise starter
*
* #author Sam Scott
*/
public class BouncingBallsDoneRight extends Application {
/**
* Sets up the stage and starts the main thread. Your drawing code should
* NOT go here.
*
* #param stage The first stage
*/
#Override
public void start(Stage stage) {
stage.setTitle("Bouncing Balls!"); // window title here
Canvas canvas = new Canvas(400, 300); // canvas size here
Group root = new Group();
Scene scene = new Scene(root);
root.getChildren().add(canvas);
stage.setScene(scene);
stage.show();
GraphicsContext gc = canvas.getGraphicsContext2D();
// This code starts an AnimationTimer which will run your animation
AnimationTimer at = new AnimationTimer() {
Ball ball = new Ball(100, 50, -1, -1, 10, Color.RED);
#Override
public void handle(long arg0) {
// draw screen
gc.setFill(Color.YELLOW);
gc.fillRect(0, 0, 400, 300);
ball.draw(gc);
// moving
ball.moveOneStep();
// bouncing
if (ball.getX() <= 0 || ball.getX() >= 400 - (ball.getSize() - 1)) {
ball.bounceX();
}
if (ball.getY() <= 0 || ball.getY() >= 300 - (ball.getSize() - 1)) {
ball.bounceY();
}
}
};
at.start();
}
/**
* Launches the app
*
* #param args unused
*/
public static void main(String[] args) {
launch(args);
}
}
I tried to stay as close to your original code as possible. An even better solution for the task would be to use the scene graph and not the canvas.
The main problem is that JavaFX is not thread-safe (https://docs.oracle.com/javafx/2/threads/jfxpub-threads.htm).
That is the reason of freeze. Each access, modification or drawing to UI should be done on UI thread.
That could be done with Platform.runLater().
Platform.runLater(() -> {
/*your code here*/
});
I have created an arc. I want to do certain things on different arcs when an arc is clicked. How do I know if an arc is touched or not? Can someone please provide some code for onTouch method to do such a calculation. Also please explain it a little bit.
package com.example.android.customviews;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
/**
* Limit Indicator is used to show any kind of limits such as Balance and Actual
* Amount of wallet present in an account. In order to use this in the XML
* Layout, please include the following: <br />
* <br />
*
* xmlns:custom="http://schemas.android.com/apk/res/com.example.android.customviews"
*
* <br /> <br />
*
* Following custom attributes are provided: <br />
* <br />
*
* custom:borderColor <br />
* custom:borderRadius <br />
* custom:outerCircleRadius <br />
* custom:text <br />
* custom:textSize <br />
* custom:innerCircleColor <br />
*
* #author Syed Ahmed Hussain
*/
public class LimitIndicator extends ViewGroup {
// ============================================================================================
// Variables Declaration
private int mInnerCircleColor;
private int mBorderColor;
private int mTextColor;
private float mTextSize;
private String mTitleText = "";
private float mHalfOfBorderWidth = 0.0f;
private float mOuterCircleRadius = 2.0f;
private float mBorderWidth = 30.0f;
private Paint mDialPaint, mTextPaint, mBorderPaint, mInnerCirclePaint;
private float mCenterX = 100.0f;
private float mCenterY = 100.0f;
private int mTotalProgressInDegrees;
private int mTotalProgress = -1;
// Start Angle should be 90 degrees to create a clockwise illusion.
private int mStartAngle = 270;
// This should be the one which provides us a percentage wise drawing
private int mSweepAngle = 1;
private RectF mBorderBounds = null;
// ============================================================================================
// Constructors
public LimitIndicator(Context pContext) {
super(pContext);
Log.d("LimitIndicator", "LimitIndicator(Context pContext) called");
initialize();
}
public LimitIndicator(Context pContext, AttributeSet pAttrs) {
super(pContext, pAttrs);
Log.d("LimitIndicator", "LimitIndicator(Context pContext, AttributeSet pAttrs) called");
TypedArray typedArray = pContext.obtainStyledAttributes(pAttrs, R.styleable.LimitIndicator, 0, 0);
try {
mOuterCircleRadius = typedArray.getDimension(R.styleable.LimitIndicator_outerCircleRadius, mOuterCircleRadius);
mTextColor = typedArray.getColor(R.styleable.LimitIndicator_textColor, Color.WHITE);
mTitleText = typedArray.getString(R.styleable.LimitIndicator_text);
mTextSize = typedArray.getDimension(R.styleable.LimitIndicator_textSize, 25);
mTotalProgress = typedArray.getInteger(R.styleable.LimitIndicator_numerator, mTotalProgress);
mBorderColor = typedArray.getColor(R.styleable.LimitIndicator_borderColor, Color.BLACK);
mBorderWidth = typedArray.getDimension(R.styleable.LimitIndicator_borderRadius, mBorderWidth);
mInnerCircleColor = typedArray.getColor(R.styleable.LimitIndicator_innerCircleColor, Color.GREEN);
} finally {
typedArray.recycle();
}
initialize();
}
// ============================================================================================
// Initialization
/**
* Initialize all elements
*/
private void initialize() {
// Set up the paint for the dial
mDialPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mDialPaint.setStyle(Paint.Style.FILL);
mDialPaint.setColor(Color.GRAY);
// Set up the paint for the label text
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setTypeface(Typeface.SANS_SERIF);
mTextPaint.setTextAlign(Align.CENTER);
mTextPaint.setColor(mTextColor);
mTextPaint.setTextSize(mTextSize);
// Set up the paint for the border
mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBorderPaint.setStyle(Paint.Style.STROKE);
mBorderPaint.setStrokeWidth(mBorderWidth);
mBorderPaint.setColor(mBorderColor);
mBorderPaint.setAntiAlias(true);
mBorderPaint.setDither(true);
mInnerCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mInnerCirclePaint.setStyle(Paint.Style.FILL);
mInnerCirclePaint.setColor(mInnerCircleColor);
// mBorderPaint.setStrokeJoin(Paint.Join.ROUND);
// mBorderPaint.setStrokeCap(Paint.Cap.ROUND);
mBorderBounds = new RectF(getLeft(), getTop(), getRight(), getBottom());
}
// ============================================================================================
// Drawing on surface
#Override
protected void onDraw(Canvas pCanvas) {
super.onDraw(pCanvas);
Log.d("LimitIndicator", "OnDraw called");
Log.d("Measured Spec Width", mCenterX + "");
Log.d("Measured Spec Height", mCenterY + "");
pCanvas.drawCircle(mCenterX, mCenterY, mOuterCircleRadius, mDialPaint);
pCanvas.drawCircle(mCenterX, mCenterY, (float) (mOuterCircleRadius - mBorderWidth + 1) , mInnerCirclePaint);
pCanvas.drawText(mTitleText, mCenterX, mCenterY + 5, mTextPaint);
pCanvas.drawArc(mBorderBounds, mStartAngle, mSweepAngle, false, mBorderPaint);
if (mSweepAngle < mTotalProgressInDegrees) {
mSweepAngle+=3;
mBorderPaint.setStrokeWidth(mBorderWidth++);
invalidate();
}
}
#Override
protected void onLayout(boolean pChanged, int pLeft, int pTop, int pRight, int pBottom) {
Log.d("LimitIndicator", "OnLayout called");
for (int i = 0; i < getChildCount(); i++) {
getChildAt(i).layout(0, 0, pRight, pBottom);
}
}
#Override
protected void onSizeChanged(int pW, int pH, int pOldw, int pOldh) {
super.onSizeChanged(pW, pH, pOldw, pOldh);
Log.d("LimitIndicator", "OnSizeChanged called");
float xPad = (getPaddingLeft() + getPaddingRight());
float yPad = (getPaddingTop() + getPaddingBottom());
// To draw Circle in the middle
mCenterX = (float) ((pW - xPad) * 0.5);
mCenterY = (float) ((pH - yPad) * 0.5);
// This (mBorderBounds.bottom needs to be fixed. Width &
// Height should be equal in order
// to create a perfect circle. Otherwise an
// Oval will be created! :P
// Bounds for creating an arc
mHalfOfBorderWidth = (float) (mBorderWidth * 0.5);
mBorderBounds.right = mCenterX + mOuterCircleRadius - mHalfOfBorderWidth;
mBorderBounds.left = mCenterX - mOuterCircleRadius + mHalfOfBorderWidth;
mBorderBounds.top = mCenterY - mOuterCircleRadius + mHalfOfBorderWidth;
mBorderBounds.bottom = mCenterY + mOuterCircleRadius - mHalfOfBorderWidth;
}
// =========================================================================================================
/**
* Start the progress/animation. Use this method to start the animated view.
*/
public void startProgress() {
if (mTotalProgress >= 0) {
float progressInDegrees = mTotalProgress;
mTotalProgressInDegrees = (int) (progressInDegrees/100 * 360);
invalidate();
}
}
#Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent pEvent) {
return super.dispatchPopulateAccessibilityEvent(pEvent);
}
// =========================================================================================================
// Getters && Setters!
/**
* #return the dialRadius
*/
public float getDialRadius() {
return mOuterCircleRadius;
}
/**
* #param pDialRadius
* the dialRadius to set
*/
public void setDialRadius(float pDialRadius) {
mOuterCircleRadius = pDialRadius;
}
/**
* #return the textSize
*/
public float getTextSize() {
return mTextSize;
}
/**
* #param pTextSize the textSize to set
*/
public void setTextSize(float pTextSize) {
mTextSize = pTextSize;
}
/**
* #return the textColor
*/
public int getTextColor() {
return mTextColor;
}
/**
* #param pTextColor the textColor to set
*/
public void setTextColor(int pTextColor) {
mTextColor = pTextColor;
}
/**
* #return the borderColor
*/
public int getBorderColor() {
return mBorderColor;
}
/**
* #param pBorderColor the borderColor to set
*/
public void setBorderColor(int pBorderColor) {
mBorderColor = pBorderColor;
}
/**
* #return the innerCircleColor
*/
public int getInnerCircleColor() {
return mInnerCircleColor;
}
/**
* #param pInnerCircleColor the innerCircleColor to set
*/
public void setInnerCircleColor(int pInnerCircleColor) {
mInnerCircleColor = pInnerCircleColor;
}
/**
* #return the titleText
*/
public String getTitleText() {
return mTitleText;
}
/**
* #param pTitleText the titleText to set
*/
public void setTitleText(String pTitleText) {
mTitleText = pTitleText;
}
/**
* #return the outerCircleRadius
*/
public float getOuterCircleRadius() {
return mOuterCircleRadius;
}
/**
* #param pOuterCircleRadius the outerCircleRadius to set
*/
public void setOuterCircleRadius(float pOuterCircleRadius) {
mOuterCircleRadius = pOuterCircleRadius;
}
/**
* #return the borderWidth
*/
public float getBorderWidth() {
return mBorderWidth;
}
/**
* #param pBorderWidth the borderWidth to set
*/
public void setBorderWidth(float pBorderWidth) {
mBorderWidth = pBorderWidth;
}
/**
* #return the totalProgress
*/
public int getTotalProgress() {
return mTotalProgress;
}
/**
* #param pTotalProgress the totalProgress to set
*/
public void setTotalProgress(int pTotalProgress) {
mTotalProgress = pTotalProgress;
}
}
EDIT
#Override
public boolean onTouchEvent(MotionEvent pEvent) {
double x = pEvent.getX();
double y = pEvent.getY();
double x1 = x - mCenterX;
double y1 = y - mCenterY;
double distance = Math.sqrt(x1*x1 + y1*y1);
RectF topBoundingRect = new RectF(mCenterX - mOuterCircleRadius, mCenterY - mOuterCircleRadius, mCenterX + mOuterCircleRadius, mCenterY);
if (Math.abs(distance - mOuterCircleRadius) <= MAX_TOUCH_TOLERANCE && topBoundingRect.contains((float) x, (float) y)) {
// the user is touching the arc. Which arc is tapped? How do I know that?
}
return true;
}
I won't post code for this, since I'm not completely comfortable working with Java UIs, but the math behind what you're describing shouldn't be too hard.
To make sure that I understand what you're doing: you have a circular arc defined by some center point (x0, y0), a radius r, a start angle θ0, and an end angle θ1. You then want to take a test point (x, y) and determine whether the user clicked on the circle.
This problem is a lot easier to solve if we translate everything back to the origin, since trigonometry is always easier relative to the origin. So let's let
x' = x - x0
y' = y - y0
Now that you have x' and y', we can determine how far away it is from the center of the circle by computing
dist = √(x'2 + y'2)
If this value isn't close to the radius r, then there's no way that the point clicked is anywhere near the arc. Since the arc mathematically is infinitely small, you probably want to set up some "tolerance" for when the user clicks on the arc. You could, for example, define some constant TOLERANCE and then assume the user is clicking on the circumference of the circle if
|dist - r| ≤ TOLERANCE
Now, this assumes that the arc is just the border of the circle. If you are drawing a filled-in circular arc, you can instead just check whether
dist ≤ r + TOLERANCE
This checks whether the point is inside the circle at all.
Now, at this point you can check whether the point is in/inside the circle at all. The next question is whether they clicked on a part of the circle that's part of the arc. To do this, you can compute the angle θ at which the the point is relative to the center of the circle by using Math.atan2 and computing Math.atan2(y', x'). This gives you back an angle θ (in radians). You can then check whether θ0 ≤ θ ≤ θ1, which will then tell you if they clicked on the part of the circle you care about.
In short:
Compute x' and y' from x, y, x0, and y0.
Compute dist from x' and y'.
Determine whether they hit the circle / circumference by using the above math.
Use Math.atan2 to get the angle θ
See if θ is in the range you want.
Hope this helps!
I'm trying to a make simple project by myself.This project works like the game of Tetris.Rectangle Objects are created with timer and randomly in 3 different colors.objects are kept on the list.
I checked the alignment of Y with moveRectangle() and control() function.
If three of the same color side by side or on top of block,they will be removed and upper blocks will replace them.
That's my problem.I've tried every way but I couldn't do that.
That works for creating the rectangle objects and controlling objects x and y coordinates;
package p1;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.Timer;
public class DropObject {
private int yukseklik;
private int block;
private Color r;
//private static int sayac=-1;
private static int cntr;
private Rectangle object;
private static int sx=60;
private static int [] sy=new int[9];
public static int a=3;
public static Color [] colorR=new Color[25];
public DropObject(int yukseklik,int x,int y,int size,Color r)
{
x=sx;
//y=sy;
this.r=EventRect.RColor();
this.yukseklik=yukseklik;
//set();
this.object=new Rectangle(x,y,size,size);
}
/*public void set(){
colorR[cntr]=this.r;
//System.out.println("Renk: "+colorR[cntr]);
cntr++;
}*/
public static int getSX(){
return sx;
}
public static void setSX(int x){
sx=x;
}
public static int getSY(){
return sy[a];
}
public static void setSY(int y){
sy[a]=y;
}
public Color getColor(){
//colorR[++sayac]=this.r;
return this.r;
}
public int getYukseklik(){
return yukseklik;
}
public void setYukseklik(int yukseklik){
this.yukseklik=yukseklik;
}
public Rectangle getObject(){
return object;
}
public void setObject(Rectangle object){
this.object=object;
this.object.x=sx;
}
}
EventRect class provides objects movements and specifies objects y coordinate position;
package p1;
import java.awt.Button;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
//import java.util.Timer;
import java.util.TimerTask;
import javax.swing.Timer;
import java.util.Random;
import javax.swing.JPanel;
public class EventRect extends JPanel {
private static int yukseklik=340;
private static Color BlockC;
private DropObject koordinat;
private int zheight=yukseklik;
private int fheight=yukseklik;
private int sheight=yukseklik;
private int theight=yukseklik;
private int foheight=yukseklik;
private int fiheight=yukseklik;
private int ssheight=yukseklik;
private int seheight=yukseklik;
private int seiheight=yukseklik;
private int counter=-1;
private int counter1=-1;
private int counter0=-1;
private List<DropObject> objects=new LinkedList<DropObject>();
private LinkedList<TimerTask> tasklist=new LinkedList<TimerTask>();
Timer timet;
int i=1;
public EventRect(){
addKeyListener(new InputKey());
setFocusable(true);
//System.out.println((objects.size()));
koordinat=new DropObject(yukseklik,60,-20,20,BlockC);
objects.add(koordinat);
startSampling();
//timet=new Timer(10,this);
//timet.start();
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d=(Graphics2D)g;
for(DropObject o : objects){
Rectangle r=o.getObject();
g2d.setColor(o.getColor());
//System.out.println(r.x);
g2d.fillRect(r.x, r.y, r.width+1, r.height+1);
// System.out.println(objects.size());
}
}
public void moveRectangle(){
for(int i=(objects.size()-1);i>=(objects.size()-1);i--){
DropObject o=objects.get(i);
Rectangle r=o.getObject();
if(DropObject.a==0){
DropObject.setSY(zheight);
o.setYukseklik(DropObject.getSY());
}
if(DropObject.a==1){
DropObject.setSY(fheight);
o.setYukseklik(DropObject.getSY());
}
if(DropObject.a==2){
DropObject.setSY(sheight);
o.setYukseklik(DropObject.getSY());
}
if(DropObject.a==3){
DropObject.setSY(theight);
o.setYukseklik(DropObject.getSY());
}
if(DropObject.a==4){
DropObject.setSY(foheight);
o.setYukseklik(DropObject.getSY());
}
if(DropObject.a==5){
DropObject.setSY(fiheight);
o.setYukseklik(DropObject.getSY());
}
if(DropObject.a==6){
DropObject.setSY(ssheight);
o.setYukseklik(DropObject.getSY());
}
if(DropObject.a==7){
DropObject.setSY(seheight);
o.setYukseklik(DropObject.getSY());
}
if(DropObject.a==8){
DropObject.setSY(seiheight);
o.setYukseklik(DropObject.getSY());
}
if(o.getYukseklik()>=0){
if(r.y<o.getYukseklik()){
r.y+=r.height;
o.setObject(r);
}else{
o.setYukseklik(o.getYukseklik()-r.height);
}
}
if(r.y == o.getYukseklik()){
control();
objects.add(new DropObject(o.getYukseklik(),60,-20,20,BlockC));
}
}
}
void control(){
if(DropObject.a==0){
zheight-=20;
DropObject.setSY(zheight);
}
if(DropObject.a==1){
fheight-=20;
DropObject.setSY(fheight);
}
if(DropObject.a==2){
sheight-=20;
DropObject.setSY(sheight);
}
if(DropObject.a==3){
theight-=20;
DropObject.setSY(theight);
}
if(DropObject.a==4){
foheight-=20;
DropObject.setSY(foheight);
}
if(DropObject.a==5){
fiheight-=20;
DropObject.setSY(fiheight);
}
if(DropObject.a==6){
ssheight-=20;
DropObject.setSY(ssheight);
}
if(DropObject.a==7){
seheight-=20;
DropObject.setSY(seheight);
}
if(DropObject.a==8){
seiheight-=20;
DropObject.setSY(seiheight);
}
}
void startSampling(){
TimerTask task=new TimerTask(){
public void run(){
moveRectangle();
repaint();
}
};
java.util.Timer timer=new java.util.Timer();
timer.scheduleAtFixedRate(task,0,150);
tasklist.add(task);
}
void stopSampling(){
if(tasklist.isEmpty()){
return;
}
tasklist.removeFirst().cancel();
}
public static Color RColor(){
Color [] Array={Color.blue,Color.pink,Color.gray};
Random random=new Random();
BlockC=Array[random.nextInt(Array.length)];
return BlockC;
}
}
Keyboard movements;
package p1;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
public class InputKey extends KeyAdapter{
public void keyPressed(KeyEvent e){
int keys=e.getKeyCode();
if(keys==KeyEvent.VK_RIGHT){
//System.out.println("saga kaydi");
if(DropObject.getSX()<160)
DropObject.setSX(DropObject.getSX()+20);
if(DropObject.a<8){
DropObject.a++;
System.out.println("a nin degeri: "+DropObject.a);
}
}
else if(keys==KeyEvent.VK_LEFT){
if(DropObject.getSX()>0)
DropObject.setSX(DropObject.getSX()-20);
if(DropObject.a>0){
DropObject.a--;
}
}
}
}
package p1;
import java.awt.Color;
import java.awt.Container;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Main implements Runnable{
#Override
public void run(){
JFrame frame=new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container container=frame.getContentPane();
container.setLayout(new GridLayout(0,1));
container.add(new EventRect());
frame.setSize(197,400);
frame.setVisible(true);
//container.setBackground(Color.BLUE);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Main());
}
}
Here is my heavily modified solution based on your original code and idea. It implements an object oriented approach to tracking the blocks. It uses a list of blocks to represent each column. It also has an ArrayList to store a reference to all the blocks on the screen so that we can loop through for drawing. Sorry it won't be in whatever your native language was, but hopefully you can still understand it. The main code you need is stored in the BlockBuster class, which handles the adjacency detection and removal of blocks. You should separate out each class into its own file.
Block.java
package p1;
import java.awt.Color;
import java.awt.Rectangle;
import java.util.Random;
public class Block {
/**
* Color
*/
private Color color;
/**
* The pixel coordinate bounding rectangle for the block.
*/
private Rectangle bounds;
/**
* the integer column
*/
private int column;
/**
* Creates a new instance of a block at the specified
*
* #param y Distance in pixels from the top of the form.
* #param column The integer column index (0-8)
* #param size The integer size.
*/
public Block(int y, int column, int size) {
this.color = RandomColor();
this.bounds = new Rectangle(20*column, y, size, size);
this.column = column;
}
/**
* Gets the color of this block.
* #return the color of this block.
*/
public Color getColor() {
return this.color;
}
/**
* Gets the pixel bounds for this block.
* #return The bounds.
*/
public Rectangle getBounds() {
return bounds;
}
/**
* Sets the bounds for this block.
* #param object
*/
public void setBounds(Rectangle object) {
this.bounds = object;
}
/**
* This is Elevation in units of blocks, like column
* #return
*/
public int getLevel(){
return (340 - this.getBounds().y)/20;
}
/**
* Gets the bottom of the block.
* #return
*/
public int getBottom(){
return this.bounds.y + this.bounds.height;
}
/**
* Gets the integer column index of the block (0-8)
* #return the column
*/
public int getColumn() {
return column;
}
/**
* Sets the integer column index of the block (0-8)
* #param column the column to set
*/
public void setColumn(int column) {
this.column = column;
}
/**
* Generates a random color.
* #return Color
*/
public static Color RandomColor() {
Color[] Array = {Color.blue, Color.pink, Color.gray};
Random random = new Random();
return Array[random.nextInt(Array.length)];
}
}
BlockBuster.java
package p1;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
/**
* This class handles the work of finding adjacent blocks.
* To delete blocks ONLY if the blocks are horizontally adjacent,
* then you can remove the "Up" and "Down" sections.
* #author Ted
*/
public class BlockBuster {
private List<Block> queue;
private List<Block> bustList;
/**
* Creates a new instance of the BlockBuster class.
*/
public BlockBuster() {
queue = new ArrayList<Block>();
}
/**
* This method busts the blocks of with matching colors
*/
public void bustBlocks() {
Block block = BlockManager.getInstance().getFallingBlock();
bustList = new ArrayList<Block>();
queue.add(block);
while (queue.size() > 0) {
Block current = queue.get(0);
addMatchingNeighbors(current);
queue.remove(0);
if(!bustList.contains(current)){
bustList.add(current);
}
}
if(bustList.size() > 2){
for(Block busted : bustList){
BlockManager.getInstance().RemoveBlock(busted);
List<Block> check = BlockManager.getInstance().getAllBlocks();
if(check.contains(busted)){
boolean stop = true;
}
}
}
}
/**
* This method adds neighbors that match in color to the queue
* as long as they don't already exist in the queue or bustlist.
* #param block The current block to consider.
*/
private void addMatchingNeighbors(Block block) {
// currently only check by 4 connectivity
int x = block.getColumn();
int y = block.getLevel();
Color color = block.getColor();
// left
Block leftBlock = getBlock(x - 1, y);
if(colorMatches(color, leftBlock)){
queue.add(leftBlock);
}
// Right
Block rightBlock = getBlock(x + 1, y);
if(colorMatches(color, rightBlock)){
queue.add(rightBlock);
}
// Up
Block upBlock = getBlock(x, y + 1);
if(colorMatches(color, upBlock)){
queue.add(upBlock);
}
Block downBlock = getBlock(x, y-1);
if(colorMatches(color, downBlock)){
queue.add(downBlock);
}
}
/**
* This tests whether the block's color matches the specified
* color, and whether or not the block is already in a list.
* #param color The color that should match for deletion.
* #param block The block to be removed.
* #return
*/
public boolean colorMatches(Color color, Block block){
if(block == null){
return false;
}
if(bustList.contains(block)){
return false;
}
if(queue.contains(block)){
return false;
}
return (block.getColor().equals(color));
}
/**
* This finds a block at the specified column and height.
* #param column The integer column index (0-8)
* #param height The integer level (in blocks)
* #return a Block
*/
public Block getBlock(int column, int level) {
Column[] columns = BlockManager.getInstance().getColumns();
if (column >= 0 && column < 10) {
if (columns[column].getBlocks().size() > level && level >= 0) {
Block result = columns[column].getBlocks().get(level);
if(bustList.contains(result)){
return null;
}
return result;
}
}
return null;
}
}
BlockManager.java
package p1;
import java.util.ArrayList;
import java.util.List;
/**
* This is a singleton to control the blocks and is visible
* from the other classes in the application.
* #author Ted
*/
public class BlockManager {
/**
* The single instance of a falling block.
*/
private Block fallingBlock;
/**
* All blocks on the page, including the falling one.
*/
private List<Block> allBlocks;
/**
* The array of columns at the bottom, containing blocks.
*/
private Column[] columns;
/**
* Creates an instance of a BlockManager class. As part
* of the singleton pattern, this is private so that
* you can only access the shared instance.
*/
private BlockManager() {
allBlocks = new ArrayList<Block>();
columns = new Column[9];
for(int i = 0; i < 9; i++)
{
columns[i] = new Column();
}
}
/**
* Gets the single shared instance of the singleton.
* #return BlockManager
*/
public static BlockManager getInstance() {
return BlockManagerHolder.INSTANCE;
}
private static class BlockManagerHolder {
private static final BlockManager INSTANCE = new BlockManager();
}
/**
* Adds a new falling block to the top of the control.
*/
public void AddBlock(){
fallingBlock = new Block(0, 3, 20);
allBlocks.add(fallingBlock);
}
/**
* Removes the specified block.
* #param block
*/
public void RemoveBlock(Block block){
allBlocks.remove(block);
List<Block> blocks = columns[block.getColumn()].getBlocks();
if(blocks.size() > block.getLevel()){
for(int i = block.getLevel(); i < blocks.size(); i++)
{
// Make blocks above this block drop.
blocks.get(i).getBounds().y += 20;
}
}
columns[block.getColumn()].getBlocks().remove(block);
}
/**
* Gets the falling block.
* #return the fallingBlock
*/
public Block getFallingBlock() {
return fallingBlock;
}
/**
* Sets the falling block.
* #param fallingBlock the fallingBlock to set
*/
public void setFallingBlock(Block fallingBlock) {
this.fallingBlock = fallingBlock;
}
/**
* Gets the list of all blocks visible on the form,
* including the falling block.
* #return the allBlocks
*/
public List<Block> getAllBlocks() {
return allBlocks;
}
/**
* Sets the list of all blocks visible on the form,
* including the falling block.
* #param allBlocks the allBlocks to set
*/
public void setAllBlocks(List<Block> allBlocks) {
this.allBlocks = allBlocks;
}
/**
* Gets the array of columns containing blocks at the bottom.
* #return the columns
*/
public Column[] getColumns() {
return columns;
}
/**
* Sets the array of columns containing blocks at the bottom.
* #param columns the columns to set
*/
public void setColumns(Column[] columns) {
this.columns = columns;
}
}
Column.java
package p1;
import java.util.ArrayList;
import java.util.List;
/**
* A Column is an object that contains a list of the blocks stored
* in a vertical stack at the bottom of the game. Each block object
* points to the same object instance in allBlocks.
* #author Ted
*/
public class Column {
private static int maxHeight = 340;
private List<Block> blocks;
/**
* Gets the maximum vertical pixel height of the form.
* #return the maxHeight
*/
public static int getMaxHeight() {
return maxHeight;
}
/**
* Sets the maximum vertical pixel height of the form.
* #param aMaxHeight the maxHeight to set
*/
public static void setMaxHeight(int aMaxHeight) {
maxHeight = aMaxHeight;
}
/**
* Creates a new instance of the column class.
*/
public Column(){
blocks = new ArrayList<Block>();
}
/**
* Gets the amount of space remaining in this column
* #return
*/
public int getDepthRemaining(){
return maxHeight - getBlockHeight();
}
/**
* Gets the total height of all the blocks in this column
* #return
*/
public int getBlockHeight(){
int height = 0;
for(Block block : blocks){
height += block.getBounds().height;
}
return height;
}
/**
* Gets the list of blocks associated with this column.
* The 0 index is at the bottom.
* #return the dropObjects
*/
public List<Block> getBlocks() {
return blocks;
}
/**
* Sets the list of blocks associated with this column.
* #param dropObjects the dropObjects to set
*/
public void setBlocks(List<Block> dropObjects) {
this.blocks = dropObjects;
}
}
EventRect.java
package p1;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.LinkedList;
import java.util.TimerTask;
import javax.swing.Timer;
import javax.swing.JPanel;
/**
* EventRect is the main form for the game.
*/
public class EventRect extends JPanel {
/**
* #return the fallingBlock
*/
public static Block getFallingBlock() {
return BlockManager.getInstance().getFallingBlock();
}
private BlockBuster blockBuster;
private LinkedList<TimerTask> tasklist = new LinkedList<TimerTask>();
Timer timet;
int i = 1;
/**
* Creates a new instance of the main form for the project.
*/
public EventRect() {
BlockManager.getInstance().AddBlock();
this.blockBuster = new BlockBuster();
addKeyListener(new InputKey());
setFocusable(true);
startSampling();
}
/**
* Paints all the blocks onto the screen, including the falling block.
* #param g The Graphics surface of the form.
*/
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (Block dropObject : BlockManager.getInstance().getAllBlocks()) {
Rectangle bounds = dropObject.getBounds();
g2d.setColor(dropObject.getColor());
g2d.fillRect(bounds.x, bounds.y, bounds.width + 1, bounds.height + 1);
}
}
/**
* This handles the downward motion of the falling block.
*/
public void moveRectangle() {
Column[] columns = BlockManager.getInstance().getColumns();
if(getFallingBlock().getBounds().getY() >=
columns[getFallingBlock().getColumn()].getDepthRemaining()){
// We hit the top of the column, so add this block to the column
columns[getFallingBlock().getColumn()].getBlocks().add(getFallingBlock());
blockBuster.bustBlocks();
BlockManager.getInstance().AddBlock();
}
else{
BlockManager.getInstance().getFallingBlock().getBounds().y += 20;
}
}
/**
* This method was made final because it is called from the
* constructor. This starts game timers (from original post).
*/
final void startSampling() {
TimerTask task = new TimerTask() {
#Override
public void run() {
moveRectangle();
repaint();
}
};
java.util.Timer timer = new java.util.Timer();
timer.scheduleAtFixedRate(task, 0, 150);
tasklist.add(task);
}
/**
* This method removes the timer from the tasklist.
*/
void stopSampling() {
if (tasklist.isEmpty()) {
return;
}
tasklist.removeFirst().cancel();
}
}
InputKey.java
package p1;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
/**
* This class handles the movement of blocks. This
* class was modified to include collision detection
* with blocks in a neighboring column.
*/
public class InputKey extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
int keys = e.getKeyCode();
Block block = EventRect.getFallingBlock();
if (block == null) {
return;
}
Rectangle rect = block.getBounds();
if (keys == KeyEvent.VK_RIGHT) {
//Check for right bounds
if (rect.x < 160) {
// Check to see if we run into another block
int level = block.getLevel();
int column = block.getColumn();
if (BlockManager.getInstance().getColumns()[column + 1].getBlocks().size() > level) {
// we ran into a block
return;
}
rect.x += 20;
block.setColumn(rect.x / 20);
}
} else if (keys == KeyEvent.VK_LEFT) {
// Check for left bounds
if (rect.x > 0) {
// Check to see if we run into another block
int level = block.getLevel();
int column = block.getColumn();
if (BlockManager.getInstance().getColumns()[column + 1].getBlocks().size() > level) {
// we ran into a block
return;
}
rect.x -= 20;
block.setColumn(rect.x / 20);
}
}
}
}
Main.java
package p1;
import java.awt.Container;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
/**
* This class runs the program.
* #author Ted
*/
public class Main implements Runnable {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container container = frame.getContentPane();
container.setLayout(new GridLayout(0, 1));
container.add(new EventRect());
frame.setSize(197, 400);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Main());
}
}
I had once implemented tetris like game and the way i detected collision and filled areas was by following datastructures
The whole space where you shapes are going to settle down is an grid represented with array, inititally 0 and filled with 1 on cells where a block falls.
Every block is also a square grid with shape defined by filling it with 1s where required.
After this model its trivial to detect collision as well as filled areas. If you want colors treated differently you could assign 1,2,3..so on for every color to the cell. Hope that helps.
I have the following NavigableImagePanel, it is under BSD license and I found it in the web. What I want to do with this panel is as follow:
I want to add a JScrollPane to it in order to show images in their full size and let the users to re-center the image using the small navigation panel. Right now, the panel resize the images to fit them in the current panel size. I want it to load the image in its real size and let users to navigate to different parts of the image using the navigation panel.
Source code for the panel:
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
/**
* #author pxt
*
*/
public class NavigableImagePanel extends JPanel {
/**
* <p>Identifies a change to the zoom level.</p>
*/
public static final String ZOOM_LEVEL_CHANGED_PROPERTY = "zoomLevel";
/**
* <p>Identifies a change to the zoom increment.</p>
*/
public static final String ZOOM_INCREMENT_CHANGED_PROPERTY = "zoomIncrement";
/**
* <p>Identifies that the image in the panel has changed.</p>
*/
public static final String IMAGE_CHANGED_PROPERTY = "image";
private static final double SCREEN_NAV_IMAGE_FACTOR = 0.15; // 15% of panel's width
private static final double NAV_IMAGE_FACTOR = 0.3; // 30% of panel's width
private static final double HIGH_QUALITY_RENDERING_SCALE_THRESHOLD = 1.0;
private static final Object INTERPOLATION_TYPE =
RenderingHints.VALUE_INTERPOLATION_BILINEAR;
private double zoomIncrement = 0.2;
private double zoomFactor = 1.0 + zoomIncrement;
private double navZoomFactor = 1.0 + zoomIncrement;
private BufferedImage image;
private BufferedImage navigationImage;
private int navImageWidth;
private int navImageHeight;
private double initialScale = 0.0;
private double scale = 0.0;
private double navScale = 0.0;
private int originX = 0;
private int originY = 0;
private Point mousePosition;
private Dimension previousPanelSize;
private boolean navigationImageEnabled = true;
private boolean highQualityRenderingEnabled = true;
private WheelZoomDevice wheelZoomDevice = null;
private ButtonZoomDevice buttonZoomDevice = null;
/**
* <p>Defines zoom devices.</p>
*/
public static class ZoomDevice {
/**
* <p>Identifies that the panel does not implement zooming,
* but the component using the panel does (programmatic zooming method).</p>
*/
public static final ZoomDevice NONE = new ZoomDevice("none");
/**
* <p>Identifies the left and right mouse buttons as the zooming device.</p>
*/
public static final ZoomDevice MOUSE_BUTTON = new ZoomDevice("mouseButton");
/**
* <p>Identifies the mouse scroll wheel as the zooming device.</p>
*/
public static final ZoomDevice MOUSE_WHEEL = new ZoomDevice("mouseWheel");
private String zoomDevice;
private ZoomDevice(String zoomDevice) {
this.zoomDevice = zoomDevice;
}
public String toString() {
return zoomDevice;
}
}
//This class is required for high precision image coordinates translation.
private class Coords {
public double x;
public double y;
public Coords(double x, double y) {
this.x = x;
this.y = y;
}
public int getIntX() {
return (int)Math.round(x);
}
public int getIntY() {
return (int)Math.round(y);
}
public String toString() {
return "[Coords: x=" + x + ",y=" + y + "]";
}
}
private class WheelZoomDevice implements MouseWheelListener {
public void mouseWheelMoved(MouseWheelEvent e) {
Point p = e.getPoint();
boolean zoomIn = (e.getWheelRotation() < 0);
if (isInNavigationImage(p)) {
if (zoomIn) {
navZoomFactor = 1.0 + zoomIncrement;
} else {
navZoomFactor = 1.0 - zoomIncrement;
}
zoomNavigationImage();
} else if (isInImage(p)) {
if (zoomIn) {
zoomFactor = 1.0 + zoomIncrement;
} else {
zoomFactor = 1.0 - zoomIncrement;
}
zoomImage();
}
}
}
private class ButtonZoomDevice extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
Point p = e.getPoint();
if (SwingUtilities.isRightMouseButton(e)) {
if (isInNavigationImage(p)) {
navZoomFactor = 1.0 - zoomIncrement;
zoomNavigationImage();
} else if (isInImage(p)) {
zoomFactor = 1.0 - zoomIncrement;
zoomImage();
}
} else {
if (isInNavigationImage(p)) {
navZoomFactor = 1.0 + zoomIncrement;
zoomNavigationImage();
} else if (isInImage(p)) {
zoomFactor = 1.0 + zoomIncrement;
zoomImage();
}
}
}
}
/**
* <p>Creates a new navigable image panel with no default image and
* the mouse scroll wheel as the zooming device.</p>
*/
public NavigableImagePanel() {
setOpaque(false);
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
if (scale > 0.0) {
if (isFullImageInPanel()) {
centerImage();
} else if (isImageEdgeInPanel()) {
scaleOrigin();
}
if (isNavigationImageEnabled()) {
createNavigationImage();
}
repaint();
}
previousPanelSize = getSize();
}
});
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
if (isInNavigationImage(e.getPoint())) {
Point p = e.getPoint();
displayImageAt(p);
}
}
}
public void mouseClicked(MouseEvent e){
if (e.getClickCount() == 2) {
resetImage();
}
}
});
addMouseMotionListener(new MouseMotionListener() {
public void mouseDragged(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)
&& !isInNavigationImage(e.getPoint())) {
Point p = e.getPoint();
moveImage(p);
}
}
public void mouseMoved(MouseEvent e) {
//we need the mouse position so that after zooming
//that position of the image is maintained
mousePosition = e.getPoint();
}
});
setZoomDevice(ZoomDevice.MOUSE_WHEEL);
}
/**
* <p>Creates a new navigable image panel with the specified image
* and the mouse scroll wheel as the zooming device.</p>
*/
public NavigableImagePanel(BufferedImage image) throws IOException {
this();
setImage(image);
}
private void addWheelZoomDevice() {
if (wheelZoomDevice == null) {
wheelZoomDevice = new WheelZoomDevice();
addMouseWheelListener(wheelZoomDevice);
}
}
private void addButtonZoomDevice() {
if (buttonZoomDevice == null) {
buttonZoomDevice = new ButtonZoomDevice();
addMouseListener(buttonZoomDevice);
}
}
private void removeWheelZoomDevice() {
if (wheelZoomDevice != null) {
removeMouseWheelListener(wheelZoomDevice);
wheelZoomDevice = null;
}
}
private void removeButtonZoomDevice() {
if (buttonZoomDevice != null) {
removeMouseListener(buttonZoomDevice);
buttonZoomDevice = null;
}
}
/**
* <p>Sets a new zoom device.</p>
*
* #param newZoomDevice specifies the type of a new zoom device.
*/
public void setZoomDevice(ZoomDevice newZoomDevice) {
if (newZoomDevice == ZoomDevice.NONE) {
removeWheelZoomDevice();
removeButtonZoomDevice();
} else if (newZoomDevice == ZoomDevice.MOUSE_BUTTON) {
removeWheelZoomDevice();
addButtonZoomDevice();
} else if (newZoomDevice == ZoomDevice.MOUSE_WHEEL) {
removeButtonZoomDevice();
addWheelZoomDevice();
}
}
/**
* <p>Gets the current zoom device.</p>
*/
public ZoomDevice getZoomDevice() {
if (buttonZoomDevice != null) {
return ZoomDevice.MOUSE_BUTTON;
} else if (wheelZoomDevice != null) {
return ZoomDevice.MOUSE_WHEEL;
} else {
return ZoomDevice.NONE;
}
}
//Called from paintComponent() when a new image is set.
private void initializeParams() {
double xScale = (double)getWidth() / image.getWidth();
double yScale = (double)getHeight() / image.getHeight();
initialScale = Math.min(xScale, yScale);
scale = initialScale;
//An image is initially centered
centerImage();
if (isNavigationImageEnabled()) {
createNavigationImage();
}
}
//Centers the current image in the panel.
private void centerImage() {
originX = (int)(getWidth() - getScreenImageWidth()) / 2;
originY = (int)(getHeight() - getScreenImageHeight()) / 2;
}
//Creates and renders the navigation image in the upper let corner of the panel.
private void createNavigationImage() {
//We keep the original navigation image larger than initially
//displayed to allow for zooming into it without pixellation effect.
navImageWidth = (int)(getWidth() * NAV_IMAGE_FACTOR);
navImageHeight = navImageWidth * image.getHeight() / image.getWidth();
int scrNavImageWidth = (int)(getWidth() * SCREEN_NAV_IMAGE_FACTOR);
int scrNavImageHeight = scrNavImageWidth * image.getHeight() / image.getWidth();
navScale = (double)scrNavImageWidth / navImageWidth;
navigationImage = new BufferedImage(navImageWidth, navImageHeight,
image.getType());
Graphics g = navigationImage.getGraphics();
g.drawImage(image, 0, 0, navImageWidth, navImageHeight, null);
}
/**
* <p>Sets an image for display in the panel.</p>
*
* #param image an image to be set in the panel
*/
public void setImage(BufferedImage image) {
BufferedImage oldImage = this.image;
this.image = image;
//Reset scale so that initializeParameters() is called in paintComponent()
//for the new image.
scale = 0.0;
firePropertyChange(IMAGE_CHANGED_PROPERTY, (Image)oldImage, (Image)image);
repaint();
}
/**
* <p>resets an image to the centre of the panel</p>
*
*/
public void resetImage() {
BufferedImage oldImage = this.image;
this.image = image;
//Reset scale so that initializeParameters() is called in paintComponent()
//for the new image.
scale = 0.0;
firePropertyChange(IMAGE_CHANGED_PROPERTY, (Image)oldImage, (Image)image);
repaint();
}
/**
* <p>Tests whether an image uses the standard RGB color space.</p>
*/
public static boolean isStandardRGBImage(BufferedImage bImage) {
return bImage.getColorModel().getColorSpace().isCS_sRGB();
}
//Converts this panel's coordinates into the original image coordinates
private Coords panelToImageCoords(Point p) {
return new Coords((p.x - originX) / scale, (p.y - originY) / scale);
}
//Converts the original image coordinates into this panel's coordinates
private Coords imageToPanelCoords(Coords p) {
return new Coords((p.x * scale) + originX, (p.y * scale) + originY);
}
//Converts the navigation image coordinates into the zoomed image coordinates
private Point navToZoomedImageCoords(Point p) {
int x = p.x * getScreenImageWidth() / getScreenNavImageWidth();
int y = p.y * getScreenImageHeight() / getScreenNavImageHeight();
return new Point(x, y);
}
//The user clicked within the navigation image and this part of the image
//is displayed in the panel.
//The clicked point of the image is centered in the panel.
private void displayImageAt(Point p) {
Point scrImagePoint = navToZoomedImageCoords(p);
originX = -(scrImagePoint.x - getWidth() / 2);
originY = -(scrImagePoint.y - getHeight() / 2);
repaint();
}
//Tests whether a given point in the panel falls within the image boundaries.
private boolean isInImage(Point p) {
Coords coords = panelToImageCoords(p);
int x = coords.getIntX();
int y = coords.getIntY();
return (x >= 0 && x < image.getWidth() && y >= 0 && y < image.getHeight());
}
//Tests whether a given point in the panel falls within the navigation image
//boundaries.
private boolean isInNavigationImage(Point p) {
return (isNavigationImageEnabled() && p.x < getScreenNavImageWidth()
&& p.y < getScreenNavImageHeight());
}
//Used when the image is resized.
private boolean isImageEdgeInPanel() {
if (previousPanelSize == null) {
return false;
}
return (originX > 0 && originX < previousPanelSize.width
|| originY > 0 && originY < previousPanelSize.height);
}
//Tests whether the image is displayed in its entirety in the panel.
private boolean isFullImageInPanel() {
return (originX >= 0 && (originX + getScreenImageWidth()) < getWidth()
&& originY >= 0 && (originY + getScreenImageHeight()) < getHeight());
}
/**
* <p>Indicates whether the high quality rendering feature is enabled.</p>
*
* #return true if high quality rendering is enabled, false otherwise.
*/
public boolean isHighQualityRenderingEnabled() {
return highQualityRenderingEnabled;
}
/**
* <p>Enables/disables high quality rendering.</p>
*
* #param enabled enables/disables high quality rendering
*/
public void setHighQualityRenderingEnabled(boolean enabled) {
highQualityRenderingEnabled = enabled;
}
//High quality rendering kicks in when when a scaled image is larger
//than the original image. In other words,
//when image decimation stops and interpolation starts.
private boolean isHighQualityRendering() {
return (highQualityRenderingEnabled
&& scale > HIGH_QUALITY_RENDERING_SCALE_THRESHOLD);
}
/**
* <p>Indicates whether navigation image is enabled.<p>
*
* #return true when navigation image is enabled, false otherwise.
*/
public boolean isNavigationImageEnabled() {
return navigationImageEnabled;
}
/**
* <p>Enables/disables navigation with the navigation image.</p>
* <p>Navigation image should be disabled when custom, programmatic navigation
* is implemented.</p>
*
* #param enabled true when navigation image is enabled, false otherwise.
*/
public void setNavigationImageEnabled(boolean enabled) {
navigationImageEnabled = enabled;
repaint();
}
//Used when the panel is resized
private void scaleOrigin() {
originX = originX * getWidth() / previousPanelSize.width;
originY = originY * getHeight() / previousPanelSize.height;
repaint();
}
//Converts the specified zoom level to scale.
private double zoomToScale(double zoom) {
return initialScale * zoom;
}
/**
* <p>Gets the current zoom level.</p>
*
* #return the current zoom level
*/
public double getZoom() {
return scale / initialScale;
}
/**
* <p>Sets the zoom level used to display the image.</p>
* <p>This method is used in programmatic zooming. The zooming center is
* the point of the image closest to the center of the panel.
* After a new zoom level is set the image is repainted.</p>
*
* #param newZoom the zoom level used to display this panel's image.
*/
public void setZoom(double newZoom) {
Point zoomingCenter = new Point(getWidth() / 2, getHeight() / 2);
setZoom(newZoom, zoomingCenter);
}
/**
* <p>Sets the zoom level used to display the image, and the zooming center,
* around which zooming is done.</p>
* <p>This method is used in programmatic zooming.
* After a new zoom level is set the image is repainted.</p>
*
* #param newZoom the zoom level used to display this panel's image.
*/
public void setZoom(double newZoom, Point zoomingCenter) {
Coords imageP = panelToImageCoords(zoomingCenter);
if (imageP.x < 0.0) {
imageP.x = 0.0;
}
if (imageP.y < 0.0) {
imageP.y = 0.0;
}
if (imageP.x >= image.getWidth()) {
imageP.x = image.getWidth() - 1.0;
}
if (imageP.y >= image.getHeight()) {
imageP.y = image.getHeight() - 1.0;
}
Coords correctedP = imageToPanelCoords(imageP);
double oldZoom = getZoom();
scale = zoomToScale(newZoom);
Coords panelP = imageToPanelCoords(imageP);
originX += (correctedP.getIntX() - (int)panelP.x);
originY += (correctedP.getIntY() - (int)panelP.y);
firePropertyChange(ZOOM_LEVEL_CHANGED_PROPERTY, new Double(oldZoom),
new Double(getZoom()));
repaint();
}
/**
* <p>Gets the current zoom increment.</p>
*
* #return the current zoom increment
*/
public double getZoomIncrement() {
return zoomIncrement;
}
/**
* <p>Sets a new zoom increment value.</p>
*
* #param newZoomIncrement new zoom increment value
*/
public void setZoomIncrement(double newZoomIncrement) {
double oldZoomIncrement = zoomIncrement;
zoomIncrement = newZoomIncrement;
firePropertyChange(ZOOM_INCREMENT_CHANGED_PROPERTY,
new Double(oldZoomIncrement), new Double(zoomIncrement));
}
//Zooms an image in the panel by repainting it at the new zoom level.
//The current mouse position is the zooming center.
private void zoomImage() {
Coords imageP = panelToImageCoords(mousePosition);
double oldZoom = getZoom();
scale *= zoomFactor;
Coords panelP = imageToPanelCoords(imageP);
originX += (mousePosition.x - (int)panelP.x);
originY += (mousePosition.y - (int)panelP.y);
firePropertyChange(ZOOM_LEVEL_CHANGED_PROPERTY, new Double(oldZoom),
new Double(getZoom()));
repaint();
}
//Zooms the navigation image
private void zoomNavigationImage() {
navScale *= navZoomFactor;
repaint();
}
/**
* <p>Gets the image origin.</p>
* <p>Image origin is defined as the upper, left corner of the image in
* the panel's coordinate system.</p>
* #return the point of the upper, left corner of the image in the panel's coordinates
* system.
*/
public Point getImageOrigin() {
return new Point(originX, originY);
}
/**
* <p>Sets the image origin.</p>
* <p>Image origin is defined as the upper, left corner of the image in
* the panel's coordinate system. After a new origin is set, the image is repainted.
* This method is used for programmatic image navigation.</p>
I've used this component before myself, but never in exactly the way you describe. There is an existing function in the code, setZoom(), that can be used to programatically set the zoom level and repaint the image. I would recommend that you experiment with that function and the zoom level.