#include <complex>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <utility>
#include <queue>
#include <vector>
#include <cassert>
#include <algorithm>
using namespace std;

#define X real()
#define Y imag()

#define fi first
#define se second

// ----- debug macro -----
#define DMPPT(p) (p).X << "," << (p).Y
// ----- ----- ----- -----

// ????????????????§???????????????????????????\??????????????????????????¨???????????????
typedef complex<double> PT;
typedef complex<double> VC;
// ??´?????????????????????????§?????????????????????\??§??\??????typedef
typedef pair<PT,PT> LN;
typedef pair<PT,PT> SG;
// ????????????????§????
typedef pair<PT,double> CIR;

// paramater
const double EPS = 1e-8;

double dot_prod(VC a, VC b) {
	return a.X * b.X + a.Y * b.Y;
}
double cross_prod(VC a, VC b) {
	return a.X * b.Y - a.Y * b.X;
}

// ??´??? or ???????????????????????????????????????
VC get_vc_normal(VC a) {
	a = a * PT(0,-1);
	return a / abs(a);
}

// ??±?????\???(common tangent) ?????????????????????????????¨?????????????????§??¨???(???????????\????????????)
vector<LN> get_comm_tan(CIR c0, CIR c1) {
	vector<LN> res;
	VC v, n;
	double d;
	double theta, phi, psi;
	const int sign[] = {1,-1};

	if(c0.se > c1.se)
		swap(c0,c1);
	v = c1.fi-c0.fi;
	n = get_vc_normal(v);
	d = abs(v);
	theta = arg(v);
	// ??±?????\?????????
	if(d < abs(c1.se-c0.se) + EPS)
		return res;
	// ??±?????\??? (2???????????§??????)
	if(abs(abs(c1.se-c0.se)-d) < EPS || d < abs(c1.se-c0.se)) {
		// ????????\??????
		assert(abs(c1.se-c0.se) > EPS);
		v *= c0.se / (c1.se - c0.se);
		res.push_back(LN(c0.fi + v, c0.fi + v + n));
	}
	else {
		phi = asin((c1.se-c0.se)/d) + M_PI / 2.0;
		for(int i = 0; i < 2; i++) {
			psi = theta + sign[i] * phi;
			res.push_back(SG(c0.fi + polar(c0.se, psi),
					c1.fi + polar(c1.se, psi)));
		}
	}
	// ??±?????\??? (2???????????§??????)
	if(abs(c0.se + c1.se - d) < EPS) {
		// ????????\??????
		v *= c0.se / (c0.se + c1.se);
		res.push_back(LN(c0.fi + v, c0.fi + v + n));
	}
	else if(c0.se + c1.se < d) {
		phi = acos((c0.se+c1.se)/d);
		for(int i = 0; i < 2; i++) {
			psi = theta + sign[i] * phi;
			res.push_back(SG(c0.fi + polar(c0.se, psi),
						c1.fi + polar(c1.se, psi + M_PI)));
		}
	}
	return res;
}

double dist_pts_line(PT p, LN l) {
	VC a, b;
	a = l.fi - p;
	b = l.se - p;
	return abs(cross_prod(a,b)) / abs(l.fi-l.se);
}

vector<PT> intersec_line_line(LN a, LN b) {
	vector<PT> res;
	if(abs(cross_prod(a.fi-a.se, b.fi-b.se)) < EPS)
		return res;
	if(abs(cross_prod(b.fi-a.fi, b.se-a.fi)) < EPS)
		swap(a.fi, a.se);

	VC va, vb, vc;
	double s, t;
	va = b.fi-a.fi;
	vb = b.se-a.fi;
	vc = a.se-a.fi;
	s = cross_prod(va,vc) / cross_prod(va,vb);
	t = cross_prod(vc,vb) / cross_prod(va,vb);
	res.push_back(a.fi + (t * va + s * vb) / (s+t));
	return res;
}

bool on_seg(PT p, SG a) {
	VC b, c;
	b = a.fi - p;
	c = a.se - p;
	return dot_prod(b,c) < EPS && abs(cross_prod(b,c)) < EPS;
}

