I'm trying to draw a recursive tree, but I'm already stuck at the beginning. I've been trying to figure this out, for a while now, and I just can't seem to get it right.
I am using the StdDraw library. Here's what I want to do:
I've already drawn the trunk (black line). But I need to draw the red line too.
In the main function I've set the canvas size to 500x500px and I've set the x and y limit from 0-500.
StdDraw.setCanvasSize(500, 500);
StdDraw.setScale(0, 500);
The tree parameters that I receive are the tree length (d) and the angle (sine). There are also some others (needed for recursion, but not relevant for this simple problem)
I've drawn the trunk using the following code:
StdDraw.line(250, 150, 250, 150+d);
If you're not familiar with StdDraw it's: StdDraw.line(x0, y0, x1, y1)
Now for the red line I've been struggling with some basic trigonometry, but I don't know where I'm wrong. Here's the code:
//The length of the red line is 3/4 the length of the trunk(given in instructions)
double hypotenuse = (3/4)*d;
//We get the opposite by multiplying the sine * hypotenuse, correct?
double opposite = Math.sin(alpha) * hypotenuse;
//Pythagorean Theorem to get the adjacent
double adjacent = Math.sqrt(Math.pow(hypotenuse, 2)-Math.pow(opposite, 2));
//We draw the line from the last x position minus adjacent to move to the left
//and from last y position upwards by 150+d(previous position)+opposite
StdDraw.line(250,150+d,250-adjacent,150+d+opposite);
I create a new Tree object in main with d=110, alpha=40 to test it.
If I remove the trunk and only try to draw the red line (using StdDraw.line(250,150+d,250-adjacent,150+d+opposite)). I only get a black dot, like this:
All your sides are getting set to zero, because here:
double hypotenuse = (3/4)*d;
(3/4) equals 0. Change to:
double hypotenuse = (3/4.0)*d;
or:
double hypotenuse = 0.75 * d;
Related
I am generating 2D arcs using the following code.
final Arc2D.Double arcPath = new Arc2D.Double();
arcPath.setArcByCenter(centerPoint.getX(), centerPoint.getY(), radius, fDXFArc.getStartAngle(), fDXFArc.getTotalAngle(), Arc2D.OPEN);
The arcs are perfectly rendered on my Canvas but I do not know if they are Clockwise or Counter Clockwise. Can someone share the algorithm to detect the arc's orientation ?
I see two hints for always counterclockwise (80% sure):
First java.awt.geom.Arc2D itself tells in it's class description:
* The angles are specified relative to the non-square
* framing rectangle such that 45 degrees always falls on the line from
* the center of the ellipse to the upper right corner of the framing
* rectangle.
This can only be true if 0 degrees are at 12 pm and degrees measured clockwise or 3 pm and degrees measured counterclockwise.
Second public void setAngles() in the same package measure degrees counterclockwise:
* The arc will always be non-empty and extend counterclockwise
* from the first point around to the second point.
following that it would make sense to follow the same pattern in all functions of that class.
If you need to be sure: Ask the author of that class:
* #author Jim Graham
I actually manage to determine my Arcs direction. I just splitted every arc that is larger than 180degrees to 2 smaller arcs and I use the following code
Point startPoint = arc.getBorderPoint(EBorderPoint.StartPoint);
Point endPoint = arc.getBorderPoint(EBorderPoint.EndPoint);
Point centerPoint = arc.getBorderPoint(EBorderPoint.CenterPoint);
final double result = (endPoint.getX() - startPoint.getX()) * (centerPoint.getY() - startPoint.getY()) - (endPoint.getY() - startPoint.getY()) * (centerPoint.getX() - startPoint.getX());
boolean isClockWise = result > 0 ? false : true;
if (result == 0)
{
// Since I splitted the large arcs to 2 smaller arcs
// the code will never go to this if statement
}
I need to draw a fill square, that square must be painted with lines from center.
we can't use java shapes
We have a center point and the pen must draw a line from center to the square edge in each angle.
The problem here is the Maths, what can i use to calculate the distance needed to paint. Because if i use always same distance it will draw a circle.
Thanks
When drawing the square you can think of the length you need to draw at any angle as being the length of the hypoteneuse of a right-angled triangle. You can solve this with trigonometric ratios easily enough. The tricky part is that the base of the triangle moves around.
Taking the example of a line at 45 degrees shown in the left half of the diagram below:
You need to work out the length of the red line (hyp). You can use
trigonometry to work out the length of hyp based on its angle to
adj and the length of the
adj. The length of the adj side is half the height of the
square.
The formula to use is:
cos(angle) = adj/hyp
rearranged:
hyp = adj/cos(angle)
The code would look something like this:
public static double calculateLengthToPaint(double angle, double heightOfSquare){
return (heightOfSquare/2.0) / Math.cos(Math.toRadians(angle));
}
Unfortunately that's not all though. This works perfectly for the first 45 degrees, but when angle > 45 degrees then the adjacent side of the triangle changes place (as can be seen in the right half of the diagram below). It keeps flipping over every 45 degrees.
To handle this flipping you need to use the angle that's passed into the method (the angle around the square from the 12 o'clock position) to work out the angle of the triangle we are imagining. I've modified the method above to add logic to work out the corrected angle.
public static double calculateLengthToPaint(double angle, double heightOfSquare){
double flippy = angle % 90;
if (flippy > 45.0){
flippy -= 90;
flippy = Math.abs(flippy);
}
return (heightOfSquare/2.0) / Math.cos(Math.toRadians(flippy));
}
Notes: This code takes it's angle in degrees and only works for positive angles. Also, if you want to have the lines meet the square at even increments around the perimiter then you need to come up with a solution that uses the pythagorean theorem to work out the length of the hypoteneuse and then use trigonometry to work out the angle to draw it at.
Hope that helps.
Just use java.awt.Graphics.fillRect(int, int, int, int).
I would say something like this
for(int i = sideLength * -1; i < sideLength; i++) {
for(int j = sideLength * -1; j < sideLength; j++) {
graphics.drawLine(centerX + i, centerY + j);
}
}
That way, it will paint a line to every point in your square that you're trying to make. I'm not sure if this is what you wanted, but it will work.
I am trying to compute a polygon that surrounds a line connecting multiple points (e.g. a GPX track).
The image below shows an example with the track as red line and the desired polygon in blue.
As simplification the red points are denoted by x and y - not by latitude/longitude.
How do I compute such an environment (light blue polygon) if I only have the list of the three points specifying the path?
Partial solutions (e.g. for only two points) or hints about mathematical libraries (in Java) that provide algorithms for such a computation would also bring me one step forward.
Further assumptions:
The track is intersection free.
Update:
Using the approach as presented by Rogach and xan I ran into some problems if the angle between the lines is smaller than 90 degree or larger than 270 degree:
As you can see the polygon gets intersects itself which leads to a serious problem.
From my point of view using an java.awt.geom.Area is the better approach:
My solution (based on the code by Rogach):
For each line connecting two points of the track I compute a surrounding polygon. Afterwards I add (area union) the computed polygon to an Area which does all the necessary computation for me. As the Area strictly uses the "or" algorithm on adding new polygons I do not have to care about "self intersections" of the polygon as presented in the update above.
Area area = new Area();
for (int i = 1; i < points.size(); i++) {
Point2D point1 = points.get(i - 1);
Point2D point2 = points.get(i);
Line2D.Double ln = new Line2D.Double(point1.getX(), point1.getY(), point2.getX(), point2.getY());
double indent = 15.0; // distance from central line
double length = ln.getP1().distance(ln.getP2());
double dx_li = (ln.getX2() - ln.getX1()) / length * indent;
double dy_li = (ln.getY2() - ln.getY1()) / length * indent;
// moved p1 point
double p1X = ln.getX1() - dx_li;
double p1Y = ln.getY1() - dy_li;
// line moved to the left
double lX1 = ln.getX1() - dy_li;
double lY1 = ln.getY1() + dx_li;
double lX2 = ln.getX2() - dy_li;
double lY2 = ln.getY2() + dx_li;
// moved p2 point
double p2X = ln.getX2() + dx_li;
double p2Y = ln.getY2() + dy_li;
// line moved to the right
double rX1_ = ln.getX1() + dy_li;
double rY1 = ln.getY1() - dx_li;
double rX2 = ln.getX2() + dy_li;
double rY2 = ln.getY2() - dx_li;
Path2D p = new Path2D.Double();
p.moveTo(lX1, lY1);
p.lineTo(lX2, lY2);
p.lineTo(p2X, p2Y);
p.lineTo(rX2, rY2);
p.lineTo(rX1_, rY1);
p.lineTo(p1X, p1Y);
p.lineTo(lX1, lY1);
area.add(new Area(p));
}
As I see, this problem is similar to polygon buffering problem.
I think following approach can help you:
For each segment of your track, find two lines - one to the left and one to the right.
Then, iterate for over your ofsetted lines, and resolve intersections. For example:
http://img25.imageshack.us/img25/7660/temprhk.png
Add caps to ends, and you're done! :)
And some code:
Moving a line to the left:
Line2D l;
double indent; // distance from central line
double dx = ln.getX2() - ln.getX1();
double dy = ln.getY2() - ln.getY1();
double length = ln.getP1().distance(ln.getP2());
double newX1 = l.getX1() - indent*(dy/length);
double newY1 = l.getY1() + indent*(dx/length);
double newX2 = l.getX2() - indent*(dy/length);
double newY2 = l.getY2() + indent*(dx/length);
Line2D leftLine = new Line2D.Double(newX1, newY1, newX2, newY2);
For moving it to the right, change "+" to "-" and vice versa in the last 4 lines of code.
About working with intersections - if two line segment intersect, you just output the intersection point. If they do not, then situation is a bit more complicated - you can, of course, still output the intersection, but in case of rapidly turning track, there will be strange outbursts. I inserted an arc segment in similar situation, but the code is to big and scattered, so I can't paste it here.
Or, you can do as you show on your picture - just connect end points.
And, by the way, if speed is not a big issue, you can use even better way - for each line of track, find left and right lines, add caps, pack it all into Path2D, then create Area from Path2D.
In such case, you can make this "line with caps" as intersection of three areas: rectangle, whose points are just end points of right and left line, and two circles with centers on original track segment ends.
When you compute Areas for all lines, just intersect them using Area add() method.
This approach deals with just any situations, even self-intersections and breaks in the track.
See my answer to a similar question, "How to draw an outline around any line."
Same idea as Rogach provides here, but perhaps different drawings and explanations will help clarify it.
If you don't want to write the code for the buffering as described by Rogach, JTS could do the magic for you.
See the developer guide for a quick introduction.
Half-baked suggestion: Calculate the normal to each segment. Then, for each vertex V_i, interpolate the normals from its adjacent segments to get n_i (normalise it again) and add two vertices at V_i +/- a*n_i where a is some scaling factor.
If you join these points, you won't get exactly your blue polygon, but it might be good enough.
You may have to keep track of which "side" the new vertices are on. If you can close the curve without self-intersections this just becomes a point in polygon test for each vertex.
How could I draw a quadratic curve or a trigonometric curve (such as sin(x)) on a Canvas?
Like you, I needed to draw a curved line from point(x1, y1) to point (x2, y2). I did some searching around which lead me to the Path class (android.graphics.Path). Path has numerous methods for drawing lines. Once you have created a path you use a draw method to make the actual line. The paths can be rotated, transformed, saved, and added to. There are arcs, circles, and rectangles that be drawn with this class too.
http://developer.android.com/reference/android/graphics/Path.html
Set start point of path → mPath.moveTo(x1, y1);
Set constant and end points → mPath.quadTo(cx, cy, x2, y2);
Convert path to line → canvas.drawPath(mPath, mPaint);
Here is a drawEquation() method I wrote for a Graph class - I think it may help. The basic idea to create a method that accepts an equation (which is basically just a function) like
function(x) = Math.sin(x);
and then loop through the bounds of the graph and draws small segments connecting each point. The transformContext() just inverts the canvas context so that increasing values of y go upwards and not downwards:
Graph.prototype.transformContext = function(){
var canvas = this.canvas;
var context = this.context;
// move context to center of canvas
this.context.translate(this.centerX, this.centerY);
// stretch grid to fit the canvas window, and
// invert the y scale so that that increments
// as you move upwards
context.scale(this.scaleX, -this.scaleY);
};
Graph.prototype.drawEquation = function(equation, color, thickness){
var canvas = this.canvas;
var context = this.context;
context.save();
this.transformContext();
context.beginPath();
context.moveTo(this.minX, equation(this.minX));
for (var x = this.minX + this.iteration; x <= this.maxX; x += this.iteration) {
context.lineTo(x, equation(x));
}
context.restore();
context.lineJoin = "round";
context.lineWidth = thickness;
context.strokeStyle = color;
context.stroke();
};
Most drawing APIs dont provide such functions, you will have to calculate the pixels of your desired curve in pixels and draw piece by piece on the canvas using one or more calls to the canvas API.
Use Canvas.drawPath and Path.quadTo.
I'm going to assume that you are familiar with drawing basic lines on a canvas, if not then respond back and we can delve further back. However, as far as just drawing a sine function there is a function within the Math class that has just what you need.
http://download.oracle.com/javase/1.4.2/docs/api/java/lang/Math.html#sin%28double%29
From there you just need to pass your x variable(in radians) into the function and save it's output as a y variable. This represent a point on your graph. Now increment the x1 variable by a small amount (perhaps 1/100 of your graph, though you will need to adjust this to taste), run it through the function again and save those variables(x2 and y2) as your second point. Draw a line between these two points. Save your x2,y2 variables as x1, y1 and increment your x value again to find the third point, so on and so forth. This is not a "true" curve as it is really just a series of lines which approximate the function, a calculus approach if you will.
So:
x1 = x; // where x is some point on the x axis which you would like to start graphing at.
y1 = sin(x);
x2 = x1 + increment;
y2 = sin(x2);
//Draw a line here
x1 = x2;
y1 = y2;
//return to top, this code would obviously be in a loop in which uses increment as it's own increment with the initial value being equal to the amount you want to increment each time(let's say....5) and the "next" statement being increment = increment + 5.
There is also a GraphCanvas class which I am unfamiliar with which appears to take those same points and draw the curve between them, though I am unsure what sort of transform is being used to draw the curve and how accurate that is. Here is the Class:
http://www.java2s.com/Code/Java/Swing-Components/GraphCanvas.htm
Im trying to get into some basic JavaFX game development and I'm getting confused with some circle maths.
I have a circle at (x:250, y:250) with a radius of 50.
My objective is to make a smaller circle to be placed on the circumference of the above circle based on the position of the mouse.
Where Im getting confused is with the coordinate space and the Trig behind it all.
My issues come from the fact that the X/Y space on the screen is not centered at 0,0. But the top left of the screen is 0,0 and the bottom right is 500,500.
My calculations are:
var xpos:Number = mouseEvent.getX();
var ypos:Number = mouseEvent.getY();
var center_pos_x:Number = 250;
var center_pos_y:Number = 250;
var length = ypos - center_pos_y;
var height = xpos - center_pos_x;
var angle_deg = Math.toDegrees(Math.atan(height / length));
var angle_rad = Math.toRadians(angle_deg);
var radius = 50;
moving_circ_xpos = (radius * Math.cos(angle_rad)) + center_pos_x;
moving_circ_ypos = (radius * Math.sin(angle_rad)) + center_pos_y;
I made the app print out the angle (angle_deg) that I have calculated when I move the mouse and my output is below:
When the mouse is (in degrees moving anti-clockwise):
directly above the circle and horizontally inline with the center, the angle is -0
to the left and vertically centered, the angle is -90
directly below the circle and horizontally inline with the center, the angle is 0
to the right and vertically centered, the angle is 90
So, what can I do to make it 0, 90, 180, 270??
I know it must be something small, but I just cant think of what it is...
Thanks for any help
(and no, this is not an assignment)
atan(height/length) is not enough to get the angle. You need to compensate for each quadrant, as well as the possibility of "division-by-zero". Most programming language libraries supply a method called atan2 which take two arguments; y and x. This method does this calculation for you.
More information on Wikipedia: atan2
You can get away without calculating the angle. Instead, use the center of your circle (250,250) and the position of the mouse (xpos,ypos) to define a line. The line intersects your circle when its length is equal to the radius of your circle:
// Calculate distance from center to mouse.
xlen = xpos - x_center_pos;
ylen = ypos - y_center_pos;
line_len = sqrt(xlen*xlen + ylen*ylen); // Pythagoras: x^2 + y^2 = distance^2
// Find the intersection with the circle.
moving_circ_xpos = x_center_pos + (xlen * radius / line_len);
moving_circ_ypos = y_center_pos + (ylen * radius / line_len);
Just verify that the mouse isn't at the center of your circle, or the line_len will be zero and the mouse will be sucked into a black hole.
There's a great book called "Graphics Gems" that can help with this kind of problem. It is a cookbook of algorithms and source code (in C I think), and allows you to quickly solve a problem using tested functionality. I would totally recommend getting your hands on it - it saved me big time when I quickly needed to add code to do fairly complex operations with normals to surfaces, and collision detections.