// Copyright 2018-2024, Vladimir Kuptsov
include('\drawObj.js');

function sDraw() {
	
}

sDraw.createArcBy2PointsAndRadius = function(x1,y1,x2,y2, radius, pos){
	//Находим расстояние между точками, длину хорды дуги
	var chord_length = calcLengthBy2Points(x1,y1,x2,y2);
	//Находим угол прямоугольного треугольника 1-3-0
	var alpha = Math.radToDeg(Math.acos((chord_length/2)/radius));
	//Создаем объект линии хорды и находим ее угол
	var line_1_2 = this.createLineBy2Points(x1,y1,x2,y2);
	var angle_1_2 = Math.radToDeg(line_1_2.angleRad);
	//Определяем расположение центральной точки относительно точки 1 (pos = 1 - положительное вращение, 0 - отрицательное) 
	if(pos){
		var delta_1 = angle_1_2 - alpha;
		var delta_2 = angle_1_2 + 180 + alpha;
	}else{
		var delta_1 = angle_1_2 + alpha;
		var delta_2 = angle_1_2 + 180 - alpha;
	}
	//Находим линии и точку через пересечение этих линий
	var line_1 = this.createLineByPointAngle(x1,y1, delta_1);
	var line_2 = this.createLineByPointAngle(x2,y2, delta_2);
	var point_0 = this.intersection(line_1, line_2, 'higher');
	return new Arc(Math.radToDeg(line_2.angleRad) - 180, Math.radToDeg(line_1.angleRad) - 180, radius, point_0.x(), point_0.y());
}

sDraw.createLineBy2Points = function(x0,y0,x1,y1) {
	var angleDeg;
	if (Math.abs(x1-x0) == 0) {
		angleDeg = 90;
	}else{
		angleDeg = Math.atan2(y1-y0, x1-x0) * 180 / Math.PI;
	}
	return new Line(x0,y0,angleDeg);
}

sDraw.createLineByPointAngle = function(x0,y0,angleDeg) {
	return new Line(x0,y0,angleDeg);
}

sDraw.createLineParallel = function(line, distance, position) {
	// create line parallel to "line" on the given distance 
	var alfa = line.angleRad;
	var x0 = line.x0;
	var y0 = line.y0;
	var dx = distance*Math.sin(alfa);
	var dy = distance*Math.cos(alfa);
	var x1 = x0 - dx;
	var y1 = y0 + dy;
	var x2 = x0 + dx;
	var y2 = y0 - dy;
	if (position == 'left') {
		if (x1 > x2) {
			var x = x2; var y = y2;
		}
		else {
			var x = x1; var y = y1;
		}
	}
	else if (position == 'right') {
		if (x1 < x2) {
			var x = x2; var y = y2;
		}
		else {
			var x = x1; var y = y1;
		}
	}
	else if (position == 'lower') {
		if (y1 > y2) {
			var x = x2; var y = y2;
		}
		else {
			var x = x1; var y = y1;
		}
	}
	else if (position == 'higher') {
		if (y1 < y2) {
			var x = x2; var y = y2;
		}
		else {
			var x = x1; var y = y1;
		}
	}
	else {return null;}
	var sdraw = new Line(x,y,alfa * 180 / Math.PI);
	return sdraw;
}

sDraw.createCircle = function(x0,y0,radius) {
	return new Circle(x0,y0,radius);
}

