Adding colours to custom edges - java

I want to add colours to the edges of the graph which I created using the JUNG library. I have edges which are in the type of custom edges where I set the labels and weights to the edges.
Transformer<CustomEdge, Paint> edgesPaint = new Transformer<CustomEdge, Paint>() {
private final Color[] palette = {Color.GREEN,
Color.YELLOW, Color.RED};
public Paint transform(CustomEdge edgeValue) {
String stringvalue=edgeValue.toString();
stringvalue=stringvalue.replaceAll("%","");
int value=Integer.valueOf(stringvalue);
if (value<= 10) {
return palette[0];
}
if (value> 10 && value<=20 ) {
return palette[1];
}
else {
return palette[2];
}
}
};
The following line returns an error message saying that the type of the edgesPaint should be (string,Paint):
visualizationViewer.getRenderContext().setEdgeFillPaintTransformer(edgesPaint);
Please help me with this.

Offhand I'd guess that your VisualizationViewer was declared to have edge type "String" (i.e., VisualizationViewer. But without more context it's hard to be sure.
Please print the exact error message and stack trace. Showing the declaration of the VisualizationViewer would also probably be helpful.

Related

Java: Create array of predefined Colors

I wish to create an array of all the Colors predefined in java.awt.Color in order to randomly select one of them.
My current best attempt is:
` Color[] colors = Color.getClass().getEnumConstants();
which was suggested in the top answer to the question: Color Class in Java
but that generates the error:
Cannot make a static reference to the non-static method getClass() from the type Object
The constructor in which the erroneous call is made is below:
private Ball() {
Random initialSetter = new Random();
ballX = marginSize + initialSetter.nextInt(xSize - 2 * marginSize);
ballY = marginSize + initialSetter.nextInt(ySize - 2 * marginSize);
ballXV = initialSetter.nextInt(doubleMaxV) - doubleMaxV/2;
ballYV = initialSetter.nextInt(doubleMaxV) - doubleMaxV/2;
Color[] colors = Color.getClass().getEnumConstants();
color = colors[initialSetter.nextInt(colors.length)];
}
Replacing ".getClass().getEnumConstants()" with ".values()" generates much the same error (static reference to non-static method).
To fix your immediate error, you can do:
Color[] colors = Color.class.getEnumConstants();
But this only works if Color is an enum. According to your comments, Color refers to java.awt.Color, which is not an enum. The first way suggested by the linked answer is quite incorrect (Maybe it was 6 years ago?).
As far as I know, the best thing you can do here is to list them all out. There's not much - only 13. It's not like this number is going to change any time soon, as AWT is pretty old, they are unlikely to add new colours in at this stage.
For fun (this is only for fun), you can do it with reflection:
List<Color> colors = Arrays.stream(Color.class.getFields())
// fields of type Color, and in all caps, snake case
.filter(x -> x.getType() == Color.class && x.getName().matches("[A-Z_]+"))
.map(x -> {
try {
return (Color)x.get(null);
} catch (IllegalAccessException e) {
e.printStackTrace();
return Color.BLACK;
}
}).collect(Collectors.toList());
Note that reflection is really slow though, which is why your best choice is to hardcode it.

How to use HashMap to compare and match colors

I would like to match color search results text with the checkbox text after clicking search button. See pic.
Currently I can view the Search Results text color but it doesn't match the checkbox text color after clicking search button. The code below is only for the Search Car Results text area and a class named CarBrand that matches the key of this HashMap carDetails. I am not sure how to compare and match its color using HashMap. Any suggestions would be great!
import java.awt.Color;
import java.swing.tree.DefaultTreeCellRenderer
public final class CarDetails extends DefaultTreeCellRenderer
{
private final Color defaultColor;
private final HashMap<String, Color> carDetails = new HashMap<>();
public CarDetails()
{
int i = 0;
defaultColor = getBackground(); //default color
int [][] rgb = {
{ 200, 000, 200 },
{ 000, 140, 000 },
{ 000, 200, 200 }
};
for (CarBrand car: CarModel.getCarBrandDetails()) {
carDetails.put(car.getCarBrand(), new Color(
rgb[i][0], rgb[i][1], rgb[i][2]));
i++;
// TODO this part is what I am not sure.
if (carDetails.containsKey(car.getCarBrand()) && carTable != null) {
for (Component c : carTable.getComponents()) {
if (c.getName().equals(car.getCarBrand())) {
c.setForeground(carDetails.containsObject(new
Color(rgb[i][0], rgb[i][1], rgb[i][2])));
}
}
}
}
}
}
I expect the output to match the color coding of the Search Car Results with the CheckBox text (Honda, Hundai, BMW) like the pic below.
I agree with Andrew Thompson a lot of code is missing to be able to reproduce your problem.
You should check if your conditions are correct (if statements).
The declaration of carTable is missing so we can't verify how it is build up. You use Component.getName(), are you sure this contains the value you expect ?
see what-is-java-awt-component-getname-and-setname-used-for
It is not filled by default so if you fill it before with the expected values, than it's fine to use.
Assuming c.getName() returns the name of the carBrand, you can do the following:
for (CarBrand car: CarModel.getCarBrandDetails()) {
carDetails.put(car.getCarBrand(), new Color(
rgb[i][0], rgb[i][1], rgb[i][2]));
i++;
}
if (carTable != null) {
for (Component c : carTable.getComponents()) {
if(carDetails.containsKey(c.getName()) {
c.setForeground(carDetails.get(c.getName())));
}
}
}
In your code you are not using the values from carDetails map you filled before. By calling ´carDetails.get()` method you reuse the created Color object.

