move individual Point3f in shape3D consisting of big Point3f array - java

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.

Related

How to extend JavaFX Line or other shapes

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.

Jtable dynamical fixed columns problems

I created a class that can dynamically to lock and unlock columns .
In my program i create two tables with the same tablemodel.
One is in the Jviewport of the scrollpane, the other in the RowHeaderView.
The problem is when you unlock all the locked columns
and you want to start to lock again, doesn't work. There are no errors but it's like the event doesn't answer.
Steps to produce the problem:
Try the code,
put all the columns in the fixed table,
then unlock with right double click,
then start again to lock, and unlock
Do this procedure and you can see that the mouse event doesnt answer anymore
public class Prova extends JFrame{
private JTable mainTable,fixedTable;
private JScrollPane scrollPane;
private JTableHeader mainTableHeader;
private TableColumnModel originalColumnModel,mainColumnModel,fixedColumnModel;
private TableColumn[] columns;
private int ncols,counter;
public Prova(){
counter = 0;
TableModel mainTableModel = new DefaultTableModel(5, 10);
scrollPane = new JScrollPane();
mainTable = new JTable(mainTableModel);
mainColumnModel = mainTable.getColumnModel();
fixedTable = new JTable();
fixedTable.setAutoCreateColumnsFromModel(false);
fixedTable.setModel(mainTable.getModel() );
ncols = mainTableModel.getColumnCount();
columns = new TableColumn[ncols];
for (int i=0;i<ncols;i++){
columns[i] = mainColumnModel.getColumn(i);
}
mainColumnModel = mainTable.getColumnModel();
fixedColumnModel = fixedTable.getColumnModel();
mainTableHeader = mainTable.getTableHeader();
mainTableHeader.addMouseListener( new MouseAdapter(){
#Override
public void mouseClicked(MouseEvent me){
if (SwingUtilities.isRightMouseButton(me)){
if (ncols - counter>1){
counter ++;
int col = mainTable.columnAtPoint(me.getPoint());
TableColumn column = mainColumnModel.getColumn(col);
mainColumnModel.removeColumn(column);
fixedTable.getColumnModel().addColumn(column);
scrollPane.setRowHeaderView(fixedTable);
scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, fixedTable.getTableHeader());
}
}
}
});
fixedTable.getTableHeader().addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent me){
if (SwingUtilities.isRightMouseButton(me) && me.getClickCount()== 2 ){
while (mainColumnModel.getColumnCount() > 0){
mainColumnModel.removeColumn(mainColumnModel.getColumn(0));
}
while (fixedColumnModel.getColumnCount() > 0){
fixedColumnModel.removeColumn(fixedColumnModel.getColumn(0));
}
for(int i=0;i<ncols;i++){
mainColumnModel.addColumn(columns[i]);
}
scrollPane.setRowHeaderView(null);
}
}
});
scrollPane.setViewportView(mainTable);
add(scrollPane, BorderLayout.CENTER);
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Prova().setVisible(true);
}
});
}
}
A few pointers when posting a SSCCE:
for (int i=0;i<ncols;i++){
Don't be afraid to use whitespace in you code to make it more readable be separating the 3 statements of the for statement.
for (int i = 0; i < ncols; i++){
Keep the code simple and directly related to the problem:
TableModel mainTableModel = new EmployeeTableModel(listEmployees);
You question is about "moving columns", not about the data in the table so there is no need for a special TableModel and the Employee class. Just use the DefaultTableModel:
TableModel mainTableModel = new DefaultTableModel(5, 10);
Your current code won't compile because you didn't include the Employee class. By using JDK classes the code is smaller and easier to read.
The problem is when you unlock all the locked columns and you want to start to lock again, doesnt work
Your looping code is wrong. I didn't bother to figure out what was wrong. Instead I made the code simpler:
//for(int i=0;i<(ncols-counter);i++){
while (mainColumnModel.getColumnCount() > 0)
{
mainColumnModel.removeColumn(mainColumnModel.getColumn(0));
}
//for(int i=0;i<counter;i++){
while (fixedColumnModel.getColumnCount() > 0)
{
fixedColumnModel.removeColumn(fixedColumnModel.getColumn(0));
}
Another problem is your fixed table doesn't have a header so you don't know what the columns are. This is fixed by using:
scrollPane.setRowHeaderView(fixedTable);
scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, fixedTable.getTableHeader());
Now that you have a header you need to add the MouseListener to the header, not the scrollpane:
//scrollPane.addMouseListener(new MouseAdapter() {
fixedTable.getTableHeader().addMouseListener(new MouseAdapter() {
Edit:
You have a similar problem to what I fixed above. That is don't keep using variable to track values when you can use the component itself.
if (ncols - counter>1){
You never reset the value of the counter so the if condition won't be true the second time.
As I did above just use the value from the column model:
//if (ncols - counter>1){
if (mainColumnModel.getColumnCount() > 1) {
This is just basic problem solving. Put a display statement in the block of code to see if it executes when you have problems.

Null Pointer Exception when trying to set a value

So pretty much I'm making an array of obvjects called Spots which symbolise the different faces of a dice.
It takes user input (manually set to three for this example), and then creates that make Spots and rolls a random number from 1 to 6.
However when I go to use the rollAgain() method on the aleady created array of Spots I get a null pointer even though I am using the same variable length in both for loops (the one that creates and one that rolls the spots).
My code
Global Variables
private Spots[] spots;
private int x = 3;
Contructor
public Director(JFrame window, String args[]) {
JMenuBar menus = new JMenuBar();
window.setJMenuBar(menus);
menus.add(makeFileMenu());
window.getContentPane().add(makePanel(), BorderLayout.WEST);
window.getContentPane().add(makeSpots(x), BorderLayout.CENTER);
rollAgain();
}
rollAgain() method
public void rollAgain() {
int v = 1 + (int) (Math.random() * 6);
for (int i = 0; i < x; i++) {
spots[i].setValue(v);
}
}
makeSpots() method
private JComponent makeSpots(int x) {
JPanel p = new JPanel();
p.setBorder(BorderFactory.createTitledBorder("Dice"));
Spots[] spots = new Spots[x];
for (int i = 0; i < x; i++) {
spots[i] = new Spots(200, 200);
spots[i].setBorder(BorderFactory.createEtchedBorder());
p.add(spots[i]);
}
return p;
}
You are setting a local variable
Spots[] spots = new Spots[x];
This doesn't change the field (which happens to have the same name)
private Spots[] spots;
The simplest solution is to not have a local variable
this.spots = new Spots[x];
You need to instantiate a new Spots array in your constructor.
this.spots = new Spots[x];
spots[i].setValue(v);
Judging from this line, my guess is that the Spot object in the array in null.
The error is in your makeSpots() method. You don't update the value of the field x, but use a local variable. Add this.x = x at the beginning of the method.
In your makeSpots() method, you are creating a new Spots object called spots:
Spots[] spots = new Spots[x];
This effectively hides your private member variable spots from the method. Instead, do this in your makeSpots() method:
spots = new Spots[x];
You are declaring a global 'spots' array and then in the makeSpots() you create the spots with a local variable also named 'spots'. Just substitute
Spots[] spots = new Spots[x];
by
spots = new Spots[x];
so the global variable gets a value.

Why can't Java resolve the variable in this for each loop?

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.

JScrollBar visible

Is there some way to know if a JScrollBar is visible or not inside a JPanel?
I mean, some times, my panel has many rectangles (think of it as buttons) and needs a scrollbar and some times it doesn't need it. I'd like to know if I can know when it is being shown.
If you extend the JPanel and add yourself the JScrollbars (horizontal and/or vertical), then you can control when they must be visible or invisible
(you can check if they are currently visible with the isvisible() function)
You can find two example of such classes that determine the need for visible scrollbar depending on their content:
JGraphPanel (its callback actionPerformed(Event e) will adjust the visibility based on a zoom factor)
Plane (its function adjustComponents() will call setVisible() on the JScrollBar if needed)
Assuming you have a reference to a JScrollPane, you should be able to just call
yourJScrollPane.getHorizontalScrollBar().isVisible()
or
yourJScrollPane.getVerticalScrollBar().isVisible()
If you need also to be notified about visibility changes than you can use a code as follows:
final JScrollPane scroll = new JScrollPane(createMyPanel());
scroll.getVerticalScrollBar().addHierarchyListener(new HierarchyListener() {
#Override
public void hierarchyChanged(HierarchyEvent e) {
if (e.getID() == HierarchyEvent.HIERARCHY_CHANGED &&
(e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) {
System.out.println(scroll.getVerticalScrollBar().isVisible());
}
}
});
Further to the answers by VonC and Joshua, it's worth noting that isVisible() is a method on the super class Component. Also, the javadoc states:
Determines whether this component should be visible when its parent is visible.
Components are initially visible, with the exception of top level components such as Frame objects.
What this means is that until the JScrollPane is added to a sized frame, calling isVisible() on the JScrollBar will always return true.
Consider the following SSCCE:
public static void main(String[] args) {
// creates a small table in a larger scroll pane
int size = 5;
JTable table = new JTable(makeData(size), makeHeadings(size));
JScrollPane pane = new JScrollPane(table);
pane.setPreferredSize(new Dimension(200, 200));
System.out.println(pane.getVerticalScrollBar().isVisible()); // prints true
JFrame frame = new JFrame("JScrollPane Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(pane);
System.out.println(pane.getVerticalScrollBar().isVisible()); // prints true
frame.pack();
System.out.println(pane.getVerticalScrollBar().isVisible()); // prints false
frame.setVisible(true);
System.out.println(pane.getVerticalScrollBar().isVisible()); // prints false
}
private static Object[] makeHeadings(int size) {
Object[] headings = new Object[size];
for (int i=0; i<size; i++){
headings[i] = i;
}
return headings;
}
private static Object[][] makeData(int size) {
Object[][] data = new Object[size][size];
for (int i=0; i<size; i++){
for (int j=0; j<size; j++){
data[i][j] = i*j;
}
}
return data;
}
Similarly, it's worth adding that if you're adding the JScrollPane to an internal frame, then scrollBar.isVisible() will only work once the internal frame has been added to another component.

Categories