sDraw.intersection = function(sdraw1, sdraw2, position) {
	if (sdraw1.radius && sdraw2.radius) {                  // intersection of two circles
		var r1 = sdraw1.radius;
		var xc1 = sdraw1.x0;
		var yc1 = sdraw1.y0;
		var r2 = sdraw2.radius;
		var xc2 = sdraw2.x0;
		var yc2 = sdraw2.y0;
		var itype = 3;
	} 
	else if (sdraw1.radius) {                               // intersection of line and circle
		var r = sdraw1.radius;
		var xc = sdraw1.x0;
		var yc = sdraw1.y0;
		var x0 = sdraw2.x0;
		var y0 = sdraw2.y0;
		var angleRad = sdraw2.angleRad;
		var itype = 1;
	}         
	else if (sdraw2.radius) {                               // intersection of line and circle
		var r = sdraw2.radius;
		var xc = sdraw2.x0;
		var yc = sdraw2.y0;
		var x0 = sdraw1.x0;
		var y0 = sdraw1.y0;
		var angleRad = sdraw1.angleRad;
		var itype = 1;
	}               
	else {                                                  // intersection of two lines
		var x1 = sdraw1.x0;
		var y1 = sdraw1.y0;
		var angleRad1 = sdraw1.angleRad;
		var x2 = sdraw2.x0;
		var y2 = sdraw2.y0;
		var angleRad2 = sdraw2.angleRad;
		var itype = 2;
	}
	
	if (itype == 1)         // intersection of line and circle
	{
		if (Math.abs(Math.cos(angleRad)) < 1e-10) {
			var a=1; var b=0; var c=xc-x0;
		}
		else {
			var a=Math.tan(angleRad); var b=-1; var c=(y0-yc)-(x0-xc)*Math.tan(angleRad);
		}
		var xclosest=-a*c/(a*a+b*b); var yclosest=-b*c/(a*a+b*b);
		if (c*c > r*r*(a*a+b*b)) {
			return null;            // no intersection
		} 
		else {
			var d = r*r - c*c/(a*a+b*b);
			var mult = Math.sqrt(d / (a*a+b*b));
			if (mult < 1e-12) {           // only one intersection point
				var x = xclosest;
				var y = yclosest;
				x = x + xc;
				y = y + yc;
			}
			else {
				var x1 = xclosest + b * mult;
				var x2 = xclosest - b * mult;
				var y1 = yclosest - a * mult;
				var y2 = yclosest + a * mult;
				x1 = x1 + xc; x2 = x2 + xc;
				y1 = y1 + yc; y2 = y2 + yc;
				if (position == 'left') {
					if (x1 > x2) {
						var x = x2; var y = y2;
					}
					else {
						var x = x1; var y = y1;
					}
				}
				else if (position == 'right') {
					if (x1 < x2) {
						var x = x2; var y = y2;
					}
					else {
						var x = x1; var y = y1;
					}
				}
				else if (position == 'lower') {
					if (y1 > y2) {
						var x = x2; var y = y2;
					}
					else {
						var x = x1; var y = y1;
					}
				}
				else if (position == 'higher') {
					if (y1 < y2) {
						var x = x2; var y = y2;
					}
					else {
						var x = x1; var y = y1;
					}
				}
				else if (position.length == 2) {
					// position defined as a range between two points
					var x1_range = position[0].x();
					var y1_range = position[0].y();
					var x2_range = position[1].x();
					var y2_range = position[1].y();
					if ((x1 >= x1_range && x1 <= x2_range) || (x1 >= x2_range && x1 <= x1_range)) {
						if ((y1 >= y1_range && y1 <= y2_range) || (y1 >= y2_range && y1 <= y1_range)) {
							var xypoint = new QPointF(x1, y1);
							return xypoint;
						}
					}
					if ((x2 >= x1_range && x2 <= x2_range) || (x2 >= x2_range && x2 <= x1_range)) {
						if ((y2 >= y1_range && y2 <= y2_range) || (y2 >= y2_range && y2 <= y1_range)) {
							var xypoint = new QPointF(x2, y2);
							return xypoint;
						}
					}
					return null;
				}
				else {return null;}
			}
		}
	}	
	else if (itype == 2)                   // intersection of two lines
	{
		if (angleRad1 == angleRad2 || angleRad1 + Math.PI == angleRad2 || angleRad1 == angleRad2 + Math.PI) {
			return null;    // parallel lines
		} 
		else if (Math.abs(Math.cos(angleRad1)) >= 1e-10 && Math.abs(Math.cos(angleRad2)) >= 1e-10) {
			var x = (x1*Math.tan(angleRad1)-x2*Math.tan(angleRad2)+y2-y1)/(Math.tan(angleRad1)-Math.tan(angleRad2));
			var y = (x-x2)*Math.tan(angleRad2)+y2;
		}
		else if (Math.abs(Math.cos(angleRad1)) < 1e-10) {
			var x = x1;
			var y = (x-x2)*Math.tan(angleRad2)+y2;
		}
		else if (Math.abs(Math.cos(angleRad2)) < 1e-10) {
			var x = x2;
			var y = (x-x1)*Math.tan(angleRad1)+y1;
		}
	}
	else if (itype == 3)                  // intersection of two circles
	{
		var K = r2*r2 - r1*r1 + (xc1 - xc2)*(xc1 - xc2) + (yc1 - yc2)*(yc1 - yc2);
		var a = -4*((xc1 - xc2)*(xc1 - xc2) + (yc1 - yc2)*(yc1 - yc2));
		var b = 4*(yc1 - yc2)*K;
		var c = 4*(xc1 - xc2)*(xc1 - xc2)*r2*r2 - K*K;
		a = a/K; b = b/K; c = c/K;
		var D = b*b - 4*a*c;
		/*console.info("K:" + K);
		console.info("a:" + a);
		console.info("b:" + b);
		console.info("c:" + c);
		console.info("D:" + D);*/
		if (D < 0) {
			console.warn("D:" + D);
			return null;    // circles do not have intersection
		} 
		else if (D > 0) {
			var y1 = (-b + Math.sqrt(D))/2/a + yc2;
			var y2 = (-b - Math.sqrt(D))/2/a + yc2;
			var x1 = Math.sqrt(r2*r2 - (y1 - yc2)*(y1 - yc2)) + xc2;
			var x2 = Math.sqrt(r2*r2 - (y2 - yc2)*(y2 - yc2)) + xc2;
			
			if (position == 'left') {
				if (x1 > x2) {
					var x = x2; var y = y2;
				}
				else {
					var x = x1; var y = y1;
				}
			}
			else if (position == 'right') {
				if (x1 < x2) {
					var x = x2; var y = y2;
				}
				else {
					var x = x1; var y = y1;
				}
			}
			else if (position == 'lower') {
				if (y1 > y2) {
					var x = x2; var y = y2;
				}
				else {
					var x = x1; var y = y1;
				}
			}
			else if (position == 'higher') {
				if (y1 < y2) {
					var x = x2; var y = y2;
				}
				else {
					var x = x1; var y = y1;
				}
			}
			else if (position.length == 2) {
				// position defined as a range between two points
				var x1_range = position[0].x();
				var y1_range = position[0].y();
				var x2_range = position[1].x();
				var y2_range = position[1].y();
				if ((x1 >= x1_range && x1 <= x2_range) || (x1 >= x2_range && x1 <= x1_range)) {
					if ((y1 >= y1_range && y1 <= y2_range) || (y1 >= y2_range && y1 <= y1_range)) {
						var xypoint = new QPointF(x1, y1);
						return xypoint;
					}
				}
				if ((x2 >= x1_range && x2 <= x2_range) || (x2 >= x2_range && x2 <= x1_range)) {
					if ((y2 >= y1_range && y2 <= y2_range) || (y2 >= y2_range && y2 <= y1_range)) {
						var xypoint = new QPointF(x2, y2);
						return xypoint;
					}
				}
				return null;
			}
		}
		else {   // D == 0
			//console.info("D=0");
			var y = -b/2/a + yc2;
			var x = Math.sqrt(r2*r2 - (y - yc2)*(y - yc2)) + xc2;
		}
	}
	
	var xypoint = new QPointF(x, y);
	return xypoint;
};

