Bagaimana cara menggambar enam garis dengan jarak simetris dalam radius di Canvas?

Saya mencoba membuat aplikasi menggambar kepingan salju di Canvas, sangat mirip dengan ini: http://www.pumpkinpirate.info/ks/.

Pada dasarnya, jika Anda menggambar sebuah titik, itu harus diulangi 5 kali lagi dalam pola lingkaran simetris di dalam kanvas. Berikut ilustrasinya:

konsep kepingan salju

Saya tahu cara memberitahu kanvas untuk menggambar sesuatu beberapa kali, tetapi kemampuan matematika saya terbatas dan saya bertanya-tanya bagaimana cara menghitung posisi 5 titik yang disalin dalam kaitannya dengan titik aslinya.

Saya berpikir bahwa saya harus mencari bagian tengah kanvas dan menghitung jarak dari titik tersebut ke posisi x dan y dari titik aslinya. Setelah itu saya berasumsi mungkin saya harus mencari jari-jari jika saya menggambar lingkaran dari titik itu dan kemudian... saat itulah kepala saya mulai berasap.

Saya ingin tahu apakah saya dapat melakukan hal serupa dengan apa yang mereka lakukan di sini: Balikkan koordinat X dan Y pada kanvas HTML5

Sekadar latar belakang, saya awalnya memulai dengan 6 kanvas terpotong di mana 5 diputar dan akan menyalin yang pertama, tetapi saya menyadari bahwa pendekatan ini tidak benar-benar berhasil, sebagian karena kanvas akan sedikit tumpang tindih (tetapi ada masalah lain) juga).

Berikut kode yang saya miliki sejauh ini:

drawing = false;
startX = 0;
startY = 0;
imageData = null;

const canvas = document.getElementById("canvas");

canvas.addEventListener("mousedown", startPosition());
canvas.addEventListener("mouseup", endPosition());
canvas.addEventListener("mousemove", draw());

function startPosition(e) {
  const canvas = this;
  const ctx = canvas.getContext("2d");

  startX = e.clientX;
  startY = e.clientY;
  imageData = ctx.getImageData(0,0,canvas.width,canvas.height);
  drawing = true;
}

function endPosition(e) {
  const canvas = document.getElementById("canvas");
  const ctx = canvas.getContext("2d");

  ctx.lineWidth = 4;
  ctx.lineCap = "round";
  ctx.moveTo(startX, startY);
  ctx.lineTo(e.clientX, e.clientY);
  ctx.strokeStyle = '#ffffff';
  ctx.stroke();
  ctx.beginPath();

  drawing = false;
}
function draw(e) {
  const canvas = document.getElementById("canvas");
  const ctx = canvas.getContext("2d");

  if(!drawing) {
    return;
  }

  ctx.putImageData(imageData, 0, 0);
  ctx.lineWidth = 4;
  ctx.lineCap = "round";
  ctx.moveTo(startX, startY);
  ctx.lineTo(e.clientX, e.clientY);    
  ctx.strokeStyle = '#ffffff';
  ctx.stroke();
  ctx.beginPath();
  ctx.moveTo(e.clientX, e.clientY);

  //draw line 2
  ctx.moveTo(startX - 50, startY - 50);
  ctx.lineTo(e.clientX - 50, e.clientY - 50);
  ctx.stroke();
  ctx.beginPath();
  ctx.moveTo(e.clientX - 50, e.clientY - 50);

  //draw line 3
  ctx.moveTo(startX - 100, startY - 100);
  ctx.lineTo(e.clientX - 100, e.clientY - 100);
  ctx.stroke();
  ctx.beginPath();
  ctx.moveTo(e.clientX - 100, e.clientY - 100);
}

