
=head1 NAME

 ImpCouponData - 阻抗数据模块

=head1 DESCRIPTION

 阻抗数据模块

=head1 VERSION HISTORY

=head2 V1.00 2011-12-03 Tony Guo

 1.新版本;

=head2 V1.01 2013-04-15 Tony Guo

 1.Header text文字函数增加side参数;
 
=head2 V1.02 2017-10-31 Cody Yu

 1.round_number函数从IKM中改到PubFunction模块;

=head2 V1.02 2018-06-12 Tony Guo

 1.修正因imp->{id}在IKM中为imp->{uid}的Bug;

=head2 V1.03 2018-06-28 Tony Guo

 1.修正只有一组粗线时,线会碰到GND Pad的Bug;

=cut

package Top::ImpCouponData;
use strict;
use Gtk2;
use Top ':subs';
use utf8;
require Exporter;
our @ISA = qw/Exporter/;
use Data::Dump 'dump';
use_module('TL_GenMath');
use_module('TL_Data_Inch');
use_module('PubFunction');
my $Func = PubFunction->new();
use JSON;
my $json = new JSON;
#use Number::Format 'round';


sub new {
    my $class = shift;
	my %par = @_;
    $class = ref($class) || $class;
    my $self = {};
    bless $self,$class;
	$self->{IKM} = $par{-IKM};
    return $self;
}

sub set_coupon_data{
	my $self = shift;
	$self->{COUPON_DATA} = shift;
}

sub get_coupon_data{
	my $self = shift;
	return $self->{COUPON_DATA};
}

sub set_layer_count{
	my $self = shift;
	$self->{COUPON_DATA}{layer_count} = shift;
	unless ($self->{COUPON_DATA}{drills}){
		$self->{COUPON_DATA}{drills} = {
			drill => {
				drl_start_num => 1,
				drl_end_num => $self->{COUPON_DATA}{layer_count},
			},
		};
	}
}
sub set_matrix{
	my $self = shift;
	$self->{COUPON_DATA}{matrix} = shift;
}

sub coplanar_hole_position{
	my $self = shift;
	my %par = @_;
	$par{coupon_data} = $self->{COUPON_DATA} unless defined $par{coupon_data};
	my $COUPON_DATA = $par{coupon_data} ;
	return unless $COUPON_DATA->{is_coplanar};
	return unless $COUPON_DATA->{coplanar_hole_style} eq 'no';
	my $path_n = 0;
	my $coplanar_hole_path;
	foreach my $path (@{$COUPON_DATA->{hori_line_path}}){
		$path_n++;
		my $xmin = ($path->{xs} < $path->{xe})?$path->{xs} + $COUPON_DATA->{coplanar_hole_margin_x}:$path->{xs} - $COUPON_DATA->{coplanar_hole_margin_x} ;
		my $xmax = ($path->{xs} < $path->{xe})?$path->{xe} - $COUPON_DATA->{coplanar_hole_margin_x}:$path->{xe} + $COUPON_DATA->{coplanar_hole_margin_x} ;
		($xmin,$xmax) = ($xmax,$xmin) if $xmin > $xmax;
		my ($ymin,$ymax) = (99999,-99999);
		foreach my $layer (values %{$COUPON_DATA->{layers}}){
			next unless $layer->{layer_type} eq 'S';
			my $tymax = $path->{ys} + $layer->{line_y} + $layer->{cam_total_width}/2000 + $layer->{imp_line2copper}/1000;
			$ymax = $tymax if $tymax > $ymax;
			my $tymin = $path->{ys} + $layer->{line_y} - $layer->{cam_total_width}/2000 - $layer->{imp_line2copper}/1000;
			$ymin = $tymin if $tymin < $ymin;
		}
		my $eymax = $path->{ys} + $COUPON_DATA->{body_area_rect}{ymax} - $COUPON_DATA->{rout2copper_spacing}/1000 - $COUPON_DATA->{width_center_shift};
		my $eymin = $path->{ys} + $COUPON_DATA->{body_area_rect}{ymin} + $COUPON_DATA->{rout2copper_spacing}/1000 - $COUPON_DATA->{width_center_shift};
		if ($xmin < $xmax){
			if ($ymax < $eymax){
				my $ps = TL::GenMath->rotate_point({x=>$path->{xs},y=>$path->{ys}},{x=>$xmin,y=>($ymax+$eymax)/2},$path->{angle});
				my $pe = TL::GenMath->rotate_point({x=>$path->{xs},y=>$path->{ys}},{x=>$xmax,y=>($ymax+$eymax)/2},$path->{angle});
				push @{$coplanar_hole_path->{up}},{
					xs => $ps->{x}, xe => $pe->{x},
					ys => $ps->{y}, ye => $pe->{y},
				};
			}
			if ($ymin > $eymin){
				my $ps = TL::GenMath->rotate_point({x=>$path->{xs},y=>$path->{ys}},{x=>$xmin,y=>($ymin+$eymin)/2},$path->{angle});
				my $pe = TL::GenMath->rotate_point({x=>$path->{xs},y=>$path->{ys}},{x=>$xmax,y=>($ymin+$eymin)/2},$path->{angle});
				push @{$coplanar_hole_path->{down}},{
					xs => $ps->{x}, xe => $pe->{x},
					ys => $ps->{y}, ye => $pe->{y},
				};
			}
		}
	}
	$COUPON_DATA->{coplanar_hole_path} = $coplanar_hole_path;
	foreach my $ud ('up','down'){
		next unless $coplanar_hole_path->{$ud};
		my $path_total_length = 0;
		my $tmp_dist = 0;
		foreach my $it (@{$coplanar_hole_path->{$ud}}){
			$path_total_length += TL::GenMath->line_length($it);
		}
		if ($COUPON_DATA->{coplanar_hole_style} eq 'fix_hole_count'){
			$COUPON_DATA->{coplanar_hole_count} = 1 if $COUPON_DATA->{coplanar_hole_count} < 1;
			if ($COUPON_DATA->{coplanar_hole_count} > 1){
				$COUPON_DATA->{coplanar_hole_dist} = $path_total_length/($COUPON_DATA->{coplanar_hole_count} - 1);
			}
			else{
				$COUPON_DATA->{coplanar_hole_dist} = $path_total_length + 1;
			}
		}
		elsif($COUPON_DATA->{coplanar_hole_style} eq 'fix_hole_dist'){
			my $n = int($path_total_length/$COUPON_DATA->{coplanar_hole_dist});
			$tmp_dist = ($path_total_length - $COUPON_DATA->{coplanar_hole_dist}*$n)/2;
		}
		return unless $COUPON_DATA->{coplanar_hole_dist};
		my $pn = 0;
		my $path = $coplanar_hole_path->{$ud}[0];
		my $path_length = TL::GenMath->line_length($path);
		push @{$COUPON_DATA->{$ud.'_coplanar_holes'}},TL::GenMath->calc_point_on_line($path,$tmp_dist);
		while(1){
			if ($tmp_dist + $COUPON_DATA->{coplanar_hole_dist} > $path_length+0.001){
				$pn++;
				last if ($pn > scalar(@{$coplanar_hole_path->{$ud}})-1);
				$tmp_dist = $COUPON_DATA->{coplanar_hole_dist} - ($path_length - $tmp_dist);
				$path = $coplanar_hole_path->{$ud}[$pn];
				$path_length = TL::GenMath->line_length($path);
				my $p = TL::GenMath->calc_point_on_line($path,$tmp_dist);
				push @{$COUPON_DATA->{$ud.'_coplanar_holes'}},$p;
			}
			else{
				$tmp_dist += $COUPON_DATA->{coplanar_hole_dist} ;
				my $p = TL::GenMath->calc_point_on_line($path,$tmp_dist);
				push @{$COUPON_DATA->{$ud.'_coplanar_holes'}},$p;
			}
		}
	}
}


sub body_text_position{
	my $self = shift;
	my %par = @_;
	$par{coupon_data} = $self->{COUPON_DATA} unless defined $par{coupon_data};
	my $COUPON_DATA = $par{coupon_data} ;
	my $copy_line_path = eval(dump($COUPON_DATA->{line_path}));
	my ($xs,$ys) = ($copy_line_path->[0]{x},$copy_line_path->[0]{y});
	my @hori_line_path;
	foreach my $n (1..scalar(@$copy_line_path)-1){
		my $it = $COUPON_DATA->{line_path}[$n];
		unless ($it->{direction}){
			my $angle = TL::GenMath->line_angle({xs=>$xs,xe=>$it->{x},ys=>$ys,ye=>$it->{y}});
			my $pe = TL::GenMath->rotate_point({x=>$xs,y=>$ys},$it,-$angle);
			push @hori_line_path,{xs=>$xs,xe=>$pe->{x},ys=>$ys,ye=>$pe->{y},angle=>$angle}
		}
		($xs,$ys) = ($it->{x},$it->{y});
	}
	$COUPON_DATA->{hori_line_path} = \@hori_line_path;
	
	foreach my $drill (values %{$COUPON_DATA->{drills}}){
		next unless $drill->{include_coupon_groups};
		my @imp_layers;
		foreach my $grp (@{$drill->{include_coupon_groups}}){
			foreach my $layer (@{$COUPON_DATA->{group}{$grp}{signal_layer}}){
				push @imp_layers,$COUPON_DATA->{layers}{$layer};
			}
		}
		foreach my $side ('top','bot'){
			#分别计算每个钻孔正反面的文字层
			my ($txt_layer,$txt_polarity,$txt2line);
			$txt_layer = $COUPON_DATA->{layers}{$drill->{$side.'_text_layer'}};
			my $side_grp = $drill->{$side.'_coupon_groups'};
			$txt_polarity = $COUPON_DATA->{body_text_polarity};
			$txt2line = $COUPON_DATA->{body_text2line};
			my $body_text_content;
			if ($COUPON_DATA->{body_text_content} and ref($COUPON_DATA->{body_text_content}) eq 'CODE'){
				$body_text_content = $COUPON_DATA->{body_text_content}->(
					coupon_data=>$COUPON_DATA,
					job_id => $COUPON_DATA->{job_id},
					job_name => $COUPON_DATA->{job_name},
					imp_layers => \@imp_layers,
					drill=>$drill->{tl_name},
					side=>$side,
					text_polarity=>$txt_polarity,
					layer_count => $COUPON_DATA->{layer_count},
				);
				if ($body_text_content){
					$body_text_content = [$body_text_content] unless (ref($body_text_content) eq 'ARRAY');
				}
			}
			next unless ($body_text_content and @$body_text_content);
			#计算文字位置
			my @body_text_area;
			my $path_n = 0;
			foreach my $path (@hori_line_path){
				my ($xmin,$xmax,$ymin,$ymax);
				$path_n++;
				$xmin = ($path->{xs} < $path->{xe})?$path->{xs} + $COUPON_DATA->{body_text_margin_x}:$path->{xs} - $COUPON_DATA->{body_text_margin_x} ;
				$xmax = ($path->{xs} < $path->{xe})?$path->{xe} - $COUPON_DATA->{body_text_margin_x}:$path->{xe} + $COUPON_DATA->{body_text_margin_x} ;
				#($xmin,$xmax) = ($xmax,$xmin) if $xmin > $xmax;
				$ymax = $path->{ys} + $COUPON_DATA->{body_area_rect}{ymax} - $COUPON_DATA->{body_text2rout}/1000 - $COUPON_DATA->{width_center_shift};
				if ($side_grp){
					my @tmp_line_y;
					foreach my $grp (@$side_grp){
						foreach my $layer (sort {$COUPON_DATA->{layers}{$b}{line_y} <=> $COUPON_DATA->{layers}{$a}{line_y}} @{$COUPON_DATA->{group}{$grp}{signal_layer}}){
							my $layer_info = $COUPON_DATA->{layers}{$layer};
							push @tmp_line_y,$layer_info;
						}
					}
					foreach my $layer_info (sort{$b->{line_y}+$b->{cam_total_width}/2000 <=> $a->{line_y}+$a->{cam_total_width}/2000} @tmp_line_y){
						$ymin = $path->{ys} + $layer_info->{line_y} + $layer_info->{cam_total_width}/2000 + $txt2line/1000;
						if ($ymin < $ymax){
							push @body_text_area,{xmin=>$xmin,xmax=>$xmax,ymin=>$ymin,ymax=>$ymax,angle=>$path->{angle},bx=>$path->{xs},by=>$path->{ys}};
						}
						$ymax = $path->{ys} + $layer_info->{line_y} - $layer_info->{cam_total_width}/2000 - $txt2line/1000;
					}
				}
				$ymin = $path->{ys} + $COUPON_DATA->{body_area_rect}{ymin} + $COUPON_DATA->{body_text2rout}/1000 - $COUPON_DATA->{width_center_shift};
				if ($ymin < $ymax and $xmin < $xmax){
					push @body_text_area,{xmin=>$xmin,xmax=>$xmax,ymin=>$ymin,ymax=>$ymax,angle=>$path->{angle},bx=>$path->{xs},by=>$path->{ys},path_n=>$path_n};
				}
			}
			$COUPON_DATA->{body_text_area}{$txt_layer->{tl_name}} = \@body_text_area;
			my (@body_text,$not_enought_space);
			foreach my $txt_size (@{$COUPON_DATA->{body_text_size}}){
				my %tmp_txt_size = (x_size=>$txt_size->{w}/1000,y_size=>$txt_size->{h}/1000,line_width=>$txt_size->{l});
				my @tmp_text = eval(dump(@$body_text_content));
				foreach my $area (sort{$Func->round_number($b->{ymax}-$b->{ymin},3) <=> $Func->round_number($a->{ymax}-$a->{ymin},3) || $a->{path_n} <=> $b->{path_n}} @body_text_area){
					my $x = $area->{xmin};my $y = $area->{ymax};
					my @area_text;
					while(@tmp_text){
						my $txt = $tmp_text[0];
						$txt = {text=>$txt} unless ref($txt) eq 'HASH';
						my $rect = $self->_get_text_rect(%tmp_txt_size,%$txt,anchor=>'nw');
						if ($y - $rect->{ysize} >= $area->{ymin}){
							if ($x + $rect->{xsize} <= $area->{xmax}){
								push @area_text,{
									%tmp_txt_size,
									%$txt,
									x => $x + $rect->{xsize}/2, y => $y-$rect->{ysize}/2,
									bx => $area->{bx},by => $area->{by},
									angle => $area->{angle},
									anchor => 'center',
									xmin => $x,
									xmax => $x + $rect->{xsize},
									ymin => $y - $rect->{ysize},
									ymax => $y,
								};
								$x += $rect->{xsize} + $COUPON_DATA->{body_text2text_x}/1000;
								shift @tmp_text;
							}
							else{
								$y -= ($rect->{ysize} + $COUPON_DATA->{body_text2text_y}/1000);
								$x = $area->{xmin};
								next;
							}
						}
						else{
							last;
						}
					}
					my ($tmp_min_y,$tmp_max_y);
					foreach my $text (@area_text){
						$tmp_max_y = $text->{y} if (!defined $tmp_max_y or (defined $tmp_max_y and $tmp_max_y < $text->{y}));
						$tmp_min_y = $text->{y} - $text->{y_size} if (!defined $tmp_min_y or (defined $tmp_min_y and $tmp_min_y > $text->{y} - $text->{y_size}));
					}
					my $shifty = (abs($tmp_max_y - $tmp_min_y) - abs($area->{ymax}-$area->{ymin}))/2;
					foreach my $text (@area_text){
						$text->{y} += $shifty;
						$text->{ymin} += $shifty;
						$text->{ymax} += $shifty;
					}
					push @body_text,@area_text;
				}
				if (@tmp_text){
					@body_text = ();
					$not_enought_space = 1;
				}
				else{
					$not_enought_space = 0;
					last;
				}
			}
			if ($not_enought_space){
				@body_text = ();
				my $txt_size  = $COUPON_DATA->{body_text_size}[-1];
				my %tmp_txt_size = (x_size=>$txt_size->{w}/1000,y_size=>$txt_size->{h}/1000,line_width=>$txt_size->{l});
				my @tmp_text = eval(dump(@$body_text_content));
				my ($x,$y) = (0,0);
				foreach my $txt (@tmp_text){
					$txt = {text=>$txt} unless ref($txt) eq 'HASH';
					push @body_text,{
									%tmp_txt_size,%$txt,
									x => $x, y => $y,
									angle => 0,
									anchor => 'nw',
								};
					$y -= ($tmp_txt_size{y_size} + $COUPON_DATA->{body_text2text_y}/1000);
				}
			}
			$COUPON_DATA->{'body_text_pos'}{$txt_layer->{tl_name}} = \@body_text;
			$COUPON_DATA->{'body_text_not_enought_space'}{$txt_layer->{tl_name}} = $not_enought_space;
		}
	}
}

