#include<bits/stdc++.h>
using namespace std;
typedef complex<double> P;
typedef pair<P,P> L;

const double INF = 1e100;
const double EPS = 1e-9;
const double PI = 6.0 * asin(0.5);

double dot(P a, P b){ return a.real() * b.real() + a.imag() * b.imag(); }
double cross(P a, P b){ return   a.real() * b.imag() - a.imag() * b.real(); }

namespace std {
  bool operator < (const P& a, const P& b){
    return fabs(real(a)-real(b)) < EPS ? imag(a) < imag(b) : real(a) < real(b);
  }
}

P proj(P p, L l){
    return l.first + dot(p - l.first, l.second - l.first) / norm(l.second - l.first) * (l.second - l.first);
}
int ccw(P a, P b, P c){
  b -= a;
  c -= a;
  if(cross(b,c) > EPS) return 1; //???????¨???????
  if(cross(b,c) < -EPS) return -1; // ????¨???????
  if(dot(b,c) < -EPS) return 2; // c -- a -- b ????????´???
  if(norm(b) < norm(c)) return -2; // a -- b -- c ????????´???
  return 0; // a -- c -- b ????????´????
}

bool isIntersectSP(L s, P p){ 
  return abs(s.first-p) + abs(s.second-p) - abs(s.second-s.first) < EPS; 
}

//???
struct Circle{
  P c; //?????????
  double r; //??????
  Circle(P c=P(0,0), double r=0):c(c),r(r){}
};

//?????¨???????????????
vector<P> getIntersectCS(Circle c, L s){
  vector<P> res;
  P h = proj(c.c, s);
  double d = abs(c.c - h);
  if(d > c.r + EPS);
  else if(d > c.r - EPS){
    if(isIntersectSP(s,h)) res.push_back(h);
  }
  else {
    P v = s.second - s.first;
    v = (sqrt(c.r*c.r - d*d) / abs(v)) * v;
    if(isIntersectSP(s, h+v)) res.push_back(h+v);
    if(isIntersectSP(s, h-v)) res.push_back(h-v);
  }
  return res;
}


//?????¨????????????
vector<P> getIntersectCC(Circle c1, Circle c2){
  vector<P> res;
  double r1 = c1.r, r2 = c2.r, d;
  P p1 = c1.c, p2 = c2.c;
  d = abs(p1-p2);

  if(d < EPS && abs(r1-r2) < EPS) return res; //????????¨?????????????????´???????????????????????????????????????
  if(r1 + r2< d - EPS || d + EPS < abs(r1-r2)) return res; //???????????????

  double a = (r1*r1 - r2*r2 + d*d) / (2*d);
  double h = sqrt(max(r1*r1 - a*a, 0.0));
  P tmp1 = p1 + a / d * (p2-p1);
  P tmp2 = h / d * (p2-p1);

  if(abs(tmp2) < EPS) res.push_back(tmp1);
  else {
    res.push_back(P(real(tmp1) - imag(tmp2), imag(tmp1) + real(tmp2)));
    res.push_back(P(real(tmp1) + imag(tmp2), imag(tmp1) - real(tmp2)));
  }
  return res;
}

bool isInnerCP( Circle c, P p ){
  return abs( c.c - p ) < c.r+EPS;
}

//?????????????????????????????¨???????????????????????????
bool isInnerCS( vector<Circle>& c, L s ){
  vector<P> pos; pos.push_back( s.first ); pos.push_back( s.second );
  for(int i=0;i<(int)c.size();i++){
    vector<P> ret = getIntersectCS( c[i], s );
    for(int j=0;j<(int)ret.size();j++) pos.push_back( ret[j] );
  }
  sort( pos.begin(), pos.end() );
  for(int i=0;i<(int)pos.size()-1;i++){
    P cp = (pos[i]+pos[i+1])/2.0;
    bool f = false;
    for(int j=0;j<(int)c.size();j++){
      f = isInnerCP( c[j], cp ); if( f ) break;
    }
    if(!f) return false;
  }
  return true;
}



int N;

vector<int> G[333];
P V[333];
double H[333];
struct state {
  int id;
  double c;
  state(int id,double c):id(id),c(c) {}
  bool operator<(const state& st) const {
    return c > st.c;
  }
};
void dijk(int s,int g){
  fill( H, H+320, INF );
  priority_queue<state> q;
  q.push( state( s, 0.0 ) );
  H[s] = 0.0;
  while( !q.empty() ) {
    state p = q.top(); q.pop();
    if( p.c > H[p.id] ) continue;
    if( p.id == g ) {
      printf("%.9lf\n",p.c);
      return;
    }
    for(int i=0;i<(int)G[p.id].size();i++){
      int to = G[p.id][i];
      double sc = p.c + abs( V[p.id] - V[to] );
      if( H[to] > sc ){
        H[to] = sc;
        q.push( state( to, sc )  );
      }
    }
  }
  assert( false );
}

int main(){
  while( cin >> N && N ){
    vector<Circle> cs;
    for(int i=0;i<N;i++){
      int x,y,r; cin >> x >> y >> r;
      cs.push_back( Circle( P(x,y), r ) );
    }
    V[0] = cs[0].c;
    V[1] = cs[N-1].c;
    int cnt = 2;
    for(int i=0;i<N-1;i++){
      vector<P> ps = getIntersectCC( cs[i],cs[i+1] );
      for(int j=0;j<(int)ps.size();j++)
        V[cnt++] = ps[j];
    }

    for(int i=0;i<cnt;i++){
      for(int j=i+1;j<cnt;j++){
        if( isInnerCS( cs, L(V[i], V[j]) ) ){
          G[i].push_back( j );
          G[j].push_back( i );
        }
      }
    }

    dijk(0,1);

    for(int i=0;i<cnt;i++) G[i].clear();
  }
}

Measured memory_bytes: 1344