2D曲線をデータストレージのポイントに変換する


12

曲線、つまりパスを最小数のポイントに変換して、ファイルまたはデータベースに保存できるようにするアルゴリズムを作成しました。

この方法は簡単です。3つのポイントを等間隔で移動し、これらのポイントが形成するライン間の角度を測定します。角度が許容値よりも大きい場合、そのポイントまでの新しい3次曲線が作成されます。次に、ラインを前方に移動し、角度を再度測定します…

Androidの知っている人のためにパス注こと-クラスdstPathは一方で、私は後でポイントを保存することができますので、配列にポイントを記録したカスタムクラス、あるsrcPathは地域労働組合の結果であり、したがって、私にとって何のキーポイントを持っていません保存する。

問題は、ソースパスが完全な円と長方形で構成されている下のコードで生成されたこの画像でわかるように、円が滑らかに見えないことです。許容角度とステップの長さを変更しようとしましたが、何も役に立ちません。このアルゴリズムの改善、または別のアプローチを提案できるかどうか疑問に思います。

編集:Android Javaを使用している人のためにコード全体を投稿したので、簡単に試してみることができます。

ここに画像の説明を入力してください

public class CurveSavePointsActivity extends Activity{

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(new CurveView(this));
    }

    class CurveView extends View{

        Path srcPath, dstPath;
        Paint srcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        public CurveView(Context context) {
            super(context);

            srcPaint.setColor(Color.BLACK);
            srcPaint.setStyle(Style.STROKE);
            srcPaint.setStrokeWidth(2);
            srcPaint.setTextSize(20);

            dstPaint.setColor(Color.BLUE);
            dstPaint.setStyle(Style.STROKE);
            dstPaint.setStrokeWidth(2);
            dstPaint.setTextSize(20);

            srcPath = new Path();
            dstPath = new Path();

        }

        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);

            //make a circle path
            srcPath.addCircle(w/4, h/2, w/6 - 30, Direction.CW);

            //make a rectangle path
            Path rectPath = new Path();
            rectPath.addRect(new RectF(w/4, h/2 - w/16, w*0.5f, h/2 + w/16), Direction.CW);


            //create a path union of circle and rectangle paths
            RectF bounds = new RectF();
            srcPath.computeBounds(bounds, true);
            Region destReg = new Region();
            Region clip = new Region();
            clip.set(new Rect(0,0, w, h));
            destReg.setPath(srcPath, clip);
            Region srcReg = new Region();
            srcReg.setPath(rectPath, clip); 
            Region resultReg = new Region();
            resultReg.op(destReg, srcReg, Region.Op.UNION);
            if(!resultReg.isEmpty()){
                srcPath.reset();
                srcPath.addPath(resultReg.getBoundaryPath());
            }

            //extract a new path from the region boundary path
            extractOutlinePath();

            //shift the resulting path bottom left, so they can be compared
            Matrix matrix = new Matrix();
            matrix.postTranslate(10, 30);
            dstPath.transform(matrix);

        }

         @Override 
            public void onDraw(Canvas canvas) { 
                super.onDraw(canvas);    
                canvas.drawColor(Color.WHITE);
                canvas.drawPath(srcPath, srcPaint);
                canvas.drawPath(dstPath, dstPaint);

                canvas.drawText("Source path", 40, 50, srcPaint);
                canvas.drawText("Destination path", 40, 100, dstPaint);
         }


         public void extractOutlinePath() {

             PathMeasure pm = new PathMeasure(srcPath, false); //get access to curve points

             float p0[] = {0f, 0f}; //current position of the new polygon
             float p1[] = {0f, 0f}; //beginning of the first line
             float p2[] = {0f, 0f}; //end of the first & the beginning of the second line
             float p3[] = {0f, 0f}; //end of the second line

             float pxStep = 5; //sampling step for extracting points
             float pxPlace  = 0; //current place on the curve for taking x,y coordinates
             float angleT = 5; //angle of tolerance

             double a1 = 0; //angle of the first line
             double a2 = 0; //angle of the second line

             pm.getPosTan(0, p0, null); //get the beginning x,y of the original curve into p0
             dstPath.moveTo(p0[0], p0[1]); //start new path from the beginning of the curve
             p1 = p0.clone(); //set start of the first line

             pm.getPosTan(pxStep, p2, null); //set end of the first line & the beginning of the second

             pxPlace = pxStep * 2;
             pm.getPosTan(pxPlace, p3, null); //set end of the second line


             while(pxPlace < pm.getLength()){
             a1 = 180 - Math.toDegrees(Math.atan2(p1[1] - p2[1], p1[0] - p2[0])); //angle of the first line
             a2 = 180 - Math.toDegrees(Math.atan2(p2[1] - p3[1], p2[0] - p3[0])); //angle of the second line

             //check the angle between the lines
             if (Math.abs(a1-a2) > angleT){

               //draw a straight line to the first point if the current p0 is not already there
               if(p0[0] != p1[0] && p0[1] != p1[1]) dstPath.quadTo((p0[0] + p1[0])/2, (p0[1] + p1[1])/2, p1[0], p1[1]);

               dstPath.quadTo(p2[0] , p2[1], p3[0], p3[1]); //create a curve to the third point through the second

               //shift the three points by two steps forward
               p0 = p3.clone();
               p1 = p3.clone();
               pxPlace += pxStep;
               pm.getPosTan(pxPlace, p2, null); 
               pxPlace += pxStep;
               pm.getPosTan(pxPlace, p3, null);
               if (pxPlace > pm.getLength()) break;
             }else{
               //shift three points by one step towards the end of the curve
               p1 = p2.clone(); 
               p2 = p3.clone();
               pxPlace += pxStep;
               pm.getPosTan(pxPlace, p3, null); 
             }
             }
             dstPath.close();
         }
    }

}