#根据设定的path_line进行布线
sub layout_path_lines{
	my $self = shift;
	my %par = @_;
	$par{coupon_data} = $self->{COUPON_DATA} unless defined $par{coupon_data};
	my $COUPON_DATA = $par{coupon_data} ;
	my $path_shift = ($COUPON_DATA->{'body_area_rect'}{ymax} + $COUPON_DATA->{'body_area_rect'}{ymin})/2 - $COUPON_DATA->{width_center_shift};
	my $copy_path_lines = eval(dump($COUPON_DATA->{line_path}));
	my $path_angle = TL::GenMath->line_angle({xs=>$copy_path_lines->[0]{x},ys=>$copy_path_lines->[0]{y},xe=>$copy_path_lines->[1]{x},ye=>$copy_path_lines->[1]{y}});
	foreach my $shift ($path_shift,-$path_shift){
		my $center_path_lines = TL::GenMath->polyline_offset($copy_path_lines,$shift);
		my $p = TL::GenMath->rotate_point($copy_path_lines->[0],$center_path_lines->[0],-$path_angle);
		if (abs(($p->{y} - $copy_path_lines->[0]{y}) - $path_shift) < 0.00001){
			$COUPON_DATA->{center_path_lines} = $center_path_lines;
			last;
		}
	}
	$COUPON_DATA->{center_path_lines}[0] = TL::GenMath->calc_point_on_line({xs=>$COUPON_DATA->{center_path_lines}[0]{x},ys=>$COUPON_DATA->{center_path_lines}[0]{y},
											 xe=>$COUPON_DATA->{center_path_lines}[1]{x},ye=>$COUPON_DATA->{center_path_lines}[1]{y}},
											,-$COUPON_DATA->{line_start_x});
	
	$COUPON_DATA->{center_path_lines}[-1] = TL::GenMath->calc_point_on_line({xs=>$COUPON_DATA->{center_path_lines}[-1]{x},ys=>$COUPON_DATA->{center_path_lines}[-1]{y},
											 xe=>$COUPON_DATA->{center_path_lines}[-2]{x},ye=>$COUPON_DATA->{center_path_lines}[-2]{y}},
											,-($COUPON_DATA->{coupon_length} - $COUPON_DATA->{line_end_x}));
	
	#从左向右路径
	my $copy_line_path = eval(dump($COUPON_DATA->{line_path}));
	
	#从右向左路径,圆弧方向要反向
	my $reverse_copy_line_path = eval(dump($COUPON_DATA->{line_path})); 
	foreach my $n (0..scalar(@$reverse_copy_line_path)-1){
		my $it = $reverse_copy_line_path->[$n];
		my $dir = delete $it->{direction};
		if ($dir){
			$reverse_copy_line_path->[$n-1]{xc} = delete $it->{xc};
			$reverse_copy_line_path->[$n-1]{yc} = delete $it->{yc};
			$reverse_copy_line_path->[$n-1]{direction} = ($dir eq 'cw')?'ccw':'cw';
		}
	}
	@$reverse_copy_line_path = reverse @$reverse_copy_line_path;
	
	#计算左边头部需旋转的角度和平移距离
	my ($left_header_transform,$right_header_transform);
	my $path0 = $COUPON_DATA->{line_path}[0];
	my $path1 = $COUPON_DATA->{line_path}[1];
	$left_header_transform->{angle} = TL::GenMath->line_angle({xs=>$path0->{x},ys=>$path0->{y},xe=>$path1->{x},ye=>$path1->{y}});
	$left_header_transform->{x} = $path0->{x};
	$left_header_transform->{y} = $path0->{y};
	$left_header_transform->{shift_x} = $left_header_transform->{x} - $COUPON_DATA->{line_start_x} ;
	$left_header_transform->{shift_y} = $left_header_transform->{y} - $COUPON_DATA->{width_center_shift} ;
	$COUPON_DATA->{left_header_transform} = $left_header_transform;

	#计算右边头部需旋转的角度和平移距离
	my $path_1 = $COUPON_DATA->{line_path}[-1];
	my $path_2 = $COUPON_DATA->{line_path}[-2];
	$right_header_transform->{angle} = TL::GenMath->line_angle({xs=>$path_2->{x},ys=>$path_2->{y},xe=>$path_1->{x},ye=>$path_1->{y}});
	$right_header_transform->{x} = $path_1->{x};
	$right_header_transform->{y} = $path_1->{y};
	$right_header_transform->{shift_x} =  $right_header_transform->{x} - $COUPON_DATA->{line_end_x} ;
	$right_header_transform->{shift_y} = $right_header_transform->{y} - $COUPON_DATA->{width_center_shift} ;
	$COUPON_DATA->{right_header_transform} = $right_header_transform;
	
	#平移左右头部区域
	foreach my $lr ('left','right'){
		next unless $COUPON_DATA->{$lr.'_header_area_rect'};
		$COUPON_DATA->{$lr.'_header_area_rect'}{xmin} += $COUPON_DATA->{$lr.'_header_transform'}{shift_x};
		$COUPON_DATA->{$lr.'_header_area_rect'}{xmax} += $COUPON_DATA->{$lr.'_header_transform'}{shift_x};
		$COUPON_DATA->{$lr.'_header_area_rect'}{ymin} += $COUPON_DATA->{$lr.'_header_transform'}{shift_y};
		$COUPON_DATA->{$lr.'_header_area_rect'}{ymax} += $COUPON_DATA->{$lr.'_header_transform'}{shift_y};
	}
	
	#左边中心原零点
	$COUPON_DATA->{left_zero_pad} = {
		#x =>  $COUPON_DATA->{'left_header_transform'}{shift_x} ,
		x => $COUPON_DATA->{'left_header_area_rect'}{xmin} ,
		y => ($COUPON_DATA->{'left_header_area_rect'}{ymin} + $COUPON_DATA->{'left_header_area_rect'}{ymax})/2
		#y =>  $COUPON_DATA->{'center_path_lines'}[0]{y},
	};
	$COUPON_DATA->{right_zero_pad} = {
		#x =>  $COUPON_DATA->{coupon_length} + $COUPON_DATA->{'right_header_transform'}{shift_x},
		x => $COUPON_DATA->{'right_header_area_rect'}{xmax} ,
		y => ($COUPON_DATA->{'right_header_area_rect'}{ymin} + $COUPON_DATA->{'right_header_area_rect'}{ymax})/2
		#y =>  $COUPON_DATA->{'center_path_lines'}[-1]{y},
	};
	
	#左下角零点
	$COUPON_DATA->{left_down_zero_pad} = TL::GenMath->rotate_point(
		$left_header_transform,
		{x => $COUPON_DATA->{'left_header_area_rect'}{xmin} ,
		 y => $COUPON_DATA->{'left_header_area_rect'}{ymin}} ,
		$left_header_transform->{angle},
	);
	
	if ($COUPON_DATA->{add_pin_hole} and $COUPON_DATA->{add_pin_hole} eq 'yes'){
		$COUPON_DATA->{left_pin_hole} = TL::GenMath->rotate_point(
			$left_header_transform,
			{x => $COUPON_DATA->{'left_header_area_rect'}{xmin} + $COUPON_DATA->{pin_hole_edge_dist_x},
			 y => ($COUPON_DATA->{'left_header_area_rect'}{ymin} + $COUPON_DATA->{'left_header_area_rect'}{ymax})/2},
			$left_header_transform->{angle},
		);
		$COUPON_DATA->{right_pin_hole} = TL::GenMath->rotate_point(
			$right_header_transform,
			{x => $COUPON_DATA->{'right_header_area_rect'}{xmax} - $COUPON_DATA->{pin_hole_edge_dist_x},
			y => ($COUPON_DATA->{'right_header_area_rect'}{ymin} + $COUPON_DATA->{'right_header_area_rect'}{ymax})/2},
			$right_header_transform->{angle},
		);
	}
	
	#平移左右头部文字
	foreach my $vname (values %{$COUPON_DATA->{left_header_text_pos}}){
		foreach my $vsig (values %$vname){
			foreach my $vpos (values %$vsig){
				$vpos->{xmin} += $left_header_transform->{shift_x} ;$vpos->{ymin} += $left_header_transform->{shift_y} ;
				$vpos->{xmax} += $left_header_transform->{shift_x} ;$vpos->{ymax} += $left_header_transform->{shift_y} ;
				$vpos->{x} += $left_header_transform->{shift_x} ;$vpos->{y} += $left_header_transform->{shift_y} ;
			}
		}
	}
	foreach my $vname (values %{$COUPON_DATA->{right_header_text_pos}}){
		foreach my $vsig (values %$vname){
			foreach my $vpos (values %$vsig){
				$vpos->{xmin} += $right_header_transform->{shift_x} ;$vpos->{ymin} += $right_header_transform->{shift_y} ;
				$vpos->{xmax} += $right_header_transform->{shift_x} ;$vpos->{ymax} += $right_header_transform->{shift_y} ;
				$vpos->{x} += $right_header_transform->{shift_x} ;$vpos->{y} += $right_header_transform->{shift_y} ;
			}
		}
	}
	
	
	my (@left_pads,@right_pads);
	foreach my $layer (values %{$COUPON_DATA->{layers}}){
		next unless ($layer->{layer_type} eq 'S');
		my $line_path = eval(dump($copy_line_path));
		my $reverse_line_path = eval(dump($reverse_copy_line_path));
		#为保证线终点平齐,因此路径终点应缩小半个线宽
		$line_path->[-1] =  TL::GenMath->calc_point_on_line({xs=>$line_path->[-1]{x},ys=>$line_path->[-1]{y},xe=>$line_path->[-2]{x},ye=>$line_path->[-2]{y}},$layer->{cam_line_width}/2000);
		$reverse_line_path->[-1] =  TL::GenMath->calc_point_on_line({xs=>$reverse_line_path->[-1]{x},ys=>$reverse_line_path->[-1]{y},xe=>$reverse_line_path->[-2]{x},ye=>$reverse_line_path->[-2]{y}},$layer->{cam_line_width}/2000);
		
		if ($layer->{left_tpads} and @{$layer->{left_tpads}}){
			push @left_pads,@{$layer->{left_tpads}},@{$layer->{left_gpads}};
			
			#平移左边tpad
			foreach my $it (@{$layer->{left_tpads}},@{$layer->{left_gpads}}){
				$it->{x} += $left_header_transform->{shift_x} ;$it->{y} += $left_header_transform->{shift_y} ;
				#my $tp = TL::GenMath->rotate_point($left_header_transform,$it,$left_header_transform->{angle});
				#$it->{x} = $tp->{x}; $it->{y} = $tp->{y};
			}
			#旋转左边头部走线
			foreach my $it (@{$layer->{left_header_lines1}},@{$layer->{left_header_lines2}}){
				$it->{x} += $left_header_transform->{shift_x} ;$it->{y} += $left_header_transform->{shift_y} ;
				my $tp = TL::GenMath->rotate_point($left_header_transform,{x=>$it->{x},y=>$it->{y}},$left_header_transform->{angle});
				$it->{x} = $tp->{x}; $it->{y} = $tp->{y};
			}
			
			$layer->{lines1} = $layer->{left_header_lines1};
			if ($layer->{impedance_type} =~ /difference/){
				#左边头部双线与body走线连接
				my $dist = ($layer->{cam_line_width}+$layer->{cam_line_spacing})/2000;
				#此处的layer->{line_y}还是依0点为中心的值,因此尝试4种shift值,看哪个的起点与header线的终点重合
				foreach my $d ($layer->{line_y}+$dist,$layer->{line_y}-$dist,-$layer->{line_y}+$dist,-$layer->{line_y}-$dist){
					my $tmp_lines = TL::GenMath->polyline_offset($line_path,$d);
					my $tp = TL::GenMath->get_line_intersect(
						{xs=>$tmp_lines->[0]{x},ys=>$tmp_lines->[0]{y},xe=>$tmp_lines->[1]{x},ye=>$tmp_lines->[1]{y}},
						{xs=>$layer->{lines1}[-1]{x},ys=>$layer->{lines1}[-1]{y},xe=>$layer->{lines1}[-2]{x},ye=>$layer->{lines1}[-2]{y}},1);
					if (abs($tp->{x} - $layer->{lines1}[-1]{x}) < 0.0001 and abs($tp->{y} - $layer->{lines1}[-1]{y}) < 0.0001){
						shift @$tmp_lines;
						push @{$layer->{lines1}},@$tmp_lines;
						last;
					}
				}
				$layer->{lines2} = [@{$layer->{left_header_lines2}}];
				foreach my $d ($layer->{line_y}+$dist,$layer->{line_y}-$dist,-$layer->{line_y}+$dist,-$layer->{line_y}-$dist){
					my $tmp_lines = TL::GenMath->polyline_offset($line_path,$d);
					my $tp = TL::GenMath->get_line_intersect(
						{xs=>$tmp_lines->[0]{x},ys=>$tmp_lines->[0]{y},xe=>$tmp_lines->[1]{x},ye=>$tmp_lines->[1]{y}},
						{xs=>$layer->{lines2}[-1]{x},ys=>$layer->{lines2}[-1]{y},xe=>$layer->{lines2}[-2]{x},ye=>$layer->{lines2}[-2]{y}},1);
					if (abs($tp->{x} - $layer->{lines2}[-1]{x}) < 0.0001 and abs($tp->{y} - $layer->{lines2}[-1]{y}) < 0.0001){
						shift @$tmp_lines;
						push @{$layer->{lines2}},@$tmp_lines;
						last;
					}
				}
			}
			else{
				#左边头部单线与body走线连接
				foreach my $d ($layer->{line_y},-$layer->{line_y}){
					my $tmp_lines = TL::GenMath->polyline_offset($line_path,$d);
					my $tp = TL::GenMath->get_line_intersect(
						{xs=>$tmp_lines->[0]{x},ys=>$tmp_lines->[0]{y},xe=>$tmp_lines->[1]{x},ye=>$tmp_lines->[1]{y}},
						{xs=>$layer->{lines1}[-1]{x},ys=>$layer->{lines1}[-1]{y},xe=>$layer->{lines1}[-2]{x},ye=>$layer->{lines1}[-2]{y}},1);
					if (abs($tp->{x} - $layer->{lines1}[-1]{x}) < 0.0001 and abs($tp->{y} - $layer->{lines1}[-1]{y}) < 0.0001){
						shift @$tmp_lines;
						push @{$layer->{lines1}},@$tmp_lines;
						last;
					}
				}
			}
		}
		
		if ($layer->{right_tpads} and @{$layer->{right_tpads}}){
			push @right_pads,@{$layer->{right_tpads}},@{$layer->{right_gpads}};
			#平移右边tpad
			foreach my $it (@{$layer->{right_tpads}},@{$layer->{right_gpads}}){
				$it->{x} += $right_header_transform->{shift_x} ;
				$it->{y} += $right_header_transform->{shift_y} ;
			}
			#旋转右边头部走线
			foreach my $it (@{$layer->{right_header_lines1}},@{$layer->{right_header_lines2}}){
				$it->{x} += $right_header_transform->{shift_x} ;$it->{y} += $right_header_transform->{shift_y} ;
				my $tp = TL::GenMath->rotate_point($right_header_transform,{x=>$it->{x},y=>$it->{y}},$right_header_transform->{angle});
				$it->{x} = $tp->{x}; $it->{y} = $tp->{y};
			}
			if ($COUPON_DATA->{center_tag} eq '--'){
				#如果左右是一同一根线,则直接将右边header走线与左边走线连接
				if ($layer->{impedance_type} =~ /difference/){
					my @tmp1 = reverse @{$layer->{right_header_lines1}};
					pop @{$layer->{lines1}};
					push @{$layer->{lines1}},@tmp1;
					my @tmp2 = reverse @{$layer->{right_header_lines2}};
					pop @{$layer->{lines2}};
					push @{$layer->{lines2}},@tmp2;
				}
				else{
					my @tmp1 = reverse @{$layer->{right_header_lines1}};
					pop @{$layer->{lines1}};
					push @{$layer->{lines1}},@tmp1;
				}
			}
			else{
				#右边走线连接
				$layer->{lines1} = $layer->{right_header_lines1};
				if ($layer->{impedance_type} =~ /difference/){
					my $dist = ($layer->{cam_line_width}+$layer->{cam_line_spacing})/2000;
					foreach my $d ($layer->{line_y}+$dist,$layer->{line_y}-$dist,-$layer->{line_y}+$dist,-$layer->{line_y}-$dist){
						my $tmp_lines = TL::GenMath->polyline_offset($reverse_line_path,$d);
						my $tp = TL::GenMath->get_line_intersect(
							{xs=>$tmp_lines->[0]{x},ys=>$tmp_lines->[0]{y},xe=>$tmp_lines->[1]{x},ye=>$tmp_lines->[1]{y}},
							{xs=>$layer->{lines1}[-1]{x},ys=>$layer->{lines1}[-1]{y},xe=>$layer->{lines1}[-2]{x},ye=>$layer->{lines1}[-2]{y}},1);
						if (abs($tp->{x} - $layer->{lines1}[-1]{x}) < 0.0001 and abs($tp->{y} - $layer->{lines1}[-1]{y}) < 0.0001){
							shift @$tmp_lines;
							push @{$layer->{lines1}},@$tmp_lines;
							last;
						}
					}
					$layer->{lines2} = [@{$layer->{right_header_lines2}}];
					foreach my $d ($layer->{line_y}+$dist,$layer->{line_y}-$dist,-$layer->{line_y}+$dist,-$layer->{line_y}-$dist){
						my $tmp_lines = TL::GenMath->polyline_offset($reverse_line_path,$d);
						my $tp = TL::GenMath->get_line_intersect(
							{xs=>$tmp_lines->[0]{x},ys=>$tmp_lines->[0]{y},xe=>$tmp_lines->[1]{x},ye=>$tmp_lines->[1]{y}},
							{xs=>$layer->{lines2}[-1]{x},ys=>$layer->{lines2}[-1]{y},xe=>$layer->{lines2}[-2]{x},ye=>$layer->{lines2}[-2]{y}},1);
						if (abs($tp->{x} - $layer->{lines2}[-1]{x}) < 0.0001 and abs($tp->{y} - $layer->{lines2}[-1]{y}) < 0.0001){
							shift @$tmp_lines;
							push @{$layer->{lines2}},@$tmp_lines;
							last;
						}
					}
				}
				else{
					foreach my $d ($layer->{line_y},-$layer->{line_y}){
						my $tmp_lines = TL::GenMath->polyline_offset($reverse_line_path,$d);
						my $tp = TL::GenMath->get_line_intersect(
							{xs=>$tmp_lines->[0]{x},ys=>$tmp_lines->[0]{y},xe=>$tmp_lines->[1]{x},ye=>$tmp_lines->[1]{y}},
							{xs=>$layer->{lines1}[-1]{x},ys=>$layer->{lines1}[-1]{y},xe=>$layer->{lines1}[-2]{x},ye=>$layer->{lines1}[-2]{y}},1);
						if (abs($tp->{x} - $layer->{lines1}[-1]{x}) < 0.0001 and abs($tp->{y} - $layer->{lines1}[-1]{y}) < 0.0001){
							shift @$tmp_lines;
							push @{$layer->{lines1}},@$tmp_lines;
							last;
						}
					}
				}
			}
		}
		if ($layer->{impedance_type} =~ /difference/){
			my $tmp_shift = ($layer->{cam_line_width} + $layer->{cam_line_spacing})/2000;
			my (@tmp_lines1,@tmp_lines2);
			my ($xs,$ys) = ($layer->{lines1}[0]{x},$layer->{lines1}[0]{y});
			foreach my $n (1..scalar(@{$layer->{lines1}})-1){
				my $it = $layer->{lines1}[$n];
				unless ($it->{direction}){
					push @tmp_lines1,{xs=>$xs,ys=>$ys,xe=>$it->{x},ye=>$it->{y}};
				}
				($xs,$ys) = ($it->{x},$it->{y});
			}
			($xs,$ys) = ($layer->{lines2}[0]{x},$layer->{lines2}[0]{y});
			foreach my $n (1..scalar(@{$layer->{lines2}})-1){
				my $it = $layer->{lines2}[$n];
				unless ($it->{direction}){
					push @tmp_lines2,{xs=>$xs,ys=>$ys,xe=>$it->{x},ye=>$it->{y}};
				}
				($xs,$ys) = ($it->{x},$it->{y});
			}
			foreach my $shift ($tmp_shift,-$tmp_shift){
				my $tmp_center = TL::GenMath->polyline_offset($layer->{lines1},$shift);
				my @tmp_center_lines;
				my ($xs,$ys) = ($tmp_center->[0]{x},$tmp_center->[0]{y});
				foreach my $n (1..scalar(@{$tmp_center})-1){
					my $it = $tmp_center->[$n];
					unless ($it->{direction}){
						push @tmp_center_lines,{xs=>$xs,ys=>$ys,xe=>$it->{x},ye=>$it->{y}};
					}
					($xs,$ys) = ($it->{x},$it->{y});
				}
				
				my $min_dist_line1 = 99999999;
				foreach my $cl (@tmp_center_lines){
					foreach my $l1 (@tmp_lines1){
						my $d = TL::GenMath->line2line_dist($cl,$l1);
						$min_dist_line1 = $d if $d < $min_dist_line1;
					}
				}
				my $min_dist_line2 = 99999999;
				foreach my $cl (@tmp_center_lines){
					foreach my $l2 (@tmp_lines2){
						my $d = TL::GenMath->line2line_dist($cl,$l2);
						$min_dist_line2 = $d if $d < $min_dist_line2;
					}
				}
				if (abs($min_dist_line1 - $min_dist_line2) < 0.0001){
					$layer->{center_lines} = $tmp_center;
					last;
				}
			}
			
			#AOI Pad
			if ($COUPON_DATA->{add_aoi_pad} eq 'yes'){
				my $tmp_lines_1 = TL::GenMath->polyline_offset($layer->{lines1},$COUPON_DATA->{aoi_pad_ar}/1000);
				my $tmp_lines_2 = TL::GenMath->polyline_offset($layer->{lines1},-$COUPON_DATA->{aoi_pad_ar}/1000);
				my $tmp_dist_1 = TL::GenMath->point2point_dist($layer->{center_lines}[-1],$tmp_lines_1->[-1]);
				my $tmp_dist_2 = TL::GenMath->point2point_dist($layer->{center_lines}[-1],$tmp_lines_2->[-1]);
				if ($tmp_dist_1 > $tmp_dist_2){
					push @{$layer->{aoi_pads}},{x=>$tmp_lines_1->[-1]{x},y=>$tmp_lines_1->[-1]{y}};
				}
				else{
					push @{$layer->{aoi_pads}},{x=>$tmp_lines_2->[-1]{x},y=>$tmp_lines_2->[-1]{y}};
				}
				
				$tmp_lines_1 = TL::GenMath->polyline_offset($layer->{lines2},$COUPON_DATA->{aoi_pad_ar}/1000);
				$tmp_lines_2 = TL::GenMath->polyline_offset($layer->{lines2},-$COUPON_DATA->{aoi_pad_ar}/1000);
				$tmp_dist_1 = TL::GenMath->point2point_dist($layer->{center_lines}[-1],$tmp_lines_1->[-1]);
				$tmp_dist_2 = TL::GenMath->point2point_dist($layer->{center_lines}[-1],$tmp_lines_2->[-1]);
				if ($tmp_dist_1 > $tmp_dist_2){
					push @{$layer->{aoi_pads}},{x=>$tmp_lines_1->[-1]{x},y=>$tmp_lines_1->[-1]{y}};
				}
				else{
					push @{$layer->{aoi_pads}},{x=>$tmp_lines_2->[-1]{x},y=>$tmp_lines_2->[-1]{y}};
				}
			}
		}
		else{
			#$layer->{center_lines} = eval(dump($layer->{lines1}));
			if ($COUPON_DATA->{add_aoi_pad} eq 'yes'){
				push @{$layer->{aoi_pads}},{x=>$layer->{lines1}[-1]{x},y=>$layer->{lines1}[-1]{y}};
			}
		}
	}
	
	#左边头部Copper Clip区域
	if (@left_pads){
		my $min_p = (sort{$a->{x} <=> $b->{x}} @left_pads)[0];
		$COUPON_DATA->{left_header_copper_clip} = {
			xmin => $min_p->{x} - $COUPON_DATA->{pad_size}/2000 - $COUPON_DATA->{header_copper_clip2pad}/1000 ,
			xmax => $COUPON_DATA->{'left_header_area_rect'}{xmax},
			ymin => -1 + $left_header_transform->{shift_y},
			ymax => 1 + $left_header_transform->{shift_y},
		};
	}
	if (@right_pads){
		my $min_p = (sort{$b->{x} <=> $a->{x}} @right_pads)[0];
		#右边头部Copper Clip区域
		$COUPON_DATA->{right_header_copper_clip} = {
			xmin => $COUPON_DATA->{'right_header_area_rect'}{xmin},
			xmax => $min_p->{x} + $COUPON_DATA->{pad_size}/2000 + $COUPON_DATA->{header_copper_clip2pad}/1000 ,
			ymin => -1 + $right_header_transform->{shift_y},
			ymax => 1 + $right_header_transform->{shift_y},
		};
	}
	
	
	
}

#调整阻抗宽度
sub adjust_coupon_width {
	my $self = shift;
	my %par = @_;
	$par{coupon_data} = $self->{COUPON_DATA} unless defined $par{coupon_data};
	my $COUPON_DATA = $par{coupon_data};
	my ($edge,$dn_y,$up_y);
	#计算上面和下面最小最大值
	foreach my $layer (values %{$COUPON_DATA->{layers}}){
		next unless ($layer->{layer_type} eq 'S');
		foreach my $lr ('left','right'){
			if ($layer->{$lr.'_tpads'} and @{$layer->{$lr.'_tpads'}}){
				foreach my $pad (@{$layer->{$lr.'_tpads'}},@{$layer->{$lr.'_gpads'}}){
					my $ymin = $pad->{y} - $COUPON_DATA->{pad_size}/2000 - $COUPON_DATA->{rout2copper_spacing}/1000;
					my $ymax = $pad->{y} + $COUPON_DATA->{pad_size}/2000 + $COUPON_DATA->{rout2copper_spacing}/1000;
					$edge->{$lr.'_header_dn_y'} = $ymin if $ymin < $edge->{$lr.'_header_dn_y'};
					$edge->{$lr.'_header_up_y'} = $ymax if $ymax > $edge->{$lr.'_header_up_y'};
					$dn_y = $ymin if $ymin < $dn_y;
					$up_y = $ymax if $ymax > $up_y;
				}
				foreach my $n (1..2){
					next unless $layer->{$lr.'_header_lines'.$n};
					foreach my $line (@{$layer->{$lr.'_header_lines'.$n}}){
						my $ymin =  $line->{y} - $layer->{cam_line_width}/2000 - $COUPON_DATA->{min_line2rout_spacing}/1000;
						my $ymax =  $line->{y} + $layer->{cam_line_width}/2000 + $COUPON_DATA->{min_line2rout_spacing}/1000;
						$edge->{$lr.'_header_dn_y'} = $ymin if $ymin < $edge->{$lr.'_header_dn_y'};
						$edge->{$lr.'_header_up_y'} = $ymax if $ymax > $edge->{$lr.'_header_up_y'};
						$dn_y = $ymin if $ymin < $dn_y;
						$up_y = $ymax if $ymax > $up_y;
					}
					my $body_ymin = $layer->{$lr.'_header_lines'.$n}[-1]{y} - $layer->{cam_line_width}/2000 - $COUPON_DATA->{min_line2rout_spacing}/1000;
					my $body_ymax = $layer->{$lr.'_header_lines'.$n}[-1]{y} + $layer->{cam_line_width}/2000 + $COUPON_DATA->{min_line2rout_spacing}/1000;
					$edge->{'body_dn_y'} = $body_ymin if $body_ymin < $edge->{'body_dn_y'};
					$edge->{'body_up_y'} = $body_ymax if $body_ymax > $edge->{'body_up_y'};
					$dn_y = $body_ymin if $body_ymin < $dn_y;
					$up_y = $body_ymax if $body_ymax > $up_y;
				}
			}
		}
	}
	#包含并部文字后上面和下面如果最小最大值
	foreach my $lr ('left','right'){
		foreach my $tlname (keys %{$COUPON_DATA->{$lr.'_header_text_pos'}}){
			foreach my $sig (keys %{$COUPON_DATA->{$lr.'_header_text_pos'}{$tlname}}){
				foreach my $v (values %{$COUPON_DATA->{$lr.'_header_text_pos'}{$tlname}{$sig}}){
					my $ymin = $v->{y} - $v->{y_size}/2 - $COUPON_DATA->{rout2copper_spacing}/1000;
					my $ymax = $v->{y} + $v->{y_size}/2 + $COUPON_DATA->{rout2copper_spacing}/1000;
					$edge->{$lr.'_header_dn_y'} = $ymin if $ymin < $edge->{$lr.'_header_dn_y'};
					$edge->{$lr.'_header_up_y'} = $ymax if $ymax > $edge->{$lr.'_header_up_y'};
					$dn_y = $ymin if $ymin < $dn_y;
					$up_y = $ymax if $ymax > $up_y;
				}
			}
		}
	}
	
	$COUPON_DATA->{width_center_shift} = 0;
	
	if ($COUPON_DATA->{auto_adjust_width_center} eq 'yes'){
		#如果可以自动调整宽度中心,则调整pad,线和头部文字的位置
		my $shifty = ($dn_y + $up_y)/2;
		$COUPON_DATA->{width_center_shift} = $shifty;
		foreach my $layer (values %{$COUPON_DATA->{layers}}){
			next unless ($layer->{layer_type} eq 'S');
			foreach my $lr ('left','right'){
				if ($layer->{$lr.'_tpads'} and @{$layer->{$lr.'_tpads'}}){
					foreach my $pad (@{$layer->{$lr.'_tpads'}},@{$layer->{$lr.'_gpads'}}){
						$pad->{y} += $shifty;
					}
					foreach my $n (1..2){
						next unless $layer->{$lr.'_header_lines'.$n};
						foreach my $line (@{$layer->{$lr.'_header_lines'.$n}}){
							$line->{y} += $shifty;
						}
					}
				}
			}
		}
		foreach my $lr ('left','right'){
			foreach my $tlname (keys %{$COUPON_DATA->{$lr.'_header_text_pos'}}){
				foreach my $sig (keys %{$COUPON_DATA->{$lr.'_header_text_pos'}{$tlname}}){
					foreach my $v (values %{$COUPON_DATA->{$lr.'_header_text_pos'}{$tlname}{$sig}}){
						$v->{y} += $shifty;
					}
				}
			}
		}
		foreach my $v (values %$edge){
			$v += $shifty;
		}
	}
	##阻抗整体区域
	#$COUPON_DATA->{'coupon_area_rect'}  = {
	#	ymin => -$COUPON_DATA->{coupon_header_width}/2 + $COUPON_DATA->{width_center_shift},								   
	#	ymax => $COUPON_DATA->{coupon_header_width}/2 + $COUPON_DATA->{width_center_shift},
	#	xmin => 0,
	#	xmax => $COUPON_DATA->{coupon_length},
	#};
	#阻抗body区域
	$COUPON_DATA->{'body_area_rect'}  = {
		ymin => -$COUPON_DATA->{coupon_body_width}/2 ,								   
		ymax => $COUPON_DATA->{coupon_body_width}/2 ,
		xmin => 0,
		xmax => $COUPON_DATA->{coupon_length},
	};
	if ($COUPON_DATA->{auto_enlarge_coupon_body_width} eq 'yes'){
		#如果允许自动涨大阻抗条宽度
		$COUPON_DATA->{'body_area_rect'}{ymin} = $edge->{'body_dn_y'} if $COUPON_DATA->{'body_area_rect'}{ymin}  > $edge->{body_dn_y};
		$COUPON_DATA->{'body_area_rect'}{ymax} = $edge->{'body_up_y'} if $COUPON_DATA->{'body_area_rect'}{ymax}  < $edge->{body_up_y};
	}
	$COUPON_DATA->{'body_area_rect'}{ymin} += $COUPON_DATA->{width_center_shift};
	$COUPON_DATA->{'body_area_rect'}{ymax} += $COUPON_DATA->{width_center_shift};
	#计算左右头部区域
	foreach my $lr ('left','right'){
		#next unless (defined $edge->{$lr.'_header_dn_y'});
		$COUPON_DATA->{$lr.'_header_area_rect'}  = {
			ymin => (defined $edge->{$lr.'_header_dn_y'})?-$COUPON_DATA->{coupon_header_width}/2+$COUPON_DATA->{width_center_shift}:$COUPON_DATA->{'body_area_rect'}{ymin},								   
			ymax => (defined $edge->{$lr.'_header_dn_y'})?$COUPON_DATA->{coupon_header_width}/2+$COUPON_DATA->{width_center_shift}:$COUPON_DATA->{'body_area_rect'}{ymax},	
			xmin => ($lr eq 'left')?0:$COUPON_DATA->{line_end_x},
			xmax => ($lr eq 'left')?$COUPON_DATA->{line_start_x}:$COUPON_DATA->{coupon_length},
		};
		
		#头部有绕线时,需加大header_area
		foreach my $layer (values %{$COUPON_DATA->{layers}}){
			next unless ($layer->{layer_type} eq 'S');
			my @tmp_x;
			if ($layer->{$lr.'_header_lines1'}){
				push @tmp_x,$layer->{$lr.'_header_lines1'}[-1]{x};
			}
			if ($layer->{$lr.'_header_lines2'}){
				push @tmp_x,$layer->{$lr.'_header_lines2'}[-1]{x};
			}
			@tmp_x = sort{$b<=>$a} @tmp_x;
			next unless @tmp_x;
			if ($lr eq 'left'){
				my $tx = $tmp_x[0]; #+ $layer->{cam_line_width}/2000 + $COUPON_DATA->{min_line2rout_spacing}/1000;
				$COUPON_DATA->{$lr.'_header_area_rect'}{xmax} = $tx if $tx > $COUPON_DATA->{$lr.'_header_area_rect'}{xmax};
			}
			elsif($lr eq 'right'){
				my $tx = $tmp_x[-1];# - $layer->{cam_line_width}/2000 - $COUPON_DATA->{min_line2rout_spacing}/1000;
				$COUPON_DATA->{$lr.'_header_area_rect'}{xmin} = $tx if $tx < $COUPON_DATA->{$lr.'_header_area_rect'}{xmin};
			}
		}
		
		if ($COUPON_DATA->{auto_enlarge_coupon_header_width} eq 'yes'){
			$COUPON_DATA->{$lr.'_header_area_rect'}{ymin} = $edge->{$lr.'_header_dn_y'} if $COUPON_DATA->{$lr.'_header_area_rect'}{ymin} > $edge->{$lr.'_header_dn_y'};
			$COUPON_DATA->{$lr.'_header_area_rect'}{ymax} = $edge->{$lr.'_header_up_y'} if $COUPON_DATA->{$lr.'_header_area_rect'}{ymax} < $edge->{$lr.'_header_up_y'};
		}
	}
	if ($COUPON_DATA->{coupon_header_body_same_width} eq 'yes'){
		my @tmp_y;
		foreach my $lr ('left','right'){
			next unless $COUPON_DATA->{$lr.'_header_area_rect'};
			push @tmp_y,$COUPON_DATA->{$lr.'_header_area_rect'}{ymax};
			push @tmp_y,$COUPON_DATA->{$lr.'_header_area_rect'}{ymin};
		}
		push @tmp_y,$COUPON_DATA->{'body_area_rect'}{ymin};
		push @tmp_y,$COUPON_DATA->{'body_area_rect'}{ymax};
		@tmp_y = sort {$a <=> $b} @tmp_y;
		$COUPON_DATA->{'body_area_rect'}{ymin} = $tmp_y[0];
		$COUPON_DATA->{'body_area_rect'}{ymax} = $tmp_y[-1];
		foreach my $lr ('left','right'){
			next unless $COUPON_DATA->{$lr.'_header_area_rect'};
			$COUPON_DATA->{$lr.'_header_area_rect'}{ymin} = $tmp_y[0];
			$COUPON_DATA->{$lr.'_header_area_rect'}{ymax} = $tmp_y[-1];
		}
	}
	foreach my $lr ('left','right'){
		next unless $COUPON_DATA->{$lr.'_header_area_rect'};
		if ($COUPON_DATA->{$lr.'_header_area_rect'}{ymin} > $edge->{$lr.'_header_dn_y'} or
			$COUPON_DATA->{$lr.'_header_area_rect'}{ymax} < $edge->{$lr.'_header_up_y'}
		){
			$COUPON_DATA->{header_not_enought_space}  = 1;
		}
	}
	$COUPON_DATA->{coupon_body_width} = $COUPON_DATA->{'body_area_rect'}{ymax} - $COUPON_DATA->{'body_area_rect'}{ymin};
}

