7

フラッシュで連続曲線を描こうとしています。多くの方法がありますが、これまでのところ、私の要件に完全に適合するものはありませんでした。まず、Flash グラフィック API の curveTo() メソッドを使いたいと思います。 曲線セグメントごとに lineTo() を何百回も呼び出す曲線をシミュレートしたくありません。 ライン セグメントはプロセッサ負荷が高いというのが私の経験と理解です。Flash の 2 次ベジエ カーブは、CPU パワーをあまり消費しないはずです。私が間違っていると思われる場合は、この仮定に異議を唱えてください。

また、行全体を引数として取る既製のメソッド (mx.charts.chartClasses.GraphicsUtilities.drawPolyline() など) も使用したくありません。その理由は、描いている線に装飾を加えるために最終的にロジックを変更する必要があるためです。そのため、最低レベルの理解が必要です。

私は現在、ここにある中間点法を使用して、 3 点を指定して曲線を描く方法を作成しました。

ここに写真があります:

実際の点を制御点とする連続曲線

問題は、線が実際には線の「実際の」点 (灰色の円) を通って曲がっていないことです。曲線が実際に「実際の」点を通過するように制御点を調整できる数学の力を使用する方法はありますか? 現在のポイントとその前/次のポイントのみを引数として指定しますか? 上の図を複製するコードは次のとおりです。この要件を満たすように変更できれば素晴らしいと思います (最初と最後のポイントの例外に注意してください)。

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);
  }
}

後で draw メソッドに矢印などを追加します。

4

3 に答える 3

4

Catmull-Romスプラインを探していると思います。私はあなたのためにAS3の実装をグーグルで検索しましたが、試したことがないので、あなた自身の裁量で使用してください:

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

于 2011-05-10T11:34:00.347 に答える
4

わかりました、Catmull-Rom スプラインの提案は良いものですが、まさに私が探しているものではありません。提供されたリンクの例は良い出発点でしたが、少し柔軟性がありませんでした。私はそれを取得し、それを使用するために元のソース コードを変更しました。Zevanのブログ投稿よりもモジュール化されており、理解しやすいと思うので、これを回答として投稿しています(Zevanに違反はありません!)。次のコードは、次の画像を表示します。

ライン セグメントごとに 5 つのサブセグメントを持つスプライン イメージ

コードは次のとおりです。

    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);
            }
        }
    }

これは完全な解決策ではありません。しかし残念ながら、curveTo() を使用して目的を達成する方法をまとめることができません。GraphicsUtilities.drawPolyLine() 、私がやろうとしていることを達成することに注意してください。問題は、柔軟性がなく、コードを解析できないことです (さらに重要なことに、鋭角を適切に描画していないようです。私は間違っています)。誰かが洞察を提供できる場合は、投稿してください。今のところ、上記が私の答えです。

于 2011-05-11T08:03:43.010 に答える
1

私はこれをコーディングしています。

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

コード: http://dl.dropbox.com/u/2283327/stackoverflow/SplineTest.as

コードにはたくさんのコメントを残しました。役に立てば幸いです!

コードの背後にある理論は次のとおりです。

ここに画像の説明を入力

A と C は最初と最後のポイントで、B は AS3 の「コントロール ポイント」で、次のように曲線を描くことができます。

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

ここで、D はベクトル AC の中点です。DB の中点は曲線の中点です。コードで行ったことは、B を D+DB*2 に正確に移動することでした。そのため、その点を制御点として使用して曲線を描くと、曲線の中点は B になります。

PS: 下手な英語でごめんなさい

于 2011-05-11T14:11:48.250 に答える