オリジナルと私のアルゴリズムが生成するものとの比較は次のとおりです。

パス間の比較;  微分係数の著しく滑らかなコーナー


なぜb-スプラインを使用しないのですか?
GriffinHeart

4
円と長方形であることがわかっている場合は、円と長方形を保存してみませんか?そして、一般化された形式-あなたの物を生成した入力は、おそらくそれを保存するための合理的な形式です。別の質問のように見える圧縮スキームを探している場合役立つように)。
ジェフゲイツ

最初の文で私が言ったように、それはどんな予測不可能な形でも構いません-ここの円と長方形はテスト例にすぎません。
ルミス

@Lumis、あなたは本当にb-スプラインを調べる必要があります。独自のソリューションを実装しようとする理由はありますか?
GriffinHeart

1
ウェルパスクラスはこれらの曲線をスプラインで構築するので、既に使用しています。別の提案があります。数学指向ではありません。ポイントを保存する代わりに、ユーザー入力(コマンドパターン)を保存し、それを再生して同じ「イメージ」を作成します。
GriffinHeart

回答:


6

次の2つの問題があると思います。

非対称コントロールポイント

最初は、p0からp1までとp1からp2までの等しい距離で開始します。ラインセグメント間の許容角度が満たされない場合、p1とp2を前方に移動しますが、p0は元の位置のままにします。これにより、p1からp2までの距離を同じに保ちながら、p0からp1までの距離が増加します。p1を制御点として使用して曲線を作成する場合、最後の曲線からの反復回数に応じて、p2に大きく偏らせることができます。p2をp1の2倍の量だけ移動すると、ポイント間の距離が均等になります。

二次曲線

他の回答でも述べたように、この場合、2次曲線はあまり良くありません。作成する隣接曲線は、制御点と接線を共有する必要があります。入力データが単なるポイントである場合、Catmull-Rom Splineはその目的に適しています。これは3次エルミート曲線で、制御点の接線は前の点と次の点から計算されます。

AndroidのPath APIは、パラメーターに関するエルミート曲線とは少し異なるベジェ曲線をサポートしています。幸いなことに、エルミート曲線はベジェ曲線に変換できます。ここではグーグルときに私が最初に見つかったサンプルコードです。このStackoverflowの答えも式を与えているようです。

また、鋭いエッジの問題についても言及しました。持っている入力データでは、実際に鋭い角があるのか​​、非常に急な曲線があるのか​​を検出することはできません。これが問題になる場合は、必要に応じてオンザフライでステップを増減することにより、反復をより適応的にすることができます。

編集: さらに考えた後、二次曲線は結局使用することができます。制御点としてp1を使用してp0からp2に2次曲線を描く代わりに、制御点として新しい点p0_1を使用してp0からp1にそれを描画します。下の写真をご覧ください。 新しい制御点

