// Stator with parallel tooth
// Copyright 2018-2024, Vladimir Kuptsov
include('..\\sDraw.js');
include('..\\Matrix.js');

function Stator()
{
	// Stator inner radius
	var Rsinner = motor.statorInnerRadius;
	// Stator outer radius
	var Rsouter = motor.statorOuterRadius;
	// Number of stator slots
	var Ns = motor.statorNumberSlots;
	
	// Default stator dimensions:
	// Slot depth
	this.Sds = 0.6*Math.abs(Rsouter - Rsinner);
	// Tooth width
	if (motor.isInnerRotor())
		this.Ws = 0.35*Math.PI*(2*Rsinner+this.Sds)/Ns;
	else
		this.Ws = 0.35*Math.PI*(2*Rsouter-this.Sds)/Ns;
	// Slot opening depth
	this.Ods = 0.04*this.Sds;
	// Slot opening width
	this.Ows = 0.04*this.Sds;
	// Tooth tip angle
	this.Tas = 15;	
	// Slot bottom corner type: General | Round
	this.bottomCornerType = 'General';
	// Bottom corner radius (away from airgap)
	this.Rcs = 0;	
	// Top corner radius (closer to airgap)
	this.Rcs_ag = 0;	
	// Tooth edge chamfer depth 
	this.Dch = 0;
	// Tooth edge chamfer ratio 
	this.Rch = 0;
	// Tooth edge chamfer angle 
	this.Tach = 0;
	// Slot insulation thickness
	this.Ith = 0;
	// Between layers insulation thickness
	this.Ibl = 0;
};