sDraw.tangent2circle = function(x1, y1, circle, position) {
// returns point (X,Y) on a circle; line connecting (x1,y1) and (X,Y) is a tangent to the circle
	if (circle.radius) {                               
		var r = circle.radius;
		var x0 = circle.x0;
		var y0 = circle.y0;
	}
	else {return null;}    // not a circle
	
	var A = -(y1-y0)*(y1-y0) - (x0-x1)*(x0-x1); var B = 2*y0*(x0-x1)*(x0-x1) - 2*(y1-y0)*(y0*y0-r*r-y0*y1); 
	var C = (x0-x1)*(x0-x1)*(r*r-y0*y0) - (y0*y0-r*r-y0*y1)*(y0*y0-r*r-y0*y1);
	var D = B*B - 4*A*C;
	
	if (D < 0) {return null;}   // point (x1,y1) is inside the circle
	else if (D == 0) { // point (x1,y1) is on the circle
		var Y = -B/(2*A);
		var X = Math.sqrt(r*r - (Y-y0)*(Y-y0)) + x0;
	}
	else {
		var Y1 = (-B + Math.sqrt(D))/(2*A);
		var Y2 = (-B - Math.sqrt(D))/(2*A);
		var X1 = Math.sqrt(r*r - (Y1-y0)*(Y1-y0)) + x0;
		var X2 = Math.sqrt(r*r - (Y2-y0)*(Y2-y0)) + x0;
	}
	if (position == 'left') {
		if (X1 > X2) {
			var X = X2; var Y = Y2;
		}
		else {
			var X = X1; var Y = Y1;
		}
	}
	else if (position == 'right') {
		if (X1 < X2) {
			var X = X2; var Y = Y2;
		}
		else {
			var X = X1; var Y = Y1;
		}
	}
	else if (position == 'lower') {
		if (Y1 > Y2) {
			var X = X2; var Y = Y2;
		}
		else {
			var X = X1; var Y = Y1;
		}
	}
	else if (position == 'higher') {
		if (Y1 < Y2) {
			var X = X2; var Y = Y2;
		}
		else {
			var X = X1; var Y = Y1;
		}
	}
	else {return null;}
	
	var xypoint = new QPointF(X, Y);
	return xypoint;
}
		
		