JavaFX rectangle mouseonclick grid return

I am writing a board game which has a 20x20 grid.
This is in my board class:
private final Position[][] grid = new Position[GRID_SIZE][GRID_SIZE];
each position has :
public class Position {
private final Coordinates pos;
private Player player;
private final static double RECTANGLE_SIZE = 40.0;
private final Rectangle rect = new Rectangle(RECTANGLE_SIZE, RECTANGLE_SIZE);
}
so basically I have 20x20 Positions and each positions has a Rectangle
This is what I do to display the grid
for (int cols = 0; cols < GRID_SIZE; ++cols) {
for (int rows = 0; rows < GRID_SIZE; ++rows) {
grid.add(gameEngine.getBoard().getGrid()[cols][rows].getRect(), cols, rows);
}
}
Anyway, the grid is initialized and works properly. What I want to do is to make the rectangle objects clickable and to be able to return their Coordinates when they are clicked.
This is how I handle the mouse click
private void setUpRectangle() {
rect.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
rect.setFill(Color.BLACK);
}
});
}
What this code does is to change the color of the rectangle to black, but how could I return the Coordinates.
Basically, I can edit the onclick function to return the coordinates of this position, but how can I acquire them later?
This is not a JavaFX question as much as it is a design question. You have a container (Position) of 2 objects (Coordinates and Rectangle) and you want one of them to know about the other. That is, the rectangle should know the coordinates of its position.
There are a few approaches here, and depending on the bigger picture, one might be better than the others. James_D mentioned a couple in a comment.
Keep a reference of the position object in the rectangle object. This is useful if rectangle needs to access various datum in the container from various places. You would do something like rectangle.getPosition().getCoordinates() or .getPlayer().
Keep a reference of the coordinates object in the rectangle object. This is a more specific approach of 1 useful if you only need that object. You would do something like rectangle.getCoordinates().
Pass the coordinates to your setUpRectangle method. This is useful if you rectangle doesn't need access to this data from various places, it's a local solution. Then in the handle method you would return the coordinates you passed to setUpRectangle, though we can't see what class this method is in.
Use external help. You can keep something like Map<Rectangle, Coordinates> and then call map.get(rectangle). You can hide this map in a method Coordinates getCoordinatesForRectangle(Rectangle rectangle) instead of calling it directly.
You could store this data as userData (or use properties in case userData is preserved for something else in your program):
private final Rectangle rect;
public Position() {
rect = new Rectangle(RECTANGLE_SIZE, RECTANGLE_SIZE);
rect.setUserData(this);
}
rect.setOnMouseClicked((MouseEvent event) -> {
Position pos = (Position) ((Node) event.getSource()).getUserData();
...
});
You could also use a listener that knows about the position:
class CoordinateAwareListener implements EventHandler<MouseEvent> {
private final int coordinateX;
private final int coordinateY;
public CoordinateAwareListener(int coordinateX, int coordinateY) {
this.coordinateX = coordinateX;
this.coordinateY = coordinateY;
}
#Override
public void handle(MouseEvent event) {
// do something with the coordinates
}
}