// generate stator geometry 
// call from с++ code
Stator.prototype.getGeometry = function (){

	// Machine type: InnerRotor | OuterRotor
	var machineType = motor.machineType;
	// Stator inner radius
	var Rsinner = motor.statorInnerRadius;
	// Stator outer radius
	var Rsouter = motor.statorOuterRadius;
	// Number of stator slots
	var Ns = motor.statorNumberSlots;
	// Air gap length
	var lag = motor.airGap;
	
	var Rcs = this.Rcs;
	
	if (this.Ods == 0) {
		var Ows = 0;
		var Tas = 0;
		var Rcs_ag = 0;
	}
	else {
		var Ows = this.Ows;
		var Tas = this.Tas;
		var Rcs_ag = this.Rcs_ag;
		if (this.Dch > this.Ods)
			this.Dch = this.Ods;
	}
	if (motor.isInnerRotor()) {
		var point_1 = new QPointF(0, Rsinner+this.Ods);
		
		var point_2 = new QPointF(Ows/2, Rsinner+this.Ods);
		var Rsds = Rsinner + this.Sds;	
	
		// search for point 3
		var line1 = sDraw.createLineByPointAngle(0, 0, 90-360/Ns/2);        // slot pitch border
		var line_3_5 = sDraw.createLineParallel(line1, this.Ws/2, 'left'); 
		var line_2_3 = sDraw.createLineByPointAngle(point_2.x(), point_2.y(), Tas);
		var point_3 = sDraw.intersection(line_2_3, line_3_5); 
	
		// search for point 4
		var circle_Rsinner = sDraw.createCircle(0, 0, Rsinner);
		var line_2_4 = sDraw.createLineByPointAngle(point_2.x(), point_2.y(), -90);
		var point_4 = sDraw.intersection(line_2_4, circle_Rsinner, 'higher'); 

		// search for point 5
		var circle_Rsds = sDraw.createCircle(0, 0, Rsds);                   // upper slot border
		var point_5 = sDraw.intersection(line_3_5, circle_Rsds, 'higher');   
		var line_0_5 = sDraw.createLineBy2Points(0, 0, point_5.x(), point_5.y());
		//console.info("circle_Rsds: " + circle_Rsds.x0 + ', ' + circle_Rsds.y0 + ', ' + circle_Rsds.radius);
		//console.info("line_3_5: " + line_3_5.x0 + ', ' + line_3_5.y0 + ', ' + Math.radToDeg(line_3_5.angleRad));
		//console.info("point_5: " + point_5.x() + ', ' + point_5.y());
		
		// draw slot geometry
		var geoms = new Array;
		
		var line_0_6 = sDraw.createLineByPointAngle(0, 0, 90);
		var point_A = sDraw.intersection(line_0_6, line_3_5); 
		var h = Rsds - point_A.y();
		var alfa = Math.PI/2 - line_3_5.angleRad;
		var Rcs_max = h*Math.sin(alfa) / (1 + Math.sin(alfa));
		if(stator.bottomCornerType == 'Round') {
			Rcs = Rcs_max;
			//console.info("h: " + h + ', Rcs: ' + Rcs + ', alfa: ' + alfa);
		}
		else {   // stator.bottomCornerType == 'General'
			if (Rcs > Rcs_max)	{
				Rcs = Rcs_max;
				console.warn("Bottom corner radius (Rcs) does not match slot dimensions, maximum possible radius is used instead");
			}
		}
		
		if (this.Ods == 0) { // if there is open slot
			// research for point 3 (point 3 is on circle Rsinner!!!)
			var point_3 = sDraw.intersection(line_3_5, circle_Rsinner, 'higher'); 
			var line_0_3 = sDraw.createLineBy2Points(0, 0, point_3.x(), point_3.y());
			geoms.push(new Arc(Math.radToDeg(line_0_3.angleRad), 90, Rsinner, 0, 0));    // arc 1-3
			//console.info("geoms: " + geoms);
			// round off the bottom slot corner
			var g2 = couplingSegments(new Segment(point_3, point_5), new Arc(Math.radToDeg(line_0_5.angleRad), 90, Rsds, 0, 0), Rcs);
		}
		else	{
			if (Ows > 0) {
				geoms.push(new Segment(point_1, point_2)); }    // line 1-2
			
			// round off the top slot corner
			var g1 = couplingSegments(new Segment(point_2, point_3), new Segment(point_5, point_3), Rcs_ag); 
			//console.info("g1: " + g1);
			geoms.push(g1[0]);   // line 2-3a
			
			if (g1[2].radius > 0 && Math.abs(g1[2].angle1 - g1[2].angle2) > 1e-10) 
				{geoms.push(g1[2]);}   // arc 3a-3b
			// g1[1] - line 3b-5
			
			// round off the bottom slot corner
			// console.info("line_3_5.angle: " + Math.radToDeg(line_3_5.angleRad));
			var g2 = couplingSegments(g1[1], new Arc(Math.radToDeg(line_0_5.angleRad), 90, Rsds, 0, 0), Rcs);
		}
		geoms.push(g2[0]);   // line 3b-5a
		if (g2[2].radius > 0 && Math.abs(g2[2].angle1 - g2[2].angle2) > 1e-10)	
			{geoms.push(g2[2]);}   // arc 5a-5b
		if (g2[1].radius > 0 && Math.abs(g2[1].angle1 - g2[1].angle2) > 1e-10)	
			{geoms.push(g2[1]);}   // arc 5b-6
		//console.info("g2: " + g2);
		if (this.Ith > 0) {
			// draw insulation geometry
			var point_1_ = new QPointF(0, Rsinner + this.Ods + this.Ith);
			var point_2_ = new QPointF(Ows/2, Rsinner + this.Ods + this.Ith);
			var Rsds_ = Rsinner + this.Sds - this.Ith;	
		
			// search for point 3_
			var line_3_5_ = sDraw.createLineParallel(line_3_5, this.Ith, 'left'); 
			var line_2_3_ = sDraw.createLineParallel(line_2_3, this.Ith, 'higher');
			var point_3_ = sDraw.intersection(line_2_3_, line_3_5_); 
		
			// search for point 5_
			var circle_Rsds_ = sDraw.createCircle(0, 0, Rsds_);                   // upper slot border
			var point_5_ = sDraw.intersection(line_3_5_, circle_Rsds_, 'higher');   
			var line_0_5_ = sDraw.createLineBy2Points(0, 0, point_5_.x(), point_5_.y());
			
			var geomslot = 	new Array;
			
			if (this.Ods == 0) {
				// research for point 3_ (point 3_ is on circle Rsinner)
				var point_3_ = sDraw.intersection(line_3_5_, circle_Rsinner, 'higher'); 
				var line_0_3_ = sDraw.createLineBy2Points(0, 0, point_3_.x(), point_3_.y());
				geomslot.push(new Arc(Math.radToDeg(line_0_3_.angleRad), 90, Rsinner, 0, 0));    // arc 1-3_
				//console.info("geoms: " + geoms);
				// round off the bottom slot corner
				var Rcs_ = Rcs - this.Ith;
				if (Rcs_ < 0) {Rcs_ = 0;}
				var g2 = couplingSegments(new Segment(point_3_, point_5_), new Arc(Math.radToDeg(line_0_5_.angleRad), 90, Rsds_, 0, 0), Rcs_);
			}
			else	{
				if (point_2_.x() > point_3_.x()) {
					// research for point 3_    (point_1_ -> point_3_ -> point_5a_)
					var line_1_2_ = sDraw.createLineBy2Points(point_1_.x(), point_1_.y(),point_2_.x(), point_2_.y());
					var point_3_ = sDraw.intersection(line_1_2_, line_3_5_); 
					
					geoms.push(new Segment(point_1_, point_3_));    // line 1_-3_
					geomslot.push(new Segment(point_1_, point_3_));
					
					var g1 = couplingSegments(new Segment(point_1_, point_3_), new Segment(point_5_, point_3_), 0); 
					// g1[1] - line 3_-5_
				}
				else {
					if (Ows > 0) {
						geoms.push(new Segment(point_1_, point_2_));    // line 1_-2_
						geomslot.push(new Segment(point_1_, point_2_));
					}
					// round off the top slot corner
					var Rcs_ag_ = Rcs_ag - this.Ith;
					if (Rcs_ag_ < 0) {Rcs_ag_ = 0;}
					var g1 = couplingSegments(new Segment(point_2_, point_3_), new Segment(point_5_, point_3_), Rcs_ag_); 
					//console.info("g1: " + g1);
					geoms.push(g1[0]);   // line 2_-3a_
					geomslot.push(g1[0]);
					if (g1[2].radius > 0 && Math.abs(g1[2].angle1 - g1[2].angle2) > 1e-10) {
						geoms.push(g1[2]);   // arc 3a_-3b_
						geomslot.push(g1[2]);
					}
					// g1[1] - line 3b_-5_
				}
				// round off the bottom slot corner
				var Rcs_ = Rcs - this.Ith;
				if (Rcs_ < 0) {Rcs_ = 0;}
				var g2 = couplingSegments(g1[1], new Arc(Math.radToDeg(line_0_5_.angleRad), 90, Rsds_, 0, 0), Rcs_);
			}
			geoms.push(g2[0]);   // line 3b_-5a_   (line 3_-5a_ if Ods == 0)
			geomslot.push(g2[0]);
			if (g2[2].radius > 0 && Math.abs(g2[2].angle1 - g2[2].angle2) > 1e-10) {
				geoms.push(g2[2]);   // arc 5a_-5b_
				geomslot.push(g2[2]);
			}   
			if (g2[1].radius > 0 && Math.abs(g2[1].angle1 - g2[1].angle2) > 1e-10) {
				geoms.push(g2[1]);   // arc 5b_-6_
				geomslot.push(g2[1]);
			}   
		}
		else {
			var geomslot = 	geoms;
		}
		
	}
	else {     //Outer rotor
		var point_1 = new QPointF(0, Rsouter-this.Ods);
		var point_2 = new QPointF(Ows/2, Rsouter-this.Ods);
		Tas = -Tas;
		var Rsds = Rsouter - this.Sds;	
	
		// search for point 4
		var circle_Rsouter = sDraw.createCircle(0, 0, Rsouter);
		var line_2_4 = sDraw.createLineByPointAngle(point_2.x(), point_2.y(), 90);
		var point_4 = sDraw.intersection(line_2_4, circle_Rsouter, 'higher'); 
		
		// search for point 3
		var line1 = sDraw.createLineByPointAngle(0, 0, 90-360/Ns/2);        // slot pitch border
		var line_3_5 = sDraw.createLineParallel(line1, this.Ws/2, 'left'); 
		if (this.Ods == 0) { // if there is open slot
			// point 3 is on circle Rsouter!!!
			var point_3 = sDraw.intersection(line_3_5, circle_Rsouter, 'higher'); 
		}
		else {	
			var line_2_3 = sDraw.createLineByPointAngle(point_2.x(), point_2.y(), Tas);
			var point_3 = sDraw.intersection(line_2_3, line_3_5); 
		}
		
		// search for point 5
		var line_6_5 = sDraw.createLineByPointAngle(0, Rsds, 0);                   // lower slot border
		var point_5 = sDraw.intersection(line_3_5, line_6_5, 'higher');   

		var point_6 = new QPointF(0, Rsds);
		
		// draw slot geometry
		var geoms = new Array;
		
		var useRcs_max = 0;
		var line_0_6 = sDraw.createLineByPointAngle(0, 0, 90);
		var point_A = sDraw.intersection(line_0_6, line_3_5); 
		var h = Rsds - point_A.y();
		var alfa = Math.PI/2 - line_3_5.angleRad;
		var Rcs_max = h*Math.sin(alfa) / (1 - Math.sin(alfa));
		if(stator.bottomCornerType == 'Round') {
			Rcs = Rcs_max;
			//console.info("h: " + h + ', Rcs: ' + Rcs + ', alfa: ' + alfa);
		}
		else {   // stator.bottomCornerType == 'General'
			if (Rcs > Rcs_max)	{
				Rcs = Rcs_max;
				useRcs_max = 1;
				console.warn("Bottom corner radius (Rcs) does not match slot dimensions, maximum possible radius is used instead");
			}
		}
		
		// round off the bottom slot corner
		// console.info("line_3_5.angle: " + Math.radToDeg(line_3_5.angleRad));
		if(stator.bottomCornerType == 'Round' || useRcs_max) {
			var point_minus_5 = new QPointF(-point_5.x(), point_5.y());
			var g1 = couplingSegments(new Segment(point_minus_5, point_5), new Segment(point_3, point_5), Rcs);
		}
		else { // bottomCornerType == 'General'
			var g1 = couplingSegments(new Segment(point_6, point_5), new Segment(point_3, point_5), Rcs);
			geoms.push(g1[0]);   // line 6-5b
		}
		if (g1[2].radius > 0 && Math.abs(g1[2].angle1 - g1[2].angle2) > 1e-10)
			{
				geoms.push(g1[2]);  // arc 5b-5a
			}   
		// g1[1] - line 5a-3
		//console.info("g1: " + g1);
		
		if (this.Ods == 0) { // if there is open slot
			// point 3 is on circle Rsouter!!!
			var line_0_3 = sDraw.createLineBy2Points(0, 0, point_3.x(), point_3.y());
			geoms.push(g1[1]);     // line 5a-3
			geoms.push(new Arc(Math.radToDeg(line_0_3.angleRad), 90, Rsouter, 0, 0));    // arc 3-1
			//console.info("geoms: " + geoms);
		}
		else {
			// round off the top slot corner
			var g2 = couplingSegments(g1[1], new Segment(point_2, point_3), Rcs_ag); 
			geoms.push(g2[0]);   // line 5a-3b
			if (g2[2].radius > 0 && Math.abs(g2[2].angle1 - g2[2].angle2) > 1e-10)
				{geoms.push(g2[2]);}   // arc 3b-3a
			geoms.push(g2[1]);   // line 3a-2
			// console.info("g2: " + g2);
			
			if (Ows > 0) {
				geoms.push(new Segment(point_2, point_1)); }   // line 2-1
		}
		
		if (this.Ith > 0) {
			// draw insulation geometry
			var point_1_ = new QPointF(0, Rsouter - this.Ods - this.Ith);
			var point_2_ = new QPointF(Ows/2, Rsouter - this.Ods - this.Ith);
			var Rsds_ = Rsouter - this.Sds + this.Ith;	
		
			// search for point 3_
			var line_3_5_ = sDraw.createLineParallel(line_3_5, this.Ith, 'left'); 
			if (this.Ods == 0) { // if there is open slot
				// point 3_ is on circle Rsouter!!!
				var point_3_ = sDraw.intersection(line_3_5_, circle_Rsouter, 'higher'); 
			}
			else {	
				var line_2_3_ = sDraw.createLineParallel(line_2_3, this.Ith, 'lower');
				var point_3_ = sDraw.intersection(line_2_3_, line_3_5_); 
			}
			var skippoint2_ = false;
			if (point_2_.x() > point_3_.x()) {
				// research for point 3_    (point_5a_ -> point_3_ -> point_1_)
				skippoint2_ = true;
				var line_1_2_ = sDraw.createLineBy2Points(point_1_.x(), point_1_.y(),point_2_.x(), point_2_.y());
				var point_3_ = sDraw.intersection(line_1_2_, line_3_5_); 
			}
			
			// search for point 5_
			var line_6_5_ = sDraw.createLineParallel(line_6_5, this.Ith, 'higher');
			var point_5_ = sDraw.intersection(line_3_5_, line_6_5_);   

			var point_6_ = new QPointF(0, Rsds_);
		
			var geomslot = 	new Array;
			
			// round off the bottom slot corner
			var Rcs_ = Rcs - this.Ith;
			if (Rcs_ < 0) {Rcs_ = 0;}
			if(stator.bottomCornerType == 'Round') {
				var point_minus_5_ = new QPointF(-point_5_.x(), point_5_.y());
				var g1 = couplingSegments(new Segment(point_minus_5_, point_5_), new Segment(point_3_, point_5_), Rcs_);
			}
			else { // bottomCornerType == 'General'
				var g1 = couplingSegments(new Segment(point_6_, point_5_), new Segment(point_3_, point_5_), Rcs_);
				geoms.push(g1[0]);   // line 6_-5b_
				geomslot.push(g1[0]);
			}
			if (g1[2].radius > 0 && Math.abs(g1[2].angle1 - g1[2].angle2) > 1e-10) {
				geoms.push(g1[2]);   // arc 5b_-5a_
				geomslot.push(g1[2]);
			}
			// g1[1] - line 5a_-3_
			//console.info("g1: " + g1);
			
			if (this.Ods == 0) { // if there is open slot
				// point 3_ is on circle Rsouter!!!
				geoms.push(g1[1]);     // line 5a_-3_
				geomslot.push(g1[1]); 
				
				var line_0_3_ = sDraw.createLineBy2Points(0, 0, point_3_.x(), point_3_.y());
				geomslot.push(new Arc(Math.radToDeg(line_0_3_.angleRad), 90, Rsouter, 0, 0));    // arc 1-3_
			}
			else {
				if (skippoint2_) {      // point_2_.x() > point_3_.x()
					// point_5a_ -> point_3_ -> point_1_
					geoms.push(g1[1]);     // line 5a_-3_
					geomslot.push(g1[1]); 
					
					geoms.push(new Segment(point_3_, point_1_));    // line 3_-1_
					geomslot.push(new Segment(point_3_, point_1_));
				}
				else {
					// round off the top slot corner
					var Rcs_ag_ = Rcs_ag - this.Ith;
					if (Rcs_ag_ < 0) {Rcs_ag_ = 0;}
					var g2 = couplingSegments(g1[1], new Segment(point_2_, point_3_), Rcs_ag_); 
					geoms.push(g2[0]);   // line 5a_-3b_
					geomslot.push(g2[0]);
					if (g2[2].radius > 0 && Math.abs(g2[2].angle1 - g2[2].angle2) > 1e-10) {
						geoms.push(g2[2]);   // arc 3b_-3a_
						geomslot.push(g2[2]);
					}	
					geoms.push(g2[1]);   // line 3a_-2_
					geomslot.push(g2[1]);
					//console.info("g2: " + g2);
					
					if (Ows > 0) {
						geoms.push(new Segment(point_2_, point_1_));    // line 2_-1_
						geomslot.push(new Segment(point_2_, point_1_)); 
					}
				}
			}
		}
		else {
			var geomslot = 	geoms;
		}
	}
	
	if (motor.isStatorNumberSlotLayersDouble()) {  // if double layer slot
		//console.info("geomslot: " + geomslot);
		if (motor.isStatorSlotLayersOrientationUpperLower()) {   // slot layers orientation "Upper/Lower"
			var slotdivider = divideSlotHorizontal_cpp(geomslot, this.Ibl);
			this.slotdivider = slotdivider;
			//console.info("slotdivider: " + slotdivider);
			
			var x1_slotdivider = slotdivider[0];
			var y1_slotdivider = slotdivider[1];
			var x2_slotdivider = slotdivider[2];
			var y2_slotdivider = slotdivider[3];
			
			var point1_slotdivider1 = new QPointF(0, y1_slotdivider);
			var point2_slotdivider1 = new QPointF(x1_slotdivider, y1_slotdivider);
			geoms.push(new Segment(point1_slotdivider1, point2_slotdivider1));
			
			if (Math.abs(y1_slotdivider - y2_slotdivider) > 1e-10) {
				var point1_slotdivider2 = new QPointF(0, y2_slotdivider);
				var point2_slotdivider2 = new QPointF(x2_slotdivider, y2_slotdivider);
				geoms.push(new Segment(point1_slotdivider2, point2_slotdivider2));
			}
		}
		else {                                                    // slot layers orientation "Left/Right"
			var slotdivider = divideSlotVertical(geomslot, this.Ibl);
			this.slotdivider = slotdivider;
			if (slotdivider != null) {
				var x_slotdivider = this.Ibl/2;
				var y1_slotdivider = slotdivider[0];
				var y2_slotdivider = slotdivider[1];
				//console.info("x_slotdivider: " + x_slotdivider + ", y1_slotdivider:" + y1_slotdivider + ", y2_slotdivider:" + y2_slotdivider);
				var point1_slotdivider = new QPointF(x_slotdivider, y1_slotdivider);
				var point2_slotdivider = new QPointF(x_slotdivider, y2_slotdivider);
				geoms.push(new Segment(point1_slotdivider, point2_slotdivider));
			}
		}
	}
	
	if (motor.isInnerRotor()) {
		var Rs = Rsinner;
		geoms.push(new Arc(90-360/Ns/2,90,Rsouter,0,0));  // outer radius arc	
	}
	else {               //Outer rotor
		var Rs = Rsouter;
		geoms.push(new Arc(90-360/Ns/2,90,Rsinner,0,0));  // outer radius arc	
	}


	if (this.Ods>0 && Ows>0) { // if there is slot opening
		geoms.push(new Segment(point_4, point_2));
		var line_0_4 = sDraw.createLineBy2Points(0, 0, point_4.x(), point_4.y());
		geoms.push(new Arc(Math.radToDeg(line_0_4.angleRad), 90, Rs, 0, 0));    // arc border between airgap and slot opening
		if (this.Rch>0 && this.Dch>0)  {  // if there are stator tooth chamfers
			this.point_4 = point_4;
			var args = toothChamfer(Rsinner, Rsouter, Ns, machineType, Ows, this.Dch, this.Rch, this.Tach, geoms);
			points = args[0];
			geoms = args[1];
			var alfa = Math.asin(Ows/Rs/2);
			var gamma = this.Rch*(Math.PI/Ns-alfa);
			//console.info("Math.radToDeg(gamma+alfa): " + Math.radToDeg(gamma+alfa));
			geoms.push(new Arc(90-360/Ns/2, 90 - Math.radToDeg(gamma+alfa), Rs, 0, 0));    // draw arc from slot pitch border to end of tooth chamfer
			var errorreported = false;
			var ip = 0;
			for (var i = 1; i < points.length; i++)  {
				var r = Math.sqrt(points[i].x()*points[i].x() + points[i].y()*points[i].y());   // point radius vector
				//console.info("r: " + r + "; Rs: " + Rs);
				if (motor.isInnerRotor()) {
					if (r < Rs-0.05*lag && !errorreported) {
						console.error("Tooth edge chamfer parameters are inconsistent: stator reaches beyond air gap bounds"); 
						errorreported = true; }
				}
				else {               //Outer rotor
					if (r > Rs+0.05*lag && !errorreported) {
						console.error("Tooth edge chamfer parameters are inconsistent: stator reaches beyond air gap bounds"); 
						errorreported = true; }
				}
				if (ip == 0) {
					if (Math.abs(r - Rs) < lag/5) {   // Math.abs(r - Rs): distance between point points[i] and airgap
						ip = i; 
						// console.info("ip: " + ip + "; lag/5: " + lag/5); 
					}
				}
			}
			var circle_Rs = sDraw.createCircle(0, 0, Rs);   // stator-airgap boundary circle
			// line_agch: line passing through point points[ip]; line_agch angle: 45 for inner rotor, -45 for outer rotor
			if (motor.isInnerRotor()) {
				var line_agch = sDraw.createLineByPointAngle(points[ip].x(), points[ip].y(), 45); } 
			else {               //Outer rotor
				var line_agch = sDraw.createLineByPointAngle(points[ip].x(), points[ip].y(), -45); } 
			
			var point_agch = sDraw.intersection(line_agch, circle_Rs, 'higher'); 	       // intersection point of line_agch and stator-airgap boundary circle_Rs
			var line_0_agch = sDraw.createLineBy2Points(0, 0, point_agch.x(), point_agch.y());   // line from (0,0) to point_agch
			
			geoms.push(new Arc(Math.radToDeg(line_0_agch.angleRad), Math.radToDeg(line_0_4.angleRad), Rs, 0, 0));    // draw arc from point_agch to point 4
			geoms.push(new Segment(point_agch, points[ip]));
		}
		else {
			geoms.push(new Arc(90-360/Ns/2, Math.radToDeg(line_0_4.angleRad), Rs, 0, 0));    // draw arc from slot pitch border to point 4
		}
	}
	else if (this.Ods == 0) { // if there is open slot
		if (this.Ws > 0) { // draw arc from slot pitch border to point 3 (point 3 is on circle Rs)
			geoms.push(new Arc(90-360/Ns/2, Math.radToDeg(line_0_3.angleRad), Rs, 0, 0));   
		}
	}
	else if (Ows == 0) { // if there is NO slot opening
		geoms.push(new Arc(90-360/Ns/2, 90, Rs, 0, 0));    // all the way border between airgap and stator
	}
	
	//console.info("call getGeometry of stator, result: [" + geoms.length + "] " + geoms);
	
	return geoms;
};
	
	
// call from с++ code
Stator.prototype.getMaterials = function() { // assign material types

	var materials = new Array();
	
	if (this.Ods == 0) {
		var Ows = 0;
		//var Tas = 0;
		//var Rcs_ag = 0;
	}
	else {
		var Ows = this.Ows;
		//var Tas = this.Tas;
		//var Rcs_ag = this.Rcs_ag;
	}
	var delta = 0.01;

	if (this.Ods > 0 && Ows > 0) {
		// slot opening
		if (motor.isInnerRotor()) {  // inner rotor
			materials.push( new GeneralMaterial(0, motor.statorInnerRadius + this.Ods/2)); }
		else {   // outer rotor	
			materials.push( new GeneralMaterial(0, motor.statorOuterRadius - this.Ods/2)); }	
			
		if (this.Rch>0 && this.Dch>0)  {  // if there are stator tooth chamfers
			if (motor.isInnerRotor()) {  // inner rotor
				materials.push( new GeneralMaterial(Ows/2 + delta, this.point_4.y() + this.Dch/2)); }
			else {   // outer rotor	
				materials.push( new GeneralMaterial(Ows/2 + delta, this.point_4.y() - this.Dch/2)); }
		}
	}	
	if (this.Ith > 0) {
		// slot insulation
		if (motor.isInnerRotor()) {  // inner rotor
			materials.push( new GeneralMaterial(0, motor.statorInnerRadius + this.Sds - this.Ith/2)); }
		else {   // outer rotor	
			materials.push( new GeneralMaterial(0, motor.statorOuterRadius - this.Sds + this.Ith/2)); }
	}	
	if (motor.isInnerRotor()) {  // inner rotor
		materials.push( new IronMaterial(0, motor.statorInnerRadius + this.Sds + delta)); }   // stator iron
	else {   // outer rotor		
		materials.push( new IronMaterial(0, motor.statorOuterRadius - this.Sds - delta)); }   // stator iron
	var slotdivider = this.slotdivider;		
	if (motor.isStatorNumberSlotLayersDouble()) {  // if double layer slot
		//console.info("double layer slot");
		if (motor.isStatorSlotLayersOrientationUpperLower()) {   // slot layers orientation "Upper/Lower"
			// return [x1, y_guess - insulationWidth/2, x2, y_guess + insulationWidth/2];
			var y1_slotdivider = slotdivider[1];
			var y2_slotdivider = slotdivider[3];
			//console.info("y1_slotdivider: " + y1_slotdivider + ", y2_slotdivider: " + y2_slotdivider);
			var materialWinding1 = new WindingMaterial(delta, y2_slotdivider + delta);
			//materialWinding1.pos = new QPointF(delta, y2_slotdivider + delta);
			if (motor.isInnerRotor()) {  // inner rotor
				materialWinding1.layer = 1; }
			else {   // outer rotor	
				materialWinding1.layer = 2; }
			materials.push(materialWinding1);   // winding layer 1
			var materialWinding2 = new WindingMaterial(delta, y1_slotdivider - delta);
			//materialWinding2.pos = new QPointF(delta, y1_slotdivider - delta);
			if (motor.isInnerRotor()) {  // inner rotor
				materialWinding2.layer = 2; }
			else {   // outer rotor	
				materialWinding2.layer = 1; }
			materials.push(materialWinding2);   // winding layer 2
			if (this.Ibl > 0) {
				//console.info("point: " + (y1_slotdivider + y2_slotdivider)/2);
				materials.push( new GeneralMaterial(delta, (y1_slotdivider + y2_slotdivider)/2)); }  // between layers insulation
		}
		else {  // slot layers orientation "Left/Right"
			if (slotdivider != null) {
				var y1_slotdivider = slotdivider[0];
				var y2_slotdivider = slotdivider[1];
				//console.info("y1_slotdivider: " + y1_slotdivider + ", y2_slotdivider: " + y2_slotdivider);
				var materialWinding = new WindingMaterial
				materialWinding.pos = new QPointF(this.Ibl/2 + delta, (y1_slotdivider + y2_slotdivider)/2);
				materialWinding.layer = 1;
				materials.push(materialWinding);
				if (this.Ibl > 0) {
					materials.push( new GeneralMaterial(this.Ibl/4, (y1_slotdivider + y2_slotdivider)/2)); }  // between layers insulation
			}
			else {
				console.error("Not able to assign subdomain material types because slotdivider = null"); }
		}
	}
	else {     // single layer slot
		var materialWinding = new WindingMaterial
		if (motor.isInnerRotor()) {  // inner rotor
			materialWinding.pos = new QPointF(delta, motor.statorInnerRadius + this.Sds/2); }
		else {   // outer rotor
			materialWinding.pos = new QPointF(delta, motor.statorOuterRadius - this.Sds/2); }
		materialWinding.layer = 1;
		materials.push(materialWinding);
	}

	return materials;
};
	