person hmoore    schedule 06.01.2020    source sumber
comment
Mengenai matematika: Saya rasa Anda menginginkan 12 poin yang setara, bukan 6. Lihat cabangnya di kepingan salju ini: setiap titik akhir memiliki 12 salinan: 6 di sebelah kanan dan 6 di sebelah kiri setiap cabang. Secara matematis, Anda memerlukan 5 titik yang dihasilkan dengan rotasi 60 derajat mengelilingi pusatnya, dan 6 titik lainnya dihasilkan dari titik tersebut menggunakan refleksi (dalam kepingan salju yang terhubung, bayangkan refleksi terhadap sumbu vertikal yang sesuai dengan transformasi (x,y) <-> (-x,y).   -  person Andras Deak    schedule 07.01.2020
comment
Jika Anda hanya menggunakan 6 versi yang diputar, Anda mendapatkan hal-hal seperti ini tetapi dengan 6 kaki, bukan 3 pada gambar. Anda tidak ingin kaki hanya berada di satu sisi setiap kaki.   -  person Andras Deak    schedule 07.01.2020
comment
Terima kasih Andreas! Ya, saya mengerti apa yang Anda katakan. Inspirasi saya untuk ini adalah generator kepingan salju di kalender kedatangan Jaquie Lawson dan kalender itu memiliki 6 bagian, tetapi setiap bagian mencerminkan dirinya sendiri saat Anda menggambar, sehingga Anda mendapatkan 12 bagian.   -  person hmoore    schedule 07.01.2020


Jawaban (1)


Untuk memutar suatu titik di sekitar titik lainnya

const center = {x: 100, y: 100};   // Point to rotate around
const point = {x: ?, y: ?};        // Point to rotate
const rotate = (Math.PI * 2) / 5;  // Rotate 1/5th of 360


// Get the vector from center to point
const vx = point.x - center.x;
const vy = point.y - center.y;

// Get the transformation (2D uniform rotate)
const xAx = Math.cos(rotate);
const xAy = Math.sin(rotate); 

// Transform the vector (rotates) and translate back to center
const tx = vx * xAx - vy * xAy + center.x;
const ty = vx * xAy + vy * xAx + center.y;

Sebagai sebuah fungsi

function rotate(point, center, rotate, result = {}) {
    const vx = point.x - center.x;
    const vy = point.y - center.y;
    const xAx = Math.cos(rotate);
    const xAy = Math.sin(rotate); 
    result.x = vx * xAx - vy * xAy + center.x;
    result.y = vx * xAy + vy * xAx + center.y;
    return result;
}

Untuk membuat 5 poin dari 1 menggunakan fungsi di atas

const slices = 5; // number of rotations
const center = {x: 100, y: 100}; // center point

// Rotates point steps times around center
// Returns array of points
function rotateAll(point, steps, result = []) {
    const ang = Math.PI * 2 / steps;
    result.push(point);                      // Add first point
    for (let rot = 1; rot < steps; rot++) {  // Add remaining points
        result.push(rotate(point, center, rot * ang));
    }
    return result;
}

// Usage
const points = rotateAll({x: 10, y: 20}, slices);
person Blindman67    schedule 06.01.2020
comment
Anda ingin 5 rotasi dengan seperenam lingkaran (PI/3), karena ada enam salinan (tetapi Anda sudah memiliki titik aslinya). - person Andras Deak; 07.01.2020
comment
@AndrasDeak Konstanta slices mendefinisikan jumlah poin. Cuplikan bawah. - person Blindman67; 07.01.2020
comment
Di bagian atas Anda memiliki (Math.PI * 2) / 5; // Rotate 1/5th of 360 yang tidak mungkin benar. Dan saya tidak tahu JS (?) tetapi menurut saya slices diteruskan sebagai steps ke rotateAll, jadi Anda meneruskan 5 lagi yang masuk ke const ang = Math.PI * 2 / steps;. Itu tetap berarti rotasi 72 derajat, bukan 60 derajat. Anda memerlukan steps iterasi loop tetapi Math.PI * 2 / (steps + 1) sebagai sudut rotasi. - person Andras Deak; 07.01.2020
comment
@AndrasDeak Nilai pertama sewenang-wenang, dapat berupa nilai apa pun. Fungsi rotateAll mengembalikan array dengan argumen steps poin. Jika nilai steps adalah 5 maka ada 5 titik yang berjarak sama mengelilingi lingkaran, hanya 4 titik terakhir yang diputar. - person Blindman67; 07.01.2020
comment
Ah, oke, loop Anda dimulai dari 1, bukan 0. Maka saya mengerti, terima kasih. Dalam hal ini saya hanya akan mengubah semua angka lima menjadi enam, karena sepertinya itulah yang ditanyakan OP (lihat gambaran mereka dan pengetahuan kepingan salju secara umum). - person Andras Deak; 07.01.2020
comment
Terima kasih Blindman67! Ini bekerja dengan sempurna. Sekarang saya hanya perlu mencari cara untuk mencerminkan enam poin seperti yang disarankan Andreas Deak. Berikut codepen aplikasi menggambar kepingan salju sejauh ini: codepen.io/moorehannah/pen/VwYyERG (penafian: untuk beberapa alasan titik yang digambar tidak 100% cocok dengan posisi kursor di codepen, tetapi cocok di lingkungan lokal saya, jadi saya tidak akan mengkhawatirkannya untuk saat ini) - person hmoore; 07.01.2020
comment
@hmoore Fungsi cermin (singkatan nama arg: p menunjuk ke cermin, mX mencerminkan sumbu X, mY mencerminkan sumbu Y sebagai bools, sesuai dengan sumbu cermin, c adalah pusat (menunjuk ke cermin aktif), dan r mengembalikan hasil.) fungsi function mirrorPoint(p, mX, mY, c, r = {}) {r.x = (p.x - c.x) * (mX ? -1 : 1) + c.x; r.y = (p.y - c.y) * (mY ? -1 : 1) + c.y; return r;} Penggunaan: Untuk mencerminkan pada sumbu X dan Y gunakan const mirroredPoint mirrorPoint({x: 10, y: 20}, true, true, center); (center seperti yang digunakan dalam jawaban) - person Blindman67; 07.01.2020
comment
Luar biasa, terima kasih banyak @ Blindman67! Ini indah! codepen.io/moorehannah/pen/VwYyERG - person hmoore; 07.01.2020