Checking if thousands of rectangles intersect - java

I'm currently working on a Bukkit plugin to claim custom areas, and I'm using rectangles (and .intersect()) to check if regions overlap before creating a claim.
I'm trying to figure a way where I don't need to check every single existing claim (of which there eventually will be tens of thousands) as surely this will take quite some time. I'll also need to check for claim owners when players do things such as break blocks or place blocks.
In my current system (which doesn't allow custom claim sizes, only squares) I only need to check at most about 10 claims because I can detect claims within the vicinity of the claim (at most 64 blocks away which is the max radius of claims in this system) but now the claim sizes can be infinitely large in theory with the new system.
Is checking all the rectangles going to take a massive amount of time? Am I being dumb, is there a way to check for rectangles within the vicinity even while the size is unlimited?

First of all checking thousands of rectangles is not gonna be a big deal for java(or your Plugin). Its simple math and should be done in millisecs. To deal with your owner Problem i would recommend you to create my own rectangle and owner class. So your rectangle can have a defined owner and you can simply check if the player is the owner of the area he is in right now.
public class custom_Area extends Rectangle{
private owner o;
public owner getOwner() {
return o;
}
public void setOwner(owner o) {
this.o = o;
}
}
EDIT:
I just tested it by creating 100.000 random rectangles and checking if one of them intersects with others.
--Custom rectangle class
public class area extends Rectangle{
private owner o;
public area(owner o, int i, int i1, int i2, int i3) {
super(i, i1, i2, i3);
this.o = o;
}
public owner getO() {
return o;
}
public void setO(owner o) {
this.o = o;
}
}
--Custom owner class
public class owner {
String name;
public owner(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
--Main class
public class Rectanglesearch {
public static area a[] = new area[100000];
public static owner o[] = new owner[10];
public static int intersectCounter = 0;
public static int ownerCounter = 0;
public static void main(String[] args) {
for(int y = 0; y<10;y++){
o[y] = new owner("y");
}
for (int i = 0; i < 100000; i++) {
a[i] = new area(o[(int)(Math.random() * 10)],random(),random(),random(),random());
}
checkArea(a[10]);
checkOwner(o[3]);
System.out.println("Area a[10] intersects with "+intersectCounter+" out of "+a.length);
System.out.println("Owner o[3] owns "+ownerCounter+" areas out of "+a.length);
}
public static int random(){
return (int)(Math.random() * 100000) + 1;
}
public static void checkArea(area ab){
for (area a1 : a) {
if (ab.intersects(a1)) {
intersectCounter +=1;
}
}
}
public static void checkOwner(owner ob){
for (area a1 : a){
if(a1.getOwner()==ob){
ownerCounter +=1;
}
}
}
}
method checkArea(area ab) returns you how man areas intersects with area ab
method checkOwner(owner ob) return you how man areas are owned my ob

Consider storing your rectangles in an acceleration structure such as a quadtree. To test a new rectangle against the existing set, you'd navigate down the tree to the node that would contain it, testing against the rectangles in each node along the way, but ignoring the rectangles in all the nodes you don't traverse. This quickly eliminates lots of rectangles that can't possibly intersect the new one, without having to test each one individually.
Other acceleration structures are also possible as alternatives, such as binary space partitioning. Read about spatial indexes for a list of several others that may be relevant.
Adding new rectangles to the set doesn't happen very often, so performance probably isn't a big concern. But I'd imagine that your plugin also needs to check whether a specific coordinate (such as a block) is within one of the claimed regions, and that may happen much more often — potentially every frame — so it really does need to be fast. A quadtree or other acceleration structure will be valuable for that.

Related

How to compare two objects with random constructors

I made these 2 objects:
Fighter Lucas = new Fighter(Statistics.punchStrength,
Statistics.movementSpeed, Statistics.reflex);
Opponent Simon = new Opponent(Statistics.punchStrength2,
Statistics.movementSpeed2, Statistics.reflex2);
I want to make them "fight" by comparing their random variables, but I have no idea how to do this.
Make one class fighter and then 2 instances Fighter and Opponent ... I don't understand why you make 2 classes which are doing same job.
You Fighter class should have a method attack(Opponent)
class Figher{
// ..
public void attack(Opponent opponent){
int opponentMaxDamage = calculateHitpointsBy(opponent);
int damageByOpponent = opponent.defend(this, opponentMaxDamage);
this.lifePoints-=damageByOpponent;
}
public boolean isAlive(){
return 0< this.lifePoints;
}
// ..
}
and your Opponent class should have a method defend(Fighter)
class Opponent{
// ..
public void defend(Figher fighter, int maxAttackDamage){
int myDamage = reduceDamage(fighter,int maxAttackDamage);
int attackerDamage = calculateAttackerDamage(fighter);
this.lifePoints-=myDamage ;
}
public boolean isAlive(){
return 0< this.lifePoints;
}
// ..
}
This gives you the possibility to add more complex calculations for the life points later. Eg.: the actual damage an attacker can do to the opponent may depend on the equipment she wears. The same may be true for the damage the opponent receives.
The method isAlive() could be extracted to a common base class...
You use it this way:
Fighter Lucas = new Fighter(Statistics.punchStrength, Statistics.movementSpeed, Statistics.reflex);
Opponent Simon = new Opponent(Statistics.punchStrength2, Statistics.movementSpeed2, Statistics.reflex2);
// fight
while(Lucas.isAlive()&&Simon.isAlive())
Lucas.attack(Simon);
// report winner
if(Lucas.isAlive())
System.out.println("winner is Lucas");
if(Simon.isAlive())
System.out.println("winner is Simon");
Use getter and setter methods in both of the classes i.e Fighter and Opponent
Then
Your Fighter class looks like this
class Fighter{
private int punchStrength, movementSpeed, reflex;
public int getPunchStrength() {
return punchStrength;
}
public void setPunchStrength(int punchStrength) {
this.punchStrength = punchStrength;
}
public int getMovementSpeed() {
return movementSpeed;
}
public void setMovementSpeed(int movementSpeed) {
this.movementSpeed = movementSpeed;
}
public int getReflex() {
return reflex;
}
public void setReflex(int reflex) {
this.reflex = reflex;
}
public Fighter(int punchStrength, int movementSpeed, int reflex) {
this.punchStrength = punchStrength;
this.movementSpeed = movementSpeed;
this.reflex = reflex;
}
}
Your Opponent class look like this
class Opponent{
private int punchStrength, movementSpeed, reflex;
public int getPunchStrength() {
return punchStrength;
}
public void setPunchStrength(int punchStrength) {
this.punchStrength = punchStrength;
}
public int getMovementSpeed() {
return movementSpeed;
}
public void setMovementSpeed(int movementSpeed) {
this.movementSpeed = movementSpeed;
}
public int getReflex() {
return reflex;
}
public void setReflex(int reflex) {
this.reflex = reflex;
}
public Opponent(int punchStrength, int movementSpeed, int reflex) {
this.punchStrength = punchStrength;
this.movementSpeed = movementSpeed;
this.reflex = reflex;
}
}
Now in your main class :
Fighter Lucas = new Fighter (Statistics.punchStrength,
Statistics.movementSpeed, Statistics.reflex);
Opponent Simon = new Opponent (Statistics.punchStrength2,
Statistics.movementSpeed2, Statistics.reflex2);
int avgFighter = ((Lucas.getpunchStrength())+(Lucas.getmovementSpeed())+(Lucas.getreflex()))/3;
int avgOpponent = ((Opponent .getpunchStrength())+(Opponent .getmovementSpeed())+(Opponent .getreflex()))/3;
if(avgFighter > avgOpponent )
System.out.println("Fighter wins");
else if(avgFighter == avgOpponent)
System.out.println("Draw");
else
System.out.println("Fighter looses");
Short version
Make one class - Fighter - which has fight(Fighter otherFighter) method. In that method, resolve the fight, so that the fight() logic and Fighter implementation does not leak outside the class. For an example, see below.
Long version
What I would do is abandon idea of having Opponent and Fighter classes if they're doing the same thing and have the same fields. Instead, why not have one Fighter class? The way I understand, the only difference between your objects is that they have different values - and that does not necessarily mean that you need to belong to different classes. Hence, unless Fighter can fight some other entities that have vastly different properties, one class should be enough. Besides, you can always extend the class if you need more fields or/and methods that behave slightly differently.
Now, determining who would win the fight is a rather interesting problem because you can do it in many ways, some more maintainable than others. On the one hand, you could calculate the outcome outside the classes. However, I do not think that this is the best approach. First of all, this means you need to know quite a lot about the Fighter, and how the fights work. Imagine if someone else worked with your code and wanted to resolve a fight. Would he/she be able to do it easily, or would he/she have to consult with you?
In my opinion, the logic should not leak to the outside, as this will make your code harder to maintain in the long run. E.g., what if attributes change? What if you want to resolve fights in multiple places of your code? Are you going to create a class whose sole purpose is to resolve a fight? Seems a bit strange. Or, worse yet, are you going to copy-paste the code? If so, what will you do if you need to change the logic of an outcome? Are you sure you can adjust all code you've copy-pasted? These are the questions you should be actively thinking about.
What I immediately think when I see a class named Fighter is that the entity represented, well, fights. Wouldn't it be nice if it could resolve the fights itself? Wouldn't it be logical place to put the code? Hence, my solution would be something like this:
public class Fighter {
private int speed;
private int reflex;
private int strength;
public Fighter(int speed, int reflex, int strength) {
this.speed = speed;
this.reflex = reflex;
this.strength = strength;
}
public String fight(Fighter otherFighter) {
int thisSum = speed + reflex + strength;
int thatSum = otherFighter.speed + otherFighter.reflex + otherFighter.strength;
int outcome = thisSum - thatSum;
if (outcome == 0) {
return "Draw";
} else if (outcome > 0) {
return "First fighter won";
} else {
return "Second fighter won";
}
}
}
Notice the fight() method. You pass another Fighter instance into it and without any effort the fight is resolved. The only caveat is that you need a way to figure out to determine which attributes are 'higher' and calculate the overall outcome. There are many ways to do it, of course. I used ints for clarity and ease of use. But you could use anything you like, or implement any logic you want. In any case, now you can resolve the fight with one method call. Hence, the whole code outside the class looks something like this:
public class Main {
public static void main(String[] args) {
Fighter fighter = new Fighter(1, 2, 3);
Fighter opponent = new Fighter(4, 2, 1);
System.out.println(fighter.fight(opponent));
}
}
Naturally, fight() could be extended to contain much more complex logic. Or it could be changed to accept some common base class of opponents, monsters, or fighters, which would allow you not only to fight Fighter's, but also other entities, too. Nevertheless, one thing I would try to achieve is to have as little logic about the fight as possible outside the Fighter class, so as to not have to change code in multiple places if I change my mind about how the fights work.
By the way, I have not used getters and setters, but it would probably be a good idea if getting an attribute involves some kind of logic.

Java, Multiple classes and their multiple booleans

So I've been stuck on this issue for quite some time now and I just can't seem to figure out the solution. I'm currently working on a project that simulates a parking garage. The parking garage itself isn't the issue; it's the several types of customers that are supposed to be simulated. To make things a little easier I'll ask for the solution of one and with that I should be able to work out the others myself.
For starters it is a requirement to create a separate class for customers with a parking pass and integrate this in a way that shows which cars are parking pass holders and which aren't.
import java.util.Random;
/* creates a boolean called isPass that is randomly picked to be true or false. */
public interface ParkPass {
public Random rnd = new Random();
public boolean isPass = rnd.nextBoolean();
}
This is the class that allows me to randomly set a parking pass. Since the simulation happens through a different class, all I can do is create the method to set the Pass to true or false; I can't set the Pass itself in this class.
public abstract class Car {
private Location location;
private int minutesLeft;
public boolean isPaying;
public boolean isBlue;
public void setIsPaying(boolean isPaying) {
this.isPaying = isPaying;
}
// added a method to allow us to set the colour of the car to blue for when they have a parking pass.
public void setIsBlue(boolean isBlue) {
this.isBlue = isBlue;
}
This is a small snippet of the Car class that shows which booleans belong to it and might show you which direction I'm trying to go with this simulation.
public class AdHocCar extends Car implements ParkPass{
public AdHocCar() {
setIsBlue(isPass);
setIsPaying(!isPass);
}
}
This is the class that is called when simulating a car going in and out of the parking garage. Here you can see I tried implementing the ParkPass class in order to set the Isblue and IsPaying booleans in the Car class so that I can call upon these in the next bit of code which is the simulation view that I'm currently stuck on trying to fix.
import javax.swing.*;
import java.awt.*;
public class SimulatorView extends JFrame {
private CarParkView carParkView;
private int numberOfFloors;
private int numberOfRows;
private int numberOfPlaces;
private Car[][][] cars;
public void updateView() {
/* Create a new car park image if the size has changed.
added 2 colours to show the difference between the three different customer types.*/
if (!size.equals(getSize())) {
size = getSize();
carParkImage = createImage(size.width, size.height);
}
Graphics graphics = carParkImage.getGraphics();
for(int floor = 0; floor < getNumberOfFloors(); floor++) {
for(int row = 0; row < getNumberOfRows(); row++) {
for(int place = 0; place < getNumberOfPlaces(); place++) {
Location location = new Location(floor, row, place);
Car car = getCarAt(location);
Color color = car == null ? Color.white : Car.isBlue ? Color.blue /*: isReservation == true ? Color.green*/ :Color.red ;
drawPlace(graphics, location, color);
}
}
}
repaint();
}
And here we finally get to the problem I have been facing. If you look at it right now you'll probably notice quite a few things wrong. This is because after 10 hours of research and constant changing of the Color attribute I kind of lost track of the exact way I was trying to implement the booleans that were created earlier in order to show the difference between the two types of customer. I'm not extremely experienced with programming so after awhile I just gave in and decided to ask here.
Now for the question, with all these separate classes creating their own booleans how can I make sure that when I use the simulation the cars using a Parking Pass will be blue while the cars that have to pay normally are shown as red?
public interface ParkPass {
public Random rnd = new Random();
public boolean isPass = rnd.nextBoolean();
}
Problem is in the above part. You can not define instance variables in interfaces. These members becoming static final as default.
Move this members to Car class and it will work.

How to create a best algorithm for tha car race?

I should to implmenets an application to control a race of car.
So in a race I have for example 5 car. I want to know the position of this car in every turn, the last time, and best round for all car. Now I wanto to know what structure of data is the best solution for this.
I have create this object:
package Auto;
import java.security.Timestamp;
public class Macchina {
Integer corsia;
Integer giro;
Timestamp ultimoTempo;
Timestamp migliorTempo;
public void setCorsia(Integer corsia) {
this.corsia = corsia;
}
public void setGiro(Integer giro) {
this.giro = giro;
}
public Timestamp getUltimoTempo() {
return ultimoTempo;
}
public void setUltimoTempo(Timestamp ultimoTempo) {
this.ultimoTempo = ultimoTempo;
}
public Timestamp getMigliorTempo() {
return migliorTempo;
}
public void setMigliorTempo(Timestamp migliorTempo) {
this.migliorTempo = migliorTempo;
}
public Macchina(int c, int g)
{
this.corsia=c;
this.giro=g;
}
public int getCorsia(){
return corsia;
}
public int getGiro(){
return giro;
}
}
This class is used to stored the information for the car in the race.
Keep in mind that A car has a speed.
The track has the distance and cars on it.
Your object design should reflect that.
Depending on how many properties a car has, you might not need a car object.
If you plan on extending your car racing program, you might want an abstract vehicle class. Especially if tracks become complicated, and cars become highly varied.
If you just want to find something like the position of a car at a discrete time and when a car reaches a certain distance then following might work for your purposes.
Pseudo code:
track {
int length, time, Maxtime;
array<int> carSpeeds, carPositions;
main(){
Maxtime=100;
for(time=1; time<MaxTime; time++){
for(int j= 0; j < carSpeeds.size(); j++){
carPositions[j] = time*careSpeed // reset for each lap.
// if position is greater than track length, you might have a winner or a tie.
}
}
}
}
You can add in a max distance and other things.
If you want to eventually model warp capable space ship racing to various waypoints, submarine racing between underwater cities, and dirtbike racing with shortcuts, the code should have high cohesion, and low coupling. But the first of many iterations would probably look similar to the above.

A* pathfinding with Multiple Agents

I've currently been learning and programming pathfinding(in Java) using the A* algorithm. A problem I've run into is when multiple entities are trying to pathfind, they both alter the previousNode(the Node that the Node being calculated on came from), messing up the algorithm, and eventually Node A will point to Node B and Node B will point to Node A.
How can I change the algorithm to either
Not use this previousNode system that is littered throughout all of the A * algorithms(that I have seen, that is)
Alter this system to be used concurrently
I am trying to avoid having one entity finish pathfinding, then telling the next entity to pathfinding, and so on. Like doing a wait() - notify() pair in Java.
public Path findPath(int startX, int startY, int goalX, int goalY) {
//Path is basically just a class that contains an ArrayList,
//containing Nodes, which contains the steps to reach a goal.
if(map.getNode(goalX, goalY).isObstacle()) {
return null;
}
map.getNode(startX, startY).setDistanceFromStart(0);
closedList.clear();
openList.clear(); //A List with added getFirst() - gets the first Node in the list
openList.add(map.getNode(startX, startY));
while(openList.size() != 0) {
//Node contains a List that has all of the Nodes around this node, a
//F, G, and H value, and its row(y) and column(x)
Node current = openList.getFirst();
if(current.getX() == goalX && current.getY() == goalY) {
return backtrackPath(current);
}
openList.remove(current);
closedList.add(current);
for(Node neighbor : current.getNeighborList()) {
boolean neighborIsBetter;
//If I've already searched this neighbor/node, don't check it
if(closedList.contains(neighbor)) {
continue;
}
if(!neighbor.isObstacle()) {
float neighborDistanceFromStart = (current.getDistanceFromStart() + map.getDistanceBetween(current, neighbor));
if(!openList.contains(neighbor)) {
openList.add(neighbor);
neighborIsBetter = true;
} else if(neighborDistanceFromStart < current.getDistanceFromStart()) {
neighborIsBetter = true;
} else {
neighborIsBetter = false;
}
if(neighborIsBetter) {
neighbor.setPreviousNode(current);
neighbor.setDistanceFromStart(neighborDistanceFromStart);
neighbor.setHeuristic(getManhattanDistance(neighbor.getX(), neighbor.getY(), goalX, goalY));
}
}
}
}
return null;
}
public Path backtrackPath(Node fromNode) {
Path path = new Path();
while(fromNode.getPreviousNode() != null) {
path.prependWaypoint(fromNode);
fromNode = fromNode.getPreviousNode();
}
return path;
}
I am specifically talking about(within findPath())
if(neighborIsBetter) {
neighbor.setPreviousNode(current); //previousNode is a value in the Node class that points to the Node that it came from
neighbor.setDistanceFromStart(neighborDistanceFromStart);
neighbor.setHeuristic(getManhattanDistance(neighbor.getX(), neighbor.getY(), goalX, goalY));
}
I don't think you can do A* (or any pathfinding algorithm, for that matter) without somehow storing a backpointer for a given path. So that leaves you with two options
Require each agent (Thread, I assume) to create their own copy of the graph to work on. That way each A* call going on won't interfere with one another, as they are working with the fields of the same node on different graphs.
Change your A* code to be able to handle multiple concurrent calls.
Option 1 is fairly self-explanatory and probably the better option. If this is just for you, you should probably just go with that one (instead of trying to make A* fully concurrent on a single graph). This would entail adding map as an input parameter (and requiring that concurrent calls should use a different map instance, either throwing an exception or having unspecified behavior if that doesn't occur). Additionally, you should instantiate closedList and openList as new data structures in each call, rather than share a list.
If that's not to your liking - you really want to fully encapsulate the mutli-call usage into the method itself, I think the simplest way you could do this is require an additional parameter of an id - some unique string that is guaranteed not to be the same as the id of another concurrent call. So the header of A* now looks like:
public Path findPath(final String ID, int startX, int startY, int goalX, int goalY) {
From there, change all of the implementations of each of the settable pathfinding fields in Node to a HashMap with the id as the key. From your code, I'm going to guess that your Node class looks something like this:
public class Node{
//Fields used by the A* call - no problem here
private boolean obstacle;
//Fields *edited* by the A* call
private float distanceFromStart;
private Node previous;
private int heuristic;
//other fields and stuff
public boolean isObstacle(){
return obstacle;
}
public float getDistanceFromStart(){
return distanceFromStart;
}
public void setDistanceFromStart(float f){
distanceFromStart = f;
}
public Node getPrevious(){
return previous;
}
public void setPrevious(Node p){
previous = p;
}
public int getHeuristic(){
return heuristic;
}
public void setHeuristic(int h){
heuristic = h;
}
}
We can edit the edited fields to be able to store many values, by id, as such:
public class Node{
//Fields used by the A* call - no problem here
private boolean obstacle;
//Fields *edited* by the A* call
private HashMap<String,Float> distanceFromStart;
private HashMap<String,Node> previous;
private HashMap<String,Integer> heuristic;
//other fields and stuff
public boolean isObstacle(){
return obstacle;
}
public float getDistanceFromStart(String id){
return distanceFromStart.get(id);
}
public void setDistanceFromStart(String id, float f){
distanceFromStart.put(id, f);
}
public Node getPrevious(String id){
return previous.get(id);
}
public void setPrevious(String id, Node p){
previous.put(id,p);
}
public int getHeuristic(String id){
return heuristic.get(id);
}
public void setHeuristic(String id,int h){
heuristic.put(id,h);
}
}
From there, simply edit your A* method to give the id from the method call to the getters and setters when called for. So long as two concurrent method calls don't have the same id value, they won't interfere with each other. Three things to keep in mind for this to work correctly:
Make sure that every editable field gets this treatment. It won't work if you forget about one. Non-editable fields (fields that don't get altered as a byproduct of running A*) can stay singular.
If you use the the above, you should probably add to the cleanup stage a step of removing all the information for the given ID from the graph, or the nodes' hashmaps will grow larger with each call.
Either way, you still should make openList and closedList new local instances, no matter what concurrent approach you pick. There's nothing to gain from making openList and closedList shared instances, and only bugs can come of it.
List<Node> closedList = new LinkedList<Node>();
List<Node> openList = new LinkedList<Node>();
//Don't have to clear them anymore - they're new lists
openList.add(map.getNode(startX, startY));

How to label edges with probabilities in graph using Jung 2

I am fairly new to Java and Jung. I am writing a program where I need to add probabilities on edges of the event occurrence(means probability of the event that data will flow from first node to other). I am a little confuse that will Max-Flow do the trick for me or do I need to use some other option or there is no option to do it within Jung and in that case do I need to write it on my own? Any help in this regard will be appreciated.
regards,
waqas
Do you intend to set the edge weights to represent the probabilities of certain events? The Max-Flow algorithm will use the "capacities" you assign to each edge to find the path of maximum flow from the source vertex to the sink vertex. What exactly are you trying to do here?
I'm not very sure what your final aim is, so I'll try my best to help out.
You can first represent the probabilities by defining a custom Edge and Edge Factory classes. What I did was:
0. Imports:
import org.apache.commons.collections15.Factory;
1. Add in your custom classes. They custom edge class might be something like:
public static class MyEdge {
private int flow;
private int capacity;
private String name;
private int eIndex;
public MyEdge(String name, int eIndex) {
this.name = name;
this.eIndex = eIndex;
}
public int getCapacity() {
return this.capacity;
}
public void setCapacity(int edgeCapacity) {
this.capacity = edgeCapacity;
}
public int getFlow() {
return this.flow;
}
public void setFlow(int edgeFlow) {
this.flow = edgeFlow;
}
public String toString() {
return this.name;
}
}
The custom edge factory is what actually creates your edges each time you draw them on the canvas graphically, it might look like:
public static class MyEdgeFactory implements Factory {
private static int defaultFlow = 0;
private static int defaultCapacity = 0;
private int edgeCount;
private MyEdgeFactory() {
}
public MyEdge create() {
String name = "E" + edgeCount;
MyEdge e = new MyEdge(name, edgeCount);
edgeCount++;
e.setFlow(defaultFlow);
e.setCapacity(defaultCapacity);
return e;
}
}
2. Tell your visualization viewer how to display the edge labels; you'll need to add this in wherever you're creating your graph and VisualizationViewer object (vv):
vv.getRenderContext().setEdgeLabelTransformer(new Transformer() {
public String transform(MyEdge e) {
return (e.toString() + " " + e.getFlow() + "/" + e.getCapacity());
}
});
Now everytime you create an edge, it's label will be of the form "E0 0/0", "E1 0/0" and so on.
I'll be posting detailed tutorials and code on my blog soon so you could watch that space if you're going to be spending significant time on whatever project you're working on.
Look at the way you're calling setEdgeLabelTransformer, you need to pass it a new Transformer(), like I've done in my code snippet numbered 2.
When you pass a new ToStringLabeller(), you're telling the viewer to label using the toString() method of the edge object. You'll need to pass a custom Transformer instead, just correct your code to look like mine and you'll be fine.

Categories

Resources