Глава 16  ◄     Содержание     ►   Глава 18

Three.js и геометрия.   Глава 17.   Построение рундиста огранки имеющей рундист в форме груши (pear)

Направляющей линией для цилиндра рундиста является комбинация из трех дуг эллипсов. Программа в работе показана ниже.

Построение и отрисовка на холсте кривой рундиста груши (pear)

Расстановка вершин на линии рундиста

Исходный текст программы расчета координат вершин кривой рундиста огранки груша

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] );
	}
*/
}

   Глава 16  ◄     Содержание     ►   Глава 18