Tangente a una elipse con OpenSCAD

Cuando trabajamos con superficies planas y curvas en OpenSCAD es interesante evitar los escalones. En el caso más simple mezclaremos cilindros con planos ortogonales, y calcular las coordenadas de corte es fácil conociendo el radio del cilindro. Podemos encontrar problemas si el número de facetas del cilindro es tal que no coincide una arista con el plano al que hay que unirlo. Eso se soluciona fácilmente haciendo que la cantidad de caras sea múltiplo de 4.

Si el plano no es ortogonal respecto del cilindro podemos calcular el punto de tangencia con algo de trigonometría elemental. Pero cuando el cilindro está deformado y tiene base elíptica tendremos más complicaciones.

En el gráfico adjunto tenemos una elipse que hemos hecho en OpenSCAD mediante un cilindro de radio a con un escalado de b/a en el eje Y. De momento, queremos saber el punto de tangencia de una recta que forma un ángulo v con la vertical. Para ello partiremos de la fórmula de la elipse que relaciona cada uno de sus puntos con sus semiejes:

x2/a2 + y2/b2 = 1

despejando y:

y = ± b*raiz(1-x2/a2) [1]

tomamos el valor positivo de y porque en este caso no nos sirve el negativo, y la derivada será:

y' = - bx/(a2 * raiz(1 - x2 / a2) ) [2]

como la derivada de una función en un punto es su tangente, tenemos que

y' = tan(v-90) = -1/tan(v) [3]

a partir de [2] y [3] despejamos x:

x=raiz(1/((b*tan(v)/a2)2 + 1/a2))

y terminamos resolviendo [1] una vez conocido x para calcular y.

planteamiento geométrico del problema

Puedes probarlo en OpenSCAD con este código:


/* planteamiento:

- sea una elipse proyectada en el eje Z, con semiejes semieje_a y semieje_b y altura alto

y un tarugo de ancho x largo y altura alto

- se trata de poner el tarugo de forma que su lado izquierdo toque a la elipse

con un angulo dado, para lo que necesitamos las coordenadas de tangencia

*/


// suavizar las superficies curvas

$fs= .1 ;

$fa= .1 ;


alto = 10 ;


ancho = 45 ;

largo = 40 ;

angulo = 62 ;


semieje_a = 10 ;

semieje_b =18 ;


color("red") scale([1, semieje_b/semieje_a, 1])

cylinder(r=semieje_a, h=alto, center=true);


// calcular X

X = sqrt(1/(pow(semieje_b*tan(angulo)/pow(semieje_a,2),2)+1/pow(semieje_a,2)));


// trasladar al punto de contacto

translate([X, semieje_b*sqrt(1-pow(X/semieje_a,2)), 0])

// rotar el angulo de tangencia

rotate([0, 0, angulo])

// colocar en [0, 0, 0] el punto que debe tocar la elipse

translate([ancho/2, 0, 0])

// tarugo de partida

cube([ancho, largo, alto], center=true);

También podemos encontrarnos con el problema opuesto: tenemos un plano inclinado en un ángulo v y queremos que se fusione con un plano vertical mediante una superficie elíptica. Podemos fijar a y h como parámetros para modelar el aspecto de la unión, y hallar el b necesario.


Con las expresiones [2] y [3] podemos despejar b:

b = a2 * raiz(1 - x2/a2) / (x * tan(v)) [4]

Sustituyendo en [1] este valor de b y simplificando:

y = (a2 - x2) / (x * tan(v)) [5]

Viendo el gráfico, por trigonometría elemental podemos concluir que:

tan(v) = (a - x) / (y - h)

Si despejamos y podemos igualar con [5]:

y = (a - x) / tan(v) + h = (a2 - x2) / (x * tan(v))

Sólo queda despejar x como función de los parámetros a, h y v

x = a2 / (a + h * tan(v))

Conocido x podemos calcular b con [4], y finalmente y con [1]


planteamiento en la práctica

