Programim dhe zhvillim, javascript, python, php, html

Përafrimi i harkut eliptik svg në kanavacë me javascript

Po përpiqem të krijoj një hark eliptik duke përafruar një kurbë bezier si në postimin https://mortoray.com/2017/02/16/rendering-an-svg-elliptical-arc-as-bezier-curves/

Megjithatë, zbatimi im nuk duket se ka rezultatin e duhur. (Vija e kuqe është SVG dhe vija e zezë është rruga e kanavacës)

Ky është kodi im

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

// M100,350
// a45,35 -30 0,1 50,-25

canvas.width = document.body.clientWidth;
canvas.height = document.body.clientHeight;
ctx.strokeWidth = 2;
ctx.strokeStyle = "#000000";

function clamp(value, min, max) {
  return Math.min(Math.max(value, min), max)
}

function svgAngle(ux, uy, vx, vy ) {
  var dot = ux*vx + uy*vy;
  var len = Math.sqrt(ux*ux + uy*uy) * Math.sqrt(vx*vx + vy*vy);

  var ang = Math.acos( clamp(dot / len,-1,1) );
  if ( (ux*vy - uy*vx) < 0)
    ang = -ang;
  return ang;
}

function generateBezierPoints(rx, ry, phi, flagA, flagS, x1, y1, x2, y2) {
  var rX = Math.abs(rx);
  var rY = Math.abs(ry);

  var dx2 = (x1 - x2)/2;
  var dy2 = (y1 - y2)/2;

  var x1p =  Math.cos(phi)*dx2 + Math.sin(phi)*dy2;
  var y1p = -Math.sin(phi)*dx2 + Math.cos(phi)*dy2;

  var rxs = rX * rX;
  var rys = rY * rY;
  var x1ps = x1p * x1p;
  var y1ps = y1p * y1p;

  var cr = x1ps/rxs + y1ps/rys;
  if (cr > 1) {
    var s = Math.sqrt(cr);
    rX = s * rX;
    rY = s * rY;
    rxs = rX * rX;
    rys = rY * rY;
  }

  var dq = (rxs * y1ps + rys * x1ps);
  var pq = (rxs*rys - dq) / dq;
  var q = Math.sqrt( Math.max(0,pq) );
  if (flagA === flagS)
    q = -q;
  var cxp = q * rX * y1p / rY;
  var cyp = - q * rY * x1p / rX;

  var cx = Math.cos(phi)*cxp - Math.sin(phi)*cyp + (x1 + x2)/2;
  var cy = Math.sin(phi)*cxp + Math.cos(phi)*cyp + (y1 + y2)/2;

  var theta = svgAngle( 1,0, (x1p-cxp) / rX, (y1p - cyp)/rY );

  var delta = svgAngle(
    (x1p - cxp)/rX, (y1p - cyp)/rY,
    (-x1p - cxp)/rX, (-y1p-cyp)/rY);

  delta = delta - Math.PI * 2 * Math.floor(delta / (Math.PI * 2));

  if (!flagS)
    delta -= 2 * Math.PI;

  var n1 = theta, n2 = delta;


  // E(n)
  // cx +acosθcosη−bsinθsinη
  // cy +asinθcosη+bcosθsinη
  function E(n) {
    var enx = cx + rx * Math.cos(phi) * Math.cos(n) - ry * Math.sin(phi) * Math.sin(n);
    var eny = cy + rx * Math.sin(phi) * Math.cos(n) + ry * Math.cos(phi) * Math.sin(n);
    return {x: enx,y: eny};
  }

  // E'(n)
  // −acosθsinη−bsinθcosη
  // −asinθsinη+bcosθcosη
  function Ed(n) {
    var ednx = -1 * rx * Math.cos(phi) * Math.sin(n) - ry * Math.sin(phi) * Math.cos(n);
    var edny = -1 * rx * Math.sin(phi) * Math.sin(n) + ry * Math.cos(phi) * Math.cos(n);
    return {x: ednx, y: edny};
  }

  var en1 = E(n1);
  var en2 = E(n2);
  var edn1 = Ed(n1);
  var edn2 = Ed(n2);

  var alpha = Math.sin(n2 - n1) * (Math.sqrt(4 + 3 * Math.pow(Math.tan((n2 - n1)/2), 2)) - 1)/3;

  console.log(en1, en2);

  return {
    cpx1: en1.x + alpha*edn1.x,
    cpy1: en1.y + alpha*edn1.y,
    cpx2: en2.x - alpha*edn2.x,
    cpy2: en2.y - alpha*edn2.y
  };
}

// M100,100
ctx.moveTo(100,100)
// a45,35 -30 0,1 50,-25
cp = generateBezierPoints(
  45,35,                            // Radii
  -30 * Math.PI / 180,              // xAngle
  0,                                // Large arc flag
  1,                                // Sweep flag
  100,100,                          // Endpoint1
  100 + 50, 100 - 25                // Endpoint2
  );
