Rectangles are not drawn as expected - codenameone

Recently I was somewhat unsure about the presentation of my app and therefore I created a small tester app to look at this topic more closely.
For that I derived my own component class which did nothing els than draw the outer bounds of itself and was surprised that it wasn't completely shown - neither in the simulator, nor on my iPhone 5S.
Now to my question: Why is it that a component drawn as aGraphics.drawRect(getX() + 2, getY() + 2, 10, 10) shows as eleven pixels wide and high in the simulator and on my iPhone 5S?
Here is some code which can be used to demonstrate this:
public class FormRectangleComponent extends Form {
private class RectangleComponent extends Component {
private int color;
public RectangleComponent(int aColor) {
color = aColor;
}
#Override
public void paint(Graphics aGraphics) {
aGraphics.setColor(color);
aGraphics.drawRect(getX(), getY(), getWidth(), getHeight());
Map<String,Object> map = new LinkedHashMap<String,Object>() {{
put("Display.displayWidth", Display.getInstance().getDisplayWidth());
put("rectangleComponent.x", getX());
put("rectangleComponent.y", getY());
put("rectangleComponent.width", getWidth());
put("rectangleComponent.height", getHeight());
}};
aGraphics.setColor(0xffffff);
aGraphics.drawRect(getX() + 2, getY() + 2, 10, 10);
report(map);
}
#Override
protected Dimension calcPreferredSize() {
int side = Display.getInstance().getDisplayHeight() / 10;
return new Dimension(side, side);
}
}
public FormRectangleComponent() {
Display.getInstance().areMutableImagesFast();
setTitle("FormRectangleComponent");
setScrollable(false);
Container contentPane = getContentPane();
contentPane.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
contentPane.setScrollableY(true);
contentPane.getAllStyles().setBgPainter((Graphics aGraphics, Rectangle aRectangle) -> {
aGraphics.setColor(0x00c0c0);
aGraphics.fillRect(aRectangle.getX(), aRectangle.getY(), aRectangle.getWidth(), aRectangle.getHeight());
});
contentPane.add(new SpanLabel(
"Below should be some frames showing all sides."));
contentPane.add(new RectangleComponent(0xff0000));
contentPane.add(new RectangleComponent(0x0000ff));
contentPane.add(new RectangleComponent(0x00ff00));
}
private void report(Map<String, Object> map) {
int maximumLength = 0;
for (Entry<String, Object> entry: map.entrySet()) {
maximumLength = Math.max(maximumLength, entry.getKey().length());
}
for (Entry<String, Object> entry: map.entrySet()) {
StringBuilder stringBuilder = new StringBuilder();
for (int tally = 0; tally < (maximumLength - entry.getKey().length()); tally ++) {
stringBuilder.append(' ');
}
stringBuilder
.append(entry.getKey())
.append(": ")
.append(entry.getValue());
Log.p(stringBuilder.toString());
}
}
}

It's mentioned here in brief but this behavior is based on the convention within the Java graphics API's documented here.
Graphics is a low level API and we tried to keep it consistent with the way Java works. All our samples rely on the width/height -1 convention so it's documented by example & consistency if by nothing else.
The logic for this is mathematical, you don't draw on pixels but in a virtual co-ordinate space that resides between the pixels which makes more sense as you start to transform graphics operations.

Related

Why is the first pointerDragged location depending on the size of the Form?