var stator = new Stator();

// stator widgets constuctor
function StatorUI(ui)
{
	this.ui = ui;

	this.labelSds = new QLabel("Slot depth (<b>Sds</b>):");
	this.labelSds.toolTip = "Use the ‘? Guide’ button above for explanation on this parameter";
	this.editSds = new QDoubleSpinBox;
	this.editSds.objectName = "Sds";
	this.editSds.setRange(0.0, Math.abs(motor.statorOuterRadius - motor.statorInnerRadius));
	this.editSds.decimals = 2;
	this.editSds.singleStep = 1;
	this.editSds.keyboardTracking = false;
	this.editSds.value = stator.Sds;
	this.unitsSds = new QLabel("mm");
	
	this.labelWs = new QLabel("Tooth width (<b>Ws</b>):");
	this.labelWs.toolTip = "Use the ‘? Guide’ button above for explanation on this parameter";
	this.editWs = new QDoubleSpinBox;
	this.editWs.objectName = "Ws";
	this.editWs.setRange(0, 10000000);
	this.editWs.decimals = 2;
	this.editWs.singleStep = 1;
	this.editWs.keyboardTracking = false;
	this.editWs.value = stator.Ws;
	this.unitsWs = new QLabel("mm");

	this.labelOds = new QLabel("Slot opening depth (<b>Ods</b>):");
	this.labelOds.toolTip = "Use the ‘? Guide’ button above for explanation on this parameter";
	this.editOds = new QDoubleSpinBox;
	this.editOds.objectName = "Ods";
	this.editOds.setRange(0, 10000000);
	this.editOds.decimals = 2;
	this.editOds.singleStep = 0.1;
	this.editOds.keyboardTracking = false;
	this.editOds.value = stator.Ods;
	this.unitsOds = new QLabel("mm");

	this.labelOws = new QLabel("Slot opening width (<b>Ows</b>):");
	this.labelOws.toolTip = "Use the ‘? Guide’ button above for explanation on this parameter";
	this.editOws = new QDoubleSpinBox;
	this.editOws.objectName = "Ows";
	this.editOws.setRange(0, 10000000);
	this.editOws.decimals = 2;
	this.editOws.singleStep = 0.1;
	this.editOws.keyboardTracking = false;
	this.editOws.value = stator.Ows;
	this.unitsOws = new QLabel("mm");

	this.labelTas = new QLabel("Tooth tip angle (<b>Tas</b>):");
	this.labelTas.toolTip = "Use the ‘? Guide’ button above for explanation on this parameter";
	this.editTas = new QDoubleSpinBox;
	this.editTas.objectName = "Tas";
	this.editTas.setRange(0, 90);
	this.editTas.decimals = 1;
	this.editTas.singleStep = 1;
	this.editTas.keyboardTracking = false;
	this.editTas.value = stator.Tas;
	this.unitsTas = new QLabel("°");

	this.labelBottomCornerType = new QLabel("Bottom corner type:");
	this.comboboxBottomCornerType = new QComboBox();
	this.comboboxBottomCornerType.objectName = "bottomCornerType";
	this.comboboxBottomCornerType.addItem("General");
	this.comboboxBottomCornerType.addItem("Round");
	this.comboboxBottomCornerType.currentText = stator.bottomCornerType;
	
	this.labelRcs = new QLabel("Bottom corner radius (<b>Rcs</b>):");
	this.labelRcs.toolTip = "Use the ‘? Guide’ button above for explanation on this parameter";
	this.editRcs = new QDoubleSpinBox;
	this.editRcs.objectName = "Rcs";
	this.editRcs.setRange(0, 10000000);
	this.editRcs.decimals = 2;
	this.editRcs.singleStep = 1;
	this.editRcs.keyboardTracking = false;
	this.editRcs.value = stator.Rcs;
	this.unitsRcs = new QLabel("mm");

	this.labelRcs_ag = new QLabel("Top corner radius (<b>Rcs_ag</b>):");
	this.labelRcs_ag.toolTip = "Use the ‘? Guide’ button above for explanation on this parameter";
	this.editRcs_ag = new QDoubleSpinBox;
	this.editRcs_ag.objectName = "Rcs_ag";
	this.editRcs_ag.setRange(0, 10000000);
	this.editRcs_ag.decimals = 2;
	this.editRcs_ag.singleStep = 1;
	this.editRcs_ag.keyboardTracking = false;
	this.editRcs_ag.value = stator.Rcs_ag;
	this.unitsRcs_ag = new QLabel("mm");

	this.labelDch = new QLabel("Chamfer depth (<b>Dch</b>):");
	this.labelDch.toolTip = "Use the ‘? Guide’ button above for explanation on this parameter";
	this.editDch = new QDoubleSpinBox;
	this.editDch.objectName = "Dch";
	this.editDch.setRange(0, 10000000);
	this.editDch.decimals = 2;
	this.editDch.singleStep = 0.1;
	this.editDch.keyboardTracking = false;
	this.editDch.value = stator.Dch;
	this.unitsDch = new QLabel("mm");
	
	this.labelRch = new QLabel("Chamfer ratio (<b>Rch</b>):");
	this.labelRch.toolTip = "Use the ‘? Guide’ button above for explanation on this parameter";
	this.editRch = new QDoubleSpinBox;
	this.editRch.objectName = "Rch";
	this.editRch.setRange(0, 1);
	this.editRch.decimals = 2;
	this.editRch.singleStep = 0.05;
	this.editRch.keyboardTracking = false;
	this.editRch.value = stator.Rch;
	
	this.labelTach = new QLabel("Chamfer angle (<b>Tach</b>):");
	this.labelTach.toolTip = "Use the ‘? Guide’ button above for explanation on this parameter";
	this.editTach = new QSpinBox;
	this.editTach.objectName = "Tach";
	this.editTach.setRange(0, 90);
	this.editTach.singleStep = 1;
	this.editTach.keyboardTracking = false;
	this.editTach.value = stator.Tach;
	this.unitsTach = new QLabel("°");
	
	this.labelIth = new QLabel("Slot insulation thickness (<b>Ith</b>):");
	this.labelIth.toolTip = "Use the ‘? Guide’ button above for explanation on this parameter";
	this.editIth = new QDoubleSpinBox;
	this.editIth.objectName = "Ith";
	this.editIth.setRange(0, 10000000);
	this.editIth.decimals = 2;
	this.editIth.singleStep = 0.1;
	this.editIth.keyboardTracking = false;
	this.editIth.value = stator.Ith;
	this.unitsIth = new QLabel("mm");
	
	this.labelIbl = new QLabel("Between layers insulation thickness (<b>Ibl</b>):");
	this.labelIbl.toolTip = "Use the ‘? Guide’ button above for explanation on this parameter";
	this.editIbl = new QDoubleSpinBox;
	this.editIbl.objectName = "Ibl";
	this.editIbl.setRange(0, 10000000);
	this.editIbl.decimals = 2;
	this.editIbl.singleStep = 0.1;
	this.editIbl.keyboardTracking = false;
	this.editIbl.value = stator.Ibl;
	this.unitsIbl = new QLabel("mm");
	
	this.layout = new QGridLayout(ui);
	
//	{ // Пример создания группы
//		var groupBox1 = new QGroupBox("Slot setting"); // Группа
//		var layoutGroup = new QGridLayout(groupBox1); // Конпоновщик виджетов группы
	
//		layoutGroup.addWidget(this.labelSds, 0, 0);
//		layoutGroup.addWidget(this.editSds, 0, 1);
//		layoutGroup.addWidget(this.unitsSds, 0, 2);

//		layoutGroup.addWidget(this.labelWs, 1, 0);
//		layoutGroup.addWidget(this.editWs, 1, 1);
//		layoutGroup.addWidget(this.unitsWs, 1, 2);

//		layoutGroup.addWidget(this.labelOds, 2, 0);
//		layoutGroup.addWidget(this.editOds, 2, 1);
//		layoutGroup.addWidget(this.unitsOds, 2, 2);

//		layoutGroup.addWidget(this.labelOws, 3, 0);
//		layoutGroup.addWidget(this.editOws, 3, 1);
//		layoutGroup.addWidget(this.unitsOws, 3, 2);

//		this.layout.addWidget(groupBox1, 0, 0, 1, 3); // Добавить группу в основной компоновщик,
													  // 1 = группа занимает одну строку
													  // 3 = группа занимает три колонки
//	}

	this.layout.addWidget(this.labelSds, 0, 0);
	this.layout.addWidget(this.editSds, 0, 1);
	this.layout.addWidget(this.unitsSds, 0, 2);

	this.layout.addWidget(this.labelWs, 1, 0);
	this.layout.addWidget(this.editWs, 1, 1);
	this.layout.addWidget(this.unitsWs, 1, 2);

	this.layout.addWidget(this.labelOds, 2, 0);
	this.layout.addWidget(this.editOds, 2, 1);
	this.layout.addWidget(this.unitsOds, 2, 2);

	this.layout.addWidget(this.labelOws, 3, 0);
	this.layout.addWidget(this.editOws, 3, 1);
	this.layout.addWidget(this.unitsOws, 3, 2);

	this.layout.addWidget(this.labelTas, 4, 0);
	this.layout.addWidget(this.editTas, 4, 1);
	this.layout.addWidget(this.unitsTas, 4, 2);

	this.layout.addWidget(this.labelBottomCornerType, 5, 0);
	this.layout.addWidget(this.comboboxBottomCornerType, 5, 1, 1, 2);
	
	this.layout.addWidget(this.labelRcs, 6, 0);
	this.layout.addWidget(this.editRcs, 6, 1);
	this.layout.addWidget(this.unitsRcs, 6, 2);
	
	this.layout.addWidget(this.labelRcs_ag, 7, 0);
	this.layout.addWidget(this.editRcs_ag, 7, 1);
	this.layout.addWidget(this.unitsRcs_ag, 7, 2);
	
	var group_chamfer = new QGroupBox("Tooth edge chamfer"); // Группа
	var layoutGroup_chamfer = new QGridLayout(group_chamfer); // Конпоновщик виджетов группы

	layoutGroup_chamfer.addWidget(this.labelDch, 0, 0);
	layoutGroup_chamfer.addWidget(this.editDch, 0, 1);
	layoutGroup_chamfer.addWidget(this.unitsDch, 0, 2);

	layoutGroup_chamfer.addWidget(this.labelRch, 1, 0);
	layoutGroup_chamfer.addWidget(this.editRch, 1, 1);

	layoutGroup_chamfer.addWidget(this.labelTach, 2, 0);
	layoutGroup_chamfer.addWidget(this.editTach, 2, 1);
	layoutGroup_chamfer.addWidget(this.unitsTach, 2, 2);
	
	this.layout.addWidget(group_chamfer, 8, 0, 1, 3); // Добавить группу в основной компоновщик
	
	var group_insulation = new QGroupBox("Insulation");
	var layoutGroup_insulation = new QGridLayout(group_insulation);
	
	layoutGroup_insulation.addWidget(this.labelIth, 0, 0);
	layoutGroup_insulation.addWidget(this.editIth, 0, 1);
	layoutGroup_insulation.addWidget(this.unitsIth, 0, 2);
	
	layoutGroup_insulation.addWidget(this.labelIbl, 1, 0);
	layoutGroup_insulation.addWidget(this.editIbl, 1, 1);
	layoutGroup_insulation.addWidget(this.unitsIbl, 1, 2);
	
	this.layout.addWidget(group_insulation, 9, 0, 1, 3); // Добавить группу в основной компоновщик
};