function toothChamfer(Rsinner, Rsouter, Ns, machineType, Ows, Dch, Rch, Tach, geoms)
{
	if (machineType == 'InnerRotor') {
		var Rs=Rsinner; var y0=Math.sqrt(Rs*Rs-(Ows/2)*(Ows/2)); var y_ch1=y0+Dch;}
	else {              //Outer rotor
		var Rs=Rsouter; var y0=Math.sqrt(Rs*Rs-(Ows/2)*(Ows/2)); var y_ch1=y0-Dch; Tach=-Tach;}

	var x_ch1=Ows/2;
	var D1 = -Math.tan(Math.PI/2-Tach*Math.PI/180);
	var alfa = Math.asin(Ows/Rs/2);
	var gamma = Rch*(Math.PI/Ns-alfa);
	var x_ch2 = Rs*Math.sin(gamma+alfa);   var y_ch2 = Rs*Math.cos(gamma+alfa);
	var D2 = -Math.tan(gamma+alfa);
	var A=[[x_ch1*x_ch1*x_ch1,   x_ch1*x_ch1,  x_ch1,  1],
		   [x_ch2*x_ch2*x_ch2,   x_ch2*x_ch2,  x_ch2,  1],
		   [3*x_ch1*x_ch1,       2*x_ch1,      1,      0],
		   [3*x_ch2*x_ch2,       2*x_ch2,      1,      0]];
	var B=[y_ch1, y_ch2, D1, D2];
	var P3 = SolveRoots(A, B);
	//console.info("P3: " + P3);
	var xx_ch = [], yy_ch = [];
	var points = [];
	for (var i = 0; i < 10; i++)  {
		xx_ch[i] = x_ch1 + i*(x_ch2-x_ch1)/9;
		yy_ch[i] = P3[0]*xx_ch[i]*xx_ch[i]*xx_ch[i] + P3[1]*xx_ch[i]*xx_ch[i] + P3[2]*xx_ch[i] + P3[3];
		//console.info("xx_ch[i]: " + xx_ch[i]);
		if (i>0)
		{
			var point_start = new QPointF(xx_ch[i-1], yy_ch[i-1]);
			var point_end = new QPointF(xx_ch[i], yy_ch[i]);
			//console.info("line: from (" + point_start.x() + ', ' + point_start.y() + ' ) to (' + point_end.x() + ', ' + point_end.y() + ')');
			//console.info("point_end: " + point_end.x() + ', ' + point_end.y());
			geoms.push(new Segment(point_start, point_end)); 
			points.push(point_end);
		}
		else {  // i==0
			points.push(new QPointF(xx_ch[0], yy_ch[0]));
		}
	}
	//g_=[1 cp2(1) x_ch2 cp2(2) y_ch2 1 2 0 0 Rs]; g=[g g_'];
	return [points, geoms];
	//args[0] = points;
	//args[1] = geoms;
	//return args;
}