I noticed some strange behaviour with dragging Switch instances and such. To investigate this I created a small non-scrollable Form derivate which contains a Component derivate with a custom paint method which draws a round rect in the component size and a green crosshair for a pointerPressed location and a blue crosshair for the first pointerDragged location.
Testing using the Simulator with the Desktop Skin one gets views like that dragging within the custom component:
So it appears that the first pointerDragged location one gets is farther away from the pointerPressed location the larger the Form is.
That is a problem whereever pointerDragged events are required. For example with a Switch component this is very noticeable on an iPad in landscape.
Why is that? Is it a bug or a feature?
Here is the code:
Form hi = new Form("Hi World", BoxLayout.y());
hi.setScrollable(false);
Command commandReload = new Command("Reload") {
#Override
public void actionPerformed(ActionEvent aActionEvent) {
start();
}
};
hi.getToolbar().addCommandToRightBar(commandReload);
Component component = new Component() {
Point pointPressed = null, pointDraggedFirst = null;
#Override
protected Dimension calcPreferredSize() {
return new Dimension(120, 120);
}
#Override
public void paint(Graphics aGraphics) {
aGraphics.setColor(0x000000);
aGraphics.drawRoundRect(getX(), getY(), getWidth() - 1, getHeight() - 1, 20, 20);
if (null != pointPressed) {
aGraphics.setColor(0x00C000);
aGraphics.drawLine(getX(), pointPressed.getY() - getAbsoluteY(), getX() + getWidth() - 1, pointPressed.getY() - getAbsoluteY());
aGraphics.drawLine(pointPressed.getX() - getAbsoluteX(), getY(), pointPressed.getX() - getAbsoluteX(), getY() + getHeight() - 1);
if (null != pointDraggedFirst) {
aGraphics.setColor(0x0000ff);
aGraphics.drawLine(getX(), pointDraggedFirst.getY() - getAbsoluteY(), getX() + getWidth() - 1, pointDraggedFirst.getY() - getAbsoluteY());
aGraphics.drawLine(pointDraggedFirst.getX() - getAbsoluteX(), getY(), pointDraggedFirst.getX() - getAbsoluteX(), getY() + getHeight() - 1);
}
}
}
#Override
public void pointerPressed(int x, int y) {
pointPressed = new Point(x, y);
}
#Override
public void pointerDragged(int x, int y) {
if (null == pointDraggedFirst) {
pointDraggedFirst = new Point(x, y);
}
}
#Override
public void pointerReleased(int x, int y) {
pointPressed = null;
pointDraggedFirst = null;
}
};
hi.add(FlowLayout.encloseLeftMiddle(component));
hi.add(new Label("X") {
int width = 0, height = 0;
#Override
public void paint(Graphics aGraphics) {
if (width != hi.getWidth() || height != hi.getHeight()) {
width = hi.getWidth();
height = hi.getHeight();
Display.getInstance().callSerially(() -> setText("Form Width/Height: " + hi.getWidth() + "/" + hi.getHeight()));
}
super.paint(aGraphics);
}
});
hi.show();
Some devices send pointer dragged immediately and create an effect where it's hard to distinguish between a click and a drag. So we have a threshold composed from screen percentage and event count during which we block native drag events.
You can shorten this time by overriding getDragRegionStatus to indicate your interest in specific drag events.

Initializing a grid of JButton and adding them to a JFrame

I'm trying to write a program that plays Yahtzee in a JFrame. Our teacher has a way we're supposed to write it, and it involves adding ConfigurationButtons - a class that extends JButton - to the JFRame. Here is the code I have so far:
builder= new PlayerPanel();
manager = new JFrame("Yahtzee!");
manager.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
manager.setLayout(new GridLayout(TOTAL, numPlayers));
manager.add(builder);
//manager.add(howManyPlayers);
manager.setSize(1200,600);
manager.setVisible(true);
private class PlayerPanel extends JPanel
{
private final int ROWS = 18;
private JLabel[]titles;
private PlayerPanel()
{
for(int j=0;j<numPlayers;j++)
{
for(int i=0;i<ROWS;i++)
{
fields[i][j]=new ConfigurationButton(i,j);
manager.add(fields[i][j]);
}
}
}
I tried implementing this solution but it didn't work- all I get is a blank JFrame.
OK, to fill out your JFrame, see what I have done with the following code, and apply it to your situation.
JFrame manager = new JFrame("Manager");
JPanel panel = new JPanel(new GridLayout(10, 10));
for(int i = 0; i < 10; i++)
{
for(int j = 0; j < 10; j++)
{
JButton button = new JButton(i + " - " + j);
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
((JButton)(e.getSource())).setText("Clicked");
}
});
panel.add(button);
}
}
manager.add(panel);
manager.setSize(750, 750);
manager.setVisible(true);
Take note of how the panel is set up in a GridLayout (as you had), but the panel is then added to the JFrame.
You should be able to build your panel based off this example, substituting your custom components where necessary.