ctx.bezierCurveTo(cp.cpx1,cp.cpy1,cp.cpx2,cp.cpy2,150,75);
ctx.stroke()

Kam nevojë për ndihmë për të kuptuar se ku po gaboj

PËRDITËSIM:

E kalova postimin disa herë të tjera dhe ka një pjesë të postimit që nuk e kuptoj mirë, e cila gjithashtu mund të mungojë në zbatimin tim.

Gjithçka që më duhej të bëja ishte të ndaja diapazonin e këndit në seksione të vogla për të marrë një përafrim të mirë. Nuk i kuptova fare mirë llogaritjet e gabimeve të gazetës, por gjeta një tjetër letër nga Joe Cridge që tregon ndarjet e π/2 ofron një gabim të mundshëm me një piksel në një pajisje me rezolucion mjaft të lartë. Kështu që zgjodha π/4 për të siguruar animacion të qetë, edhe për harqe të pjesshme në pajisjet celulare me densitet të lartë.

Nuk e kuptoj se çfarë do të thotë autori me nënndarjen e këndeve...


  • Dublikat i mundshëm i Si të merret me saktësinë e numrit të pikës lundruese në JavaScript? 13.05.2017
  • Unë mendoj se është më shumë një problem implementimi sesa saktësia me pikë lundruese e javascript 13.05.2017
  • Nuk i keni lexuar të gjitha formulat tuaja, por a e dini konstruktorin Path2D? jsfiddle.net/fqdhv8jr/2 13.05.2017
  • Po, por për fat të keq nuk mbështetet nga të gjithë shfletuesit dhe sinqerisht doja të shkruaj analizuesin tim të rrugës svg. 13.05.2017
  • @KaranJitSingh është mbushur tashmë shumë: github.com/google/ canvas-5-polyfill/blob/master/canvas.js Testuar në IE9. jsfiddle.net/fqdhv8jr/9 Nëse dëshironi algot, lexoni kodin. 14.05.2017
  • @Kaiido faleminderit! Unë mendoj se kjo duhet të zgjidhë problemin tim. 14.05.2017
  • Pse të mos vizatoni një hark eliptik në kanavacë (CanvasRenderingContext2D) drejtpërdrejt në vend që të përafroni me një kurbë bezier? 21.11.2019

Përgjigjet:


1

Pra, me sa duket një hark eliptik nuk mund të përafrohet me një kurbë të vetme bezier, kështu që duhen kthesa të shumta bezier duke i ndarë të dy këndet në vargje.

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

// M100,350
// a45,35 -30 0,1 50,-25

canvas.width = document.body.clientWidth;
canvas.height = document.body.clientHeight;
ctx.strokeWidth = 2;
ctx.strokeStyle = "#000000";
function clamp(value, min, max) {
  return Math.min(Math.max(value, min), max)
}

function svgAngle(ux, uy, vx, vy ) {
  var dot = ux*vx + uy*vy;
  var len = Math.sqrt(ux*ux + uy*uy) * Math.sqrt(vx*vx + vy*vy);

  var ang = Math.acos( clamp(dot / len,-1,1) );
  if ( (ux*vy - uy*vx) < 0)
    ang = -ang;
  return ang;
}

