Ésta es una tarea difícil de hacer manualmente mediante la colocación visual círculos a lo largo del camino de la carta.
Es aún más difícil de forma automática (automágicamente!) Que sin la intervención humana.
Aquí se explica cómo organizar automáticamente círculos para formar letras.
La respuesta está en 2 partes ...
Encontrar el "letterform",
Creación de círculos de relleno y contorno del letterform.
1. La parte difícil
Frederik De Bleser ha codificado una bonita biblioteca llamada opentype.js
que toma un archivo de fuentes .ttf y analiza a cabo perfil del glifo de cualquier carácter especificado utilizando curvas cuadráticas sobre un lienzo: https://github.com/nodebox/opentype.js
2. La única parte un poco menos dura
Para cada letra:
Encuentra "muchos" puntos en cada curva cuadrática. Aquí está el algoritmo para calcular un [x, y] en la curva en un intervalo T. T va desde 0.00 al comienzo de la curva hasta 1.00 al final de la curva. T no producirá [x, y] espaciados uniformemente a lo largo de la curva, por lo que tendrá que sobremuestrear (por lo que "muchos" podría significar 1000 valores de T entre 0.00 y 1.00).
function getQuadraticBezierXYatT(startPt,controlPt,endPt,T) {
var x = Math.pow(1-T,2) * startPt.x + 2 * (1-T) * T * controlPt.x + Math.pow(T,2) * endPt.x;
var y = Math.pow(1-T,2) * startPt.y + 2 * (1-T) * T * controlPt.y + Math.pow(T,2) * endPt.y;
return({x:x,y:y});
}
encontrar el ángulo que es tangente a la curva de ángulo en esos puntos. (Básicamente, calcule cuál sería el ángulo recto de la curva). Puede hacerlo con el siguiente derivado de la fórmula cuadrática:
function quadraticBezierTangentAngle(t, p0, p2, p1) {
var tt = 1 - t;
var dx = (tt * p1.x + t * p2.x) - (tt * p0.x + t * p1.x);
var dy = (tt * p1.y + t * p2.y) - (tt * p0.y + t * p1.y);
return Math.tan(Math.atan2(dy,dx));
}
Comenzando por el principio de la curva, el cálculo de cada distancia de la corriente [x, y] a la siguiente [x, y].Puede hacer esto con el teorema de Pitágoras:
var dx=nextX-currentX;
var dy=nextY-currentY;
var distance=Math.sqrt(dx*dx+dy*dy);
De-duplicar la matriz de modo que toda la restante [x, y] elementos son 1px distante del elemento anterior [x, y]. Puede hacerlo rellenando un segundo conjunto con valores del primero donde parseInt(nextInOriginalArray - lastDistanceInNewArray)==1;
Decida el radio de los círculos que conformarán cada letra. Esto es realmente más difícil de lo que parece. Para fuentes "en bloque", puede dibujar la letra "I" en el lienzo. A continuación, busque todos los píxeles usando getImageData
. Calcule el ancho del trazo vertical de "I" buscando el recuento de píxeles opacos que se ejecutan horizontalmente en el medio vertical de la letra. Para tipos de letra blocky, var radius = horizontalOpaquePixelCount/2;
. Para las fuentes con trazos de anchura variable, tendrá que ser inventivo. Tal vez var radius = horizontalOpaquePixelCount/3;
o var radius = horizontalOpaquePixelCount/4;
.
Iterate a través de la matriz de puntos y define un nuevo círculo cada radius*2
píxeles. Se calcula el punto central de cada círculo usando el ángulo tangente y la trigonometría como esto:
var centerX = curvePointX + radius*Math.cos(tangentAngle);
var centerY = curvePointY + radius*Math.sin(tangentAngle);
bien la creación de círculos, en algún momento las curvas de la carta se volverán sobre sí mismos, por lo que debe comprobar cada nuevo círculo se crea para estar seguro de que no se superpondrá a un círculo existente. Se puede calcular si un nuevo círculo se cruzará cada círculo existente como esto:
var dx = newCircleCenterX - existingCircleCenterX;
var dy = newCircleCenterY - existingCircleCenterY;
var distance=Math.sqrt(dx*dx+dy*dy);
var circlesAreIntersecting=(distance<=newCircleRadius+existingCircleRadius);
Puesta a punto: Cerca algunos puntos puntos finales en el camino de la carta, se encuentra que la siguiente círculo de radio completo se derrame fuera de la forma de letra. Si eso ocurre, podría reducir el radio de algunos círculos para que se ajuste a la forma de la letra. Si solo quiere un radio fijo para sus círculos, puede volver a calcular el radio fijo de todos los círculos en función de los radios promedio de todos los círculos, incluidos los que tenía que "encoger" para ajustarse a la forma de la letra.
Por ejemplo. Esta es la letra "L formado por 15 círculos.
Pero los 2 círculos rojos caen fuera de su letterform. Se podría (1) reducir el tamaño de los círculos rojos para encajar dentro del letterform o (2) volver a calcular un nuevo círculo fijo radios basado en los radios promedio que se adapta a la letterform:
var total=0;
total += greenRadii * 13;
total += verticalRedRadiusResizedToFitInsideLetterform;
total += horizontalRedRadiusResizedToFitInsideLetterform;
var newRadius = total/15;
se puede calcular la longitud del radio rojo que se ajuste a la letterform mediante el cálculo de la intersección de 2 líneas: (1) el segmento de línea formado conectando el último centro de círculos verdes y el centro de círculos rojos, (2) la línea formada perpendicularmente m el último punto de la curva. Aquí hay un algoritmo para calcular el punto de 2 líneas de intersección:
// Get interseting point of 2 line segments (if any)
// Attribution: http://paulbourke.net/geometry/pointlineplane/
function line2lineIntersection(p0,p1,p2,p3) {
var unknownA = (p3.x-p2.x) * (p0.y-p2.y) - (p3.y-p2.y) * (p0.x-p2.x);
var unknownB = (p1.x-p0.x) * (p0.y-p2.y) - (p1.y-p0.y) * (p0.x-p2.x);
var denominator = (p3.y-p2.y) * (p1.x-p0.x) - (p3.x-p2.x) * (p1.y-p0.y);
// Test if Coincident
// If the denominator and numerator for the ua and ub are 0
// then the two lines are coincident.
if(unknownA==0 && unknownB==0 && denominator==0){return(null);}
// Test if Parallel
// If the denominator for the equations for ua and ub is 0
// then the two lines are parallel.
if (denominator == 0) return null;
// If the intersection of line segments is required
// then it is only necessary to test if ua and ub lie between 0 and 1.
// Whichever one lies within that range then the corresponding
// line segment contains the intersection point.
// If both lie within the range of 0 to 1 then
// the intersection point is within both line segments.
unknownA /= denominator;
unknownB /= denominator;
var isIntersecting=(unknownA>=0 && unknownA<=1 && unknownB>=0 && unknownB<=1)
if(!isIntersecting){return(null);}
return({
x: p0.x + unknownA * (p1.x-p0.x),
y: p0.y + unknownA * (p1.y-p0.y)
});
}
posible duplicado de [intersecciones de texto lona Html5] (http://stackoverflow.com/questions/19954058/html5-canvas-text- intersecciones) –