Creating a "face" code with manipulation capabilities - java

I have an assignment where I am supposed to create a prototype drawing program to display and manipulate faces. It is supposed to use 2 windows, one to draw the face and the other to control the drawing. It is supposed to initially show the image of a face (drawn with shapes).
The face consists of a head, two eyes, a nose, and a mouth. Each of these parts is selected by clicking the mouse within the border of the object. The eyes are selected as a pair, so clicking either eye selects them both. when one face part is selected, all previous selections are forgotten.
The control window operates only upon the selected object. A click on the Change image button performs the following tasks (depending on the selected face part):
Head: Head changes from green to yellow to purple to purple, and back to green
Nose: The nose image changes another of three possible nose images
Mouth: Mouth image changes to another of three possible mouth images
Eyes: eye image changes to another of four possible eye images
Include a scroll bar with behavior:
Selected Head: Head gets wider and narrower with scroll bar value change
Selected Nose: Nose image moves up or down with scroll bar value change
Selected Mouth: Mouth gets wider or narrower with scroll bar value change
Selected Eyes: Eyes move closer together or farther apart with scroll bar value change
My problem is, I have no idea where to even start. I have watched many a tutorial on swing and awt and have even tried playing around with the palette manager in NetBeans. Can anyone point me in the right direction or maybe walk me through the code?

