Para una aplicación de dibujo, guardo las coordenadas de movimiento del mouse en una matriz y luego las dibujo con lineTo. La línea resultante no es suave. ¿Cómo puedo producir una sola curva entre todos los puntos reunidos?
Busqué en Google pero solo encontré 3 funciones para dibujar líneas: para 2 puntos de muestra, simplemente use lineTo
. Para 3 puntos de muestra quadraticCurveTo
, para 4 puntos de muestra, bezierCurveTo
.
(Traté de dibujar un bezierCurveTo
por cada 4 puntos en la matriz, pero esto conduce a torceduras cada 4 puntos de muestra, en lugar de una curva suave continua).
¿Cómo escribo una función para dibujar una curva suave con 5 puntos de muestra y más?
Respuestas:
El problema con la unión de puntos de muestra posteriores junto con funciones de tipo "curveTo" disjuntas, es que donde las curvas se encuentran no es suave. Esto se debe a que las dos curvas comparten un punto final pero están influenciadas por puntos de control completamente disjuntos. Una solución es "curvarse" a los puntos medios entre los siguientes 2 puntos de muestra posteriores. Unir las curvas usando estos nuevos puntos interpolados proporciona una transición suave en los puntos finales (lo que es un punto final para una iteración se convierte en un punto de control para la siguiente iteración). En otras palabras, las dos curvas desunidas tienen mucho más en común ahora.
Esta solución fue extraída del libro "Animación Foundation ActionScript 3.0: Hacer que las cosas se muevan". p.95 - técnicas de renderizado: creación de múltiples curvas.
Nota: esta solución en realidad no atraviesa cada uno de los puntos, que era el título de mi pregunta (más bien se aproxima a la curva a través de los puntos de muestra pero nunca pasa por los puntos de muestra), pero para mis propósitos (una aplicación de dibujo), es lo suficientemente bueno para mí y visualmente no puedes notar la diferencia. No es una solución que pasar por todos los puntos de muestra, pero se complica mucho más (ver http://www.cartogrammar.com/blog/actionscript-curves-update/ )
Aquí está el código de dibujo para el método de aproximación:
fuente
Un poco tarde, pero para que conste.
Puede lograr líneas suaves utilizando splines cardinales (también conocido como spline canónico) para dibujar curvas suaves que atraviesen los puntos.
Hice esta función para el lienzo: se divide en tres funciones para aumentar la versatilidad. La función de contenedor principal se ve así:
Para dibujar una curva de tener una matriz con x, puntos de Y en el orden:
x1,y1, x2,y2, ...xn,yn
.Úselo así:
La función anterior llama a dos subfunciones, una para calcular los puntos suavizados. Esto devuelve una matriz con nuevos puntos: esta es la función principal que calcula los puntos suavizados:
Y para dibujar realmente los puntos como una curva suavizada (o cualquier otra línea segmentada siempre que tenga una matriz x, y):
Mostrar fragmento de código
Esto da como resultado esto:
Puede extender fácilmente el lienzo para poder llamarlo así:
Agregue lo siguiente al javascript:
Puede encontrar una versión más optimizada de esto en NPM (
npm i cardinal-spline-js
) o en GitLab .fuente
ptsa
debería serpts
, o de lo contrario arrojaría errores.La primera respuesta no pasará por todos los puntos. Este gráfico pasará exactamente por todos los puntos y será una curva perfecta con los puntos como [{x:, y:}] en dichos puntos.
fuente
Como señala Daniel Howard , Rob Spencer describe lo que quiere en http://scaledinnovation.com/analytics/splines/aboutSplines.html .
Aquí hay una demostración interactiva: http://jsbin.com/ApitIxo/2/
Aquí es como un fragmento en caso de que jsbin esté inactivo.
fuente
Encontré que esto funciona bien
fuente
Decido agregar, en lugar de publicar mi solución en otra publicación. A continuación, se muestra la solución que construyo, puede que no sea perfecta, pero hasta ahora la salida es buena.
Importante: ¡ pasará por todos los puntos!
Si tienes alguna idea, para mejorarla , compártela. Gracias.
Aquí está la comparación de antes después:
Guarde este código en HTML para probarlo.
fuente
Pruebe KineticJS: puede definir una Spline con una variedad de puntos. Aquí hay un ejemplo:
URL antigua: http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-spline-tutorial/
Consulte la URL del archivo: https://web.archive.org/web/20141204030628/http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-spline-tutorial/
fuente
Increíblemente tarde, pero inspirado por la respuesta brillantemente simple de Homan, me permite publicar una solución más general (general en el sentido de que la solución de Homan se bloquea en conjuntos de puntos con menos de 3 vértices):
fuente
Para agregar al método de splines cardinales de K3N y tal vez abordar las inquietudes de TJ Crowder sobre las 'inmersiones' de curvas en lugares engañosos, inserté el siguiente código en la
getCurvePoints()
función, justo antesres.push(x);
Esto crea efectivamente un cuadro delimitador (invisible) entre cada par de puntos sucesivos y garantiza que la curva permanezca dentro de este cuadro delimitador, es decir. Si un punto en la curva está arriba / abajo / izquierda / derecha de ambos puntos, altera su posición para estar dentro del cuadro. Aquí se usa el punto medio, pero esto podría mejorarse, quizás usando interpolación lineal.
fuente
Si desea determinar la ecuación de la curva a través de n puntos, el siguiente código le dará los coeficientes del polinomio de grado n-1 y guardará estos coeficientes en la
coefficients[]
matriz (a partir del término constante). Las coordenadas x no tienen que estar en orden. Este es un ejemplo de un polinomio de Lagrange .fuente