#计算头部文字位置
sub header_text_position{
	my $self = shift;
	my %par = @_;
	$par{coupon_data} = $self->{COUPON_DATA} unless defined $par{coupon_data};
	my $COUPON_DATA = $par{coupon_data};
	my %pad_up_dn_y;
	#计算并头部最大最小Y
	foreach my $layer (values %{$COUPON_DATA->{layers}}){
		next unless ($layer->{layer_type} eq 'S');
		foreach my $lr ('left','right'){
			if ($layer->{$lr.'_tpads'} and @{$layer->{$lr.'_tpads'}}){
				foreach my $pad (@{$layer->{$lr.'_tpads'}},@{$layer->{$lr.'_gpads'}}){
					my $ymin = $pad->{y} - $COUPON_DATA->{pad_size}/2000;
					my $ymax = $pad->{y} + $COUPON_DATA->{pad_size}/2000;
					$pad_up_dn_y{$lr}{up_y} = $ymax if $ymax > $pad_up_dn_y{$lr}{up_y};
					$pad_up_dn_y{$lr}{dn_y} = $ymin if $ymin < $pad_up_dn_y{$lr}{dn_y};
				}
			}
			my $tmp_y = ($pad_up_dn_y{$lr}{up_y} + $pad_up_dn_y{$lr}{dn_y})/2;
			$pad_up_dn_y{$lr}{y_min} = $tmp_y - $COUPON_DATA->{coupon_header_width}/2 + $COUPON_DATA->{rout2copper_spacing}/1000;
			$pad_up_dn_y{$lr}{y_max} = $tmp_y + $COUPON_DATA->{coupon_header_width}/2 - $COUPON_DATA->{rout2copper_spacing}/1000;
		}
	}
	foreach my $drill (values %{$COUPON_DATA->{drills}}){
		next unless $drill->{include_coupon_groups};
		foreach my $side ('top','bot')
		{
			#分别计算每个钻孔正反面的文字层
			my ($txt_layer,$txt_polarity);
			$txt_layer = $COUPON_DATA->{layers}{$drill->{$side.'_text_layer'}};
			my $side_grp = $drill->{$side.'_coupon_groups'};
			if ($txt_layer->{layer_type} eq 'R' or $txt_layer->{impedance_type} =~ /coplanar/ or
				($txt_layer->{tl_name} =~ /(top|bottom)/ and $COUPON_DATA->{header_copper_fill} =~ /outer/) or
				($txt_layer->{tl_name} !~ /(top|bottom)/ and $COUPON_DATA->{header_copper_fill} =~ /inner/)
			){
				#如果此层为共面阻抗或并部需填铜,则需加负字
				$txt_polarity = 'negative';
			}
			else{
				$txt_polarity = 'positive';
			}
			#计算文字位置
			foreach my $lr ('left','right')
			{
				my ($up_y,$dn_y);
				if ($txt_polarity eq 'positive'){
					$up_y = $pad_up_dn_y{$lr}{up_y} + $COUPON_DATA->{header_pos_text2pad}/1000;
					$dn_y = $pad_up_dn_y{$lr}{dn_y} - $COUPON_DATA->{header_pos_text2pad}/1000;
				}
				else{
					$up_y = $pad_up_dn_y{$lr}{up_y} + $COUPON_DATA->{header_neg_text2pad}/1000;
					$dn_y = $pad_up_dn_y{$lr}{dn_y} - $COUPON_DATA->{header_neg_text2pad}/1000;
				}
				if ($side_grp){
					foreach my $grp (@$side_grp){
						foreach my $layer (@{$COUPON_DATA->{group}{$grp}{signal_layer}}){
							my $layer_info = $COUPON_DATA->{layers}{$layer};
							if ($layer_info->{$lr.'_tpads'} and @{$layer_info->{$lr.'_tpads'}}){
								foreach my $n (1..2){
									next unless $layer_info->{$lr.'_header_lines'.$n};
									foreach my $line (@{$layer_info->{$lr.'_header_lines'.$n}}){
										my ($ymin,$ymax);
										if ($txt_polarity eq 'positive'){
											$ymin =  $line->{y} - $layer_info->{cam_line_width}/2000 - $COUPON_DATA->{header_pos_text2line}/1000;
											$ymax =  $line->{y} + $layer_info->{cam_line_width}/2000 + $COUPON_DATA->{header_pos_text2line}/1000;
										}
										else{
											my $tmp_text2line = $layer_info->{imp_line2copper}/1000 - 0.002;
											$tmp_text2line = $COUPON_DATA->{header_neg_text2line}/1000 if $COUPON_DATA->{header_neg_text2line}/1000 > $tmp_text2line;
											$ymin =  $line->{y} - $layer_info->{cam_line_width}/2000 - $tmp_text2line;
											$ymax =  $line->{y} + $layer_info->{cam_line_width}/2000 + $tmp_text2line;
										}
										$up_y = $ymax if $ymax > $up_y;
										$dn_y = $ymin if $ymin < $dn_y;
									}
								}
								
								if ($txt_polarity eq 'negative'){
									foreach my $pad (@{$layer_info->{$lr.'_tpads'}}){
										my $tmp = $layer_info->{cam_line_width}/2000 + $layer_info->{imp_line2copper}/1000;
										my $ymin = $pad->{y} - $tmp;
										my $ymax = $pad->{y} + $tmp;
										$up_y = $ymax if $ymax > $up_y;
										$dn_y = $ymin if $ymin < $dn_y;
									}
								}
								
							}
						}
					}
				}
				foreach my $grp (@{$drill->{include_coupon_groups}}){
					foreach my $layer (@{$COUPON_DATA->{group}{$grp}{signal_layer}}){
						my $layer_info = $COUPON_DATA->{layers}{$layer};
						if ($layer_info->{$lr.'_tpads'} and @{$layer_info->{$lr.'_tpads'}}){
							my ($n1,$n2);
							if ($layer_info->{impedance_type} =~ /difference/){
								if ($lr eq 'left'){
									$n1 = 1;$n2 = 2;
								}
								else{
									if ($layer_info->{left_tpads} and @{$layer_info->{left_tpads}}){
										$n1 = 3;$n2 = 4;
									}
									else{
										$n1 = 1;$n2 = 2;
									}
								}
							}
							else{
								if ($lr eq 'left'){
									$n1 = 1;
								}
								else{
									if ($layer_info->{left_tpads} and @{$layer_info->{left_tpads}}){
										$n1 = 2;
									}
									else{
										$n1 = 1;
									}
								}
							}
							my $max_text_height =  $COUPON_DATA->{header_text_size}[0]{h}/1000;
							my $tmp = $self->_header_text_position(
								tpads=>$layer_info->{$lr.'_tpads'},
								gpads=>$layer_info->{$lr.'_gpads'},
								text2text=>$COUPON_DATA->{header_text2text_spacing}/1000,
								impedance_type=>$layer_info->{impedance_type},
								up_y=>$up_y,
								dn_y=>$dn_y,
								pad_size=>$COUPON_DATA->{pad_size}/1000,
								mirror_x=>$lr eq 'left'?0:1,
								mirror_y=>$layer_info->{$lr.'_tpads'}[0]{y} > $layer_info->{$lr.'_gpads'}[0]{y}?0:1,
								n1=>$n1,
								n2=>$n2,
								text2pad=>$COUPON_DATA->{header_pos_text2pad}/1000,
								max_text_height => $max_text_height,
							);
							
							foreach my $k (keys %$tmp){
								my $text = $COUPON_DATA->{'header_text_'.$lr.'_'.$k} || $COUPON_DATA->{'header_text_'.$k};
								$text = eval($text) if ($text and $text =~ /sub\s*\{/);
								next unless $text;
								my $angle = 0;
								if (ref($text) eq 'CODE'){
									$text = $text->(coupon_data=>$COUPON_DATA,layer=>$layer,imp_layer=>$layer_info,group=>$grp,side=>$side);
								}
								else{
									($text,my $sd) = split(':',$text);
									if ($sd and $sd !~ /$side/i){
										$text = undef;
									}
									elsif($text eq 'L3'){
										$text = $COUPON_DATA->{layer_mark}{$layer_info->{tl_name}};
									}
									elsif($text eq 'L3A'){
										$text = $layer_info->{number};
									}
									elsif($text eq '3'){
										$text = $layer_info->{layer_number};
									}
									elsif($text eq 'OHM'){
										$text = $layer_info->{target_impedance};
									}
									elsif($text eq 'L2/L5'){
										my @tmp;
										foreach my $r (@{$COUPON_DATA->{group}{$grp}{ref_layer}}){
											push @tmp,'L'.$COUPON_DATA->{layers}{$r}{layer_number};
										}
										$text = join('/',@tmp);
									}
									elsif($text eq '2/5'){
										my @tmp;
										foreach my $r (@{$COUPON_DATA->{group}{$grp}{ref_layer}}){
											push @tmp,$COUPON_DATA->{layers}{$r}{layer_number};
										}
										$text = join('/',@tmp);
									}
									elsif($text eq '3.5'){
										$text = $layer_info->{line_width1};
									}
								}
								
								
								next unless (defined $text and $text ne '');
								
								$angle += $tmp->{$k}{angle};
								$angle += 360 if $angle < 0;
								$angle -= 360 if $angle > 360;
								
								foreach my $txt_size (@{$COUPON_DATA->{header_text_size}}){
									if ($tmp->{$k}{ymin} < $pad_up_dn_y{$lr}{y_min}){
										$tmp->{$k}{ymin} = $tmp->{$k}{ymax} - $txt_size->{h}/1000,
									}
									if ($tmp->{$k}{ymax} > $pad_up_dn_y{$lr}{y_max}){
										$tmp->{$k}{ymax} = $tmp->{$k}{ymin} + $txt_size->{h}/1000,
									}
									last if ($tmp->{$k}{ymin} >= $pad_up_dn_y{$lr}{y_min}-0.0001 and
											 $tmp->{$k}{ymax} <= $pad_up_dn_y{$lr}{y_max}+0.0001);
								}
								my %txt_pos;
								foreach my $txt_size (@{$COUPON_DATA->{header_text_size}}){
									%txt_pos = (
										x => ($tmp->{$k}{xmin}+$tmp->{$k}{xmax})/2,
										y => ($tmp->{$k}{ymin}+$tmp->{$k}{ymax})/2,
										text => $text,
										angle => $angle,
										x_size => $txt_size->{w}/1000,
										y_size => $txt_size->{h}/1000,
										line_width => $txt_size->{l},
										anchor => 'center',
										polarity => $txt_polarity,
									);
									my $rect = $self->_get_text_rect(%txt_pos);
									if ($rect->{x1} >= $tmp->{$k}{xmin}-0.0001 and $rect->{x2} <= $tmp->{$k}{xmax}+0.0001 and
										$rect->{y1} >= $tmp->{$k}{ymin}-0.0001 and $rect->{y2} <= $tmp->{$k}{ymax}+0.0001)
									{
										last;
									}
								}
								foreach my $t (keys %txt_pos){
									$tmp->{$k}{$t} = $txt_pos{$t};
								}
								$COUPON_DATA->{$lr.'_header_text_pos'}{$txt_layer->{tl_name}}{$layer}{$k} = $tmp->{$k};
							}
						}
					}
				}
			}
		}
	}
}

#计算文字位置
sub _header_text_position{
	my $self = shift;
	my %par = @_;
	$par{tpads} = eval(dump($par{tpads}));
	$par{gpads} = eval(dump($par{gpads}));
	if ($par{mirror_y}){
		foreach my $it (@{$par{tpads}},@{$par{gpads}}){
			$it->{y} = - $it->{y};
		}
		($par{up_y},$par{dn_y}) = (-$par{dn_y},-$par{up_y});
	}
	if ($par{mirror_x}){
		foreach my $it (@{$par{tpads}},@{$par{gpads}}){
			$it->{x} = - $it->{x};
		}
	}
	my %return;
	my $max_text_h = $par{max_text_height};
	if ($par{impedance_type} =~ /difference/){
		#类似259样式
		if (abs($par{tpads}[0]{x} - $par{tpads}[1]{x}) < 0.001){
			my $dx = abs($par{tpads}[0]{x} - $par{gpads}[0]{x})/2;
			my $dx1 = abs($par{tpads}[0]{x} - $par{gpads}[0]{x}) - $par{pad_size}/2 - $par{text2pad};
			#d=>双线,s=>双线,t=>test pad,g=>gnd pad, _1,_2=>第几个位置
			$return{'d_t'.$par{n1}.'_1'} = {
				xmin=>$par{tpads}[0]{x} - $dx + $par{text2text}/2,
				xmax=>$par{tpads}[0]{x} + $dx - $par{text2text}/2,
				ymin=>$par{up_y},
				ymax=>$par{up_y} + $max_text_h,
				angle=>0,
			};
			$return{'d_t'.$par{n1}.'_2'} = {
				xmin=>$par{tpads}[0]{x}+$par{pad_size}/2+$par{text2pad},
				xmax=>$par{tpads}[0]{x}+$dx1,
				ymin=>$par{tpads}[0]{y}-$par{pad_size}/2,
				ymax=>$par{tpads}[0]{y}+$par{pad_size}/2,
				angle=>270,
			};
			$return{'d_g'.$par{n1}.'_1'} = {
				xmin=>$par{gpads}[0]{x} - $dx + $par{text2text}/2,
				xmax=>$par{gpads}[0]{x} + $dx - $par{text2text}/2,
				ymin=>$par{up_y},
				ymax=>$par{up_y} + $max_text_h,
				angle=>0,
			};
			$return{'d_g'.$par{n1}.'_2'} = {
				xmin=>$par{gpads}[0]{x}-$dx1,
				xmax=>$par{gpads}[0]{x}-$par{pad_size}/2-$par{text2pad},
				ymin=>$par{gpads}[0]{y}-$par{pad_size}/2,
				ymax=>$par{gpads}[0]{y}+$par{pad_size}/2,
				angle=>270,
			};
			$return{'d_t'.$par{n1}.'g'.$par{n1}.'_1'} = {
				xmin=>$par{gpads}[0]{x}-$par{pad_size}/2,
				xmax=>$par{tpads}[0]{x}+$par{pad_size}/2,
				ymin=>$par{up_y},
				ymax=>$par{up_y} + $max_text_h,
				angle=>0,
			};
			$return{'d_t'.$par{n1}.'g'.$par{n1}.'_2'} = {
				xmin=>$par{gpads}[0]{x}+$par{pad_size}/2+$par{text2text},
				xmax=>$par{tpads}[0]{x}-$par{pad_size}/2-$par{text2text},
				ymin=>$par{gpads}[0]{y}-$par{pad_size}/2,
				ymax=>$par{tpads}[0]{y}+$par{pad_size}/2,
				angle=>270,
			};
			
			$return{'d_t'.$par{n2}.'_1'} = {
				xmin=>$par{tpads}[1]{x} - $dx + $par{text2text}/2,
				xmax=>$par{tpads}[1]{x} + $dx - $par{text2text}/2,
				ymin=>$par{dn_y} - $max_text_h,
				ymax=>$par{dn_y},
				angle=>0,
			};
			$return{'d_t'.$par{n2}.'_2'} = {
				xmin=>$par{tpads}[1]{x}+$par{pad_size}/2+$par{text2pad},
				xmax=>$par{tpads}[1]{x}+$dx1,
				ymin=>$par{tpads}[1]{y}-$par{pad_size}/2,
				ymax=>$par{tpads}[1]{y}+$par{pad_size}/2,
				angle=>270,
			};
			$return{'d_g'.$par{n2}.'_1'} = {
				xmin=>$par{gpads}[1]{x} - $dx + $par{text2text}/2,
				xmax=>$par{gpads}[1]{x} + $dx - $par{text2text}/2,
				ymin=>$par{dn_y} - $max_text_h,
				ymax=>$par{dn_y},
				angle=>0,
			};
			$return{'d_g'.$par{n2}.'_2'} = {
				xmin=>$par{gpads}[1]{x}-$dx1,
				xmax=>$par{gpads}[1]{x}-$par{pad_size}/2-$par{text2pad},
				ymin=>$par{gpads}[1]{y}-$par{pad_size}/2,
				ymax=>$par{gpads}[1]{y}+$par{pad_size}/2,
				angle=>270,
			};
			$return{'d_t'.$par{n2}.'g'.$par{n2}.'_1'} = {
				xmin=>$par{gpads}[1]{x}-$par{pad_size}/2,
				xmax=>$par{tpads}[1]{x}+$par{pad_size}/2,
				ymin=>$par{dn_y} - $max_text_h,
				ymax=>$par{dn_y},
				angle=>0,
			};
			$return{'d_t'.$par{n2}.'g'.$par{n2}.'_2'} = {
				xmin=>$par{gpads}[1]{x}+$par{pad_size}/2+$par{text2text},
				xmax=>$par{tpads}[1]{x}-$par{pad_size}/2-$par{text2text},
				ymin=>$par{gpads}[1]{y}-$par{pad_size}/2,
				ymax=>$par{tpads}[1]{y}+$par{pad_size}/2,
				angle=>270,
			};
		}
		else{
			my $dx = abs($par{tpads}[0]{x} - $par{tpads}[1]{x})/2;
			my $dx1 = abs($par{tpads}[0]{x} - $par{tpads}[1]{x}) - $par{pad_size}/2 - $par{text2pad};
			$return{'d_t'.$par{n1}.'_1'} = {
				xmin=>$par{tpads}[0]{x} - $dx + $par{text2text}/2,
				xmax=>$par{tpads}[0]{x} + $dx - $par{text2text}/2,
				ymin=>$par{up_y},
				ymax=>$par{up_y} + $max_text_h,
				angle=>0,
			};
			$return{'d_t'.$par{n1}.'_2'} = {
				xmin=>$par{tpads}[0]{x}+$par{pad_size}/2+$par{text2pad},
				xmax=>$par{tpads}[0]{x}+$dx1,
				ymin=>$par{tpads}[0]{y}-$par{pad_size}/2,
				ymax=>$par{tpads}[0]{y}+$par{pad_size}/2,
				angle=>270,
			};
			$return{'d_t'.$par{n2}.'_1'} = {
				xmin=>$par{tpads}[1]{x} - $dx + $par{text2text}/2,
				xmax=>$par{tpads}[1]{x} + $dx - $par{text2text}/2,
				ymin=>$par{up_y},
				ymax=>$par{up_y} + $max_text_h,
				angle=>0,
			};
			$return{'d_t'.$par{n2}.'_2'} = {
				xmin=>$par{tpads}[1]{x}-$dx1,
				xmax=>$par{tpads}[1]{x}-$par{pad_size}/2-$par{text2pad},
				ymin=>$par{tpads}[1]{y}-$par{pad_size}/2,
				ymax=>$par{tpads}[1]{y}+$par{pad_size}/2,
				angle=>270,
			};
			$return{'d_t'.$par{n1}.'t'.$par{n2}.'_1'} = {
				xmin=>$par{tpads}[1]{x}-$par{pad_size}/2,
				xmax=>$par{tpads}[0]{x}+$par{pad_size}/2,
				ymin=>$par{up_y},
				ymax=>$par{up_y} + $max_text_h,
				angle=>0,
			};
			$return{'d_t'.$par{n1}.'t'.$par{n2}.'_2'} = {
				xmin=>$par{tpads}[1]{x}+$par{pad_size}/2+$par{text2pad},
				xmax=>$par{tpads}[0]{x}-$par{pad_size}/2-$par{text2pad},
				ymin=>$par{tpads}[1]{y}-$par{pad_size}/2,
				ymax=>$par{tpads}[0]{y}+$par{pad_size}/2,
				angle=>270,
			};
			$return{'d_g'.$par{n1}.'_1'} = {
				xmin=>$par{gpads}[0]{x} - $dx + $par{text2text}/2,
				xmax=>$par{gpads}[0]{x} + $dx - $par{text2text}/2,
				ymin=>$par{dn_y} - $max_text_h,
				ymax=>$par{dn_y},
				angle=>0,
			};
			$return{'d_g'.$par{n1}.'_2'} = {
				xmin=>$par{gpads}[0]{x}+$par{pad_size}/2+$par{text2pad},
				xmax=>$par{gpads}[0]{x}+$dx1,
				ymin=>$par{gpads}[0]{y}-$par{pad_size}/2,
				ymax=>$par{gpads}[0]{y}+$par{pad_size}/2,
				angle=>270,
			};
			$return{'d_g'.$par{n2}.'_1'} = {
				xmin=>$par{gpads}[1]{x} - $dx + $par{text2text}/2,
				xmax=>$par{gpads}[1]{x} + $dx - $par{text2text}/2,
				ymin=>$par{dn_y} - $max_text_h,
				ymax=>$par{dn_y},
				angle=>0,
			};
			$return{'d_g'.$par{n2}.'_2'} = {
				xmin=>$par{gpads}[1]{x}-$dx1,
				xmax=>$par{gpads}[1]{x}-$par{pad_size}/2-$par{text2pad},
				ymin=>$par{gpads}[1]{y}-$par{pad_size}/2,
				ymax=>$par{gpads}[1]{y}+$par{pad_size}/2,
				angle=>270,
			};
			$return{'d_g'.$par{n1}.'g'.$par{n2}.'_1'} = {
				xmin=>$par{gpads}[1]{x}-$par{pad_size}/2,
				xmax=>$par{gpads}[0]{x}+$par{pad_size}/2,
				ymin=>$par{dn_y} - $max_text_h,
				ymax=>$par{dn_y},
				angle=>0,
			};
			$return{'d_g'.$par{n1}.'g'.$par{n2}.'_2'} = {
				xmin=>$par{gpads}[1]{x}+$par{pad_size}/2+$par{text2pad},
				xmax=>$par{gpads}[0]{x}-$par{pad_size}/2-$par{text2pad},
				ymin=>$par{gpads}[1]{y}-$par{pad_size}/2,
				ymax=>$par{gpads}[0]{y}+$par{pad_size}/2,
				angle=>270,
			};
		}
	}
	else{
		if (abs($par{tpads}[0]{y} - $par{gpads}[1]{y}) < 0.001){
			my $dx = abs($par{tpads}[0]{x} - $par{gpads}[0]{x})/2;
			my $dx1 = abs($par{tpads}[0]{x} - $par{gpads}[0]{x}) - $par{pad_size}/2 - $par{text2pad};
			$return{'s_t'.$par{n1}.'_1'} = {
				xmin=>$par{tpads}[0]{x} - $dx + $par{text2text}/2,
				xmax=>$par{tpads}[0]{x} + $dx - $par{text2text}/2,
				ymin=>$par{up_y},
				ymax=>$par{up_y} + $max_text_h,
				angle=>0,
			};
			$return{'s_t'.$par{n1}.'_2'} = {
				xmin=>$par{tpads}[0]{x}+$par{pad_size}/2+$par{text2pad},
				xmax=>$par{tpads}[0]{x}+$dx1,
				ymin=>$par{tpads}[0]{y}-$par{pad_size}/2,
				ymax=>$par{tpads}[0]{y}+$par{pad_size}/2,
				angle=>270,
			};
			$return{'s_t'.$par{n1}.'_3'} = {
				xmin=>$par{tpads}[0]{x} - $dx + $par{text2text}/2,
				xmax=>$par{tpads}[0]{x} + $dx - $par{text2text}/2,
				ymin=>$par{dn_y} - $max_text_h,
				ymax=>$par{dn_y},
				angle=>0,
			};
			$return{'s_g'.$par{n1}.'_1'} = {
				xmin=>$par{gpads}[0]{x} - $dx + $par{text2text}/2,
				xmax=>$par{gpads}[0]{x} + $dx - $par{text2text}/2,
				ymin=>$par{up_y},
				ymax=>$par{up_y} + $max_text_h,
				angle=>0,
			};
			$return{'s_g'.$par{n1}.'_2'} = {
				xmin=>$par{gpads}[0]{x}-$dx1,
				xmax=>$par{gpads}[0]{x}-$par{pad_size}/2-$par{text2pad},
				ymin=>$par{gpads}[0]{y}-$par{pad_size}/2,
				ymax=>$par{gpads}[0]{y}+$par{pad_size}/2,
				angle=>270,
			};
			$return{'s_g'.$par{n1}.'_3'} = {
				xmin=>$par{gpads}[0]{x} - $dx + $par{text2text}/2,
				xmax=>$par{gpads}[0]{x} + $dx - $par{text2text}/2,
				ymin=>$par{dn_y} - $max_text_h,
				ymax=>$par{dn_y},
				angle=>0,
			};
			$return{'s_t'.$par{n1}.'g'.$par{n1}.'_1'} = {
				xmin=>$par{gpads}[0]{x} - $par{pad_size}/2,
				xmax=>$par{tpads}[0]{x} + $par{pad_size}/2,
				ymin=>$par{up_y},
				ymax=>$par{up_y} + $max_text_h,
				angle=>0,
			};
			$return{'s_t'.$par{n1}.'g'.$par{n1}.'_2'} = {
				xmin=>$par{gpads}[0]{x}+$par{pad_size}/2+$par{text2pad}/2,
				xmax=>$par{tpads}[0]{x}-$par{pad_size}/2-$par{text2pad},
				ymin=>$par{gpads}[0]{y}-$par{pad_size}/2,
				ymax=>$par{gpads}[0]{y}+$par{pad_size}/2,
				angle=>270,
			};
			$return{'s_t'.$par{n1}.'g'.$par{n1}.'_3'} = {
				xmin=>$par{gpads}[0]{x} - $par{pad_size}/2,
				xmax=>$par{tpads}[0]{x} + $par{pad_size}/2,
				ymin=>$par{dn_y} - $max_text_h,
				ymax=>$par{dn_y},
				angle=>0,
			};
		}
		else{
			my $dx = abs($par{tpads}[0]{y} - $par{gpads}[0]{y})/2;
			my $dx1 = abs($par{tpads}[0]{y} - $par{gpads}[0]{y}) - $par{pad_size}/2 - $par{text2pad};
			$return{'s_t'.$par{n1}.'_1'} = {
				xmin=>$par{tpads}[0]{x} - $dx + $par{text2text}/2,
				xmax=>$par{tpads}[0]{x} + $dx - $par{text2text}/2,
				ymin=>$par{up_y},
				ymax=>$par{up_y} + $max_text_h,
				angle=>0,
			};
			$return{'s_t'.$par{n1}.'_2'} = {
				xmin=>$par{tpads}[0]{x}+$par{pad_size}/2+$par{text2pad},
				xmax=>$par{tpads}[0]{x}+$dx1,
				ymin=>$par{tpads}[0]{y}-$par{pad_size}/2,
				ymax=>$par{tpads}[0]{y}+$par{pad_size}/2,
				angle=>270,
			};
			$return{'s_t'.$par{n1}.'_3'} = {
				xmin=>$par{tpads}[0]{x}-$dx1,
				xmax=>$par{tpads}[0]{x}-$par{pad_size}/2-$par{text2pad},
				ymin=>$par{tpads}[0]{y}-$par{pad_size}/2,
				ymax=>$par{tpads}[0]{y}+$par{pad_size}/2,
				angle=>270,
			};
			$return{'s_g'.$par{n1}.'_1'} = {
				xmin=>$par{gpads}[0]{x} - $dx + $par{text2text}/2,
				xmax=>$par{gpads}[0]{x} + $dx - $par{text2text}/2,
				ymin=>$par{dn_y} - $max_text_h,
				ymax=>$par{dn_y},
				angle=>0,
			};
			$return{'s_g'.$par{n1}.'_2'} = {
				xmin=>$par{gpads}[0]{x}+$par{pad_size}/2+$par{text2pad},
				xmax=>$par{gpads}[0]{x}+$dx1,
				ymin=>$par{gpads}[0]{y}-$par{pad_size}/2,
				ymax=>$par{gpads}[0]{y}+$par{pad_size}/2,
				angle=>270,
			};
			$return{'s_g'.$par{n1}.'_3'} = {
				xmin=>$par{gpads}[0]{x}-$dx1,
				xmax=>$par{gpads}[0]{x}-$par{pad_size}/2-$par{text2pad},
				ymin=>$par{gpads}[0]{y}-$par{pad_size}/2,
				ymax=>$par{gpads}[0]{y}+$par{pad_size}/2,
				angle=>270,
			};
		}
	}
	if ($par{mirror_y}){
		foreach my $item (values %return){
			($item->{ymin},$item->{ymax}) = (-$item->{ymax},-$item->{ymin});
		}
	}
	if ($par{mirror_x}){
		foreach my $item (values %return){
			($item->{xmin},$item->{xmax}) = (-$item->{xmax},-$item->{xmin});
			$item->{angle} = 360-$item->{angle}  if $item->{angle}; 
		}
	}
	return \%return;
}

sub layout_header_lines{
	my $self = shift;
	my %par = @_;
	$par{coupon_data} = $self->{COUPON_DATA} unless defined $par{coupon_data};
	my $COUPON_DATA = $par{coupon_data} ;
	my (@left_pads,@right_pads,@left_tpads,@left_gpads,@right_tpads,@right_gpads);
	foreach my $item (values %{$COUPON_DATA->{layers}}){
		next unless ($item->{layer_type} eq 'S');
		push @left_tpads,@{$item->{left_tpads}} if $item->{left_tpads};
		push @left_gpads,@{$item->{left_gpads}} if $item->{left_gpads};
		push @right_tpads,@{$item->{right_tpads}} if $item->{right_tpads};
		push @right_gpads,@{$item->{right_gpads}} if $item->{right_gpads};
	}
	@left_pads = (@left_tpads,@left_gpads);
	@right_pads = (@right_tpads,@right_gpads);
	foreach my $group (keys %{$COUPON_DATA->{group}}){
		#left
		if (@left_pads){
			my @tmp_pad = sort{$b->{x} <=> $a->{x} || $a->{y} <=> $b->{y}} @left_pads;
			my $p_xmax = $tmp_pad[0];
			@tmp_pad = sort{$b->{y} <=> $a->{y} || $a->{x} <=> $b->{x}} @left_pads;
			my ($p_ymax,$p_ymin) = ($tmp_pad[0],$tmp_pad[-1]);
			my $tpad_block_ymin = $p_ymax->{y} - $COUPON_DATA->{pad_size}/2000 - $COUPON_DATA->{line2pad_spacing}/1000;
			my $tpad_block_ymax = $p_ymax->{y} + $COUPON_DATA->{pad_size}/2000 + $COUPON_DATA->{line2pad_spacing}/1000;
			my $gpad_block_ymin = $p_ymin->{y} - $COUPON_DATA->{pad_size}/2000 - $COUPON_DATA->{line2pad_spacing}/1000;
			my $gpad_block_ymax = $p_ymin->{y} + $COUPON_DATA->{pad_size}/2000 + $COUPON_DATA->{line2pad_spacing}/1000;
			
			my $dist_pad2cornerline = sqrt(2)*$COUPON_DATA->{pad_size}/2000 + ($COUPON_DATA->{line2pad_spacing_2} || $COUPON_DATA->{line2pad_spacing})/1000;
			my $tpad_rerout_kb1 = {k => 0,b=>$p_ymax->{y} + $COUPON_DATA->{pad_size}/2000 + ($COUPON_DATA->{line2pad_spacing_1} || $COUPON_DATA->{line2pad_spacing})/1000};
			my $tpad_rerout_kb2 = TL::GenMath->get_kbline_parallel({k => -1,b=>$p_xmax->{x} + $p_ymax->{y}},$dist_pad2cornerline);
			my $tmp_x3 = $p_xmax->{x} + $COUPON_DATA->{pad_size}/2000 + ($COUPON_DATA->{line2pad_spacing_3} || $COUPON_DATA->{line2pad_spacing})/1000;
			my $tpad_rerout_kb3 = {k => undef,b=>$tmp_x3};
			my $tmp_y4 = $p_ymax->{y} - $COUPON_DATA->{pad_size}/2000;
			my $tpad_rerout_kb4 = {k => -1,b=>$tmp_x3+$tmp_y4+$COUPON_DATA->{chamfer_size_max}/1000};
			
			my $gpad_rerout_kb1 = {k => 0,b=>$p_ymin->{y} - $COUPON_DATA->{pad_size}/2000 - ($COUPON_DATA->{line2pad_spacing_1} || $COUPON_DATA->{line2pad_spacing})/1000};
			my $gpad_rerout_kb2 = TL::GenMath->get_kbline_parallel({k => 1,b=>$p_ymin->{y}-$p_xmax->{x}},$dist_pad2cornerline);
			my $gpad_rerout_kb3 = {k => undef,b=>$tmp_x3};
			my $tmp_y5 = $p_ymin->{y} + $COUPON_DATA->{pad_size}/2000;
			my $gpad_rerout_kb4 = {k => 1,b=>$tmp_y5 - $tmp_x3 - $COUPON_DATA->{chamfer_size_max}/1000};
			my @sort_layers = sort{abs($COUPON_DATA->{layers}{$a}{line_y_sequence}) <=> abs($COUPON_DATA->{layers}{$b}{line_y_sequence})}  @{$COUPON_DATA->{group}{$group}{signal_layer}};
			foreach my $layer (@sort_layers){
				my $layer_info = $COUPON_DATA->{layers}{$layer};
				if ($layer_info->{left_tpads} and (@{$layer_info->{left_tpads}})){
					my $tpads = eval(dump($COUPON_DATA->{layers}{$layer}{left_tpads}));
					my $tmp = $self->_layout_header_lines(
						coupon_shape_tag => $COUPON_DATA->{coupon_left_shape},
						is_first_pad=>($layer_info->{is_left_first} or $layer_info->{is_right_first})?1:0,
						line_y=>$layer_info->{line_y},
						tpads=>$tpads,
						cam_total_width => $layer_info->{cam_total_width},
						cam_line_width => $layer_info->{cam_line_width},
						cam_line_spacing => $layer_info->{cam_line_spacing},
						impedance_type => $layer_info->{impedance_type},
						line_shift_x=>$COUPON_DATA->{line_shift_x} || 0.05,
						rerout_line_spacing=>$COUPON_DATA->{rerout_line_spacing},
						chamfer_size_max => $COUPON_DATA->{chamfer_size_max},
						chamfer_size_min => $COUPON_DATA->{chamfer_size_min},
						tpad_rerout_kb1 => $tpad_rerout_kb1,
						tpad_rerout_kb2 => $tpad_rerout_kb2,
						tpad_rerout_kb3 => $tpad_rerout_kb3,
						tpad_rerout_kb4 => $tpad_rerout_kb4,
						gpad_rerout_kb1 => $gpad_rerout_kb1,
						gpad_rerout_kb2 => $gpad_rerout_kb2,
						gpad_rerout_kb3 => $gpad_rerout_kb3,
						gpad_rerout_kb4 => $gpad_rerout_kb4,
						line_y_sequence => $layer_info->{line_y_sequence},
						mirror_y => $layer_info->{left_gpads}[0]{y} > $layer_info->{left_tpads}[0]{y}?1:0,
					);
					$tpad_rerout_kb1 = $tmp->{tpad_rerout_kb1};
					$tpad_rerout_kb2 = $tmp->{tpad_rerout_kb2};
					$tpad_rerout_kb3 = $tmp->{tpad_rerout_kb3};
					$tpad_rerout_kb4 = $tmp->{tpad_rerout_kb4};
					$gpad_rerout_kb1 = $tmp->{gpad_rerout_kb1};
					$gpad_rerout_kb2 = $tmp->{gpad_rerout_kb2};
					$gpad_rerout_kb3 = $tmp->{gpad_rerout_kb3};
					$gpad_rerout_kb4 = $tmp->{gpad_rerout_kb4};
					$layer_info->{left_header_lines1} = $tmp->{lines1};
					$layer_info->{left_header_lines2} = $tmp->{lines2};
				}
			}
		}
		#Right
		if (@right_pads){
			my @tmp_right_pads = eval(dump(@right_pads));
			foreach my $it (@tmp_right_pads){
				$it->{x} = -$it->{x};
			}
			my @tmp_pad = sort{$b->{x} <=> $a->{x} || $a->{y} <=> $b->{y}} @tmp_right_pads;
			my $p_xmax = $tmp_pad[0];
			@tmp_pad = sort{$b->{y} <=> $a->{y} || $a->{x} <=> $b->{x}} @tmp_right_pads;
			my ($p_ymax,$p_ymin) = ($tmp_pad[0],$tmp_pad[-1]);
			my $tpad_block_ymin = $p_ymax->{y} - $COUPON_DATA->{pad_size}/2000 - $COUPON_DATA->{line2pad_spacing}/1000;
			my $tpad_block_ymax = $p_ymax->{y} + $COUPON_DATA->{pad_size}/2000 + $COUPON_DATA->{line2pad_spacing}/1000;
			my $gpad_block_ymin = $p_ymin->{y} - $COUPON_DATA->{pad_size}/2000 - $COUPON_DATA->{line2pad_spacing}/1000;
			my $gpad_block_ymax = $p_ymin->{y} + $COUPON_DATA->{pad_size}/2000 + $COUPON_DATA->{line2pad_spacing}/1000;
			
			my $dist_pad2cornerline = sqrt(2)*$COUPON_DATA->{pad_size}/2000 + ($COUPON_DATA->{line2pad_spacing_2} || $COUPON_DATA->{line2pad_spacing})/1000;
			my $tpad_rerout_kb1 = {k => 0,b=>$p_ymax->{y} + $COUPON_DATA->{pad_size}/2000 + ($COUPON_DATA->{line2pad_spacing_1} || $COUPON_DATA->{line2pad_spacing})/1000};
			my $tpad_rerout_kb2 = TL::GenMath->get_kbline_parallel({k => -1,b=>$p_xmax->{x} + $p_ymax->{y}},$dist_pad2cornerline);
			my $tmp_x3 = $p_xmax->{x} + $COUPON_DATA->{pad_size}/2000 + ($COUPON_DATA->{line2pad_spacing_3} || $COUPON_DATA->{line2pad_spacing})/1000;
			my $tpad_rerout_kb3 = {k => undef,b=>$tmp_x3};
			my $tmp_y4 = $p_ymax->{y} - $COUPON_DATA->{pad_size}/2000;
			my $tpad_rerout_kb4 = {k => -1,b=>$tmp_x3+$tmp_y4+$COUPON_DATA->{chamfer_size_max}/1000};
			
			my $gpad_rerout_kb1 = {k => 0,b=>$p_ymin->{y} - $COUPON_DATA->{pad_size}/2000 - ($COUPON_DATA->{line2pad_spacing_1} || $COUPON_DATA->{line2pad_spacing})/1000};
			my $gpad_rerout_kb2 = TL::GenMath->get_kbline_parallel({k => 1,b=>$p_ymin->{y}-$p_xmax->{x}},$dist_pad2cornerline);
			my $gpad_rerout_kb3 = {k => undef,b=>$tmp_x3};
			my $tmp_y5 = $p_ymin->{y} + $COUPON_DATA->{pad_size}/2000;
			my $gpad_rerout_kb4 = {k => 1,b=>$tmp_y5 - $tmp_x3 - $COUPON_DATA->{chamfer_size_max}/1000};
			my @sort_layers = sort{abs($COUPON_DATA->{layers}{$a}{line_y_sequence}) <=> abs($COUPON_DATA->{layers}{$b}{line_y_sequence})}  @{$COUPON_DATA->{group}{$group}{signal_layer}};
			foreach my $layer (@sort_layers){
				my $layer_info = $COUPON_DATA->{layers}{$layer};
				if ($layer_info->{right_tpads} and (@{$layer_info->{right_tpads}})){
					my $tpads = eval(dump($COUPON_DATA->{layers}{$layer}{right_tpads}));
					foreach my $tpad (@$tpads){
						$tpad->{x} = - $tpad->{x};
					}
					my $tmp = $self->_layout_header_lines(
						coupon_shape_tag => $COUPON_DATA->{coupon_right_shape},
						is_first_pad=>($layer_info->{is_left_first} or $layer_info->{is_right_first})?1:0,
						line_y=>$layer_info->{line_y},
						tpads=>$tpads,
						cam_total_width => $layer_info->{cam_total_width},
						cam_line_width => $layer_info->{cam_line_width},
						cam_line_spacing => $layer_info->{cam_line_spacing},
						impedance_type => $layer_info->{impedance_type},
						line_shift_x=>$COUPON_DATA->{line_shift_x} || 0.05,
						rerout_line_spacing=>$COUPON_DATA->{rerout_line_spacing},
						chamfer_size_max => $COUPON_DATA->{chamfer_size_max},
						chamfer_size_min => $COUPON_DATA->{chamfer_size_min},
						tpad_rerout_kb1 => $tpad_rerout_kb1,
						tpad_rerout_kb2 => $tpad_rerout_kb2,
						tpad_rerout_kb3 => $tpad_rerout_kb3,
						tpad_rerout_kb4 => $tpad_rerout_kb4,
						gpad_rerout_kb1 => $gpad_rerout_kb1,
						gpad_rerout_kb2 => $gpad_rerout_kb2,
						gpad_rerout_kb3 => $gpad_rerout_kb3,
						gpad_rerout_kb4 => $gpad_rerout_kb4,
						line_y_sequence => $layer_info->{line_y_sequence},
						mirror_y => $layer_info->{right_gpads}[0]{y} > $layer_info->{right_tpads}[0]{y}?1:0,
					);
					$tpad_rerout_kb1 = $tmp->{tpad_rerout_kb1};
					$tpad_rerout_kb2 = $tmp->{tpad_rerout_kb2};
					$tpad_rerout_kb3 = $tmp->{tpad_rerout_kb3};
					$tpad_rerout_kb4 = $tmp->{tpad_rerout_kb4};
					$gpad_rerout_kb1 = $tmp->{gpad_rerout_kb1};
					$gpad_rerout_kb2 = $tmp->{gpad_rerout_kb2};
					$gpad_rerout_kb3 = $tmp->{gpad_rerout_kb3};
					$gpad_rerout_kb4 = $tmp->{gpad_rerout_kb4};
					if ($tmp->{lines1} and @{$tmp->{lines1}}){
						foreach my $it (@{$tmp->{lines1}}){
							$it->{x} = -$it->{x};
						}
					}
					if ($tmp->{lines2} and @{$tmp->{lines2}}){
						foreach my $it (@{$tmp->{lines2}}){
							$it->{x} = -$it->{x};
						}
					}
					$layer_info->{right_header_lines1} = $tmp->{lines1};
					$layer_info->{right_header_lines2} = $tmp->{lines2};
				}
			}
		}
	}
	
	
}


sub _layout_header_lines{
	my $self = shift;
	my %par = @_;
	$par{diff_line_dist} = ($par{cam_line_width} + $par{cam_line_spacing})/1000;
	if ($par{mirror_y}){
		foreach my $it (@{$par{tpads}}){
			$it->{y} = - $it->{y};
		}
		$par{line_y} = - $par{line_y};
		if ($par{line_y_sequence} < 0){
			$par{line_y_sequence} = 10000 + $par{line_y_sequence};
		}
		elsif($par{line_y_sequence} > 100){
			$par{line_y_sequence} = -$par{line_y_sequence}
		}
	}
	my $line_edge_ymin = $par{line_y} - $par{cam_total_width}/2000;
	my $line_edge_ymax = $par{line_y} + $par{cam_total_width}/2000;
	my @lines;
	@{$par{tpads}} = sort{$a->{x} <=> $b->{x}} @{$par{tpads}};
	#GS==SG
	if (length($par{coupon_shape_tag}) == 2){
		#在中间
		if (($par{line_y_sequence} > 0 and $par{line_y_sequence} < 100) or $par{is_first_pad} and $par{impedance_type} =~ /single/){
			push @lines,{x=>$par{tpads}[0]{x} + abs($par{tpads}[0]{y} - $par{line_y}),y=>$par{line_y}};
		}
		elsif($par{line_y_sequence} < 0){
			#下端不需绕回中心
			if($line_edge_ymax <= $par{gpad_rerout_kb1}{b} + 0.0001){
				push @lines,{x=>$par{tpads}[0]{x} + abs($par{tpads}[0]{y} - $par{line_y}),y=>$par{line_y}};
				$par{gpad_rerout_kb1}{b} = $par{line_y} + $par{cam_total_width}/2000 + $par{rerout_line_spacing}/1000;
			}
			#下端需绕回中心
			else{
				$par{gpad_rerout_kb1} = TL::GenMath->get_kbline_parallel($par{gpad_rerout_kb1},-$par{cam_total_width}/2000);
				$par{gpad_rerout_kb2} = TL::GenMath->get_kbline_parallel($par{gpad_rerout_kb2},$par{cam_total_width}/2000);
				$par{gpad_rerout_kb3} = TL::GenMath->get_kbline_parallel($par{gpad_rerout_kb3},$par{cam_total_width}/2000);
				$par{gpad_rerout_kb4} = TL::GenMath->get_kbline_parallel($par{gpad_rerout_kb4},$par{cam_total_width}/2000);
				my ($xs,$ys,$xe,$ye);
				($xs,$ys) = ($par{tpads}[0]{x},$par{tpads}[0]{y});
				$ye = $par{gpad_rerout_kb1}{b};
				$xe = $xs + abs($ye - $ys);
				push @lines,{x=>$xe,y=>$ye};
				($xs,$ys) = ($xe,$ye);
				my $p1 = TL::GenMath->get_kbline_intersect($par{gpad_rerout_kb1},$par{gpad_rerout_kb2});
				$xe = $p1->{x}; $ye = $p1->{y};
				push @lines,{x=>$xe,y=>$ye};
				($xs,$ys) = ($xe,$ye);
				my $p2 = TL::GenMath->get_kbline_intersect($par{gpad_rerout_kb2},$par{gpad_rerout_kb3});
				my $p3 = TL::GenMath->get_kbline_intersect($par{gpad_rerout_kb3},$par{gpad_rerout_kb4});
				if ($par{line_y} > $p3->{y}){
					$xe = $p2->{x}; $ye = $p2->{y};
					push @lines,{x=>$xe,y=>$ye};
					($xs,$ys) = ($xe,$ye);
					$xe = $p3->{x}; $ye = $p3->{y};
					push @lines,{x=>$xe,y=>$ye};
					my $p4 = TL::GenMath->get_kbline_intersect($par{gpad_rerout_kb4},{k=>0,b=>$par{line_y}});
					($xs,$ys) = ($xe,$ye);
					$xe = $p4->{x}; $ye = $p4->{y};
					push @lines,{x=>$xe,y=>$ye};
				}
				else{
					my $p4 = TL::GenMath->get_kbline_intersect($par{gpad_rerout_kb2},{k=>0,b=>$par{line_y}});
					($xs,$ys) = ($xe,$ye);
					$xe = $p4->{x}; $ye = $p4->{y};
					push @lines,{x=>$xe,y=>$ye};
				}
				$par{gpad_rerout_kb1} = TL::GenMath->get_kbline_parallel($par{gpad_rerout_kb1},-$par{cam_total_width}/2000-$par{rerout_line_spacing}/1000);
				$par{gpad_rerout_kb2} = TL::GenMath->get_kbline_parallel($par{gpad_rerout_kb2},$par{cam_total_width}/2000+$par{rerout_line_spacing}/1000);
				$par{gpad_rerout_kb3} = TL::GenMath->get_kbline_parallel($par{gpad_rerout_kb3},$par{cam_total_width}/2000+$par{rerout_line_spacing}/1000);
				$par{gpad_rerout_kb4} = TL::GenMath->get_kbline_parallel($par{gpad_rerout_kb4},$par{cam_total_width}/2000+$par{rerout_line_spacing}/1000);
				
			}
		}
		else{
			#上端不需绕回中心
			if($line_edge_ymin >= $par{tpad_rerout_kb1}{b} - 0.0001){
				push @lines,{x=>$par{tpads}[0]{x} + abs($par{tpads}[0]{y} - $par{line_y}),y=>$par{line_y}};
				$par{tpad_rerout_kb1}{b} = $par{line_y} + $par{cam_total_width}/2000 + $par{rerout_line_spacing}/1000;
			}
			#上端需绕回中心
			else{
				$par{tpad_rerout_kb1} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb1},$par{cam_total_width}/2000);
				$par{tpad_rerout_kb2} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb2},$par{cam_total_width}/2000);
				$par{tpad_rerout_kb3} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb3},$par{cam_total_width}/2000);
				$par{tpad_rerout_kb4} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb4},$par{cam_total_width}/2000);
				my ($xs,$ys,$xe,$ye);
				($xs,$ys) = ($par{tpads}[0]{x},$par{tpads}[0]{y});
				$ye = $par{tpad_rerout_kb1}{b};
				$xe = $xs + abs($ye - $ys);
				push @lines,{x=>$xe,y=>$ye};
				($xs,$ys) = ($xe,$ye);
				my $p1 = TL::GenMath->get_kbline_intersect($par{tpad_rerout_kb1},$par{tpad_rerout_kb2});
				$xe = $p1->{x}; $ye = $p1->{y};
				push @lines,{x=>$xe,y=>$ye};
				($xs,$ys) = ($xe,$ye);
				my $p2 = TL::GenMath->get_kbline_intersect($par{tpad_rerout_kb2},$par{tpad_rerout_kb3});
				my $p3 = TL::GenMath->get_kbline_intersect($par{tpad_rerout_kb3},$par{tpad_rerout_kb4});
				if ($par{line_y} < $p3->{y}){
					$xe = $p2->{x}; $ye = $p2->{y};
					push @lines,{x=>$xe,y=>$ye};
					($xs,$ys) = ($xe,$ye);
					$xe = $p3->{x}; $ye = $p3->{y};
					push @lines,{x=>$xe,y=>$ye};
					my $p4 = TL::GenMath->get_kbline_intersect($par{tpad_rerout_kb4},{k=>0,b=>$par{line_y}});
					($xs,$ys) = ($xe,$ye);
					$xe = $p4->{x}; $ye = $p4->{y};
					push @lines,{x=>$xe,y=>$ye};
				}
				else{
					my $p4 = TL::GenMath->get_kbline_intersect($par{tpad_rerout_kb2},{k=>0,b=>$par{line_y}});
					($xs,$ys) = ($xe,$ye);
					$xe = $p4->{x}; $ye = $p4->{y};
					push @lines,{x=>$xe,y=>$ye};
				}
			
				$par{tpad_rerout_kb1} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb1},$par{cam_total_width}/2000+$par{rerout_line_spacing}/1000);
				$par{tpad_rerout_kb2} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb2},$par{cam_total_width}/2000+$par{rerout_line_spacing}/1000);
				$par{tpad_rerout_kb3} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb3},$par{cam_total_width}/2000+$par{rerout_line_spacing}/1000);
				$par{tpad_rerout_kb4} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb4},$par{cam_total_width}/2000+$par{rerout_line_spacing}/1000);
				
			}
		}
	}
	#其它
	else{
		#SSGG在中间
		if (($par{line_y_sequence} > 0 and $par{line_y_sequence} < 100) or $par{is_first_pad} and $par{impedance_type} =~ /single/){
			push @lines,{x=>$par{tpads}[0]{x} + abs($par{tpads}[0]{y} - $par{line_y}),y=>$par{line_y}};
		}
		#SSGG下端
		elsif($par{line_y_sequence} < 0){
			#SSGG下端不需绕回中心
			if($line_edge_ymax <= $par{gpad_rerout_kb1}{b} + 0.0001){
				my ($xs,$ys,$xe,$ye);
				($xs,$ys) = ($par{tpads}[0]{x},$par{tpads}[0]{y});
				$xe = $xs + $par{line_shift_x};
				$ye = $ys - abs($xe - $xs);
				push @lines,{x=>$xe,y=>$ye};
				($xs,$ys) = ($xe,$ye);
				$xe = $xs;
				$ye = $par{line_y} + $par{chamfer_size_min}/1000;
				push @lines,{x=>$xe,y=>$ye};
				($xs,$ys) = ($xe,$ye);
				$xe = $xs + $par{chamfer_size_min}/1000;
				$ye = $par{line_y};
				push @lines,{x=>$xe,y=>$ye};
				$par{gpad_rerout_kb1}{b} = $par{line_y} - $par{cam_total_width}/2000 - $par{rerout_line_spacing}/1000;
			}
			#SSGG下端需绕回中心
			else{
				$par{gpad_rerout_kb1} = TL::GenMath->get_kbline_parallel($par{gpad_rerout_kb1},-$par{cam_total_width}/2000);
				$par{gpad_rerout_kb2} = TL::GenMath->get_kbline_parallel($par{gpad_rerout_kb2},$par{cam_total_width}/2000);
				$par{gpad_rerout_kb3} = TL::GenMath->get_kbline_parallel($par{gpad_rerout_kb3},$par{cam_total_width}/2000);
				$par{gpad_rerout_kb4} = TL::GenMath->get_kbline_parallel($par{gpad_rerout_kb4},$par{cam_total_width}/2000);
				my ($xs,$ys,$xe,$ye);
				($xs,$ys) = ($par{tpads}[0]{x},$par{tpads}[0]{y});
				$xe = $xs + $par{line_shift_x};
				$ye = $ys - abs($xe - $xs);
				push @lines,{x=>$xe,y=>$ye};
				($xs,$ys) = ($xe,$ye);
				$xe = $xs;
				$ye = $par{gpad_rerout_kb1}{b} + $par{chamfer_size_min}/1000;
				push @lines,{x=>$xe,y=>$ye};
				($xs,$ys) = ($xe,$ye);
				$xe = $xs + $par{chamfer_size_min}/1000;
				$ye = $par{gpad_rerout_kb1}{b};
				push @lines,{x=>$xe,y=>$ye};
				($xs,$ys) = ($xe,$ye);
				my $p1 = TL::GenMath->get_kbline_intersect($par{gpad_rerout_kb1},$par{gpad_rerout_kb2});
				$xe = $p1->{x}; $ye = $p1->{y};
				push @lines,{x=>$xe,y=>$ye};
				($xs,$ys) = ($xe,$ye);
				my $p2 = TL::GenMath->get_kbline_intersect($par{gpad_rerout_kb2},$par{gpad_rerout_kb3});
				my $p3 = TL::GenMath->get_kbline_intersect($par{gpad_rerout_kb3},$par{gpad_rerout_kb4});
				if ($par{line_y} > $p3->{y}){
					$xe = $p2->{x}; $ye = $p2->{y};
					push @lines,{x=>$xe,y=>$ye};
					($xs,$ys) = ($xe,$ye);
					$xe = $p3->{x}; $ye = $p3->{y};
					push @lines,{x=>$xe,y=>$ye};
					my $p4 = TL::GenMath->get_kbline_intersect($par{gpad_rerout_kb4},{k=>0,b=>$par{line_y}});
					($xs,$ys) = ($xe,$ye);
					$xe = $p4->{x}; $ye = $p4->{y};
					push @lines,{x=>$xe,y=>$ye};
				}
				else{
					my $p4 = TL::GenMath->get_kbline_intersect($par{gpad_rerout_kb2},{k=>0,b=>$par{line_y}});
					($xs,$ys) = ($xe,$ye);
					$xe = $p4->{x}; $ye = $p4->{y};
					push @lines,{x=>$xe,y=>$ye};
				}
				
				$par{gpad_rerout_kb1} = TL::GenMath->get_kbline_parallel($par{gpad_rerout_kb1},-$par{cam_total_width}/2000-$par{rerout_line_spacing}/1000);
				$par{gpad_rerout_kb2} = TL::GenMath->get_kbline_parallel($par{gpad_rerout_kb2},$par{cam_total_width}/2000+$par{rerout_line_spacing}/1000);
				$par{gpad_rerout_kb3} = TL::GenMath->get_kbline_parallel($par{gpad_rerout_kb3},$par{cam_total_width}/2000+$par{rerout_line_spacing}/1000);
				$par{gpad_rerout_kb4} = TL::GenMath->get_kbline_parallel($par{gpad_rerout_kb4},$par{cam_total_width}/2000+$par{rerout_line_spacing}/1000);
				
			}
		}
		else{
			#SSGG上端first pad差动只需绕一根线
			if ($par{is_first_pad} and $par{impedance_type} =~ /difference/){
				my $tmp_line_edge_ymin = $par{line_y} + $par{cam_line_spacing}/2000;
				if($tmp_line_edge_ymin >= $par{tpad_rerout_kb1}{b} - 0.0001){
					push @lines,{x=>$par{tpads}[0]{x} + abs($par{tpads}[0]{y} - $par{line_y}),y=>$par{line_y}};
					$par{tpad_rerout_kb1}{b} = $par{line_y} + $par{cam_line_width}/2000 + $par{rerout_line_spacing}/1000;
				}
				else{
					$par{tpad_rerout_kb1} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb1},$par{cam_line_width}/2000);
					$par{tpad_rerout_kb2} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb2},$par{cam_line_width}/2000);
					$par{tpad_rerout_kb3} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb3},$par{cam_line_width}/2000);
					$par{tpad_rerout_kb4} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb4},$par{cam_line_width}/2000);
					
					my ($xs,$ys,$xe,$ye);
					($xs,$ys) = ($par{tpads}[0]{x},$par{tpads}[0]{y});
					$ye = $par{tpad_rerout_kb1}{b};
					$xe = $xs + abs($ye - $ys);
					push @lines,{x=>$xe,y=>$ye};
					($xs,$ys) = ($xe,$ye);
					my $p1 = TL::GenMath->get_kbline_intersect($par{tpad_rerout_kb1},$par{tpad_rerout_kb2});
					$xe = $p1->{x}; $ye = $p1->{y};
					push @lines,{x=>$xe,y=>$ye};
					($xs,$ys) = ($xe,$ye);
					my $p2 = TL::GenMath->get_kbline_intersect($par{tpad_rerout_kb2},$par{tpad_rerout_kb3});
					my $p3 = TL::GenMath->get_kbline_intersect($par{tpad_rerout_kb3},$par{tpad_rerout_kb4});
					if ($par{line_y} < $p3->{y}){
						$xe = $p2->{x}; $ye = $p2->{y};
						push @lines,{x=>$xe,y=>$ye};
						($xs,$ys) = ($xe,$ye);
						$xe = $p3->{x}; $ye = $p3->{y};
						push @lines,{x=>$xe,y=>$ye};
						my $p4 = TL::GenMath->get_kbline_intersect($par{tpad_rerout_kb4},{k=>0,b=>$par{line_y}});
						($xs,$ys) = ($xe,$ye);
						$xe = $p4->{x}; $ye = $p4->{y};
						push @lines,{x=>$xe,y=>$ye};
					}
					else{
						my $p4 = TL::GenMath->get_kbline_intersect($par{tpad_rerout_kb2},{k=>0,b=>$par{line_y}});
						($xs,$ys) = ($xe,$ye);
						$xe = $p4->{x}; $ye = $p4->{y};
						push @lines,{x=>$xe,y=>$ye};
					}
				
					$par{tpad_rerout_kb1} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb1},$par{cam_line_width}/2000+$par{rerout_line_spacing}/1000);
					$par{tpad_rerout_kb2} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb2},$par{cam_line_width}/2000+$par{rerout_line_spacing}/1000);
					$par{tpad_rerout_kb3} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb3},$par{cam_line_width}/2000+$par{rerout_line_spacing}/1000);
					$par{tpad_rerout_kb4} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb4},$par{cam_line_width}/2000+$par{rerout_line_spacing}/1000);
				
				}
			}
			#上端不需绕回中心
			elsif($line_edge_ymin >= $par{tpad_rerout_kb1}{b} - 0.0001){
				push @lines,{x=>$par{tpads}[0]{x} + abs($par{tpads}[0]{y} - $par{line_y}),y=>$par{line_y}};
				$par{tpad_rerout_kb1}{b} = $par{line_y} + $par{cam_total_width}/2000 + $par{rerout_line_spacing}/1000;
			}
			#上端需绕回中心
			else{
				$par{tpad_rerout_kb1} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb1},$par{cam_total_width}/2000);
				$par{tpad_rerout_kb2} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb2},$par{cam_total_width}/2000);
				$par{tpad_rerout_kb3} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb3},$par{cam_total_width}/2000);
				$par{tpad_rerout_kb4} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb4},$par{cam_total_width}/2000);
				
				my ($xs,$ys,$xe,$ye);
				($xs,$ys) = ($par{tpads}[0]{x},$par{tpads}[0]{y});
				$ye = $par{tpad_rerout_kb1}{b};
				$xe = $xs + abs($ye - $ys);
				push @lines,{x=>$xe,y=>$ye};
				($xs,$ys) = ($xe,$ye);
				my $p1 = TL::GenMath->get_kbline_intersect($par{tpad_rerout_kb1},$par{tpad_rerout_kb2});
				$xe = $p1->{x}; $ye = $p1->{y};
				push @lines,{x=>$xe,y=>$ye};
				($xs,$ys) = ($xe,$ye);
				my $p2 = TL::GenMath->get_kbline_intersect($par{tpad_rerout_kb2},$par{tpad_rerout_kb3});
				my $p3 = TL::GenMath->get_kbline_intersect($par{tpad_rerout_kb3},$par{tpad_rerout_kb4});
				if ($par{line_y} < $p3->{y}){
					$xe = $p2->{x}; $ye = $p2->{y};
					push @lines,{x=>$xe,y=>$ye};
					($xs,$ys) = ($xe,$ye);
					$xe = $p3->{x}; $ye = $p3->{y};
					push @lines,{x=>$xe,y=>$ye};
					my $p4 = TL::GenMath->get_kbline_intersect($par{tpad_rerout_kb4},{k=>0,b=>$par{line_y}});
					($xs,$ys) = ($xe,$ye);
					$xe = $p4->{x}; $ye = $p4->{y};
					push @lines,{x=>$xe,y=>$ye};
				}
				else{
					my $p4 = TL::GenMath->get_kbline_intersect($par{tpad_rerout_kb2},{k=>0,b=>$par{line_y}});
					($xs,$ys) = ($xe,$ye);
					$xe = $p4->{x}; $ye = $p4->{y};
					push @lines,{x=>$xe,y=>$ye};
				}
			
				$par{tpad_rerout_kb1} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb1},$par{cam_total_width}/2000+$par{rerout_line_spacing}/1000);
				$par{tpad_rerout_kb2} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb2},$par{cam_total_width}/2000+$par{rerout_line_spacing}/1000);
				$par{tpad_rerout_kb3} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb3},$par{cam_total_width}/2000+$par{rerout_line_spacing}/1000);
				$par{tpad_rerout_kb4} = TL::GenMath->get_kbline_parallel($par{tpad_rerout_kb4},$par{cam_total_width}/2000+$par{rerout_line_spacing}/1000);
				
			}
		}
	}
	push @lines,{x=>5,y=>$par{line_y}};
	my ($lines1,$lines2);
	if ($par{impedance_type} =~ /difference/){
		my @tpads = @{$par{tpads}};
		
		if ($par{is_first_pad} and abs($tpads[0]{y} - $tpads[1]{y}) < 0.0001 and $par{line_y_sequence} > 100){
			@tpads = sort{$a->{x} <=> $b->{x}} @tpads;
			$lines1 = [@lines];
			my $tp11 = TL::GenMath->get_line_intersect(
				{xs=>$tpads[0]{x},ys=>$tpads[0]{y},xe=>$tpads[0]{x}+1,ye=>$tpads[0]{y}+1},
				{xs=>$lines1->[0]{x},ys=>$lines1->[0]{y},xe=>$lines1->[1]{x},ye=>$lines1->[1]{y}},1);
			$lines1->[0] = $tp11;
			unshift @$lines1,{x=>$tpads[0]{x},y=>$tpads[0]{y}};
			pop @$lines1;
			my $tp12 = TL::GenMath->get_line_intersect(
				{xs=>$lines1->[-2]{x},ys=>$lines1->[-2]{y},xe=>$lines1->[-1]{x},ye=>$lines1->[-1]{y}},
				{xs=>0,ys=>$par{line_y}+$par{diff_line_dist}/2,xe=>1,ye=>$par{line_y}+$par{diff_line_dist}/2},1);
			$lines1->[-1] = $tp12;
			push @$lines1,{x=>5,y=>$par{line_y}+$par{diff_line_dist}/2};
			
			$lines2 = [
				{x=>$tpads[1]{x},y=>$tpads[1]{y}},
				{x=>$tpads[1]{x}+abs($par{line_y}-$par{diff_line_dist}/2 - $tpads[1]{y}),y=>$par{line_y}-$par{diff_line_dist}/2},
				{x=>5,y=>$par{line_y}-$par{diff_line_dist}/2},
			];
		}
		else{
			my ($dist1,$dist2,$sy1,$sy2);
			if (abs($tpads[0]{y} - $tpads[1]{y}) < 0.0001){
				@tpads = sort{$a->{x} <=> $b->{x}} @tpads;
				if ($lines[0]{y} > $tpads[0]{y}){
					$dist1 = $par{diff_line_dist}/2; $dist2 = -$par{diff_line_dist}/2;
					$sy1 = $sy2 = 1;
				}
				else{
					if (scalar(@lines) > 2){
						$dist1 = -$par{diff_line_dist}/2; $dist2 = $par{diff_line_dist}/2;
						$sy1 = -1; $sy2 = 1;
					}
					else{
						$dist1 = -$par{diff_line_dist}/2; $dist2 = $par{diff_line_dist}/2;
						$sy1 = $sy2 = -1;
					}
				}
			}
			else{
				@tpads = sort{$b->{y} <=> $a->{y}} @tpads;
				$dist1 = $par{diff_line_dist}/2; $dist2 = -$par{diff_line_dist}/2;
				$sy1 = -1; $sy2 = 1;
			}
			$lines1 = TL::GenMath->polyline_offset(\@lines,$dist1);
			my $tp1 = TL::GenMath->get_line_intersect(
				{xs=>$tpads[0]{x},ys=>$tpads[0]{y},xe=>$tpads[0]{x}+1,ye=>$tpads[0]{y}+$sy1},
				{xs=>$lines1->[0]{x},ys=>$lines1->[0]{y},xe=>$lines1->[1]{x},ye=>$lines1->[1]{y}},1);
			$lines1->[0] = $tp1;
			unshift @$lines1,{x=>$tpads[0]{x},y=>$tpads[0]{y}};
			
			$lines2 = TL::GenMath->polyline_offset(\@lines,$dist2);
			my $tp2 = TL::GenMath->get_line_intersect(
				{xs=>$tpads[1]{x},ys=>$tpads[1]{y},xe=>$tpads[1]{x}+1,ye=>$tpads[0]{y}+$sy2},
				{xs=>$lines2->[0]{x},ys=>$lines2->[0]{y},xe=>$lines2->[1]{x},ye=>$lines2->[1]{y}},1);
			$lines2->[0] = $tp2;
			unshift @$lines2,{x=>$tpads[1]{x},y=>$tpads[1]{y}};
		}
		
	}
	else{
		my @tpads = @{$par{tpads}};
		my $sy;
		if ($lines[0]{y} > $tpads[0]{y}){
			$sy = 1;
		}
		else{
			$sy = -1;
		}
		$lines1 = [@lines];
		my $tp1 = TL::GenMath->get_line_intersect(
			{xs=>$tpads[0]{x},ys=>$tpads[0]{y},xe=>$tpads[0]{x}+1,ye=>$tpads[0]{y}+$sy},
			{xs=>$lines1->[0]{x},ys=>$lines1->[0]{y},xe=>$lines1->[1]{x},ye=>$lines1->[1]{y}},1);
		$lines1->[0] = $tp1;
		unshift @$lines1,{x=>$tpads[0]{x},y=>$tpads[0]{y}};
	}
	if ($lines1 and @$lines1){
		pop @$lines1;
		if (abs($lines1->[0]{x}-$lines1->[1]{x}) < 0.0001  and abs($lines1->[0]{y}-$lines1->[1]{y}) < 0.001){
			$lines1->[0]{x} -= 0.001; $lines1->[0]{y} -= 0.001;
		}
	}
	if ($lines2 and @$lines2){
		pop @$lines2;
		if (abs($lines2->[0]{x}-$lines2->[1]{x}) < 0.0001  and abs($lines2->[0]{y}-$lines2->[1]{y}) < 0.001){
			$lines2->[0]{x} -= 0.001; $lines2->[0]{y} -= 0.001;
		}
	}
	
	if ($par{mirror_y}){
		if ($lines1 and @$lines1){
			foreach my $it (@$lines1){
				$it->{y} = -$it->{y};
			}
		}
		if ($lines2 and @$lines2){
			foreach my $it (@$lines2){
				$it->{y} = -$it->{y};
			}
		}
	}
	return {
		%par,
		lines1 => $lines1,
		lines2 => $lines2,
		#lines1 => \@lines,
	}
	
}

