/*
NAME: 
DESCRIPTION: ;
PARAMETER:
    [
		{
			name : 'pcs',
			title : 'pcs名称',
			type : 'LineEdit',
			property : {tool_tip : 'pcs名称'},
		},
		{
			name : 'set',
			title : 'set名称',
			type : 'LineEdit',
			property : {tool_tip : 'set名称'},
		},
		{
			name : 'panel',
			title : 'panel名称',
			type : 'LineEdit',
			property : {tool_tip : 'panel名称'},
		},
		{
			name : 'outline',
			title : 'outline层名称',
			type : 'LineEdit',
			property : {tool_tip : 'outline层名称'},
		},
		{
			name : 'drill',
			title : 'drill层名称',
			type : 'LineEdit',
			property : {tool_tip : 'drill层名称'},
		},
		{
			name : 'dc',
			title : '钉床层名称',
			type : 'LineEdit',
			property : {tool_tip : '钉床层名称'},
		},
		{
            name : 'auto_save',
			title : '自动保存',
            type : 'RadioBox',
            property : {
				item_list:[
					{name:'yes',text:'YES'},
					{name:'no',text:'NO'},
				],
				tool_tip:'是否自动保存料号开关'
			}
        }
	]
	
 VERSION_HISTORY:
	V1.00 2020-09-01 Scott Sun
	    1.新版本
		
 HELP:
 	<html><body bgcolor="#DDECFE">
		<font size="3" color="#003DB2"><p>功能简介</p></font>
		<p> 生成导气板及输出 </p>
		<br>
		<font size="3" color="#003DB2"><p>参数配置</p></font>
		<p> step信息 </p>
		<br>
		<font size="3" color="#003DB2"><p>注意事项</p></font>
		<p> 无 </p>
		<br>
	</body></html>	
*/
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
console.log("==============================>template");
// 引入模块 包
var $ = require('topcam.scriptfunc').argv();
var fs = require('fs');
var _ = require('lodash');
var mode = $.ikm ? "topcam" : "aimdfm";
var IKM = $.ikm; 
if (IKM==undefined ) { IKM = require('topcam.ikm6')($) }
var GEN = $.gen;
var GUI = $.gui || {};
var Job = $.job || $.job_name;
var JobId = $.job_id;
var db = $.db || IKM.db
var PAR = {}
if ($.par) { PAR = $.par } else if ($.hasOwnProperty('script_parameter')){ PAR = JSON.parse($.script_parameter); }
if (mode === "aimdfm") {
	var database = require("topsin.database");
	database.addConnection($.conf.database_conf, "DFM");
	var QDfm = database.query("DFM");
	$.QDfm = QDfm;
	if ($.conf.product_type == "aimdfm") {
		QDfm.updateRow({ table: "pdm_aimdfm_task", data: { current_process_title: $.process_title }, where: { id: $.task_id } });
	}
}
require("topsin.genmath")
var genMath = new GenMath();
addApi(genMath);
var gui = new NewGUI(GUI);
var Status = 'ok';
var resultData = [];
var par = PAR;
var default_par = {
    pcs:"edit",
    set:"step",
    panel:"panel",
    outline:"out",
    drill:"drl",
    dc:"dc",
	auto_save: "No",
	units:"mm"
}
for(var key in default_par){ if (!par.hasOwnProperty(key) || par[key] == ""){ par[key] = default_par[key] }}
var job = Job;
try {
	if(_.isEmpty(job)){throw "参数job不存在"} else { job = job.toLowerCase() }
	if(!GEN.isJobExists({job:job})){throw "料号"+job+"不存在"}
	if(!GEN.isJobOpen({job:job})){ GEN.openJob({job:job}) }
    if(mode == "aimdfm"){ if(GEN.checkInout({job:job,mode:"test"}) != 0){ throw "the job check" } else { GEN.checkInout({job:job,mode:"out"}) } }
	var stepList = GEN.getStepList({job:job})
	stepList = stepList.filter(function(step){
		var reg = new RegExp(par.step,"ig")
		return reg.test(step)
	})
	var edit = par.pcs;
	var step = par.set;
	var panel = par.panel;
	var out_layer = par.outline;
	var drill_layer = par.drill;
	var dc_layer = par.dc;
	var auto_save = par.auto_save;
	var units = par.units;

	GEN.openStep({job:job,name:panel});
	GEN.clearLayers();
	GEN.affectedLayer( {mode:'all',affected:'no'} );
	GEN.COM( "sel_options,clear_mode=clear_after,display_mode=all_layers,area_inout=inside,area_select=select,select_mode=standard,area_touching_mode=exclude");
	GEN.units({ type:units });
	GEN.zoomHome();
	GEN.createLayer({job:job,layer:dc_layer,context:'misc',type:'drill',delete_exists:'yes'});


	// 添加板边孔
	// copy钻孔层所有r3148的孔和r490的孔
	GEN.workLayer({name:drill_layer, clear_before:'yes'});
	GEN.selectByFilter({feat_type:'pad', polarity:'positive', include_syms:'r3148\;r490', profile:'in'});
	if (GEN.getSelectCount()>0){GEN.selCopyOther({dest:'layer_name', target_layer:dc_layer}) };
	// 距离板内0.2mm添加一圈2.6mm的孔,间隔距离30~50mm
	GEN.workLayer({name:dc_layer, clear_before:'yes'});
	var dist_edge = 0.2; // 孔边距离板内的距离
	var size = 2600;
	var dist_center = dist_edge + size / 2000;
	var symbol = 'r' + size;
	var sr_limit = GEN.getSRLimits({job:job,step:panel,units:units});
	sr_limit.xmin = Number(sr_limit.xmin) - dist_center;
	sr_limit.xmax = Number(sr_limit.xmax) + dist_center;
	sr_limit.ymin = Number(sr_limit.ymin) - dist_center;
	sr_limit.ymax = Number(sr_limit.ymax) + dist_center;
	sr_limit.xsize = sr_limit.xmax - sr_limit.xmin;
	sr_limit.ysize = sr_limit.ymax - sr_limit.ymin;
	var nx = parseInt(sr_limit.xsize / 30) + 1;
	var dx = sr_limit.xsize / (nx - 1);
	var ny = parseInt(sr_limit.ysize / 30) - 1;
	var dy = sr_limit.ysize / (ny + 1);
	GEN.addPad({x:sr_limit.xmin,y:sr_limit.ymin,nx:nx,dx:dx * 1000,symbol:symbol});
	GEN.addPad({x:sr_limit.xmin,y:sr_limit.ymax,nx:nx,dx:dx * 1000,symbol:symbol});
	GEN.addPad({x:sr_limit.xmin,y:sr_limit.ymin + dy,ny:ny,dy:dy * 1000,symbol:symbol});
	GEN.addPad({x:sr_limit.xmax,y:sr_limit.ymin + dy,ny:ny,dy:dy * 1000,symbol:symbol});
	// 去除与dir层料号孔重叠的孔
	GEN.selRefFeat({layers:drill_layer,mode:'touch',include_syms:'r490',filter:{include_syms:symbol}});
	if (GEN.getSelectCount() > 0){GEN.selDelete() };
	// 添加拼版间距孔
	var repeat = GEN.getRepeat({job:job,step:panel,units:units});
	
	var gap_lines = get_gap_lines({repeat:repeat});
	
	var gap_pin_syms = get_gap_pin_syms({gap_lines:gap_lines, step : 30, size_list : [2600, 1600]});

	gap_pin_syms.forEach(function(sym){
		GEN.addPad(sym);
	})

	// 添加板内捞槽孔
	var work_step = GEN.isStepExists({job:job,step:step}) ? step : edit;
	if (!GEN.isStepExists({job:job,step:work_step})) {
		throw "不存在edit或step:" + work_step
	}
	GEN.createLayer({job:job,layer:'tl_script_surface',delete_exists:'yes'});
	if (work_step == step) {
		GEN.openStep({job:job,name:edit});
		GEN.srFill({layer:'tl_script_surface'});
		GEN.closeStep();
	}
	GEN.openStep({job:job,name:work_step});
	GEN.units({type:units} );
	GEN.srFill({layer:'tl_script_surface'});
	GEN.flattenLayer({source_layer:'tl_script_surface',target_layer:'tl_script_surface_flatten'});
	GEN.workLayer({name:'tl_script_surface_flatten', clear_before:'yes'});
	GEN.selContourize();
	GEN.workLayer({name:out_layer, clear_before:'yes'});
	GEN.selCopyOther({target_layer:'tl_script_surface_flatten',invert:'yes'});
	GEN.workLayer({name:'tl_script_surface_flatten', clear_before:'yes'});
	GEN.selContourize();
	var features = GEN.getFeatures({job:job,step:work_step,layer:'tl_script_surface_flatten',options:'feat_index',surface:1,units:units});
	//$GUI->debug(dump(@features));
	var indexes = [];
	var polygons = [];
	features.forEach(function(feature){
		if(feature.type == "surface"){
			if(feature.feats.filter(function(v){return /^#OB \S+ \S+ H$/.test(v)}).length == 0){
				var polygon = profile2Polygon(feature.feats);
				var area = polygonArea(JSON.parse(JSON.stringify(polygon)));
				if(area && area > 30){
					indexes.push(feature.index)
					polygons.push(polygon)
				}
			}
		}
	})
	if(GEN.isLayerExists({job:job,layer:"tl_script_inner_rout"})){GEN.deleteLayer({job:job,layer:"tl_script_inner_rout"})}
	GEN.createLayer({job:job,layer:'tl_script_inner_rout'});
	indexes.forEach(function(index){
		GEN.COM("sel_layer_feat,operation=select,layer=tl_script_surface_flatten,index="+index);
	})
	if(GEN.getSelectCount()>0){
		GEN.selCopyOther({target_layer:'tl_script_inner_rout'}) 
	};
	
	if(GEN.isLayerExists({job:job,layer:"tl_script_inner_dc"})){GEN.deleteLayer({job:job,layer:"tl_script_inner_dc"})}
	GEN.createLayer({job:job,layer:'tl_script_inner_dc',delete_exists:'yes'});
	GEN.workLayer({name:'tl_script_inner_dc', clear_before:'yes'});
	var rout_pin_syms = get_rout_pin_syms({polygons:polygons, step:30, size_list:[2600, 1600]});	
	rout_pin_syms.forEach(function(sym){
		GEN.addPad(sym);
	})
	
	// 添加防焊开窗孔
	var matrix = GEN.getMatrix({job:job});
	var tmp_matrix = {};
	// 获取防焊层
	for (var layer in matrix) {
		if(matrix[layer].context == "board" && matrix[layer].layer_type=="solder_mask"){
			tmp_matrix[layer] = matrix[layer];
		}
	}
	var sm_layer = gui.selectSingle({title:"请选择防焊层别",list: Object.keys(tmp_matrix).map(function(key){
		var tmp = {};
		tmp[key] = key
		return tmp
	}),"default": Object.keys(tmp_matrix)[0]}) 

		var outer_layer = sm_layer == /smb/ ? 'bot' : 'top';
		GEN.flattenLayer({source_layer:outer_layer,target_layer:outer_layer+'_flatten'});
		GEN.flattenLayer({source_layer:drill_layer,target_layer:drill_layer+'_flatten'});
		GEN.flattenLayer({source_layer:sm_layer,target_layer:sm_layer+'_flatten'});
		GEN.workLayer({name:sm_layer+'_flatten', clear_before:'yes'});
		GEN.selContourize();
		GEN.selRefFeat({layers:'tl_script_inner_rout'});
		if( GEN.getSelectCount()>0){GEN.selDelete()};
		GEN.selRefFeat({layers:drill_layer+'_flatten'});
		if( GEN.getSelectCount()>0){GEN.selDelete()};
		var out_symbols_sys = GEN.getLayerSymsHist({job:job,step:work_step,layer:outer_layer+'_flatten',units:'mm'});
		var out_symbols = Object.keys(out_symbols_sys).filter(function(symbol){
			if(/^[rs]([0-9.]+)/.test(symbol)){
				return RegExp.$1 < 1600
			} else if (/^(rect|oval)([0-9.]+)x([0-9.]+)/.test(symbol)) {
				return RegExp.$2 < 1600 || RegExp.$3 < 1600
			} else {
				return false
			}
		})
		GEN.selRefFeat({layers:outer_layer+'_flatten', include_syms:out_symbols.join('\;')});
		//$GUI->debug(dump(@out_symbols));
		GEN.closeStep();
		
		GEN.openStep({job:job,name:panel});
		GEN.units( {type:units} );
		GEN.flattenLayer({source_layer:'tl_script_inner_dc',target_layer:'tl_script_inner_dc_flatten'});
		GEN.workLayer({name:'tl_script_inner_dc_flatten', clear_before:'yes'});
		GEN.selCopyOther({target_layer:dc_layer});
		
		GEN.workLayer({name:dc_layer, clear_before:'yes'});
		GEN.selCopyOther({target_layer:dc_layer+'_tmp'});
		GEN.workLayer({name:dc_layer+'_tmp', clear_before:'yes'});
		GEN.COM("sel_resize,size=60000,corner_ctl=no");
		
		GEN.flattenLayer({source_layer:sm_layer+'_flatten',target_layer:sm_layer+'_flatten_panel'});
		
		GEN.createLayer({job:job,layer:'tl_script_sm_dc',delete_exists:'yes'});
		GEN.workLayer({name:'tl_script_sm_dc', clear_before:'yes'});
		var sm_features = GEN.getFeatures({job:job,step:panel,layer:sm_layer+'_flatten_panel',options:'feat_index',surface:1,units:units});
		sm_features.forEach(function(feature){
			if(feature.type == "surface"){
				if(feature.feats.filter(function(v){return /^#OB \S+ \S+ H$/.test(v)}).length == 0){
					var polygon = profile2Polygon(feature.feats);
					var limits = polygonLimits(polygon);
					if(limits.xsize >= 1.6 && limits.ysize >= 1.6){
						var x = (Number(limits.xmin) + Number(limits.xmax)) / 2;
						var y = (Number(limits.ymin) + Number(limits.ymax)) / 2;
						GEN.addPad({x:x, y:y, symbol:'r1600'});
					}
				}
			}
		})
		
		GEN.selRefFeat({layers:sm_layer+'_flatten_panel', mode:'cover'});
		GEN.selReverse();
		if( GEN.getSelectCount() > 0){GEN.selDelete() };
		
		GEN.selRefFeat({layers:dc_layer+'_tmp'});
		if( GEN.getSelectCount() > 0){GEN.selDelete() };
		GEN.selCopyOther({target_layer:dc_layer});
		
		
		// 清理辅助层
		matrix = GEN.getMatrix({job:job,type:'hash'});
		var tmp_layers = Object.keys(matrix).filter(function(v){return /^tl_script/.test(v)});
		GEN.deleteLayer({job:job,layer:tmp_layers});
		GEN.deleteLayer({job:job,layer:outer_layer+'_flatten'});
		GEN.deleteLayer({job:job,layer:drill_layer+'_flatten'});
		GEN.deleteLayer({job:job,layer:sm_layer+'_flatten'});
		GEN.deleteLayer({job:job,layer:sm_layer+'_flatten_panel'});
		GEN.deleteLayer({job:job,layer:dc_layer+'_tmp'});


	// 保存 
	if(/yes/ig.test(par.auto_save)){GEN.checkInout({job:job,mode:"out"}); GEN.saveJob({ job: job });GEN.checkInout({job:job,mode:"in"});GEN.closeJob({job:job});} else {GEN.checkInout({job:job,mode:"in"})}
	if (mode === "aimdfm") {
		$.QDfm.updateRow({table: "pdm_aimdfm_task",data: {progress: 33.33},where: { id: $.task_id }});
		if (GEN.hasError()) { Status = 'error';resultData.push({ type: "error", title: "GEN错误!", detail: [{ desc: _.join(GEN.STATUS, "\n") }] });
			return {status: Status,result_data: resultData};
		} else { resultData.push({ type: "info", title: "操作完成, 请注意检查!" }); return {status: Status,result_data: resultData}; }
	}else { return "Done" }
} catch (e) {
	IKM.msg(_.join(GEN.STATUS, "\n"));IKM.msg(e);Status = 'error';
    resultData.push({type: "error", title: "脚本执行出错!", detail: [{desc: _.toString(e)}]});
    return (mode === "aimdfm") ? {status: Status, result_data: resultData} : "Error";
}


function get_rout_pin_syms(par) {
	var polygons = par.polygons;
	var step = par.step;
	var size_list = par.size_list.sort(function(a,b){return b-a})
    var symbols = [];
	polygons.forEach(function(polygon){
        var limits = polygonLimits(polygon);
        var sym_size;
		size_list.forEach(function(size){
			if (limits.xsize >= (size / 1000) && limits.ysize >= (size / 1000) && !sym_size) {
				sym_size = size;
			}
        })
		if (sym_size) {
		  var box = genMath.resizeBox(limits, -(2 + sym_size / 2000));
		  resizeBox(box)
		  var direct = limits.xsize > limits.ysize ? 'w' : 'n';
		  var points = genMath.gpListArea(box, step, direct, '', 'normal');
		  points = points.map(function (v) {
		    v.symbol = "r" + sym_size;
		    return v
		  });
		  points.forEach(function (point) {
		    var point_size = point.symbol.slice(1) - 0;
			var size_idx = size_list.reduce(function(a,b){if(b == point_size){a=b} ;return a}, 0)
			size_list.forEach(function(v,i){if(v==size_idx){size_idx = i}})
		    var dist = point2polygon_dist(point, polygon);
		    if (dist <= (point_size / 2000)) {
		      size_idx++;
		      while (size_idx < size_list.length) {
		        var size = size_list[size_idx];
		        if (dist > (size / 2000)) {
		          point.symbol = "r" + size;
		          symbols.push(point);
		          break;
		        }
		        size_idx++;
		      }
		    } else {
		      symbols.push(point)
		    }
		  })
		}
	})
	return symbols
}

function polygonLimits(par) {
	var tmpx = par.map(function(v){return v.x}).sort(function(a,b){return a-b})
	var xmin = tmpx[0]
	var xmax = tmpx[tmpx.length - 1]
	var tmpy = par.map(function(v){return v.y}).sort(function(a,b){return a-b})
	var ymin = tmpy[0]
	var ymax = tmpy[tmpy.length - 1]
	var xsize = xmax - xmin;
	var ysize = ymax - ymin;
	return {xmin:xmin, xmax:xmax, ymin:ymin, ymax:ymax, xsize:xsize, ysize:ysize};
}

function polygonArea(points) {
	if (points.length < 3) {
		return;
	}
	if(points[0].x != points[points.length - 1].x || points[0].y != points[points.length - 1].y){
		points.push(points[0])
	}
	var area = 0;
	while(points.length >= 2){
		area += points[0].x*points[1].y - points[1].x*points[0].y;
		points.shift();
	}
	return area > 0 ? (area/2.0) : -(area/2.0);
}

function get_gap_pin_syms(par) {
	var gap_lines = par.gap_lines;
	var step = par.step;
	var size_list = par.size_list.sort(function(b,a){return a-b});
	var symbols = [];
	gap_lines.forEach(function(line){
		Object.keys(line).forEach(function(key){
            line[key] = Number(line[key])
        })
		var sym_size;
		size_list.forEach(function(size){
			if (line.width >= size / 1000 && !sym_size) {
				sym_size = size;
			}
		})
		if(sym_size){
			var symbol = "r"+sym_size;
			if (line.xs == line.xe) {
				for (var y = line.ys + 5; y <= line.ye - 5; y += 30) {
					var pin = {x:line.xs,y:y,symbol:symbol};
					var found_nearly = 0;
					symbols.forEach(function(other){
						var dist = genMath.point2PointDis(pin,other);
						if (dist < 29 && !found_nearly) {
							found_nearly = 1;
						}
					})
					if(found_nearly==0){
						symbols.push(pin)
					}
				}
			} else if (line.ys == line.ye) {
				for (var x = line.xs + 5; x <= line.xe - 5; x += 30) {
					var pin = {x:x,y:line.ys,symbol:symbol};
					var found_nearly = 0;
					symbols.forEach(function(other){
						var dist = genMath.point2PointDis(pin,other);
						if (dist < 29 && !found_nearly) {
							found_nearly = 1;
						}
					})
					if(found_nearly==0){
						symbols.push(pin)
					}
				}
			}
		}
	})
	return symbols
}

function get_gap_lines(par) {
	var repeat = par.repeat;
	var lines = [];
	for (var i = 0; i < repeat.length; i++) {
		var step1 = repeat[i];
		for (var j = i+1; j < repeat.length; j++) {
			var step2 = repeat[j];
			// 判断x和y方向坐标范围是否有重合,有部分重合且相邻的才需要处理
			if ((step1.xmax < step2.xmin || step1.xmin > step2.xmax) && (step1.ymax < step2.ymin || step1.ymin > step2.ymax)) {
				continue;
			}
			if (step1.xmax < step2.xmin || step1.xmin > step2.xmax) {
				var gap_x = genMath.minInArray(Math.abs(step1.xmax - step2.xmin),Math.abs(step1.xmin - step2.xmax));
				if (gap_x > 0 && gap_x < 20) {
					var x_sites = [step1.xmin, step1.xmax, step2.xmin, step2.xmax].sort(function(a,b){return a-b});
					var y_sites = [step1.ymin, step1.ymax, step2.ymin, step2.ymax].sort(function(a,b){return a-b});
					var xs = (Number(x_sites[1]) + Number(x_sites[2])) / 2;
					var xe = xs;
					var ys = y_sites[1];
					var ye = y_sites[2];
					var line = {xs:xs,xe:xe,ys:ys,ye:ye,width:gap_x};
					lines.push(line)
				}
			}
			else {
				var gap_y = genMath.minInArray(Math.abs(step1.ymax - step2.ymin),Math.abs(step1.ymin - step2.ymax));
				if (gap_y > 0 && gap_y < 20) {
					var x_sites = [step1.xmin, step1.xmax, step2.xmin, step2.xmax].sort(function(a,b){return a-b});
					var y_sites = [step1.ymin, step1.ymax, step2.ymin, step2.ymax].sort(function(a,b){return a-b});
					var xs = x_sites[1];
					var xe = x_sites[2];
					var ys = (Number(y_sites[1]) + Number(y_sites[2])) / 2;
					var ye = ys;
					var line = {xs:xs,xe:xe,ys:ys,ye:ye,width:gap_y};
					lines.push(line)
				}
			}
		}
	}
	// 截断与step相交的线
	for (var i = lines.length; i >= 0; i--) {
		var line = lines[i];
		repeat.forEach(function(step){
			var rect = {x1:step.xmin,x2:step.xmax,y1:step.ymin,y2:step.ymax};
			var points = get_line_rect_intersect(line, rect);
			// var dist = genMath.line2RectDis(line,rect); // js genmath无此api   
			if (points.length > 0) {
				var new_lines = [];
				if (points.length == 1) {
					if (line.xs == line.xe) {
						if (line.ys > rect.y1 && line.ys < rect.y2) {
							new_lines.push({xs : line.xe, ys : line.ye, xe : points[0].x, ye : points[0].y, width:line.width});
						}
						else {
							new_lines.push({xs : line.xs, ys : line.ys, xe : points[0].x, ye : points[0].y, width:line.width});
						}
					}
					else if (line.ys == line.ye) {
						if (line.xs > rect.x1 && line.xs < rect.x2) {
							new_lines.push({xs : line.xe, ys : line.ye, xe : points[0].x, ye : points[0].y, width:line.width});
						}
						else {
							new_lines.push({xs : line.xs, ys : line.ys, xe : points[0].x, ye : points[0].y, width:line.width});
						}
					}
				}
				else if (points.length == 2) {
					if (line.xs == line.xe) {
						var sites = [{x:line.xs,y:line.ys},{x:line.xe,y:line.ye},points[0],points[1]].sort(function(a,b){return a.y-b.y});
						new_lines.push({xs : sites[0].x, ys : sites[1].y, xe : sites[1].x, ye : sites[1].y, width:line.width});
						new_lines.push({xs : sites[2].x, ys : sites[2].y, xe : sites[3].x, ye : sites[3].y, width:line.width});
					}
					else if (line.ys == line.ye) {
						var sites = [{x:line.xs,y:line.ys},{x:line.xe,y:line.ye},points[0],points[1]].sort(function(a,b){return a.x-b.x});
						new_lines.push({xs : sites[0].x, ys : sites[1].y, xe : sites[1].x, ye : sites[1].y, width:line.width});
						new_lines.push({xs : sites[2].x, ys : sites[2].y, xe : sites[3].x, ye : sites[3].y, width:line.width});
					}
				}
				lines.splice(i, 1);
				lines.push(new_lines)
			}
		})
	}
	// 将连续线连接
	var pair_array = [];
	for (var i = 0; i < lines.length; i++) {
		var line1 = lines[i];
		for (var j = i+1; j < lines.length; j++) {
			var line2 = lines[j];
			var is_continue = 0;
			if (line1.xs == line1.xe && line2.xs == line2.xe && line1.xs == line2.xs) {
				var y_sites =[line1.ys, line1.ye, line2.ys, line2.ye].sort(function(a,b){return a-b});
				if (y_sites[2] - y_sites[1] < 20) {
					var new_line = {xs:line1.xs, xe:line1.xe, ys:y_sites[0], ye:y_sites[3]};
					is_continue = 1;
				}
			}
			else if (line1.ys == line1.ye && line2.ys == line2.ye && line1.ys == line2.ys) {
				var x_sites = [line1.xs, line1.xe, line2.xs, line2.xe].sort(function(a,b){return a-b});
				if (x_sites[2] - x_sites[1] < 20) {
					var new_line = {xs:x_sites[0], xe:x_sites[3], ys:line1.ys, ye:line1.ye};
					is_continue = 1;
				}
			}
			if (is_continue) {
				var found = 0;
				pair_array.forEach(function(pair,index){
					if (pair.filter(function(v){return v==i||v==j}).length > 0) {  //? todo 
						var tmp = pair.concat([i, j]);
						// tmp 去重
						tmp = tmp.reduce(function(a,b){
							if(a.indexOf(b) < 0) {
								a.push(b)
							}
							return a
						}, [])
						pair_array[index] = tmp;
						found = 1;
					}
				})
				if (found == 0) {
					pair_array.push([i,j])
				}
			}
		}
		var found_in_pair = 0;
		pair_array.forEach(function(pair){
			if(pair.filter(function(v){return v==i}).length > 0){
				found_in_pair = 1;
			}
		})
		if (found_in_pair == 0) {
			pair_array.push([i])
		}
	}
	var new_lines = [];
	pair_array.forEach(function(pair){
		if (pair.length == 1) {
			new_lines.push(lines[pair[0]])
		} else {
			var x_sites = pair.reduce(function(a,b){a.push(lines[b].xs);a.push(lines[b].xe);return a},[]).sort();
			var y_sites = pair.reduce(function(a,b){a.push(lines[b].ys);a.push(lines[b].ye);return a},[]).sort();
			new_lines.push({xs:x_sites[0],ys:y_sites[0],xe:x_sites[x_sites.length-1],ye:y_sites[y_sites.length-1],width:lines[pair[0]].width})
		}
	})
	return new_lines;
}

function point2polygon_dist(point,polygon ) {
    var min_dist = 9999999;
    for (var i = 0; i < polygon.length - 1; i++) {
        var line = {xs:polygon[i].x, ys:polygon[i].y, xe:polygon[i+1].x, ye:polygon[i+1].y};
        var dist = genMath.point2LineDis(point,line);
        if(dist < min_dist){
            min_dist = dist
        }
    }
    return min_dist;
}

// 获取线跟矩形交点
function get_line_rect_intersect(line,rect) {
	var intersects = [];
	var edges = [
		{xs:rect.x1,ys:rect.y1,xe:rect.x1,ye:rect.y2},
		{xs:rect.x1,ys:rect.y2,xe:rect.x2,ye:rect.y2},
		{xs:rect.x2,ys:rect.y2,xe:rect.x2,ye:rect.y1},
		{xs:rect.x2,ys:rect.y1,xe:rect.x1,ye:rect.y1}
	];
	edges.forEach(function(edge){
		var point = genMath.getLineIntersect(line,edge,0);
		if(point){
			intersects.push(point)
		}
	})
	return intersects
}

function addApi(api){
	api.minInArray = function(){
		var arr = [];
		[].forEach.call(arguments,function(v){
			if(Array.isArray(v)){
				arr = arr.concat(v)
			} else {
				arr.push(v)
			}
		})
		return arr.reduce(function(a,b){return a<b? a:b})
	}
}

function NewGUI (gui) {
    this.msgBox = function(props){ // title  type  content  button
        props = props || {}
        return gui.msgBox(props.title || "title",props.type || "info",props.content || "content",props.button || ["ok", "cancel"]);
    }
    this.selectFromTreeview = gui.selectFromTreeview
    this.confirm = function(props) {
        props = props || {}
        return gui.confirm(props.content || "content",props.button || ["yes", "no"], props.type || "question");
    }
    this.selectFile = function(props){ // "choose something", "*", true, "file", "/home/abby/fast_io"
        props = props || {}
        return gui.selectFile(props.title || "choose something",props.filter || "*",props.multiple || false, "file","");
    }
    this.selectSingle = function(props) {
        props = props || {}
        return gui.selectSingle({
            "title": props.title || "select",
            "list": props.list || [],
            "default": props["default"] || "",
            "columns": props.columns || 2,
            "gen":props.gen || GEN
        });
    }
    this.selectMultiple = function(props) {
        props = props || {}
        return gui.selectMultiple({
            "title": props.title || "select",
            "list": props.list || [],
            "defaultvalue": props["defaultvalue"] || [""],
            "defaultsize": props["defaultsize"] || [200, 100],
            "columns": props.columns || 2,
            "gen":props.gen || GEN
        });
    }
    this.selectFromTable = gui.selectFromTable
    this.snapScreen = gui.snapScreen
    this.imageViewer = gui.imageViewer
    this.inputBox = gui.inputBox
    this.showForm = gui.showForm
    this.selectLayer = function(props){
        props = props || {}
        return gui.selectLayer({
            title: props.title || "请选择层",
            filter: props.filter || ".*", // regular expression
            selectmode: props.selectmode || "multiple", // single, multiple
            context: props.context || "all", // all, board, misc
            layertype: props.layertype || "signal", // default type of layertypelist
            defaultsize: props.defaultsize || [600, 400], // window size
            layermatrix: props.layermatrix || props.matrix,
            layertypelist: props.layertypelist || [
                {name: "all", display_name: "all", filter: function(x) { return x }},
                {name: "signal", display_name: "signal", filter: function(x) { return x["layer_type"] === "signal"; }},
                {name: "power_ground", display_name: "power_ground", filter: function(x) { return x["layer_type"] === "power_ground"; }},
                {name: "mixed", display_name: "mixed", filter: function(x) { return x["layer_type"] === "mixed"; }},
                {name: "solder_mask", display_name: "solder_mask", filter: function(x) { return x["layer_type"] === "solder_mask"; }},
                {name: "silk_screen", display_name: "silk_screen", filter: function(x) { return x["layer_type"] === "silk_screen"; }},
                {name: "solder_paste", display_name: "solder_paste", filter: function(x) { return x["layer_type"] === "solder_paste"; }},
                {name: "drill", display_name: "drill", filter: function(x) { return x["layer_type"] === "drill"; }},
                {name: "rout", display_name: "rout", filter: function(x) { return x["layer_type"] === "rout"; }},
                {name: "document", display_name: "document", filter: function(x) { return x["layer_type"] === "document"; }}
            ],
            gen: props.GEN || GEN
        })
    } 
    this.lockUnlockLayer = function(props){
        props = props || {}
        var tmp = {}
        if (props.matrix) {
            for (var key in props.matrix) {
                var item = props.matrix[key]
                tmp[key] = {
                    row : item.row,
                    name : item.name
                }
            }
        }
        return gui.lockUnlockLayer({
            title: props.title || "lockUnlockLayer",
            layermatrix:props.layermatrix || tmp 
        })
    } 
    this.passwordBox = function(props){
        props = props || {}
        return gui.passwordBox({title:props.title || "password", password:props.password || "1212"})
    } 
    this.selectJobLayer = function (props) {
        props = props || {}
        return gui.selectJobLayer({
            layertypelist: props.layertypelist || [
                {name: "all", display_name: "all", filter: function(x) { return x }},
                {name: "signal", display_name: "signal", filter: function(x) { return x["layer_type"] === "signal"; }},
                {name: "power_ground", display_name: "power_ground", filter: function(x) { return x["layer_type"] === "power_ground"; }},
                {name: "mixed", display_name: "mixed", filter: function(x) { return x["layer_type"] === "mixed"; }},
                {name: "solder_mask", display_name: "solder_mask", filter: function(x) { return x["layer_type"] === "solder_mask"; }},
                {name: "silk_screen", display_name: "silk_screen", filter: function(x) { return x["layer_type"] === "silk_screen"; }},
                {name: "solder_paste", display_name: "solder_paste", filter: function(x) { return x["layer_type"] === "solder_paste"; }},
                {name: "drill", display_name: "drill", filter: function(x) { return x["layer_type"] === "drill"; }},
                {name: "rout", display_name: "rout", filter: function(x) { return x["layer_type"] === "rout"; }},
                {name: "document", display_name: "document", filter: function(x) { return x["layer_type"] === "document"; }}
            ],
            //defaultlayertype: "ha",
            joblist: props.joblist || [],
            defaultJob: props.defaultJob || [], // select by default
            steplist: props.steplist || [],
            // defaultstep: "step3",
            showstep: props.showstep || true,
            selectmode: props.selectmode || "multiple", // single/multiple
            layermatrix: props.layermatrix || {  },
            defaultlayer: props.defaultlayer || []
        })
    }
}


function resizeBox(box){
    box.xmin = Number(box.xmin);
    box.xmax = Number(box.xmax);
    box.ymin = Number(box.ymin);
    box.ymax = Number(box.ymax);
    box.xsize = Math.abs(box.xmin-box.xmax)
    box.ysize = Math.abs(box.ymin-box.ymax)
    box.ld = { "x" : box.xmin, "y" : box.ymin }
    box.lu = { "x" : box.xmin, "y" : box.ymax }
    box.rd = { "x" : box.xmax, "y" : box.ymin }
    box.ru = { "x" : box.xmax, "y" : box.ymax }
}

function profile2Polygon(feat) {
	var info = [];
	feat.forEach(function(v){
		if (/^#O. ([\S]+) ([\S]+)/.test(v)) {
			info.push({
				x: RegExp.$1 - 0,
				y: RegExp.$2 - 0
			})
		}
	})
	return info
}