Currently I am trying to build an application that will visualize an xml tree in Processing. I am new to Processing (and Java for that matter) and I am having some problems getting my ideas into objects. Currently I have a Node class:
class Node {
//DATA
color textColor;
color boxColor;
float xpos = width/2;
float ypos = 100;
float nodeWidth;
float nodeHeight;
boolean overBox = false;
boolean active = false;
PFont font;
//CONSTRUCTOR
Node(){
textColor = color(0);//sets text color
boxColor = color(244);//sets box color
font = createFont("Gil Sans", 16, true);
textFont(font,50);
nodeWidth = textWidth("Modernism");//INPUT TEXT
nodeHeight = textAscent();//?textDescent()?
rectMode(RADIUS);
}
void displayText(){
fill(textColor);
text("Modernism",xpos-nodeWidth/2,ypos+nodeHeight/2.3);
}
void displayBox(){
//stroke(boxColor);
noStroke();
noFill();
rect(xpos, ypos, nodeWidth/2, nodeHeight/2);
//FOR DEBUGGING OVERBOX
//stroke(135);
//point(300,200);
}
void overBox(){
if(mouseX > xpos-nodeWidth/2 && mouseX < xpos+nodeWidth/2 &&
mouseY > ypos-nodeHeight/2 && mouseY < ypos+nodeHeight/2) {
overBox = true;
}else{
overBox = false;
}
}
void clicked(){
if(active) {
//If box was already clicked, trigger response
textColor = color(0);
overBox = false;
active = false;
}
if(overBox) {
//checks to see if click happened within bounds of box, makes active
textColor = color(100);
active = true;
}
}
boolean activeCheck(){
if(active == true){
return true;
}else{
return false;
}
}
}
And then I want a connection cable drawn between the parent and children of the XML document:
class Connectors{
//DATA
color lineColor;
int lineWeight;
int lineX1 = 12;
int lineY1 = 155;
int lineX2 = 12;
int lineY2 = 475;
//CONSTRUCTOR
Connectors(){
lineColor = color(0);
lineWeight = 2;
}
//FUNCTIONALITY
void displayConnection(){
stroke(lineColor);
strokeWeight(lineWeight);
line(lineX1,lineY1,lineX2,lineY2);
}
}
This is all very rough, but I was wondering how the connections should relate to the nodes. To build the connections, it needs to know where it's parent node is located. Should the connections be a subclass of it's respective node?
You should favor aggregation over inheritance in this case. That is, have the Connector keep a reference to two Nodes.
public class Connector {
// other members
private Node firstNode;
private Node secondNode;
public Connector(Node firstNode, Node secondNode) {
this.firstNode = firstNode;
this.secondNode = secondNode;
}
}
Now the Connector knows about the two Nodes it connects.
The reason why you want to do this is the fact that Nodes and Connectors don't share a lot in common. We have conventions for how we can translate OOP relationships into English.
Use "is a" to describe inheritance.
Use "has a" to describe aggregation and composition.
Which makes more sense? "Connector is a Node," or "Connector has a Node"?
Now, you might want to make an abstract parent class, or even better, an interface, for both of Connector and Node if they do share some things in common, such as the ability to draw them.
public interface MyDrawable {
/**
* Draw this object on the "processing" canvas
* note: 2rs2ts does not know anything about the "processing" library
*/
public void draw();
}
// another file...
public class Connector implements MyDrawable {
// all of the other stuff
public void draw() {
stroke(lineColor);
// etc.
}
}
Inheritance means that you pull everything the parent class does into the child class. (That's the reason for the keyword extends.) If there's stuff about Nodes that is totally separate from stuff about Connectors, don't have one inherit from the other. If you do want to gain OOP abstraction, separate the commonalities into an interface or a common parent class.
I'm assuming you want build your XML parser or tree as a exercise, if not use some java library to walk the xml tree. There are a lot of libraries to do that.
Don't represent connection as a subclass of Node, in no way. A connection is not a node. You can represent your Tree as a special graph, the node is the vertex and the connection is the edges.
You have the option to make the node has other nodes, and that is a connection.
Like this
public class Node {
Node parent;
Node left;
Node right;
// Or if there is N Connections, or as we say in tree leafs
List<Node> connections;
}
Then you use an algorithm to walk the nodes and display the connections starting form the initial node (header), you could use list or hashtables to represent your tree as a collection of nodes. If you study a little of Graph theory or Algorith and Structural Data in computer cience you can do that.
Related
I have a player which can feed a dog or chop a tree.
Below are the classes I have written:
public class Dog {
private int health;
public void feed(Food food){
health = health + food.getNutritionalValue();
}
}
public class Player{
public void feed(Dog dog, Food food) {
dog.feed(food);
}
Player and Dog both have methods that are "active".
Player feeds the dog and dog starts eating the food (I am not really sure if it is good to couple methods in this way).
On the other hand, I have tree. And player is able to chop the tree.
public class Player{
public void chop(Tree tree) {
//At this point I am not sure
}
I am not sure if I would use getters and setters of Tree class to interact with the Tree.
Or if I should write an own method for this because the tree gets chopped so it is nothing really active I would call.
So, in the end, there would be two or more kinds of implementations but the two I am thinking of are:
tree.setAmountofWood = x
or
tree.gettingChopped(Damage int)
I think I should make an own method for this chopping-process.
Or is there any design principle I should follow?
I see 3 principles here,
SRP - It is the responsibility of the Tree to get chopped and fall down, but to cut is the responsibility of the Person!
Demeter's law - looks good from my POV.
OCP - The tree must be able to do further actions when get cut.
So you must use
tree.gettingChopped(Damage damage)
To your code:
The method Dog.feed is wrong, rename it to Dog.eat because the Dog is not feeding, the dog is eating. By the way, the food must reduce its NutritionalValue.
The health is an integer value, this is bad because in reality there is nothing like a numeral health. We may have a handicapped numeral value in percent, but this is more a byte who not can be in negative value. You should create a custom class for the Health! This way your code is open(OCP) for extensions like to be toxified or depresive.
I would start from something like this.
Tree can grow and receive damage.
public class Tree {
private int lumber;
public Tree(int size) {
this.lumber = size;
}
public void grow() {
this.lumber++;
}
public void grow(int size) {
this.lumber += size;
}
public int receiveDamage(int damage) {
int lumber = 0;
if (damage > this.lumber) {
lumber = this.lumber;
this.lumber = 0;
} else {
lumber = damage;
this.lumber -= damage;
}
return lumber;
}
}
Food just stores nutritional value.
public class Food {
private int nutrition;
public Food(int nutrition) {
this.nutrition = nutrition;
}
public int getNutritionalValue() {
return this.nutrition;
}
}
I'm not sure if all types of player can chop trees, so I created a class to separate responsibilities. You can move methods to the Player class if you like.
public class Woodcutter extends Player {
public int chop(Tree tree) {
// lumber amount may depend on a tool,
// i.e. axe, chainsaw, etc.
return tree.receiveDamage(10);
}
// fell down the tree
public int fell(Tree tree) {
int result = 0;
int lumber = 0;
do {
lumber = chop(tree);
result += lumber;
} while (lumber > 0);
return result;
}
}
Somewhere in your code
// create a tree and let it grow for a while
Tree tree = new Tree(10);
tree.grow(90);
// Start chopping
Woodcutter woodcutter = new Woodcutter();
System.out.println("Lumber received: " + woodcutter.chop(tree));
System.out.println("Lumber received: " + woodcutter.fell(tree));
Dog dog = new Dog();
Food food = new Food(5);
woodcutter.feed(dog, food);
I wouldn't dive into passive/active methods here. An 'active tree' may indeed be a misnomer.
I would rather consider calling an object's method as passing a message to the object. And you apparently need to send the message to the tree that it is currently being cut by someone, and let the tree decide when to e.g. fall() or to bend(), or to shake().
The tree has some internal state (strength? thickness of its trunk? health?). 'Sending a message' to the tree means to call its method, e.g. beingCut(), which in turn deteriorates the state of the tree. After the state of the tree reaches a certain limit, other actions (=consequences of tree's bad state) may be started by the tree.
Of course, as in every iteration of your main loop you tree has also the chance to get the message to grow(), so its state may improve a little each time, so eventually it may even recover from being only partially cut and reach its initial, perfect state back.
So, yes, while trees seem rather passive, they still react to messages/stimulus. :-)
Im working on a game in which multiple "notes" (sprites) are generated.
The notes are created at random. Each of them has a random velocity and are created in a different thread. The Notes class is a child of the sprite class. It has 2 properties and 1 method:
vel - a Velocity2 object holding the x and y component on the
velocity of the note object
pos - a Vector2 object holding the x and y coordinates of the note object.
changepos() - a method that changes the position based on the velocity of the object
(I cannot post the code of that class due to privacy reasons)
I currently have a static class "NoteStack", which can hold up to 64 references to Notes objects.
public class NoteStack {
public Notes[] note_array;
public int stack_len;
public NoteStack(){
note_array = new Notes[64];
stack_len = 0;
}
public void push(Notes n){
if(stack_len<64){
note_array[stack_len] = n;
stack_len++;
Gdx.app.log("push", "pushed");
}
}
public void delete_note(int pos){
if(note_array[pos] != null){
note_array[pos] = null;
for(int i = pos; i<stack_len; i++){
note_array[pos] = note_array[pos+1];
}
note_array[stack_len] = null;
stack_len = stack_len - 1;
}
}
}
Here's the code for my "update" function
public void update(float d, SpriteBatch b){
core.draw(b);
for(int i = 0; i< noteStack.stack_len; i++){
Gdx.app.log("update", "Update function running" + i);
noteStack.note_array[i].changePos(d);
noteStack.note_array[i].draw(b);
// scr_w - screen width , scr_h - screen height
if(noteStack.note_array[i].pos.x > scr_w || noteStack.note_array[i].pos.x < 0 || noteStack.note_array[i].pos.y > scr_h || noteStack.note_array[i].pos.y < 0){
noteStack.delete_note(i);
}
}
}
The issue (as you may see) is that whenever a note object from NoteStack gets removed (i.e. the delete_note method is called), other Notes objects in the array are affected.
Hence my question: What is the best way to reference multiple sprite (note) objects in LibGDX?
Generally speaking in programming, you should never implement own "classic" datastructures, only if it's really necessary and you can't use or extend a collection type, because the standard implementations are well programmed and tested, so those are safer to use.
In your case, I would use libGDX Array. That class has add, get, size methods, and if you really want to, you can extend the Array class to have an update function.
But in short, if you replace public Notes[] note_array; with public Array<Notes> note_array = new Array<>(true, 64); and use get and remove and size for iterating and managing the collection that should work.
I'm creating a project which models an airport landing system. I have a plane object which stores all the information I need to sort the plane into a queue and store in a database. All the vital information is included in the object but I have also included the co-ordinates for each plane. My issue is that it may not be considered cohesive because each plane does a lot of different things.
I just want to know if this is considered bad design or is there a better way to do this?
Also, what is the "rule" for cohesion inside of objects? is there a specific design pattern that can maybe deal with this?
public class Plane extends Aircraft {
/*
* Flight status should only take one of these enum values
*/
private static enum Status {
REGISTERED, IN_QUEUE, LANDING, LANDED
};
// Set aircraft status to REGISTERED when created
private Status status = Status.REGISTERED;
private double fuelLevelPercentage;
private int passengerCount;
private int aircraftNumber;
private String airlineCompany;
private String departureAirport;
// This is used by the constructor to assign a random city to each new Aircraft
private final String[] cities = { "Rome", "Berlin", "Heathrow",
"Edinburgh", "Cardiff", "Dublin", "Stansted" };
// Used to set airline companies
private final String[] airLineCompanies = { "Easyjet", "Ryanair",
"British Airways","Flybe","Air Lingus", "Virgin" };
// Random number generator used by the constructor
private Random rand;
// Thread for this instance of Aircraft
private Thread aircraftThread;
// Radius of path when flying in circle (km?)
private final double FLIGHT_RADIUS = 10;
// Time taken to complete one complete loop (ms)
private final double FLIGHT_PERIOD = 120000;
// Angular frequency omega (rad/s)
private double OMEGA = 2 * Math.PI / FLIGHT_PERIOD;
// Time taken between being directed to land, and landing (ms)
private int TIME_TAKEN_TO_LAND = 30000;
// Time take to use one percent of fuel (ms)
private double time_taken_to_use_one_percent_of_fuel = 30000;
// variable to keep track of time since instantiated (ms)
private int time = 0;
// The aircraft Thread sleeps for TIME_STEP between updating
private final int TIME_STEP = 20;
private int time_when_called_to_land;
private int hour_of_arrival;
private int minute_of_arrival;
/*
* Set coordinates at time zero
*/
private double x_coord = 0;
private double y_coord = FLIGHT_RADIUS;
private double altitude = 1000;
/*
* Used to calculate path to airport
*/
private double x_coord_when_called;
private double y_coord_when_called;
private double altitude_when_called;
Calendar calendar = Calendar.getInstance();
/**
* This constructor sets the following fields to random values Dummy Data -
* should have a better way to do this
*/
public Plane() {
rand = new Random();
this.fuelLevelPercentage = rand.nextInt(100);
this.departureAirport = cities[rand.nextInt(cities.length)];
this.passengerCount = rand.nextInt(500);
this.aircraftNumber = rand.nextInt(50000000);
this.airlineCompany = airLineCompanies[rand.nextInt(airLineCompanies.length)];
}
/**
* this fly method will call on a different method depending on the status
* of the Aircraft
*/
public void fly() {
if (status == Status.REGISTERED) {
useFuel();
} else if (status == Status.IN_QUEUE) {
flyInCircle();
useFuel();
} else if (status == Status.LANDING) {
flyToAirport();
useFuel();
} else if (status == Status.LANDED) {
}
}
public void flyInCircle() {
x_coord = FLIGHT_RADIUS * (Math.cos(OMEGA * (time)));
y_coord = FLIGHT_RADIUS * (Math.sin(OMEGA * (time)));
}
public void flyToAirport() {
if (!(x_coord < 1 && x_coord > -1 && y_coord < 1 && y_coord > -1
&& altitude < 1 && altitude > -1)) {
x_coord -= x_coord_when_called * TIME_STEP / TIME_TAKEN_TO_LAND;
y_coord -= y_coord_when_called * TIME_STEP / TIME_TAKEN_TO_LAND;
altitude -= altitude_when_called * TIME_STEP / TIME_TAKEN_TO_LAND;
} else {
System.out.println("Aircraft landed");
status = Status.LANDED;
hour_of_arrival = calendar.get(Calendar.HOUR_OF_DAY);
minute_of_arrival = calendar.get(Calendar.MINUTE);
}
}
/**
* This method changes the flight status to IN_QUEUE - simulates telling the
* plane to join queue
*/
public void directToJoinQueue() {
setFlightStatus(Status.IN_QUEUE);
}
/**
* This method changes the flight status to LANDING - simulates telling the
* plane to land
*/
public void directToflyToAirport() {
setFlightStatus(Status.LANDING);
time_when_called_to_land = time;
x_coord_when_called = x_coord;
y_coord_when_called = y_coord;
altitude_when_called = altitude;
}
/**
* This method reduces fuel level according to fuel usage
*/
private void useFuel() {
if (this.fuelLevelPercentage - TIME_STEP
/ time_taken_to_use_one_percent_of_fuel > 0) {
this.fuelLevelPercentage -= TIME_STEP
/ time_taken_to_use_one_percent_of_fuel;
} else {
this.fuelLevelPercentage = 0;
}
}
/**
* this method sets the flight status
*/
private void setFlightStatus(Status status) {
this.status = status;
}
public double getfuelLevelPercentage() {
return fuelLevelPercentage;
}
public int getPassengerCount() {
return passengerCount;
}
public void setPassengerCount(int passengerCount) {
this.passengerCount = passengerCount;
}
public int getAircraftNumber() {
return aircraftNumber;
}
public String getDepartureAirport() {
return departureAirport;
}
public void stop() {
;
}
public String getAirlineCompany() {
return airlineCompany;
}
public void setAirlineCompany(String airlineCompany) {
this.airlineCompany = airlineCompany;
}
#Override
public String toString() {
if (status == Status.LANDED) {
return String
.format("Flight %-8d | Fuel %-4.1f | Passengers %-3d | From %-10s | %-8s at %d:%d ",
aircraftNumber, fuelLevelPercentage,
passengerCount, departureAirport, status,
hour_of_arrival, minute_of_arrival);
} else {
return String
.format("Flight %-8d | Fuel %-4.1f | Passengers %-3d | From %-10s | %-8s | Coords (%-3.2f,%-3.2f) | Altitude %-4.2f",
aircraftNumber, fuelLevelPercentage,
passengerCount, departureAirport, status, x_coord,
y_coord, altitude);
}
}
public void start() {
aircraftThread = new Thread(this, this.getClass().getName());
aircraftThread.start();
}
#Override
public void run() {
try {
while (true) {
calendar = Calendar.getInstance();
fly();
Thread.sleep(TIME_STEP);
time += TIME_STEP;
}
// System.out.println("aircraft number "+aircraftNumber+" safely landed");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Cohesion is a difficult concept. Despite the other answer's flippant responses, the true answer depends very much on what your system does and how it works. For example lets examine the queue mechanism. In your system, does a plane respond to commands differently when in a queue? If so, then the fact that it is in a queue should be integral to the plane. Does it respond differently when in different queues? If so then the queue itself should be integral to the plane. If, however, it's the airport that responds differently because the plane is in a queue, then the airport should control the queue and the plane should know nothing about it -- it should simply be given a flight path by the airport (or by the control tower at the airport, depending on the resolution of your model).
Cohesion isn't your only problem here. Encapsulation is also a big issue. You are letting other objects have access to your internal state. To model this in a fully OO way, you should consider using the CQRS pattern. If you also consider DDD (Domain Driven Design) techniques, and start by identifying your bounded contexts and aggregate routes, you'll be more likely to derive a correct design.
There's no "standard" for Java or any other language.
I have a "plane" object which stores all the information I need to
sort the plane into a queue and pass to a database. All the vital
information is included in the object but I have also included the
co-ordinates for each plane.
I think your Plane model object is doing too much.
I don't see why it should know whether or not it's in a queue. I'd have a separate object that owns the queue know the rules.
Is queue an in-memory collection or a message queue? Does it matter to you?
Model objects persisting themselves is a point of debate. I think it's easier to separate persistence into a separate data access object so it's easier to test.
Your model might look like this:
package model;
public class Plane {
private int id;
public void save() {
// persist the state of this
// INSERT INTO PLANE(id) VALUES(?)
}
}
I'd have a DAO interface in a separate package:
package persistence;
public interface PlaneDAO {
void save(Plane p);
}
Cohesion can be defined as the degree to which the elements of a module belong together.
Visualizing it helps. Imagine the attributes of a class and the methods. If your class is cohesive, it means the methods are going to use many of the attributes, and conversely, the attributes will be used by many of the methods. This is the "sticking together" notion of cohesion. I like the following visualization that comes from NDepend's placemat:
As others pointed out, the methods that direct the plane (e.g., directToX) are possibly outside of the "theme" of what is a Plane, but they're not flagrantly wrong. Those elements (responsibilities) might be better in another class, say, AirTrafficController. In reality, planes don't decide much how they fly. Their pilots must follow instructions from the ground.
I'd argue that the Thread stuff (start, run) is definitely outside the theme of a Plane. Those methods hardly use anything that are part of a Plane (they are distractions from its theme). You could use an anonymous inner class to handle the processing in a thread from the main and your Plane would be even more reusable (and cohesive).
A cohesive object gets to the essence of the thing it models. This means it could more likely be re-used easily in another application (or even another OO language). Anything that starts to creep outside the true theme of your concept will likely make it harder to re-use the concept in another application. The "distractions" don't make sense anymore in another application.
If you're developing a Kamikaze project (one where you just want to make it work and don't care about re-use), it's perfectly OK to forget about cohesion (and other design elements). Design choices are trade-offs. You could refactor your Plane class to make it more cohesive, but if you never reuse it in another application, you've perhaps wasted your time. On the other hand, design is a learning process; even if you over-design something for one application, you maybe learned something for the next.
Finally, all design aspects are difficult to quantify and therefore there are few standards. Some companies have been known to set (arbitrary) standards for metrics such as LCOM in their development processes. I've read of team standards that say if a class has bad value for LCOM, then it must be refactored until its value goes low enough (its cohesion is stronger). Unfortunately, LCOM can be a bad measure of cohesion (especially in classes that have lots of get/set methods).
There is no java standard regarding object cohesion. (I don't repeat the advices of duffymo, I agree with all of them).
One thing to keep in mind when you elaborate an object model mapping the real world is to try to have one class mapping one concept of the real-world.
As an illustration, in your sample code you have at least 2 distincts concepts : Plane and Flight and so you can split them into 2 separate classes with a one-to-many relationship between them.
I'm developing a Java application and I'm using the JUNG library.
In my application I first create a DelegateTree and draw it to the screen:
public static GraphZoomScrollPane generateTree(Tree tree,
GraphicalUserInterface gui) {
/* Create a new tree */
edu.uci.ics.jung.graph.Tree<Node, Edge> graphTree = new DelegateTree<Node, Edge>();
/* Add all nodes and vertices to the tree */
graphTree.addVertex(tree.getRoot());
addChildren(tree.getRoot(), graphTree);
/* Create the visualization */
TreeLayout<Node, Edge> treeLayout = new TreeLayout<Node, Edge>(graphTree);
VisualizationViewer<Node, Edge> vv = new VisualizationViewer<Node, Edge>(treeLayout);
vv.setBackground(Color.WHITE);
vv.getRenderContext().setEdgeLabelTransformer(new ToStringLabeller<Edge>());
vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line<Node, Edge>());
vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller<Node>());
vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.S);
vv.addGraphMouseListener(new ClickNode(gui, vv));
final DefaultModalGraphMouse<Node, Edge> graphMouse = new DefaultModalGraphMouse<Node, Edge>();
graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING);
vv.setGraphMouse(graphMouse);
return new GraphZoomScrollPane(vv);
}
Afterwards the user is able to add new children to the leaves of my tree. But when I just do
graphTree.addEdge(edge, parent, child);
and then redraw the VisualizationViewer, the visualization lost the 'Tree' structure. It just adds the child somewhere above the parent and all other children of that new child right on top of it.
Is there a better way to dynamically add children to the leaves of my tree? Or do I have to use something else for redrawing instead of just vv.repaint()?
Any help would really be appreciated.
An example of what happens:
http://www.dylankiss.be/JUNGExample.PNG
Starting with just the root (OUTLOOK), after adding 3 children (Leaf, Leaf, Leaf) with different edges (sunny, overcast, rainy), they just appear on top of each other.
EDIT: This is the addChildren() method.
private static void addChildren(Node node, edu.uci.ics.jung.graph.Tree<Node, Edge> tree) {
for (int i = 0; i < node.getChildren().size(); i++) {
tree.addEdge(new Edge(node.getChildren().get(i).getParentValue()), node, node.getChildren().get(i));
addChildren(node.getChildren().get(i), tree);
}
}
EDIT 2: This is the part of an AWT ActionListener where I add new children to the tree.
while (there are still edges to be added) {
value = name of new edge;
child = new Node(this.m_node, value);
this.m_node.addChild(child);
graphTree.addEdge(new Edge(value), this.m_node, child);
}
Posting the method that is in charge of adding new edges would help here :)
But at first glance, it seems that you are adding 3 different edges between the same two nodes (OUTLOOK and Leaf). I'm guessing you are doing this (or the equivalent with Node and Edge instances):
graphTree.addChild("sunny", "OUTLOOK", "Leaf");
graphTree.addChild("overcast", "OUTLOOK", "Leaf");
graphTree.addChild("rainy", "OUTLOOK", "Leaf");
In this case, as JUNG graphs maintain unicity of nodes, you end up with only two nodes, and 3 different edges between them. When JUNG tries to display this graph, you will get the two nodes and the 3 overlapping edges as you used EdgeShape.Line.
If you original goal was indeed to set 3 different edges between two nodes, try using different edge shapes to avoid overlapping and get a better rendering, e.g. EdgeShape.BentLine or such.
If you wanted 3 different nodes, you will have to use 3 different names, or 3 different Node instances which are not equals.
Good luck :)
EDIT:
Following your comment, I took a look at the TreeLayout sources, and there is a small issue which makes it impossible to dynamically update the layout.
To fix the problem, use this class instead:
import edu.uci.ics.jung.algorithms.layout.TreeLayout;
import java.awt.Point;
import java.util.Collection;
import edu.uci.ics.jung.graph.Forest;
import edu.uci.ics.jung.graph.util.TreeUtils;
public class DynamicTreeLayout<V, E>
extends TreeLayout<V, E>
{
public DynamicTreeLayout(Forest<V, E> g) {
this(g, DEFAULT_DISTX, DEFAULT_DISTY);
}
public DynamicTreeLayout(Forest<V, E> g, int distx) {
this(g, distx, DEFAULT_DISTY);
}
public DynamicTreeLayout(Forest<V, E> g, int distx, int disty) {
super(g, distx, disty);
}
protected void buildTree() {
alreadyDone.clear(); // This was missing and prevented the layout to update positions
this.m_currentPoint = new Point(20, 20);
Collection<V> roots = TreeUtils.getRoots(graph);
if (roots.size() > 0 && graph != null) {
calculateDimensionX(roots);
for (V v : roots) {
calculateDimensionX(v);
m_currentPoint.x += this.basePositions.get(v) / 2 + this.distX;
buildTree(v, this.m_currentPoint.x);
}
}
}
private int calculateDimensionX(V v) {
int localSize = 0;
int childrenNum = graph.getSuccessors(v).size();
if (childrenNum != 0) {
for (V element : graph.getSuccessors(v)) {
localSize += calculateDimensionX(element) + distX;
}
}
localSize = Math.max(0, localSize - distX);
basePositions.put(v, localSize);
return localSize;
}
private int calculateDimensionX(Collection<V> roots) {
int localSize = 0;
for (V v : roots) {
int childrenNum = graph.getSuccessors(v).size();
if (childrenNum != 0) {
for (V element : graph.getSuccessors(v)) {
localSize += calculateDimensionX(element) + distX;
}
}
localSize = Math.max(0, localSize - distX);
basePositions.put(v, localSize);
}
return localSize;
}
}
You will also need to add the following if you want the layout to be updated and the viewer to be repainted for each modification of your graph:
layout.setGraph(g);
vv.repaint();
I have a design and object structuring related question. Here is the problem statement:
I have a Robot object which is suppose to traverse the ground on its own. It would be provided movement instructions and it must parse accordingly. For example sample input would be:
a. RotateRight|Move|RotateLeft|Move|Move|Move
Where move is a unit movement on a grid.
I did a very basic design in java. (Complete Code Pasted below)
package com.roverboy.entity;
import com.roverboy.states.RotateLeftState;
import com.roverboy.states.RotateRightState;
import com.roverboy.states.State;
public class Rover {
private Coordinate roverCoordinate;
private State roverState;
private State rotateRight;
private State rotateLeft;
private State move;
public Rover() {
this(0, 0, Compass.NORTH);
}
public Rover(int xCoordinate, int yCoordinate, String direction) {
roverCoordinate = new Coordinate(xCoordinate, yCoordinate, direction);
rotateRight = new RotateRightState(this);
rotateLeft = new RotateLeftState(this);
move = new MoveState(this);
}
public State getRoverState() {
return roverState;
}
public void setRoverState(State roverState) {
this.roverState = roverState;
}
public Coordinate currentCoordinates() {
return roverCoordinate;
}
public void rotateRight() {
roverState = rotateRight;
roverState.action();
}
public void rotateLeft() {
roverState = rotateLeft;
roverState.action();
}
public void move() {
roverState = move;
roverState.action();
}
}
package com.roverboy.states;
public interface State {
public void action();
}
package com.roverboy.entity;
import com.roverboy.states.State;
public class MoveState implements State {
private Rover rover;
public MoveState(Rover rover) {
this.rover = rover;
}
public void action() {
rover.currentCoordinates().setXCoordinate(
(Compass.EAST).equalsIgnoreCase(rover.currentCoordinates()
.getFacingDirection()) ? rover.currentCoordinates()
.getXCoordinate() + 1 : rover.currentCoordinates()
.getXCoordinate());
rover.currentCoordinates().setXCoordinate(
(Compass.WEST).equalsIgnoreCase(rover.currentCoordinates()
.getFacingDirection()) ? rover.currentCoordinates()
.getXCoordinate() - 1 : rover.currentCoordinates()
.getXCoordinate());
rover.currentCoordinates().setYCoordinate(
(Compass.NORTH).equalsIgnoreCase(rover.currentCoordinates()
.getFacingDirection()) ? rover.currentCoordinates()
.getYCoordinate() + 1 : rover.currentCoordinates()
.getYCoordinate());
rover.currentCoordinates().setYCoordinate(
(Compass.SOUTH).equalsIgnoreCase(rover.currentCoordinates()
.getFacingDirection()) ? rover.currentCoordinates()
.getYCoordinate() - 1 : rover.currentCoordinates()
.getYCoordinate());
}
}
package com.roverboy.states;
import com.roverboy.entity.Rover;
public class RotateRightState implements State {
private Rover rover;
public RotateRightState(Rover rover) {
this.rover = rover;
}
public void action() {
rover.currentCoordinates().directionOnRight();
}
}
package com.roverboy.states;
import com.roverboy.entity.Rover;
public class RotateLeftState implements State {
private Rover rover;
public RotateLeftState(Rover rover)
{
this.rover = rover;
}
public void action() {
rover.currentCoordinates().directionOnLeft();
}
}
package com.roverboy.entity;
public class Coordinate {
private int xCoordinate;
private int yCoordinate;
private Direction direction;
{
Direction north = new Direction(Compass.NORTH);
Direction south = new Direction(Compass.SOUTH);
Direction east = new Direction(Compass.EAST);
Direction west = new Direction(Compass.WEST);
north.directionOnRight = east;
north.directionOnLeft = west;
east.directionOnRight = north;
east.directionOnLeft = south;
south.directionOnRight = west;
south.directionOnLeft = east;
west.directionOnRight = south;
west.directionOnLeft = north;
direction = north;
}
public Coordinate(int xCoordinate, int yCoordinate, String direction) {
this.xCoordinate = xCoordinate;
this.yCoordinate = yCoordinate;
this.direction.face(direction);
}
public int getXCoordinate() {
return xCoordinate;
}
public void setXCoordinate(int coordinate) {
xCoordinate = coordinate;
}
public int getYCoordinate() {
return yCoordinate;
}
public void setYCoordinate(int coordinate) {
yCoordinate = coordinate;
}
public void directionOnRight()
{
direction.directionOnRight();
}
public void directionOnLeft()
{
direction.directionOnLeft();
}
public String getFacingDirection()
{
return direction.directionValue;
}
}
class Direction
{
String directionValue;
Direction directionOnRight;
Direction directionOnLeft;
Direction(String directionValue)
{
this.directionValue = directionValue;
}
void face(String directionValue)
{
for(int i=0;i<4;i++)
{
if(this.directionValue.equalsIgnoreCase(directionValue))
break;
else
directionOnRight();
}
}
void directionOnRight()
{
directionValue = directionOnRight.directionValue;
directionOnRight = directionOnRight.directionOnRight;
directionOnLeft = directionOnRight.directionOnLeft;
}
void directionOnLeft()
{
directionValue = directionOnLeft.directionValue;
directionOnRight = directionOnLeft.directionOnRight;
directionOnLeft = directionOnLeft.directionOnLeft;
}
}
Now my doubt is with this last class "Direction" and "Coordinate". coordinate represents a coordinate object for rover which helps it maintain its direction. Currently to keep track of direction I am using a doubly linked list of Direction objects, which pretty much work like a compass. Rotate left or right.
Here are the questions that I have.
1. I have used state pattern and shown design for direction tracking. Is there a better approach to simplify even this? Rem. I need to maintain coordinates correctly; such that if you move towards +y axis, my coordinates should be in + else in minus. Same for X axis.
Currently the responsibility for changing the face of the rover is indirectly delegated to coordinates and to direction class. Is this really correct? Isn't rover responsible for maintaining direction? Am I really right in my design to delegate that responsibility down to coordinate and direction class; just because it is easier to manipulate it there?
Any simple design improvements and suggestions on code will be most welcome. Feel free to critique.
Thanks for your patience and feedback; in advance.
Here's a Direction enum I came up with the other day, of which I am perhaps inordinately fond. Perhaps you will find it useful in your code.
import java.awt.Point;
public enum Direction {
E(1, 0), N(0, 1), W(-1, 0), S(0, -1);
private final int dy;
private final int dx;
private Direction(int dx, int dy) {
this.dx = dx;
this.dy = dy;
}
public Direction left() {
return skip(1);
}
public Direction right() {
return skip(3);
}
public Direction reverse() {
return skip(2);
}
private Direction skip(int n) {
final Direction[] values = values();
return values[(ordinal() + n) % values.length];
}
public Point advance(Point point) {
return new Point(point.x + dx, point.y + dy);
}
}
You're asking for how to simplify. If I may suggest something bold, why not use an opaque int for direction and have a static class to deal with it? By "opaque int" I mean that your code would never use it directly, but only as argument to the Direction class.
Here's some partial java-styled pseudocode to show what I mean.
// 0 = east, 1 = north, 2 = west, ...
public class Direction {
static int [] moveX = [ 1, 0, -1, 0];
static final int NORTH = 1;
// coordinates after moving one step in the given direction
static Pair move(int direction, Pair old) {
return new Pair( old.x + moveX[direction] , old.y + moveY[direction] );
}
static int turnLeft(int direction) {
return (direction+1) % 4;
}
static int turnRight(int direction) {
return (direction+3) % 4;
}
}
This way of doing things would have the advantage of using fewer allocations, so the garbage collector won't need to run as often. Another advantage is that the design remains object-oriented in the sense that you can easily change the direction class if later you want to be able to rotate by e.g. 45 degrees at a time.
To answer your other questions, I think it's perfectly fine to delegate to the Direction class the task of changing a coordinate along a certain direction. The rover would be responsible for maintaining direction only in the sense that the rover object would contain an int field to store the direction it's facing.
The first thing What comes to my mind when I see this code is that Direction should not have a String field directionValue, but rather a field storing a Compass (i.e. Compass.EAST, Compass.WEST). This would get you rid of the String comparisons in MoveState.action() and should therefore make your code considerably cleaner.
There also seems to be a problem with naming: maybe NORTH, EAST, WEST, and SOUTH should be in an enum called Direction (instead of Compass), and directionOnRight() etc. in the current Direction implementation should be its static methods (getting the current direction as a single argument, and returning the right/left/reverse direction)? You don't really need to store them in extra fields IMHO (remember the saying about premature optimization ;-).
My immediate thought upon looking at this is some confusion. The Rover class has 4 states and a direction, which seems a little counter intuitive. I would expect a position and a direction (for State I would, perhaps, expect, ON/OFF/RECHARGING or something similar).
So, I would investigate Java enums and have a NORTH/SOUTH/EAST/WEST Direction enum for the direction. The position (coordinate) has x/y positions, and to move, I would simply implement a deltaX() and deltaY() on the facing enumeration (it looks like Carl has just posted something similar)
Then your movement code would simply look like:
x += facing.deltaX()
y += facing.deltaY()
whichever direction you're facing. Note this doesn't delegate the movement. The Rover always moves, but the Direction enumeration gives it the dx/dy to change by.
The enumeration can also have methods clockwise() and counterClockwise(), so calling NORTH.clockwise() would return your new facing value EAST. Each enumeration instance would only have the delta and clockwise/counter-clockwise methods, and your Rover simply has the following:
private Direction facing;
private int x;
private int y;
which seems much more intuitive and what I'd expect. I've expressed x and y separately, but you may want to wrap in one class. If you do that, then the Direction enumeration should handle such an object, and not rely on it being broken apart again into x and y.
It seems too complicated for me. I think it should be done in such a way: let your robot know his turning angle. Then if he is asked to turn left or right he will just change this angle. When he is asked to move he will move according to this angle in x,y coordinates. angle can be stored like compass or even simplier with real angle (0, 90, 180, 270). It is easy to move robot in angle direction by multiplying movement step on sin(angle) and cos(angle). Why cant it be that simple? It will also handle more directions that just 4 and youll be able to move in any step range.