function divideSlotHorizontal(slotGeom, isolationWidth)
{
	var N = slotGeom.length, perimeter = 0;
	if (N < 2) {return null;}
	// slot border perimeter
    for (var i = 0; i < N; ++i) {
		var obj = slotGeom[i];
		if (obj.radius === undefined) {// if Line
			var p = Math.sqrt((obj.p2.x() - obj.p1.x())*(obj.p2.x() - obj.p1.x()) + (obj.p2.y() - obj.p1.y())*(obj.p2.y() - obj.p1.y()));
		}
		else {// if Arc
			if (obj.angle2 > obj.angle1) {
				var p = 2*Math.PI*obj.radius*(obj.angle2 - obj.angle1)/360;
			}
			else {
				var p = 2*Math.PI*obj.radius*(obj.angle2 + (360 - obj.angle1))/360;
			}
		}
		perimeter = perimeter + p;
	}
	//console.info("perimeter: " + perimeter);
	var xx = [], yy = [];
	var x1_current = 0, y1_current = 0, x2_current = 0, y2_current = 0, x1_prev = null, y1_prev = null, x2_prev = null, y2_prev = null;
	// determine first point - определяем общую точку первого и второго элементов
	if (Math.abs(slotGeom[0].p2.x() - slotGeom[1].p1.x()) < 1e-10 && Math.abs(slotGeom[0].p2.y() - slotGeom[1].p1.y()) < 1e-10) {
		x1_current = slotGeom[0].p1.x();  // start point
		y1_current = slotGeom[0].p1.y();  // start point
		x2_current = slotGeom[0].p2.x();  // end point
		y2_current = slotGeom[0].p2.y();  // end point
	}
	else if (Math.abs(slotGeom[0].p2.x() - slotGeom[1].p2.x()) < 1e-10 && Math.abs(slotGeom[0].p2.y() - slotGeom[1].p2.y()) < 1e-10) {
		x1_current = slotGeom[0].p1.x();  // start point
		y1_current = slotGeom[0].p1.y();  // start point
		x2_current = slotGeom[0].p2.x();  // end point
		y2_current = slotGeom[0].p2.y();  // end point
	}
	else if (Math.abs(slotGeom[0].p1.x() - slotGeom[1].p1.x()) < 1e-10 && Math.abs(slotGeom[0].p1.y() - slotGeom[1].p1.y()) < 1e-10) {
		x1_current = slotGeom[0].p2.x();  // start point
		y1_current = slotGeom[0].p2.y();  // start point
		x2_current = slotGeom[0].p1.x();  // end point
		y2_current = slotGeom[0].p1.y();  // end point
	}
	else if (Math.abs(slotGeom[0].p1.x() - slotGeom[1].p2.x()) < 1e-10 && Math.abs(slotGeom[0].p1.y() - slotGeom[1].p2.y()) < 1e-10) {
		x1_current = slotGeom[0].p2.x();  // start point
		y1_current = slotGeom[0].p2.y();  // start point
		x2_current = slotGeom[0].p1.x();  // end point
		y2_current = slotGeom[0].p1.y();  // end point
	}
	else {
		console.error("divideSlotHorizontal error: slot border is not continuous");
		return null;
	}
	for (var n = 0; n < N; ++n) {
		var obj = slotGeom[n];
		if (n>0) {
			if (Math.abs(x2_prev - obj.p1.x()) < 1e-10 && Math.abs(y2_prev - obj.p1.y()) < 1e-10) {
				x1_current = obj.p1.x();  // start point
				y1_current = obj.p1.y();  // start point
				x2_current = obj.p2.x();  // end point
				y2_current = obj.p2.y();  // end point
			}
			else if (Math.abs(x2_prev - obj.p2.x()) < 1e-10 && Math.abs(y2_prev - obj.p2.y()) < 1e-10) {
				x1_current = obj.p2.x();  // start point
				y1_current = obj.p2.y();  // start point
				x2_current = obj.p1.x();  // end point
				y2_current = obj.p1.y();  // end point
			}
			else {
				console.error("divideSlotHorizontal error: slot border is not continuous");
				return null;
			}			
		}
		xx.push(x1_current); 
		yy.push(y1_current); 
		if (!(obj.radius === undefined)) {// if Arc
			//break arc into small lines
			//console.info("obj.radius: " + obj.radius + ", obj.angle1: " + obj.angle1 + ", obj.angle2: " + obj.angle2);
			if (obj.angle2 > obj.angle1) {
				var p = 2*Math.PI*obj.radius*(obj.angle2 - obj.angle1)/360;   // arc length
				var angle1 = obj.angle1, angle2 = obj.angle2;
				var nlines = Math.round(p/(0.001*perimeter));
			}
			else {
				var p = 2*Math.PI*obj.radius*(obj.angle2 + (360 - obj.angle1))/360;   // arc length
				var angle1 = obj.angle1 - 360, angle2 = obj.angle2;
				if (angle1 > angle2) {
					console.error("divideSlotHorizontal error: something goes wrong with breaking arc into small lines (1)");
					return null;
				}
				var nlines = Math.round(p/(0.001*perimeter));
			}
			if (nlines < 10) {nlines = 10;}
			var t = angle1;
			var x = obj.radius*Math.cos(Math.degToRad(t)) + obj.center.x();
			var y = obj.radius*Math.sin(Math.degToRad(t)) + obj.center.y();
			var inverse_t = false;
			if (Math.abs(x1_current - x) > 1e-10 || Math.abs(y1_current - y) > 1e-10)
				inverse_t = true;
			for (var i = 0; i <= nlines; ++i) {
				if (inverse_t)
					t = angle2 - i*(angle2-angle1)/nlines;
				else
					t = angle1 + i*(angle2-angle1)/nlines;
				x = obj.radius*Math.cos(Math.degToRad(t)) + obj.center.x();
				y = obj.radius*Math.sin(Math.degToRad(t)) + obj.center.y();
				if (i == 0) {
					//console.info("x1_current: " + x1_current + ", x: " + x + ", y1_current: " + y1_current + ", y: " + y);
					//console.info("nlines: " + nlines);
					//console.info("angle1: " + angle1 + ", angle2: " + angle2);
					if (Math.abs(x1_current - x) > 1e-10 || Math.abs(y1_current - y) > 1e-10) {
						console.error("divideSlotHorizontal error: something goes wrong with breaking arc into small lines (2)");
						return null;
					}
				}
				else if (i == nlines) {
					if (Math.abs(x2_current - x) > 1e-10 || Math.abs(y2_current - y) > 1e-10) {
						console.error("divideSlotHorizontal error: something goes wrong with breaking arc into small lines (3)");
						return null;
					}
				}
				else {
					xx.push(x); 
					yy.push(y); 
				}
			}
		}
		x1_prev = x1_current;
		x2_prev = x2_current;
		y1_prev = y1_current;
		y2_prev = y2_current;
	}
	xx.push(x2_current); 
	yy.push(y2_current);
	//verification of xx and yy
	var perimeter_test = 0;
	for (var i = 1; i < xx.length; ++i) {
		p = Math.sqrt((xx[i-1] - xx[i])*(xx[i-1] - xx[i]) + (yy[i-1] - yy[i])*(yy[i-1] - yy[i]));
		perimeter_test = perimeter_test + p;
	}
	if (Math.abs(perimeter - perimeter_test)/perimeter > 0.001) {
		console.error("divideSlotHorizontal error: something goes wrong with xx and yy arrays!");
		return null;
	}
	//console.info("perimeter: " + perimeter + ', ' + perimeter_test);
	
	var y_guess = (yy[0] + yy[yy.length-1])/2;
	if (y_guess + isolationWidth/2 >= Math.max(yy[0], yy[yy.length-1])) {
		console.error("divideSlotHorizontal error: incorrect between layers isolation width");
		return null;
	}
	
	// golden section search
	var phi = 1.6180, outputs = []; area_err = 1000; iter = 0;
	var y_guess_max = Math.max(yy[0], yy[yy.length-1]) - isolationWidth/2;
	var y_guess_min = Math.min(yy[0], yy[yy.length-1]) + isolationWidth/2;
	var y_guess_1=y_guess_max-(y_guess_max-y_guess_min)/phi;
	var y_guess_2=y_guess_min+(y_guess_max-y_guess_min)/phi;

	outputs = layerArea(xx, yy, slotGeom, y_guess_1, isolationWidth);
	var area1_1 = outputs[0];
	var area2_1 = outputs[1];
	var area_err_1 = 2*Math.abs(area1_1 - area2_1)/(area1_1 + area2_1);
	outputs = layerArea(xx, yy, slotGeom, y_guess_2, isolationWidth);
	var area1_2 = outputs[0];
	var area2_2 = outputs[1];
	var area_err_2 = 2*Math.abs(area1_2 - area2_2)/(area1_2 + area2_2);
	
	while (area_err > 0.0001) {
		if (area_err_1 > area_err_2) {
			y_guess_min = y_guess_1;
			y_guess_1 = y_guess_2;
			y_guess_2=y_guess_min+(y_guess_max-y_guess_min)/phi;
			area1_1 = area1_2; area2_1 = area2_2;
			area_err_1 = area_err_2;
			outputs = layerArea(xx, yy, slotGeom, y_guess_2, isolationWidth);
			area1_2 = outputs[0];
			area2_2 = outputs[1];
			area_err_2 = 2*Math.abs(area1_2 - area2_2)/(area1_2 + area2_2);
			//console.info("area search: " + area1_2 + ', ' + area2_2);
		}
		else {
			y_guess_max = y_guess_2;
			y_guess_2 = y_guess_1;
			y_guess_1=y_guess_max-(y_guess_max-y_guess_min)/phi;
			area1_2 = area1_1; area2_2 = area2_1;
			area_err_2 = area_err_1;
			outputs = layerArea(xx, yy, slotGeom, y_guess_1, isolationWidth);
			area1_1 = outputs[0];
			area2_1 = outputs[1];
			area_err_1 = 2*Math.abs(area1_1 - area2_1)/(area1_1 + area2_1);
			//console.info("area search: " + area1_1 + ', ' + area2_1);
		}
		iter = iter + 1;
		area_err = (area_err_1 + area_err_2)/2;
	}
	y_guess = (y_guess_1 + y_guess_2)/2;
	outputs = layerArea(xx, yy, slotGeom, y_guess, isolationWidth);
	var area1 = outputs[0];
	var area2 = outputs[1];
	var x1 = outputs[2];
	var x2 = outputs[3];
	//console.info("divideSlotHorizontal result: iter=" + iter + ' (' + area1 + ', ' + area2 + ')');	
	
	return [x1, y_guess - isolationWidth/2, x2, y_guess + isolationWidth/2];

	//test.
	//var area = evalArea(xx, yy);
	//console.info("area: " + area);
}

