Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

rotating coordinate plane for data and text in Java

I need to:
1.) move the origin and also rotate the coordinate plane so that x-values progress rightward and y-values progress upward from the new origin(which needs to be the bottom left corner of the inner, blue rectangle in the code below). This will enable me to plot points at x,y coordinate pairs in the code below.
2.) plot rotated labels for the tic marks on the y-axis of the data plot.

The code below sets up this problem. It works, except for two problems:
1.) the data points are being plotted with the upper left hand corner as the origin and y-values descending downward
2.) the labels for the tic marks on the y-axis are not being drawn on the screen

Can anyone show me how to fix the code below so that it fixes these two problems and does what the first paragraph above describes?

The code is in the following two java files:

DataGUI.java

import java.awt.*;
import java.util.ArrayList;
import javax.swing.*;

class DataGUI extends JFrame{
DataGUI() {
    super("X,Y Plot");
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setPreferredSize(new Dimension(800, 400));
    this.pack();
    this.setSize(new Dimension(800, 600));
    this.setLocationRelativeTo(null);


    setLayout(new GridLayout());
    ArrayList<Double> myDiffs = new ArrayList<Double>();
            myDiffs.add(25.0);
            myDiffs.add(9.0);
            myDiffs.add(7.0);
            myDiffs.add(16.0);
            myDiffs.add(15.0);
            myDiffs.add(6.0);
            myDiffs.add(2.0);
            myDiffs.add(8.0);
            myDiffs.add(2.0);
            myDiffs.add(27.0);
            myDiffs.add(14.0);
            myDiffs.add(12.0);
            myDiffs.add(19.0);
            myDiffs.add(10.0);
            myDiffs.add(11.0);
            myDiffs.add(8.0);
            myDiffs.add(19.0);
            myDiffs.add(2.0);
            myDiffs.add(16.0);
            myDiffs.add(5.0);
            myDiffs.add(18.0);
            myDiffs.add(23.0);
            myDiffs.add(9.0);
            myDiffs.add(4.0);
            myDiffs.add(8.0);
            myDiffs.add(9.0);
            myDiffs.add(3.0);
            myDiffs.add(3.0);
            myDiffs.add(9.0);
            myDiffs.add(13.0);
            myDiffs.add(17.0);
            myDiffs.add(7.0);
            myDiffs.add(0.0);
            myDiffs.add(2.0);
            myDiffs.add(3.0);
            myDiffs.add(33.0);
            myDiffs.add(23.0);
            myDiffs.add(26.0);
            myDiffs.add(12.0);
            myDiffs.add(12.0);
            myDiffs.add(19.0);
            myDiffs.add(14.0);
            myDiffs.add(9.0);
            myDiffs.add(26.0);
            myDiffs.add(24.0);
            myDiffs.add(13.0);
            myDiffs.add(19.0);
            myDiffs.add(2.0);
            myDiffs.add(7.0);
            myDiffs.add(28.0);
            myDiffs.add(15.0);
            myDiffs.add(2.0);
            myDiffs.add(5.0);
            myDiffs.add(17.0);
            myDiffs.add(2.0);
            myDiffs.add(16.0);
            myDiffs.add(19.0);
            myDiffs.add(2.0);
            myDiffs.add(31.0);
    DataPanel myPP = new DataPanel(myDiffs,this.getHeight(),this.getWidth());
    this.add(myPP);
    this.setVisible(true);// Display the panel.
}
public static void main(String[] args){
    DataGUI myDataGUI = new DataGUI();
    myDataGUI.setVisible(true);
}
}

