I'm starting to think I'm just not able to see the obvious.
Given the following code, I would like to draw a line from the coordinates [x1, y1] to [x2, y2].
int x1 = 20;
int y1 = 10;
int x2 = 30;
int y2 = 5;
XSLFSlide pptSlide = ...
XSLFAutoShape shape = pptSlide.createAutoShape();
shape.setShapeType(ShapeType.LINE);
shape.setAnchor(x1, y1, <width>, <height>);
From what I can see the line starts at the anchor of [x1, y1] but then I have to enter a width and height instead of the coordinates of the target point. But the y component of the target coordinate is less than that if the start coordinate so I tried setting the height to a negative values, which results in an error when PowerPoint tries to open the generated PPTX document ("PowerPoint found a problem with content in the file out.pptx.");
I'm pretty sure I'm simply overlooking the obvious solution to this so can anybody help me in finding out how to draw a line for one point within the document to another point?
SetAnchor() takes an AWT Rectangle2D, which actually doesn't care if your width or height is negative (though a rectangle with negative height is not a real object after all, is it?). But POI doesn't interpret it that way, and unfortunately doesn't throw an exception to let you know.
As I understand your scenario, you just need to choose the lower starting coordinates between x1 and x2, y1 and y2 so that a positive width and height agree with your desired endpoint.
Something like this:
// using Apache POI ooxml 3.17
static void drawBetweenTwoPoints(XSLFAutoShape shape, double x1, double x2, double y1, double y2) {
shape.setAnchor(new Rectangle2D.Double(
x1 <= x2 ? x1 : x2, // choose the lowest x value
y1 <= y2 ? y1 : y2, // choose the lowest y value
Math.abs(x2 - x1), // get the actual width
Math.abs(y2 - y1) // get the actual height
));
shape.setFlipVertical(y2 < y1); // lines are drawn from rectangle top-left to
// bottom right by default.
// When y2 is less than y1, flip the shape.
}
Related
Im trying to create a slope in java. I can use the DrawLine function and it'll create a perfect one but I dont want to use that but rather create my own function for it. The problem is that it has gaps between the dots.
import java.applet.Applet;
import java.awt.Graphics;
public class slope extends Applet{
public void drawLine(int x1, int y1, int x2, int y2, Graphics g) {
double m = (y2 - y1) / (double)(x2-x1);
double y = y1;
for (int x =x1; x < x2; x++) {
drawPoint(x,(int)y,g);
y +=m;
}
}
public void paint(Graphics g) {
drawLine(20, 10, 300, 700, g); //has spaces between the dots
g.drawLine(20, 10, 300, 700); //this is perfect
}
private void drawPoint(int x, int y, Graphics g) {
g.drawLine(x, y, x, y);
}
}
Two loops: you loop over x++ only when deltaX > deltaY.
else you loop over y++ only.
Dual stepping x and y in the same loop, deciding which should be incremented (assuming you have x as a function of y too) could lead to slower drawing due to extra tests and adjacent pixels may look like a dot in the line. You'd need to play with color intensity to do antialiasing by hand (gold plating). Two loops is much simpler.
fyi, you are trying to generate an image, you could also just set ints in a matrix and make an offscreen raw image (BufferedImage and it's .setRGB() method), which you draw later. That would likely be faster and avoid visible painting delays.
Generally this is done by using an algorithm that doesn't step only along the x or y axis, but adjust the update increment by a variable amount, such that each dot is at most sqrt(2) away from each other.
So, if you think you have a point at the x value, but when you calculate it, you find that it is 3.5 pixels away (because the slope is very steep), you fall into a routine that calculates (typically recursively) an intermediate pixel between that x step
(x, y)
(0, 0) to (1, 5) distance 5.09
-> fill routine
(0, 0) to (0.5, 2.5) distance 2.69
-> fill routine
(0, 0) to (0.25, 1.25) distance 1.34 < 1.41
(0.25, 1.25) to (0.5, 2.5) distance 1.34 < 1.41
(0.5, 2.5) to (0.75, 3.75) distance 1.34 < 1.41
(0.75, 3.75) to (1, 5) distance 1.34 < 1.41
(1, 5) to (2, 10) etc...
The reason one uses 1.41 (sqrt(2)) as the maximum distance allowed is because a of pixels at a 45 degree angle from the bottom of the screen would still appear connected.
Now, in your plotting, you'll need to round the values to align to the exact pixels. There are a number of ways to do this. The simplest is just to round to the next valid value, and this works most of the time. It does have one unfortunate side effect, that is your line will appear to have jagged steps (where the rounding is moving the pixel more, the step will appear more jagged). This jaggedness is called "aliasing" as the true point is presenting through a non-true representation of the point (the alias).
A second approach is to alternatively darken both pixels proportionally, based on how close the point is. A point that is at (0.5) on the x axis would darken both pixels by 50% while a point that is at (0.25) would darken the 0 pixel by 75% and the 1 pixel by 25%. This is anti-aliasing, and may result in a line that is slightly more fuzzy, but appears to be straighter. This fuzziness can be somewhat combated by drawing a thicker line.
I hope this gives you some idea of the math behind many of the higher quality drawing routines, and certainly there are approaches that are even more sophisticated than the one I just presented.
Based on the Bresenham Algorithm, here is a java implementation that
assumes x2 > x1 and y2 > y1 and uses integer arithmetic
import java.applet.Applet;
import java.awt.*;
public class Slope extends Applet{
private int x1 = 20, y1 = 10;
private int x2 = 300, y2 = 700;
#Override
public void paint(Graphics g) {
drawLine(x1, y1, x2, y2, g);
//g.drawLine(x1, y1, x2, y2, g);
}
private void drawPoint(int x, int y, Graphics g) {
g.drawLine(x,y,x,y);
}
#Override
public void init(){
this.setSize(500,700);
}
private void drawLine(int x1,int y1,int x2,int y2,Graphics g){
int dx = x2 - x1;
int dy = y2 - y1;
int xi = 1;
int D = 2*dx - dy;
int x = x1;
for(int y = y1; y <y2; y++) {
drawPoint(x,y,g);
if(D > 0) {
x = x + xi;
D = D - 2 * dy;
}
D = D + 2 * dx;
}
}
}
I wrote a gui where a user draws something in a (640x480) window. It makes that drawing into a set of points stored in a Vector array. Now, how do I translate those set of points to the origin (0,0 top left corner of the window) or put it at a specified pos? The width and height of the window I want it in is also 640x480.
After that is solved, how do you scale that new set of points to a size I want?
UPDATE 1
I solved the scale issue, but not the positioning issue. The drawing is not going where I tell it to be. Code below of what I have so far.
float scaleX = (float)width/boundingPoints.width;
float scaleY = (float)height/boundingPoints.height;
for(int i = 0; i < cg_points.size()-1; i++){
Point p1 = cg_points.get(i);
Point p2 = cg_points.get(i+1);
g.drawLine((int)(p1.x*scaleX) + pos.x, (int)(p1.y*scaleY) + pos.y, (int)(p2.x*scaleX) + pos.x, (int)(p2.y*scaleY) + pos.y);
}
I want the drawing to start at where pos [x, y] is. What is currently the problem is this. It does follow what pos.x and pos.y does, but it is way off and not starting at pos[x,y].
Here is a screen shot of the issue
As you can see from the picture, the box is where the star is supposed to be. The scaling is right as you can see, just not the pos. That is because the points in the drawing may NOT start at (0,0).
Any suggestions?
Thanks!
To translate a drawing, simply
foreach point in array
point.x += translate.x
point.y += translate.y
If you're going to center a drawing, pick a center (such as averaging all your points), negate that value, then translate all your points by that value.
To scale a drawing:
foreach point in array
point.x *= scale
point.y *= scale
So I solved it...YAY!!! Here is the code below in case you run into the same issue as I had.
float scaleX = (float)width/boundingPoints.width;
float scaleY = (float)height/boundingPoints.height;
int bx = boundingPoints.x;
int by = boundingPoints.y;
for(int i = 0; i < cg_points.size()-1; i++){
Point p1 = cg_points.get(i);
Point p2 = cg_points.get(i+1);
int x1 = (int) ((p1.x-bx)*scaleX);
x1 += pos.x;
int y1 = (int) ((p1.y-by)*scaleY);
y1 += pos.y;
int x2 = (int) ((p2.x-bx)*scaleX);
x2 += pos.x;
int y2 = (int) ((p2.y-by)*scaleY);
y2 += pos.y;
g.drawLine(x1, y1, x2, y2);
}
this is my first time posting on a forum. But I guess I will just jump in and ask.. I am trying to draw a rectangle with x, y, width, height, and angle. I do not want to create a graphics 2D object and use transforms. I'm thinking that's an inefficient way to go about it. I am trying to draw a square with rotation using a for loop to iterate to the squares width, drawing lines each iteration at the squares height. My understanding of trig is really lacking so... My current code draws a funky triangle. If there is another question like this with an answer sorry about the duplicate. If you have got any pointers on my coding I would love some corrections or pointers.
/Edit: Sorry about the lack of a question. I was needing to know how to use sine and cosine to draw a square or rectangle with a rotation centered at the top left of the square or rectangle. By using sin and cos with the angle to get the coordinates (x1,y1) then using the sin and cos functions with the angle plus 90 degrees to get the coordinates for (x2,y2). Using the counter variable to go from left to right drawing lines from top to bottom changing with the angle.
for (int s = 0; s < objWidth; s++){
int x1 = (int)(s*Math.cos(Math.toRadians(objAngle)));
int y1 = (int)(s*Math.sin(Math.toRadians(objAngle)));
int x2 = (int)((objWidth-s)*Math.cos(Math.toRadians(objAngle+90)));
int y2 = (int)((objHeight+s)*Math.sin(Math.toRadians(objAngle+90)));
b.setColor(new Color((int)gArray[s]));
b.drawLine(objX+x1, objY+y1, objX+x2, objY+y2);
}
It is called the Rotation matrix.
If your lines has the following coordinates before rotation:
line 1: (0, 0) - (0, height)
line 2: (1, 0) - (1, height)
...
line width: (width, 0) - (width, height)
Then applying the rotation matrix transform will help you:
for (int s = 0; s < objWidth; s++){
int x1 = cos(angle)*s
int y1 = sin(angle)*s
int x2 = s * cos(angle) - objHeight * sin(angle)
int y2 = s * sin(angle) + objHeight * cos(angle)
//the rest of code
}
Hope I didn't make a mistakes.
Do you mean like a "rhombus"? http://en.wikipedia.org/wiki/Rhombus (only standing, so to speak)
If so, you can just draw four lines, the horizontal ones differing in x by an amount of xdiff = height*tan(objAngle).
So that your rhombus will be made up by lines with points as
p1 = (objX,objY) (lower left corner)
p2 = (objX+xdiff,objY+height) (upper left corner)
p3 = (objX+xdiff+width,objY+height) (upper right corner)
p4 = (objX+xdiff+width,objY) (lower right corner)
and you will draw lines from p1 to p2 to p3 to p4 and back again to p1.
Or did you have some other shape in mind?
I've created a graph using java and swing but it is only good for showing positive numbers and some negative numbers less than -14 the idea that I've used was :
create a box
add my X and Y axis label
get an array of numbers
get max number for indicating the max number in the array
create an scale using following code :
double scale = (double)(height - 2*borderSpace)/getMax();
and then plot my line graph , above solutions is perfect for positive values for negative values I did a trick
int height2 = getHeight() - getHeight()/2;
double scale = (double)(height2 - 2*borderSpace)/getMax();
which is only works till -14 not less than that.
for drawing lines I use this code
//borderspace = 20
double xInc = (double)(width - 2*borderSpace)/(data.length-1);
double scale = (double)(height - 2*borderSpace)/getMax();
g2.setPaint(Color.green.darker());
for(int i = 0 ; i < data.length-1; i++) {
double x1 = borderSpace + i*xInc;
double y1 = height - borderSpace - scale*data[i];
double x2 = borderSpace + (i+1)*xInc;
double y2 = height - borderSpace - scale*data[i+1];
g2.draw(new Line2D.Double(x1, y1, x2, y2));
}
I want to have the box but Y axis should be on the left side( I don't want to change the Y axis place) and I just want to change the place X axis in case of having negative numbers
for making it more clarify you can have a look at this picture :
You might want to take a look at JFreeChart to create your graphs rather than trying to brew your own solution from scratch.
I have two points of circle and center of this circle. I want to draw an arc between these points. Method drawArc is to simple and doesn't fit my purpose.
Anybody help?
You can use Canvas.drawArc, but you must compute the arguments it needs:
Lets say that the center of the circle is (x0, y0) and that the arc contains your two points (x1, y1) and (x2, y2). Then the radius is: r=sqrt((x1-x0)(x1-x0) + (y1-y0)(y1-y0)). So:
int r = (int)Math.sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0));
int x = x0-r;
int y = y0-r;
int width = 2*r;
int height = 2*r;
int startAngle = (int) (180/Math.PI*atan2(y1-y0, x1-x0));
int endAngle = (int) (180/Math.PI*atan2(y2-y0, x2-x0));
canvas.drawArc(x, y, width, height, startAngle, endAngle);
Good luck!
Graphics.drawArc expects the following parameters:
x
y
width
height
startAngle
arcAngle
Given your arc start and end points it is possible to compute a bounding box where the arc will be drawn. This gives you enough information to provide parameters: x, y, width and height.
You haven't specified the desired angle so I guess you could choose one arbitrarily.