Направляющей линией для цилиндра рундиста является комбинация из трех дуг эллипсов. Программа в работе показана ниже.
function init_girdle()
{
// Параметр vp обозначен как δ на экране при запуске программы.
// При расчете формы рундиста и получении формул используемых
// для этого расчета (полуоси эллипса a и b) использовалось свйство касательной к эллипсу.
// Подробно это рассмотрено в комментариях к программе MarquiseGirdle.html
// g - длина отрезка расположенного между вершиной 0 рундиста огранки и точкой O.
// u - длина отрезка OB
// ширина рундиста принята равной 2
// g + u = 2*lw
// Lh = g / 2*lw
// Lh задает соотношение величин g и u.
var i;
// Введем переменную v
v = lw * Lh - 0.5;
// тогда размер полуоси эллипса ellipse_3 по оси OY равен величине
g = 1.0 + v + v;
if ( g < 0 )
return null;
u = 2*lw - g; // u - длина отрезка OB (на экране при запуске программы)
if ( u < 0 )
return null;
fi = Math.asin ( ( u + u ) / ( u * u + 1.0) );
psi = fi + vp; // vp (на экране это угол δ) - определяет величину отличия формы эллипса от окружности
t = u * Math.tan(psi); // на экране это длина отрезка OM
if ( vp >= Math.PI/2 )
return null;
if ( t <= 2.0)
return null;
// a и b - полуоси эллипса (на экране a < b по модулю)
a = ( t - 1.0 ) / ( t - 2.0 );
s = a - 1.0;
b = u * ( 1.0 - t ) / Math.sqrt ( t * t - ( t + t ) );
// Расстановка вершин на линии рундиста.
// Переменные AA, BB и CC определяют уравнение AA*xx + BB*y + CC = 0
var AA, BB, CC;
var angle_current;
var rez = [2];
var delta, x, y, w, x_rez, y_rez;
var bRez;
if (DelAngGd_16 <= 0.0)
{
// Сдвиг центральной точки рундиста в сторону
// противоположную от носика груши.
var ang_0_16 = M_PI_2 + DelAngGd_16;
var E = new Point2D(Math.sin(ang_0_16), Math.cos(ang_0_16));
var alpha = Math.atan2(E[1], (E[0] + s));
// Вершины в квадранте (X > 0; Y > 0)
var ang_0_8 = ang_0_16 / 2 + DelAngGirdle_8;
var ang_8_16 = ang_0_16 - ang_0_8;
var ang_0_4 = ang_0_8 / 2 + DelAngGirdle_4;
var ang_4_8 = ang_0_8 - ang_0_4;
var ang_8_12 = ang_8_16 / 2 + DelAngGirdle_12;
var ang_12_16 = ang_8_16 - ang_8_12;
angle_current = 0.0;
for ( i = 0; i <= 16; i++ )
{
x = Math.sin(angle_current);
y = Math.cos (angle_current);
girdle[i] = new Point2D(x, y * g);
if (i < 4)
delta = ang_0_4 / 4;
else if (i < 8)
delta = ang_4_8 / 4;
else if (i < 12)
delta = ang_8_12 / 4;
else
delta = ang_12_16 / 4;
angle_current = angle_current + delta;
}
var v16_1_x = girdle[16][0];
var v16_1_y = girdle[16][1];
// Вершины в квадранте (X > 0; Y < 0)
var ang_32 = Math.acos(s / a);
var ang_16 = - alpha;
var ang_24 = ang_32 - (ang_32 - ang_16 )/ 2.0 + DelAngGirdle_24;
var ang_20 = ang_24 - (ang_24 - ang_16) / 2.0 + DelAngGirdle_20;
var ang_28 = ang_32 - (ang_32 - ang_24) / 2.0 + DelAngGirdle_28;
var j = 31;
angle_current = ang_32;
for ( i = 15; i >= 0; i-- )
{
if (i < 4)
delta = (ang_20 - ang_16) / 4.0;
else if ( (i >= 4) && (i < 8) )
delta = (ang_24 - ang_20) / 4.0;
else if ( (i >= 8) && (i < 12) )
delta = (ang_28 - ang_24) / 4.0;
else
delta = (ang_32 - ang_28) / 4.0;
angle_current = angle_current - delta;
if (angle_current <= 0)
{
// x и y - координаты точки L
x = a * Math.cos(angle_current) - s;
y = b * Math.sin(angle_current);
// k - угловой коэффициент прямой DL
var k = y/(x+s);
// Находим координаты точки K,
// лежащей на пересечении прямой DL с Ellipse_1
AA = g*g + k*k;
BB = 2*k*k*s;
CC = s*s*k*k - g*g;
var x_rez, x_rez; // координаты точки K
if(!QuadraticEquation(AA, BB, CC, rez))
{
return null;
}
if (rez[0] > rez[1])
x_rez = rez[0];
else
x_rez = rez[1];
y_rez = (x_rez + s)*k;
girdle[j] = new Point2D(x_rez, y_rez);
j--;
}
else
{
girdle[j] = new Point2D();
girdle[j][0] = a * Math.cos(angle_current) - s;
girdle[j][1] = b * Math.sin(angle_current);
j--;
}
}
girdle[16] = new Point2D(v16_1_x, v16_1_y);
}
else
{
// DelAngGd_16 > 0.0
// Сдвиг центральной точки рундиста в сторону носика груши.
// Определяем координаты точки N (g16)
var N = new Point2D(a * Math.cos(DelAngGd_16) - s, b * Math.sin(DelAngGd_16));
// Находим точку M пересечения прямой ON с Ellipse_1
var k = N[1]/N[0];
var beta = Math.atan2(-N[1], N[0]);
var ang_0_16 = M_PI/2 + beta;
// Вершины g0 - g16
var ang_0 = 0.0;
var ang_8 = ang_0_16 / 2.0 + DelAngGirdle_8;
var ang_4 = ang_0 + (ang_8 - ang_0) / 2.0 + DelAngGirdle_4;
var ang_12 = ang_8 + (ang_0_16 - ang_8) / 2.0 + DelAngGirdle_12;
var ang_16 = ang_0_16;
angle_current = 0.0;
for ( i = 0; i <= 15; i++ )
{
if (i <= 4)
delta = (ang_4 - ang_0) / 4.0;
else if ( (i >= 5) && (i <= 8) )
delta = (ang_8 - ang_4) / 4.0;
else if ( (i >= 9) && (i <= 12) )
delta = (ang_12 - ang_8) / 4.0;
else
delta = (ang_16 - ang_12) / 4.0;
// Это могут быть и координаты точки R
// если окжется, что angle_current > M_PI_2
var x = Math.sin(angle_current);
var y = g*Math.cos(angle_current);
if (angle_current > M_PI_2)
{
// Находим точку P пересечения
// прямой OR с Ellipse_2
k = y / x;
AA = b*b + a*a*k*k;
BB = 2*b*b*s;
CC = b*b*s*s - a*a*b*b;
var x_rez, y_rez;
if(!QuadraticEquation(AA, BB, CC, rez))
{
return null;
}
if (rez[0] > rez[1])
x_rez = rez[0];
else
x_rez = rez[1];
y_rez = k * x_rez;
girdle[i] = new Point2D(x_rez, y_rez);
}
else
{
var w = Math.pow (Math.abs(x), 2) + Math.pow (Math.abs(y), 2);
w = 1.0 / Math.pow(w, 1.0 / 2);
girdle[i] = new Point2D(w * x, w * y * g); // ?? что лучше
//girdle[i] = new Point2D(x, y * g); // ?? что лучше
}
angle_current = angle_current + delta;
}
// Вершины g16 - g32
var ang_32 = Math.acos(s / a);
ang_16 = DelAngGd_16;
var ang_24 = ang_32 - (ang_32 - ang_16 )/ 2.0 + DelAngGirdle_24;
var ang_20 = ang_24 - (ang_24 - ang_16) / 2.0 + DelAngGirdle_20;
var ang_28 = ang_32 - (ang_32 - ang_24) / 2.0 + DelAngGirdle_28;
var j = 31;
angle_current = Math.acos(s / a);
for ( i = 16; i > 0; i-- )
{
if (i <= 4)
delta = (ang_20 - ang_16) / 4.0;
else if ( (i > 4) && (i <= 8) )
delta = (ang_24 - ang_20) / 4.0;
else if ( (i > 8) && (i <= 12) )
delta = (ang_28 - ang_24) / 4.0;
else
delta = (ang_32 - ang_28) / 4.0;
angle_current = angle_current - delta;
girdle[j] = new Point2D();
girdle[j][0] = a * Math.cos(angle_current) - s;
girdle[j][1] = b * Math.sin(angle_current);
j--;
}
var uuu = 10;
}
// Остальные вершины
girdle[32] = new Point2D(0.0, -u);
for ( i = 1; i < 32; ++ i )
{
girdle[i+32] = new Point2D(- girdle[32-i][0], girdle[32-i][1]);
}
// Если не требуется настраивать размер сегментов рундиста, то тогда
// линия рундиста в квадранте (X > 0; Y > 0) просто делится на 16 равных частей.
// Линия рундиста в квадранте (X > 0; Y < 0) также делится также на 16 равных частей.
/*
var angle_current;
var x, y, w;
var bRez;
var delta = Math.PI/32;
// Вершины в квадранте (X > 0; Y > 0)
angle_current = 0.0;
for ( i = 0; i <= 16; i++ )
{
x = Math.sin(angle_current);
y = Math.cos (angle_current);
girdle[i] = new Point2D( x, y * g);
angle_current = angle_current + delta;
}
// Вершины в квадранте (X > 0; Y < 0)
var ang_32 = Math.acos (s / a);
angle_current = 0;
delta = ang_32/16;
for ( i = 1; i < 17; i++)
{
angle_current = angle_current + delta;
girdle[i+16] = new Point2D(a * Math.cos(angle_current) - s, b * Math.sin(angle_current));
}
girdle[32][0] = 0;
girdle[32][1] = -u;
for (i = 1; i < 32; i++)
{
girdle[32 + i] = new Point2D( -girdle[32 - i][0], girdle[32 - i][1] );
}
*/
}