DataPanel.java (Note: I edited the code below to include trashgod's suggestions, but it still does not work.)

import java.awt.*;
import java.awt.geom.AffineTransform;
import javax.swing.*;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.*;

class DataPanel extends JPanel {
Insets ins; // holds the panel's insets
ArrayList<Double> myDiffs;
double maxDiff = Double.NEGATIVE_INFINITY;
double minDiff = Double.POSITIVE_INFINITY;
double maxPlot;

DataPanel(ArrayList<Double> Diffs, int h, int w){
    setOpaque(true);// Ensure that panel is opaque.
    setPreferredSize(new Dimension(w, h));
    setMinimumSize(new Dimension(w, h));
    setMaximumSize(new Dimension(w, h));
    myDiffs = Diffs;
    repaint();
    this.setVisible(true);
}

protected void paintComponent(Graphics g){// Override paintComponent() method.
    super.paintComponent(g);
    //get data about plotting environment and about text
    int height = getHeight();
    int width = getWidth();
    ins = getInsets();
    Graphics2D g2d = (Graphics2D)g;
    FontMetrics fontMetrics = g2d.getFontMetrics();
    String xString = ("x-axis label");
    int xStrWidth = fontMetrics.stringWidth(xString);
    int xStrHeight = fontMetrics.getHeight();
    String yString = "y-axis label";
    int yStrWidth = fontMetrics.stringWidth(yString);
    int yStrHeight = fontMetrics.getHeight();
    String titleString ="Title of Graphic";
    int titleStrWidth = fontMetrics.stringWidth(titleString);
    int titleStrHeight = fontMetrics.getHeight();
    int leftMargin = ins.left;
    //set parameters for inner rectangle
    int hPad=10;
    int vPad = 6;
    int testLeftStartPlotWindow = ins.left+5+(3*yStrHeight);
    int testInnerWidth = width-testLeftStartPlotWindow-ins.right-hPad;
    getMaxMinDiffs();
    getMaxPlotVal();
    double increment = 5.0;
    int numTicks = (int)(maxPlot/increment);//will use numTicks for: remainder, leftStartPlotWindow, innerRectangle+labels+tickmarks
    int remainder = testInnerWidth%numTicks;
    int leftStartPlotWindow = testLeftStartPlotWindow-remainder;
    System.out.println("remainder is: "+remainder);
    int bottomPad = (3*xStrHeight)-vPad;
    int blueTop = ins.bottom+(vPad/2)+titleStrHeight;
    int blueHeight = height-bottomPad-blueTop;
    int blueWidth = blueHeight;
    int blueBottom = blueHeight+blueTop;

    //plot outer rectangle
    g.setColor(Color.red);
    int redWidth = width-leftMargin-1;
    g.drawRect(leftMargin, ins.bottom, redWidth, height-ins.bottom-1);
    //write top label
    g.setColor(Color.black);
    g.drawString(titleString, leftStartPlotWindow+((blueWidth/2)-(titleStrWidth/2)), titleStrHeight);
    // fill, then plot, inner rectangle
    g.setColor(Color.white);
    g.fillRect(leftStartPlotWindow, blueTop, blueWidth, blueHeight);
    g.setColor(Color.blue);
    g.drawRect(leftStartPlotWindow, blueTop, blueWidth, blueHeight);
    //scale the diffs to fit window
    double Scalar = blueWidth/maxPlot;
    ArrayList<Double> scaledDiffs = new ArrayList<Double>();
    for(int e = 0;e<myDiffs.size();e++){scaledDiffs.add(myDiffs.get(e)*Scalar);}
    //plot the scaled Diffs
    AffineTransform at = g2d.getTransform();//save the graphics context's transform
    g2d.translate(leftStartPlotWindow, blueTop);//translate origin to bottom-left corner of blue rectangle
    g2d.scale(1, -1);//invert the y-axis
    for(int w = 0;w<scaledDiffs.size();w++){
        if(w>0){
            double prior = scaledDiffs.get(w-1);
            int priorInt = (int)prior;
            double current = scaledDiffs.get(w);
            int currentInt = (int)current;
            g2d.drawOval(priorInt, currentInt, 4, 4);
        }
    }
    g2d.setTransform(at);//restore the transform for conventional rendering
    //write x-axis label
    g.setColor(Color.red);
    g.drawString(xString, leftStartPlotWindow+((blueWidth/2)-(xStrWidth/2)), height-ins.bottom-vPad);
    //write y-axis label
    g2d.rotate(Math.toRadians(-90), 0, 0);//rotate text 90 degrees counter-clockwise
    g.drawString(yString, -(height/2)-(yStrWidth/2), yStrHeight);
    g2d.rotate(Math.toRadians(+90), 0, 0);//rotate text 90 degrees clockwise
    // draw tick marks on x-axis
    NumberFormat formatter = new DecimalFormat("#0.0");
    double k = (double)blueWidth/(double)numTicks;
    double iteration = 0;
    for(int h=0;h<=numTicks;h++){
        int xval = (int)(h*k);
        g.setColor(Color.red);
        g.drawLine(leftStartPlotWindow+xval, blueBottom+2, leftStartPlotWindow+xval, blueBottom+(xStrHeight/2));//draw tick marks
        g.drawString(formatter.format(iteration),leftStartPlotWindow+xval-(fontMetrics.stringWidth(Double.toString(iteration))/2),blueBottom+(xStrHeight/2)+13);
        iteration+=increment;
    }
    // draw tick marks on y-axis
    iteration = 0;
    for(int h=0;h<=numTicks;h++){
        int yval = (int)(h*k);
        g.setColor(Color.red);
        g.drawLine(leftStartPlotWindow-2, blueBottom-yval, leftStartPlotWindow-(yStrHeight/2), blueBottom-yval);//draw tick marks
        g2d.rotate(Math.toRadians(-90), 0, 0);//rotate text 90 degrees counter-clockwise
        g.drawString(formatter.format(iteration),leftStartPlotWindow-2,blueBottom-(fontMetrics.stringWidth(Double.toString(iteration))/2));
        g2d.rotate(Math.toRadians(+90), 0, 0);//rotate text 90 degrees clockwise
        iteration+=increment;
    }
}
void getMaxMinDiffs(){// get max and min of Diffs
    for(int u = 0;u<myDiffs.size();u++){
        if(myDiffs.get(u)>maxDiff){maxDiff = myDiffs.get(u);}
        if(myDiffs.get(u)<minDiff){minDiff = myDiffs.get(u);}
    }
}
void getMaxPlotVal(){
    maxPlot = maxDiff;
    maxPlot += 1;//make sure maxPlot is bigger than the max data value
    while(maxPlot%5!=0){maxPlot+=1;}//make sure maxPlot is a multiple of 5
}
}

Also, as always, links to articles or tutorials on the topic are much appreciated.

like image 967
CodeMed Avatar asked Feb 21 '12 03:02

CodeMed


2 Answers

One approach is shown in SineTest. In outline,

  1. Save the graphics context's transform.

    Graphics2D g2d = (Graphics2D) g;
    AffineTransform at = g2d.getTransform();
    
  2. Translate the origin to the center.

    g2d.translate(w / 2, h / 2);
    
  3. Invert the y-axis.

    g2d.scale(1, -1);
    
  4. Render using cartesian coordinates.

  5. Restore the transform for conventional rendering.

    g2d.setTransform(at);
    

enter image description here

like image 106
trashgod Avatar answered Nov 04 '22 04:11

trashgod


Apologies for somewhat incomplete answer, but this may get your gears turning. Java draws things the way you described them: It considers the top left corner of the screen to be 0, 0 and draws x increasing to the right and y increasing downwards. If you make the line that states

g2d.drawOval(priorInt, currentInt, 4, 4);

into

g2d.drawOval(blueWidth - priorInt, blueHeight - currentInt, 4, 4);

it should yield the correct results for your first issue. I need a bit more info on the second problem to help you with that one though. Are they just off the screen or are the getting drawn over by something else? Try flipping +s and -s around to see if you can get the correct result if that is the case.

like image 25
bob_twinkles Avatar answered Nov 04 '22 06:11

bob_twinkles