p5.j​​s - JavaScript で太い線をプログラムで描画するにはどうすればよいですか?

okwaves2024-01-25  8

ユーザーがキャンバス要素に描画できる動的なラインを使用して競馬場を定義しようとしています。したがって、線が描かれたら、プログラムは以下の図に示すように、その線にサイドラインを追加する必要があります。

線法線を使用してアイデアを模倣することはできましたが、正しく行うことができません。今は線の途中に線法線方向にポイントを置き、そのポイントを使って輪郭を描いています。大きなターンの場合、生成されたラインは比較的スムーズですが、タイトなターンではループが発生する傾向があります。

以下の画像に示すように:

上記のサイドラインのポイントを生成する現在のコードは次のとおりです (p5.js JavaScript ライブラリを使用しています)。

var sketch = function (p) {
  with(p) {

    let handpoints;
    let walkhandpoints;
    let collect;
    let parsepath;
    let shapse;
    let step;
    let tmp; 
    let dorender;
    let lineoffset;

    p.setup = function() {
      createCanvas(600, 600);
      handpoints = [];
      walkhandpoints = 10;
      collect = true;
      parsepath = false;
      shapes = [];
      step = 2;
      tmp = [];
      dorender = true;
      lineoffset = 15;
    };

    p.draw = function() {
      if(dorender) {
        background(220);
        update();
        for (let shape of shapes) {
          shape.show();
        }
      }
    };

    function update() {
      if (mouseIsPressed) {
        if (collect) {
          let mouse = createVector(mouseX, mouseY);
          handpoints.push(mouse);
          Shape.drawPath(handpoints);
          parsepath = true;
        }
      } else if (parsepath) {
        let tmp1 = Shape.cleanPath(handpoints, step);
        let s1 = new Shape(tmp1, 1, 'line', color(175));
        shapes.push(s1);
        let tmp2 = Line.sidePoints(tmp1, lineoffset);
        let s2 = new Shape(tmp2.sideA, 1, 'line', color(175,120,0));
        let s3 = new Shape(tmp2.sideB, 1, 'line', color(175,0, 120));
        shapes.push(s2);
        shapes.push(s3);
        handpoints = [];
        parsepath = false;
        //dorender = false;
      }
    }

    class Shape {
      constructor(points, mag, type = 'line', shader = color(200, 0, 100)) {
        this.points = points.slice().map(item => item.copy());
        this.type = type;
        this.mag = mag;
        this.shader = shader;
      }

      static cleanPath(points, step) {
        let tmp = [];
        let output = [];
        for (let i = 1; i < points.length; i++) {
          let prev = points[i - 1];
          let curr = points[i];
          if (!prev.equals(curr)) {
            tmp.push(prev.copy())
            if (i === points.length - 1) {
              tmp.push(curr.copy())
            }
          }
        }
        for (let i = 0; i < tmp.length; i++) {
          if(i % step === 0) {
            output.push(tmp[i]);
          }
        }
        output.push(output[0]);
        return output;
      }
  
      static drawPath(points, mag = 1, type = 'line', shader = color(175)) {
        let s = new Shape(points, mag, type, shader);
        s.show();
      }
  
      show() {

        for (let i = 0; i < this.points.length; i++) {
          if (this.type === 'line' && i > 0) {
            let prev = this.points[i - 1];
            let curr = this.points[i];
            strokeWeight(this.mag);
            stroke(this.shader);
            line(prev.x, prev.y, curr.x, curr.y);
          } else if (this.type === 'point') {
            noStroke();
            fill(this.shader);
            ellipse(this.points[i].x, this.points[i].y, this.mag * 2, this.mag * 2);
          }
        }
      }
    }

    class Line {
      static sidePoints(points, lineoffset) {
        let sideA = [];
        let sideB = [];
        for(let i = 1; i < points.length; i++) {

          // take consecutive points
          let prev = points[i-1];
          let curr = points[i];

          // calculate normals
          let dx = curr.x-prev.x;
          let dy = curr.y-prev.y;
          let a = createVector(-dy, dx).normalize();
          let b = createVector(dy, -dx).normalize();

          // calculate midway of the two points
          let px = (prev.x+curr.x)/2;
          let py = (prev.y+curr.y)/2;
          let p = createVector(px,py);
      
          // put created points back along drawed line
          a.mult(lineoffset).add(p);
          b.mult(lineoffset).add(p);
          sideA.push(a);
          sideB.push(b);
        }

        // close paths
        if(!sideA[0].equals(sideA[sideA.length-1])) {
          sideA.push(sideA[0]);
        }
        if(!sideB[0].equals(sideB[sideB.length-1])) {
          sideB.push(sideB[0]);
        }
        return {sideA, sideB};
      }
    }


  }
};

