I am attempting to create an application that draws Fibonacci Arcs similar to these.
However, I'd like full circles instead of arcs, and I'd like to draw more than the three Fibonacci lines shown in the picture. I've created an application using JFreeChart to attempt to accomplish this. However, here is the result when trying to draw the same arcs (but as circles) shown in the previous picture.
Initially, it just looks wrong, but when I zoom out, it is indeed a circle, but it's way too big.
To calculate the arcs, you draw a line, then take a Fibonacci ratio - let's use .381 for example - the percentage of that line. If you look at the first picture, you'll see the innermost arc intersects the line at .381% the distance of the line from the centre of the circle. First I calculate this point. Then I construct a line from the .381% point to the centre. Then I take the distance of this line, which should be the radius. Then I use this radius to draw the circle.
Here's the code to calculate the radius. Where stop and start are the stop and start points of the line drawn.
multiplier = ratio38Value + i;
diffx = (stop.getX() - start.getX()) * multiplier;
diffy = (stop.getY() - start.getY()) * multiplier;
xValue = start.getX() + diffx;
yValue = start.getY() + diffy;
point = new Point(xValue, yValue);
lineSegment = new Line(point, stop);
radius = lineSegment.getDistance();
circle = new Circle(stop.getX(), stop.getY(), radius);
circles.add(circle);
Here is the code to calculate the distance of a line
public double getDistance(){
double x = Math.pow(endPoint.getX() - startPoint.getX(), 2);
double y = Math.pow(endPoint.getY() - startPoint.getY(), 2);
return Math.sqrt(x + y);
}
I get back a list of circle objects (this is an object I created that holds the radius and centre point) one for each circle that needs to be drawn and then draw them.
List<Circle> circles = fibonacciCalculations.getFibonacciArcs(startPoint, endPoint);
if(circles != null)
{
for (Circle circle : circles){
double xCenter = circle.getX();
double yCenter = circle.getY();
double radius = circle.getRadius();
plot.addAnnotation(new XYShapeAnnotation(new Ellipse2D.Double(xCenter - radius, yCenter - radius, radius + radius, radius + radius)));
}
}
I think the issue has something to do with how the x-axis of time and the y axis of price doesn't exactly correlate. What I mean is, if the radius is 20, you'll be going 20 units away from the centre at each point. So say you're stock price is only 5 dollars, at your lowest point you will then be at -15. If that is the case, I have no idea how to fix it. But it also could be some error in my logic. Any ideas would be appreciated.
EDIT: While the bars look like they may be weekly bars in the first picture, they are indeed daily bars. Also, I have already converted the coordinates from data space to x y coordinates. I use this code below to do that.
#Override
public void chartMouseMoved(ChartMouseEvent event) {
Rectangle2D dataArea = cp.getScreenDataArea();
JFreeChart chart = event.getChart();
XYPlot plot = (XYPlot) chart.getPlot();
ValueAxis xAxis = plot.getDomainAxis();
ValueAxis yAxis = plot.getRangeAxis();
double x = xAxis.java2DToValue(event.getTrigger().getX(), dataArea,
RectangleEdge.BOTTOM);
double y = yAxis.java2DToValue(event.getTrigger().getY(), dataArea,
RectangleEdge.LEFT);
I'm not sure of the proper terminology, so lets call the actual (x,y) coordinates that represent where you are on your monitor "screen space" and let's call the (x,y) coordinates of the chart "chart space".
My issue was I was converting the points from screen space to chart space and then calculating my points. Instead, I should have calculated all my points in screen space, and then converted each calculated point to chart space.
Where i is the amount of groups of arcs I want to draw. (i = 0, then I am drawing circles for the 38, 50, 62 ratios, i = 1 then I'm drawing circles for the -1.68, -1.50...1.50, 1.68 ratios) I use this code to get my points that are a given ratio between the center and the starting point.
multiplier = ratio62Value + i;
diffx = (stop.getX() - start.getX()) * multiplier;
diffy = (stop.getY() - start.getY()) * multiplier;
xValue = start.getX() + diffx;
yValue = start.getY() + diffy;
point = new Point(xValue, yValue);
line = new Line(point, stop);
line.calculateCirclePoints();
Here is the method to calculate the points on the circle. Where, endPoint is the center point, and the radius is the distance from the start point to the end point.
public void calculateCirclePoints(){
double radius = getDistance();
double radians;
double x;
double y;
Point currentPoint;
for (int degrees = 0; degrees <= 360; degrees += 1){
radians = Math.toRadians(degrees);
x = endPoint.getX() + (radius * Math.cos(radians));
y = endPoint.getY() + (radius * Math.sin(radians));
currentPoint = new Point(x, y);
points.add(currentPoint);
}
}
Lastly, I convert all of these points to chart space, and draw them on the chart.
public static Point converPointTo2D(Point point, Rectangle2D dataArea, XYPlot plot){
double x;
double y;
CustomNumberAxis xAxis = (CustomNumberAxis) plot.getDomainAxis();
CustomNumberAxis yAxis = (CustomNumberAxis) plot.getRangeAxis();
x = xAxis.java2DToValue(point.getX(), dataArea,
RectangleEdge.BOTTOM);
y = yAxis.java2DToValue(point.getY(), dataArea,
RectangleEdge.RIGHT);
return new Point(x, y);
}
One point to note, the radius of the circles is dependent on how much of a specific chart you're showing. A circle drawn on a 1 year chart from point a to point b will be smaller than a circle drawn on a 5 year chart from those same points.
Related
I am creating a Cartesian Coordinate graph using NumberAxis to create an x and y axis in a LineChart, and use getDisplayPosition to find the distance between two points along the x axis and two points along the y axis. The differences do not equal each other.
I set upper/lower bounds of the x and y axis to be the same with the same tickUnit setting. Visually, the grid looks square and equal on all sides but I cannot seem to make them exactly equal.
Here is how I set NumberAxis for x and y within my linechart:
xAxis.setLowerBound(-2.4);
xAxis.setUpperBound(2.4);
xAxis.setTickUnit(.1);
xAxis.setSide(Side.BOTTOM);
xAxis.setAutoRanging(false);
yAxis.setLowerBound(-2.4);
yAxis.setUpperBound(2.4);
yAxis.setTickUnit(.1);
yAxis.setSide(Side.LEFT);
yAxis.setAutoRanging(false);
LineChart linechart = new LineChart<Number,Number>(xAxis, yAxis);
Here is how I convert inputs for a rectangle to be displayed properly
private Rectangle calcRectangle(SectionRectangle rectangle){
double xPosition = xAxis.getDisplayPosition(rectangle.getXorigin());
double yPosition = yAxis.getDisplayPosition(rectangle.getYorigin());
double xCoord = xPosition + chartZeroX;//x origin of rectangle
double yCoord = yPosition + chartZeroY;//y origin of rectangle
double width = rectangle.getXorigin() + rectangle.getWidth();
double widthPosition = xAxis.getDisplayPosition(width);
double heightPosition = yAxis.getDisplayPosition(rectangle.getYorigin() + rectangle.getHeight());
Rectangle calculatedRectangle = new Rectangle(xCoord, yCoord - (yPosition - heightPosition), widthPosition - xPosition, yPosition - heightPosition);
calculatedRectangle.setFill(Color.TRANSPARENT);
Rotate rotate = new Rotate();
rotate.setAngle(360 - rectangle.getRotation());
rotate.setPivotX(xPosition + chartZeroX);
rotate.setPivotY(yPosition + chartZeroY);
calculatedRectangle.getTransforms().add(rotate);
calculatedRectangle.setStroke(Color.BLACK);
return calculatedRectangle;
And here is how I add the shape onto the graph
Pane chartContent = (Pane) linechart.lookup(".chart-content");
Rectangle rectangle = calcRectangle(rectangleData);
chartContent.getChildren().add(rectangle);
Without rotating, the rectangle will be displayed to scale as expected, but because of the slight variance in pixel width vs pixel height, rotating 90 degrees causes the rectangle to measure taller than it had measured wide.
Here is a more precise example of the issue
double xdiff = xAxis.getDisplayPosition(1) - xAxis.getDisplayPosition(0);
double ydiff = yAxis.getDisplayPosition(0) - yAxis.getDisplayPosition(1);
here xdiff = 195.208 and ydiff = 191.041
Hoping for some way to force the x and y axis to maintain exact equal distance.
I solved my own problem by controlling the height and width of the linechart:
linegraph.setPrefHeight(850);
linegraph.setMinHeight(850);
linegraph.setMaxHeight(850);
linegraph.setMinWidth(853);
linegraph.setMaxWidth(853);
linegraph.setPrefWidth(853);
linegraph.setPrefSize(853, 850);
linegraph.setMinSize(853, 850);
linegraph.setMaxSize(853, 850);
I did a check of the distance between x=0, x=1 and y=0, y=1 using:
double xlength = xAxis.getDisplayPosition(1) - xAxis.getDisplayPosition(0);
double ylength = yAxis.getDisplayPosition(0) - yAxis.getDisplayPosition(1);
I slowly incremented my width until both lengths were equal.
I am making a game in java and my player can be rotated 360 degrees. I have to make a hitbox that rotates and moves with the player. I used a Rectangle to do this, however, it uses int values instead of double values and as a result, the hitbox's positioning is a little off (because I add double values to integers). How do I fix this?
I've already tried Rectangle2D.Double but I can't modify the x and y values.
Here is the relevant code:
Declaration:
private Rectangle hitbox;
private Shape transformed;
Initialization
hitbox = new Rectangle((int) x, (int) y + 20, currentImg.getWidth(), currentImg.getHeight()/2 + 20);
transformed = hitbox;
Movement
hitbox.x += xM * Math.sin(Math.toRadians(angle));
hitbox.y += yM * Math.cos(Math.toRadians(angle));
Rotation
AffineTransform transform = new AffineTransform();
transform.rotate(Math.toRadians(this.angle), hitbox.x + hitbox.getWidth()/2, hitbox.y + hitbox.getHeight()/2);
transformed = transform.createTransformedShape(hitbox);
Problem
I'd like to calculate the origin of a bullet which comes out of turret barrels which aren't at the center of the turret.
Current Situation
I have a turret with a center barrel which can shoot bullets from it. The turret is at the vector location and as a varying angle, i. e. it keeps rotating. In order to make the center turret shoot bullets from the end of the barrel I calculate the end of it like this:
double speed = 1;
double angle = turret.angle;
// start at the end of the barrel
double x = turret.location.x + Math.cos( angle) * turret.centerCannonLength;
double y = turret.location.y + Math.sin( angle) * turret.centerCannonLength;
// calculate angle and velocity of bullets
double dx = Math.cos(angle) * speed;
double dy = Math.sin(angle) * speed;
from that I can set the initial location and the velocity of the bullets with this:
Bullet bullet = new Bullet();
bullet.setLocation( x, y);
bullet.setVelocity( dx, dy);
bullet.setAngle( angle);
Task
Now I'd like to have 2 additional barrels from which the turret can fire. The barrels aren't in the center, they are offset left and right.
Question
How do I calculate the origin of the bullets from the left and right barrel?
Here's a demo screenshot:
The blue circle describes the rotation of the turret. The yellow parts are the turret and the center barrel. The red parts are the additional left and right barrels of which I'd like to calculate the bullet origin position.
Thank you very much for the help!
First, define the location of the origin in the turret's local coordinate space. The center barrel would be:
localX = turret.centerCannonLength
localY = 0
Then, find the current rotation matrix of the turret:
M = / cos(angle) -sin(angle) \
\ sin(angle) cos(angle) /
And multiply this matrix with the local position:
globalX = turret.location.x + cos(angle) * localX - sin(angle) * localY
globalY = turret.location.y + sin(angle) * localX + cos(angle) * localY
I am working on creating graphs with vertices and edges. The graph is directed, so the edges are represented as arrows. My problem is getting the correct coordinates for the arrows.
A Vertex has a Coordinate (see class below), while an Edge goes from a Vertex to another Vertex. The challenge is that a vertex is drawn with a fixed radius (see picture below). I'm having problems getting the arrow pointing to the correct place on the circles circumference. It seems like with the code I currently have, the arrow points to the top-left corner, not the closest point.
I have the following method for drawing the arrows:
public static void drawArrow(Graphics g, Color color, int size,
Coordinate from, Coordinate to, Coordinate offset) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setColor(color);
double dx = to.x - from.x, dy = to.y - from.y;
double angle = Math.atan2(dy, dx);
int len = (int) Math.sqrt(dx*dx + dy*dy);
AffineTransform at = AffineTransform.getTranslateInstance(from.x + offset.x, from.y + offset.y);
at.concatenate(AffineTransform.getRotateInstance(angle));
g2.transform(at);
// Draw horizontal arrow starting in (0, 0)
g2.drawLine(0, 0, len, 0);
g2.fillPolygon(new int[] {len, len-size, len-size, len},
new int[] {0, -size, size, 0}, 4);
}
I got the essentials of the arrow code from an answer by aioobe, here.
I this method by overriding Edge's paintComponent function:
#Override
public void paintComponent(Graphics g) {
double radius = this.from.getRadius();
Coordinate vector = this.from.getPosition().clone();
vector.normalize();
vector.x = vector.x * radius; vector.y = vector.y * radius;
Coordinate to = new Coordinate(this.to.getPosition().x - vector.x,
this.to.getPosition().y - vector.y);
GraphicsUtils.drawArrow(g, this.color, ARROW_SIZE,
this.from.getPosition(), to,
new Coordinate(radius, radius));
}
As the drawArrow method does what it's supposed to, it draws an arrow from a to b, I want to change the way that I am calling it in the above method. For example, by using the offset parameter for the drawArrow method or something alike.
The Coordinate class:
public class Coordinate {
public double x;
public double y;
...
public void normalize() {
double length = Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2));
this.x = this.x / length;
this.y = this.y / length;
}
...
}
A screenshot of my current output:
Note there are both arrows from D to E and E to D. The latter is not showing because the arrow head is behind D's circle.
Now to be clear, the problem is:
In the paintComponent-method, I am taking the radius of the circle and multiplying it with the normalized (see method) vector. This would give me a point of the circle's circumference, but it seems that always results in the top-left corner, which I don't get. I want to calculate the point on the circumference closest to the source vertex.
Like so:
Any suggestions?
You can calculate the arrow endpoints from the coordinates of the vertex centers and the vertex image radius. If (xa, ya) and (xb, yb) are the centers of two vertices a and b, and the vertices are drawn with radius r, then the directed line from a to be can be represented as
x = xa + t*(xb - xa)
y = ya + t*(yb - ya)
for a parameter t that varies from 0 to 1. Since t == 1 corresponds to a distance of d = sqrt((xb - xa)2 + (yb - ya)2), you just need to evaluate the above for t = r / d and t = (d-r) / d. (No trig required.)
I'm working with a Java 3D application called "Walrus" that is used to display directed graphs. The code already has a feature to highlight a node and draw label adjacent in graph given its screen coordinates.
Upon rotating the screen, the node is no more highlighted.
What I have is the node coordinates in 3D. I need to draw label to it.
Code for highlight using 3D coordinates
Point3d p = new Point3d();
m_graph.getNodeCoordinates(node, p);
PointArray array = new PointArray(1, PointArray.COORDINATES);
array.setCoordinate(0, p);
m_parameters.putModelTransform(gc);
gc.setAppearance(m_parameters.getPickAppearance());
How can I draw Label with 3D coordinates( Raster graphics throws error Renderer: Error creating immediate mode Canvas3D graphics context )
How can I convert 3D coordinates to 2D screen and use existing code to draw label at 2D screen point
Thanks,
Dakshina
I have an algorithm/method for converting [x,y,z] into [x,y] with the depth parameter:
The x value is : (int) (x - (z / depth * x))
The y value is : (int) (y - (z / depth * y))
Essentially, the depth is the focal point. The vanishing point will be at [0,0,depth].
Here's what i used to convert my 3D coordinates into perspective 2D, x2 and y2 being the 2dimensional coordinates, xyz being the 3D coordinates.
use these formulas:
x2 = cos(30)*x - cos(30)*y
y2 = sin(30)*x + sin(30)*y + z
I picked the angle 30 as it is easy for perspective purposes, also used in Isometric grids for drawing 3D on 2D papers. As the z axe will be the vertical one, x and y are the ones at 60 degrees from it right and left. Isometric Grid Picture.
I'm still working on rotation, but without altering the axes, just coordinate rotation in 3D.
Enjoy.
I found the solution.
This is the function to display Text3D at image 2D coordinates
public void drawLabel(GraphicsContext3D gc, double x, double y, int zOffset, String s) {
boolean frontBufferRenderingState = gc.getFrontBufferRendering();
gc.setBufferOverride(true);
gc.setFrontBufferRendering(true);
Point3d eye = getEye();
double labelZ = zOffset * LABEL_Z_OFFSET_SCALE
+ LABEL_Z_SCALE * eye.z + LABEL_Z_OFFSET;
double xOffset = LABEL_X_OFFSET * m_pixelToMeterScale;
double yOffset = LABEL_Y_OFFSET * m_pixelToMeterScale;
Point3d p = new Point3d(x + xOffset, y + yOffset, 0.0);
{
// Project given (x, y) coordinates to the plane z=labelZ.
// Convert from image-plate to eye coordinates.
p.x -= eye.x;
p.y -= eye.y;
double inversePerspectiveScale = 1.0 - labelZ / eye.z;
p.x *= inversePerspectiveScale;
p.y *= inversePerspectiveScale;
// Convert from eye to image-plate coordinates.
p.x += eye.x;
p.y += eye.y;
}
Transform3D scale = new Transform3D();
scale.set(LABEL_SCALE);
Vector3d t = new Vector3d(p.x, p.y, labelZ);
Transform3D translation = new Transform3D();
translation.set(t);
translation.mul(scale);
Transform3D transform = new Transform3D(m_imageToVworld);
transform.mul(translation);
gc.setModelTransform(transform);
//-----------------
int fontSize=(int)(10*m_magnification);
if(fontSize>20)
fontSize=20;
//---------------
// XXX: Courier may not be available on all systems.
Text2D text = new Text2D(s, new Color3f(1.0f, 1.0f, 1.0f),
"Courier", fontSize, Font.BOLD);
gc.draw(text);
gc.flush(true);
// NOTE: Resetting the model transform here is very important.
// For some reason, not doing this causes the immediate
// following frame to render incorrectly (but subsequent
// frames will render correctly). In some ways, this
// makes sense, because most rendering code assumes that
// GraphicsContext3D has been set to some reasonable
// transform.
gc.setModelTransform(m_objectTransform);
gc.setFrontBufferRendering(frontBufferRenderingState);
}
This is the function to take 3D coordinates and convert them to image 2D coordinates and render using above function
private boolean displayOnScreenLabel(int node, String label) {
boolean success = false;
try {
Transform3D transform = m_parameters.getObjectToEyeTransform();
Point3d nodeC = new Point3d();
m_graph.getNodeCoordinates(node, nodeC);
transform.transform(nodeC);
Point3d eye = m_parameters.getEye();
double perspectiveScale = 1.0 / (1.0 - nodeC.z / eye.z);
double centerX = eye.x + nodeC.x * perspectiveScale;
double centerY = eye.y + nodeC.y * perspectiveScale;
GraphicsContext3D gc = m_canvas.getGraphicsContext3D();
m_parameters.drawLabel(gc, centerX, centerY, m_labelZOffsetCounter++, label);
success = true;
} catch (final java.lang.OutOfMemoryError error) {
JOptionPane.showMessageDialog(m_frame, "The 3D Graphics is unable to find enough memory on your system. Kill the application!", "Out Of Memory!", JOptionPane.ERROR_MESSAGE);
} catch (Exception e) {
success = false;
}
return success;
}