Java free spot pieces

I have a piece that needs to get surrounded by other grey pieces if the coordinate around the piece is not used.
I tried to do the following:
public List<Piece> boardPiece(){
List<Piece> boardPieces = new ArrayList<>();
for (Piece pieces : listToPiece) {
if (pieces.getCoordinate() != null){
boardPieces.add(pieces);
}
}
return boardPieces;
}
public List<Coordinate> getSurroundings() {
List<Coordinate> surroundings = new ArrayList<>();
for (Piece boardpieces : boardPiece()) {
for (Coordinate coordinate : makeDirections()) {
surroundings.add(new Coordinate(boardpieces.getCoordinate().getRow() + coordinate.getRow(), boardpieces.getCoordinate().getColumn() + coordinate.getColumn()));
}
}
return surroundings;
}
public List<Coordinate> makeDirections(){
directions.add(new Coordinate(1,-1));
directions.add(new Coordinate(-1,1));
directions.add(new Coordinate(-1,0));
directions.add(new Coordinate(1,0));
directions.add(new Coordinate(0,-1));
directions.add(new Coordinate(0,1));
return directions;
}
A boardpiece is a piece that is on the board and that has a coordinate. A boardpiece who doesn't have a coordinate shouldn't be surrounded by other grey pieces. In my drawer the following I have written the following code:
public void drawEmptyPieces() {
for (Coordinate emptyPiece : emptyPieces) {
EmptyPiece emptyPiece1 = new EmptyPiece(new Coordinate(emptyPiece.getRow(), emptyPiece.getColumn()));
emptyPiece1.setFill(Color.LIGHTGRAY);
emptyPiece1.setStroke(Color.BLACK);
gameField.getChildren().add(emptyPiece1);
}
}
I tried to avoid that grey pieces are drawed over boardpieces:
public void drawEmptyPieces() {
for (Coordinate emptyPiece : emptyPieces) {
for (Piece pieceObjects : pieces){
if (pieceObjects.getCoordinate().getRow() != emptyPiece.getRow() && pieceObjects.getCoordinate().getColumn() != emptyPiece.getRow()){
EmptyPiece emptyPiece1 = new EmptyPiece(emptyPiece);
emptyPiece1.setFill(Color.LIGHTGRAY);
emptyPiece1.setStroke(Color.BLACK);
gameField.getChildren().add(emptyPiece1);
}
}
}
}
But this code doesn't work.
I don't know that you have provided enough information to answer your problem. But here are a few thoughts.
makeDirections() is either broken (missing declaration of directions local variable), or broken because it keeps adding 6 new Coordinate objects to a directions field each time it is called. In either case, it is doing more work than needed. Consider instead:
private final static List<Coordinate> directions;
static {
List<Coordinate> dirs = new ArrayList<>();
dirs.add(new Coordinate(1,-1));
dirs.add(new Coordinate(-1,1));
dirs.add(new Coordinate(-1,0));
dirs.add(new Coordinate(1,0));
dirs.add(new Coordinate(0,-1));
dirs.add(new Coordinate(0,1));
directions = Collections.immutableList(dirs);
}
public static List<Coordinate> makeDirections() { // or getDirections()
return directions;
}
getSurroundings() looks like it can (will?) add the same Coordinate to surroundings multiple times. If a piece is at [5,5], and another piece is at [7,5], you will add [5,5]+[1,0] and [7,5]+[-1,0] to surroundings. There may even be a piece at [6,5] which will also add [5,5] and [7,5] to surroundings as well.
Instead ...
Ensure Coordinate implements equals and hashCode, so two Coordinate objects with the same row and column are equal, and both hash to the same value. Then, in getSurroundings() use:
Set<Coordinate> surroundings = new HashSet<>();
So that surroundings only contains each coordinate once. Before returning, you could remove all "occupied" coordinates from the surroundings.
for (Piece piece : pieces) {
surroundings.remove(piece.getCoordinate());
}
You could return the set instead of a list, or if you want to keep it as a list:
return new ArrayList<>(surroundings);
Without "surroundings" containing duplicates and coordinates where existing pieces are, you may find drawing your board much, much easier.
Unless you game board is infinite in extents, you will be creating Coordinate objects beyond the edges of you board. Consider adding the method getNeighbours() to the Coordinate class:
class Coordinate {
public Collection<Coordinate> getNeighbours() {
List<Coordinate> neighbours = new ArrayList<>(6);
for (Coordinate dir: getDirections()) {
Coordinate n = new Coordinate(getRow() + dir.getRow(), getColumn() + dir.getColumn());
if (n.isValid()) {
neighbours.add(n);
}
}
return neighbours;
}
}
Then getSurroundings becomes a little simpler, with:
for (Piece boardpieces : boardPiece()) {
surroundings.addAll(boardpieces.getCoordinate().getNeighbours());
}
Without any "surroundings" being off the board.
A "Direction" is not a Coordinate. They are used for different things. A Coordinate is a position on the board, where as your "Direction" is a relative offset. The board may have constraints that row and column values must be between (say) 0 and 20; your "Direction" objects have negative row and column values; which would violate those constraints. This means you could not add proper checking and throw new IllegalArgumentException("Invalid coordinate") in the constructor of Coordinate if given a bad value.
I would create a new class for Direction objects. Actually, I'd use enum Direction { ... }. Then Direction.values() would return the set of all directions -- you wouldn't need to create the set yourself. This is a much larger change than other ones, and may have consequences beyond the small subset of code you provided. A good change, but perhaps a lot of work.
Hope that helps.

