I want to extend the JavaFX class Line, cause i want the startpoint and endingpoint to be a circle or arrow or sth. like that. In addition to that, i want to tag the line in the future.
The problem is how to overwrite the paint method? What method is responsible for drawing the line and how do I have to implement my wishes?
Until now, i got that, but if I instaciate a Line it doesn´t change the appearance:
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
public class LabeledLine extends Line {
private Circle startCircle;
private Circle endCircle;
public LabeledLine(){
super();
startCircle = new Circle(getStartX(), getStartY(), 10);
endCircle = new Circle(getEndX(), getEndY(), 10);
startCircle.setFill(getFill());
endCircle.setFill(getFill());
}
public LabeledLine(double startX, double startY, double endX, double endY){
super(startX, startY, endX, endY);
startCircle = new Circle(getStartX(), getStartY(), 10);
endCircle = new Circle(getEndX(), getEndY(), 10);
startCircle.setFill(getFill());
endCircle.setFill(getFill());
}
}
To the extent of my knowledge you shouldn't extend base Shapes directly. Rather extend Region and use a composed Line as a child node.
https://github.com/aalmiray/jsilhouette uses a similar approach. Properties are set on types that "look like" Shapes but aren't.
Note that the implementation you provided won't work if, e.g. you were to do
LabeledLine l = new LabeledLine() ;
l.setStartX(100);
It is actually quite difficult to fix this using a strategy of subclassing Line.
At any rate, Shapes are rendered by delegating to a private native peer class, which (as I understand it) allows them to be rendered highly efficiently by a hardware graphics pipeline in many cases. So there is no accessible "paint" method for you to override here.
Instead, subclass Group and let your subclass wrap a line (basically, this is "favor composition over inheritance" from Effective Java by Joshua Bloch):
public class LabeledLine extends Group {
public LabeledLine(Line line) {
Circle startCircle = new Circle(10);
startCircle.centerXProperty().bind(line.startXProperty());
startCircle.centerYProperty().bind(line.startYProperty());
startCircle.fillProperty().bind(line.fillProperty());
Circle endCircle = new Circle(10);
endCircle.centerXProperty().bind(line.endXProperty());
endCircle.centerYProperty().bind(line.endYProperty());
endCircle.fillProperty().bind(line.fillProperty());
getChildren().addAll(line, startCircle, endCircle);
}
}
You would then use this as:
Line line = new Line();
Pane somePane = new Pane();
somePane.getChildren().add(new LabeledLine(line));
Note also that the implementation above actually adds no state or behavior to Group, so you could completely refactor it as a method:
public Group labelLine(Line line) {
Circle startCircle = new Circle(10);
startCircle.centerXProperty().bind(line.startXProperty());
startCircle.centerYProperty().bind(line.startYProperty());
startCircle.fillProperty().bind(line.fillProperty());
Circle endCircle = new Circle(10);
endCircle.centerXProperty().bind(line.endXProperty());
endCircle.centerYProperty().bind(line.endYProperty());
endCircle.fillProperty().bind(line.fillProperty());
return new Group(line, startCircle, endCircle);
}
and then
Pane somePane = new Pane();
Line line = new Line();
somePane.getChildren().add(labelLine(line));
I tried this here, and it worked well:
public Group labelLine(Line line) {
Circle startCircle = new Circle(line.getStartX(), line.getStartY(), 10);
Circle endCircle = new Circle(line.getEndX(), line.getEndY(), 10);
return new Group(line, startCircle, endCircle);
}
Still wonderin why your implementation won't work, because it looks nice.
Related
I have the following code that paints 4 points in a canvas3D window
public final class energon extends JPanel {
int s = 0, count = 0;
public energon() {
setLayout(new BorderLayout());
GraphicsConfiguration gc=SimpleUniverse.getPreferredConfiguration();
Canvas3D canvas3D = new Canvas3D(gc);//See the added gc? this is a preferred config
add("Center", canvas3D);
BranchGroup scene = createSceneGraph();
scene.compile();
// SimpleUniverse is a Convenience Utility class
SimpleUniverse simpleU = new SimpleUniverse(canvas3D);
// This moves the ViewPlatform back a bit so the
// objects in the scene can be viewed.
simpleU.getViewingPlatform().setNominalViewingTransform();
simpleU.addBranchGraph(scene);
}
public BranchGroup createSceneGraph() {
BranchGroup lineGroup = new BranchGroup();
Appearance app = new Appearance();
ColoringAttributes ca = new ColoringAttributes(new Color3f(204.0f, 204.0f, 204.0f), ColoringAttributes.SHADE_FLAT);
app.setColoringAttributes(ca);
Point3f[] plaPts = new Point3f[4];
for (int i = 0; i < 2; i++) {
for (int j = 0; j <2; j++) {
plaPts[count] = new Point3f(i/10.0f,j/10.0f,0);
//Look up line, i and j are divided by 10.0f to be able to
//see the points inside the view screen
count++;
}
}
PointArray pla = new PointArray(4, GeometryArray.COORDINATES);
pla.setCoordinates(0, plaPts);
Shape3D plShape = new Shape3D(pla, app);
TransformGroup objRotate = new TransformGroup();
objRotate.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
objRotate.addChild(plShape);
lineGroup.addChild(objRotate);
return lineGroup;
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.add(new JScrollPane(new energon()));
frame.setSize(300, 300);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Now i want to add a timertask that regularly updates the position of one of the points in the plaPts Point3f array. However, when i call plaPts[1].setX(2), nothing happens on the screen, they remain in the same position.
Do you have to have each point in a separate TransformGroup (consisting of a shape3D with a Point3f array of size 1) for this to be possible? I'm later going to use 100000 points, is it bad for performance if they all are in separate TransformGroups? Is there an easier way of doing this? Something like shape3D.repaint(), that automatically updates the position of the points based on the new values in plaPTS.
Do not use 10000 TransformGroup instances, it will lead to horrible performance (compared to a single PointArray).
You should have a look at the GeometryArray JavaDocs, particularly about the distiction between using data by copying and by reference.
Newer versions of Java3D only support the by copying method. (Your approach would have worked with the by reference method, but you still would have to trigger an update).
The fact that the data is used by copying means that a copy of your Point3f array is created when you pass it to the constructor of the PointArray method. So modifications of these points will not affect the PointArray.
Instead, you can use the setCoordinate method to modify the points. Here is an example, you can simply add this method and pass your PointArray to it:
private static void moveOnePointOf(final PointArray pa)
{
pa.setCapability(PointArray.ALLOW_COORDINATE_WRITE);
Thread t = new Thread(new Runnable()
{
Point3f p = new Point3f();
#Override
public void run()
{
while (true)
{
pa.getCoordinate(0, p);
p.x = (float)Math.sin(System.currentTimeMillis()/1000.0);;
pa.setCoordinate(0, p);
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
}
}
});
t.setDaemon(true);
t.start();
}
Further notes and hints:
Don not use one thread for each point. This is just an example ;-)
Don't forget to set the capability for writing coordinates:
pointArray.setCapability(PointArray.ALLOW_COORDINATE_WRITE);
You might even consider to not use your Point3f array at all, and set the coordinates directly in the PointArray instead
On some systems, the native rendering components are somewhat odd. For Java3D or other OpenGL-based windows, this means that the window initially is gray, unless you resize or repaint it. You can avoid this by adding
System.setProperty("sun.awt.noerasebackground", "true");
as the first line of your main method.
In the following for each loop, I get a "bird cannot be resolved" error at lines marked !. I have setup an interface Bird implemented by an abstract class BirdType of which Cardinal, Hummingbird, Bluebird, and Vulture are children. getColor() and getPosition() methods are defined in the abstract class while fly() is unique to each of the child classes. This client code was actually provided by my professor to test the interface/inheritance relations I have setup. Note I have tested the interface, abstract class and child classes and they all seem to work. I think the issue is with the for-each loop but I can provide code for the other classes if needs be. Any advice?
import java.awt.*;
public class Aviary {
public static final int SIZE = 20;
public static final int PIXELS = 10;
public static void main(String[] args){
//create drawing panel
DrawingPanel panel = new DrawingPanel(SIZE*PIXELS,SIZE*PIXELS);
//create pen
Graphics g = panel.getGraphics();
//create some birds
Bird[] birds = {new Cardinal(7,4), new Cardinal(3,8),
new Hummingbird(2,9), new Hummingbird(16,11),
new Bluebird(4,15), new Bluebird(8,1),
new Vulture(3,2), new Vulture(18,14)};
while (true){
//clear screen
g.setColor(Color.WHITE);
g.fillRect(0, 0, SIZE*PIXELS, SIZE*PIXELS);
//tell birds to fly and redraw birds
for (Bird bird : birds)
bird.fly();
! g.setColor(bird.getColor());
! Point pos = bird.getPosition();
g.fillOval((int)pos.getX()*PIXELS, (int)pos.getY()*PIXELS,
PIXELS, PIXELS);
panel.sleep(500);
}
}
}
You need to wrap the body you want to be executed in the for loop in braces
for (Bird bird : birds) {
bird.fly();
g.setColor(bird.getColor());
Point pos = bird.getPosition();
g.fillOval((int)pos.getX()*PIXELS, (int)pos.getY()*PIXELS,
PIXELS, PIXELS);
panel.sleep(500);
}
Otherwise, the body of the for loop is the next statement following the ). So it is equivalent to
for (Bird bird : birds)
bird.fly();
// bird is not in scope anymore
g.setColor(bird.getColor());
Point pos = bird.getPosition();
g.fillOval((int)pos.getX()*PIXELS, (int)pos.getY()*PIXELS,
PIXELS, PIXELS);
panel.sleep(500);
As Darien has said in the comments, indentation is not part of the Java language, it has no bearing on the syntax. But you should use it to make your code easier to read, as expressed in the Java code style conventions.
Hey I am new java so forgive me if what I am about to ask is obvious, but I will try to explain as best as I can.
Its just a project that has been set for university so its not in a serious manner.
I have a class called MarsRoom which holds the attributes say for all the dimensions of the room like the totalheight and width of the walls in order to calculate the heat loss that the room will suffer in order to adjust the amount of solar energy that is needed to keep the room at the room temperature set.
The problem I am having is what is better practice or solution, to pass the attributes of the size of the room in a constructor(but this could get quite long in size, as the ones below are not only the ones that I may need) or create a whole different class specifically for that room like ROOM TYPE U? and set the attributes in there.
As it stands I can create a whole new room just by instantiating the room with the new values, but its going to get a little long, whereas I would rather not create a whole new class for a different room which may only differ from another room by a few meters on one of the walls!.
So what I am really trying to get at it, is is it ok to pass that many attributes to the constructor on instantiation?
//the instantiation in the runnable
MarsRoom room1 = new MarsRoom("RoomU", 40, 40, 20, 20, 8, 2, 4);
//the constructor in the MarsRoom class
public MarsRoom(String roomname, int windowsH, int windowsW, int wallsH, int wallsW, int windowC, int heaters, int lights){
name = roomname;
TotalWindowHeight = windowsH;
TotalWindowWidth = windowsW;
TotalWallHeight = wallsH;
TotalWallWidth = wallsW;
windowCeiling = windowC;
numheaters = heaters;
numlights = lights;
roomheaters = new Heaters[numheaters];
}
I'd say that you should be adding factory methods here.
Basically, keep your constructor, but add methods like
static Room createLaundryRoom(laundryRoomParameters) {
return new Room(...laundry room parameters plus defaults
common to all laundry rooms...);
}
One of the great benefits object oriented programming is the possibility of not repeating yourself in code. Hence objects, which define data (members) and functionality (methods), and no requirement to create instances of these "prototypes" with hard values until run-time. To create a new class for each room when it
may only differ from another room by a few meters on one of the walls
would be to deny OOP (and Java) by gross repetition. I'd stick with the constructors, and if similar kinds of rooms end up emerging, try one of the static factory methods suggested, or break up common functionality using inheritanceOracle.
Create a map with the keys being
Map<String, Integer> map = new HashMap();
map.put("TotalWindowHeight", new Integer(10));
map.put("TotalWindowWidth", new Integer(5));
...
map.put("NumberOfHeaters", new Integer(3));
MarsRoom room1 = new MarsRoom("RoomU", map);
Constructor will be like:
public MarsRoom(String roomname, HashMap<String, Integer> params) {
name = roomname;
TotalWindowHeight = map.get("TotalWindowHeight").intValue();
TotalWindowWidth = map.get("TotalWindowWidth").intValue;
...
roomheaters = new Heaters[map.get("NumberOfHeaters").intValue()];
}
this is not good OO however, but it seems like you are looking for something quick. If you want good OO you need to create an object for Window and in it you have hieght and width, another for ceiling, and you should not have number of something as a field, you should have an array to store the heater objects, and so and so forth, but this is quick and meets your requirement.
While technically legal, constructors with very long argument lists may be inconvenient to use. It also depends on whether you this the list may grow in the future or in subclasses.
If you have many parameters, but they have defaults and sometimes only a few need to be changed, you may find the Builder pattern useful. The idea is to replace constructor arguments with function calls, and allow them to be chained, for example:
public MarsRoom() {
//empty or just basic stuff set here
}
public MarsRoom setTotalWindowHeight(int TotalWindowHeight) {
this.TotalWindowHeight = TotalWindowHeight;
return this;
}
public MarsRoom setTotalWindowWidth(int TotalWindowWidth) {
this.TotalWindowWidth = TotalWindowWidth;
return this;
}
...
then, you can call:
MarsRoom room1 = new MarsRoom()
.setTotalWindowHeight(20)
.setTotalWindowWidth(40);
Of course, if you wanted to set all parameters this way, it's longer (thou maybe more readable) than the single constructor. But if you only set 2 parameters out of 10, it will usually be more convenient.
You don't show what the fields of MarsRoom are, but for each feature, I would have a Collection of sub-objects. A MarsRoom has-a List of Windows. A MarsRoom has-a List of Walls. etc... Then have setters and getters for each and methods to add new instances of these features.
Since this is for school, I'm only including a little bit of pseudo code.
public class MarsWindow {
int height;
int length;
// Setters & Getters
// standard getters & setters go here
int getArea() {
return this.height * this.width;
}
}
public class MarsRoom {
List<MarsWindow> windows;
List<MarsWall> walls;
List<MarsLight> lights;
List<MarsHeater> heaters;
public List<MarsWindow> addWindow(MarsWindow window) {
// Add a window to the "windows" list here
}
public List<MarsWall> addWall(MarsWall wall) {
// Add a wall to the "walls" list here
}
// Do this for the other fields
int getTotalWindowArea() {
int area = 0;
// Iterate over all windows
for(MarsWindow window : windows) {
area += window.getArea();
}
return area;
}
// Add other calculation methods here
}
If what you're trying to do is simply not duplicate the parameters you're passing the constructor, you can simply put them in a separate static method, like so:
public static MarsRoom newRoomU() {
return new MarsRoom("RoomU", 40, 40, 20, 20, 8, 2, 4);
}
You could also use some polymorphism or have different types of rooms or something similar to this and then have a superclass with the common values that all rooms will have.
You can also have more than one constructor and have different ones for values you wish to set depending on the room type etc.
Its always better to work with objects rather than primitives, you could use factory to create objects.
//the constructor in the MarsRoom class
public MarsRoom(String roomname, WindowDimension windowDimension, WallsDimensions wallDimension, RoomAmbience ambience){
}
public class WindowDimension{
private int height; //int windowsH
private int width; //int windowsW
private int circumference; //assumed windowC is circumference
}
public class WallsDimension{
private int height; //int wallsH
private int width; //int wallsW
}
public class RoomAmbience{
private int heaters;
private int lights;
}
So I have been busting my head over this program assignment and I have no idea how do finish it. I have already completed 80% of it but the last part I don't know even the general idea. The question is
"Then write a method randomVehicle that randomly generates Vehicle references, with an equal probability for constructing cars and trucks, with random positions. Call it 10 times and draw all of them."
I have a main method, an Abstract Superclass Vehicle and two subclasses of Car and Truck. I know how to do probability using loops but I have no idea how to take that probability answer (1 will be truck and 2 will be car) and use it to reference the predefine shapes of the car and and truck.
In other words, how would I create the program that after I hit compile and run, will make these randomly decided car and truck and display it.
Sorry if the question is confusing but I am just learning about abstract classes.
Here is the code thus far:
Main Method
import java.awt.*;
#SuppressWarnings("serial")
class drawTest extends Canvas{
public drawTest(){
setSize(800, 600);
setBackground(Color.white);
}
public static void main(String[] argS){
drawTest canvas = new drawTest();
Frame aFrame = new Frame();
aFrame.setSize(800, 600);
aFrame.add(canvas);
aFrame.setVisible(true);
}
public void paint(Graphics canvas){
Vehicle car = new CarTest();
car.paint(canvas);
paint(canvas);
}
}
CarTest Class
import java.awt.*;
#SuppressWarnings("serial")
public class CarTest extends Vehicle{
public void paint(Graphics canvas){
canvas.drawOval(10+super.x, 30+super.y, 15, 15); // Front Wheel
canvas.drawOval(45+super.x, 30+super.y, 15, 15); // Back Wheel
canvas.drawRect(5+super.x, 10+super.y, 60, 20); // Bottom of Car
canvas.drawRect(15+super.x, 5+super.y, 40, 5); // Bottom of Car
}
}
TructTest Class
import java.awt.*;
#SuppressWarnings("serial")
public class TruckTest extends Vehicle{
public void paint(Graphics canvas){
canvas.drawRect(30+super.x, 5+super.y, 100, 30); // Cargo Section
canvas.drawOval(30+super.x, 35+super.y, 15, 15); // Wheel Under Cargo
canvas.drawOval(45+super.x, 35+super.y, 15, 15); // Wheel Under Cargo
canvas.drawOval(100+super.x, 35+super.y, 15, 15); // Wheel Under Cargo
canvas.drawOval(115+super.x, 35+super.y, 15, 15); // Wheel Under Cargo
canvas.drawRect(5+super.x, 15+super.y, 20, 20); // Driver Section
canvas.drawOval(5+super.x, 35+super.y, 15, 15); // Wheel Under Driver
}
}
Abstract Vehicle Class
import javax.swing.*;
import java.awt.*;
#SuppressWarnings("serial")
public abstract class Vehicle extends JApplet{
public Vehicle(){
// Generates Random position
x = (int)(Math.random()*700);
y = (int)(Math.random()*550);
}
public abstract void paint(Graphics canvas);
public void randomVehicle(){
int carTruckChoice = (int)(1+Math.random()*2);
if (carTruckChoice == 1){
// don't know if this is the right implementation
}
else if (carTruckChoice == 2){
// don't know if this is the right implementation
}
}
public int x;
public int y;
}
Pseudocode to get you started:
randomVehicle =
randomPosition = randomPosition()
randomNumber = randomOneOrTwo()
if (randomNumber is 1)
vehicle = new Car()
else
vehicle = new Truck()
vehicle.setPosition(randomPosition)
return vehicle
for 1..10
randomVehicle().draw()
You'd have an abstract class Vehicle, and two concrete subclasses Car and Truck.
public void randomVehicle() {
Random random = new Random();
Vehicle vehicle;
for(int i = 0; i<10; i++) {
int randomChoice = random.nextInt(2);
if(randomChoice == 1){
vehicle = new Car();
} else {
vehicle = new Truck();
}
//draw vehicle here .. vehicle.draw();
}
}
Generally, StackOverflow does not accept homework questions, but I'll make an exception since this is basically an idiom, and since I take it on good faith that you've already tried this.
Here is a method that generates one random vehicle:
public static Vehicle randomVehicle(){
return (Math.random() < .5) ? new CarTest() : new TruckTest();
}
Explanation:
Math.random() generates a number between 0 and 1.
The expression in parentheses tests if it is less than one-half; i.e. it randomly chooses between true and false.
The question mark is the Java ternary operator. If the expression in parentheses returns true, vehicle is initialized to the first operand, i.e. a new instance of Car(), and vice versa for false.
If you want to generate random instances of A subclass then you need 2 things .
1) a data structure holding all "n" instance classes
2) a random distribution of equal probability of landing on any number from 0 to n-1.
The data structure can be implemented using any linear data structure in java ( a list , array , or enum) which holds classes. Then, by iterating through 100 random number generations, you can use reflection to generate instances of your class by directly accessing the class from your array (or enum, or list, etc) based on the index.
The advantage of this solution is that, as you add new car types, you need only to add the class name to the array structure... With no other changes to the underlying implementation.
i have a program i have to do where i have to take individual shape objects and combine them to create a final car shape. we are given premade shapes such as front tire, back tire, body, windshield, and roof and supposed to combine them into one car shape. the code already given to me is the following:
CompositeShape shape = new CompositeShape();
final double WIDTH = 60;
Rectangle2D.Double body
= new Rectangle2D.Double(0, WIDTH / 6,
WIDTH, WIDTH / 6);
Ellipse2D.Double frontTire
= new Ellipse2D.Double(WIDTH / 6, WIDTH / 3,
WIDTH / 6, WIDTH / 6);
Ellipse2D.Double rearTire
= new Ellipse2D.Double(WIDTH * 2 / 3, WIDTH / 3,
WIDTH / 6, WIDTH / 6);
shape.add(body);
shape.add(frontTire);
shape.add(rearTire);
now, i have to create the compositeShape class which is where the combining takes place, but im not sure what to do in the add(Shape) method. we were also told that we were supposed to use a pathiterator method, but we werent really taught about pathiterator or what we are supposed to do with it. Im not asking for someone to tell me what exactly to code, just some helpful starter points.
the first thing that came to my mind was something like this:
public class CompositeShape implements Shape {
Graphics2D g2;
public void add(Shape shape){
g2.draw(shape);
}
but it doesnt work because i cant instantiate a new graphics object and i get a null pointer exception. after that, im pretty much stumped as to what to do. any help would be greatly appreciated. thanks!
Probably, instead of drawing the Shape inside the add() method, you're just supposed to store the added Shape for drawing later. You could do that by giving CompositeShape some kind of collection to hold Shapes that are added, and that's all I'd put in the add() method. Beyond that, it would depend what other behavior CompositeShape is supposed to have. If you have to be able to draw a CompositeShape, then you'll probably be given an Graphics object to draw on. You won't have to create your own. Then drawing a CompositeShape would be drawing all of the Shapes that it contains.
java.awt.geom.Area can combine multiple shapes with methods add, subtract, exclusiveOr, and intersect. It's a ready-made class for CompositeShape.
It seems extremely weird that you've been asked to recreate it as "CompositeShape", because Area already does what you want.
The solution could be as simple as
class CompositeShape extends java.awt.geom.Area {}
and you're done.
Or, the fact that you've been given a hint about PathIterator, it might be that you're being encouraged to manage the added shapes in a list manually, then implement all the methods of the Shape interface in terms of iterating over the other shapes.
E.g., getBounds() needs to return the rectangular bounds of the shape, so get the rectangular bounds of the first, then use Rectangle.union to join it with the bounds of the others.
And for getPathIterator(), return a new inner class implementing PathIterator that will iterate over all the shapes in your collection, and iterate the path segments of each of their getPathIterator methods, returning each path segment.
It all sounds unnecessary in practice, since the needed class already exists. I think you should get clarification on what is wanted. Good luck.
To clarify what I said about the implementation of getPathIterator, return something like this. I didn't test this. This assumes your list is called shapes.
public PathIterator getPathIterator(final AffineTransform at, final double flatness) {
return new PathIterator() {
private PathIterator currentPathIterator;
private Iterator<Shape> shapeIterator = shapes.iterator();
{ nextShape(); }
private void nextShape() {
if (shapeIterator.hasNext()) {
currentPathIterator = shapeIterator.next().getPathIterator(at, flatness);
} else {
currentPathIterator = null;
}
}
public int getWindingRule() {
return WIND_NON_ZERO;
}
public boolean isDone() {
for (;;) {
if (currentPathIterator == null) return true;
if (!currentPathIterator.isDone()) return false;
nextShape();
}
}
public void next() {
currentPathIterator.next();
}
public int currentSegment(float[] coords) {
return currentPathIterator.currentSegment(coords);
}
public int currentSegment(double[] coords) {
return currentPathIterator.currentSegment(coords);
}
};
}