p0_1がp0とp1の接線の交点にある場合、結果は滑らかになります。さらに良いことに、PathMeasure.getPosTan()3番目のパラメーターとして正接も返すため、隣接点からの近似の代わりに実際の正確な接線を使用できます。このアプローチを使用すると、既存のソリューションへの変更が少なくなります。

この答えに基づいて、交点は次の式で計算できます。

getPosTan(pxPlace0, p0, t0); // Also get the tangent
getPosTan(pxPlace1, p1, t1);
t1 = -t1; // Reverse direction of second tangent
vec2 d = p1 - p0;
float det = t1.x * t0.y - t1.y * t0.x;
float u = (d.y * t1.x - d.x * t1.y) / det;
float v = (d.y * t0.x - d.x * t0.y) / det; // Not needed ... yet
p0_1 = p0 + u * t0;

ただし、このソリューションは、uとvの両方が負でない場合にのみ機能します。2番目の図を参照してください。 光線は交差しません

ここで、uは負なので、線は交差しますが、光線は交差しません。この場合、前の曲線に滑らかに接続する2次曲線を描くことはできません。ここでは、ベジェ曲線が必要です。この回答で前述した方法を使用してコントロールポイントを計算するか、接線から直接コントロールポイントを取得できます。p0を正接光線p0 + u * t0に投影し、他の光線を逆に投影すると、制御点c0とc1の両方が得られます。接線光線上にある限り、c0の代わりにp0とc0の間の任意の点を使用して曲線を調整することもできます。

Edit2: 描画位置がp1の場合、次の擬似コードを使用して、p2へのベジェ制御点を計算できます。

vec2 p0, p1, p2, p3; // These are calculated with PathMeasure
vec2 cp1 = p1 + (p2 - p0) / 6;
vec2 cp2 = p2 - (p3 - p1) / 6;

これらを使用して、p1からp2へのパスを追加できます。

path.cubicTo(cp1.x, cp1.y, cp2.x, cp2.y, p2.x, p2.y);

ベクトル演算をfloat [ 2 ]配列のコンポーネントごとの演算に置き換えて、コードに一致させます。初期化p1 = start;から始めて、p2とp3が次のポイントです。p0は最初は未定義です。まだp0がない最初のセグメントでは、cp2を制御点として、p1からp2への2次曲線を使用できます。p3がないパスの終わりについても同様に、cp1を制御点としてp1からp2への2次曲線を描くことができます。または、最初のセグメントでp0 = p1を、最後のセグメントでp3 = p2を初期化できます。すべてのセグメントの後、p0 = p1; p1 = p2; and p2 = p3;前進するときに値をシフトします。

パスを保存するときは、すべてのポイントp0 ... pNを保存するだけです。制御ポイントcp1とcp2を保存する必要はありません。必要に応じて計算できるためです。

Edit3: 曲線生成のための適切な入力値を取得するのは難しいと思われるため、別のアプローチを提案します:シリアル化を使用します。Android Pathはサポートしていないようですが、幸いなことにRegionクラスはサポートしています。コードについては、この回答を参照してください。これにより、正確な結果が得られます。シリアル化された形式で最適化されていない場合は、ある程度のスペースが必要になる場合がありますが、その場合は非常にうまく圧縮されるはずです。GZIPOutputStreamを使用したAndroid Javaでの圧縮は簡単です。


それは有望に思えます。ただし、使用されるのはp0ではなくp1、p2、p3です。p0は、計算されるとき、および直線のために新しい確定ポイントを格納するためだけに使用されるため、各ステップでサンプリングされません。新しいコントロールポイントのx、yを計算する方法を教えていただけますか?
ルミス

後でできますが、その間はstackoverflow.com/questions/2931573/…を確認してください。uとvを使用すると、交点を取得できます。
-msell、

助けてくれてありがとう、私はこれを試してみたいのですが、Android用のJavaで書かれる必要があります。vector2はなく、t1とp1などはfloat配列なので、t1 = -t1またはu * t0のように直接操作することはできません。t1 = -t1はt1.x = -t1xを意味すると仮定します。t1.y = -t1.yなど、そうですか?
ルミス

