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

二次信任 提交于 2019-11-30 09:47:15

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

Ok, the Catmull-Rom spline suggestion is a good one but not exactly what I am looking for. The example from the link provided was a good starting point, but a bit inflexible. I have taken it and modified my original source code to use it. I am posting this as an answer because I think it is more modular and easier to understand than Zevan's blog post (no offense Zevan!). The following code will display the following image:

Here is the code:

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

        [SWF(width="300",height="300")]
        public class TestCurves extends Sprite {
            public function TestCurves() {
                stage.scaleMode = "noScale";

                //draw a helpful grid
                graphics.lineStyle(1, 0xC0C0C0, 0.5);
                for (var x:int = 0; x <= 300; x += 10) {
                    graphics.moveTo(x, 0);
                    graphics.lineTo(x, 300);
                    graphics.moveTo(0, x);
                    graphics.lineTo(300, x);
                }

                var points:Array = [
                    new Point(40, 20), 
                    new Point(120, 80), 
                    new Point(120, 160), 
                    new Point(60, 160), 
                    new Point(60, 200), 
                    new Point(240, 150), 
                    new Point(230, 220), 
                    new Point(230, 280)
                ];

                SplineMethod.setResolution(5);

                graphics.lineStyle(2, 0xF00000);
                graphics.moveTo(points[0].x, points[0].y);

                var n:int = points.length;
                var i:int;

                for (i = 0; i < n - 1; i++) {
                    SplineMethod.drawSpline(
                        graphics, 
                        points[i], //segment start
                        points[i + 1], //segment end
                        points[i - 1], //previous point (may be null)
                        points[i + 2] //next point (may be null)
                    );
                }

                //straight lines and vertices for comparison
                graphics.lineStyle(2, 0x808080, 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 {

        //default setting will just draw a straight line
        private static var hermiteValues:Array = [0, 0, 1, 0];

        public static function setResolution(value:int):void {
            var resolution:Number = 1 / value;
            hermiteValues = [];
            for (var t:Number = resolution; t <= 1; t += resolution) {
                var h00:Number = (1 + 2 * t) * (1 - t) * (1 - t);
                var h10:Number = t  * (1 - t) * (1 - t);
                var h01:Number = t * t * (3 - 2 * t);
                var h11:Number = t * t * (t - 1);
                hermiteValues.push(h00, h10, h01, h11);
            }
        }

        public static function drawSpline(target:Graphics, segmentStart:Point, segmentEnd:Point, prevSegmentEnd:Point=null, nextSegmentStart:Point=null):void {
            if (!prevSegmentEnd) {
                prevSegmentEnd = segmentStart;
            }
            if (!nextSegmentStart) {
                nextSegmentStart = segmentEnd;
            }

            var m1:Point = new Point((segmentEnd.x - prevSegmentEnd.x) / 2, (segmentEnd.y - prevSegmentEnd.y) / 2);
            var m2:Point = new Point((nextSegmentStart.x - segmentStart.x) / 2, (nextSegmentStart.y - segmentStart.y) / 2);

            var n:int = hermiteValues.length;
            for (var i:int = 0; i < n; i += 4) {
                var h00:Number = hermiteValues[i];
                var h10:Number = hermiteValues[i + 1];
                var h01:Number = hermiteValues[i + 2];
                var h11:Number = hermiteValues[i + 3];

                var px:Number = h00 * segmentStart.x + h10 * m1.x + h01 * segmentEnd.x + h11 * m2.x;
                var py:Number = h00 * segmentStart.y + h10 * m1.y + h01 * segmentEnd.y + h11 * m2.y;

                target.lineTo(px, py);
            }
        }
    }

This is not a perfect solution. But unfortunately, I cannot piece together how to accomplish what I want using curveTo(). Note that GraphicsUtilities.drawPolyLine() does accomplish what I am attempting to do--the problem there is that it is inflexible and I cannot parse the code (more importantly, it doesn't appear to properly draw acute angles--correct me if I am wrong). If anyone can provide any insight, please post. For now, the above is my answer.

I code this, I think it may help:

SWF: http://dl.dropbox.com/u/2283327/stackoverflow/SplineTest.swf

Code: http://dl.dropbox.com/u/2283327/stackoverflow/SplineTest.as

I left a lot of comments on the code. I wish it helps!

Here is the theory behind the code:

A and C are the first and last point, B is the "control point" in AS3 you can draw the curve like this:

graphics.moveTo(A.x, A.y);
graphics.curveTo(B.x, B.y, C.x, C.y);

Now, D is the mid-point of the vector AC. And the mid-point of DB is the mid-point of the curve. Now what I did in the code was to move B exactly to D+DB*2 so, if you draw the curve using that point as control point, the mid-point of the curve will be B.

PS: Sorry for my poor Enlgish

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!