function generateBezierPoints(rx, ry, phi, flagA, flagS, x1, y1, x2, y2) {
  var rX = Math.abs(rx);
  var rY = Math.abs(ry);

  var dx2 = (x1 - x2)/2;
  var dy2 = (y1 - y2)/2;

  var x1p =  Math.cos(phi)*dx2 + Math.sin(phi)*dy2;
  var y1p = -Math.sin(phi)*dx2 + Math.cos(phi)*dy2;

  var rxs = rX * rX;
  var rys = rY * rY;
  var x1ps = x1p * x1p;
  var y1ps = y1p * y1p;

  var cr = x1ps/rxs + y1ps/rys;
  if (cr > 1) {
    var s = Math.sqrt(cr);
    rX = s * rX;
    rY = s * rY;
    rxs = rX * rX;
    rys = rY * rY;
  }

  var dq = (rxs * y1ps + rys * x1ps);
  var pq = (rxs*rys - dq) / dq;
  var q = Math.sqrt( Math.max(0,pq) );
  if (flagA === flagS)
    q = -q;
  var cxp = q * rX * y1p / rY;
  var cyp = - q * rY * x1p / rX;

  var cx = Math.cos(phi)*cxp - Math.sin(phi)*cyp + (x1 + x2)/2;
  var cy = Math.sin(phi)*cxp + Math.cos(phi)*cyp + (y1 + y2)/2;

  var theta = svgAngle( 1,0, (x1p-cxp) / rX, (y1p - cyp)/rY );

  var delta = svgAngle(
    (x1p - cxp)/rX, (y1p - cyp)/rY,
    (-x1p - cxp)/rX, (-y1p-cyp)/rY);

  delta = delta - Math.PI * 2 * Math.floor(delta / (Math.PI * 2));

  if (!flagS)
    delta -= 2 * Math.PI;

  var n1 = theta, n2 = delta;


  // E(n)
  // cx +acosθcosη−bsinθsinη
  // cy +asinθcosη+bcosθsinη
  function E(n) {
    var enx = cx + rx * Math.cos(phi) * Math.cos(n) - ry * Math.sin(phi) * Math.sin(n);
    var eny = cy + rx * Math.sin(phi) * Math.cos(n) + ry * Math.cos(phi) * Math.sin(n);
    return {x: enx,y: eny};
  }

  // E'(n)
  // −acosθsinη−bsinθcosη
  // −asinθsinη+bcosθcosη
  function Ed(n) {
    var ednx = -1 * rx * Math.cos(phi) * Math.sin(n) - ry * Math.sin(phi) * Math.cos(n);
    var edny = -1 * rx * Math.sin(phi) * Math.sin(n) + ry * Math.cos(phi) * Math.cos(n);
    return {x: ednx, y: edny};
  }

  var n = [];
  n.push(n1);

  var interval = Math.PI/4;

  while(n[n.length - 1] + interval < n2)
    n.push(n[n.length - 1] + interval)

  n.push(n2);

  function getCP(n1, n2) {
    var en1 = E(n1);
    var en2 = E(n2);
    var edn1 = Ed(n1);
    var edn2 = Ed(n2);

    var alpha = Math.sin(n2 - n1) * (Math.sqrt(4 + 3 * Math.pow(Math.tan((n2 - n1)/2), 2)) - 1)/3;

    console.log(en1, en2);

    return {
      cpx1: en1.x + alpha*edn1.x,
      cpy1: en1.y + alpha*edn1.y,
      cpx2: en2.x - alpha*edn2.x,
      cpy2: en2.y - alpha*edn2.y,
      en1: en1,
      en2: en2
    };
  }

  var cps = []
  for(var i = 0; i < n.length - 1; i++) {
    cps.push(getCP(n[i],n[i+1]));
  }


  return cps;
}

// M100,100
ctx.moveTo(100,100)
// a45,35 -30 0,1 50,-25
var rx = 45, ry=35,phi =  -30 * Math.PI / 180, fa = 0, fs = 1, x = 100, y = 100, x1 = x + 50, y1 = y - 25;

  var cps = generateBezierPoints(rx, ry, phi, fa, fs, x, y, x1, y1);

  var limit = 2;

  for(var i = 0; i < limit && i < cps.length; i++) {
    ctx.bezierCurveTo(cps[i].cpx1, cps[i].cpy1,
                      cps[i].cpx2, cps[i].cpy2,
                      i < limit - 1 ? cps[i].en2.x : x1, i < limit - 1 ? cps[i].en2.y : y1);
  }
ctx.stroke()
13.05.2017
Materiale të reja

Masterclass Coroutines: Kapitulli-3: Anulimi i korutinave dhe trajtimi i përjashtimeve.
Mirë se vini në udhëzuesin gjithëpërfshirës mbi Kotlin Coroutines! Në këtë seri artikujsh, unë do t'ju çoj në një udhëtim magjepsës, duke filluar nga bazat dhe gradualisht duke u thelluar në..

Faketojeni derisa ta arrini me të dhënat false
A e gjeni ndonjëherë veten duke ndërtuar një aplikacion të ri dhe keni nevojë për të dhëna testimi që duken dhe duken më realiste ose një grup i madh të dhënash për performancën e ngarkesës...

Si të përdorni kërkesën API në Python
Kërkesë API në GitHub për të marrë depot e përdoruesve duke përdorur Python. Në këtë artikull, unë shpjegoj procesin hap pas hapi për të trajtuar një kërkesë API për të marrë të dhëna nga..

Një udhëzues hap pas hapi për të zotëruar React
Në këtë artikull, do të mësoni se si të krijoni aplikacionin React, do të mësoni se si funksionon React dhe konceptet thelbësore që duhet të dini për të ndërtuar aplikacione React. Learning..

AI dhe Psikologjia — Pjesa 2
Në pjesën 2 të serisë sonë të AI dhe Psikologji ne diskutojmë se si makineritë mbledhin dhe përpunojnë të dhëna për të mësuar emocione dhe ndjenja të ndryshme në mendjen e njeriut, duke ndihmuar..

Esencialet e punës ditore të kodit tim VS
Shtesat e mia të preferuara - Git Graph 💹 Kjo shtesë është vërtet e mahnitshme, e përdor përpara se të filloj të punoj për të kontrolluar dy herë ndryshimet dhe degët më të fundit, mund të..

Pse Python? Zbulimi i fuqisë së gjithanshme të një gjiganti programues
Në peizazhin gjithnjë në zhvillim të gjuhëve të programimit, Python është shfaqur si një forcë dominuese. Rritja e tij meteorike nuk është rastësi. Joshja e Python qëndron në thjeshtësinë,..