Affine transforms for graph, not for text and labels - java

This post is a suite to an answer I made to question: Transforming a shape
Here is the image I want:
Here is the image a simple program produces, as you can see the text is rotated. I want horizontal text:
The canvas is scaled, translated, rotated to do the drawing, so the text is not displayed horizontaly and the font size need to be extremely reduced (1.4). The program is wrote in Java (awt and JavaFX) but the problem is not language or technology relevant, so any suggestion is welcome.
Here is the simple program:
import javafx.application.Application;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.TextAlignment;
import javafx.stage.Stage;
public class TransRotScale extends Application {
private static void drawGraph( GraphicsContext g ) {
//---
g.scale( 10.0, 10.0 );
g.rotate( Math.toDegrees( Math.atan2( -15.0, 40.0 )));
g.translate( -8, -10 );
//---
g.setStroke( Color.DARKRED );
g.setLineWidth( LINE_WIDTH );
g.strokeLine( 10, 20, 10, 30 );
g.strokeLine( 10, 30, 50, 30 );
g.strokeLine( 50, 30, 50, 35 );
//---
g.setFill( Color.BLACK );
g.fillOval( 50-ENDPOINT_RADIUS, 35-ENDPOINT_RADIUS,
ENDPOINT_DIAMETER, ENDPOINT_DIAMETER );
g.fillOval( 10-ENDPOINT_RADIUS, 20-ENDPOINT_RADIUS,
ENDPOINT_DIAMETER, ENDPOINT_DIAMETER );
//---
g.setFill( Color.LIGHTSALMON );
g.fillOval( 10-ENDPOINT_RADIUS, 30-ENDPOINT_RADIUS,
ENDPOINT_DIAMETER, ENDPOINT_DIAMETER );
g.fillOval( 50-ENDPOINT_RADIUS, 30-ENDPOINT_RADIUS,
ENDPOINT_DIAMETER, ENDPOINT_DIAMETER );
//---
g.setStroke( Color.DARKGRAY );
g.setFont( Font.font( Font.getDefault().getFamily(), 1.4 ));
g.setLineWidth( 0.1 );
g.setTextAlign( TextAlignment.CENTER );
g.setTextBaseline( VPos.BOTTOM );
g.strokeText( "[10, 20]", 10, 20-ENDPOINT_RADIUS );
g.setTextBaseline( VPos.TOP );
g.strokeText( "[10, 30]", 10, 30+ENDPOINT_RADIUS );
g.setTextBaseline( VPos.BOTTOM );
g.strokeText( "[50, 30]", 50, 30-ENDPOINT_RADIUS );
g.setTextBaseline( VPos.TOP );
g.strokeText( "[50, 35]", 50, 35+ENDPOINT_RADIUS );
}
#Override
public void start( Stage primaryStage ) throws Exception {
BorderPane bp = new BorderPane();
Canvas canvas = new Canvas( 540, 240 );
bp.setCenter( canvas );
drawGraph( canvas.getGraphicsContext2D());
primaryStage.setScene( new Scene( bp ));
primaryStage.centerOnScreen();
primaryStage.show();
}
public static final double ENDPOINT_RADIUS = 2.0;
public static final double ENDPOINT_DIAMETER = 2.0*ENDPOINT_RADIUS;
public static final double LINE_WIDTH = 1.0;
public static void main( String[] args ) {
launch();
}
}
In the program used to display first image (the goal), I use two canvases, the first canvas is scaled, translated, rotated to do the drawing without any text and the second canvas is used only to draw labels horizontally, using java.awt.geom.AffineTransform to compute coordinates to match the item displayed in the first canvas. Both canvases are displayed superposed, they are transparent.

