2012-01-03 11 views
6

Estoy desarrollando una aplicación donde los usuarios dibujan construcciones euclidianas en el lienzo de HTML5. Como tal, no puedo realmente limitar el tamaño de ciertas formas. Al analizar círculos muy grandes dibujados en la pantalla, noté que los círculos muy grandes no tienen un radio constante.Círculo de lienzo HTML5 muy grande impreciso

Para ser más específicos, un círculo definido por dos puntos, un punto central y uno que especifique el radio ya no pasa por el punto del radio.

Large circle with radius point

círculos Progressivly más grandes. Se supone todo para pasar a través del punto E.

Larger circles

el error no occure en múltiplos de 45 grados = PI/4. Entre estos múltiplos el error es mayor (PI/8, por ejemplo)

Aquí está una jsFiddle que contiene el primer ejemplo anterior:

http://jsfiddle.net/D28J2/2/

Mis preguntas: ¿Por qué este aparecer dentro? y ¿hay alguna manera de (eficientemente) solucionar esto?

+1

en Chrome 16.0.912.63 en MacOS X 10.7.2 el círculo en su violín _touches_ pero no pasa por el punto requerido. – Alnitak

+1

Interesante, estoy en Chrome 16.0.912.63 Windows 7 por cierto. Este problema también ocurrió en Chrome en Linux. En Firefox Windows 7 se produce el mismo error, pero es un orden de magnitud menor (solo cuando se nota r = 100 000). En IE 9 el error es aún menor (notable en r = 1 000 000). Todas estas pruebas se realizan con alpha = PI/8 –

Respuesta

3

La forma en que trabajé este problema por completo fue rodar mi propia implementación de una aproximación circular con curvas Bezier. Un artículo que detalla la implementación se puede encontrar aquí http://www.tinaja.com/glib/ellipse4.pdf.

function magic_circle(ctx, x, y, r){ 
    m = 0.551784 

    ctx.save() 
    ctx.translate(x, y) 
    ctx.scale(r, r) 

    ctx.beginPath() 
    ctx.moveTo(1, 0) 
    ctx.bezierCurveTo(1, -m, m, -1, 0, -1) 
    ctx.bezierCurveTo(-m, -1, -1, -m, -1, 0) 
    ctx.bezierCurveTo(-1, m, -m, 1, 0, 1) 
    ctx.bezierCurveTo(m, 1, 1, m, 1, 0) 
    ctx.closePath() 
    ctx.restore() 
} 

Con sólo estas cuatro segements que fue capaz de aproximarse a un círculo mucho mejor que la estructura en Google Chrome aplicación de lona.

+1

Esto es mucho mejor que el método predeterminado de arc() para dibujar solo círculos. Reemplacé CanvasRenderingContext2D.prototype.arc con esto y arreglé todo tipo de artefactos extraños y glitching. – mclaassen

0

En Google Chrome puedo repetir el problema pero en IE 9 e IE 10 todo está bien.

Así que mi suposición es que la implementación es incorrecta en Chrome. Puede ser un error de redondeo o utilizaron un método de interpolación para evitar el pecado y el cos que no es muy preciso.

Mire aquí también: HTML5 canvas arcs not rendering correctly in Google Chrome

El único trabajo en todo lo que puedo imaginar es el dibujo del círculo por su propio código o el uso de un (jQuery?) Plug-in que lo hace por usted.

2

Este es probablemente un error de punto flotante. Posiblemente porque el seno y el coseno no están dando valores perfectamente precisos. Puede sortearlo (al menos en Chrome) girando el lienzo en lugar del arco.

ctx.save();   // Save the canvas so we can rotate back. 
ctx.translate(x, y); // Translate to the origin point. 
ctx.rotate(alpha); // Rotate the proper angle. 

ctx.arc(0, 0, 3, 0, Math.PI*2); // Draw the small circle at the origin. 
ctx.fill(); 

ctx.arc(r, 0, r, 0, Math.PI*2); // Create a big with the origin 1 radius away. 
ctx.restore();     // Restore the canvas to the original orientation 
           // before drawing. Otherwise the circle looks bad. 
ctx.strokeStyle = "black"; 
ctx.stroke();     // Draw! 

Soy un gran admirador de la manipulación del lienzo en lugar de las formas. Le da un área más lógica para trabajar. Ver http://jsfiddle.net/D28J2/10/

+1

¡Guau, eso es genial! Sin embargo, eso no soluciona exactamente mi problema. Necesito que el círculo sea "perfecto" en todas partes, no solo un punto arbitrario. –

+0

El círculo debería ser perfecto en todas partes. El problema probablemente no estaba en el radio del círculo sino en la imprecisión matemática de 'Math.sin' y' Math.cos' que se exageran cuando se multiplican por una constante grande. Dicho esto, tendrías problemas para colocar cualquier punto usando el método original, así que si colocas un segundo punto en x + 2 * cos (a), y + 2 * sin (a) no se alineará. Para obtener un gráfico verdaderamente preciso, debe hacer todos los dibujos sobre el lienzo girado. –

+1

El problema es que 'Math.cos' y' Math.sin' son implementados por algunas series taylor en V8. Para fines de optimización, estas series de taylor no tienen un orden muy alto, por lo tanto, al dibujar círculos muy grandes, hay errores. –

1

¿Simplemente lanzando esto pero podría ser un problema sin especificar suficientes dígitos de PI? Cada vez que hago cosas así, tiendo a ir un poco por la borda y uso unos 10 dígitos de PI.