はい、それはよりコンパクトで読みやすくするための単なる擬似コードでした。
msell

まあ、プロットは厚くなっています。Androidの2つのパスの領域交差はアンチエイリアスされていないパスを返すため、接線はその場所にあります。したがって、適切な解決策は、最初に特定のポイントをスムーズに通過してからサンプリングすることです。コードはアンチエイリアス処理されたパスで完全に正常に動作し、適切なコントロールポイントを生成します。
ルミス

13

W3Cは何をしますか?

インターネットにはこの問題があります。ワールド・ワイド・ウェブ・コンソーシアムは気づきました。1999年以降、推奨される標準ソリューションであるScalable Vector Graphics(SVG)があります。これは、2D形状を保存するために特別に設計されたXMLベースのファイル形式です。

スケーラブルなもの

スケーラブルなベクターグラフィックス

  • 拡張性:任意のサイズにスムーズに拡張することを目的としています。
  • ベクター:それは数学の概念に基づいているのベクトル
  • グラフィックス。写真を作るためのものです。

これがSVGバージョン1.1の技術仕様です。
(名前に怖がらないでください。実際に読むのは楽しいです。)

長方形などの基本的な形状をどのように保存するかを正確に書き留めています。例えば、長方形はこれらの性質を持っています:、、、、、。(およびxywidthheightrxryrxryは、角丸に使用できます。)

SVGの長方形の例を以下に示します。

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="12cm" height="4cm" viewBox="0 0 1200 400"
     xmlns="http://www.w3.org/2000/svg" version="1.1">
  <desc>Example rect01 - rectangle with sharp corners</desc>

  <!-- Show outline of canvas using 'rect' element -->
  <rect x="1" y="1" width="1198" height="398"
        fill="none" stroke="blue" stroke-width="2"/>

  <rect x="400" y="100" width="400" height="200"
        fill="yellow" stroke="navy" stroke-width="10"  />
</svg>

以下がその意味です。

青い輪郭の黄色い長方形

仕様にあるように、必要のないプロパティは自由に除外できます。(例えば、rxおよびry属性はここでは使用されませんでした。)はい、約上部に嫌なもののトンがありますDOCTYPEあなたは自分のゲームのために必要ではないであろうが。それらもオプションです。

パス

SVG パス、紙に鉛筆を置いて動かし、最終的にもう一度上げるとパスがあるという意味での「パス」です。閉じている必要はありません閉じられている可能性があります。

各パスにはd属性があり(「描画」の略だと思います)、パスデータ、基本的にペンを紙に置いて移動するための一連のコマンドが含まています。

三角形の例を示します。

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="4cm" height="4cm" viewBox="0 0 400 400"
     xmlns="http://www.w3.org/2000/svg" version="1.1">
  <title>Example triangle01- simple example of a 'path'</title>
  <desc>A path that draws a triangle</desc>
  <rect x="1" y="1" width="398" height="398"
        fill="none" stroke="blue" />
  <path d="M 100 100 L 300 100 L 200 300 z"
        fill="red" stroke="blue" stroke-width="3" />
</svg>

赤い三角形

?のd属性を参照してくださいpath

d="M 100 100 L 300 100 L 200 300 z"

Mあるコマンドのために移動し(座標が続く)、Lsはのためにあるラインに(座標)とz(すなわち、第1の位置にラインバックを描く、座標を必要としない)パスを閉じるためのコマンドです。

直線は退屈ですか?3次または2次ベジエコマンドを使用してください!

いくつかのキュービックベジエ

ベジエ曲線の背後にある理論は、他の場所(ウィキペディアなど)で十分に説明されていますが、エグゼクティブサマリーは次のとおりです。ベジエには開始点と終了点があります。

二次ベジェをトレースする

仕様には、必要に応じて、ほとんどの基本的な形状をパスに変換するための手順も記載されています。

SVGを使用する理由とタイミング

テキストで任意の2D形状を表現するのは非常に複雑なので、この道をたどりたいかどうかを慎重に決定します(意図したしゃれ)。たとえば、(可能性のある本当に多くの)直線でできたパスだけに制限することで、人生をもっと楽にすることができます。

あなたが任意の形状をしたい決定しない場合でも、SVGは、移動するための方法である:それは素晴らしいツールをサポートしている:あなたは見つけることができる多くの低レベルのXML解析用ライブラリやSVGエディタツールを高いレベルで。

