im trying to draw an arc - just a simple looking arc from point (x1,y1) to point (x2,y2)
how do i do that?
i been using the so complex and not freindly to user method called drawArc on Graphics class. no luck yet tho.
thats what i tried:
void drawArc(Graphics2D g, int x1, int y1, int x2, int y2) {
AffineTransform prev = g.getTransform();
double dx = x2 - x1, dy = y2 - y1;
double angle = Math.atan2(dy, dx);
int len = (int) Math.sqrt(dx*dx + dy*dy);
AffineTransform at = AffineTransform.getTranslateInstance(x1, y1);
at.rotate(angle);
g.transform(at);
g.drawArc(len/2, len/2, len ,len/2, 0, 60);
g.setTransform(prev);
}
thanks ahead.
graphics.drawLine(x1,y1,x2,y2) is the simplest possible arc that you can draw with these information.
Probably it is not what you want. If you want something more ... curvy you need to define somehow how curvy it is, in what direction. The drawArc method requires you to calculate an ellipse that touches both points. The arc is the segment of the circle between those points. There is an infinite number of possible ellipses. (The drawLine example assumes an infinite ellipse.) But this requires more information (what ellipse to chose) and some calculation.
If you want to draw curves between two points and control points (what you probably want) you need to look into QuadCurve2D or CubicCurve2D and drawShape. You can find sample code here.
Related
I like to have maximum control over the screen, so I have to control every pixel, and that has some pros and cons. one con is that I don't really have the help from any built-in functions. so I have no idea how to draw a line.
I've tried to make a function to handle line drawing but I just can't get it to work!
here's the code I used to draw the line
int startX;
int startY;
int deltaX = x1/x2;
int deltaY = y1/y2;
float deltaPixl = deltaX/deltaY;
for(int i=0;i<deltaY;i=i+1){
if(x1>x2){ startX = x2;}else{ startX=x1;}
if(y1>y2){ startY = y2;}else{ startY=y1;}
pixl(startX+i,round(startY+(deltaPixl*i)),0);
}
it uses a function called pixl so that it easily draw a pixel to the pixel array,
just to clarify why there's a function called pixl in the code.
and when I try to use this code, it doesn't crash, like processing usually does when it has an error!
it just doesn't work, instead just doing nothing!
I'd like some help on this subject, please.
You could get away with simply using PGraphics.
The idea is once you have a PGraphics instance you use dot notation to access the drawing functions used to (as long as they're called between .beginDraw() and .endDraw()).
Using noSmooth() you can get it looking pixel perfect.
Here's a basic sketch to illustrate the idea:
// disable anti-aliasing
noSmooth();
// create a PGraphics layer
PGraphics layer = createGraphics(25, 25);
// render a line
layer.beginDraw();
layer.line(0, 24, 24, 0);
layer.endDraw();
// render the line at 100%
image(layer, 0, 0);
// render the line scaled up
image(layer, 0, 0, width, height);
This should do for most cases. (It's only trickier cases with very small values and transparency that might give you headaches)
If for some reason you need a lot more control, you can you always implement your own method of rasterising. Once place you can probably start with is Bresenham's line algorithm
Regarding your code there are a few things that could go wrong:
float deltaPixl = deltaX/deltaY;: if deltaY is zero you'll run into an exception
you're doing integer division for deltaX and deltaY (potentially making it likely to get 0 for either of the values)
you should try a println() statement before the for loop with the start/end values to get a feel if that loop will actually execute or not. Additionally, within the for loop you can println(i) to see if you get the value you expect.
Overall I recommend checking Kevin Workman's How to Debug guide.
Additionally you could use lerp() to calculate linearly interpolated position between the line's start and end points. Pass each coordinate and a normalized (between 0.0, 1.0) value, where 0.0 = at the start point, 1.0 = at the end point and anything in between is on the line (e.g. 0.5 = 50% along the line).
Here's a basic example:
void drawLinePoints(int x1, int y1, int x2, int y2, int numberOfPoints){
// for each point
for(int i = 0; i < numberOfPoints; i++){
// map the counter to a normalized (0.0 to 1.0) value for lerp
// 0.0 = 0 % along the line, 0.5 = 50% along the line, 1.0 = 100% along the line
float t = map(i, 0, numberOfPoints, 0.0, 1.0);
// linearly interpolate between the start / end points (and snap to whole pixels (casting to integer type))
int x = (int)lerp(x1, x2, t);
int y = (int)lerp(y1, y2, t);
// render the point
point(x, y);
}
}
void setup(){
// render points are large squares
strokeWeight(6);
strokeCap(PROJECT);
}
void draw(){
// clear frame
background(255);
// calculate distance
float distance = dist(10, 10, mouseX, mouseY);
// map distance the number of points to illustrate interpolation (more points = continuous line)
int numPoints = (int)distance / 8;
// render points along the line
drawLinePoints(10, 10, mouseX, mouseY, numPoints);
}
For the sake of completeness here's the above snippet using the pixels[] instead:
void drawLinePoints(int x1, int y1, int x2, int y2, int numberOfPoints){
// for each point
for(int i = 0; i < numberOfPoints; i++){
// map the counter to a normalized (0.0 to 1.0) value for lerp
// 0.0 = 0 % along the line, 0.5 = 50% along the line, 1.0 = 100% along the line
float t = map(i, 0, numberOfPoints, 0.0, 1.0);
// linearly interpolate between the start / end points (and snap to whole pixels (casting to integer type))
int x = (int)lerp(x1, x2, t);
int y = (int)lerp(y1, y2, t);
// convert the x, y coordinate to pixels array index and render the point in black
pixels[x + (y * width)] = color(0);
}
}
void setup(){
noSmooth();
}
void draw(){
// clear frame
loadPixels();
java.util.Arrays.fill(pixels, color(255));
// calculate distance
float distance = dist(10, 10, mouseX, mouseY);
// map distance the number of points to illustrate interpolation (more points = continuous line)
int numPoints = (int)distance;
// render points along the line
drawLinePoints(10, 10, mouseX, mouseY, numPoints);
// update pixels
updatePixels();
}
I'm a bit late but I found a very simple method for line drawing to a pixel array on this website.
Here is a simple implementation I made in Monogame (btw sorry its not using processing - I have never used it):
public void drawLine(int x1, int y1, int x2, int y2)
{
//this will store the colour data of the canvas pixels
Color[] canvasData = new Color[canvas.Width * canvas.Height];
//store the pixel data of the canvas in canvasData
canvas.GetData<Color>(canvasData);
//drawing line starts here
int dx = x2 - x1;
int dy = y2 - y1;
for (int x = x1; x < x2; x++)
{
int y = y1 + dy * (x - x1) / dx;
//[y*canvas.Width+x] converts the 2d array index to a 1d array index
canvasData[y * canvas.Width + x] = Color.Black;
}
//line drawing ended
//setting the canvas' pixels to the modified pixels with the line
canvas.SetData<Color>(canvasData);
}
I tried to create and translate a polygon in openGL, I create a function for translation but this create a white line from x0 to v_size and I don't understand why ?
This is my function for polygon translation
public void translate1(GL2 gl, double x0, double x1, double y0, double y1){
double step = 0.2;
for(double i = 0; i < v_size; i += step){
gl.glBegin(GL2.GL_POLYGON);
gl.glVertex2d(x0 + i, y0);
gl.glVertex2d(x0 + i, y1);
gl.glVertex2d(x1 + i, y1);
gl.glVertex2d(x1 + i, y0);
gl.glEnd();
}
}
Initial x0 = 0, x1 = 10, y0 = 20, y1 = 30.
Thanks !
Have a nice day!
The reason for this is, that you draw squares every step units away from each other. Since nothing gets cleared in the meantime, the overlapping quads form a line.
It is rather unclear what you are trying to achieve. A translation would usually not draw multiple quads. If you are trying to do an animation, then you'll have to split the movement over multiple frames and draw exactly one square in every frame.
I would like to generate a bezier curve between two shapes (rectangles for the purposes of this question) such that the curve meets the shape at a 90 degree angle. Here's a MS Paint-generated example:
For my purposes, it can be assumed that the angle only needs to be perpendicular to an axis, not to something arbitrary. In other words, the angle of the curve at the endpoints only needs to be 0, 90, 180, or 270 degrees.
My understanding of bezier curves tells me that I need 2 endpoints and 2 control points. The endpoints are simple to calculate, but I have only a basic understanding of how to manipulate the control points.
Is there a convenient formula to accomplish this? I'm having surprising difficulty finding relevant tutorials.
I'm using Java and am currently drawing curves using java.awt.geom.Path2D.Double.curveTo(). If there's a pre-built Java class or method to accomplish this, that would be ideal, but I'm willing to implement an algorithm or equation myself if needed.
Based on Sage's excellent answer and after playing around with http://www.openprocessing.org/sketch/2123 for a while, here's what I ended up with.
Putting the control point collinear with the angle you want to create pulls the curve in that direction. The farther the control point is from the endpoint, the more pronounced the effect becomes. Since I only deal with 90 degree angles, this means I can just alter the x or y coordinates of the control points by a set distance (which I call delta like Sage did) to produce the desired effect. I didn't use anything fancy to find delta - I just tried several values until I found one I was happy with.
Here's a snippet of my final code.
public class BezierCurve {
private CubicCurve2D curve;
private static final int delta = 100;
private double x1, y1, x2, y2;
private double ctrlx1, ctrly1, ctrlx2, ctrly2;
public BezierCurve(Point p1, Side side1, Point p2, Side side2) {
this.x1 = p1.x;
this.y1 = p1.y;
this.x2 = p2.x;
this.y2 = p2.y;
Point ctrl1 = getControlPoint(p1, side1);
Point ctrl2 = getControlPoint(p2, side2);
ctrlx1 = ctrl1.x;
ctrly1 = ctrl1.y;
ctrlx2 = ctrl2.x;
ctrly2 = ctrl2.y;
curve = new CubicCurve2D.Double();
curve.setCurve(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2);
}
private Point getControlPoint(Point p, Side s) {
int x = p.x;
int y = p.y;
switch (s) {
case Left:
x -= delta;
break;
case Right:
x += delta;
break;
case Bottom:
y += delta;
break;
case Top:
y -= delta;
break;
}
return new Point(x, y);
}
public void draw(Graphics2D g2) {
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.draw(curve);
}
}
This handles each case of top, bottom, right, and left.
Here's a screenshot of the code in action.
intelligent use of the Class CubicCurve2D should be able achieve what you want: The CubicCurve2D class implements the Shape interface. This class represents a cubic parametric curve segment in (x, y) coordinate space. CubicCurve2D.Float and CubicCurve2D.Double subclasses specify a cubic curve in float and double precision.
This class's setCurve(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2); allows to set two control point. If we set the control point right before the curve start (ctrlx1, ctrly1) and right after the curve end (ctrlx2, ctrly2). To maintain the angle of the curve to be multiple of 90 degree,
We can calculate the control point similarly as follows (which is calculated for 90 deegree):
ctrlx1 = x1; // curve start x
ctrly1 = y2 - delta; // curve start y
ctrlx2 = x1 + delta; // curve end x
ctrly2 = y2; // curve end y
In the following example i have assumed delta = 10;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
CubicCurve2D c = new CubicCurve2D.Double();
int x1 = 150, y1 = 150; //p1
int x2 = 350, y2 = 300;//p3
int ctrlx1, ctrly1, ctrlx2, ctrly2;
int delta = 10;
ctrlx1 = x1; // curve start x
ctrly1 = y2 - delta; // curve start y
ctrlx2 = x1 + delta; // curve end x
ctrly2 = y2;
g2d.drawRect(x1-50, y1-100, 100, 100);
c.setCurve(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2);
g2d.drawRect(x2, y2-50, 100, 100);
g2d.draw(c);
}
What is a method in Java that draws a rectangle given the following:
The coordinates of the center of the square
The angle of the rectangle from vertical, in degrees
To draw a rectangle in the way you suggest you need to use the class AffineTransform. The class can be used to transform a shape in all manner of ways. To perform a rotation use:
int x = 200;
int y = 100;
int width = 50;
int height = 30;
double theta = Math.toRadians(45);
// create rect centred on the point we want to rotate it about
Rectangle2D rect = new Rectangle2D.Double(-width/2., -height/2., width, height);
AffineTransform transform = new AffineTransform();
transform.rotate(theta);
transform.translate(x, y);
// it's been while, you might have to perform the rotation and translate in the
// opposite order
Shape rotatedRect = transform.createTransformedShape(rect);
Graphics2D graphics = ...; // get it from whatever you're drawing to
graphics.draw(rotatedRect);
For the first point, you can just figure out the coordinates of the center of the square by using a distance formula, (int)Math.sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2)); them divide by 2. you can do this for the width and height. I don't know enough about Java draw to give you better answers based on what was in your question but I hope that helps.
For the second, you would need to just create a polygon right?
i'm stuck on following problem;
I have a rectangle (50x40 px lets say, position : x1,y1) and a cirle (radius 30, positin x2,y2). Now I want to draw an arrow between them
void drawArrow(Graphics g1, int x1, int y1, int x2, int y2,) {
//x1 and y1 are coordinates of circle or rectangle
//x2 and y2 are coordinates of circle or rectangle, to this point is directed the arrow
Graphics2D g = (Graphics2D) g1.create();
double dx=x2-x1;
double dy=y2-y1;
double angle = Math.atan2(dy, dx);
int len = (int) Math.sqrt(dx*dx + dy*dy);
AffineTransform at = AffineTransform.getTranslateInstance(x1, y1);
at.concatenate(AffineTransform.getRotateInstance(angle));
g.transform(at);
g.drawLine(0,0,len,0);
g.fillPolygon(new int[] {len, len-ARR_SIZE, len-ARR_SIZE, len},
new int[] {0, -ARR_SIZE, ARR_SIZE, 0}, 4);
}
This Code obviously connects only the specific points of rect and circle ( on this picture i connected the points in the middle http://imageshack.us/photo/my-images/341/arrk.jpg/ ). Do you have any idea how to achieve stg like this? (http://imageshack.us/photo/my-images/833/arr2u.jpg/ ) ... my idea was to shorten the length and calculate the new coordinates, but i'm bit struggling with it.
// I call this function this way:
drawArrow(g,temp.x+radius/2,temp.y+radius/2,temp2.x+width/2,temp2.y+height/2);
Easiest way is to set the clipping. If you add your circle and your rect to the clipping, it won't draw on it.
It doesn't solve the problem or drawing the arrow though. To solve this problem, you need to use Shape.getBounds(), figure out the bounds for the rectangle, then calculate the angle to your circle and use trigonometry to find the right spot on the rectangle