sub calc_line_y{
	my $self = shift;
	my %par = @_;
	$par{coupon_data} = $self->{COUPON_DATA} unless defined $par{coupon_data};
	my $COUPON_DATA = $par{coupon_data} ;
	foreach my $group (keys %{$COUPON_DATA->{group}}){
		my $n = 0;
		my $tmp_total_width = 0;
		my $previous_line_width = 0;
		foreach my $layer (@{$COUPON_DATA->{group}{$group}{signal_layer}}){
			$tmp_total_width += $COUPON_DATA->{layers}{$layer}{cam_total_width};
			
			if ($previous_line_width){
				my $tmp_max_width = ($previous_line_width > $COUPON_DATA->{layers}{$layer}{cam_line_width}) ? $previous_line_width : $COUPON_DATA->{layers}{$layer}{cam_line_width};
				my $tmp_spacing = $tmp_max_width * $COUPON_DATA->{line2line_times};
				$tmp_spacing = $COUPON_DATA->{line2line_spacing} if $COUPON_DATA->{line2line_spacing} > $tmp_spacing;
				$tmp_total_width += $tmp_spacing;
			}
			$previous_line_width = $COUPON_DATA->{layers}{$layer}{cam_line_width};
		}
		#$tmp_total_width += $COUPON_DATA->{line2line_spacing} * (scalar(@{$COUPON_DATA->{group}{$group}{signal_layer}}) - 1);
		#$GUI->debug(-text=>"tmp_total_width = $tmp_total_width  $COUPON_DATA->{line_centered}");
		if ($COUPON_DATA->{line_centered} eq 'yes'){
			my $share = ($COUPON_DATA->{pad2gnd_spacing} - $tmp_total_width)/(scalar(@{$COUPON_DATA->{group}{$group}{signal_layer}}) + 1)/1000;
			$share = 0 if $share < 0;
			my $tmp_y = 0;
			
			my $previous_line_width = 0;
			foreach my $layer (sort{$COUPON_DATA->{layers}{$a}{line_y_sequence} <=> $COUPON_DATA->{layers}{$b}{line_y_sequence}}  @{$COUPON_DATA->{group}{$group}{signal_layer}})
			{
				my $tmp_l2l_spacing = 0;
				if ($previous_line_width){
					my $tmp_max_width = ($previous_line_width > $COUPON_DATA->{layers}{$layer}{cam_line_width}) ? $previous_line_width : $COUPON_DATA->{layers}{$layer}{cam_line_width};
					$tmp_l2l_spacing = $tmp_max_width * $COUPON_DATA->{line2line_times};
					$tmp_l2l_spacing = $COUPON_DATA->{line2line_spacing} if $COUPON_DATA->{line2line_spacing} > $tmp_l2l_spacing;
				}
				
				$tmp_y += $tmp_l2l_spacing/1000;
				$COUPON_DATA->{layers}{$layer}{line_y} = $tmp_y + $COUPON_DATA->{layers}{$layer}{cam_total_width}/2000;
				$tmp_y += $COUPON_DATA->{layers}{$layer}{cam_total_width}/1000;
				$previous_line_width = $COUPON_DATA->{layers}{$layer}{cam_line_width};
			}
			my $total_width = $tmp_y;
			foreach my $layer (sort{$COUPON_DATA->{layers}{$a}{line_y_sequence} <=> $COUPON_DATA->{layers}{$b}{line_y_sequence}}  @{$COUPON_DATA->{group}{$group}{signal_layer}})
			{
				$COUPON_DATA->{layers}{$layer}{line_y} -= $total_width/2;
			}
			
			my $line_shift_y = 0;

			if ($COUPON_DATA->{group}{$group}{center_layers} and @{$COUPON_DATA->{group}{$group}{center_layers}}){
				foreach my $layer (sort{$COUPON_DATA->{layers}{$a}{line_y_sequence} <=> $COUPON_DATA->{layers}{$b}{line_y_sequence}}  @{$COUPON_DATA->{group}{$group}{center_layers}})
				{
					
					if ($COUPON_DATA->{layers}{$layer}{is_left_first} or $COUPON_DATA->{layers}{$layer}{is_right_first})
					{
						if ($COUPON_DATA->{layers}{$layer}{impedance_type} =~ /difference/){
							my $tmp_up = $COUPON_DATA->{layers}{$layer}{line_y} - $COUPON_DATA->{layers}{$layer}{cam_total_width}/2000 + $COUPON_DATA->{layers}{$layer}{cam_line_width}/1000;
							my $tmp_down = $COUPON_DATA->{layers}{$layer}{line_y} + $COUPON_DATA->{layers}{$layer}{cam_total_width}/2000 - $COUPON_DATA->{layers}{$layer}{cam_line_width}/1000;
							if ($tmp_up > $COUPON_DATA->{pad2gnd_spacing}/2000){
								my $tmp_shift_y = $COUPON_DATA->{pad2gnd_spacing}/2000 - $tmp_up;
								$line_shift_y = $tmp_shift_y if $tmp_shift_y < $line_shift_y;
							}
							if ($tmp_down < -$COUPON_DATA->{pad2gnd_spacing}/2000){
								my $tmp_shift_y = -$COUPON_DATA->{pad2gnd_spacing}/2000 - $tmp_down;
								$line_shift_y = $tmp_shift_y if $tmp_shift_y > $line_shift_y;
							}
						}
					}
					else{
						my $tmp_up = $COUPON_DATA->{layers}{$layer}{line_y} + $COUPON_DATA->{layers}{$layer}{cam_total_width}/2000;
						my $tmp_down = $COUPON_DATA->{layers}{$layer}{line_y} - $COUPON_DATA->{layers}{$layer}{cam_total_width}/2000;					
						if ($tmp_up > $COUPON_DATA->{pad2gnd_spacing}/2000){
							my $tmp_shift_y = $COUPON_DATA->{pad2gnd_spacing}/2000 - $tmp_up;
							$line_shift_y = $tmp_shift_y if $tmp_shift_y < $line_shift_y;
						}
						if ($tmp_down < -$COUPON_DATA->{pad2gnd_spacing}/2000){
							my $tmp_shift_y = -$COUPON_DATA->{pad2gnd_spacing}/2000 - $tmp_down;
							$line_shift_y = $tmp_shift_y if $tmp_shift_y > $line_shift_y;
						}
						
					}
				}
			}
			foreach my $layer (sort{$COUPON_DATA->{layers}{$a}{line_y_sequence} <=> $COUPON_DATA->{layers}{$b}{line_y_sequence}}  @{$COUPON_DATA->{group}{$group}{signal_layer}})
			{
				$COUPON_DATA->{layers}{$layer}{line_y} += $line_shift_y;
			}
			
		}
		else{
			my $share = ($COUPON_DATA->{pad2gnd_spacing} - $tmp_total_width)/(scalar(@{$COUPON_DATA->{group}{$group}{signal_layer}}) + 1)/1000;
			$share = 0 if $share < 0;
			my $tmp_y = 0;
			my $previous_line_width = 0;
			foreach my $layer (sort{$COUPON_DATA->{layers}{$a}{line_y_sequence} <=> $COUPON_DATA->{layers}{$b}{line_y_sequence}}  @{$COUPON_DATA->{group}{$group}{signal_layer}})
			{
				#$GUI->debug(-text=>dump($COUPON_DATA->{layers}{$layer}));
				if (($COUPON_DATA->{layers}{$layer}{line_y_sequence} >= 0 and $COUPON_DATA->{layers}{$layer}{line_y_sequence} < 100) or
					(($COUPON_DATA->{layers}{$layer}{is_left_first} or $COUPON_DATA->{layers}{$layer}{is_right_first}) and $COUPON_DATA->{layers}{$layer}{impedance_type} =~ /single/))
				{
					
					my $tmp_l2l_spacing = 0;
					if ($previous_line_width){
						my $tmp_max_width = ($previous_line_width > $COUPON_DATA->{layers}{$layer}{cam_line_width}) ? $previous_line_width : $COUPON_DATA->{layers}{$layer}{cam_line_width};
						$tmp_l2l_spacing = $tmp_max_width * $COUPON_DATA->{line2line_times};
						$tmp_l2l_spacing = $COUPON_DATA->{line2line_spacing} if $COUPON_DATA->{line2line_spacing} > $tmp_l2l_spacing;
					}
					
					$tmp_y += $tmp_l2l_spacing/1000;
					$COUPON_DATA->{layers}{$layer}{line_y} = $tmp_y + $COUPON_DATA->{layers}{$layer}{cam_total_width}/2000;
					$tmp_y += $COUPON_DATA->{layers}{$layer}{cam_total_width}/1000;
					$previous_line_width = $COUPON_DATA->{layers}{$layer}{cam_line_width};
					
				}
			}
			my $total_width = $tmp_y;
			foreach my $layer (sort{$COUPON_DATA->{layers}{$a}{line_y_sequence} <=> $COUPON_DATA->{layers}{$b}{line_y_sequence}}  @{$COUPON_DATA->{group}{$group}{signal_layer}})
			{
				if (($COUPON_DATA->{layers}{$layer}{line_y_sequence} >= 0 and $COUPON_DATA->{layers}{$layer}{line_y_sequence} < 100) or
					(($COUPON_DATA->{layers}{$layer}{is_left_first} or $COUPON_DATA->{layers}{$layer}{is_right_first}) and $COUPON_DATA->{layers}{$layer}{impedance_type} =~ /single/))
				{
					$COUPON_DATA->{layers}{$layer}{line_y} -= $total_width/2;
				}
			}
			my $line_shift_y = 0;
			if ($COUPON_DATA->{group}{$group}{center_layers}){

				my $isOnlyFirst = 0;
				if (scalar(@{$COUPON_DATA->{group}{$group}{center_layers}}) == 1) {
					my $layer = $COUPON_DATA->{group}{$group}{center_layers}[0];
					if ($COUPON_DATA->{layers}{$layer}{is_left_first} || $COUPON_DATA->{layers}{$layer}{is_right_first}){
						$isOnlyFirst = 1;
					}
				}

				if ($isOnlyFirst && ($COUPON_DATA->{coupon_shape_type} eq 'SSGG==GGSS' or $COUPON_DATA->{coupon_shape_type} eq 'SSGG--')) {
					my $layer = $COUPON_DATA->{group}{$group}{center_layers}[0];
					if ($COUPON_DATA->{layers}{$layer}{is_left_first}) {
						my $tmp_down = $COUPON_DATA->{layers}{$layer}{line_y} - $COUPON_DATA->{layers}{$layer}{cam_total_width}/2000;
						if ($tmp_down < -$COUPON_DATA->{pad2gnd_spacing}/2000) {
							$line_shift_y = -$COUPON_DATA->{pad2gnd_spacing}/2000 - $tmp_down;
						}
					}
					if ($COUPON_DATA->{layers}{$layer}{is_right_first}) {
						my $tmp_up = $COUPON_DATA->{layers}{$layer}{line_y} + $COUPON_DATA->{layers}{$layer}{cam_total_width}/2000;
						if ($tmp_up > $COUPON_DATA->{pad2gnd_spacing}/2000){
							$line_shift_y = $COUPON_DATA->{pad2gnd_spacing}/2000 - $tmp_up;
						}
					}
				}
				else {
					foreach my $layer (sort{$COUPON_DATA->{layers}{$a}{line_y_sequence} <=> $COUPON_DATA->{layers}{$b}{line_y_sequence}}  @{$COUPON_DATA->{group}{$group}{center_layers}})
					{
						
						if ($COUPON_DATA->{layers}{$layer}{impedance_type} =~ /difference/){
							my $tmp_up = $COUPON_DATA->{layers}{$layer}{line_y} - $COUPON_DATA->{layers}{$layer}{cam_total_width}/2000 + $COUPON_DATA->{layers}{$layer}{cam_line_width}/1000;
							my $tmp_down = $COUPON_DATA->{layers}{$layer}{line_y} + $COUPON_DATA->{layers}{$layer}{cam_total_width}/2000 - $COUPON_DATA->{layers}{$layer}{cam_line_width}/1000;
							if ($tmp_up > $COUPON_DATA->{pad2gnd_spacing}/2000){
								my $tmp_shift_y = $COUPON_DATA->{pad2gnd_spacing}/2000 - $tmp_up;
								$line_shift_y = $tmp_shift_y if $tmp_shift_y < $line_shift_y;
							}
							if ($tmp_down < -$COUPON_DATA->{pad2gnd_spacing}/2000){
								my $tmp_shift_y = -$COUPON_DATA->{pad2gnd_spacing}/2000 - $tmp_down;
								$line_shift_y = $tmp_shift_y if $tmp_shift_y > $line_shift_y;
							}
						}
						else{
							my $tmp_up = $COUPON_DATA->{layers}{$layer}{line_y} + $COUPON_DATA->{layers}{$layer}{cam_total_width}/2000;
							my $tmp_down = $COUPON_DATA->{layers}{$layer}{line_y} - $COUPON_DATA->{layers}{$layer}{cam_total_width}/2000;
							if ($tmp_up > $COUPON_DATA->{pad2gnd_spacing}/2000){
								my $tmp_shift_y = $COUPON_DATA->{pad2gnd_spacing}/2000 - $tmp_up;
								$line_shift_y = $tmp_shift_y if $tmp_shift_y < $line_shift_y;
							}
							if ($tmp_down < -$COUPON_DATA->{pad2gnd_spacing}/2000){
								my $tmp_shift_y = -$COUPON_DATA->{pad2gnd_spacing}/2000 - $tmp_down;
								$line_shift_y = $tmp_shift_y if $tmp_shift_y > $line_shift_y;
							}
							
						}
					}
				}
			}

			foreach my $layer (sort{$COUPON_DATA->{layers}{$a}{line_y_sequence} <=> $COUPON_DATA->{layers}{$b}{line_y_sequence}}  @{$COUPON_DATA->{group}{$group}{signal_layer}})
			{
				if (($COUPON_DATA->{layers}{$layer}{line_y_sequence} >= 0 and $COUPON_DATA->{layers}{$layer}{line_y_sequence} < 100) or
					(($COUPON_DATA->{layers}{$layer}{is_left_first} or $COUPON_DATA->{layers}{$layer}{is_right_first}) and $COUPON_DATA->{layers}{$layer}{impedance_type} =~ /single/))
				{
					$COUPON_DATA->{layers}{$layer}{line_y} += $line_shift_y;
				}
			}

			my $tmp_y_up = $COUPON_DATA->{pad2gnd_spacing}/2000 + $COUPON_DATA->{pad_size}/1000 + 2*$COUPON_DATA->{line2pad_spacing}/1000;
			foreach my $layer (sort{$COUPON_DATA->{layers}{$a}{line_y_sequence} <=> $COUPON_DATA->{layers}{$b}{line_y_sequence}}  @{$COUPON_DATA->{group}{$group}{signal_layer}})
			{
				if ($COUPON_DATA->{layers}{$layer}{line_y_sequence} > 100){
					if (($COUPON_DATA->{layers}{$layer}{is_left_first} or $COUPON_DATA->{layers}{$layer}{is_right_first}) and $COUPON_DATA->{layers}{$layer}{impedance_type} =~ /difference/){
						$tmp_y_up = $tmp_y_up - $COUPON_DATA->{layers}{$layer}{cam_line_width}/1000 - $COUPON_DATA->{layers}{$layer}{cam_line_spacing}/1000;
						last;
					}
					else{
						last;
					}
				}
			}
			
			my $tmp_y_down = -$COUPON_DATA->{pad2gnd_spacing}/2000 - $COUPON_DATA->{pad_size}/1000 - 2*$COUPON_DATA->{line2pad_spacing}/1000;
			foreach my $layer (sort{$COUPON_DATA->{layers}{$b}{line_y_sequence} <=> $COUPON_DATA->{layers}{$a}{line_y_sequence}}  @{$COUPON_DATA->{group}{$group}{signal_layer}})
			{
				if ($COUPON_DATA->{layers}{$layer}{line_y_sequence} < 0){
					if (($COUPON_DATA->{layers}{$layer}{is_left_first} or $COUPON_DATA->{layers}{$layer}{is_right_first}) and $COUPON_DATA->{layers}{$layer}{impedance_type} =~ /difference/){
						$tmp_y_down = $tmp_y_down + $COUPON_DATA->{layers}{$layer}{cam_line_width}/1000 + $COUPON_DATA->{layers}{$layer}{cam_line_spacing}/1000;
						last;
					}
					else{
						last;
					}
				}
			}
			
			foreach my $layer (sort{$COUPON_DATA->{layers}{$a}{line_y_sequence} <=> $COUPON_DATA->{layers}{$b}{line_y_sequence}}  @{$COUPON_DATA->{group}{$group}{signal_layer}})
			{
				if ($COUPON_DATA->{layers}{$layer}{line_y_sequence} > 100 and
					!(($COUPON_DATA->{layers}{$layer}{is_left_first} or $COUPON_DATA->{layers}{$layer}{is_right_first}) and $COUPON_DATA->{layers}{$layer}{impedance_type} =~ /single/)){
					$COUPON_DATA->{layers}{$layer}{line_y} = $tmp_y_up + $COUPON_DATA->{layers}{$layer}{cam_total_width}/2000;
					$tmp_y_up += $COUPON_DATA->{layers}{$layer}{cam_total_width}/1000 + $COUPON_DATA->{line2line_spacing}/1000;
				}
			}
			
			foreach my $layer (sort{$COUPON_DATA->{layers}{$b}{line_y_sequence} <=> $COUPON_DATA->{layers}{$a}{line_y_sequence}}  @{$COUPON_DATA->{group}{$group}{signal_layer}})
			{
				if ($COUPON_DATA->{layers}{$layer}{line_y_sequence} < 0 and
					!(($COUPON_DATA->{layers}{$layer}{is_left_first} or $COUPON_DATA->{layers}{$layer}{is_right_first}) and $COUPON_DATA->{layers}{$layer}{impedance_type} =~ /single/)){
					$COUPON_DATA->{layers}{$layer}{line_y} = $tmp_y_down - $COUPON_DATA->{layers}{$layer}{cam_total_width}/2000;
					$tmp_y_down -= $COUPON_DATA->{layers}{$layer}{cam_total_width}/1000 + $COUPON_DATA->{line2line_spacing}/1000;
				}
			}
			
		}
	
	}
	
}