function layerArea(xx, yy, slotGeom, y_guess, isolationWidth) {
	var y1_guess = y_guess - isolationWidth/2;
	var y2_guess = y_guess + isolationWidth/2;
	var N = slotGeom.length, iobj1 = null, iobj2 = null;
    for (var i = 0; i < N; ++i) {
		var obj = slotGeom[i];
		if ((obj.p1.y() >= y1_guess && obj.p2.y() <= y1_guess) || (obj.p2.y() >= y1_guess && obj.p1.y() <= y1_guess)) {
			iobj1 = i;
		}
		if ((obj.p1.y() >= y2_guess && obj.p2.y() <= y2_guess) || (obj.p2.y() >= y2_guess && obj.p1.y() <= y2_guess)) {
			iobj2 = i;
		}
		if (iobj1 != null && iobj2 != null) { break; }
	}
	if (iobj1 == null || iobj2 == null) {
		console.error("divideSlotHorizontal error: incorrect guess point");
		return null;
	}
	
	var obj1 = slotGeom[iobj1];
	if (obj1.radius === undefined) {// if Line
		var line1 = sDraw.createLineBy2Points(obj1.p1.x(),obj1.p1.y(),obj1.p2.x(),obj1.p2.y());
		var line2 = sDraw.createLineByPointAngle(0, y1_guess, 0);
		var guess1 = sDraw.intersection(line1, line2); 
		if (guess1 == null) {var x1_guess = Math.min(obj1.p1.x(), obj1.p2.x());}  // if line1 is horizontal
		else {var x1_guess = guess1.x();}
	}
	else {// if Arc
		var circle1 = sDraw.createCircle(obj1.center.x(),obj1.center.y(),obj1.radius);
		var line1 = sDraw.createLineByPointAngle(0, y1_guess, 0);
		var guess1 = sDraw.intersection(line1, circle1, 'right'); 
		if (guess1 == null) {
			console.error("divideSlotHorizontal error: something goes wrong!");
			return null;
		}
		else {var x1_guess = guess1.x();} 
	}
		
	var obj2 = slotGeom[iobj2];
	if (obj2.radius === undefined) {// if Line
		var line1 = sDraw.createLineBy2Points(obj2.p1.x(),obj2.p1.y(),obj2.p2.x(),obj2.p2.y());
		var line2 = sDraw.createLineByPointAngle(0, y2_guess, 0);
		var guess2 = sDraw.intersection(line1, line2); 
		if (guess2 == null) {var x2_guess = Math.min(obj2.p1.x(), obj2.p2.x());}  // if line1 is horizontal
		else {var x2_guess = guess2.x();}
	}
	else {// if Arc
		var circle1 = sDraw.createCircle(obj2.center.x(),obj2.center.y(),obj2.radius);
		var line1 = sDraw.createLineByPointAngle(0, y2_guess, 0);
		var guess2 = sDraw.intersection(line1, circle1, 'right'); 
		if (guess2 == null) {
			console.error("divideSlotHorizontal error: something goes wrong!");
			return null;
		}
		else {var x2_guess = guess2.x();} 
	}
	
	var xx1 = [], yy1 = [], xx2 = [], yy2 = [];
	if (yy[0] > y2_guess)
	{// points in xx and yy arranged clock-wise
		xx1.push(0);        yy1.push(y1_guess);
		xx1.push(x1_guess); yy1.push(y1_guess);
		for (var i = 0; i < xx.length; ++i) {
			if (yy[i] > y2_guess) {
				xx2.push(xx[i]); 
				yy2.push(yy[i]);
			}
			else if (yy[i] < y1_guess) {
				xx1.push(xx[i]); 
				yy1.push(yy[i]);
			}
		}
		xx2.push(x2_guess); yy2.push(y2_guess);
		xx2.push(0);        yy2.push(y2_guess);
	}
	else
	{// points in xx and yy arranged counter-clock-wise
		xx2.push(0);        yy2.push(y2_guess);
		xx2.push(x2_guess); yy2.push(y2_guess);
		for (var i = 0; i < xx.length; ++i) {
			if (yy[i] > y2_guess) {
				xx2.push(xx[i]); 
				yy2.push(yy[i]);
			}
			else if (yy[i] < y1_guess) {
				xx1.push(xx[i]); 
				yy1.push(yy[i]);
			}
		}
		xx1.push(x1_guess); yy1.push(y1_guess);
		xx1.push(0);        yy1.push(y1_guess);
	}
	var area1 = evalArea(xx1, yy1);
	var area2 = evalArea(xx2, yy2);
	
	//console.info("area1: " + area1);
	//console.info("area2: " + area2);
	
	return [area1, area2, x1_guess, x2_guess];
}