StatorUI.prototype.statorDiameterChanged = function() {
	var d = Math.abs(motor.statorOuterRadius - motor.statorInnerRadius);
	//statorUI.editSds.setRange(0, d);
};

// called if statorOuterDiameter changed
motor.statorOuterDiameterChanged.connect(this, function(d) {
	//console.log("outer diameterChanged " + d);
	statorUI.statorDiameterChanged();
});

// called if statorInnerDiameter changed
motor.statorInnerDiameterChanged.connect(this, function(d) {
	//console.log("inner diameterChanged " + d);
	statorUI.statorDiameterChanged() 
});

// update widgets of stator items
StatorUI.prototype.updateUI = function()
{
	//console.log("statorUI.updateUI");
	
	var visibleRcs = stator.bottomCornerType == "General";
	//console.info("stator.bottomCornerType: " + stator.bottomCornerType);
	this.labelRcs.setVisible(visibleRcs);
	this.editRcs.setVisible(visibleRcs);
	this.unitsRcs.setVisible(visibleRcs);
	
	var visibleIbl = motor.isStatorNumberSlotLayersDouble();
	this.labelIbl.setVisible(visibleIbl);
	this.editIbl.setVisible(visibleIbl);
	this.unitsIbl.setVisible(visibleIbl);
}

// call this function from c++ code
createStatorUI = function(ui)
{
	statorUI = new StatorUI(ui);
	statorUI.updateUI();
}