sub init_line_y_sequence{
	my $self = shift;
	my %par = @_;
	$par{coupon_data} = $self->{COUPON_DATA} unless defined $par{coupon_data};
	my $COUPON_DATA = $par{coupon_data} ;
	if ($COUPON_DATA->{coupon_left_shape} eq 'SSGG' or $COUPON_DATA->{coupon_left_shape} eq 'GGSS'){
		$COUPON_DATA->{pad2gnd_spacing} =  $COUPON_DATA->{pad2gnd_dist}*1000 - $COUPON_DATA->{pad_size} - 2*$COUPON_DATA->{line2pad_spacing};
	}
	elsif ($COUPON_DATA->{coupon_left_shape} eq 'GSGS' or $COUPON_DATA->{coupon_left_shape} eq 'SGSG'){
		$COUPON_DATA->{pad2gnd_spacing} =  $COUPON_DATA->{pad1pad2_dist}*1000 - $COUPON_DATA->{pad_size} - 2*$COUPON_DATA->{line2pad_spacing};
	}
	elsif($COUPON_DATA->{coupon_left_shape} eq 'GS' or $COUPON_DATA->{coupon_left_shape} eq 'SG'){
		$COUPON_DATA->{pad2gnd_spacing} = 0;
	}
	
	foreach my $item (values %{$COUPON_DATA->{layers}}){
		next unless ($item->{layer_type} eq 'S');
		
		if($item->{impedance_type} =~ /difference/){
			if ($item->{is_left_first} or $item->{is_right_first}){
				$item->{occupy_width} = $item->{cam_line_width};
			}
			else{
				$item->{occupy_width} = $item->{cam_total_width};
			}
		}
		else{
			if ($item->{is_left_first} or $item->{is_right_first}){
				$item->{occupy_width} = 0;
			}
			else{
				$item->{occupy_width} = $item->{cam_total_width};
			}
		}
	}
	if ($COUPON_DATA->{coupon_shape_type} eq 'SSGG==GGSS'){
		$self->_calc_center_lines_any(coupon_data=>$COUPON_DATA);
		foreach my $group (keys %{$COUPON_DATA->{group}}){
			my $n = 1;
			foreach my $layer (sort{$COUPON_DATA->{layers}{$a}{pad_position_order} <=> $COUPON_DATA->{layers}{$b}{pad_position_order}}  @{$COUPON_DATA->{group}{$group}{signal_layer}})
			{
				$n++;
				if ($COUPON_DATA->{layers}{$layer}{left_right_group} eq 'left'){
					$COUPON_DATA->{layers}{$layer}{line_y_sequence} = 1000 - $n;
				}
				elsif($COUPON_DATA->{layers}{$layer}{left_right_group} eq 'right'){
					$COUPON_DATA->{layers}{$layer}{line_y_sequence} = -1000 - $n;
				}
				
			}
			$n = 1;
			foreach my $layer (sort{$COUPON_DATA->{layers}{$a}{pad_position_order} <=> $COUPON_DATA->{layers}{$b}{pad_position_order}}  @{$COUPON_DATA->{group}{$group}{center_layers}})
			{
				$COUPON_DATA->{layers}{$layer}{line_y_sequence} = $n++;
			}
		}
	}
	elsif($COUPON_DATA->{coupon_shape_type} eq 'SSGG==SSGG'){
		if ($COUPON_DATA->{rerout_direction} eq 'tpad'){
			$self->_calc_center_lines_any(coupon_data=>$COUPON_DATA);
			foreach my $group (keys %{$COUPON_DATA->{group}}){
				my $n = 1;
				foreach my $layer (sort{$COUPON_DATA->{layers}{$a}{pad_position_order} <=> $COUPON_DATA->{layers}{$b}{pad_position_order}}  @{$COUPON_DATA->{group}{$group}{signal_layer}})
				{
					$n++;
					if ($COUPON_DATA->{layers}{$layer}{left_right_group} eq 'left'){
						$COUPON_DATA->{layers}{$layer}{line_y_sequence} = 1000 - $n;
					}
					elsif($COUPON_DATA->{layers}{$layer}{left_right_group} eq 'right'){
						$COUPON_DATA->{layers}{$layer}{line_y_sequence} = 1000 + $n;
					}
				}
				$n = 1;
				foreach my $layer (sort{$COUPON_DATA->{layers}{$a}{pad_position_order} <=> $COUPON_DATA->{layers}{$b}{pad_position_order}}  @{$COUPON_DATA->{group}{$group}{center_layers}})
				{
					$COUPON_DATA->{layers}{$layer}{line_y_sequence} = $n++;
				}
			}
		}
	}
	elsif($COUPON_DATA->{coupon_shape_type} eq 'SSGG--' or
		  $COUPON_DATA->{coupon_shape_type} eq 'SSGG--SSGG'
	){
		if ($COUPON_DATA->{rerout_direction} eq 'both'){
			$self->_calc_center_lines_cont(coupon_data=>$COUPON_DATA,start=>'center');
			foreach my $group (keys %{$COUPON_DATA->{group}}){
				my @tmp_center_pads;
				foreach my $layer (sort{$COUPON_DATA->{layers}{$a}{pad_position_order} <=> $COUPON_DATA->{layers}{$b}{pad_position_order}}  @{$COUPON_DATA->{group}{$group}{center_layers}})
				{
					push @tmp_center_pads,$COUPON_DATA->{layers}{$layer}{pad_position_order};
				}
				if (@tmp_center_pads){
					@tmp_center_pads = sort{$a <=> $b} @tmp_center_pads;
					my $center_pad_min = $tmp_center_pads[0];
					my $center_pad_max = $tmp_center_pads[-1];
					my $n = 1;
					foreach my $layer (sort{$COUPON_DATA->{layers}{$a}{pad_position_order} <=> $COUPON_DATA->{layers}{$b}{pad_position_order}}  @{$COUPON_DATA->{group}{$group}{signal_layer}})
					{
						$n++;
						if ($COUPON_DATA->{layers}{$layer}{pad_position_order} > $center_pad_max){
							$COUPON_DATA->{layers}{$layer}{line_y_sequence} = 1000 - $n;
						}
						elsif($COUPON_DATA->{layers}{$layer}{pad_position_order} < $center_pad_min){
							$COUPON_DATA->{layers}{$layer}{line_y_sequence} = -1000 + $n;
						}
						else{
							$COUPON_DATA->{layers}{$layer}{line_y_sequence} = $n;
						}
					}
				}
				else{
					my $n = 1;
					my @tmp = sort{$COUPON_DATA->{layers}{$a}{pad_position_order} <=> $COUPON_DATA->{layers}{$b}{pad_position_order}}  @{$COUPON_DATA->{group}{$group}{signal_layer}};
					my $c = int($#tmp/2);
					my $pc = $COUPON_DATA->{layers}{$tmp[$c]}{pad_position_order};
					foreach my $layer (sort{$COUPON_DATA->{layers}{$a}{pad_position_order} <=> $COUPON_DATA->{layers}{$b}{pad_position_order}}  @{$COUPON_DATA->{group}{$group}{signal_layer}})
					{
						$n++;
						if ($COUPON_DATA->{layers}{$layer}{pad_position_order} >= $pc){
							$COUPON_DATA->{layers}{$layer}{line_y_sequence} = 1000 - $n;
						}
						else{
							$COUPON_DATA->{layers}{$layer}{line_y_sequence} = -1000 + $n;
						}
					}
				}
			}
		}
		elsif($COUPON_DATA->{rerout_direction} eq 'tpad'){
			$self->_calc_center_lines_any(coupon_data=>$COUPON_DATA);
			foreach my $group (keys %{$COUPON_DATA->{group}}){
				my $n = 1;
				foreach my $layer (sort{$COUPON_DATA->{layers}{$a}{pad_position_order} <=> $COUPON_DATA->{layers}{$b}{pad_position_order}}  @{$COUPON_DATA->{group}{$group}{signal_layer}})
				{
					$n++;
					if ($COUPON_DATA->{layers}{$layer}{left_right_group} eq 'left'){
						$COUPON_DATA->{layers}{$layer}{line_y_sequence} = 1000 - $n;
					}
					elsif($COUPON_DATA->{layers}{$layer}{left_right_group} eq 'right'){
						$COUPON_DATA->{layers}{$layer}{line_y_sequence} = 1000 + $n;
					}
				}
				$n = 1;
				foreach my $layer (sort{$COUPON_DATA->{layers}{$a}{pad_position_order} <=> $COUPON_DATA->{layers}{$b}{pad_position_order}}  @{$COUPON_DATA->{group}{$group}{center_layers}})
				{
					$COUPON_DATA->{layers}{$layer}{line_y_sequence} = $n++;
				}
			}
		}
	}
	elsif($COUPON_DATA->{coupon_shape_type} eq 'GS--' or
		  $COUPON_DATA->{coupon_shape_type} eq 'GS--SG' or
		  $COUPON_DATA->{coupon_shape_type} eq 'GS==SG'
	){
		foreach my $group (keys %{$COUPON_DATA->{group}}){
			my $n = 1;
			my ($tmp_top_width,$tmp_bot_width) = (0,0);
			foreach my $layer (sort{$COUPON_DATA->{layers}{$a}{pad_position_order} <=> $COUPON_DATA->{layers}{$b}{pad_position_order}}  @{$COUPON_DATA->{group}{$group}{signal_layer}})
			{
				$n++;
				if ($COUPON_DATA->{layers}{$layer}{is_left_first} or $COUPON_DATA->{layers}{$layer}{is_right_first}){
					$COUPON_DATA->{layers}{$layer}{line_y_sequence} = $n;
				}
				elsif ($tmp_bot_width > $tmp_top_width){
					if ($COUPON_DATA->{layers}{$layer}{left_right_group} eq 'left'){
						$COUPON_DATA->{layers}{$layer}{line_y_sequence} = 1000 - $n;
					}
					else{
						$COUPON_DATA->{layers}{$layer}{line_y_sequence} = 1000 + $n;
					}
					$tmp_top_width += $COUPON_DATA->{layers}{$layer}{cam_total_width} + $COUPON_DATA->{line2line_spacing};
				}
				else{
					if ($COUPON_DATA->{layers}{$layer}{left_right_group} eq 'left'){
						$COUPON_DATA->{layers}{$layer}{line_y_sequence} = -1000 - $n;
					}
					else{
						$COUPON_DATA->{layers}{$layer}{line_y_sequence} = -2000 - $n;
					}
					$tmp_bot_width += $COUPON_DATA->{layers}{$layer}{cam_total_width} + $COUPON_DATA->{line2line_spacing};
				}
			}
		}
	}
	
}

#计算哪些线是放在中间位置的(中间位置要连续)
sub _calc_center_lines_cont{
	my $self = shift;
	my %par = @_;
	$par{coupon_data} = $self->{COUPON_DATA} unless defined $par{coupon_data};
	$par{start} = 'center' unless $par{start};
	my $COUPON_DATA = $par{coupon_data} ;
	foreach my $group (keys %{$COUPON_DATA->{group}}){
		my (@layers,@center_layers);
		if ($par{start} eq 'any'){
			
		}
		else{
			if ($par{start} eq 'center'){
				my @tmp = sort{$COUPON_DATA->{layers}{$b}{pad_position_order} <=> $COUPON_DATA->{layers}{$a}{pad_position_order}}  @{$COUPON_DATA->{group}{$group}{signal_layer}};
				#$GUI->msgbox(dump(\@tmp));
				while(@tmp){
					my $n = int(scalar(@tmp)/2);
					push @layers,$tmp[$n];
					splice(@tmp,$n,1);
				}
			}
			elsif($par{start} eq 'left'){
				@layers = sort{$COUPON_DATA->{layers}{$a}{pad_position_order} <=> $COUPON_DATA->{layers}{$b}{pad_position_order}}  @{$COUPON_DATA->{group}{$group}{signal_layer}};
			}
			elsif($par{start} eq 'right'){
				@layers = sort{$COUPON_DATA->{layers}{$b}{pad_position_order} <=> $COUPON_DATA->{layers}{$a}{pad_position_order}}  @{$COUPON_DATA->{group}{$group}{signal_layer}};
			}
			#$GUI->msgbox(dump(\@layers));
			my $tmp_width = 0;
			my $previous_line_width = 0;
			my $tmp_total_spacing = 0;
			foreach my $layer (@layers){
				if ($previous_line_width){
					my $tmp_max_width = ($previous_line_width > $COUPON_DATA->{layers}{$layer}{cam_line_width}) ? $previous_line_width : $COUPON_DATA->{layers}{$layer}{cam_line_width};
					my $tmp_spacing = $tmp_max_width * $COUPON_DATA->{line2line_times};
					$tmp_spacing = $COUPON_DATA->{line2line_spacing} if $COUPON_DATA->{line2line_spacing} > $tmp_spacing;
					$tmp_total_spacing += $tmp_spacing;
				}
				$previous_line_width = $COUPON_DATA->{layers}{$layer}{cam_line_width};
				
				if (($tmp_width + $COUPON_DATA->{layers}{$layer}{occupy_width} ) <= $COUPON_DATA->{pad2gnd_spacing} - $tmp_total_spacing){
					$tmp_width += $COUPON_DATA->{layers}{$layer}{occupy_width} ;
					push @center_layers,$layer;
				}
				else{
					last;
				}
				
			}
		}
		$COUPON_DATA->{group}{$group}{center_layers} = \@center_layers;
		#$GUI->msgbox(dump(\@center_layers));
	}
}

#计算哪些线是放在中间位置的(任意一条线组合)
sub _calc_center_lines_any{
	my $self = shift;
	my %par = @_;
	$par{coupon_data} = $self->{COUPON_DATA} unless defined $par{coupon_data};
	my $COUPON_DATA = $par{coupon_data} ;
	foreach my $group (keys %{$COUPON_DATA->{group}}){
		my @layers = @{$COUPON_DATA->{group}{$group}{signal_layer}};
		#求排列组合，找出在tpad与gpad之间能放下最多的线条
		my @list = perm([@layers],0,(scalar(@layers) > 6)?6:$#layers);
		my @center_layers;
		my $center_width = 0;
		my $center_left_right_diff = 99999;
		my $center_line_count = 0;
		foreach my $item (@list){
			my @tmp_layers;
			my ($tmp_width,$tmp_line_count) = (0,0);
			my %tmp_diff;
			my $previous_line_width = 0;
			my $tmp_total_spacing = 0;
			foreach my $layer (@$item){
				#print "layer = $layer , occupy = $COUPON_DATA->{layers}{$layer}{occupy_width} \n";
				if ($previous_line_width){
					my $tmp_max_width = ($previous_line_width > $COUPON_DATA->{layers}{$layer}{cam_line_width}) ? $previous_line_width : $COUPON_DATA->{layers}{$layer}{cam_line_width};
					my $tmp_spacing = $tmp_max_width * $COUPON_DATA->{line2line_times};
					$tmp_spacing = $COUPON_DATA->{line2line_spacing} if $COUPON_DATA->{line2line_spacing} > $tmp_spacing;
					$tmp_total_spacing += $tmp_spacing;
				}
				$previous_line_width = $COUPON_DATA->{layers}{$layer}{cam_line_width};
				
				
				if (($tmp_width + $COUPON_DATA->{layers}{$layer}{occupy_width} ) <= $COUPON_DATA->{pad2gnd_spacing} - $tmp_total_spacing){
					$tmp_width += $COUPON_DATA->{layers}{$layer}{occupy_width} ;
					if ($COUPON_DATA->{layers}{$layer}{impedance_type}=~ /difference/){
						$tmp_line_count += 2;
						$tmp_diff{$COUPON_DATA->{layers}{$layer}{left_right_group}} += 2;
					}
					else{
						$tmp_line_count += 1;
						$tmp_diff{$COUPON_DATA->{layers}{$layer}{left_right_group}} += 1;
					}
					push @tmp_layers,$layer;
				}
				else{
					last;
				}
				
			}
			
			my $tmp_diff = abs(($tmp_diff{left}||0) - ($tmp_diff{right}||0));
			
			if ( $tmp_line_count > $center_line_count or
				( $tmp_line_count == $center_line_count and $tmp_diff < $center_left_right_diff ) or
				($tmp_line_count == $center_line_count and $tmp_diff == $center_left_right_diff and $tmp_width > $center_width ))
			{
				$center_width = $tmp_width;
				@center_layers = @tmp_layers;
				$center_left_right_diff = $tmp_diff;
				$center_line_count = $tmp_line_count;
			}
		}
		$COUPON_DATA->{group}{$group}{center_layers} = \@center_layers;
	}
}


#计算coupon的长度是否足够；
sub adjust_coupon_length{
	my $self = shift;
	my %par = @_;
	$par{coupon_data} = $self->{COUPON_DATA} unless defined $par{coupon_data};
	my $COUPON_DATA = $par{coupon_data} ;
	
	#获取左右pad的长度
	my (@left_pads,@right_pads);
	foreach my $item (values %{$COUPON_DATA->{layers}}){
		next unless ($item->{layer_type} eq 'S');
		push @left_pads,@{$item->{left_tpads}} if $item->{left_tpads};
		push @left_pads,@{$item->{left_gpads}} if $item->{left_gpads};
		push @right_pads,@{$item->{right_tpads}} if $item->{right_tpads};
		push @right_pads,@{$item->{right_gpads}} if $item->{right_gpads};
	}
	@left_pads = sort {$a->{x} <=> $b->{x}} @left_pads;
	@right_pads = sort {$a->{x} <=> $b->{x}} @right_pads;
	my ($left_size,$right_size) = (0,0);
	if (@left_pads){
		$left_size = $left_pads[-1]->{x} - $left_pads[0]->{x} + $COUPON_DATA->{pad_size}/1000;
	}
	if (@right_pads){
		$right_size = $right_pads[-1]->{x} - $right_pads[0]->{x} + $COUPON_DATA->{pad_size}/1000;
	}
	my $min_test_line_length;
	if ($COUPON_DATA->{coupon_length}){
		$min_test_line_length = $COUPON_DATA->{coupon_length} - $left_size - $COUPON_DATA->{margin_pad_x}/1000 ;
		if ($right_size){
			$min_test_line_length = $min_test_line_length - $right_size - $COUPON_DATA->{margin_pad_x}/1000 - $COUPON_DATA->{line_end2pad_x}/1000;
		}
		else{
			$min_test_line_length = $min_test_line_length - $COUPON_DATA->{margin_line_x}/1000;
		}
	}
	else{
		$min_test_line_length = $COUPON_DATA->{min_test_line_length};
	}	
	
	if ((! $COUPON_DATA->{coupon_length}) or ($COUPON_DATA->{coupon_length} and $COUPON_DATA->{auto_enlarge_coupon_length} eq 'yes' and $min_test_line_length < $COUPON_DATA->{min_test_line_length})){
		$COUPON_DATA->{coupon_length} = $COUPON_DATA->{min_test_line_length} + $left_size + $COUPON_DATA->{margin_pad_x}/1000 ;
		if ($right_size){
			$COUPON_DATA->{coupon_length} = $COUPON_DATA->{coupon_length} + $right_size + $COUPON_DATA->{margin_pad_x}/1000 + $COUPON_DATA->{line_end2pad_x}/1000;
		}
		else{
			$COUPON_DATA->{coupon_length} = $COUPON_DATA->{coupon_length} + $COUPON_DATA->{margin_line_x}/1000;
		}
	}
	else{
		
	}
	
	my ($line_start_x,$line_end_x);
	$line_start_x =  $left_size + $COUPON_DATA->{margin_pad_x}/1000 + $COUPON_DATA->{line_end2pad_x}/1000 ;
	if ($right_size){
		$line_end_x = $COUPON_DATA->{coupon_length} - ($right_size + $COUPON_DATA->{margin_pad_x}/1000 + $COUPON_DATA->{line_end2pad_x}/1000);
	}
	else{
		$line_end_x = $COUPON_DATA->{coupon_length} - ($right_size + $COUPON_DATA->{margin_line_x}/1000);
	}
	
	
	$COUPON_DATA->{line_start_x} = $line_start_x;
	$COUPON_DATA->{line_end_x} = $line_end_x;
	$COUPON_DATA->{line_path} = [{x=>$line_start_x,y=>0},{x=>$line_end_x,y=>0}];
	
	my ($shift_left_x,$shift_right_x) = (0,0);
	$shift_left_x = $left_pads[0]->{x} - $COUPON_DATA->{pad_size}/2000 - $COUPON_DATA->{margin_pad_x}/1000 if @left_pads;
	$shift_right_x = $COUPON_DATA->{coupon_length} - $right_pads[-1]->{x} - $COUPON_DATA->{pad_size}/2000 - $COUPON_DATA->{margin_pad_x}/1000 if @right_pads;
	
	
	foreach my $item (values %{$COUPON_DATA->{layers}}){
		next unless ($item->{layer_type} eq 'S');
		foreach my $it (@{$item->{left_tpads}},@{$item->{left_gpads}}){
			$it->{x} -= $shift_left_x;
		}
		foreach my $it (@{$item->{right_tpads}},@{$item->{right_gpads}}){
			$it->{x} += $shift_right_x;
		}
	}
	
	foreach my $item (values %{$COUPON_DATA->{layers}}){
		next unless ($item->{layer_type} eq 'S');
		if ($item->{left_tpads} and $item->{left_tpads}[0]){
			$item->{pad_position_order} = $item->{left_tpads}[0]{x};
		}
		elsif($item->{right_tpads} and $item->{right_tpads}[0]){
			$item->{pad_position_order} = $item->{right_tpads}[0]{x};
		}
	}
	
	

	
	return $COUPON_DATA;
}

#计算测试点位置
sub init_pad_position{
	my $self = shift;
	my %par = @_;
	$par{coupon_data} = $self->{COUPON_DATA} unless defined $par{coupon_data};
	my $COUPON_DATA = $par{coupon_data} ;
	$COUPON_DATA->{pad_size} = $COUPON_DATA->{hole_size} + 2*$COUPON_DATA->{pad_ar} unless $COUPON_DATA->{pad_size};

	my ($coupon_left_shape,$center_tag,$coupon_right_shape) = $COUPON_DATA->{coupon_shape_type} =~ /^([GS]+)?([\-\=]+)?([GS]+)?$/;
	$COUPON_DATA->{coupon_left_shape} = $coupon_left_shape;
	$COUPON_DATA->{center_tag} = $center_tag;
	$COUPON_DATA->{coupon_right_shape} = $coupon_right_shape;
	if ($COUPON_DATA->{left_pad_sort_sub}){
		$COUPON_DATA->{left_pad_sort_sub} = eval($COUPON_DATA->{left_pad_sort_sub}) unless (ref($COUPON_DATA->{left_pad_sort_sub}) eq 'CODE');
	}
	else{
		$COUPON_DATA->{left_pad_sort_sub} = sub{$a->{layer_number} <=> $b->{layer_number} || $a->{impedance_type} cmp $b->{impedance_type} || $a->{target_impedance} <=> $b->{target_impedance}}
	}
	if ($COUPON_DATA->{right_pad_sort_sub}){
		$COUPON_DATA->{right_pad_sort_sub} = eval($COUPON_DATA->{right_pad_sort_sub}) unless (ref($COUPON_DATA->{right_pad_sort_sub}) eq 'CODE');
	}
	else{
		if ($center_tag eq '--' and $coupon_right_shape){
			$COUPON_DATA->{right_pad_sort_sub} = sub{$a->{layer_number} <=> $b->{layer_number} || $a->{impedance_type} cmp $b->{impedance_type} || $a->{target_impedance} <=> $b->{target_impedance}}
		}
		else{
			$COUPON_DATA->{right_pad_sort_sub} = sub{$b->{layer_number} <=> $a->{layer_number} || $b->{impedance_type} cmp $a->{impedance_type} || $b->{target_impedance} <=> $a->{target_impedance}} unless $COUPON_DATA->{right_pad_sort_sub};
		}
	}
	
	my $left_sort = $COUPON_DATA->{left_pad_sort_sub};
	my $pad_center_x = 0;
	my $is_left_first = 0;
	my $g_shift = $COUPON_DATA->{pad2gnd_shift};
	my $pos_seq = 0;
	foreach my $item (sort $left_sort values %{$COUPON_DATA->{layers}}){
		next unless ($item->{layer_type} eq 'S' and $item->{left_right_group} eq 'left');
		$item->{pos_seq} = ++$pos_seq;
		unless ($is_left_first){
			$item->{is_left_first} = 1;
			$is_left_first = 1;
		}
		if ($coupon_left_shape eq 'SSGG'){
			push @{$item->{left_tpads}},{x => $pad_center_x , y=>$COUPON_DATA->{pad2gnd_dist}/2};
			push @{$item->{left_gpads}},{x => $pad_center_x + $g_shift, y=>-$COUPON_DATA->{pad2gnd_dist}/2};
			if ($item->{impedance_type} =~ /difference/){
				$pad_center_x -= $COUPON_DATA->{pad1pad2_dist};
				push @{$item->{left_tpads}},{x => $pad_center_x , y=>$COUPON_DATA->{pad2gnd_dist}/2};
				push @{$item->{left_gpads}},{x => $pad_center_x - $g_shift, y=>-$COUPON_DATA->{pad2gnd_dist}/2};
			}
			$pad_center_x -= $COUPON_DATA->{pad2pad_dist};
		}
		elsif($coupon_left_shape eq 'GSGS'){
			if ($item->{impedance_type} =~ /difference/){
				push @{$item->{left_tpads}},{x => $pad_center_x , y=>$COUPON_DATA->{pad1pad2_dist}/2};
				push @{$item->{left_tpads}},{x => $pad_center_x , y=>-$COUPON_DATA->{pad1pad2_dist}/2};
				push @{$item->{left_gpads}},{x => $pad_center_x - $COUPON_DATA->{pad2gnd_dist} , y=>$COUPON_DATA->{pad1pad2_dist}/2 + abs($g_shift)};
				push @{$item->{left_gpads}},{x => $pad_center_x - $COUPON_DATA->{pad2gnd_dist} , y=>-$COUPON_DATA->{pad1pad2_dist}/2 - abs($g_shift)};
				$pad_center_x -= $COUPON_DATA->{pad2pad_dist};
			}
		}
		elsif($coupon_left_shape eq 'GS'){
			if ($item->{impedance_type} =~ /single/){
				push @{$item->{left_tpads}},{x => $pad_center_x , y=>0};
				$pad_center_x -= $COUPON_DATA->{pad2gnd_dist};
				push @{$item->{left_gpads}},{x => $pad_center_x , y=>0 + $g_shift};
				$pad_center_x -= $COUPON_DATA->{pad1pad2_dist};
			}
		}
	}
	
	my $right_sort = $COUPON_DATA->{right_pad_sort_sub};
	$pad_center_x = 0;
	my $is_right_first = 0;
	$pos_seq = 0;
	if ($coupon_right_shape){
		foreach my $item (sort $right_sort values %{$COUPON_DATA->{layers}}){
			next unless ($item->{layer_type} eq 'S');
			next unless ($center_tag eq '--' or ($center_tag eq '==' and $item->{left_right_group} eq 'right'));
			$item->{pos_seq} = ++$pos_seq;
			unless ($is_right_first){
				$item->{is_right_first} = 1;
				$is_right_first = 1;
			}
			if ($coupon_right_shape eq 'GGSS'){
				push @{$item->{right_gpads}},{x => $pad_center_x - $g_shift , y=>$COUPON_DATA->{pad2gnd_dist}/2};
				push @{$item->{right_tpads}},{x => $pad_center_x , y=>-$COUPON_DATA->{pad2gnd_dist}/2};
				if ($item->{impedance_type} =~ /difference/){
					$pad_center_x += $COUPON_DATA->{pad1pad2_dist};
					push @{$item->{right_gpads}},{x => $pad_center_x + $g_shift , y=>$COUPON_DATA->{pad2gnd_dist}/2};
					push @{$item->{right_tpads}},{x => $pad_center_x , y=>-$COUPON_DATA->{pad2gnd_dist}/2};
				}
				$pad_center_x += $COUPON_DATA->{pad2pad_dist};
			}
			elsif ($coupon_right_shape eq 'SSGG'){
				push @{$item->{right_tpads}},{x => $pad_center_x , y=>$COUPON_DATA->{pad2gnd_dist}/2};
				push @{$item->{right_gpads}},{x => $pad_center_x - $g_shift , y=>-$COUPON_DATA->{pad2gnd_dist}/2};
				if ($item->{impedance_type} =~ /difference/){
					$pad_center_x += $COUPON_DATA->{pad1pad2_dist};
					push @{$item->{right_tpads}},{x => $pad_center_x , y=>$COUPON_DATA->{pad2gnd_dist}/2};
					push @{$item->{right_gpads}},{x => $pad_center_x + $g_shift , y=>-$COUPON_DATA->{pad2gnd_dist}/2};
				}
				$pad_center_x += $COUPON_DATA->{pad2pad_dist};
			}
			elsif($coupon_right_shape eq 'SGSG'){
				if ($item->{impedance_type} =~ /difference/){
					if ($item->{impedance_type} =~ /difference/){
						push @{$item->{right_tpads}},{x => $pad_center_x , y=>$COUPON_DATA->{pad1pad2_dist}/2};
						push @{$item->{right_tpads}},{x => $pad_center_x , y=>-$COUPON_DATA->{pad1pad2_dist}/2};
						push @{$item->{right_gpads}},{x => $pad_center_x + $COUPON_DATA->{pad2gnd_dist} , y=>$COUPON_DATA->{pad1pad2_dist}/2 + abs($g_shift)};
						push @{$item->{right_gpads}},{x => $pad_center_x + $COUPON_DATA->{pad2gnd_dist} , y=>-$COUPON_DATA->{pad1pad2_dist}/2 - abs($g_shift)};
						$pad_center_x += $COUPON_DATA->{pad2pad_dist};
					}
				}
			}
			elsif($coupon_right_shape eq 'SG'){
				if ($item->{impedance_type} =~ /single/){
					push @{$item->{right_tpads}},{x => $pad_center_x , y=>0};
					$pad_center_x += $COUPON_DATA->{pad2gnd_dist};
					push @{$item->{right_gpads}},{x => $pad_center_x , y=>0+$g_shift};
					$pad_center_x += $COUPON_DATA->{pad1pad2_dist};
				}
			}
		}
	}
	return $COUPON_DATA;
}





sub group_left_right{
	my $self = shift;
	my %par = @_;
	$par{coupon_data} = $self->{COUPON_DATA} unless defined $par{coupon_data};
	my $COUPON_DATA = $par{coupon_data} ;
	my $PadCount = 0;
	#计算总共有多少个测试点
	foreach my $item (values %{$COUPON_DATA->{layers}}){
		next unless ($item->{layer_type} eq 'S');
		if ($item->{impedance_type} =~ /difference/){
			$PadCount += 2;
		}
		else{
			$PadCount += 1;
		}
	}
	#需要进行左右分组的类型   
	my @need_left_right_group = ('SSGG==GGSS','SSGG==SSGG','GSGS==SGSG','GS==SG');
	#进行左右分类
	if (grep({$COUPON_DATA->{coupon_shape_type} eq $_} @need_left_right_group)){
		if ($COUPON_DATA->{group_left_right_func}){
			$COUPON_DATA->{group_left_right_func} = eval($COUPON_DATA->{group_left_right_func});
		}
		else{
			$COUPON_DATA->{group_left_right_func} = sub{
				my %par = @_;
				my $n = 0;
				my $half_pad_count = int ($par{pad_count}+1)/2;
				foreach my $item (sort{$a->{layer_number} <=> $b->{layer_number} || $a->{impedance_type} cmp $b->{impedance_type}} values %{$COUPON_DATA->{layers}}){
					next unless ($item->{layer_type} eq 'S');
					$item->{left_right_group} = 'left' unless $n;
					if ($item->{impedance_type} =~ /difference/){
						$n += 2;
					}
					else{
						$n += 1;
					}
					
					unless ($item->{left_right_group}){
						if ($n <= $half_pad_count){
							$item->{left_right_group} = 'left';
						}
						else{
							$item->{left_right_group} = 'right';
					}
					}
				}
			}
		}
		$COUPON_DATA->{group_left_right_func}->(coupon_data=>$COUPON_DATA,pad_count=>$PadCount);
	}
	else{
		foreach my $item (sort{$a->{layer_number} <=> $b->{layer_number} || $a->{impedance_type} cmp $b->{impedance_type}} values %{$COUPON_DATA->{layers}}){
			next unless ($item->{layer_type} eq 'S');
			$item->{left_right_group} = 'left';
		}
	}
	return $COUPON_DATA;
}


sub line_copmensation{
	my $self = shift;
	my %par = @_;
	$par{coupon_data} = $self->{COUPON_DATA} unless defined $par{coupon_data};
	my $COUPON_DATA = $par{coupon_data} ;
	foreach my $group (keys %{$COUPON_DATA->{group}}){
		foreach my $layer (@{$COUPON_DATA->{group}{$group}{signal_layer}}){
			$COUPON_DATA->{layers}{$layer}{default_compensation} = 0;
			if ($COUPON_DATA->{compensation_mode} eq 'impedance_type'){
				if($COUPON_DATA->{layers}{$layer}{impedance_type} =~ /difference/){
					$COUPON_DATA->{layers}{$layer}{default_compensation} = $COUPON_DATA->{diff_compensation} if $COUPON_DATA->{diff_compensation};
				}
				else{
					$COUPON_DATA->{layers}{$layer}{default_compensation} = $COUPON_DATA->{single_compensation} if $COUPON_DATA->{single_compensation};
				}
			}
			elsif ($COUPON_DATA->{compensation_mode} eq 'layer_type'){
				if ($COUPON_DATA->{layers}{$layer}{tl_name} =~ /top|bottom/){
					$COUPON_DATA->{layers}{$layer}{default_compensation} = $COUPON_DATA->{outer_compensation} if $COUPON_DATA->{outer_compensation};
				}
				else{
					$COUPON_DATA->{layers}{$layer}{default_compensation} = $COUPON_DATA->{inner_compensation} if $COUPON_DATA->{inner_compensation};
				}
			}
			elsif ($COUPON_DATA->{compensation_mode} eq 'customize'){
				my $sub = eval($COUPON_DATA->{customize_compensation});
				$COUPON_DATA->{layers}{$layer}{default_compensation} = $sub->(coupon_data=>$COUPON_DATA,layer_name=>$layer,layer_data=>$COUPON_DATA->{layers}{$layer});
			}
			
			$COUPON_DATA->{layers}{$layer}{cam_line_width} +=  $COUPON_DATA->{layers}{$layer}{default_compensation} ;
			if($COUPON_DATA->{layers}{$layer}{impedance_type} =~ /difference/){
				$COUPON_DATA->{layers}{$layer}{cam_line_spacing} -= $COUPON_DATA->{layers}{$layer}{default_compensation} ;
				$COUPON_DATA->{layers}{$layer}{cam_total_width} = $COUPON_DATA->{layers}{$layer}{cam_line_spacing} + 2*$COUPON_DATA->{layers}{$layer}{cam_line_width};
			}
			else{
				$COUPON_DATA->{layers}{$layer}{cam_total_width} = $COUPON_DATA->{layers}{$layer}{cam_line_width};
			}
			$COUPON_DATA->{layers}{$layer}{cam_coplanar_spacing} -= $COUPON_DATA->{layers}{$layer}{default_compensation}/2 if($COUPON_DATA->{layers}{$layer}{impedance_type} =~ /coplanar/);
			my $tmp_layer_line2copper = $COUPON_DATA->{layers}{$layer}{cam_line_width} * $COUPON_DATA->{imp_line2copper_times};
			$tmp_layer_line2copper = $COUPON_DATA->{imp_line2copper} if $COUPON_DATA->{imp_line2copper} > $tmp_layer_line2copper;
			$COUPON_DATA->{layers}{$layer}{imp_line2copper} = ($COUPON_DATA->{layers}{$layer}{cam_coplanar_spacing})?$COUPON_DATA->{layers}{$layer}{cam_coplanar_spacing}:$tmp_layer_line2copper;
		}
	}
	return $COUPON_DATA;
}

sub analysis_group{
	my $self = shift;
	my %par = @_;
	$par{coupon_data} = $self->{COUPON_DATA} unless defined $par{coupon_data};
	my $COUPON_DATA = $par{coupon_data} ;
	foreach my $k ('header_text_size','body_text_size'){
		$COUPON_DATA->{$k} = eval($COUPON_DATA->{$k}) if ($COUPON_DATA->{$k} and ref $COUPON_DATA->{$k} ne 'ARRAY');
	}
	foreach my $k ('body_text_content'){
		$COUPON_DATA->{$k} = eval($COUPON_DATA->{$k}) if ($COUPON_DATA->{$k} and ref $COUPON_DATA->{$k} ne 'CODE');
	}
	
	
	my (@signal_layers,@all_layers,@ref_layers,%group);
	my $all_sig_count = 0;
	my $group_sig_count = 0;
	foreach my $item (sort{$COUPON_DATA->{layers}{$a}{layer_number}<=>$COUPON_DATA->{layers}{$b}{layer_number} ||
						   $COUPON_DATA->{layers}{$a}{target_impedance} <=> $COUPON_DATA->{layers}{$b}{target_impedance} ||
						   $COUPON_DATA->{layers}{$a}{mi_line_width} <=> $COUPON_DATA->{layers}{$b}{mi_line_width}
						} keys %{$COUPON_DATA->{layers}})
	{
		push @all_layers,$item;
		if (uc($COUPON_DATA->{layers}{$item}{layer_type}) eq 'R'){
			push @ref_layers,$item;
			if (@signal_layers){
				$group{join(',',@all_layers)}{signal_line_count} = $group_sig_count;
				$group{join(',',@all_layers)}{signal_layer} = [@signal_layers];
				$group{join(',',@all_layers)}{all_layer} = [@all_layers];
				$group{join(',',@all_layers)}{ref_layer} = [@ref_layers];
				foreach my $layer (@all_layers){
					if ($COUPON_DATA->{layers}{$layer}{layer_type} eq 'R'){
						push @{$COUPON_DATA->{layers}{$layer}{group}} , join(',',@all_layers);
					}
					else{
						$COUPON_DATA->{layers}{$layer}{group} = join(',',@all_layers);
					}
				}
				
				$group_sig_count = 0;
				@signal_layers=();
				@all_layers=();
				@ref_layers=();
				push @all_layers,$item;
				push @ref_layers,$item;
			}
			else{
				$group_sig_count = 0;
				@signal_layers=();
				@all_layers=();
				@ref_layers=();
				push @all_layers,$item;
				push @ref_layers,$item;
			}
		}
		elsif (uc($COUPON_DATA->{layers}{$item}{layer_type}) eq 'S'){
			push @signal_layers,$item;
			$group_sig_count++;
			$all_sig_count++;
		}
	}
	if (@signal_layers){
		$group{join(',',@all_layers)}{signal_layer}=[@signal_layers];
		$group{join(',',@all_layers)}{all_layer}=[@all_layers];
		$group{join(',',@all_layers)}{ref_layer} = [@ref_layers];
		$group{join(',',@all_layers)}{signal_line_count} = $group_sig_count;
		foreach my $layer (@all_layers){
			if ($COUPON_DATA->{layers}{$layer}{layer_type} eq 'R'){
				push @{$COUPON_DATA->{layers}{$layer}{group}} , join(',',@all_layers);
			}
			else{
				$COUPON_DATA->{layers}{$layer}{group} = join(',',@all_layers);
			}
		}
	}	
	
	#获取HDI钻孔信息
	foreach my $drl (sort{$COUPON_DATA->{drills}->{$b}{drl_start_num} <=> $COUPON_DATA->{drills}->{$a}{drl_start_num}}
						 keys %{$COUPON_DATA->{drills}})
	{
		my (%tmp_top_grp,%tmp_bot_grp,$n);
		foreach my $grp (sort{$COUPON_DATA->{layers}{$group{$a}{all_layer}[0]}{layer_number} <=> $COUPON_DATA->{layers}{$group{$b}{all_layer}[0]}{layer_number}} keys %group){
			$n++;
			foreach my $layer (@{$group{$grp}{all_layer}}){
				if ($COUPON_DATA->{layers}{$layer}{layer_number} == $COUPON_DATA->{drills}{$drl}{drl_start_num}){
					$tmp_top_grp{$grp} = $n;
				}
				if ($COUPON_DATA->{layers}{$layer}{layer_number} == $COUPON_DATA->{drills}{$drl}{drl_end_num}){
					$tmp_bot_grp{$grp} = $n;
				}
			}
			$COUPON_DATA->{drills}{$drl}{top_coupon_groups} = [sort {$tmp_top_grp{$a} <=> $tmp_top_grp{$b}} keys %tmp_top_grp] if %tmp_top_grp;
			$COUPON_DATA->{drills}{$drl}{bot_coupon_groups} = [sort {$tmp_bot_grp{$a} <=> $tmp_bot_grp{$b}} keys %tmp_bot_grp] if %tmp_bot_grp;
			if ($COUPON_DATA->{layers}{$group{$grp}{all_layer}[0]}{layer_number} >= $COUPON_DATA->{drills}{$drl}{drl_start_num} - 0.1 and
				$COUPON_DATA->{layers}{$group{$grp}{all_layer}[-1]}{layer_number} <= $COUPON_DATA->{drills}{$drl}{drl_end_num} + 0.1 
			)
			{
				push @{$COUPON_DATA->{drills}{$drl}{include_coupon_groups}},$grp;
			}
		}
		foreach my $item (sort{$COUPON_DATA->{layers}{$a}{layer_number}<=>$COUPON_DATA->{layers}{$b}{layer_number}} keys %{$COUPON_DATA->{layers}}){
			if ($COUPON_DATA->{layers}{$item}{layer_number} == $COUPON_DATA->{drills}{$drl}{drl_start_num}){
				$COUPON_DATA->{drills}{$drl}{top_text_layer} = $item;
			}
			elsif ($COUPON_DATA->{layers}{$item}{layer_number} == $COUPON_DATA->{drills}{$drl}{drl_end_num}){
				$COUPON_DATA->{drills}{$drl}{bot_text_layer} = $item;
			}
		}
	}
	
	
	$COUPON_DATA->{signal_count} = $all_sig_count;
	$COUPON_DATA->{group} = \%group;
	return $COUPON_DATA;
}

sub convert_matrix{
	my $self = shift;
	my %par = @_;
	$par{coupon_data} = $self->{COUPON_DATA} unless defined $par{coupon_data};
	my $COUPON_DATA = $par{coupon_data} ;
	$COUPON_DATA->{matrix_func} = eval($COUPON_DATA->{matrix_func}) unless (ref($COUPON_DATA->{matrix_func}) eq 'CODE');
	if ($COUPON_DATA->{matrix_func} and ref($COUPON_DATA->{matrix_func}) eq 'CODE'){
		$COUPON_DATA->{matrix} = $COUPON_DATA->{matrix_func}->(job_id=>$COUPON_DATA->{job_id},job_name=>$COUPON_DATA->{job_name},layer_count=>$COUPON_DATA->{layer_count},coupon_data=>$COUPON_DATA,matrix=>eval(dump($COUPON_DATA->{matrix})));
	}
	if ($COUPON_DATA->{hdi_stack_test} eq 'yes'){
		foreach my $layer (values %{$COUPON_DATA->{matrix}}){
			if ($layer->{tl_name} and $layer->{tl_name} =~ /^drill(\d+\-\d+)?$/){
				if ($layer->{drl_start_num} < ($COUPON_DATA->{layer_count}+1)/2 and
					$layer->{drl_end_num} > ($COUPON_DATA->{layer_count}+1)/2)
				{
					$COUPON_DATA->{drills}->{$layer->{tl_name}} = $layer;
				}
			}
		}
	}
	else{
		foreach my $layer (values %{$COUPON_DATA->{matrix}}){
			if ($layer->{tl_name} and $layer->{tl_name} eq 'drill'){
				if ($layer->{drl_start_num} < ($COUPON_DATA->{layer_count}+1)/2 and
					$layer->{drl_end_num} > ($COUPON_DATA->{layer_count}+1)/2)
				{
					$COUPON_DATA->{drills}->{$layer->{tl_name}} = $layer;
				}
			}
		}
	}
}

sub convert_units{
	my $self = shift;
	my %par = @_;
	$par{coupon_data} = $self->{COUPON_DATA} unless defined $par{coupon_data};
	my $COUPON_DATA = $par{coupon_data} ;
	$COUPON_DATA->{line_width_units} = 'mil' unless $COUPON_DATA->{line_width_units};
	my $factor = 1;
	if ($COUPON_DATA->{line_width_units} eq 'um'){
		$factor = 1/25.4;
	}
	elsif($COUPON_DATA->{line_width_units} eq 'mm'){
		$factor = 1/0.0254;
	}
	elsif($COUPON_DATA->{line_width_units} eq 'inch'){
		$factor = 25.4
	}
	return if $factor == 1;
	foreach my $imp (values %{$COUPON_DATA->{layers}}){
		next unless $imp->{layer_type} eq 'S';
		foreach my $it ('cam_line_width','cam_line_spacing','cam_coplanar_spacing',){
			$imp->{$it} *= $factor;
		}
	}
}


sub show_coupon_form{
	my $self = shift;
	my %par = @_;
	$par{coupon_data} = $self->{COUPON_DATA} unless defined $par{coupon_data};
	my $COUPON_DATA = $par{coupon_data} ;
	#$GUI->debug(dump($COUPON_DATA));
	my $template_value;
	my @buttons;
	my %response_sub;
	my %button_sensitive;
	if ($par{-buttons}){
		foreach my $item (@{$par{-buttons}}){
			push @buttons,$item->{stock},$item->{response};
			$response_sub{$item->{response}} = $item->{command};
			$button_sensitive{$item->{response}} = $item->{sensitive} if defined $item->{sensitive};
		}
	}
	else{
		push @buttons,'tl-ok'=>'ok','tl-cancel'=>'cancel';
		push @buttons,'tl-pause-genesis'=>'help' if $par{-showcheck};
	}
	my $dialog = Gtk2::Dialog->new_with_buttons ('['.$COUPON_DATA->{coupon_name}.']参数设置',$par{-parent}, 'destroy-with-parent',@buttons);
	$dialog->set_keep_above(1) if (lc($^O) eq 'linux');
	foreach my $it (keys %button_sensitive){
		$dialog->set_response_sensitive ($it, $button_sensitive{$it});
	}
	
	$dialog->{header_box} = TL::Gtk2::HeadBox->new(tl_stock=>'gtk-edit',tl_text=>'['.$COUPON_DATA->{coupon_name}.']参数设置');
	$dialog->vbox->pack_start($dialog->{header_box},0,0,0);
	$dialog->{body} = TL::Gtk2::HBoxPanel->new();
	#my $tl_data = $self->{IKM}->select_arrayhash(-table=>'sys_cfg',-field=>['name'],-where=>{class => 'impedance_coupon_template'},-order=>'flow_order ASC');
	my $tl_data = $self->{IKM}->select_arrayhash(-table=>'pub_conf',-field=>['name'],-where=>{path => 'pdm/impedance_coupon_template'});
	$dialog->{body}->set_property(
		items => [
			{
				size => 300,
				split => 1,
				body => {
					class => 'TL::Gtk2::VBoxPanel',
					property =>{
						items => [
							{
								body => {
									name => 'TEMPLATE',
									class => 'TL::Gtk2::ComboBox',
									property => {
										width_request => 2,
										tl_field => ['name' => 'text'],
										tl_value_field => 'name',
										tl_data => $tl_data,
									}
								}
							},
							{
								expand => 1,
								body => {
									name => 'sw_param',
									class=>'Gtk2::ScrolledWindow',
									property=>{border_width=>0,hscrollbar_policy=>'automatic',vscrollbar_policy=>'automatic',shadow_type=>'etched-in'},
									child=>{
										PARAMETER=>{
											class=>'TL::Gtk2::FormPanel',
											property=>{n_columns => 1},
											packing=>{}
										},
									}
								}
							}
						]
					}
				},
			},
			{
				expand => 1,
				body => {
					name=>'scw_list',
					class=>'Gtk2::ScrolledWindow',
					property=>{border_width=>0,hscrollbar_policy=>'automatic',vscrollbar_policy=>'automatic',shadow_type=>'etched-in'},
					child=>{
						TREEVIEW=>{
							class=>'TL::Gtk2::TreeView',
							property=>{can_focus=>1,enable_grid_lines=>'none','show-expanders'=>0 ,
										tl_mapping => [
											layer_name => 'Glib::String',
											layer_type => 'Glib::String',
											layer_type_display_name => 'Glib::String',
											layer_number => 'Glib::String',
											impedance_type => 'Glib::String',
											impedance_type_display_name => 'Glib::String',
											target_impedance => 'Glib::String',
											org_line_width => 'Glib::String',
											org_line_spacing => 'Glib::String',
											cam_line_width => 'Glib::String',
											cam_line_spacing => 'Glib::String',
											cam_coplanar_spacing => 'Glib::String',
											bg_color => 'Glib::String',
										],
										tl_columns => [
											{title=>'层别',resizable=>1,reorderable=>1,min_width=>50,renders=>[{class=>'Text',property=>{text=>'L'},background => 'bg_color'},{class=>'Text',text=>'layer_number',background => 'bg_color'}]},
											{title=>'层类型',resizable=>1,reorderable=>1,min_width=>50,renders=>[{class=>'Text',text=>'layer_type_display_name',background => 'bg_color'}]},
											{title=>'阻抗类型',resizable=>1,reorderable=>1,min_width=>50,renders=>[{class=>'Text',text=>'impedance_type_display_name',background => 'bg_color'}]},
											{title=>'阻抗值',resizable=>1,reorderable=>1,min_width=>50,renders=>[{class=>'Text',text=>'target_impedance',background => 'bg_color'}]},
											{title=>'原稿线宽',resizable=>1,reorderable=>1,min_width=>50,renders=>[{class=>'Text',text=>'org_line_width',background => 'bg_color'}]},
											{title=>'原稿线距',resizable=>1,reorderable=>1,min_width=>50,renders=>[{class=>'Text',text=>'org_line_spacing',background => 'bg_color'}]},
											{title=>'Coupon线宽',resizable=>1,reorderable=>1,min_width=>50,renders=>[{class=>'Text',text=>'cam_line_width',background => 'bg_color'}]},
											{title=>'Coupon线距',resizable=>1,reorderable=>1,min_width=>50,renders=>[{class=>'Text',text=>'cam_line_spacing',background => 'bg_color'}]},
											{title=>'共面间距',resizable=>1,reorderable=>1,min_width=>50,renders=>[{class=>'Text',text=>'cam_coplanar_spacing',background => 'bg_color'}]},
										],
									   },
							packing=>{}
						},
					}
				}
			}
		]
	);
	$dialog->vbox->pack_start($dialog->{body},1,1,1);
	$dialog->set_default_size(1000,600);
	$dialog->show_all();
	$dialog->set_icon_from_file('top.ico')  if (-f 'top.ico');
	$dialog->set_position('center-on-parent');
	my $wgt_template = $dialog->{body}->get_widget('TEMPLATE');
	my $wgt_param = $dialog->{body}->get_widget('PARAMETER');
	$wgt_template->signal_connect('changed' => sub{
		my $template = $wgt_template->tl_get_value();
		my $cfg = $self->{IKM}->select_value(-table=>'pub_conf',-field=>['json_data'],-where=>{path => 'pdm/impedance_coupon_template',name => $template});
		$template_value = $json->decode($cfg);
		#$template_value = eval($cfg->{json_data});
		foreach my $item (keys %$template_value){
			next if $item eq 'script_ui';
			$COUPON_DATA->{$item} = $template_value->{$item} unless $COUPON_DATA->{fixed_fields}{$item};
		}
		my $style = eval($template_value->{script_ui});
		$wgt_param->set_property(%$style);
		$wgt_param->load_data($COUPON_DATA);
	});
	$wgt_template->tl_set_value($COUPON_DATA->{coupon_template});
	my $treeview = $dialog->{body}->get_widget('TREEVIEW');
	
	
	$treeview->tl_set_data([sort{$a->{layer_number} <=> $b->{layer_number} || $a->{target_impedance} <=> $b->{target_impedance}} values %{$COUPON_DATA->{layers}}]);
	
	my $ans;
	while(1){
		$ans = $dialog->run;
		if ($response_sub{$ans}){
			$response_sub{$ans}->(dialog=>$dialog,parameter_formpanel=>$wgt_param,tempalte_widget=>$wgt_template);
		}
		else{
			if ($ans eq 'ok'){
				my $msg = $wgt_param->validate_all('save');
				if (@{$msg->{error_msg}}){
					$APP->{GUI}->msgbox(-parent=>$dialog,-icon=>'error',-text=>join("\n",@{$msg->{error_msg}}));
				}
				elsif(@{$msg->{warning_msg}}){
					if ($APP->{GUI}->confirm(join("\n",@{$msg->{warning_msg}})."\n".__('Are you sure to continue?'),undef,$dialog) eq 'yes'){
						$COUPON_DATA->{coupon_template} = $wgt_template->tl_get_value();
						my %return = $wgt_param->get_all_value();
						$dialog->hide();
						$dialog->destroy;
						refresh_ui();
						foreach my $item (keys %return){
							$COUPON_DATA->{$item} = $return{$item};
						}
						#$self->_layer_mark_mapping();
						return 1;
					}
					else{
						return 0;
					}
				}
				else{
					$COUPON_DATA->{coupon_template} = $wgt_template->tl_get_value();
					my %return = $wgt_param->get_all_value();
					$dialog->hide();
					$dialog->destroy;
					refresh_ui();
					foreach my $item (keys %return){
						$COUPON_DATA->{$item} = $return{$item};
					}
					#$self->_layer_mark_mapping();
					return 1;
				}
			}
			elsif($ans eq 'cancel'){
				$dialog->hide();
				$dialog->destroy;
				refresh_ui();
				return 0;
			}
		}
		return 1;
	}

}

sub coupon_to_layer{
	my $self = shift;
	my %par = @_;
	$par{coupon_data} = $self->{COUPON_DATA} unless defined $par{coupon_data};
	my $COUPON_DATA = $par{coupon_data} ;
	my %couponLayers;
	my $enum_imptype = $self->get_imptype_enum_hash();
	
	my %gen_layer;
	if ($COUPON_DATA->{matrix}){
		foreach my $item (keys %{$COUPON_DATA->{matrix}}){
			if ($COUPON_DATA->{matrix}->{$item}{tl_name}){
				$gen_layer{$COUPON_DATA->{matrix}{$item}{tl_name}} = $item;
			}
		}
	}
	
	my %layers;
	$layers{'top'}{layer_name} = 'Top';
	$layers{'top'}{layer_number} = 1;
	$layers{'top'}{tl_name} = 'top';
	$layers{'top'}{gen_layer} = $gen_layer{'top'};

	foreach my $i (2..$COUPON_DATA->{layer_count}-1){
		$layers{'l'.$i}{layer_name} = 'L'.$i;
		$layers{'l'.$i}{layer_number} = $i;
		$layers{'l'.$i}{tl_name} = 'l'.$i;
		$layers{'l'.$i}{gen_layer} = $gen_layer{'l'.$i};
	}
	if ($COUPON_DATA->{layer_count} >=2){
		$layers{'bottom'}{layer_name}='Bottom';
		$layers{'bottom'}{layer_number}=$COUPON_DATA->{layer_count};
		$layers{'bottom'}{tl_name} = 'bottom';
		$layers{'bottom'}{gen_layer} = $gen_layer{'bottom'};
	}

	foreach my $imp (@{$COUPON_DATA->{impedances}}){
		if ($imp->{impedance_type} =~ /coplanar/){
			$COUPON_DATA->{is_coplanar} = 1;
		}
		push @{$couponLayers{$imp->{signal1}}},{
			%$imp,
			layer_type        	=> 'S',
			org_line_width    	=> $imp->{org_line_width1},
			org_line_spacing  	=> $imp->{org_spacing},
			mi_line_width    	=> $imp->{line_width1},
			mi_line_spacing  	=> $imp->{spacing},
			mi_coplanar_spacing => $imp->{coplanar_spacing},
			cam_line_width 		=> $imp->{cam_coupon_line_width1}||($imp->{line_width1}+($imp->{cam_compensation}||0)),
			cam_line_spacing 	=> $imp->{cam_coupon_spacing}?$imp->{cam_coupon_spacing}:($imp->{spacing}?($imp->{spacing} - ($imp->{cam_compensation}||0)):''),
			#cam_coplanar_spacing => ($imp->{impedance_type} =~ /coplanar/)?($imp->{cam_coupon_coplanar_spacing}?$imp->{cam_coupon_coplanar_spacing}:($imp->{coplanar_spacing}?($imp->{coplanar_spacing}  - ($imp->{cam_compensation}||0)/2):'')) : '',
			cam_coplanar_spacing => $imp->{cam_coupon_coplanar_spacing}?$imp->{cam_coupon_coplanar_spacing}:($imp->{coplanar_spacing}?($imp->{coplanar_spacing}  - ($imp->{cam_compensation}||0)/2):''),
			layer_name        	=> $layers{$imp->{signal1}}{layer_name},
			layer_number      	=> $layers{$imp->{signal1}}{layer_number},
			tl_name           	=> $layers{$imp->{signal1}}{tl_name},
			gen_layer 			=> $layers{$imp->{signal1}}{gen_layer},
			impedance_id 		=> $imp->{uid},
		};
		if ($imp->{reference1}){
			push @{$couponLayers{$imp->{reference1}}},{
				layer_type   => 'R',
				layer_name   => $layers{$imp->{reference1}}{layer_name},
				layer_number => $layers{$imp->{reference1}}{layer_number},
				tl_name      => $layers{$imp->{reference1}}{tl_name},
				gen_layer => $layers{$imp->{reference1}}{gen_layer},
				impedance_id => $imp->{uid},
			};
			my ($min,$max)=sort {$a<=>$b} ($layers{$imp->{signal1}}{layer_number},$layers{$imp->{reference1}}{layer_number});
			foreach my $n ($min+1..$max-1){
				foreach my $layer (keys %layers){
					if (abs($layers{$layer}{layer_number} - $n) < 0.00001){
						push @{$couponLayers{$layer}},{
							layer_type   => 'B',
							layer_number => $n,
							layer_name   => $layers{$layer}{layer_name},
							tl_name      => $layers{$layer}{tl_name},
							gen_layer	 => $layers{$layer}{gen_layer},
							impedance_id => $imp->{uid},
						}
					}
				}
			}
		}
		else{
			foreach my $n (1..$layers{$imp->{signal1}}{layer_number}-1){
				foreach my $layer (keys %layers){
					if (abs($layers{$layer}{layer_number} - $n) < 0.00001){
						push @{$couponLayers{$layer}},{
							layer_type   => 'B',
							layer_number => $n,
							layer_name   => $layers{$layer}{layer_name},
							tl_name      => $layers{$layer}{tl_name},
							gen_layer	 => $layers{$layer}{gen_layer},
							impedance_id => $imp->{id},
						}
					}
				}
			}		
		}
		if ($imp->{reference2}){
			push @{$couponLayers{$imp->{reference2}}},{
				layer_type   =>'R',
				layer_name   => $layers{$imp->{reference2}}{layer_name},
				layer_number => $layers{$imp->{reference2}}{layer_number},
				tl_name      => $layers{$imp->{reference2}}{tl_name},
				gen_layer	 => $layers{$imp->{reference2}}{gen_layer},
				impedance_id => $imp->{uid},
			};
			my ($min,$max)=sort {$a<=>$b} ($layers{$imp->{signal1}}{layer_number},$layers{$imp->{reference2}}{layer_number});
			foreach my $n ($min+1..$max-1){
				foreach my $layer (keys %layers){
					if (abs($layers{$layer}{layer_number} - $n) < 0.00001){
						push @{$couponLayers{$layer}},{
							layer_type => 'B',
							layer_number => $n,
							layer_name => $layers{$layer}{layer_name},
							tl_name => $layers{$layer}{tl_name},
							gen_layer	 => $layers{$layer}{gen_layer},
							impedance_id => $imp->{uid},
						}
					}
				}
			}
		}
		else{
			foreach my $n ($layers{$imp->{signal1}}{layer_number}+1..$COUPON_DATA->{layer_count}){
				foreach my $layer (keys %layers){
					if (abs($layers{$layer}{layer_number} - $n) < 0.00001){
						push @{$couponLayers{$layer}},{
							layer_type   => 'B',
							layer_number => $n,
							layer_name   => $layers{$layer}{layer_name},
							tl_name      => $layers{$layer}{tl_name},
							gen_layer	 => $layers{$layer}{gen_layer},
							impedance_id => $imp->{uid},
						}
					}
				}
			}			
		}
	}
	
	
	
	my %CouponLayers;
	foreach my $layer (keys %layers){
		if ($couponLayers{$layer}){
			if (scalar(@{$couponLayers{$layer}}) == 1){
				$CouponLayers{$layer}=$couponLayers{$layer}->[0];
			}
			else{
				my ($pre_type,$same_type) = ('',1);
				my (@s_layers,@r_layers,@b_layers);
				foreach my $item (@{$couponLayers{$layer}}){
					if ($item->{layer_type} eq 'S'){
						push @s_layers,$item;
					}
					elsif($item->{layer_type} eq 'R'){
						push @r_layers,$item;
					}
					elsif($item->{layer_type} eq 'B'){
						push @b_layers,$item;
					}
				}
				if (scalar(@s_layers) > 0 and scalar(@r_layers) > 0){
					foreach my $item (@s_layers,@r_layers,@b_layers){
						$CouponLayers{$layer.'_'.$item->{impedance_id}} = $item;
						$CouponLayers{$layer.'_'.$item->{impedance_id}}{layer_name} = $item->{layer_name}.' ('.$item->{impedance_id}.')';
					}
				}
				elsif(scalar(@s_layers)>0){
					if (scalar(@s_layers) == 1){
						$CouponLayers{$layer} = $s_layers[0];
					}
					else{
						foreach my $item (@s_layers){
							$CouponLayers{$layer.'_'.$item->{impedance_id}} = $item;
							$CouponLayers{$layer.'_'.$item->{impedance_id}}{layer_name} = $item->{layer_name}.' ('.$item->{impedance_id}.')';
						}						
					}
				}
				elsif(scalar(@r_layers)>0){
					if(scalar(@b_layers) == 0){
						$CouponLayers{$layer} = $r_layers[0];
					}
					else{
						foreach my $item (@r_layers,@b_layers){
							$CouponLayers{$layer.'_'.$item->{impedance_id}} = $item;
							$CouponLayers{$layer.'_'.$item->{impedance_id}}{layer_name} = $item->{layer_name}.' ('.$item->{impedance_id}.')';
						}	
					}
				}
				else{
					foreach my $item (@s_layers,@r_layers,@b_layers){
						$CouponLayers{$layer.'_'.$item->{impedance_id}} = $item;
						$CouponLayers{$layer.'_'.$item->{impedance_id}}{layer_name} = $item->{layer_name}.' ('.$item->{impedance_id}.')';
					}
				}
				
			
			}
		}
		else{
			$CouponLayers{$layer}{layer_type}='';
			$CouponLayers{$layer}{layer_number} = $layers{$layer}{layer_number};
			$CouponLayers{$layer}{tl_name} = $layers{$layer}{tl_name};
			$CouponLayers{$layer}{layer_name} = $layers{$layer}{layer_name};
			$CouponLayers{$layer}{gen_layer} = $layers{$layer}{gen_layer};
			$CouponLayers{$layer}{impedance_id} = 0;
		}
	}
	foreach my $item (values %CouponLayers){
		if ($item->{layer_type} eq 'S'){
			$item->{layer_type_display_name} = '信号层';
			$item->{impedance_type_display_name} = $enum_imptype->{$item->{impedance_type}}{display_name};
			$item->{bg_color} = '#FDC74D';
		}
		elsif($item->{layer_type} eq 'R'){
			$item->{layer_type_display_name} = '参考层';
			$item->{bg_color} = 'green';
		}
		elsif($item->{layer_type} eq 'B'){
			$item->{layer_type_display_name} = '隔离层';
			$item->{bg_color} = '#FFFFCE';
		}
		else{
			
		}
	}
	#$GUI->debug(-text=>dump(\%CouponLayers));
	$COUPON_DATA->{layers} = \%CouponLayers;
	$self->_layer_mark_mapping();
	return $COUPON_DATA;
}


sub _layer_mark_mapping{
	my $self = shift;
	my %par = @_;
	my $COUPON_DATA = $self->{COUPON_DATA};
	my %layer_mark;
	$layer_mark{'top'} = 'L1';
	foreach my $i (2..$COUPON_DATA->{layer_count}-1){
		$layer_mark{'l'.$i} = 'L'.$i;
	}
	$layer_mark{'bottom'}='L'.$COUPON_DATA->{layer_count};
	my $tmp_layer_mark = eval($COUPON_DATA->{layer_number_mapping});
	$tmp_layer_mark = {} unless $tmp_layer_mark;
	foreach my $it (keys %$tmp_layer_mark){
		$layer_mark{$it} = $tmp_layer_mark->{$it};
	}
	$COUPON_DATA->{layer_mark} = \%layer_mark;
}

sub perm{
	my $list=shift;
	my $k=shift;
	my $m=shift;
	my @output;
	if ($k<=$m){
		foreach my $i ($k..$m){
			($list->[$i],$list->[$k])=($list->[$k],$list->[$i]);
			push @output,perm($list,$k+1,$m);
			($list->[$i],$list->[$k])=($list->[$k],$list->[$i]);	
		}
	}
	push @output,[@$list];
	return @output;
}

#(x=>?,y=>?,text=>?,text_length=>?,x_size=>?,y=>size=>?,line_width=>?,anchor=>?,angle=>?,mirror=>?,units=>?)
sub _get_text_rect{
	my $self = shift;
    my %par = @_;
    $par{x} = 0 unless $par{x};
	$par{y} = 0 unless $par{y};
	$par{anchor} = 'sw' unless $par{anchor};
	$par{angle} = 0 unless $par{angle};
	$par{mirror} = 'no' unless $par{mirror};
    my $units = $par{units};
	$units = 'inch' unless $units;
	if (defined $par{w_factor}){
		if ($units eq 'inch'){
			$par{line_width} = $par{w_factor}*12;
		}
		else{
			$par{line_width} = $par{w_factor}*304.8	;
		}
	}
    $par{w_factor} = delete $par{line_width};
    my $text_width;
    if ($units eq 'inch'){
		$par{w_factor} = $par{w_factor}/12;
		$text_width = $par{x_size}*0.702781 + ($par{w_factor} -1)*0.004053;
    }
    else{
		$par{w_factor} =$par{w_factor} / 304.8;
		$text_width = $par{x_size}*0.702781 + ($par{w_factor} -1)*0.102945;
    }
	my $txt = $par{text};
	my $text_length = delete $par{text_length};
	if ($text_length){
		$txt = '8' x $text_length;
	}

    my $anchor = delete $par{anchor};
    my ($cx,$cy);
    if ($anchor eq 'center'){
		($cx,$cy) = ($par{x},$par{y});
    }
    elsif($anchor eq 'e'){
		($cx,$cy) = ($par{x}-($par{x_size} * (length($txt) -1 ) + $text_width)/2,$par{y});
    }
    elsif($anchor eq 's'){
		($cx,$cy) = ($par{x},$par{y}+$par{y_size}/2);
    }
    elsif($anchor eq 'w'){
		($cx,$cy) = ($par{x}+($par{x_size} * (length($txt) -1 ) + $text_width)/2,$par{y});
    }
    elsif($anchor eq 'n'){
		($cx,$cy) = ($par{x},$par{y} - $par{y_size}/2);
    }
    elsif($anchor eq 'se'){
		($cx,$cy) = ($par{x}-($par{x_size} * (length($txt) -1 ) + $text_width)/2 ,$par{y}+$par{y_size}/2);
    }
    elsif($anchor eq 'sw'){
		($cx,$cy) = ($par{x}+($par{x_size} * (length($txt) -1 ) + $text_width)/2 ,$par{y}+$par{y_size}/2);
    }
    elsif($anchor eq 'nw'){
		($cx,$cy) = ($par{x}+($par{x_size} * (length($txt) -1 ) + $text_width)/2 ,$par{y}-$par{y_size}/2);
    }
    elsif($anchor eq 'ne'){
		($cx,$cy) = ($par{x}-($par{x_size} * (length($txt) -1 ) + $text_width)/2 ,$par{y}-$par{y_size}/2);
    }    

	my $trans_anchor = delete $par{trans_anchor};
	if ($trans_anchor and $trans_anchor eq 'yes'){
		my %tmp = $self->_trans_coord(cx=>$par{x},cy=>$par{y},x=>$cx,y=>$cy,angle=> $par{angle},mirror=>'no');
		($cx,$cy) = ($tmp{x},$tmp{y});	
	}
    my $x1 = $cx - ($par{x_size} * (length($txt) -1 ) + $text_width)/2;
    my $y1 = $cy - $par{y_size}/2;
	my $x2 = $cx + ($par{x_size} * (length($txt) -1 ) + $text_width)/2;
	my $y2 = $cy + $par{y_size}/2;
	my %return = (cx=>$cx,cy=>$cy);
    my %tmp = $self->_trans_coord(cx=>$cx,cy=>$cy,x=>$x1,y=>$y1,angle=>$par{angle},mirror=>$par{mirror});
	($return{x1},$return{y1}) = ($tmp{x},$tmp{y});
	 %tmp = $self->_trans_coord(cx=>$cx,cy=>$cy,x=>$x2,y=>$y2,angle=>$par{angle},mirror=>$par{mirror});
    ($return{x2},$return{y2}) = ($tmp{x},$tmp{y});
	($return{x1},$return{x2}) = sort {$a <=> $b} ($return{x1},$return{x2});
	($return{y1},$return{y2}) = sort {$a <=> $b} ($return{y1},$return{y2});
	$return{xsize} = abs($return{x2} - $return{x1});
	$return{ysize} = abs($return{y2} - $return{y1});
	if (wantarray){
		return %return;
	}
	else{
		return \%return;
	}
}


sub _trans_coord{
	my $self = shift;
	my %par = @_;
	$par{mirror} = 'no' if (! defined $par{mirror});
	$par{shiftx} = 0 if (! defined $par{shiftx});
	$par{shifty} = 0 if (! defined $par{shifty});
	$par{angle} = 0 if (! defined $par{angle});
	$par{cx} += $par{shiftx};
	$par{cy} += $par{shifty};
	$par{x} += $par{shiftx};
	$par{y} += $par{shifty};
	my %return;
   if ( $par{angle} == 90 ) {
        $return{x} = $par{cx} + $par{y} - $par{cy};
        $return{y} = $par{cy} - ($par{x} - $par{cx});
    }
    elsif ( $par{angle} == 180 ) {
        $return{x} = $par{cx} - ($par{x} - $par{cx});
        $return{y} = $par{cy} - ($par{y} - $par{cy});
    }
    elsif ( $par{angle} == 270 ) {
        $return{x} = $par{cx} - ($par{y} - $par{cy});
        $return{y} = $par{cy} + ($par{x} - $par{cx});
    }
    elsif ( $par{angle} == 0 ) {
        $return{x} = $par{x};
        $return{y} = $par{y};
    }
	if ((lc $par{mirror}) eq 'yes'){
		$return{x} = $par{cx} + ($par{cx} - $return{x});
	}
	if (wantarray){
		return %return;
	}
	else{
		return {%return};
	}
}

sub get_imptype_enum_hash{
	my $self = shift;
	return {
		"single" => {
			name => "single",
			display_name => "Single",
			flow_order => 1
		},
		"difference" => {
			name => "difference",
			display_name => "Difference",
			flow_order => 2
		},
		"single_coplanar" => {
			name => "single_coplanar",
			display_name => "Single Coplanar",
			flow_order => 3
		},
		"difference_coplanar" => {
			name => "difference_coplanar",
			display_name => "Difference Coplanar",
			flow_order => 4
		}
	};
}

1;

__END__
=head1 COUPON_PARAMETER

coupon_shape_type => 'SSGG==GGSS', #'SSGG==GGSS','SSGG==SSGG','GSGS==SGSG','GS==SG','GS--'

group_left_right_func => sub {
		my %par = @_; #(coupon_data=>$COUPON_DATA,pad_count=>$PadCount)
		my $n = 0;
		my $half_pad_count = int ($par{pad_count}+1)/2;
		foreach my $item (sort{$a->{layer_number} <=> $b->{layer_number} || $a->{impedance_type} cmp $b->{impedance_type}} values %{$COUPON_DATA->{layers}}){
			next unless ($item->{layer_type} eq 'S');
			if ($item->{impedance_type} =~ /difference/){
				$n += 2;
			}
			else{
				$n += 1;
			}
			if ($n <= $half_pad_count){
				$item->{left_right_group} = 'left';
			}
			else{
				$item->{left_right_group} = 'right';
			}
		}
	},

left_pad_sort_sub => sub{$a->{layer_number} <=> $b->{layer_number} || $a->{impedance_type} cmp $b->{impedance_type} || $a->{target_impedance} <=> $b->{target_impedance}},

right_pad_sort_sub => sub{$b->{layer_number} <=> $a->{layer_number} || $b->{impedance_type} cmp $a->{impedance_type} || $b->{target_impedance} <=> $a->{target_impedance}},

pad2gnd_dist => 0.1,

pad2gnd_shift => 0.1,

pad1pad2_dist => 0.1,

pad2pad_dist => 0.1,

pad_size => 60,

margin_pad_x => 30,

line_end2pad_x => 30,

margin_line_x => 30,

coupon_length => 6,

auto_enlarge_coupon_length => 'no',

min_test_line_length => 6,

coupon_body_width => 0.2,#coupon中心部位宽度

coupon_header_width => 0.3,#coupon头部宽度

auto_enlarge_coupon_header_width=> 'no',

auto_enlarge_coupon_body_width=> 'no',

coupon_header_body_same_width => 'yes',

auto_adjust_width_center => 'yes',#是否自动调整宽度中心,

rout2copper_spacing => 10, #成型避铜

min_line2rout_spacing => 20,#中间线到成型距离,避免最边缘的线没有保护铜

add_pin_hole => 'yes',

pin_hole_edge_dist_x => 0.2,

line2pad_spacing => 4,

rerout_direction => 'tpad',#'tpad'|'both',头部绕线方式

line2line_spacing => 6,

line_centered => 'yes',#头部需绕线时,是否绕回至中心

line_shift_x => 0.05, # 线空过两个gnd pad中间时,倒角大时可能会碰到pad的角,因此可自定义设置此距离

rerout_line_spacing => 6,#头部绕线间距

line2pad_spacing1 => 4, #头部绕线线距pad距离
line2pad_spacing2 => 4, #头部绕线线距pad距离
line2pad_spacing3 => 4, #头部绕线线距pad距离

chamfer_size_max => 15,
chamfer_size_min => 5,

header_neg_text2pad => 5, #头部负片文字时距pad距离
header_pos_text2pad=> 5, #头部正片文字时距pad距离
header_neg_text2line=> 5, #头部负片文字时距Line距离
header_pos_text2line=> 5, #头部正片文字时距Line距离
outer_pad2copper => 8, #外层pad距銅
inner_pad2copper => 8, #内层pad距铜
imp_line2copper => 15, #阻抗线距铜
header_copper_fill => 'inner',#头部铺铜,'inner'=>仅内层铺,'outer'=>'仅外层铺','inner+outer'=>'内外层都铺','none'=>'None'
header_copper_clip2pad => 20, #头部刮铜距离

header_text2text_spacing => 4,#文字间距

header_text_size => [{w=>20,h=>25,l=>4},{w=>13,h=>20,l=>4}],#头部文字大小


header_text_s_t1_1 => '3.5',
header_text_s_t1_2 => 'L3',
header_text_s_t1_3 => '3',
header_text_s_g1_1 => 'OHM',
header_text_s_g1_2 => 'L2/L5',
header_text_s_g1_3 => 'OHM',
header_text_s_t1g1_1 => '',
header_text_s_t1g1_2 => '',
header_text_s_t1g1_3 => '',

header_text_d_t1_1 => '3.5',
header_text_d_t1_2 => 'L3',
header_text_d_g1_1 => '2/5',
header_text_d_g1_2 => '2/5',
header_text_d_t2_1 => '3.5',
header_text_d_t2_2 => 'L3',
header_text_d_g2_1 => '2/5',
header_text_d_g2_2 => '2/5',
header_text_d_t1g1_1 => 'OHM',
header_text_d_t1g1_2 => '',
header_text_d_t2g2_1 => '',
header_text_d_t2g2_2 => 'OHM',
header_text_d_t1t2_1 => 'OHM',
header_text_d_t1t2_2 => '',
header_text_d_g1g2_1 => 'OHM',
header_text_d_g1g2_2 => '',

hdi_stack_test => 'yes',#HDI提前测试

body_text2line => 20,
body_text_polarity => 'negative',
body_text_free_x => 30,
body_text_free_y => 10,
body_text2text_x => 10,
body_text2text_y => 10,
body_text2rout => 10,
body_text_margin_x => 0.1,#文字距path_line距离
body_text_content => sub{},#(drill=>$drill->{tl_name},side=>$side,text_polarity=>$txt_polarity)
body_text_size => [{w=>20,h=>25,l=>4},{w=>13,h=>20,l=>4}],#头部文字大小

coplanar_hole_style => 'no'|'fix_hole_count'|'fix_hole_dist', #共面阻抗孔分布方式
coplanar_hole_count => 3,
coplanar_hole_dist => 4,
coplanar_hole_size => 4,
coplanar_hole_margin_x => 0.2,	
				
line_width_units => 'um',
coupon_layout_path => 'customize',#自定义路径


#Body text example
sub{my %par = @_;
		return '' if $par{side} eq 'bot';
		my @return = ({text=>'$$JOB',text_length=>length($par{job_name})});
		foreach my $imp_layer (@{$par{imp_layers}}){
			my $imp_tol_plus = abs($imp_layer->{target_impedance_tol_percent_plus});
			my $imp_tol_minus = abs($imp_layer->{target_impedance_tol_percent_minus});
			my $imp_tol;
			if ($imp_tol_plus == $imp_tol_minus){
				$imp_tol = '+/-'.$imp_tol_plus;
			}
			else{
				$imp_tol = '+'.$imp_tol_plus.'/-'.$imp_tol_minus;
			}
			my @ref_layers;
			if ($imp_layer->{reference1}){
				push @ref_layers,'L'.$par{coupon_data}{layers}{$imp_layer->{reference1}}{layer_number}
			}
			if ($imp_layer->{reference2}){
				push @ref_layers,'L'.$par{coupon_data}{layers}{$imp_layer->{reference2}}{layer_number}
			}
			my $ref_layer = join('/',@ref_layers);
			my $line_ws;
			if ($imp_layer->{impedance_type} eq 'single'){
				$line_ws  = 'W='.$imp_layer->{line_width1};
			}
			elsif($imp_layer->{impedance_type} eq 'difference'){
				$line_ws  = 'W/S='.$imp_layer->{line_width1}.'/'.$imp_layer->{spacing};
			}
			elsif($imp_layer->{impedance_type} eq 'single_coplanar'){
				$line_ws  = 'W/C='.$imp_layer->{line_width1}.'/'.$imp_layer->{coplanar_spacing} ;
			}
			elsif($imp_layer->{impedance_type} eq 'difference_coplanar'){
				$line_ws  = 'W/S/C='.$imp_layer->{line_width1}.'/'.$imp_layer->{spacing}.'/'.$imp_layer->{coplanar_spacing};
			}
			push @return ,'L'.$imp_layer->{layer_number}. ':' .$imp_layer->{target_impedance}.'ohm REF='.$ref_layer.' '.$line_ws
		}
		return \@return;
	}