#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <set>
#include <complex>
#include <map>
#include <iomanip>

#define FOR(i, a, b) for(int i=(int)a; i < (int)b; ++i)
#define REP(i, n) FOR(i,0,n)
#define RFOR(i, a, b) for(int i=(int)b-1; i >= (int)a; --i)
#define RREP(i, n) RFOR(i,0,n)
#define IN(a,x,b) (a<=x && x < b)
template<class T> inline void chmax(T& a, const T& b){if(a<b) a = b;}
template<class T> inline void chmin(T& a, const T& b){if(a>b) a = b;}

using namespace std;
using ll = long long;
template<class T> using V = std::vector<T>;
template<class T> using VV = V<V<T>>;

//---------------------幾何ここから----------------------
using Real = double;
using Point = std::complex<Real>;
const Real EPS = 1e-8, PI = std::acos(-1);

inline bool eq(Real a, Real b) { return std::fabs(b - a) < EPS; }
Point operator*(const Point& p, const Real & d) {
  return Point(real(p) * d, imag(p) * d);
};
Real distance(Point& a, Point& b) {
  return abs(a - b);
}

struct Line {
  Point a, b;
  Line() = default;
  Line(Point a, Point b) : a(a), b(b) {}
  Line(Real A, Real B, Real C) {
    if(eq(A, 0)) a = Point(0, C / B), b = Point(1, C / B);
    else if(eq(B, 0)) b = Point(C / A, 0), b = Point(C / A, 1);
    else a = Point(0, C / B), b = Point(C / A, 0);
  }
};

struct Segment : Line {
  Segment() = default;
  Segment(Point a, Point b) : Line(a, b) {}
};

struct Circle{
  Point p;
  Real r;
  Circle() = default;
  Circle(Point p, Real r) : p(p), r(r) {}
};

Real cross(const Point& a, const Point& b) {
  return real(a) * imag(b) - imag(a) * real(b);
}
Real dot(const Point& a, const Point& b) {
  return real(a) * real(b) + imag(a) * imag(b);
}

int ccw(const Point& a, Point b, Point c) {
  b = b - a, c = c - a;
  if(cross(b, c) > EPS) return +1;  // COUNTER_CLOCKWISE
  if(cross(b, c) < -EPS) return -1; // CLOCKWISE
  if(dot(b, c) < 0) return +2;      // ONLINE_BACK
  if(norm(b) < norm(c)) return -2;  // ONLINE_FRONT
  return 0;                         // ON_SEGMENT
}

bool intersect(const Segment& s, const Segment& t) {
  return ccw(s.a, s.b, t.a) * ccw(s.a, s.b, t.b) <= 0 &&
    ccw(t.a, t.b, s.a) * ccw(t.a, t.b, s.b) <= 0; // ?
}

pair<Point, Point> crosspoint(const Circle& c1, const Circle& c2) {
  Real d = abs(c1.p - c2.p);
  Real a = acos((c1.r * c1.r + d * d - c2.r * c2.r) / (2 * c1.r * d));
  Real t = atan2(c2.p.imag() - c1.p.imag(), c2.p.real() - c1.p.real());
  Point p1 = c1.p + Point(cos(t + a) * c1.r, sin(t + a) * c1.r);
  Point p2 = c1.p + Point(cos(t - a) * c1.r, sin(t - a) * c1.r);
  return {p1, p2};
}

//---------------------幾何ここまで----------------------

Real dist[202][202];

signed main(){
  int n;
  while(std::cin >> n, n) {
    V<int> x(n), y(n), r(n);
    REP(i, n) std::cin >> x[i] >> y[i] >> r[i];

    V<Point> points;
    points.emplace_back(x[0], y[0]);
    REP(i, n-1) {
      Circle c1(Point(x[i], y[i]), r[i]);
      Circle c2(Point(x[i + 1], y[i + 1]), r[i + 1]);
      auto ps = crosspoint(c1, c2);
      points.emplace_back(ps.first);
      points.emplace_back(ps.second);
    }
    points.emplace_back(x[n-1], y[n-1]);

    V<Segment> segs;
    for(int i = 1; i < points.size(); i += 2) {
      segs.emplace_back(points[i], points[i + 1]);
    }

    auto check = [&](int i, int j) -> bool {
      Segment s(points[i], points[j]);
      int l = (i + 1) / 2;
      int r = (j - 3) / 2;
      FOR(k, l, r + 1){
        if(!intersect(s, segs[k])) return false;
      }
      return true;
    };

    REP(i, points.size()) REP(j, points.size()) dist[i][j] = 1e100;
    REP(i, points.size()) dist[i][i] = 0;
    REP(i, points.size()) FOR(j, i + 1, points.size()) {
      if(check(i, j)) dist[i][j] = distance(points[i], points[j]);
    }

    REP(k, points.size()) REP(i, points.size()) REP(j, points.size()) {
      chmin(dist[i][j], dist[i][k] + dist[k][j]);
    }

    std::cout << std::fixed << std::setprecision(6) << dist[0][points.size() - 1] << std::endl;
  }

  return 0;
}



Measured memory_bytes: 4104