จะวาดเส้นหกเส้นโดยเว้นระยะห่างอย่างสมมาตรในรัศมีใน Canvas ได้อย่างไร

ฉันกำลังพยายามสร้างแอปวาดภาพเกล็ดหิมะใน Canvas ซึ่งคล้ายกับสิ่งนี้มาก: http://www.pumpkinpirate.info/ks/.

โดยพื้นฐานแล้ว หากคุณวาดจุด ควรทำซ้ำอีก 5 ครั้งในรูปแบบวงกลมสมมาตรภายในผืนผ้าใบ นี่คือภาพประกอบ:

แนวคิดเกล็ดหิมะ

ฉันรู้วิธีบอกให้ผืนผ้าใบวาดบางสิ่งหลายๆ ครั้ง แต่ทักษะทางคณิตศาสตร์ของฉันมีจำกัด และฉันสงสัยว่าจะคำนวณตำแหน่งของจุดคัดลอก 5 จุดที่สัมพันธ์กับจุดต้นฉบับได้อย่างไร

ฉันกำลังคิดว่าจะต้องหาจุดกึ่งกลางของผืนผ้าใบและคำนวณระยะห่างจากจุดนั้นถึงตำแหน่ง x และ y ของจุดเดิม หลังจากนั้นผมถือว่าบางทีผมต้องหารัศมีว่าจะวาดวงกลมจากจุดนั้นหรือไม่ แล้ว... ก็เป็นช่วงที่หัวผมเริ่มสูบบุหรี่

ฉันสงสัยว่าฉันสามารถทำอะไรบางอย่างที่คล้ายกับสิ่งที่พวกเขาทำที่นี่ได้หรือไม่: สลับพิกัด X และ Y บนแคนวาส HTML5

สำหรับพื้นหลัง เดิมทีฉันเริ่มต้นด้วยผ้าใบที่ถูกตัด 6 ชิ้น โดยที่ 5 ชิ้นถูกหมุนและจะคัดลอกชิ้นแรก แต่ฉันรู้ว่าวิธีนี้ใช้ไม่ได้ผลจริงๆ ส่วนหนึ่งเป็นเพราะผืนผ้าใบจะเหลื่อมกันเล็กน้อย (แต่มีปัญหาอื่นๆ อยู่ด้วย) ด้วย).