とにかく、SVG標準は良い例を設定します。


問題は、曲線を保存するのではなく、ポイントに変換することです。しかし、このリファレンスをありがとう、SVG標準について知っておくと良いでしょう。
ルミス

@Lumisタイトルとコンテンツは、そうでなければ示唆します。質問の言い換えを検討してください。(または、今、これは非常に確立されており、別のものを求めています。)
アンコ

4

コードに誤解を招くコメントが含まれています:

dstPath.quadTo(p2[0] , p2[1], p3[0], p3[1]); //create a curve to the third point through the second

二次ベジェ曲線は ではない行くを通じて第二の点。2番目のポイントを通過する場合は、エルミートカーブなどの異なるタイプのカーブが必要です。です。Pathクラスを使用できるように、エルミート曲線をベジェに変換できる場合があります。

もう1つの提案は、ポイントをサンプリングする代わりに、スキップするポイントの平均を使用することです。

別の提案は、角度をしきい値として使用する代わりに、実際の曲線と近似曲線の差を使用することです。角度は本当の問題ではありません。実際の問題は、ポイントのセットがベジェ曲線に適合しない場合です。

もう1つの提案は、1つのタンジェントが次のタンジェントと一致するキュービックベジェを使用することです。それ以外の場合(2次関数を使用)、曲線がスムーズに一致しないと思います。


あなたは正しい、2番目のポイントはそれに向かって曲線を「引っ張る」だけです。cubicToには、quadToとして1つではなく2つのコントロールポイントが必要です。問題はもちろん、正しい制御点を取得する方法です。ソースパスは、任意の直線または円形の組み合わせにすることができるため、鋭い角を失いたくないことに注意してください-基本的に、選択したパスを保存できる画像選択ツールを作成しています。
ルミス

4

2つのパスをよりスムーズに交差させるには、交差する前にそれらを拡大し、その後に縮小することができます。

それが良い解決策かどうかはわかりませんが、うまくいきました。また、高速です。この例では、丸みを帯びたパスと作成したパターン(ストライプ)を交差させます。スケーリングしても見栄えがします。

ここに私のコード:

    Path mypath=new Path(<desiredpath to fill with a pattern>);
    String sPatternType=cpath.getsPattern();

    Path pathtempforbounds=new Path(cpath.getPath());
    RectF rectF = new RectF();
     if (sPatternType.equals("1")){
         turnPath(pathtempforbounds, -45);
     }
     pathtempforbounds.computeBounds(rectF, true);

     float ftop=rectF.top;
     float fbottom=rectF.bottom;
     float fleft=rectF.left;
     float fright=rectF.right;
     float xlength=fright-fleft;

     Path pathpattern=new Path();

     float ypos=ftop;
     float xpos=fleft;

     float fStreifenbreite=4f;

     while(ypos<fbottom){
         pathpattern.moveTo(xpos,ypos);
         xpos=xpos+xlength;
         pathpattern.lineTo(xpos, ypos);
         ypos=ypos+fStreifenbreite;
         pathpattern.lineTo(xpos, ypos);
         xpos=xpos-xlength;
         pathpattern.lineTo(xpos, ypos);
         ypos=ypos-fStreifenbreite;
         pathpattern.lineTo(xpos, ypos);
         pathpattern.close();
         ypos=ypos+2*fStreifenbreite;

     }

     // Original vergrössern

     scalepath(pathpattern,10);
     scalepath(mypath,10);

     if (sPatternType.equals("1")){
         Matrix mdrehen=new Matrix();
         RectF bounds=new RectF();
         pathpattern.computeBounds(bounds, true);
         mdrehen.postRotate(45, (bounds.right + bounds.left)/2,(bounds.bottom + bounds.top)/2);
         pathpattern.transform(mdrehen);
     }

     RectF rectF2 = new RectF();
     mypath.computeBounds(rectF2, true);

     Region clip = new Region();
     clip.set((int)(rectF2.left-100f),(int)(rectF2.top -100f), (int)(rectF2.right+100f),(int)( rectF2.bottom+100f));
     Region region1 = new Region();
     region1.setPath(pathpattern, clip);

     Region region2 = new Region();
     region2.setPath(mypath, clip);

     region1.op(region2, Region.Op.INTERSECT);


     Path pnew=region1.getBoundaryPath();


     scalepath(pnew, 0.1f);
     cpath.setPathpattern(pnew);