This is what suggest Alexander Kirov, if I understand well:
import javafx.application.Application;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Polyline;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.stage.Stage;
public class TransRotScal extends Application {
#Override
public void start( Stage primaryStage ) throws Exception {
Pane pane = new Pane();
pane.setScaleX( 10.0 );
pane.setScaleY( 10.0 );
pane.setRotate( theta );
pane.setTranslateX( 468.0 );
pane.setTranslateY( 152.0 );
Polyline line = new Polyline( 10,20, 10,30, 50,30, 50,35 );
line.setStroke( Color.DARKRED );
Circle c0 = new Circle( 10, 20, 2, Color.BLACK );
Circle c1 = new Circle( 10, 30, 2, Color.LIGHTSALMON );
Circle c2 = new Circle( 50, 30, 2, Color.LIGHTSALMON );
Circle c3 = new Circle( 50, 35, 2, Color.BLACK );
Text t0 = createText( 10, 20, "[10,20]", VPos.BOTTOM );
Text t1 = createText( 10, 30, "[10,30]", VPos.TOP );
Text t2 = createText( 50, 30, "[50,30]", VPos.BOTTOM );
Text t3 = createText( 50, 35, "[50,35]", VPos.TOP );
pane.getChildren().addAll( line, c0, c1, c2, c3, t0, t1, t2, t3 );
primaryStage.setScene( new Scene( pane ));
primaryStage.centerOnScreen();
primaryStage.setWidth ( 580 );
primaryStage.setHeight( 280 );
primaryStage.show();
}
private Text createText( int x, int y, String label, VPos vPos ) {
Text text = new Text( x, y, label );
text.setFill( Color.DARKGRAY );
text.setFont( Font.font( Font.getDefault().getFamily(), 1.4 ));
text.rotateProperty().set( -theta );
text.textAlignmentProperty().setValue( TextAlignment.CENTER );
text.setX( text.getX() - text.getBoundsInLocal().getWidth()/2.0);
text.textOriginProperty().set( vPos );
if( vPos == VPos.BOTTOM ) {
text.setY( text.getY() - 2 );
}
else {
text.setY( text.getY() + 2 );
}
return text;
}
private final double theta = Math.toDegrees( Math.atan2( -15.0, 40.0 ));
public static void main( String[] args ) {
launch();
}
}
It works but it use Node in place of canvas and the values to adjust texts are obtained by iterative tries (a lot!); I don't know how to calculate them.
Alexander, you may edit this post or post your own to complete it, in the later case I'll delete this.
Here is the result, note the approximative placement of text around discs:

Instead of drawing the string directly onto the Graphics object can you create a GlyphVector from the string instead and draw that to the Graphics object? The advantage to this approach would be that the GlyphVector can have its own transform which you could use to effectively cancel the rotation of the canvas. I don't remember the exact details to creating the glyphs, but you need a Font and a FontRenderContext...both of which are already available to the Graphics context.

Related

JSplitPane Setting Resize Weight not Working

I am trying to set the weight of the splitter to 0.9 yet it does not seem to work. What am I missing and what am i to do? I've checked this post, yet I could not neither understand nor solve the problem of mine. What I want basically is
something like this though the split pane and the table is always %50,%50. So splitter.setResizeWeight( 0.9 ); is not working.
Here's the code of the panel:
public FlightPanel( final SomeOtherClass category, final SomeClass dar2 )
{
this.detailsPanel = new JPanel( new GridLayout( 0, 1 ) );
this.sum = new JPanel( new GridLayout( 0, 1 ) );
this.model =
new FlightPanelTableModel(...);
this.timeTable = new JTable( this.model );
this.timeTable.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
this.setLayout( new GridLayout( 0, 1 ) );
this.treeView = new FlightPanelTreeView( dar2 );
ToolTipManager.sharedInstance().registerComponent( this.treeView );
this.detailsPanel.add( this.treeView );
final JSplitPane splitter =
new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, new JScrollPane( this.timeTable ),
new JScrollPane( this.detailsPanel ) );
splitter.setResizeWeight( 0.9 );
this.sum.add( splitter );
this.add( this.sum );
}
How could I solve it?
Thanks in advance.

How can I create the following layout in Swing?