นี่คือรหัสที่ฉันมี:

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 แหล่งที่มา
comment
เกี่ยวกับคณิตศาสตร์: ฉันคิดว่าคุณต้องการ 12 คะแนนที่เทียบเท่า ไม่ใช่ 6 ดูง่ามใน เกล็ดหิมะนี้: แต่ละจุดสิ้นสุดมี 12 ชุด: 6 ชุดทางด้านขวาและ 6 ชุดทางด้านซ้ายของแต่ละสาขา ในทางคณิตศาสตร์ คุณต้องมี 5 จุดที่สร้างด้วยการหมุน 60 องศารอบๆ จุดศูนย์กลาง และอีก 6 จุดที่สร้างจากการสะท้อนเหล่านี้โดยใช้การสะท้อน (ในเกล็ดหิมะที่เชื่อมโยงกัน ลองจินตนาการถึงการสะท้อนที่เกี่ยวข้องกับแกนตั้งที่สอดคล้องกับการแปลง (x,y) <-> (-x,y)   -  person Andras Deak    schedule 07.01.2020
comment
หากคุณใช้เวอร์ชันที่หมุนเวียน 6 เวอร์ชันเท่านั้น คุณจะได้รับสิ่งต่างๆ เช่น สิ่งนี้ แต่มี 6 ขา แทนที่จะเป็น 3 ขาในภาพ คุณคงไม่อยากให้เท้าอยู่เพียงข้างเดียวของขาแต่ละข้าง   -  person Andras Deak    schedule 07.01.2020
comment
ขอบคุณแอนเดรียส! ใช่ ฉันเห็นสิ่งที่คุณพูด แรงบันดาลใจของฉันสำหรับสิ่งนี้คือเครื่องกำเนิดเกล็ดหิมะในปฏิทินจุติของ Jaquie Lawson และส่วนนั้นมี 6 ส่วน แต่แต่ละส่วนจะสะท้อนตัวเองเมื่อคุณวาด ดังนั้นคุณจะได้ 12 ส่วน   -  person hmoore    schedule 07.01.2020


คำตอบ (1)


เพื่อหมุนจุดหนึ่งไปรอบจุดอื่น

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;

เป็นฟังก์ชัน

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

เพื่อสร้าง 5 คะแนนจาก 1 โดยใช้ฟังก์ชันด้านบน

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
คุณต้องการการหมุน 5 รอบโดยมีหนึ่งในหกของวงกลม (PI/3) เนื่องจากมีสำเนาหกชุด (แต่คุณมีจุดเดิมอยู่แล้ว) - person Andras Deak; 07.01.2020
comment
@AndrasDeak ค่าคงที่ slices กำหนดจำนวนคะแนน ตัวอย่างด้านล่าง - person Blindman67; 07.01.2020
comment
ในอันบนสุดคุณมี (Math.PI * 2) / 5; // Rotate 1/5th of 360 ซึ่งไม่ถูกต้อง และฉันไม่รู้จัก JS (?) แต่สำหรับฉันแล้วดูเหมือนว่า slices กำลังถูกส่งผ่านเป็น steps ไปยัง rotateAll ดังนั้นคุณกำลังส่งผ่าน 5 อีกครั้งซึ่งจะเข้าสู่ const ang = Math.PI * 2 / steps; นั่นยังคงหมายถึงการหมุน 72 องศา ไม่ใช่การหมุน 60 องศา คุณต้องวนซ้ำ steps ครั้ง แต่เป็น Math.PI * 2 / (steps + 1) เป็นมุมการหมุน - person Andras Deak; 07.01.2020
comment
@AndrasDeak ค่าแรกเป็นค่าใดก็ได้ สามารถเป็นค่าใดก็ได้ ฟังก์ชัน rotateAll ส่งคืนอาร์เรย์ที่มีอาร์กิวเมนต์ steps พอยต์ หากค่า steps คือ 5 แสดงว่าจะมี 5 จุดที่มีระยะห่างเท่ากันรอบวงกลม มีเพียง 4 จุดสุดท้ายเท่านั้นที่จะถูกหมุน - person Blindman67; 07.01.2020
comment
โอเค ลูปของคุณเริ่มจาก 1 ไม่ใช่ 0 งั้นฉันก็เข้าใจ ขอบคุณ ในกรณีนั้น ฉันแค่เปลี่ยนห้าทั้งหมดเป็นหก เพราะนั่นคือสิ่งที่ OP ดูเหมือนจะถาม (ดู ภาพลักษณ์และตำนานเกล็ดหิมะโดยทั่วไป) - person Andras Deak; 07.01.2020
comment
ขอบคุณ Blindman67! มันทำงานได้อย่างสมบูรณ์แบบ ตอนนี้ฉันแค่ต้องหาวิธีสะท้อนหกแต้มตามที่ Andreas Deak แนะนำ จนถึงตอนนี้นี่คือ Codepen ของแอปวาดรูปเกล็ดหิมะ: codepen.io/moorehannah/pen/VwYyERG (ข้อจำกัดความรับผิดชอบ: ด้วยเหตุผลบางประการ จุดที่ถูกวาดไม่ตรงกับตำแหน่งเคอร์เซอร์ 100% ใน codepen แต่เกิดขึ้นในสภาพแวดล้อมท้องถิ่นของฉัน ดังนั้นฉันจะไม่กังวลเกี่ยวกับมันในตอนนี้) - person hmoore; 07.01.2020
comment
@hmoore Mirror function (ชื่อย่อ arg: p ชี้ไปที่มิเรอร์, mX มิเรอร์แกน X, mY มิเรอร์แกน Y เป็นบูล, จริงกับแกนมิเรอร์, c เป็นศูนย์กลาง (ชี้ไปที่มิเรอร์บน) และ r ส่งคืนผลลัพธ์) ฟังก์ชั่น 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;} การใช้งาน: หากต้องการสะท้อนทั้งในแกน X และ Y ให้ใช้ const mirroredPoint mirrorPoint({x: 10, y: 20}, true, true, center); (center ตามที่ใช้ในคำตอบ) - person Blindman67; 07.01.2020
comment
เยี่ยมมาก ขอบคุณมาก @Blindman67! สวยจังเลย! codepen.io/moorehannah/pen/VwYyERG - person hmoore; 07.01.2020