function evalArea(xx, yy) {
	var area = 0;
	for (var i = 0; i < xx.length-1; ++i) {
		area = area + xx[i]*yy[i+1] - xx[i+1]*yy[i];
	}
	area = area + xx[xx.length-1]*yy[0] - xx[0]*yy[xx.length-1];
	return Math.abs(area)/2;
}

function divideSlotVertical(slotGeom, isolationWidth) {
	var x = isolationWidth/2;
	var y1 = null, y2 = null;
    for (var i = 0; i < slotGeom.length; ++i) {
		var obj = slotGeom[i];
		//console.info("x1: " + obj.p1.x() + ", x2: " + obj.p2.x());
		if (y1 == null) {
			if ((obj.p1.x() >= x && obj.p2.x() <= x) || (obj.p2.x() >= x && obj.p1.x() <= x)) {
				y1 = objintersect(obj, x);
				if (y1 == null) return null;
			}
		}
		else {
			if ((obj.p1.x() >= x && obj.p2.x() <= x) || (obj.p2.x() >= x && obj.p1.x() <= x)) {
				y2 = objintersect(obj, x);
				if (y2 == null) return null;
				if (y2 == y1) y2 = null;
			}
		}
		//console.info("y1: " + y1 + ", y2: " + y2);
		if (y1 != null && y2 != null) { break; }
	}
	if (y1 == null || y2 == null) {
		console.error("divideSlotVertical error: something goes wrong(1)");
		return null;
	}
	//console.info("y1: " + y1 + ", y2: " + y2);
	
	return [y1, y2];
}

function objintersect(obj, x) {
	if (obj.radius === undefined) {// if Line
		var line1 = sDraw.createLineBy2Points(obj.p1.x(),obj.p1.y(),obj.p2.x(),obj.p2.y());
		var line2 = sDraw.createLineByPointAngle(x, 0, 90);
		var point = sDraw.intersection(line1, line2); 
		if (point == null) return null;
		return point.y();
	}
	else {// if Arc
		var circle1 = sDraw.createCircle(obj.center.x(),obj.center.y(),obj.radius);
		var line1 = sDraw.createLineByPointAngle(x, 0, 90);
		var point = sDraw.intersection(line1, circle1, [obj.p1, obj.p2]); 
		if (point == null) {
			console.error("divideSlotVertical error: something goes wrong(2)");
			return null;
		}
		return point.y();
	}	
}