How I can stop an animated GIF in JavaFX?

I want to use an animated GIF in my project, but I dont know how I can stop the loop animation. I mean, I want the GIF to play 1 time only.
Thanks!
I haven't done GIF animation, wasn't even aware that JavaFX would have methods for starting and stopping them. If you wish to do ANY animation using images, I rather suggest you do it frame by frame yourself. This way you have full control over it and you can have more than just 256 colors in your image.
I read a very good article about Creating a Sprite Animation with JavaFX in Mike's blog.
It's very easy to do. You simply extend the Transition class, add an ImageView to it and implement the Transition's Interpolate method.
Edit: oh, and by the way, GIFs have a loop flag which tells them to either play in a loop or not to play in a loop. In other words: In theory you could modify the GIF file's loop property. In theory only, because I just tried with specifying to play only once and in JavaFX it still played in an endless loop while in FireFox it played once. By the way, JavaFX doesn't seem to support animated PNGs (APNG) which would support more than 256 colors. So the automatic image animation capabilities are very limited. Best to do the animation by yourself.
I hope someone comes up with something better, but here's an example code about how you could get full control over your gif.
import java.awt.image.BufferedImage;
import java.net.URISyntaxException;
import javafx.animation.Interpolator;
import javafx.animation.Transition;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
* Requires GifDecoder from here: http://www.java2s.com/Code/Java/2D-Graphics-GUI/DecodesaGIFfileintooneormoreframes.htm
*/
public class AnimatedGifDemo extends Application {
#Override
public void start(Stage primaryStage) throws URISyntaxException {
HBox root = new HBox();
// TODO: provide gif file, ie exchange banana.gif with your file
Animation ani = new AnimatedGif(getClass().getResource("banana.gif").toExternalForm(), 1000);
ani.setCycleCount(10);
ani.play();
Button btPause = new Button( "Pause");
btPause.setOnAction( e -> ani.pause());
Button btResume = new Button( "Resume");
btResume.setOnAction( e -> ani.play());
root.getChildren().addAll( ani.getView(), btPause, btResume);
Scene scene = new Scene(root, 1600, 900);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
public class AnimatedGif extends Animation {
public AnimatedGif( String filename, double durationMs) {
GifDecoder d = new GifDecoder();
d.read( filename);
Image[] sequence = new Image[ d.getFrameCount()];
for( int i=0; i < d.getFrameCount(); i++) {
WritableImage wimg = null;
BufferedImage bimg = d.getFrame(i);
sequence[i] = SwingFXUtils.toFXImage( bimg, wimg);
}
super.init( sequence, durationMs);
}
}
public class Animation extends Transition {
private ImageView imageView;
private int count;
private int lastIndex;
private Image[] sequence;
private Animation() {
}
public Animation( Image[] sequence, double durationMs) {
init( sequence, durationMs);
}
private void init( Image[] sequence, double durationMs) {
this.imageView = new ImageView(sequence[0]);
this.sequence = sequence;
this.count = sequence.length;
setCycleCount(1);
setCycleDuration(Duration.millis(durationMs));
setInterpolator(Interpolator.LINEAR);
}
protected void interpolate(double k) {
final int index = Math.min((int) Math.floor(k * count), count - 1);
if (index != lastIndex) {
imageView.setImage(sequence[index]);
lastIndex = index;
}
}
public ImageView getView() {
return imageView;
}
}
}
It provides a pause/resume button for testing. What you need in addition is the Gif Decoder code and an animated banana.gif.

Expanding standard Polygon class

I am trying to create a program which can union polygons. I want to create a Polygon class, which can do some operations like Initialize, FindOfOrientation and so on. I am using WPF and with an abstract class - Shape. I want to inherit it and use some properties of this class. But I have problem with Property: "System.Windows.Media.Geometry DefiningGeometry" How should I accomplish this?
class SpecialPolygon: Shape
{
#region Constants
private const int STROKE_THIKNESS = 1; // Толщина линии
private const double OPACITY = 0.5; // Прозрачность
#endregion
int _amountOfVertexes; // Количество сторон полигона
List<Point> _listOfVertexes; // Список всех вершин
List<Stroke> _listOfStrokes; // Список ребер
PointCollection Points { get; set; }
protected override System.Windows.Media.Geometry DefiningGeometry
{
WHAT I SHOULD WRITE IN THIS CASE ???
}
/// <summary>
/// Конструктор с параметрами инициализирует полигон с заданным количеством вершин
/// </summary>
/// <param name="amountOfVertexes">Число вершин</param>
/// <param name="listOfVertexes">Список координат</param>
public SpecialPolygon(int amountOfVertexes, List<Point> listOfVertexes)
{
_amountOfVertexes = amountOfVertexes;
_listOfVertexes = listOfVertexes;
_listOfStrokes = new List<Stroke>();
this.Stroke = System.Windows.Media.Brushes.Black;
this.Fill = System.Windows.Media.Brushes.LightBlue;
this.StrokeThickness = STROKE_THIKNESS;
this.Opacity = OPACITY;
this.Points = new PointCollection();
for (int i = 0; i < _amountOfVertexes; i++)
{
Point a;
a = _listOfVertexes[i];
this.Points.Add(a);
}
// Записываем каждую пару точек в отрезок.(Точка начала отрезка и конца)
for (int i = 0; i < _amountOfVertexes - 1; i++)
{
Stroke stroke = new Stroke(_listOfVertexes[i], _listOfVertexes[i + 1]);
this._listOfStrokes.Add(stroke);
}
// Не забываем записать последний отрезок, соединяющий конец ломанной с её началом
// Таким образом получается полигон
Stroke lastStroke = new Stroke(_listOfVertexes[_amountOfVertexes - 1], _listOfVertexes[0]);
this._listOfStrokes.Add(lastStroke);
}
Rather than going over subject matter in detail that has already been written many times before, I'll just give you a summary and a link to a book that has an excellent section that will answer all of your questions and probably a few that you didn't even have. As #Clemens succinctly said, The DefiningGeometry property would simply return the Geometry that makes up your Shape.
The following example of how to create a Geometry object was taken from the excellent WPF Control Development Unleashed book:
private Geometry GetArcGeometry()
{
Point startPoint = PointAtAngle(Math.Min(StartAngle, EndAngle));
Point endPoint = PointAtAngle(Math.Max(StartAngle, EndAngle));
Size arcSize = new Size(Math.Max(0, (RenderSize.Width –
StrokeThickness)/2),
Math.Max(0, (RenderSize.Height - StrokeThickness)/2));
bool isLargeArc = Math.Abs(EndAngle - StartAngle) 180;
StreamGeometry geom = new StreamGeometry();
using (StreamGeometryContext context = geom.Open())
{
context.BeginFigure(startPoint, false, false);
context.ArcTo(endPoint, arcSize, 0, isLargeArc,
SweepDirection.Counterclockwise, true, false);
}
geom.Transform = new TranslateTransform(StrokeThickness/2,
StrokeThickness/2);
return geom;
}
I'd recommend that you read the Sample: Building a Circular Minute Timer section, starting on page 40, but really, you could do a lot worse than read the whole thing. While I'm suggesting reading material for you, it seems that it might be a good idea for you to read through the various pages of the StackOverflow Help Center... it will help you to get the most out of this website.

Movieclip not being removed from array or stage - AS3 - CS6

my enemy movie clips are not being removed from the stage on collision/hitTestObject with the bullet movie clip, the bullets are being removed though, only the enemies that are not. Appreciate all help with this. Thanks you guys, your a brilliant community here, respect you loads.
I used the same code that made the bullet movie clips be removed for the enemies (though change changing the vars etc accordingly).
My code for Main.as and Enemy.as is here:
Main.as
package
{
import flash.display.Stage;
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent;
public class Main extends MovieClip
{
public var player:Player;
public var enemy:Enemy;
public var bulletList:Array = [];
public var mousePressed:Boolean = false; //keeps track of whether the mouse is currently pressed down
public var delayCounter:int = 0; //this adds delay between the shots
public var delayMax:int = 7; //change this number to shoot more or less rapidly
public var enemies:Array = [];
public function Main():void
{
player = new Player(stage, 320, 240);
stage.addChild(player);
//stage.addEventListener(MouseEvent.CLICK, shootBullet, false, 0, true); //remove this
stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler, false, 0, true);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, false, 0, true);
stage.addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
for(var numBaddies=0; numBaddies<6;numBaddies++){
var enemy:Enemy = new Enemy(null);
enemy.x = numBaddies*50;
enemy.y = numBaddies*50
stage.addChild(enemy);
enemies.push(enemy);
}
}
public function loop(e:Event):void
{
if(mousePressed) // as long as the mouse is pressed...
{
delayCounter++; //increase the delayCounter by 1
if(delayCounter == delayMax) //if it reaches the max...
{
shootBullet(); //shoot a bullet
delayCounter = 0; //reset the delay counter so there is a pause between bullets
}
}
if(bulletList.length > 0)
{
for(var i:int = bulletList.length-1; i >= 0; i--)
{
bulletList[i].loop();
}
}
for(var h = 0; h<bulletList.length; ++h)
{
if(bulletList[h].hitTestObject(this)){
trace("player hit by baddie " + h);
}
}
for(var u:int=0; u<enemies.length; u++) {
Enemy(enemies[u]).moveTowards(player.x, player.y);
}
}
public function mouseDownHandler(e:MouseEvent):void //add this function
{
mousePressed = true; //set mousePressed to true
}
public function mouseUpHandler(e:MouseEvent):void //add this function
{
mousePressed = false; //reset this to false
}
public function shootBullet():void //delete the "e:MouseEvent" parameter
{
var bullet:Bullet = new Bullet(stage, player.x, player.y, player.rotation, enemies);
bullet.addEventListener(Event.REMOVED_FROM_STAGE, bulletRemoved, false, 0, true);
bulletList.push(bullet);
stage.addChild(bullet);
}
public function bulletRemoved(e:Event):void
{
e.currentTarget.removeEventListener(Event.REMOVED_FROM_STAGE, bulletRemoved);
bulletList.splice(bulletList.indexOf(e.currentTarget),1);
}
}
}
Enemy.as
package {
import flash.display.Stage;
import flash.display.MovieClip;
import flash.events.Event;
public class Enemy extends MovieClip {
public var bullets:Array;
public var stageRef:Stage;
private var enemyPositionX, enemyPositionY,xDistance,yDistance,myRotation:int;
public function Enemy(bulletList:Array) {
// constructor code
bullets = bulletList;
}
public function moveTowards(playerX:int, playerY:int){
xDistance = this.x - playerX;
yDistance = this.y - playerY;
myRotation = Math.atan2(yDistance, xDistance);
this.x -= 3 * Math.cos(myRotation);
this.y -= 3 * Math.sin(myRotation);
}
public function loop():void{
for(var i=0; i<bullets.length; ++i)
{
if(bullets[i].hitTestObject(this)){
trace("you killed enemy " + i);
removeSelf();
}
}
}
private function removeSelf():void
{
removeEventListener(Event.ENTER_FRAME, loop);
if (stageRef.contains(this))
stageRef.removeChild(this);
}
}
}
And for reference so you can see how I used the same code to remove the movie clips, I'll add the Bullet.as too:
Bullet.as
package
{
import flash.display.Stage;
import flash.display.MovieClip;
import flash.events.Event;
public class Bullet extends MovieClip
{
private var stageRef:Stage; //checks if the bullet leaves the screen borders
private var speed:Number = 10; //speed that the bullet will travel at
private var xVel:Number = 0; //current x velocity
private var yVel:Number = 0; //current y velocity
private var rotationInRadians = 0; //convenient to store our rotation in radians instead of degrees
public var allBaddies:Array;
//our constructor requires: the stage, the position of the bullet, and the direction the bullet should be facing
public function Bullet(stageRef:Stage, X:int, Y:int, rotationInDegrees:Number, enemies:Array):void
{
this.stageRef = stageRef;
this.x = X;
this.y = Y;
this.rotation = rotationInDegrees;
this.rotationInRadians = rotationInDegrees * Math.PI / 180; //convert degrees to radians, for trigonometry
allBaddies = enemies;
}
public function loop():void //we don't need to include the "e:Event" because we aren't using an EventListener
{
for(var b=0; b<allBaddies.length; ++b)
{
if(allBaddies[b].hitTestObject(this)){
trace("bullet hit baddie " + b);
removeSelf();
}
}
xVel = Math.cos(rotationInRadians) * speed; //uses the cosine to get the xVel from the speed and rotation
yVel = Math.sin(rotationInRadians) * speed; //uses the sine to get the yVel
x += xVel; //updates the position
y += yVel;
//if the bullet goes off the edge of the screen...
if(x > stageRef.stageWidth || x < 0 || y > stageRef.stageHeight || y < 0)
{
this.parent.removeChild(this); //remove the bullet
}
}
private function removeSelf():void
{
removeEventListener(Event.ENTER_FRAME, loop);
if (stageRef.contains(this))
stageRef.removeChild(this);
}
}
}
Ok there are a couple of things I would change around, alongside a couple issues I noticed.
First, your Enemy class as far as I can tell never gets the stageRef variable set. Therefore in your Enemy class removeSelf() your stageRef will be null.
You can either pass the stage in through the constructor and set it (as you do in your bullet class), or you can set it after you instantiate in your enemies loop with enemy.stageRef = stage. Or you can add and event listener to your enemy class for Event.ADDED_TO_STAGE and when that happens your enemy will have a reference to the stage inherited.
Now, the real meat of the matter. You have 2 for loops testing for collision (aside from one in your Main class too, but I'll get to that). One is in your bullet class and one is in your enemy class. This is overkill and is difficult on performance if you start adding in lots of bullets and enemies. You really only need a single for loop (like the one in your bullet class), and when it detects a collision it calls the collided objects remove method. That would look something like this in your Bullet class:
public function loop():void //we don't need to include the "e:Event" because we aren't using an EventListener
{
for(var b=0; b<allBaddies.length; ++b)
{
if(allBaddies[b].hitTestObject(this)){
trace("bullet hit baddie " + b);
removeSelf();
allBaddies[b].removeSelf();
}
}
Although, for this to work you would need to set the removeSelf() in your Enemy class access to public:
public function removeSelf():void
{
//this is removing a listener not even added Enemy...
//removeEventListener(Event.ENTER_FRAME, loop);
if (stageRef.contains(this))
stageRef.removeChild(this);
}
Then you can probably even eliminate your loop() from the Enemy class seeing as how all it does it detect collisions that will no longer be necessary.
Lastly as to why this might happened? Well it could be the bullet hitTest which is running first from your call in the main class: bulletList[i].loop(); This hitTest is being detected, then removing the bullet leaving your Enemy class without an object to hit test against. BUT! you are not calling the loop() from enemy anywhere in your main class, therefore that hit test will never run.
It's best to condense your collisions down. You are currently looping through your Bullet instances in the main class, calling their loop() (which tests for collisions), and then afterwards doing a for loop doing the collision test again. You could change this:
if(bulletList.length > 0)
{
for(var i:int = bulletList.length-1; i >= 0; i--)
{
bulletList[i].loop();
}
}
//this is no longer needed...
/*for(var h = 0; h<bulletList.length; ++h)
{
if(bulletList[h].hitTestObject(this)){
trace("player hit by baddie " + h);
}
}*/
There is no reason to test the collision in the Bullet class AND in the Main class loop(e:Event).
Hope this information helps! Good luck :)

Resources