Your question is not a good fit here. Also, despite your instructors alleged inaccuracies, it is still conceivably possible for a person to do the research on their own to go from the rather nice set of specific requirements you have to a functioning program. I don't mean that to be as critical as it may sound.
However, out of appreciation of the fact that you approached your instructor, and also out of pity, maybe I can give you some starting points and hints.
First of all, Swing is the right way to start. Read through the relevant sections of the tutorial at http://zetcode.com/tutorials/javaswingtutorial/ and follow along with some of the projects.
You can code Swing interfaces by hand quite easily, and for your relatively simple application, and for the learning experience, it is a reasonable option. However, for future reference, many IDEs have GUI editors (NetBeans ships with the feature I think, Eclipse has the WindowBuilder plugin) - the caveat being that you have to add learning how to use the IDE to your task list (of course, this will ultimately save you time in the long run).
In any case, your first step should be to design your software. Determine what actions you need to perform, what user actions you need to respond to and how, and what information you are working with. You give the following requirements:
The control window operates only upon the selected object. A click on
the Change image button performs the following tasks (depending on the
selected face part):
Head: Head changes from green to yellow to purple to purple, and back
to green
Nose: The nose image changes another of three possible nose
images
Mouth: Mouth image changes to another of three possible mouth
images
Eyes: eye image changes to another of four possible eye images
Include a scroll bar with behavior:
Selected Head: Head gets wider and narrower with scroll bar value
change
Selected Nose: Nose image moves up or down with scroll bar
value change
Selected Mouth: Mouth gets wider or narrower with scroll
bar value change
Selected Eyes: Eyes move closer together or farther
apart with scroll bar value change
The basic component you are working with here is a face, so let's make a design decision here to have a face be an object with other properties (you could take a different approach and have the different parts of the face be your basic units). Based on your description of scroll bars, a face has the following modifiable properties:
Head width.
Vertical nose position.
Mouth width.
Distance between eyes.
A face also, of course, has other constant characteristics that are pretty much up to you to arbitrarily decide the value of, for example the diameter of the eyes, the color of the skin, etc. You will have to work those out.
In addition to modifying the above properties, there are a few things you need to be able to do with a face, also based directly on your requirements:
Draw (paint) the face.
Redraw (repaint) the face when a property changes.
Allow the user to select a part by clicking, which breaks down into:
Responding to mouse clicks.
Determining the component under the cursor given an XY location.
Providing information about the selection to the application.
The last part "providing information about the selection" is an implementation detail that you have a few options for, and you will have to decide. For example, you could create some type of event listener interface and tell the face about it, and let it call methods on that interface when the selection changes -- this is a very swing way to do it. You could also have the face internally maintain which property is currently selected, in which case you need to give it the ability to get/set the value of the currently selected property. You could also have the face directly call methods on your editor UI window. All of these ways have distinct advantages and disadvantages, and I'll leave it as an exercise to the reader to experiment.
Now, you also, as per your requirements, need to provide a scroll bar for the user to edit properties with, in a separate window. So now, think about what UI components you need and how you want them to tie together:
Face Window
Contains a single face component (or more if you want).
Editor Window
Contains a scroll bar.
Might contain a label describing the current selection, if you'd like.
Swing already provides most of these components. A window is a JFrame. A scroll bar is a JScrollBar. A label is a JLabel. Swing doesn't provide a face, so you'll need to write a custom component yourself.
As for how they tie together:
Changing the object selection in the face could:
Update the scrollbar to the current value, if you'd like.
Update the scrollbar range to appropriate values.
Update the label to describe the current selection, if you'd like.
Changing the scrollbar must:
Update the currently selected property on the face.
Ultimately cause the face to be redrawn.
The major task now, then, is to implement a Swing component that draws a face and responds to mouse clicks.
Since you are creating a new component, you'll want to pick an appropriate base, perhaps a JComponent or a JPanel, up to you. Exactly how to create new components can be easily found in many tutorials on Google (including some basics in the one I linked to above), and that is up to you to research. Essentially, though, reiterating what was said above, your custom face component has the following custom behaviors:
Draw a face.
Receive mouse events.
Determine clicked component from mouse X,Y coordinates. At its most basic level, this will require some math. If your head, eyes, mouth, and nose are ellipses, for example, it will be up to you to compute whether or not a given X,Y coordinate is within the bounds of one of those ellipses. However, you will want to look into Shape, which will allow you to easily define shapes and provides bounds checking methods already.
I think this is enough for you to get started and put it all together. The following general tasks are at hand:
Read that tutorial I linked to, a lot of basics should become clear. Try writing some simple "hello world" style applications.
Try writing some custom components; your requirements are basic and much of it is covered in the "Painting" section of that tutorial.
Check out a tutorial or some examples on how to use Shape and experiment.
If you have specific questions about code you've written, with a specific problem you may be trying to solve, you can always come back and post that on SO.
I hope this helps. More importantly, I hope this gives you at least some idea of how to go from requirements to implementation in general.
Edit:
It's been a while since I played with Shape so I put together this tiny example. Click / drag over the two shapes to select them, which will turn them yellow:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class ShapeSelect extends JPanel {
private final Shape firstShape = new Rectangle2D.Float(60, 40, 70, 70);
private final Shape secondShape = new Ellipse2D.Float(30, 30, 90, 50);
private Shape selected = null;
public ShapeSelect () {
// set up the component
setPreferredSize(new Dimension(200, 200));
addMouseMotionListener(new MouseMotionAdapter() {
#Override public void mouseDragged (MouseEvent event) {
selectShapeUnder(event.getX(), event.getY());
}
});
addMouseListener(new MouseAdapter() {
#Override public void mousePressed (MouseEvent event) {
selectShapeUnder(event.getX(), event.getY());
}
});
}
// draw our shapes, selected shape is yellow.
#Override protected void paintComponent (Graphics g) {
Graphics2D graphics = (Graphics2D)g;
graphics.setColor((selected == firstShape) ? Color.YELLOW : Color.RED);
graphics.fill(firstShape);
graphics.setColor((selected == secondShape) ? Color.YELLOW : Color.GREEN);
graphics.fill(secondShape);
}
// updates 'selected' based on x,y coordinate and redraws component on change.
public void selectShapeUnder (int x, int y) {
Shape oldSelected = selected;
// note that since second shape is draw on top of first, we give second preference.
// for overlapping shapes the selection should be consistent with the gui display.
if (secondShape.contains(x, y))
selected = secondShape;
else if (firstShape.contains(x, y))
selected = firstShape;
else
selected = null;
if (selected != oldSelected)
repaint();
}
public static final void main (String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override public void run () {
JFrame frame = new JFrame("Shape Select");
frame.getContentPane().add(new ShapeSelect(), BorderLayout.CENTER);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}

Related

How to draw selectable, movable, and re-sizable rectangles on JavaFX canvas?

Is there a way to allow the user to draw individually selectable, movable, and re-sizable shapes within JavaFX and/or canvas that I am not considering or aware of?
I am new to JavaFX (Java GUIs in general, actually) and am trying to devise a method of allowing a user to draw (click & drag) up to 100 rectangles on a pane/workspace/page. I am also looking for a method to make this scalable, but keeping static proportions for now will be fine. I have not found a great deal of comparable examples in my search, most of which explore only one of those options but leave out certain things I need to accomplish. Usually they show pre-defined nodes or canvas shapes that aren't individually workable. So I am trying to find the best method to accomplish this.
Drawing the shapes is NOT the issue, I know how to do that. It's making them individual objects within the workspace (selectable, movable, re-sizable).
My current idea is to create a Javafx pane, then create 100 canvases within the pane as predefined layers which will be the same dimensions as the entire workspace. Each drawn rectangle will occupy one of the layers/canvas. Selecting each rectangle will involve a click event within the drawn rectangle within the layer allowing the user to edit the, (move, re-size, etc.)
The following Oracle tutorial is what I am going off of for this method of layering. http://docs.oracle.com/javafx/2/canvas/jfxpub-canvas.htm
I have the feeling that my working premise for this method is flawed. One reason is it requires pre-defined number of layers. Although I want to limit this anyway, it is not very dynamic. But mostly, it just seems convoluted.
My apologies for the lack of code, and for the the conceptual nature of the question, but I've been searching and experimenting (unsuccessfully) for a couple weeks. Any help or insight would be appreciated.
Thank You
Project Context
I'm creating a basic form creator. The user (in creation mode) will drag a series of rectangles that are associated with corresponding objects indicating certain attributes, coordinates, and dimensions of each rectangle. This data will be saved and used (in form mode) for the placement of text fields for form use. Wherever rectangle were drawn on the workspace, text fields of the same location and dimension will be placed on the form.
The easiest method I can think of is by making a group, and adding rectangles to the group, on-demand. You can then attach transforms and mouselisteners to each rectangle, and have it so that the properties of the transform is updated when a drag is detected. I've put together a simple demo here. Use a right-click to make a rectangle, left mouse drag to resize, and middle mouse drag to move the rectangle.
Group root = new Group();
Scene scene = new Scene(root, 1080, 720, true);
scene.addEventFilter(MouseEvent.MOUSE_PRESSED,e->{
if(e.getButton()==MouseButton.SECONDARY)
{
//make a new rectangle
Rectangle rect = new Rectangle(100,100);
root.getChildren().add(rect);
rect.setFill(Color.CYAN);
Scale scaler = new Scale(1,1);
Translate locationCenter = new Translate(e.getX(),e.getY());
rect.getTransforms().add(locationCenter);
rect.getTransforms().add(scaler);
rect.getTransforms().add(new Translate(-50,-50));
//listen for left mouse drags
rect.addEventFilter(MouseEvent.MOUSE_DRAGGED, e2->{
if(e2.getButton()==MouseButton.PRIMARY)
{
//resize with left drag
double scaleX=e2.getSceneX()-rect.localToParent(0,0).getX();
double scaleY=e2.getSceneY()-rect.localToParent(0,0).getY();
scaler.setX(scaleX/100);
scaler.setY(scaleY/100);
} else if(e2.getButton()==MouseButton.MIDDLE)
{
//move with middle drag
locationCenter.setX(e2.getSceneX());
locationCenter.setY(e2.getSceneY());
}
});
}
});

Flickering when updating overlapping JPanels inside a JLayeredPane using timestep

I am making a game in Java. Basically, I have two different "planes" of updating that I need to take care of the. The base layer is the actual game painting itself. It is simply a JPanel that covers the entire JFrame, and is drawn to using its Graphics object.
I use a fixed timestep to take care of these first graphical updates. I have overwritten the paintComponent() method to do absolutely nothing, as I have written a custom render(float interpolation) method that takes care of that, as to prevent unwanted updates.
However, this panel can take no input beyond primitive mouse clicks and keyboard input. I need the ability to create various menus, text boxes, etc, that are also on the screen. Like various abilities, or even the "Menu" button that usually appears in the upper left corner of most games.
To take care of that input, such as creating buttons, I have a second JPanel that has setOpaque(false) applied to it. Then I create various Swing components that I might need, such as a JButton.
To contain the two JPanels, I use a JLayeredPane, and set their layers appropriately, as seen below. This way the input layer should always be on top of the actual game layer.
The code below shows how I create and add the Swing components to each other. addLoginDialog() is a method that adds a Swing component for the login. It has been tested and works properly, and isn't the problem.
private void initComponents()
{
//This code is inside of the JFrame
wholePane = new JLayeredPane();
add(wholePane);
guiPanel = new GUIPanel();
guiPanel.setOpaque(false);
gamePanel = new RPGPanel();
gamePanel.setOpaque(false);
wholePane.add(gamePanel, JLayeredPane.DEFAULT_LAYER);
wholePane.add(guiPanel, JLayeredPane.POPUP_LAYER);
guiPanel.addLoginDialog();
}
So when I run the code, I get horrible flickering. This is the code that is run from my fixed timestep ~60 times per second.
public void handleRepaint()
{
//I don't use repaint() for the game drawing so I can be sure that fps is controlled.
Graphics g = gamePanel.getGraphics();
gamePanel.render(g);
g.dispose();
wholePane.repaint();
}
The problem is, I think, that the two different systems of updating the screen are clashing. The standard paintComponent() system is great for more static screens, but when I need to update consistently and keep track of the fps, I can't have updates going off randomly.
However, for the input layer, I only want to update as Swing normally does. When the mouse moves over a button, when I component is moved or is resized, etc.
Also, note the way the screen flickers: The Background image goes blank and then comes back again repeatedly. The input panel is always there, but is actually painted behind the game drawing, which shouldn't happen, because it is put in the default layer. The reason I know it isn't completely disappearing is because the game painting is partially transparent, so I can see underneath it, and the buttons I added are still there.
My main question is, how can I stop the flickering? Why is the game drawing being drawn on top of the input components when the game drawing is being done on the Panel that is in a lower layer in the JLayeredPane? And I supposed most importantly, what is causing the flickering? Thank you for any help.
Why is the game drawing being drawn on top of the input components
when the game drawing is being done on the Panel that is in a lower
layer in the JLayeredPane?
Mostly because you've circumvented how Swing works.
Let's start with the fact that the Graphics context is a shared resource (typically there is a single Graphics context per native peer), that is, every component gets the same context, this means, when you use your own painting routine, you are basically painting over every thing else.
The RepaintManager is responsible for making decisions about what and when something should be painted. So what you now have is two artist fighting over the same canvas, wanting to paint on it simultaneously, which is just making a mess.
Add into the fray that Swing's painting process is not thread safe and you end up with a complete mess.
If you absolutely must have control, then you need to remove Swing's painting engine from the equation.
Start by taking a look at Passive vs. Active Rendering
ps- There is also hard ware acceleration benefits to using a BufferStrategy

Identify which of three partially overlapping images is clicked

I have several JLabels, each holding an ImageIcon like this:
ImageIcon icon = new ImageIcon("path/to/image.jpg");
JLabel label = new JLabel(icon);
Those images are .PNGs in a hexagonal shape. The edges "outside" the hexagon (the redundant part that exists is using a rectangular canvas) are transparent.
The JLabels, ergo the images, are ordered like in this example, so there are always three interfering images.
Since there is no "visible" layer beneath or over an other, I want to define the "clickable area" to exactly the visible layer. What is the cleverest way to do so, or is there an even more elegant solution?
I can think of three solutions for this problem:
If your hexagonal shape is just "black lines" - you can implement paintComponent and paint Polygons. You will have to have the references to them. Later on - on mouse click you will have to get shapes' component and mouse point in it. Then iterate over shapes calling contains for each of them. You will lose layout support using this solution.
Implement GlassPane/layer and dispatch mouse event to appropriate JLabel - iterating over JLabels
This would be least preferable (because of tight coupling) - extend JLabel and add references to adjacent hexagonal components. When mouse click is fired up you check if this component should "catch" this event. If not - you "forward" the event to appropriate component.

Java Swing/AWT - painting objects with context menus

I want to make something akin to a diagram editor - an application that allows the user to create, view and edit a bunch of shapes on a canvas. My GUI has essentially three parts - a standard JMenuBar, one JPanel on the right side for showing info about the shapes and a JPanel next to it that should be used for visualizing the shapes.
Aside from that, I have a package that defines the shapes. For simplicity's sake say there is just a Square, containing the following information: coordinates on the canvas, size, user-defined name and description, color.
The main class of the project (an extension of JFrame) contains lists of Squares.
Now, I could visualize the shapes by simply drawing them in the JPanel using drawrect and whatnot, but I want to create an interactive editor - if the user right-clicks on a visualized shape, a context menu would pop-up allowing him to move it, change its properties or remove the shape altogether. Clicking an empty spot in the JPanel would allow the user (again, via a menu) to create a new shape.
Is there an automated way to do this as opposed to manually keeping a matrix mapping each pixel of the canvas to a certain shape and checking it upon right click? One where I could say, for example, draw this here within the JPanel and do something onclick...
A related question, when I edit the shape in the JPanel, how do I access the Square/Circle list in the main application class, so that I can really change it? I suppose this is simple, but right now I don't know how I would do it.
Please point me in the right direction, anything will be appreciated, tips, examples, links to relevant tutorials...
As suggested by #eugener, classes that implement the Shape interface have a contains() method that is useful for this. GraphPanel is an example that illustrates several of the features you mention.
The most common way to accomplish this is to allow shapes to determine if point is inside the shape. Hopefully your code is object oriented and each type of your shape is a class.
All you have to do is to define a method such as boolean isInside( point: Point) for each shape. Once you have those all you have to do is walk the shapes in the reverse z-order (from top to bottom) and and see where the mouse click point lends. If it does not lend on any shape - you clicked the canvas. Once you have this info you can show an appropriate menu.
Hope this helps

Java2D: Capturing an event on a Line object

I have a JPanel which has a line, circle, etc. Now when I click on the line, will the event get reported as a line event or a general JFrame event. I need to be able to move the line if the user clicks on the line and moves it. Is this possible in Java2D?
Yes, but you will need to do some work (see java.awt.Shape). Basically you need to track a list of Shapes. The JPanel will recieve a mouse event, which you can translate to (x,y) coordinates. You can then call Shape.contains(x,y) to see if your various shapes were clicked on.
This will work well for Circle, Polygon, Arc, etc; however in the case of Line2D, it won't work as easily, but you can use Line2D.intersects() with a small rectangle around the mouse click (this is also good UI since you don't want to force the user to click exactly on a pixel that is hard to see).
There is no such concept as a "line event" unless you decide to implement one.
I would suggest adding a MouseListener and a MouseMotionListener to the Canvas or JPanel onto which your geometric shapes are drawn. Use the MouseListener's mousePressed(MouseEvent) callback to determine whether a given shape has been clicked on. Once you've established this, use MouseMotionListener's mouseDragged(MouseEvent) method to move and redraw the shape as the mouse cursor is moved.
Here's a simple example that demonstrates some of the techniques adduced in other answers.
I created a canvas markup library in Java a few years back and if you don't need to worry about transforms on the canvas (scaling, rotation, etc.) it is very easy to do.
Basically you just need to maintain a collection of the canvas shapes in a List (not a Set because Z order is probably important). The mouse listener will be on your canvas, and not on individual shapes. Add new items to the beginning of your collection (or iterate the list backwards later).
When the canvas receives a mouse down event iterate through your collection of shapes until you find one that is underneath your mouse coordinates. The easiest way to do this is to have your shapes implement an interface that defines some sort of hitPoint(int x, int y) method. That way your rectangles can implement a contains(), lines can do intersects() or graphics paths, you can account for some hit padding, etc.
Taking it one step further, your shapes should define their own draw(Graphics2D g) method so that you can easily do things like selection boxes, or set the paint mode to XOR to make shape 'moving' easier. The paintComponent method of your canvas would just have to iterate through your collection of shapes, calling shape.draw(g) on each one, passing in the graphics instance provided to the paintComponent method.

Categories