function calcLengthBy2Points(x1, y1, x2, y2){
	return Math.sqrt(Math.pow(x2-x1, 2) + Math.pow(y2-y1, 2));
}

function mirrorY(geom) {  // mirror geometry about y axis
	var geom_mirror = new Array;
	for (var i = 0; i < geom.length; i++)  {
		if (geom[i].isSegment()) {
			segment = geom[i];
			var p1mirror = new QPointF(-segment.x1, segment.y1);
			var p2mirror = new QPointF(-segment.x2, segment.y2);
			geom_mirror.push(new Segment(p1mirror, p2mirror));
		}
		else if (geom[i].isArc()) {
			arc = geom[i];
			var cxmirror = -arc.centerX;
			var cymirror = arc.centerY;
			angle1mirror = (arc.angle2 - 90)*(-1) + 90;
			while (angle1mirror > 360) {
				angle1mirror = angle1mirror - 360; }
			while (angle1mirror < 0) {
				angle1mirror = angle1mirror + 360; }
			angle2mirror = (arc.angle1 - 90)*(-1) + 90;
			while (angle2mirror > 360) {
				angle2mirror = angle2mirror - 360; }
			while (angle2mirror < 0) {
				angle2mirror = angle2mirror + 360; }	
			if (angle1mirror > 360 || angle1mirror < 0 || angle2mirror > 360 || angle2mirror < 0) {
				console.error("mirrorY: at least one arc angle exceeds 0...360 degrees range (angle1mirror=" + angle1mirror + ", angle2mirror="  + angle2mirror + ")"); }
			geom_mirror.push(new Arc(angle1mirror, angle2mirror, arc.radius, cxmirror, cymirror));
		}
		else {
			console.error("mirrorY: undefined geometry object");
			return null;
		}
	}		
	return geom_mirror;
}

function concgeom(geom1,geom2) {  //concatenate two geometry arrays
	for (var i = 0; i < geom2.length; i++)  {
			geom1.push(geom2[i]);
	}	
	return geom1;
}

function rotate00(geom,angle) {  // rotate geometry about origin (0; 0) counter-clock-wise
	var geom_rot = new Array;
	for (var i = 0; i < geom.length; i++)  {
		if (geom[i].isSegment()) {
			segment = geom[i];
			var p1rot = rotatepoint(segment.x1, segment.y1, angle);
			var p2rot = rotatepoint(segment.x2, segment.y2, angle);
			geom_rot.push(new Segment(p1rot, p2rot));
		}
		else if (geom[i].isArc()) {
			arc = geom[i];
			var Crot = rotatepoint(arc.centerX, arc.centerY, angle);
			var p1rot = rotatepoint(arc.p1.x(), arc.p1.y(), angle);    // arc start point
			var p2rot = rotatepoint(arc.p2.x(), arc.p2.y(), angle);    // arc end point
			var angle1rot = Math.atan2((p1rot.y() - Crot.y()), (p1rot.x() - Crot.x()));
			angle1rot = angle1rot*180/Math.PI;
			while (angle1rot > 360) {
				angle1rot = angle1rot - 360; }
			while (angle1rot < 0) {
				angle1rot = angle1rot + 360; }
			var angle2rot = Math.atan2((p2rot.y() - Crot.y()), (p2rot.x() - Crot.x()));
			angle2rot = angle2rot*180/Math.PI;
			while (angle2rot > 360) {
				angle2rot = angle2rot - 360; }
			while (angle2rot < 0) {
				angle2rot = angle2rot + 360; }	
			if (angle1rot > 360 || angle1rot < 0 || angle2rot > 360 || angle2rot < 0) {
				console.error("rotate00: at least one arc angle exceeds 0...360 degrees range (angle1rot=" + angle1rot + ", angle2rot="  + angle2rot + ")"); }
			geom_rot.push(new Arc(angle1rot, angle2rot, arc.radius, Crot));
		}
		else {
			console.error("mirrorY: undefined geometry object");
			return null;
		}
	}		
	return geom_rot;
}

function rotatepoint(x,y,angle) { // rotate point about origin counter-clock-wise
    //RM=[cos(angle_rad) -sin(angle_rad);
    //    sin(angle_rad)  cos(angle_rad)];          counter-clock-wise rotation matrix
	//[x_rot   RM*[x;
	// y_rot]=     y]
	var angle_rad = angle*Math.PI/180;
	var x_rot = x*Math.cos(angle_rad) - y*Math.sin(angle_rad);
	var y_rot = x*Math.sin(angle_rad) + y*Math.cos(angle_rad);		  
	return new QPointF(x_rot, y_rot);
}