public void turnPath(Path p,int idegree){
     Matrix mdrehen=new Matrix();
     RectF bounds=new RectF();
     p.computeBounds(bounds, true);
     mdrehen.postRotate(idegree, (bounds.right + bounds.left)/2,(bounds.bottom + bounds.top)/2);
     p.transform(mdrehen);
}

public void scalepath(Path p,float fscale){
     Matrix mverkleinern=new Matrix();
     mverkleinern.preScale(fscale,fscale);
     p.transform(mverkleinern);
}

ここに画像の説明を入力してください

canvas.scale()でズームしても滑らかに見える: ここに画像の説明を入力してください


画像を追加するために私に10の評判を費やした人に感謝します:
user1344545

1
驚くべきことに、この単純なトリックは2つの問題を解決します。1つ目は結果の交差または結合のパスを滑らかにし、2つ目はこの同じスケールアップパスをサンプリングするときに問題のコードを完全に滑らかにすることです。なんという予想外の簡単な解決策、ありがとう!
ルミス

@user編集は無料です。<2k-repユーザーの場合、実際には+2です。
アンコ

@Lumis私は少し混乱しています- パスを保存する方法を尋ねたと思いますか?
アンコ

1
残念ながら、さらにテストを重ねた結果、Regionは描画時にパスが占めるピクセルを使用するため、パスのスケーリングが大きく、繰り返し実行されると、アプリのメモリが簡単に不足することがわかりました。そのため、このソリューションには制限があり危険です。
ルミス

3

ポリゴン補間を見てください( http://en.wikipedia.org/wiki/Polynomial_interpolation

基本的に、n個の等間隔ノードを使用します(最適な補間は等間隔ではありませんが、場合によっては十分に実装しやすいはずです)

最終的に、ラインが十分に滑らか場合(<-大きい場合)曲線間の誤差を減らす次数nのポリゴンになります。

あなたのケースでは、線形(1次)補間を行っています。

もう1つのケース(GriffinHeart推奨)は、スプラインを使用することでした(http://en.wikipedia.org/wiki/Spline_interpolation

どちらの場合も、曲線に適合する多項式の形を与えます。


2

変換のポイントがストレージのみであり、画面にレンダリングするときに滑らかにする必要がある場合、特定の曲線を維持するために必要なストレージの合計を最小限に抑えながら、取得できる最高の忠実度のストレージ実際に円(または円弧)の属性を保存し、必要に応じて再描画します。

原点。半径。円弧を描くための開始/停止角度。

とにかくレンダリングのために円/円弧をポイントに変換する必要がある場合は、ストレージからロードするときに、属性のみを常に保存しながら、おそらくそれをポイントに変換できます。


ソースパス/曲線は、自由な線の描画を含む任意の形状にすることができます。私は、各コンポーネントを個別に保存してからロードするときにそれらを結合する必要があるソリューションを検討していましたが、多大な作業が必要であり、すべての変換を各再度保存できるようにするためのコンポーネント。
ルミス

2

直線ではなく曲線を選ぶ理由はありますか?直線は作業が簡単で、ハードウェアで効率的にレンダリングできます。

検討する価値のある他のアプローチは、ピクセルごとに数ビットを格納し、形状の内側、外側、または輪郭上にあるかどうかを示すことです。これは圧縮率が高く、複雑な選択の場合よりも効率的です。

また、これらの記事が興味深く/役に立つかもしれません:


1

曲線補間を見てください-曲線を滑らかにするのに役立ついくつかの異なるタイプが実装できます。そのサークルで獲得できるポイントが多ければ多いほど良いです。ストレージは非常に安価です。したがって、360個の近いノードを抽出するだけで十分です(位置が8バイトであっても、360個のノードを保存するのはほとんどコストがかかりません)。

ここにいくつかの補間サンプルを配置できますが、4つのポイントしかありません。結果は非常に良好です(私の場合、このケースのベジェがお気に入りですが、他の効果的なソリューションについては他の人が口にするかもしれません)。

あなたもここで遊ぶことができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.