• help with making a nice arrow
    6 replies, posted
Hi! I had to draw a lot of graphs lately for class and I got tired of manually creating arrows in MS Paint because it doesnt have a pre-made arrow pointing in custom direction (yea, I know its stupid to make this in ms paint in the first place but oh well). So I wanted to make a little program that draws me an arrow exactly how I want it. But later I discovered that the head of arrow is distorted at some angles and that really annoyed me. If I made it thicker it was somewhat better. [IMG]http://shrani.si/f/P/ys/3xiDLuhL/untitlded.png[/IMG] Now I might be doing this in totally wrong way, but I would like to request some suggestions on how to make the arrow look nice on all angles. Thanks. [CODE] static int oldX; static int oldY; static int currentX; static int currentY; public void paintComponent(Graphics g){ super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; if(aaToggle.isSelected()){ g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); } int thickness = thckSlider.getValue(); double angle = returnAngle(); int arrowSize = arrowSlider.getValue(); g2.setStroke(new BasicStroke(thickness)); g2.drawLine(oldX, oldY, currentX, currentY); g2.fillPolygon(returnArrowShape(angle, arrowSize)); } private static double returnAngle(){ //calculates angle with trigonometric functions double hypotenuse = Math.sqrt((currentX - oldX)*(currentX - oldX)+(currentY - oldY)*(currentY - oldY)); int zeroX = (int)(oldX + Math.round(hypotenuse)); int zeroY = oldY; int middleX = (currentX + zeroX)/2; int middleY = (currentY + zeroY)/2; double adjacent = Math.sqrt((middleX - oldX)*(middleX - oldX)+(middleY - oldY)*(middleY - oldY)); double rotRad= 2*(Math.acos(adjacent/hypotenuse)); double rotDeg = rotRad * 180/Math.PI; if(currentY > zeroY){ rotDeg=360-rotDeg; } return rotDeg; } private static Polygon returnArrowShape(double angle, int arrowSize){ //creates arrow shape based on angle Polygon arrowPolygon = new Polygon(); int tempX = (int)(currentX + arrowSize * Math.cos(angle*Math.PI/180)); int tempY = (int)(currentY - arrowSize * Math.sin(angle*Math.PI/180)); arrowPolygon.addPoint(tempX, tempY); tempX = (int)(currentX + arrowSize * Math.cos((angle+135)%360*Math.PI/180)); tempY = (int)(currentY - arrowSize * Math.sin((angle+135)%360*Math.PI/180)); arrowPolygon.addPoint(tempX, tempY); arrowPolygon.addPoint(currentX, currentY); tempX = (int)(currentX + arrowSize * Math.cos((angle+225)%360*Math.PI/180)); tempY = (int)(currentY - arrowSize * Math.sin((angle+225)%360*Math.PI/180)); arrowPolygon.addPoint(tempX, tempY); return arrowPolygon; } private void formMousePressed(java.awt.event.MouseEvent evt) { oldX = evt.getX(); oldY = evt.getY(); } private void formMouseReleased(java.awt.event.MouseEvent evt) { currentX = evt.getX(); currentY = evt.getY(); repaint(); } [/CODE] [IMG]http://shrani.si/f/3n/cB/4hKHMBVv/untistled.png[/IMG]
I doubt there's something wrong with your drawing code (but I didn't look at it) It looks more like an issue with aliasing. You have the anti-aliasing option there but I don't know if it works or not. In any case, when you want to draw a diagonal line, 1 pixel thick, you don't just draw black pixels, you need to draw grey ones between them. Anyways, that's a very oversimplified explanation of AA. You should google how to implement it if the library you use for drawing doesn't already have that option.
I know what you mean but Im afraid thats not the case: [IMG]http://shrani.si/f/2m/Xw/1IPW79lM/untitled.png[/IMG] My guess would be that since there are very little pixels available to draw an arrow the calculation fails. Even if its possible to draw an arrow the calculation doesn't make compromises and thinks if the specified shape cannot be drawn exactly like its supposed to be then it cant be drawn at all and the result is missing half of the arrow. The red part is how I would make a compromise, but computers dont make compromises :\
[QUOTE=Jinx786;34787633]I know what you mean but Im afraid thats not the case: //image My guess would be that since there are very little pixels available to draw an arrow the calculation fails. Even if its possible to draw an arrow the calculation doesn't make compromises and thinks if the specified shape cannot be drawn exactly like its supposed to be then it cant be drawn at all and the result is missing half of the arrow. The red part is how I would make a compromise, but computers dont make compromises :\[/QUOTE] Use vectors, mang. Vectors make stuff like this easy. Okay, take a look at this. We have: Vec2 a = arrow start Vec2 b = arrow end float l = arrow thingy length float ang = arrow thingy angle We want: c = clockwise arrow thingy d = counter-clockwise arrow thingy [code] float dx = b.x - a.x; float dy = b.y - a.y; float lineAng = (float)Math.atan2(dy, dx); float cAng = lineAng - ang; float dAng = lineAng + ang; c = new Vec2(); c.x = b.x + l*(float)Math.cos(cAng); c.y = b.y + l*(float)Math.sin(cAng); d = new Vec2(); d.x = b.x + l*(float)Math.cos(dAng); d.y = b.y + l*(float)Math.sin(dAng); [/code] Then you draw lines like so: [code] whatever.drawLine(A, B); whatever.drawLine(B, C); whatever.drawLine(B, D); [/code] EDIT: Okay, to be honest, I didn't read your code at all. Vectors won't help you because you're already kind of using them. Having read it, my best advice would be: DON'T USE INTEGERS. (Somehow)
[QUOTE=Smashmaster;34789004] Having read it, my best advice would be: DON'T USE INTEGERS. (Somehow)[/QUOTE] But a coordinate has to be represented with integers? I mean, you cant say "two and three quarters of a pixel"? Or do you mean that temporary coordinates inside the calculation must not be integers?
[QUOTE=Jinx786;34789161]But a coordinate has to be represented with integers? I mean, you cant say "two and three quarters of a pixel"?[/QUOTE] You absolutely can. Keep everything as floats or doubles until the very end, then simply round them. [QUOTE=Jinx786;34789161]Or do you mean that temporary coordinates inside the calculation must not be integers?[/QUOTE] Yes. EDIT: Also, [code]a = (int)b;[/code] is not as good as [code]a = Math.round(b);[/code] for things like this.
[QUOTE=Smashmaster;34789186]You absolutely can. Keep everything as floats or doubles until the very end, then simply round them.[/QUOTE] Oh yes, that thought has just crossed my mind. Thanks! Edit: Yes, just tested it, it still isn't puuurfect but its much better than before.
Sorry, you need to Log In to post a reply to this thread.