#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <cfloat>
#include <map>
#include <utility>
#include <set>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
#include <sstream>
#include <complex>
#include <stack>
#include <queue>
using namespace std;
static const double EPS = 1e-10;
typedef long long ll;
#define rep(i,n) for(int i=0;i<n;i++)
#define rev(i,n) for(int i=n-1;i>=0;i--)
#define sz(a) a.size()
#define all(a) a.begin(),a.end()
#define mp(a,b) make_pair(a,b)
#define pb(a) push_back(a)
#define SS stringstream
#define DBG1(a) rep(_X,sz(a)){printf("%d ",a[_X]);}puts("");
#define DBG2(a) rep(_X,sz(a)){rep(_Y,sz(a[_X]))printf("%d ",a[_X][_Y]);puts("");}
#define bitcount(b) __builtin_popcount(b)
#define REP(i, s, e) for ( int i = s; i <= e; i++ )  
 
const double INF = 1e12;
typedef complex<double> P,point;
typedef vector<P> G,polygon;
namespace std {bool operator < (const P& a, const P& b) {return real(a) != real(b) ? real(a) < real(b) : imag(a) < imag(b);} }
double cross(const P& a, const P& b) {return imag(conj(a)*b);}
double dot(const P& a, const P& b) {return real(conj(a)*b);}
struct L : public vector<P> {L(const P &a, const P &b) {push_back(a); push_back(b);} };
struct C {P p; double r;C(const P &p, double r) : p(p), r(r) { } C(){} };
int ccw(P a, P b, P c) {
	b -= a; c -= a;
	if (cross(b, c) > 0)   return +1;
	if (cross(b, c) < 0)   return -1;
	if (dot(b, c) < 0) return +2;
	if (norm(b) < norm(c)) return -2;
	return 0;
}
 
// Check Funcs //
bool intersectLL(const L &l, const L &m) {return abs(cross(l[1]-l[0], m[1]-m[0])) > EPS || abs(cross(l[1]-l[0], m[0]-l[0])) < EPS;}
bool intersectLS(const L &l, const L &s) {return cross(l[1]-l[0], s[0]-l[0])*cross(l[1]-l[0], s[1]-l[0]) < EPS;}
bool intersectLP(const L &l, const P &p) {return abs(cross(l[1]-p, l[0]-p)) < EPS;}
bool intersectSS(const L &s, const L &t) {return ccw(s[0],s[1],t[0])*ccw(s[0],s[1],t[1]) <= 0 && ccw(t[0],t[1],s[0])*ccw(t[0],t[1],s[1]) <= 0;}
bool intersectSP(const L &s, const P &p) {return abs(s[0]-p)+abs(s[1]-p)-abs(s[1]-s[0]) < EPS;}
 
P projection(const L &l, const P &p) {double t = dot(p-l[0], l[0]-l[1]) / norm(l[0]-l[1]);return l[0] + t*(l[0]-l[1]);}
P reflection(const L &l, const P &p) {return p + 2.0 * (projection(l, p) - p);}
 
double distanceLP(const L &l, const P &p) {
	return abs(p - projection(l, p));
}
double distanceLL(const L &l, const L &m) {
	return intersectLL(l, m) ? 0 : distanceLP(l, m[0]);
}
double distanceLS(const L &l, const L &s) {
	if (intersectLS(l, s)) return 0;
	return min(distanceLP(l, s[0]), distanceLP(l, s[1]));
}
double distanceSP(const L &s, const P &p) {
	const P r = projection(s, p);
	if (intersectSP(s, r)) return abs(r - p);
	return min(abs(s[0] - p), abs(s[1] - p));
}
double distanceSS(const L &s, const L &t) {
	if (intersectSS(s, t)) return 0;
	return min(min(distanceSP(s, t[0]), distanceSP(s, t[1])),min(distanceSP(t, s[0]), distanceSP(t, s[1])));
}
 
P crosspoint(const L &l, const L &m) {
	double A = cross(l[1] - l[0], m[1] - m[0]);
	double B = cross(l[1] - l[0], l[1] - m[0]);
	if (abs(A) < EPS && abs(B) < EPS) return m[0];
	return m[0] + B / A * (m[1] - m[0]);
}
 
vector<P> C_cp(C a,C b){
	vector<P> ret;
	double L = abs(a.p-b.p);
 
	/* anma dekitenai */
	if(	L-a.r-b.r > EPS || (abs(a.p-b.p)<EPS && fabs(a.r-b.r)<EPS) || 
		abs(a.p-b.p) < abs(a.r-b.r)
	)return ret;
 
	double theta = atan2(b.p.imag()-a.p.imag(),b.p.real()-a.p.real());
	double c = (L*L+a.r*a.r-b.r*b.r)/(2*L*a.r);
	ret.push_back(
		P(a.p.real()+a.r*cos(theta+acos(c)),
		  a.p.imag()+a.r*sin(theta+acos(c)))
	);
	if(fabs(L-a.r-b.r) > EPS)
		ret.push_back(
			P(a.p.real()+a.r*cos(theta-acos(c)),
			  a.p.imag()+a.r*sin(theta-acos(c)))
		);
	return ret;
}

int main(){
	int n;
	while(cin >> n && n){
		C c[100];
		for(int i = 0 ; i < n ; i++){
			cin >> c[i].p.real() >> c[i].p.imag() >> c[i].r;
		}
		vector<P> cp[200];
		for(int i = 0 ; i < n - 1 ; i++)
			cp[i] = C_cp(c[i],c[i+1]);
	
		vector<P> p;
		p.push_back(c[0].p);
		p.push_back(c[n-1].p);
		for(int i = 0 ; i < n-1 ; i++){
			vector<P> a = C_cp(c[i],c[i+1]);
			for(int k = 0 ; k < a.size() ; k++)
				p.push_back(a[k]);
		}
		priority_queue< pair<double,int> , vector< pair<double,int> > , greater< pair<double,int> > > Q;
		vector<int> done(p.size(),0);
		Q.push(make_pair(0,0));
		while(Q.size()){
			int qi = Q.top().second;
			double qcost = Q.top().first;
			Q.pop();
			if( done[qi]++) continue;
			//cout << qi << p[qi] << " " << -qcost << endl;
			if( qi == 1 ){
				printf("%.10lf\n",qcost);
				break;
			}
			for(int i = 0 ; i < p.size() ; i++){
				if( !done[i] ){
					int minIdx = 1e9;
					int maxIdx = -1e9;
					for(int k = 0 ; k < n ; k++){
						if( distanceSP(L(p[i],p[qi]),c[k].p) < c[k].r + EPS ){
							minIdx = min(minIdx,k);
							maxIdx = max(maxIdx,k);
						}
					}
					int flag = true;
					for(int k = minIdx ; k < maxIdx ; k++){
						vector<P> a = cp[k];
						if( !intersectSS(L(p[i],p[qi]),L(a[0],a[1])) || distanceSP(L(a[0],a[1]), crosspoint(L(p[i],p[qi]),L(a[0],a[1]) ) ) > EPS || distanceSP(L(p[i],p[qi]), crosspoint(L(p[i],p[qi]),L(a[0],a[1]) ) ) > EPS ){
							flag = false;
							break;
						}
					}
					
					if( flag ) {
						Q.push(make_pair(qcost+abs(p[i]-p[qi]),i));
					}
				}
			}
		}
	}
}

Measured memory_bytes: 1448