I am new to Swing and I don't understand how to do layouts properly. I need to create the following layout
I have tried to use a grid layout and a border layout but I just can't get it to look the way I designed it in the picture. Can anyone help me?
Attempt
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Test extends JFrame
{
public Test()
{
//Make a content frame
Container contentPane = getContentPane();
Container contentPane2 = getContentPane();
Container contentPane3 = getContentPane();
//Create a grid layout - This will go to the left
contentPane.setLayout ( new GridLayout ( 4, 1 ) ); //4 Rows and 1 Columns
//Button 1
contentPane.add ( new JButton ( "Button 1" ) );
//Button 2
contentPane.add ( new JButton ( "Button 2" ) );
//Button 3
contentPane.add ( new JButton ( "Button 3" ) );
//Button 4
contentPane.add ( new JButton ( "Button 4" ) );
//Create a border layout - This will go in the middle.
contentPane2.setLayout ( new BorderLayout() );
//Label - Welcome to my application
contentPane2.add ( new JLabel ( "Welcome to my application" ) );
//Image 1
contentPane2.add ( new ImageIcon("img/button.png" ) );
//Change background colour
//Create a grid layout - This will go to the right
contentPane3.setLayout ( new GridLayout ( 4, 1 ) ); //4 Rows and 1 Columns
//Button 5
contentPane3.add ( new JButton ( "Button 5" ) );
//Button 6
contentPane3.add ( new JButton ( "Button 6" ) );
//Button 7
contentPane3.add ( new JButton ( "Button 7" ) );
//Button 8
contentPane3.add ( new JButton ( "Button 8" ) );
//Set window parameters
setTitle ( "Test Application" );
setSize ( 200, 200 );
setVisible ( true );
}
public static void main ( String[] args )
{
Test myFrame = new Test();
}//End main
}//End Class
Please read comments :
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
class Test extends JFrame{
//when posting code make resources available
URL url = new URL("http://www.digitalphotoartistry.com/rose1.jpg");
public Test() throws IOException {
//You clearly have three different areas in your design, so start by making:
JPanel left = new JPanel();
JPanel center = new JPanel();
JPanel right = new JPanel();
//left and right panels holds 4 buttons each. GridLayout will make
//them occupy equal space. You could also use other layout managers like
//Box
left.setLayout ( new GridLayout ( 4, 1 ) ); //4 Rows and 1 Columns
//Button 1
left.add ( new JButton ( "Button 1" ) );
//Button 2
left.add ( new JButton ( "Button 2" ) );
//Button 3
left.add ( new JButton ( "Button 3" ) );
//Button 4
left.add ( new JButton ( "Button 4" ) );
//Create a border layout - This will go in the middle.
center.setLayout ( new BorderLayout() );
//Label - Welcome to my application
center.add ( new JLabel ( "Welcome to my application"),BorderLayout.NORTH);
//Image 1
ImageIcon icon= new ImageIcon(ImageIO.read(url));
center.add ( new JLabel(icon), BorderLayout.CENTER);
//Create a grid layout - This will go to the right
right.setLayout ( new GridLayout ( 4, 1 ) ); //4 Rows and 1 Columns
//Button 5
right.add ( new JButton ( "Button 5" ) );
//Button 6
right.add ( new JButton ( "Button 6" ) );
//Button 7
right.add ( new JButton ( "Button 7" ) );
//Button 8
right.add ( new JButton ( "Button 8" ) );
//add JPanel to content pane which uses Borderlayout by default
getContentPane().add(left, BorderLayout.WEST);
getContentPane().add(center, BorderLayout.CENTER);
getContentPane().add(right, BorderLayout.EAST);
//Set window parameters
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle ( "Test Application" );
//setSize ( 200, 200 ); //size set by layout
pack();
setVisible ( true );
}
public static void main ( String[] args ) throws IOException {
new Test();
}//End main
}//End Class

Real time on x-axis with adjustable scale on x-axis?