vector<PT> intersec_seg_seg(SG a, SG b) {
	vector<PT> tmp, res;
	tmp = intersec_line_line(a,b);
	for(int i = 0; i < tmp.size(); i++) {
		if(on_seg(tmp[i],a) && on_seg(tmp[i],b))
			res.push_back(tmp[i]);
	}
	return res;
}

vector<PT> intersec_circle_circle(CIR a, CIR b) {
	vector<PT> res;
	double d = abs(a.fi-b.fi);
	if(abs(a.se+b.se-d) < EPS || abs(abs(a.se-b.se)-d) < EPS) {
		res.push_back(a.fi + polar(a.se, arg(b.fi-a.fi)));
		return res;
	}
	if(a.se+b.se < d || d < abs(a.se-b.se))
		return res;
	double theta;
	double aa, bb, cc;
	aa = b.se;
	bb = a.se;
	cc = d;
	// ????????????
	theta = acos((bb*bb + cc*cc - aa*aa)/ (2.0*bb*cc));
	for(int i = 0; i < 2; i++) {
		res.push_back(a.fi + polar(a.se, theta + arg(b.fi-a.fi)));
		theta *= -1;
	}
	return res;
}

// test code
#define rep(i,n) for(int i = 0; i < n; i++)
#define rrep(i,n) for(int i = n-1; i >= 0; i--)

typedef pair<double,int> pdi;

int main() {
	int n;
	while(cin >> n, n) {
		vector<CIR> v(n);
		rep(i,n) {
			int a, b, r;
			cin >> a >> b >> r;
			v[i] = CIR(PT(a,b),r);
		}
		vector<vector<pair<PT,int> > > p; // pt, id;
		vector<vector<pdi> > edge; // to, cost
		vector<SG> sg;

		int id_cnt = 0;
		rep(i,n) {
			vector<PT> tmp;
			vector<pair<PT,int> > push_v;
			if(i) {
				tmp = intersec_circle_circle(v[i],v[i-1]);
				sg.push_back(SG(tmp[0],tmp[1]));
				assert(abs(tmp[0]-tmp[1]) > EPS);
			}
			tmp.push_back(v[i].fi);
			rep(j,tmp.size()) {
				push_v.push_back(pair<PT,int>(tmp[j],id_cnt++));
			}
			p.push_back(push_v);
		}
		edge.resize(id_cnt);
		rep(i,p.size()) {
			// ?§????
			rep(j,p[i].size()) {
				// ??????
				rep(k,p[i].size()) {
					if(j == k)
						continue;
					edge[p[i][j].se].push_back(pdi(abs(p[i][k].fi-p[i][j].fi), p[i][k].se));
				}
				// ??????
				rrep(k,i) {
					// ??????
					rep(l,p[k].size()) {
						SG a(p[i][j].fi, p[k][l].fi);
						bool flg = true;
						for(int m = k; m < i; m++) {
							if(intersec_seg_seg(a,sg[m]).size() == 0)
								flg = false;
						}
						if(flg) {
							double dist = abs(p[i][j].fi-p[k][l].fi);
							edge[p[i][j].se].push_back(pdi(dist, p[k][l].se));
							edge[p[k][l].se].push_back(pdi(dist, p[i][j].se));
						}
					}
				}
			}
		}
		/*
		rep(i,edge.size()) {
			rep(j,edge[i].size())
				cout << edge[i][j].fi << ":" << edge[i][j].se << " ";
			cout << endl;
		}
		rep(i,p.size()) {
			rep(j,p[i].size())
				cout << DMPPT(p[i][j].fi) << " ";
			cout << endl;
		}
		// */

		priority_queue<pdi> q;
		vector<double> mincost(id_cnt, 1e9);
		mincost[0] = 0;
		q.push(pdi(0,0));
		while(q.size()) {
			pdi a = q.top(); q.pop();
			if(mincost[a.se] < -a.fi)
				continue;
			rep(i,edge[a.se].size()) {
				pdi b;
				b = edge[a.se][i];
				if(mincost[b.se] > -a.fi + b.fi) {
					mincost[b.se] = -a.fi + b.fi;
					q.push(pdi(a.fi-b.fi, b.se));
				}
			}
		}
		/*
		for(int i = 0; i < mincost.size(); i++)
			cout << mincost[i] << " ";
		cout << endl;
		// */
		printf("%.10lf\n", mincost[id_cnt-1]);
	}
}

Measured memory_bytes: 1820