El siguiente ejemplo es más complejo, porque no se limita a dibujar un óvalo tangente a una recta, sino que ya hace el recorte, restando a un tarugo el óvalo de redondeo, y el resultado se resta al tarugo inicial:


/* planteamiento:

- sea un tarugo de ancho * largo, con un un corte que forma con la vertical un angulo preestablecido

si lo vemos desde arriba (Ctrl+4 con OpenSCAD) veremos que el lado izquierdo del tarugo mide "largo",

y el lado izquierdo mide "h" (a calcular en funcion del angulo)

- queremos suavizar el angulo derecho con un ovalo de anchura arbitraria y con un grado de estiramiento

tal que sea tangente al corte citado


*/


// suavizar las superficies curvas

$fs= .1 ;

$fa= .1 ;


alto = 10 ;


ancho = 45 ;

largo = 40 ;

angulo = 62 ;

a = ancho/3 ; // semieje horizontal del ovalo de redondeo (este valor es arbitrario)

h = largo-ancho/tan(angulo); // este valor es conocido o facilmente calculable


// calculos: X e Y son las coordenadas de contacto, y B el semieje vertical del ovalo

X = pow(a,2)/(a+h*tan(angulo));

B = pow(a,2)*sqrt(1-pow(X/a,2)) / (X * tan(angulo));

Y = B * sqrt(1 - pow(X/a, 2));


difference() {

translate([0, largo/2, 0])

cube([ancho, largo, alto], center = true);

translate([-ancho/2, largo,0])

rotate([0,0,angulo-90])

translate([100/2, 100/2, 0])

cube(100, center = true);

x_origen_ovalo=ancho/2-a;

difference() {

translate([x_origen_ovalo + X + 100/2,Y-100/2,0])

cube(100, center = true);

# translate([x_origen_ovalo,0,0])

scale([1,B/a,1.01]) cylinder(r=a, h=alto, center=true);

}

}

Todavía podemos darle otra vuelta al tema, y plantear la curva de transición con una línea que empieza vertical desde un punto de origen y llega a uno de destino con un ángulo dado.

El desarrollo matemático está en la página laderas con OpenSCAD.


/* Ejemplo de uso de transicion_2D() en el que se plantea la forma de expresar una curva. En un

desarrollo real hace falta unir el cuadrado superior al resto, pero lo complejo queda hecho.

*/


origen = [20, 30];

destino = [40, 60];

angulo = 30;


linear_extrude(4) {

translate(origen - [0, 20])

square(20);


translate(destino)

rotate([0, 0, angulo-90])

square(30);


translate(origen)

transicion_2D(destino - origen, angulo, $fa = 1);

}




/////////////////////////////////////////////////////////////////////////////////////////////////////////

/// MÓDULO PARA HACER TRANSICIONES //////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////////////////////////


// construir una transición desde (0,0) al objetivo marcado, al que debe llegar con el ángulo indicado.

// el parámetro "sobra" se puede usar para extender un poco los bordes rectos, para facilitar las uniones

// ejemplo:

// transicion_2D([20, 35], angulo=36);


module transicion_2D(objetivo, angulo=0, sobra=0, center=false) {

module inviable() { echo("ERROR: TRANSICION INVIABLE!"); }

nada = 1e-6;

exceso = [1,1] * sobra;


if ( objetivo[0]>0 && objetivo[1]>0 )

translate( center ? objetivo/-2 : [0,0] ) {

T = tan(90-(angulo<nada ? nada : angulo));

B = sqrt(pow(T*objetivo[1] - objetivo[0], 2) / (pow(T,2) - 2*objetivo[0]*T/objetivo[1])) ;

A = objetivo[0] / (1-sqrt(1-pow((objetivo[1]>B?B:objetivo[1])/B,2))) ;

if (A+0==A && B+0==B) {

intersection() {

translate(-exceso/2)

square(objetivo + exceso);

translate([A,0])

scale([1,B/A,1])

circle(r=A);

}

} else

inviable();

}

else

inviable();

}