How to dynamically add .css to a custom Javafx LineChart Node?

So, my issue is this: I'm attempting to define a custom set of nodes for a Javafx XYChart LineChart, where each node corresponds to a point that was plotted directly from the datasets. After looking around a little bit, Jewlesea actually had a solution at one point about how to add dynamic labels to nodes on a linechart graph that gave me enough of a push in the right direction to create black symbols (they are dots at the moment, but they can be many different things). Now I have a requirement that requires me to change ONE of the nodes on the XY chart into an 'X'. this could be either through loading an image in place of the 'node', or through physically manipulating the 'shape' parameter in .css.
The problem begins when I try to add this property dynamically, since which node has the 'x' will always be changing. Here are the things I've tried, and they all end up with no results whatsoever, regardless of the property used.
private XYChart.Data datum( Double x, Double y )
{
final XYChart.Data data = new XYChart.Data(x, y);
data.setNode(
new HoveredThresholdNode(x, y));
//data.getNode().setStyle("-fx-background-image: url(\"redX.png\");");
data.getNode().styleProperty().bind(
new SimpleStringProperty("-fx-background-color: #0181e2;")
.concat("-fx-font-size: 20px;")
.concat("-fx-background-radius: 0;")
.concat("-fx-background-insets: 0;")
.concat("-fx-shape: \"M2,0 L5,4 L8,0 L10,0 L10,2 L6,5 L10,8 L10,10 L8,10 L5,6 L2,10 L0,10 L0,8 L4,5 L0,2 L0,0 Z\";")
);
data.getNode().toFront();
return data;
}
So in the above, you can see that this is adding a property through the use of the 'bind' function after the dataNode has already been created. Also note above, I tried doing it through the 'setStyle' interface at this level to give it a background image, with no success. Also, no errors are being thrown, no 'invalid css' or anything of the sort, just simply no display on the graph at all when done this way.
now, in the HoveredThresholdNode (Again a big thanks to Jewelsea for being a master of Javafx and putting this bit of code online, it's where 90% of this class came from.) I tried essentially the same thing, at a different level. (actually being IN the node creation class, as opposed to a layer above it).
class HoveredThresholdNode extends StackPane {
/**
*
* #param x the x value of our node (this gets passed around a bunch)
* #param y the y value of our node (also gets passed around a bunch)
*/
HoveredThresholdNode(Double x, Double y) {
//The preferred size of each node of the graph
//getStylesheets().add(getClass().getResource("style/XYChart.css").toExternalForm());
//getStyleClass().add("xyChart-Node");
//setOpacity(.8);
styleProperty().bind(
new SimpleStringProperty("-fx-background-color: #0181e2;")
.concat("-fx-font-size: 20px;")
.concat("-fx-background-radius: 0;")
.concat("-fx-background-insets: 0;")
.concat("-fx-shape: \"M2,0 L5,4 L8,0 L10,0 L10,2 L6,5 L10,8 L10,10 L8,10 L5,6 L2,10 L0,10 L0,8 L4,5 L0,2 L0,0 Z\";")
);
//this label is the 'tooltip' label for the graph.
final Label label = createDataThresholdLabel(x, y);
final double Myx = x;
final double Myy = y;
setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
if (Myx == 0) {
label.setTextFill(Color.DARKGRAY);
} else if (Myx > 0) {
label.setTextFill(Color.SPRINGGREEN);
} else {
label.setTextFill(Color.FIREBRICK);
}
label.setText("Current position: " + Myx + " , " + Myy);
//setCursor(Cursor.NONE);
toFront();
}
});
setOnMouseExited(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
//getChildren().clear();
//setCursor(Cursor.CROSSHAIR);
}
});
}
Now note, I also tried the setStyle(java.lang.String) method, with all of the same type of CSS, with no success. I have NO idea why this isn't styling dynamically. It's almost as if the custom nodes are simply ignoring all new .css that I define at runtime?
Any help would be greatly appreciated, please don't be shy if you need more details or explanation on any points.
So, I did finally find a good workaround to solve my problem, although not in the way I thought it would happen. The main problem I was having, was that I was extending from stackPane to create my node, which only had a very small number of graphical display options available to it, and by switching the 'prefSize()' property, I was simply changing the size of that stackPane, and then filling in the background area of that stack pane black, giving it a very deceptive shape-look to it.
So rather than use a stack pane, whenever I reached the node that I needed to place the red 'X' on, I simply called a different Datum method that returned a datum with an ImageView Attached, like so:
private XYChart.Data CoLDatum(Double x, Double y){
final XYChart.Data data = new XYChart.Data(x, y);
ImageView myImage = new ImageView(new Image(getClass().getResource("style/redX.png").toExternalForm()));
data.setNode(myImage);
data.getNode().setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
main_label.setText("Some Text.");
}
});
data.getNode().setOnMouseExited(new EventHandler<MouseEvent>(){
#Override public void handle(MouseEvent mouseEvent) {
main_label.setText("");
}
});
return data;
}
and since ImageView is an implementing class of Node, this worked out just fine, and allowed me to load up an image for that one single node in the graph, while still maintaining a listener to give custom text to our information label when the red 'x' was hovered over with a mouse. Sometimes, it's the simple solutions that slip right past you.
I imagine that, had I employed stackPane properties properly with the setStyle(java.lang.String) method, they would have absolutely shown up, and I was just butchering the nature of a stack pane. Interesting.
Hopefully this helps somebody else stuck with similar problems!

Categories

Resources