let node = document.createElement('div');
window.document.getElementById('p5-container').appendChild(node);
new p5(sketch, node);
body {
  background-color:#ffffff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
    <div id="p5-container"></div>

まずそれらの点を描画する方法を見つけたいと思います描かれた線の対応するコーナーポイントなので、描かれた線に数個の点しかない場合でも、輪郭は描かれた形状を保持します。 第二に、生成されたラインの小さな隅のループやその他のタイプエラーを減らすために、ポイントがいくつかある領域上のポイントを減らす良い方法はありますか? アイデアは、ラインのポイントを取得することです。これにより、レースカーの速度ベクトルがラインを横切る場合にラインの交差を検出するのが簡単になります。 残念ながら、私は数学表記にあまり慣れていないので、うまく機能するような派手な数学があれば、わかりやすいバージョンを使用してみてください。

ここまでのコードの残りの部分 (スケッチ) を見せていただけますか。

– ドミニク・フォルティン

2020 年 9 月 5 日 1:14

これまでのスケッチ全体です editor.p5js.org/lazydistribution/sketches/r60EQ2cVg

– lazydistribution

2020 年 9 月 5 日 6:33

実際には、「並列」を計算したいのです。輪郭。

– ラビッド76

2020 年 9 月 5 日 7:16

はい。基本的にはそれが私がやろうとしていることのようです。

– lazydistribution

2020 年 9 月 5 日 7:30

2

まず、クリックしただけではコードがクラッシュします。些細なことですが、人々は彼の代わりにそのバグを修正しようとするでしょうアルゴリズムについて詳しく説明します。

– ドミニク・フォルティン

2020 年 9 月 5 日 16:11



------------------------

今後は、最小限の例を投稿するようにしてください。あなたが投稿したコードを実行できませんでした。

そうは言っても、検討できるオプションの 1 つは、ストロークウェイト() 関数を使用して、さまざまな幅でパスを描画することです。以下に例を示します。

const path = [];

function setup() {
  createCanvas(400, 400);
  
  // Add some default points to the path.
  path.push(createVector(0, 0));
  path.push(createVector(width/4, height/4));
}

function draw() {
  background(220);
  
  // Draw the path with a thick gray line.
  strokeWeight(50);
  stroke(200);
  for(let i = 1; i < path.length; i++){
    const prevPoint = path[i-1];
    const nextPoint = path[i];
    line(prevPoint.x, prevPoint.y, nextPoint.x, nextPoint.y);
  }
 
  // Draw the path with a thin black line.
  strokeWeight(1);
  stroke(0);
  for(let i = 1; i < path.length; i++){
    const prevPoint = path[i-1];
    const nextPoint = path[i];    
     line(prevPoint.x, prevPoint.y, nextPoint.x, nextPoint.y);
  }
}

// Add a point to the path when the user clicks.
function mousePressed(){
 path.push(createVector(mouseX, mouseY));
}

ここでのコツは、パスを 2 つのパスで描くことです。最初に太い線を使用してパスを描画し、次に今度は細い線を使用して再度パスを描画します。

8

ありがとう @KevinWorkman 。将来的には、その最小限の例を実行してみます。ただし、「道路」の各側に少なくとも 2 つの線が必要ですが、ストロークウェイト() は「道路」の中心の 1 本の線のピクセル表現を変更するだけです。

– lazydistribution

2020 年 9 月 7 日 10:05

@lazydistribuなぜ 2 行必要なのでしょうか?

– ケビン・ワークマン

2020 年 9 月 7 日 15:35

私のアイデアは、レースカーの速度ベクトルが道路のベンチを横切るかどうかを線の交差で検出し、ゲームが終了したことを知ることでした。

– lazydistribution

2020 年 9 月 8 日 7:57

@lazydis貢献 私の提案の結果として描かれた太い線で衝突検出を行うことができます。

– ケビン・ワークマン

2020 年 9 月 9 日 3:11

どうすればいいでしょうか?

– lazydistribution

2020 年 9 月 9 日 9:00

総合生活情報サイト - OKWAVES
総合生活情報サイト - OKWAVES
生活総合情報サイトokwaves(オールアバウト)。その道のプロ(専門家)が、日常生活をより豊かに快適にするノウハウから業界の最新動向、読み物コラムまで、多彩なコンテンツを発信。