Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to draw a continuous curved line from 3 given points at a time

I am trying to draw a continuous curved line in flash. There are many methods but none of the ones I have found so far quite fit my requirements. First of all, I want to use the flash graphic api's curveTo() method. I DO NOT want to simulate a curve with hundreds of calls to lineTo() per curved line segment. It is my experience and understanding that line segments are processor heavy. Flash's quadratic bezier curve should take less CPU power. Please challenge this assumption if you think I am wrong.

I also do not want to use a pre-made method that takes the entire line as an argument (eg mx.charts.chartClasses.GraphicsUtilities.drawPolyline()). The reason is that I will need to modify the logic eventually to add decorations to the line I am drawing, so I need something I understand at its lowest level.

I have currently created a method that will draw a curve given 3 points, using the mid-point method found here.

Here is a picture:

A continuous curved line using the actual points as control points

The problem is that the lines do not actually curve through the "real" points of the line (the gray circles). Is there a way using the power of math that I can adjust the control point so that the curve will actually pass through the "real" point? Given only the current point and its prev/next point as arguments? The code to duplicate the above picture follows. It would be great if I could modify it to meet this requirement (note the exception for first and last point).

package {
  import flash.display.Shape;
  import flash.display.Sprite;
  import flash.display.Stage;
  import flash.geom.Point;

  [SWF(width="200",height="200")]
  public class TestCurves extends Sprite {
    public function TestCurves() {
      stage.scaleMode = "noScale";
      var points:Array = [
        new Point(10, 10), 
        new Point(80, 80), 
        new Point(80, 160), 
        new Point(20, 160), 
        new Point(20, 200),
        new Point(200, 100)
      ];

      graphics.lineStyle(2, 0xFF0000);

      var point:Point = points[0];
      var nextPoint:Point = points[1];

      SplineMethod.drawSpline(graphics, point, null, nextPoint);

      var prevPoint:Point = point;

      var n:int = points.length;
      var i:int;
      for (i = 2; i < n + 1; i++) {
        point = nextPoint;
        nextPoint = points[i]; //will eval to null when i == n

        SplineMethod.drawSpline(graphics, point, prevPoint, nextPoint);

        prevPoint = point;
      }

      //straight lines and vertices for comparison
      graphics.lineStyle(2, 0xC0C0C0, 0.5);
      graphics.drawCircle(points[0].x, points[0].y, 4);
      for (i = 1; i < n; i++) {
        graphics.moveTo(points[i - 1].x, points[i - 1].y);
        graphics.lineTo(points[i].x, points[i].y);
        graphics.drawCircle(points[i].x, points[i].y, 4);
      }

    }
  }
}
import flash.display.Graphics;
import flash.geom.Point;

internal class SplineMethod {
  public static function drawSpline(target:Graphics, p:Point, prev:Point=null, next:Point=null):void {
    if (!prev && !next) {
      return; //cannot draw a 1-dimensional line, ie a line requires at least two points
    }

    var mPrev:Point; //mid-point of the previous point and the target point
    var mNext:Point; //mid-point of the next point and the target point

    if (prev) {
      mPrev = new Point((p.x + prev.x) / 2, (p.y + prev.y) / 2);
    }
    if (next) {
      mNext = new Point((p.x + next.x) / 2, (p.y + next.y) / 2);
      if (!prev) {
        //This is the first line point, only draw to the next point's mid-point
        target.moveTo(p.x, p.y);
        target.lineTo(mNext.x, mNext.y);
        return;
      }
    } else {
      //This is the last line point, finish drawing from the previous mid-point
      target.moveTo(mPrev.x, mPrev.y);
      target.lineTo(p.x, p.y);
      return;
    }
    //draw from mid-point to mid-point with the target point being the control point.
    //Note, the line will unfortunately not pass through the actual vertex... I want to solve this
    target.moveTo(mPrev.x, mPrev.y);
    target.curveTo(p.x, p.y, mNext.x, mNext.y);
  }
}

Later I will be adding arrows and things to the draw method.

like image 436
jpwrunyan Avatar asked May 10 '11 06:05

jpwrunyan


People also ask

What tool do you use to draw a curved line?

If you plan to draw a series of connected curved segments, the Pen tool is frequently the best option. The Pen tool, as discussed briefly in Recipe 1.1, enables you to draw line segments by plotting points.

Where is the tool for drawing a curved line located?

To draw curving lines: Press p or select the Pen tool from the toolbar. (If you've used one of the shape tools, you may need to click and hold the shape tool icon to select the Pen tool from a pop-up menu.) Click the stage where you want the curve to begin and hold down the mouse button.


1 Answers

I think you're looking for a Catmull-Rom spline. I've googled an AS3 implementation for you but haven't tried it so use at your own discretion:

http://actionsnippet.com/?p=1031

like image 52
frankhermes Avatar answered Sep 30 '22 16:09

frankhermes