I want to implement a trend graph with real time data coming continuously,for which am having time on x-axis with format "HH:MM:SS". Now my requirement is to have adjustable scale like 10min or 15min etc....And also how could i limit the time values on x-axis ?
Please help me.
Thanks!
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.data.general.SeriesException;
import org.jfree.data.time.Second;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.XYDataset;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RefineryUtilities;
public class TimeSeries_AWT extends ApplicationFrame
{
public TimeSeries_AWT( final String title )
{
super( title );
final XYDataset dataset = createDataset( );
final JFreeChart chart = createChart( dataset );
final ChartPanel chartPanel = new ChartPanel( chart );
chartPanel.setPreferredSize( new java.awt.Dimension( 560 , 370 ) );
chartPanel.setMouseZoomable( true , false );
setContentPane( chartPanel );
}
private XYDataset createDataset( )
{
final TimeSeries series = new TimeSeries( "Random Data" );
Second current = new Second( );
double value = 100.0;
for (int i = 0; i < 4000; i++)
{
try
{
value = value + Math.random( ) - 0.5;
series.add(current, new Double( value ) );
current = ( Second ) current.next( );
}
catch(SeriesException e )
{
System.err.println("Error adding to series");
}
}
return new TimeSeriesCollection(series);
}
private JFreeChart createChart( final XYDataset dataset )
{
return ChartFactory.createTimeSeriesChart(
"Computing Test",
"Seconds",
"Value",
dataset,
false,
false,
false);
}
public static void main( final String[ ] args )
{
final String title = "Time Series Management";
final TimeSeries_AWT demo = new TimeSeries_AWT( title );
demo.pack( );
RefineryUtilities.positionFrameRandomly( demo );
demo.setVisible( true );
}
}
If you see output, the time is constant just static not changing dynamically. and while if time updates dynamically,i want the scale or tickunits to be adjusted dynamically at runtime like for example in the output graph the scale time is 5min. but i want that to be controlled at runtime to be anything like 10min,20min,40min etc...
And another thing is in the output graph we can see 14 values on x-axis.which changes automatically when the graph is resized.but i want to control how many to be seen on the x-axis means i want to show some particular x time values on x-axis.which should also be configurable at launch of application.

JLayeredPane clicking on hidden layer widgets

I am encountering a strange behaviour using a JLayeredPane. When I click on empty area of the topmost (displayed one) panel, Swing actually clicks on a button that is at that exact position but on a hidden layer.
Here is how I build the JLayeredPane:
volatilityOptionsPane = new OptionsPane( this, optionsBo, mpp.getSkewParams() );
volatilityOptionsPane.setBounds( 0, 0, 300, 750 );
screenOptionsPane = new CScreenOptionsPanel( this, optionsBo );
screenOptionsPane.setBounds( 0, 0, 300, 760 );
divFwdOptionsPane = new DivFwdOptionsPanel( this, optionsBo );
divFwdOptionsPane.setBounds( 0, 0, 300, 770 );
emptyOptionsPane = new JPanel();
emptyOptionsPane.setBounds( 0, 0, 300, 900 );
layeredOptionPane.add( volatilityOptionsPane, new Integer( 3 ) );
layeredOptionPane.add( emptyOptionsPane, new Integer( 2 ) );
layeredOptionPane.add( divFwdOptionsPane, new Integer( 1 ) );
layeredOptionPane.add( cscreenOptionsPane, new Integer( 0 ) );

Scrolling & Selecting of JList in undecorated frame doesn't work properly

I have this strange behavior, look at the following code (or try it out yourself):
public class JListProblem
{
public static void main (String[] args)
{
JFrame frame = new JFrame("JList Problem");
frame.setSize( 300, 500);
JScrollPane sp = new JScrollPane();
DefaultListModel dlm = new DefaultListModel();
for ( int i = 0; i < 10000; i++ )
{
dlm.addElement( i);
}
JList list = new JList(dlm );
sp.setViewportView( list );
frame.add( sp );
frame.setUndecorated( true );
frame.setBackground( new Color( 0.0f, 0.0f, 0.0f, 0.0f ) );
frame.setVisible( true );
}
}
Here's my problem:
When you try to scroll, it does not scroll "smoothly" (sorry, I don't know the correct word for this).
Try selecting an entry after scrolling: After you clicked, another entry is selected.
How can I correct this behavior?
When you decrease the amount of entries (change the value of maximum i to 1000 for example), everything is working fine.

Categories

Resources