title: How to draw smooth continuous line in java swing that also varies in Size?
body: <p>I am writing a 2D traditional animation program in Java using swing and JPen.  The application works well.  However, i am dissatisfied with the results I am getting when I draw lines.  </p>
  
  <p><a href="https://i.stack.imgur.com/tinXn.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/tinXn.png" alt="when drawing, the line seems to &quot;rotating&quot; as you move from one point to another.  It then produces the annoying effect you see where the line appears non continuous.  Note.  "></a></p>
  
  <p>Using the JPen API, the swing panel listens for the stylus input and its code is:</p>
  
  <pre><code>    /**
   * This method is called whenever the stylus makes contact with the tablet while inside of the JPanel functioning
   * as the Drawing Canvas.
   * @param evt
   */
  public void penLevelEvent(PLevelEvent evt) {
      // Get kind of event: does it come from mouse (CURSOR), STYLUS or ERASER?
      PKind kind = evt.pen.getKind(); 
      // Discard events from mouse
      if (kind == PKind.valueOf(PKind.Type.CURSOR)){
          //System.out.println("returning since this is only a mouse cursor");
         return;
  
      }
  
  
      // Get the current cursor location
      // position value is in with respect to entire application window
      // Get the tilt values (not with a Bamboo... so untested!)
      float curX      = evt.pen.getLevelValue(PLevel.Type.X);
      float curY      = evt.pen.getLevelValue(PLevel.Type.Y);
      float pressure  = evt.pen.getLevelValue(PLevel.Type.PRESSURE);// 0.0 - 1.0
      float xTilt     = evt.pen.getLevelValue(PLevel.Type.TILT_X);
      float yTilt     = evt.pen.getLevelValue(PLevel.Type.TILT_Y);
  
      // Set the brush's size, and darkness relative to the pressure
      float darkness = 255 * pressure;
  
  
      // Transform them to azimuthX and altitude, two angles with the projection of the pen against the X-Y plane
      // azimuthX is the angle (clockwise direction) between this projection and the X axis. Range: -pi/2 to 3*pi/2.
      // altitude is the angle between this projection and the pen itself. Range: 0 to pi/2.
      // Might be more pratical to use than raw x/y tilt values.
      double[] aa = { 0.0, 0.0 };
      PLevel.Type.evalAzimuthXAndAltitude(aa, xTilt, yTilt);
      // or just PLevel.Type.evalAzimuthXAndAltitude(aa, evt.pen);
      double azimuthX = aa[0];
      double altitude = aa[1];
  
      //-------------------------------------------------------------------------------------
      // If the stylus is being pressed down, we want to draw a black
      // line onto the screen. If it's the eraser, we want to create
      // a white line, effectively "erasing" the black line
      //-------------------------------------------------------------------------------------
      if (kind == PKind.valueOf(PKind.Type.STYLUS)) {
          //System.out.println("Darkness "+darkness);
          int alpha = 255 - (int)darkness;
          color = new Color(0,0,255,  255 - alpha);
      }
      else if (kind == PKind.valueOf(PKind.Type.ERASER)) {
          System.out.println("Handle eraser");
      }
      else {
          return; // IGNORE or CUSTOM...
      }
  
      //If movement of the stylus is occuring
      if (evt.isMovement()) {
          //and the buttonIsDown(boolean)
          if(buttonIsDown) {
              //drawingCanvas:JPanel -&gt; instruct the jpanel to draw at the following coordinate using the specified pressure
              drawingCanvas.stylusMovementInput( prevXPos,prevYPos, curX,curY, pressure);
          }
          prevXPos = curX;
          prevYPos = curY;
      }
  
      prevXPos = curX;
      prevYPos = curY;
  
  }
  </code></pre>
  
  <p>So after the above method is invoked, the jpanel(drawingCanvas) starts to draw on a BufferedImage by obtaining the image's graphics2D.  Here is the code stylusMovementInput->calls -> performDrawOnBufferImageGraphic2D :</p>
  
  <pre><code>/**
   * Draw on the active frame that is selected.  Then call channel refresh, to refresh the the composite image derived
   * from call changes related to the current frame
   * @param cX current
   * @param cY current
   * @param oX previous
   * @param oY previous
   * @param pressure pressure 0 - 1f
   */
  private void performDrawOnBufferImageGraphic2D(float oX, float oY, float cX, float cY, float pressure){
      //Obtain the current layer that user wants to draw one
      //MyImageData is encapsulating a BufferedImage
      MyImageData $activeData = getActiveLayer();
      //Exit if one is not valid
      if( $activeData == null) return;
      //if valid layer, get the, get the bufferedImage.getGraphics
      Graphics2D $activeDataGFX = $activeData.getImageGraphics();
  
  
      // Customize the drawing brush (create a BasicStroke)
      Stroke thickness = Sys.makeStroke(getPencilSize(pressure), null);
      // Determine the tranparency with respect to the pressure
      int alpha = (int)(255 * pressure * getPencilOpacityPercentage());
      // Get the current color found in the color wheel
      Color cwVal = Sys.getColorFromColorWheel();
  
      Color drawingColor ;
      if(cwVal != null){
          // add alpha value to it
          drawingColor = new Color(cwVal.getRed(), cwVal.getGreen(), cwVal.getBlue(), alpha);
  
      }else throw new RuntimeException("ColorWheel is null drawing stylus draw");
  
      //set the brush and drawingColor
      $activeDataGFX.setStroke(thickness);
  
      // Save reference to the current bufferedImage graphic component
      Composite originalComposite =$activeDataGFX.getComposite();
      if(getCurrentTool() == DrawingCanvasTool.ERASER){
          //If eraser,  set new composite information, to allow erasing to transparency
          $activeDataGFX.setPaint( new Color(255,255,255, 0));
          $activeDataGFX.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, 0.0f));
      }else {
          //set the drawing color
          $activeDataGFX.setPaint(drawingColor);
      }
  
  
      //---------------------------------------------------------------
      // Rotate, Translate, Zoom the image according to the panning, zoom, and rotate
      // set by the user
      //---------------------------------------------------------------
      //Figure out the canvas center, as it is used for rotating
      float theta  = (float)Math.toRadians(canvasRotation);
      Dimension drawingAreaComponentSize = getSize();
      float centerX = drawingAreaComponentSize.width/2;
      float centerY = drawingAreaComponentSize.height/2;
      AffineTransform transform = new AffineTransform();
      transform.rotate(-theta, centerX, centerY);
      transform.scale(1.0f / canvasZoom, 1.0f / canvasZoom);//erase
      transform.translate(-canvasPan.x, -canvasPan.y);//erase
      $activeDataGFX.setTransform(transform);
  
  
      //Now Draw inside of the active data graphics object  
      Shape line = new Line2D.Float(cX,cY, oX, oY);
      Path2D.Float t = new Path2D.Float(line);
      $activeDataGFX.draw(t);
  
      //drawing is complete
      if(getCurrentTool() ==DrawingCanvasTool.ERASER){
          //Restore the old composite object
          $activeDataGFX.setComposite(originalComposite);
      }
  
      //Refresh basically merges frames along a frame column into a single preview buffered image
      //which will later be used to view the tool animation when user clicks "play" button
      channelPannel.refreshFrameOut(  channelPannel.getCurrentFrame()  );
  }
  </code></pre>
  
  <p>I commented a lot of the code, and provided the critical points related to the question.  Any help is much appreciated.  And again, the problem is how do i draw a smooth line worthy of a descent drawing program.</p>