Problem:
凸多角形柱工業都市
凸多角形柱工業都市 (Industrial Convex Pillar City, ICPC) はいくつかの凸多角形柱の形状をした建物からなる街である．あなたはこの街の中を現在地
S
から目的地
T
に向けて歩こうとしている．今日は日差しが強いので，出来る限りひなたを通らずに目的地へ向かいたい．あなたが立っている地点と太陽を一直線に結んだ線上に建物がある場合，あなたは建物の陰にいるので日差しを受けずにすむ．また，この街の建物の外周には全てひさしが付いているので，建物の外周に沿って歩いている間は，日が当たる縁に沿って歩いても日差しを受けることはない．あなたは建物の内部以外はこの街をどこでも自由に歩くことができる．
もっとも日差しを受けないように現在地から目的地まで歩いた時の，ひなたを歩く距離を出力するプログラムを作成しなさい．
図E1: 第1インプットの場合
図E2: 第2インプットの場合
図E3: 第3インプットの場合
Input
入力は複数のデータセットからなる．
データセットの個数は最大でも 30 個を超えない．
各データセットは次の形式で表される．
N
NV
1
H
1
X
1,1
Y
1,1
X
1,2
Y
1,2
...
X
1,NV
1
Y
1,NV
1
...
NV
N
H
N
X
N,1
Y
N,1
X
N,2
Y
N,2
...
X
N,NV
N
Y
N,NV
N
θ
φ
S
x
S
y
T
x
T
y
１行目の
N
は建物の数を表す．続く
N
行は各建物の形状を指定する．
NV
i
は
i
番目の建物を上から見た多角形の頂点数，
H
i
は
i
番目の建物の高さを表す．
X
i, j
と
Y
i, j
は
i
番目の建物を上から見た多角形の
j
番目の頂点の
x
座標と
y
座標を表す．頂点は反時計回りの順で与えられる．全ての建物は上から見ると凸多角形であり，建物の内部に他の建物があったり，頂点や辺が他の多角形と重なったりすることはない．続く行には太陽の方向を表す
θ
と
φ
が与えられ，
θ
は太陽の向きを
x
の正の方向から反時計回りの角度で表し，
φ
は地平線からの太陽の仰角，すなわち太陽を見上げた時の視線の方向と地表面がなす角度を表す．ただし，太陽は無限遠にあり，移動中には位置を変えないものとする．続く行には現在地と目的地の座標， (
S
x
,
S
y
) と (
T
x
,
T
y
) が与えられる．
入力で与えられる数値は全て整数であり，以下の条件を満たす．
1 ≤ N ≤ 100
3 ≤ NV
i
≤ 12
1 ≤ H
i
≤ 1,
000
0 ≤ θ < 360
0 < φ < 90
座標は全て
-1,
000
以上
1,
000
以下である．
現在地と目的地は異なり，どちらも建物の内部及び外周には存在しない．
入力の終わりは，１つのゼロからなる行で表される．
Output
各データセットについて，ひなたを歩く最短距離を１行に出力せよ．出力には 0.001 を超える絶対誤差があってはならない．
Sample Input
2
4 1 0 0 1 0 1 1 0 1
4 2 2 2 3 2 3 3 2 3
60 45
-1 -1 4 4
4
4 1 0 0 3 1 1 2 0 1
3 2 10 7 8 2 12 4
6 8 7 12 8 13 9 15 10 19 11 24 10 25
5 4 16 2 16 4 12 8 14 2 15 0
167 38
3 3 15 21
12
4 3 -8 -3 -9 -3 -9 -5 -8 -6
4 5 -4 -5 -7 -5 -7 -6 -5 -6
4 2 -4 1 -5 1 -5 -4 -4 -4
4 1 -1 1 -2 1 -2 -4 -1 -3
4 2 2 3 -1 3 -2 2 3 2
4 1 3 1 2 1 2 -3 3 -4
4 7 1 0 0 0 0 -1 1 -1
4 4 9 5 7 5 7 4 10 4
4 3 6 5 5 4 5 0 6 0
4 5 8 -1 5 -1 6 -2 8 -2
4 1 10 0 9 0 9 -2 10 -1
4 6 10 2 8 2 8 1 10 1
131 78
-10 10 10 -10
0
Output for Sample Input
1.93185
7.87174
20.86840

Submitted solutions:

code_snippet: #include "bits/stdc++.h"
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int INF = 1e9;
const ll LINF = 1e18;
template<class S,class T> ostream& operator << (ostream& out,const pair<S,T>& o){ out << "(" << o.first << "," << o.second << ")"; return out; }
template<class T> ostream& operator << (ostream& out,const vector<T> V){ for(int i = 0; i < V.size(); i++){ out << V[i]; if(i!=V.size()-1) out << " ";} return out; }
template<class T> ostream& operator << (ostream& out,const vector<vector<T> > Mat){ for(int i = 0; i < Mat.size(); i++) { if(i != 0) out << endl; out << Mat[i];} return out; }
template<class S,class T> ostream& operator << (ostream& out,const map<S,T> mp){ out << "{ "; for(auto it = mp.begin(); it != mp.end(); it++){ out << it->first << ":" << it->second; if(mp.size()-1 != distance(mp.begin(),it)) out << ", "; } out << " }"; return out; }

/*
 <url:>
 問題文============================================================
 =================================================================
 解説=============================================================
 ================================================================
 */

typedef double ld;
typedef complex<ld> Point;
const ld eps = 1e-9, pi = acos(-1.0);
namespace std {
    bool operator<(const Point &lhs, const Point &rhs) {
        if (lhs.real() < rhs.real() - eps) return true;
        if (lhs.real() > rhs.real() + eps) return false;
        return lhs.imag() < rhs.imag();
    }
}
Point input_point() { ld x, y; scanf("%lf%lf",&x,&y); return Point(x, y); } // 点の入力
bool eq(ld a, ld b) { return (abs(a - b) < eps); } // 誤差つき等号判定
ld dot(Point a, Point b) { return real(conj(a) * b); } // 内積
ld cross(Point a, Point b) { return imag(conj(a) * b); } // 外積

// 直線の定義
class Line {
public:
    Point a, b;
    Line() : a(Point(0, 0)), b(Point(0, 0)) {}
    Line(Point a, Point b) : a(a), b(b) {}
    Point operator[](const int _num) {
        if (_num == 0)return a;
        else if (_num == 1)return b;
        else assert(false);
    }
};

// 円の定義
class Circle {
public:
    Point p;
    ld r;
    Circle() : p(Point(0, 0)), r(0) {}
    Circle(Point p, ld r) : p(p), r(r) {}
};

// 垂線の足
Point proj(Line l, Point p) {
    ld t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
    return l.a + t * (l.a - l.b);
}

// CCW
int ccw(Point a, Point b, Point c) {
    b -= a; c -= a;
    if (cross(b, c) > eps) return 1;   // a,b,cが反時計周りの順に並ぶ
    if (cross(b, c) < -eps) return -1; // a,b,cが時計周りの順に並ぶ
    if (dot(b, c) < 0) return 2;       // c,a,bの順に直線に並ぶ
    if (norm(b) < norm(c)) return -2;  // a,b,cの順に直線に並ぶ
    return 0;                          // a,c,bの順に直線に並ぶ
}

/* 交差判定 */
// 直線と直線の交差判定
bool isis_ll(Line l, Line m) { return !eq(cross(l.b - l.a, m.b - m.a), 0); }
// 直線と線分の交差判定
bool isis_ls(Line l, Line s) {
    return isis_ll(l, s) &&
    (cross(l.b - l.a, s.a - l.a) * cross(l.b - l.a, s.b - l.a) < eps);
}
// 線分と線分の交差判定
bool isis_ss(Line s, Line 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;
}
// 点の直線上判定
bool isis_lp(Line l, Point p) { return (abs(cross(l.b - p, l.a - p)) < eps); }
// 点の線分上判定
bool isis_sp(Line s, Point p) { return (abs(s.a - p) + abs(s.b - p) - abs(s.b - s.a) < eps); }

/* 距離 */
// 直線と直線の交点
Point is_ll(Line s, Line t) {
    Point sv = s.b - s.a, tv = t.b - t.a;
    assert(cross(sv, tv) != 0);
    return s.a + sv * cross(tv, t.a - s.a) / cross(tv, sv);
}

// 直線と点の距離
ld dist_lp(Line l, Point p) {
    return abs(p - proj(l, p));
}

// 直線と直線の距離
ld dist_ll(Line l, Line m) {
    return isis_ll(l, m) ? 0 : dist_lp(l, m.a);
}

// 直線と線分の距離
ld dist_ls(Line l, Line s) {
    return isis_ls(l, s) ? 0 : min(dist_lp(l, s.a), dist_lp(l, s.b));
}

// 線分と点の距離
ld dist_sp(Line s, Point p) {
    Point r = proj(s, p);
    return isis_sp(s, r) ? abs(r - p) : min(abs(s.a - p), abs(s.b - p));
}

// 線分と線分の距離
ld dist_ss(Line s, Line t) {
    if (isis_ss(s, t)) return 0;
    return min({ dist_sp(s, t.a), dist_sp(s, t.b), dist_sp(t, s.a), dist_sp(t, s.b) });
}

/* 円 */
// 円と円の交点
vector<Point> is_cc(Circle c1, Circle c2) {
    vector<Point> res;
    ld d = abs(c1.p - c2.p);
    ld rc = (d * d + c1.r * c1.r - c2.r * c2.r) / (2 * d);
    ld dfr = c1.r * c1.r - rc * rc;
    if (abs(dfr) < eps) dfr = 0.0;
    else if (dfr < 0.0) return res; // no intersection
    ld rs = sqrt(dfr);
    Point diff = (c2.p - c1.p) / d;
    res.push_back(c1.p + diff * Point(rc, rs));
    if (dfr != 0.0) res.push_back(c1.p + diff * Point(rc, -rs));
    return res;
}
// 円と直線の交点
vector<Point> is_lc(Circle c, Line l) {
    vector<Point> res;
    ld d = dist_lp(l, c.p);
    if (d < c.r + eps) {
        ld len = (d > c.r) ? 0.0 : sqrt(c.r * c.r - d * d); //safety;
        Point nor = (l.a - l.b) / abs(l.a - l.b);
        res.push_back(proj(l, c.p) + len * nor);
        res.push_back(proj(l, c.p) - len * nor);
    }
    return res;
}
// 円と線分の距離
vector<Point> is_sc(Circle c, Line l) {
    vector<Point> v = is_lc(c, l), res;
    for (Point p : v)
        if (isis_sp(l, p)) res.push_back(p);
    return res;
}
// 円と点の接線
//vector<Line> tangent_cp(Circle c, Point p) {
//    vector<Line> ret;
//    Point v = c.p - p;
//    ld d = abs(v);
//    ld l = sqrt(norm(v) - c.r * c.r);
//    if (isnan(l)) { return ret; }
//    Point v1 = v * Point(l / d, c.r / d);
//    Point v2 = v * Point(l / d, -c.r / d);
//    ret.push_back(Line(p, p + v1));
//    if (l < eps) return ret;
//    ret.push_back(Line(p, p + v2));
//    return ret;
//}
//// 円と円の接線
//vector<Line> tangent_cc(Circle c1, Circle c2) {
//    vector<Line> ret;
//    if (abs(c1.p - c2.p) - (c1.r + c2.r) > -eps) {
//        Point center = (c1.p * c2.r + c2.p * c1.r) / (c1.r + c2.r);
//        ret = tangent_cp(c1, center);
//    }
//    if (abs(c1.r - c2.r) > eps) {
//        Point out = (-c1.p * c2.r + c2.p * c1.r) / (c1.r - c2.r);
//        vector<Line> nret = tangent_cp(c1, out);
//        ret.insert(ret.end(), nret.begin(), nret.end());
//    }
//    else {
//        Point v = c2.p - c1.p;
//        v /= abs(v);
//        Point q1 = c1.p + v * Point(0, 1) * c1.r;
//        Point q2 = c1.p + v * Point(0, -1) * c1.r;
//        ret.push_back(Line(q1, q1 + v));
//        ret.push_back(Line(q2, q2 + v));
//    }
//    return ret;
//}

/* 多角形 */
typedef vector<Point> Polygon;
// 面積
ld area(const Polygon &p) {
    ld res = 0;
    int n = (int)p.size();
    for (int j = 0;j < n;j++) res += cross(p[j], p[(j + 1) % n]);
    return res / 2;
}
// 多角形の回転方向
bool is_counter_clockwise(const Polygon &poly) {
    ld angle = 0;
    int n = (int)poly.size();
    for (int i = 0;i < n;i++) {
        Point a = poly[i], b = poly[(i + 1) % n], c = poly[(i + 2) % n];
        angle += arg((c - b) / (b - a));
    }
    return angle > eps;
}
// 凸性判定
bool isConvex(const Polygon &poly){
    int n = (int)poly.size();
    if(n < 3) return false;
    int s = -3;
    for(int i = 0; i < n;i++){
        int r = ccw(poly[(i+n-1)%n],poly[i%n],poly[(i+1)%n]);
        if(r == 1 && s == -3) s = r;
        if(s*r == -1) return false;
    }
    return true;
}
// 点の内外判定
//  0 => out : 1 => on : 2 => in
int is_in_polygon(const Polygon &poly, Point p) {
    ld angle = 0;
    int n = (int)poly.size();
    for (int i = 0;i < n;i++) {
        Point a = poly[i], b = poly[(i + 1) % n];
        if (isis_sp(Line(a, b), p)) return 1;
        angle += arg((b - p) / (a - p));
    }
    return eq(angle, 0) ? 0 : 2;
}

// 凸包 : 凸多角形のある一辺上にある点を含まない
Polygon convex_hull(vector<Point> ps) {
    int n = (int)ps.size();
    int k = 0;
    sort(ps.begin(), ps.end());
    Polygon ch(2 * n);
    for (int i = 0; i < n; ch[k++] = ps[i++])
        while (k >= 2 && ccw(ch[k - 2], ch[k - 1], ps[i]) <= 0) --k;
    for (int i = n - 2, t = k + 1; i >= 0; ch[k++] = ps[i--])
        while (k >= t && ccw(ch[k - 2], ch[k - 1], ps[i]) <= 0) --k;
    ch.resize(k - 1);
    return ch;
}
// 凸包 : 凸多角形のある一辺上にある点も含む
Polygon convex_hull2(vector<Point> ps) {
    int n = (int)ps.size();
    if (n < 3) return ps;
    sort(ps.begin(), ps.end());
    Polygon u = { ps[0], ps[1] }, l = { ps[n - 1],ps[n - 2] };
    for (int i = 2; i < n; i++) {
        for (int j = (int)u.size(); j >= 2 && ccw(u[j - 2], u[j - 1], ps[i]) >= 0;j--)u.pop_back();
        u.push_back(ps[i]);
    }
    for (int i = n - 3;i >= 0;i--) {
        for (int j = (int)l.size(); j >= 2 && ccw(l[j - 2], l[j - 1], ps[i]) >= 0;j--)l.pop_back();
        l.push_back(ps[i]);
    }
    reverse(l.begin(), l.end());
    for (int i = (int)u.size() - 2; i >= 1; i--)l.push_back(u[i]);
    return l;
}

// 凸多角形の直径
pair<pll,ld> convex_diameter(const Polygon& poly){
    int n = (int)poly.size();
    if(n == 2) return make_pair(pll(0,1),abs(poly[0]-poly[1]));
    int ii = 0, jj = 0;
    for(int i = 1;i < n;i++){
        if(poly[i].imag() > poly[ii].imag())ii = i;
        if(poly[i].imag() < poly[jj].imag())jj = i;
    }
    pll resp = make_pair(-1,-1);
    ld resd = 0;
    
    int i, maxi,j,maxj;
    i = maxi = ii; j = maxj = jj;
    while(i != maxj || j != maxi){
        ld cur = abs(poly[i] - poly[j]);
        if(resd + eps < cur){ resd = cur; resp = pll(i,j); }
        int di = (i+1)%n, dj = (j+1)%n;
        if(cross(poly[di]-poly[i],poly[dj]-poly[j]) < 0) i = di;
        else j = dj;
    }
    return make_pair(resp,resd);
}
// 凸カット
Polygon convex_cut(const Polygon &ps, Line l) {
    int n = (int)ps.size();
    Polygon Q;
    for (int i = 0;i < n;i++) {
        Point A = ps[i], B = ps[(i + 1) % n];
        Line m = Line(A, B);
        if (ccw(l.a, l.b, A) != -1) Q.push_back(A);
        if (ccw(l.a, l.b, A) * ccw(l.a, l.b, B) < 0 && isis_ll(l, m))
            Q.push_back(is_ll(l, m));
    }
    return Q;
}

// 円と多角形の共通部分
double area_of_polygon_and_circle(const Polygon& poly, const Circle c) {
    int n = (int)poly.size();
    ld r = 0;
    for (int i = n - 1, j = 0; j < n; i=j++) {
        Point v = abs(poly[j] - poly[i]) / (poly[j] - poly[i]);
        if (poly[j] == poly[i])continue;
        assert(poly[j] != poly[i]);
        Point a = (poly[i] - c.p)*v, b = (poly[j] - c.p)*v;
        ld d = norm(c.r) - norm(a.imag());
        if (abs(a.imag()) < eps) continue;
        if (d < 0)d = 0;
        d = sqrt(d);
        double l, m;
        r += norm(c.r)*((l = atan2(b.imag(), min(b.real(), -d)) - atan2(a.imag(), min(a.real(), -d)))
                        + (m = atan2(b.imag(), max(b.real(), d)) - atan2(a.imag(), max(a.real(), d))))
        + a.imag()*(min(d, max(a.real(), -d)) - max(-d, min(b.real(), d)));
        assert(-pi < l && -pi < m && l < pi && m < pi);
    }
    return r / 2;
}

int N;
ld G[110][110];
ld Hs[110];
Polygon polys[110];
ld solve(){
    ld res = 0;
    for(int i = 0; i < N;i++) polys[i].clear();
//    vector<Polygon> polys(N);
    for(int i = 0; i < N;i++){
        int n; scanf("%d%lf",&n,&Hs[i]);
        for(int j = 0; j < n;j++){
            polys[i].push_back(input_point());
        }
    }
    double theta,phi; cin >> theta >> phi;
    theta = theta*pi/180; phi = phi*pi/180;
    Point S = input_point(), T = input_point();
    for(int i = 0; i < N;i++){
        int sz = (int)polys[i].size();
        ld L = Hs[i]/tan(phi);
        for(int j = 0; j < sz;j++){
            ld x = polys[i][j].real(), y = polys[i][j].imag();
            polys[i].push_back(Point(x - L*cos(theta),y - L*sin(theta)));
        }
        polys[i] = convex_hull(polys[i]);
    }
    
    fill(*G,*G+110*110,INF);
    for(int i = 0; i < N+2;i++) G[i][i] = 0;
    for(int i = 0; i < N;i++){
        for(int j = i+1; j < N;j++){
            if(i == j) continue;
            ld D = INF;
            for(int k = 0; k < polys[i].size(); k++){
                if(is_in_polygon(polys[j],polys[i][k]) > 0){
                    D = 0;
                    break;
                }
                for(int l = 0; l < polys[j].size();l++){
                    Line l1(polys[i][k],polys[i][(k+1)%polys[i].size()]);
                    Line l2(polys[j][l],polys[j][(l+1)%polys[j].size()]);
                    D = min(D,dist_ss(l1,l2));
                }
            }
            G[i][j] = G[j][i] = D;
        }
    }
    for(int i = 0; i < N;i++){
        if(is_in_polygon(polys[i],S) > 0){
            G[N][i] = G[i][N] = 0;
        }else{
             ld D = INF;
            for(int k = 0; k < polys[i].size(); k++){
                Line l1(polys[i][k],polys[i][(k+1)%polys[i].size()]);
                D = min(D,dist_sp(l1,S));
            }
            G[N][i] = G[i][N] = D;
        }
        
        if(is_in_polygon(polys[i], T) > 0){
            G[N+1][i] = G[i][N+1] = 0;
        }else{
            ld D = INF;
            for(int k = 0; k < polys[i].size(); k++){
                Line l1(polys[i][k],polys[i][(k+1)%polys[i].size()]);
                D = min(D,dist_sp(l1,T));
            }
            G[N+1][i] = G[i][N+1] = D;
        }
    }
    G[N][N+1] = G[N+1][N] = abs(S-T);
    
    for(int i = 0; i < N+2;i++){
        for(int j = 0; j < N+2;j++){
            for(int k = 0; k < N+2;k++){
                G[j][k] = min(G[j][k],G[j][i] + G[i][k]);
            }
        }
    }
    res = G[N][N+1];
    return res;
}
int main(void) {
//    cin.tie(0); ios_base::sync_with_stdio(false);
    while(cin >> N,N){
        cout << fixed << setprecision(12) << solve() << endl;
    }
    return 0;
}
accuracy: 1
time_ms: 2040
memory_kb: 4208
final_rank: 13

code_snippet: #include <bits/stdc++.h>
#include <unordered_map>
#include <stdlib.h>
using namespace std;
#define rep(i, a, n) for(ll i = a; i < n; i++)
#define rrep(i, a, n) for(ll i = a; i >= n; i--)
#define ll long long
#define ld long double
#define pii pair<int, int>
#define pll pair<ll, ll>
#define all(x) (x).begin(), (x).end()
//constexpr ll MOD = 1000000007;
constexpr ll MOD = 998244353;
constexpr int IINF = 1001001001;
constexpr ll INF = 1LL<<60;
template<class t,class u> void chmax(t&a,u b){if(a<b)a=b;}
template<class t,class u> void chmin(t&a,u b){if(b<a)a=b;}


ll gcd(ll a, ll b){
    if(a%b == 0){
      return b;
    }else{
      return gcd(b, a%b);
    }
}

ll lcm(ll a, ll b){
    return a*b / gcd(a, b);
}

ll powMod(ll x, ll n) {
    if (n == 0) return 1 % MOD;
    ll val = powMod(x, n / 2);
    val *= val;
    val %= MOD;
    if (n % 2 == 1) val *= x;
    return val % MOD;
}


/*
前提
- 点(位置ベクトル)を複素数型で扱う
- 実軸(real)をx軸、虚軸(imag)をy軸として見る
- 比較するときは、計算誤差を意識して EPS で判定 (equal関数)
*/

namespace geometry {
    using D = long double;
    using Point = std::complex<D>;
    const D EPS = 2e-10;
    const D PI = M_PI;

    // 入出力ストリーム
    istream &operator>>(istream &is, Point &p) {
        D a, b;
        is >> a >> b;
        p = Point(a, b);
        return is;
    }
    ostream &operator<<(ostream &os, Point &p) {
        return os << fixed << setprecision(20) << p.real() << " " << p.imag();
    }

    // d 倍する
    Point add(Point a, Point b) {
    return Point(a.real() + b.real(), a.imag() + b.imag());
    }

    // 偏角（0 <= Θ < 2π）
    D argument(Point p) {
        D res = arg(p);
        if(res < 0.0) res += 2.0*PI; // [-π, π] -> [0, 2π)
        return res;
    }

    // d 倍する
    Point operator*(Point p, D d) {
    return Point(p.real() * d, p.imag() * d);
    }
    Point operator/(Point p, D d) {
    return Point(p.real() / d, p.imag() / d);
    }
    // 等しいかどうか（誤差で判定）
    inline bool equal(D a, D b) { return fabs(a - b) < EPS; }

    // 単位ベクトル
    Point unit_vector(Point a) { return a / abs(a); };

    // 法線ベクトル (逆向きがよければ (0, -1) をかける)
    Point normal_vector(Point a, D dir=1) { return a * Point(0, dir); }

    // 内積：a・b = |a||b|cosΘ
    D dot(Point a, Point b){
        return (a.real() * b.real() + a.imag() * b.imag());
    }

    // 外積：a×b = |a||b|sinΘ (外積の大きさではないか？)
    D cross(Point a, Point b){
        return (a.real() * b.imag() - a.imag() * b.real());
    }

    // 反時計回りに theta 回転
    Point rotate(Point a, D theta) {
        D c = cos(theta), s = sin(theta);
        return Point(c * a.real() - s * a.imag(), s * a.real() + c * a.imag());
    }

    // 直線 
    struct Line {
        Point a, b;
        Line() = default;
        Line(Point a_, Point b_) : a(a_), b(b_) { assert(a_ != b_); };
        // Ax+By=C
        Line(D A, D B, D C){
            if(equal(A, 0)){
                a = Point(0, C/B), b = Point(1, C/B);
            }else if(equal(B, 0)){
                b = Point(C/A, 0), a = Point(C/A, 1);
            }else{
                a = Point(0, C/B), b = Point(C/A, 0);
            }
        }
    };

    // 線分（Line と同じ）
    struct Segment : Line {
        Segment() = default;
        Segment(Point a_, Point b_) : Line(a_, b_) {};
    };

    // 円（中心と半径）
    struct Circle {
        Point p;
        D r;
        Circle() = default;
        Circle(Point p_, D r_) : p(p_), r(r_) {}
    };

    // 射影：直線（線分）に 点p から引いた垂線の足を求める
    Point projection(Line l, Point p){
        D t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
        return l.a + (l.a - l.b) * t;
    }
    Point projection(Segment l, Point p){
        D t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
        return l.a + (l.a - l.b) * t;
    }

    // 反射：直線を対象軸として 点p と線対称の位置にある点を求める
    Point reflection(Line l, Point p){
        return p + (projection(l, p)-p) * 2.0;
    }

    // 3点 a, b, c の位置関係
    // reference: https://sen-comp.hatenablog.com/entry/2020/03/12/145742#iSP3%E7%82%B9%E3%81%AE%E4%BD%8D%E7%BD%AE%E9%96%A2%E4%BF%82
    int ccw(Point a, Point b, Point c){
        b -= a, c -= a;
        // 点 a, b, c が
        if(cross(b, c) >  EPS) return  1; // 反時計回りのとき
        if(cross(b, c) < -EPS) return -1; // 時計回りのとき
        
        // 同一直線上にある場合
        if(dot(b, c) < 0) return 2;      // c, a, b の順
        if(norm(b) < norm(c)) return -2; // a, b, c の順
        return 0;                        // a, c, b の順
    }
    
    // 垂直（内積 == 0）
    bool is_vertical(Line a, Line b){
        return equal(dot(a.b - a.a, b.b - b.a), 0);
    }

    // 平行（外積 == 0）
    bool is_parallel(Line a, Line b){
        return equal(cross(a.b - a.a, b.b - b.a), 0);
    }

    // 線分の交差判定（線分 s に対して, 線分 t の端点が反対側にあればよい）
    bool is_intersect(Segment s, 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);
    }

    // 交点（交差する前提）
    Point cross_point(Line s, Line t){
        D d1 = cross(s.b - s.a, t.b - t.a);
        D d2 = cross(s.b - s.a, s.b - t.a);
        // s, t が一致する場合（適当な１点を返す）
        if(equal(abs(d1), 0) && equal(abs(d2), 0)) return t.a; 
        
        return t.a + (t.b - t.a) * (d2/d1);
    }
    Point cross_point(Segment s, Segment t) {
        assert(is_intersect(s, t)); // 交差する前提
        return cross_point(Line(s), Line(t));
    }

    // 線分と点の距離（点p から線分のどこかへの最短距離）
    D dist_segment_point(Segment l, Point p){
        if(dot(l.b - l.a, p - l.a) < EPS) return abs(p - l.a);
        if(dot(l.a - l.b, p - l.b) < EPS) return abs(p - l.b);
        return abs(cross(l.b - l.a, p - l.a)) / abs(l.b - l.a);
    }
    // 線分と線分の距離
    D dist_segment_segment(Segment s, Segment t){
        if(is_intersect(s, t)) return 0.0;
        D res = min({
            dist_segment_point(s, t.a),
            dist_segment_point(s, t.b),
            dist_segment_point(t, s.a),
            dist_segment_point(t, s.b),
        });
        return res;
    }

    // Todo : 円, 多角形

    // ２つの円の交点
    pair< Point, Point > cross_point(const Circle &c1, const Circle &c2) {
        D d = abs(c1.p - c2.p);
        D a = acos((c1.r * c1.r + d * d - c2.r * c2.r) / (2 * c1.r * d));
        D 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};
    }


    // 凸包 O(NlogN)
    vector<Point> ConvexHull(vector<Point> &p) {
        int n = (int)p.size(), k = 0;
        sort(p.begin(), p.end(), [](const Point &a, const Point &b) {
            return (real(a) != real(b) ? real(a) < real(b) : imag(a) < imag(b));
        });
        vector<Point> ch(2 * n);
        // 一直線上の3点を含める -> (< -EPS)
        // 含め無い -> (< EPS)
        for(int i = 0; i < n; ch[k++] = p[i++]) { // lower
            while(k >= 2 && cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) < EPS)
                --k;
        }
        for(int i = n - 2, t = k + 1; i >= 0; ch[k++] = p[i--]) { // upper
            while(k >= t && cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) < EPS)
                --k;
        }
        ch.resize(k - 1);
        return ch;
    }
};
using namespace geometry;

// sを始点とする各頂点への距離の最小値を格納した配列を返す
vector<ld> dijk(int s, vector<vector<pair<int, ld>>> &g){
    priority_queue<pair<ld, int>, vector<pair<ld, int>>, greater<pair<ld, int>>> que;
    vector<ld> dist(g.size(), (1LL<<60));
    que.push(make_pair(0, s));
    dist[s] = 0.0;
    while(!que.empty()){
        pair<ld, int> q = que.top(); que.pop();
        ld d = q.first;
        int u = q.second;
        if(dist[u] < d) continue;
        for(auto nq: g[u]){
            int v = nq.first;
            ld cost = nq.second;
            if(dist[v] > d+cost){
                dist[v] = d+cost;
                que.push({dist[v], v});
            }
        }
    }
    return dist;
}


int main() {

    while(true){
        int n; cin >> n;
        if(n == 0) break;


        vector<vector<Point>> builds(n);
        vector<ld> height(n);
        rep(i, 0, n){
            int nv; cin >> nv;
            cin >> height[i];
            rep(j, 0, nv){
                ld x, y; cin >> x >> y;
                builds[i].push_back(Point(x, y));
            }
        }

        ld theta, phi; cin >> theta >> phi;
        theta = M_PI*theta/180.0;
        phi = M_PI*phi/180.0;
        auto shadow = [&](Point p, ld h) -> Point {
            ld l = h/tan(phi);
            return {p.real()-l*cos(theta), p.imag()-l*sin(theta)};  
        };

        // 影でできる頂点を追加
        rep(i, 0, n){
            int nv = builds[i].size();
            rep(j, 0, nv){
                Point p = builds[i][j];
                Point np = shadow(p, height[i]);
                builds[i].push_back(np);
            }
        }

        // 凸包に置き換える
        {
            vector<vector<Point>> _builds(n);
            rep(i, 0, n){
                _builds[i] = ConvexHull(builds[i]);
            }
            swap(builds, _builds);
        }

        ld sx, sy, tx, ty; cin >> sx >> sy >> tx >> ty;
        builds.push_back({Point(sx, sy)});
        builds.push_back({Point(tx, ty)});
        vector<vector<ld>> dist(n+2, vector<ld>(n+2, 9e18));
        rep(i, 0, n){
            int nv = builds[i].size();
            rep(j, i+1, n){
                int nw = builds[j].size();
                rep(k, 0, nv){
                    Segment s(builds[i][k], builds[i][(k+1)%nv]);
                    rep(l, 0, nw){
                        Segment t(builds[j][l], builds[j][(l+1)%nw]);
                        chmin(dist[i][j], dist_segment_segment(s, t));
                    }
                }
            }
        }
        rep(i, n, n+2){
            rep(j, 0, n){
                int nw = builds[j].size();
                vector<Point> nb = builds[j];
                nb.push_back(builds[i][0]);
                vector<Point> ch = ConvexHull(nb);
                if(builds[j] == ch){
                    dist[j][i] = 0.0;
                }else{
                    rep(l, 0, nw){
                        Segment t(builds[j][l], builds[j][(l+1)%nw]);
                        chmin(dist[j][i], dist_segment_point(t, builds[i][0]));
                    }
                }
            }
        }
        chmin(dist[n][n+1], sqrt((sx-tx)*(sx-tx)+(sy-ty)*(sy-ty)));

        vector<vector<pair<int, ld>>> g(n+2);
        rep(i, 0, n+2){
            rep(j, i+1, n+2){
                g[i].push_back({j, dist[i][j]});
                g[j].push_back({i, dist[i][j]});
                // cout << i << ' ' << j << ": " << dist[i][j] << endl;
            }
        }
        vector<ld> ans = dijk(n, g);
        cout << setprecision(20) << ans[n+1] << endl;


    }
    
    return 0;
}
accuracy: 1
time_ms: 1170
memory_kb: 4896
final_rank: 20

code_snippet: #define PROBLEM "https://onlinejudge.u-aizu.ac.jp/problems/2827"
#define ERROR 0.001
#include <cstdint>
#include <cstddef>
namespace zawa {
using i16 = std::int16_t;
using i32 = std::int32_t;
using i64 = std::int64_t;
using i128 = __int128_t;
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
using usize = std::size_t;
} // namespace zawa
#include <cmath>
#include <cassert>
namespace zawa {
namespace geometryR2 {
using Real = long double;
namespace internal {
Real EPS{1e-12};
constexpr i32 negative{-1};
constexpr i32 zero{};
constexpr i32 positive{1};
} // namespace internal
Real& Eps() {
    return internal::EPS;
}
i32 Sign(Real value) {
    if (value < -Eps()) return internal::negative;
    if (value > Eps()) return internal::positive;
    return internal::zero;
}
bool Zero(Real value) {
    return Sign(value) == internal::zero;
}
bool Positive(Real value) {
    return Sign(value) == internal::positive;
}
bool Negative(Real value) {
    return Sign(value) == internal::negative;
}
bool Equal(Real a, Real b) {
    return Zero(a - b);
}
bool Smaller(Real a, Real b) {
    return Negative(a - b);
}
bool Bigger(Real a, Real b) {
    return Positive(a - b);
}
Real Square(Real value) {
    return (Zero(value) ? value : value * value);
}
Real Sqrt(Real value) {
    assert(!Negative(value));
    return (Zero(value) ? value : sqrtl(value));
}
Real Abs(Real value) {
    return (Negative(value) ? -value : value);
}
} // namespace geometryR2
 
} // namespace zawa
namespace zawa {
namespace geometryR2 {
constexpr Real PI{acosl(-1)};
constexpr Real TAU{static_cast<Real>(2) * PI};
constexpr Real ArcToRadian(Real arc) {
    return (arc * PI) / static_cast<Real>(180);
}
constexpr Real RadianToArc(Real radian) {
    return (radian * static_cast<Real>(180)) / PI;
}
} // namespace geometryR2
} // namespace zawa
#include <iostream>
namespace zawa {
namespace geometryR2 {
class Point {
private:
    Real x_{}, y_{};
public:
    /* constructor */
    Point() = default;
    Point(Real x, Real y) : x_{x}, y_{y} {}
    /* getter, setter */
    Real x() const {
        return x_;
    }
    Real& x() {
        return x_;
    }
    Real y() const {
        return y_;
    }
    Real& y() {
        return y_;
    }
    /* operator */
    Point& operator+=(const Point& rhs) {
        x_ += rhs.x();
        y_ += rhs.y();
        return *this;
    }
    friend Point operator+(const Point& lhs, const Point& rhs) {
        return Point{lhs} += rhs;
    }
    Point operator+() const {
        return *this;
    }
    Point& operator-=(const Point& rhs) {
        x_ -= rhs.x();
        y_ -= rhs.y();
        return *this;
    }
    friend Point operator-(const Point& lhs, const Point& rhs) {
        return Point{lhs} -= rhs;
    }
    Point operator-() const {
        return Point{} - *this;
    }
    Point& operator*=(Real k) {
        x_ *= k;
        y_ *= k;
        return *this;
    }
    friend Point operator*(Real k, const Point& p) {
        return Point{p} *= k;
    }
    friend Point operator*(const Point& p, Real k) {
        return Point{p} *= k;
    }
    Point& operator/=(Real k) {
        assert(!Zero(k));
        x_ /= k;
        y_ /= k;
        return *this;
    }
    friend Point operator/(Real k, const Point& p) {
        return Point{p} /= k;
    }
    friend Point operator/(const Point& p, Real k) {
        return Point{p} /= k;
    }
    friend bool operator==(const Point& lhs, const Point& rhs) {
        return Equal(lhs.x(), rhs.x()) and Equal(lhs.y(), rhs.y());
    }
    friend bool operator!=(const Point& lhs, const Point& rhs) {
        return !Equal(lhs.x(), rhs.x()) or !Equal(lhs.y(), rhs.y());
    }
    friend bool operator<(const Point& lhs, const Point& rhs) {
        return Smaller(lhs.x(), rhs.x()) or 
            (Equal(lhs.x(), rhs.x()) and Smaller(lhs.y(), rhs.y()));
    }
    friend bool operator<=(const Point& lhs, const Point& rhs) {
        return Smaller(lhs.x(), rhs.x()) or 
            (Equal(lhs.x(), rhs.x()) and (Smaller(lhs.y(), rhs.y()) or Equal(lhs.y(), rhs.y())));
    }
    friend bool operator>(const Point& lhs, const Point& rhs) {
        return Bigger(lhs.x(), rhs.x()) or
            (Equal(lhs.x(), rhs.x()) and Bigger(lhs.y(), rhs.y()));
    }
    friend bool operator>=(const Point& lhs, const Point& rhs) {
        return Bigger(lhs.x(), rhs.x()) or
            (Equal(lhs.x(), rhs.x()) and (Bigger(lhs.y(), rhs.y()) or Equal(lhs.y(), rhs.y())));
    }
    friend std::istream& operator>>(std::istream& is, Point& p) {
        is >> p.x_ >> p.y_;
        return is;
    }
    friend std::ostream& operator<<(std::ostream& os, const Point& p) {
        os << '(' << p.x_ << ',' << p.y_ << ')';
        return os;
    }
    
    /* member function */
    Real normSquare() const {
        return Square(x_) + Square(y_);
    }
    Real norm() const {
        return Sqrt(normSquare());
    }
    void normalize() {
        assert((*this) != Point{});
        (*this) /= norm(); 
    }
    Point normalized() const {
        Point res{*this};
        res.normalize();
        return res;
    }
    Point rotated(Real radian) const {
        return Point{
            x_ * cosl(radian) - y_ * sinl(radian),
            x_ * sinl(radian) + y_ * cosl(radian)
        };
    }
    void rotate(Real radian) {
        *this = rotated(radian); 
    }
    Point rotatedByArc(Real arc) const {
        return rotated(ArcToRadian(arc));
    }
    void rotateByArc(Real arc) {
        *this = rotatedByArc(arc);
    }
    Real argument() const {
        return (Negative(y_) ? TAU : static_cast<Real>(0)) + atan2l(y_, x_);
    }
    Real argumentByArc() const {
        return RadianToArc(argument());
    }
    /* friend function */
    friend Real Dot(const Point& lhs, const Point& rhs) {
        return lhs.x() * rhs.x() + lhs.y() * rhs.y();
    }
    friend Real Cross(const Point& lhs, const Point& rhs) {
        return lhs.x() * rhs.y() - lhs.y() * rhs.x();
    }
    friend Real Argument(const Point& lhs, const Point& rhs) {
        return rhs.argument() - lhs.argument();
    }
    friend bool ArgComp(const Point& lhs, const Point& rhs) {
        return Smaller(lhs.argument(), rhs.argument());
    }
};
using Vector = Point;
} // namespace geometryR2
} // namespace zawa
#include <vector>
namespace zawa {
namespace geometryR2 {
using PointCloud = std::vector<Point>;
} // namespace geometryR2
} // namespace zawa
namespace zawa {
namespace geometryR2 {
enum RELATION {
    // p0 -> p1 -> p2の順で直線上に並んでいる
    ONLINE_FRONT = -2,
    // (p1 - p0) -> (p2 - p0)が時計回りになっている
    CLOCKWISE,
    // p0 -> p2 -> p1の順で直線上に並んでいる
    ON_SEGMENT,
    // (p1 - p0) -> (p2 - p0)が反時計回りになっている
    COUNTER_CLOCKWISE,
    // p2 -> p0 -> p1、またはp1 -> p0 -> p2の順で直線上に並んでいる
    ONLINE_BACK
};
RELATION Relation(const Point& p0, const Point& p1, const Point& p2) {
    Point a{p1 - p0}, b{p2 - p0};
    if (Positive(Cross(a, b))) return COUNTER_CLOCKWISE;
    if (Negative(Cross(a, b))) return CLOCKWISE;
    if (Negative(Dot(a, b))) return ONLINE_BACK;
    if (Smaller(a.normSquare(), b.normSquare())) return ONLINE_FRONT;
    return ON_SEGMENT;
};
} // namespace geometryR2
} // namespace zawa
#include <algorithm>
#include <concepts>
namespace zawa {
namespace geometryR2 {
class Polygon {
private:
    std::vector<Point> data_;
public:
    /* member */
    usize size() const {
        return data_.size();
    }
    /* constructor */
    Polygon() = default;
    explicit Polygon(const std::vector<Point>& data) : data_{data} {}
    explicit Polygon(usize n) : data_(n) {}
    /* operator[] */
    Point& operator[](usize i) {
        assert(i < size());
        return data_[i];
    }
    const Point& operator[](usize i) const {
        assert(i < size());
        return data_[i];
    }
    friend std::istream& operator>>(std::istream& is, Polygon& polygon) {
        for (size_t i{} ; i < polygon.size() ; i++) {
            is >> polygon[i];
        }
        return is;
    }
    friend std::ostream& operator<<(std::ostream& os, const Polygon& polygon) {
        for (usize i{} ; i < polygon.size() ; i++) {
            std::cout << polygon[i] << (i + 1 == polygon.size() ? "" : " ");
        }
        return os;
    }
    /* member function */
    void orderRotate(usize i) {
        assert(i < size());
        std::rotate(data_.begin(), data_.begin() + i, data_.end());
    }
    void normalForm() {
        auto index{std::distance(data_.begin(), std::min_element(data_.begin(), data_.end()))};
        orderRotate(index);
    }
    Polygon normalFormed() const {
        Polygon res{*this};
        res.normalForm();
        return res;
    }
    bool isConvex() const {
        assert(size() >= static_cast<usize>(3));
        for (usize i{} ; i < size() ; i++) {
            if (Relation(data_[i], data_[i+1==size()?0:i+1], data_[i+2>=size()?i+2-size():i+2])
                    == CLOCKWISE) {
                return false;
            }
        }
        return true;
    }
    Real area() const {
        assert(size() >= static_cast<usize>(3));
        Real res{};
        for (usize i{1} ; i < size() ; i++) {
            res += Cross(data_[i] - data_[0], data_[i+1==size()?0:i+1] - data_[0]);
        }
        return res / static_cast<Real>(2);
    }
    void pushBack(const Point& p) {
        data_.push_back(p);
    }
    void emplaceBack(Real x, Real y) {
        data_.emplace_back(x, y);
    }
    void reserve(usize n) {
        data_.reserve(n);
    }
    template <std::input_iterator RandomAccessIterator>
    void insert(usize n, RandomAccessIterator first, RandomAccessIterator last) {
        assert(n <= size());
        data_.insert(std::next(data_.begin(), n), first, last);
    }
};
} // namespace geometryR2
} // namespace zawa
#include <iterator>
namespace zawa {
namespace geometryR2 {
template <bool strictly>
Polygon ConvexHull(const PointCloud& p) {
    PointCloud cp{p};
    std::sort(cp.begin(), cp.end());
    cp.erase(std::unique(cp.begin(), cp.end()), cp.end());
    if (cp.size() <= 2u) {
        return Polygon{cp};
    }
    PointCloud lower;
    lower.reserve(p.size());
    for (auto it{cp.begin()} ; it != cp.end() ; it++) {
        lower.push_back(*it);
        while (lower.size() >= 3u) {
            if (Relation(lower[lower.size() - 3], lower[lower.size() - 2], lower[lower.size() - 1]) == COUNTER_CLOCKWISE) {
                break;
            }
            if constexpr (!strictly) {
                if (Relation(lower[lower.size() - 3], lower[lower.size() - 2], lower[lower.size() - 1]) == ONLINE_FRONT) {
                    break;
                }
            }
            std::swap(lower[lower.size() - 2], lower[lower.size() - 1]);
            lower.pop_back();
        }
    }
    PointCloud upper;
    upper.reserve(p.size());
    for (auto it{cp.rbegin()} ; it != cp.rend() ; it++) {
        upper.push_back(*it);
        while (upper.size() >= 3u) {
            if (Relation(upper[upper.size() - 3], upper[upper.size() - 2], upper[upper.size() - 1]) == COUNTER_CLOCKWISE) {
                break;
            }
            if constexpr (!strictly) {
                if (Relation(upper[upper.size() - 3], upper[upper.size() - 2], upper[upper.size() - 1]) == ONLINE_FRONT) {
                    break;
                }
            }
            std::swap(upper[upper.size() - 2], upper[upper.size() - 1]);
            upper.pop_back();
        }
    }
    Polygon res;
    res.reserve(lower.size() + upper.size() - 2);
    res.insert(res.size(), lower.begin(), lower.end());
    res.insert(res.size(), std::next(upper.begin()), std::prev(upper.end()));
    return res;
}
} // namespace geometryR2
} // namespace zawa
namespace zawa {
namespace geometryR2 {
Real Distance(const Point& p0, const Point& p1) {
    return Point{p1 - p0}.norm();
}
Real DistanceSquare(const Point& p0, const Point& p1) {
    return Point{p1 - p0}.normSquare();
}
} // namespace geometryR2
} // namespace zawa
namespace zawa {
namespace geometryR2 {
class Segment {
private:
    Point p0_{}, p1_{};
public:
    /* constructor */
    Segment() = default;
    Segment(const Point& p0, const Point& p1) : p0_{p0}, p1_{p1} {}
    Segment(Real x0, Real y0, Real x1, Real y1) : p0_{x0, y0}, p1_{x1, y1} {}
    /* getter setter */
    const Point& p0() const {
        return p0_;
    }
    Point& p0() {
        return p0_;
    }
    const Point& p1() const {
        return p1_;
    }
    Point& p1() {
        return p1_;
    }
    /* member function */
    bool valid() const {
        return p0_ != p1_;
    }
    bool straddle(const Segment& s) const {
        return Relation(p0_, p1_, s.p0()) * Relation(p0_, p1_, s.p1()) <= 0;
    }
    Real length() const {
        assert(valid());
        return Distance(p0_, p1_);
    }
    Point midpoint() const {
        assert(valid());
        return p0_ + Vector{p1_ - p0_} / static_cast<Real>(2);
    }
};
} // namespace geometryR2
} // namespace zawa
namespace zawa {
namespace geometryR2 {
bool Intersect(const Segment& s0, const Segment& s1) {
    assert(s0.valid());
    assert(s1.valid());
    return s0.straddle(s1) and s1.straddle(s0);
}
} // namespace geometryR2
} // namespace zawa
namespace zawa {
namespace geometryR2 {
Real Distance(const Point& p, const Segment& s) {
    assert(s.valid());
    if (Negative(Dot(s.p1() - s.p0(), p - s.p0()))) {
        return Distance(p, s.p0());
    }
    if (Negative(Dot(s.p0() - s.p1(), p - s.p1()))) {
        return Distance(p, s.p1());
    }
    return Abs(Cross(s.p1() - s.p0(), p - s.p0())) / s.length();
}
bool PointOnSegment(const Point& p, const Segment& s) {
    assert(s.valid());
    return Zero(Distance(p, s));
}
} // namespace geometryR2
} // namespace zawa
namespace zawa {
namespace geometryR2 {
Real Distance(const Segment& s0, const Segment& s1) {
    assert(s0.valid());
    assert(s1.valid());
    if (Intersect(s0, s1)) {
        return static_cast<Real>(0);
    }
    else {
        return std::min({ 
                Distance(s1.p0(), s0), 
                Distance(s1.p1(), s0),
                Distance(s0.p0(), s1),
                Distance(s0.p1(), s1) });
    }
}
} // namespace geometryR2
} // namespace zawa
#include <functional>
#include <type_traits>
#include <utility>
namespace zawa {
namespace internal {
template <class T>
T MidPoint(T a, T b) {
    if (a > b) std::swap(a, b);
    return a + ((b - a) >> 1);
}
template <class T>
T Abs(T a, T b) {
    return (a >= b ? a - b : b - a);
}
} // namespace zawa::internal
template <class T, class Function>
T BinarySearch(T ok, T ng, const Function& f) {
    static_assert(std::is_integral_v<T>, "T must be integral type");
    static_assert(std::is_convertible_v<Function, std::function<bool(T)>>, "f must be function bool(T)");
    while (internal::Abs(ok, ng) > 1) {
        T mid{ internal::MidPoint(ok, ng) };
        (f(mid) ? ok : ng) = mid;
    }
    return ok;
}
template <class T, class Function>
T BinarySearch(T ok, T ng, const Function& f, u32 upperLimit) {
    static_assert(std::is_signed_v<T>, "T must be signed arithmetic type");
    static_assert(std::is_convertible_v<Function, std::function<bool(T)>>, "f must be function bool(T)");
    for (u32 _{} ; _ < upperLimit ; _++) {
        T mid{ (ok + ng) / (T)2 };
        (f(mid) ? ok : ng) = mid;
    }
    return ok;
}
} // namespace zawa
namespace zawa {
namespace geometryR2 {
enum ContainState {
    INSIDE,
    ONLINE,
    OUTSIDE
};
} // namespace geometryR2
} // namespace zawa
namespace zawa {
namespace geometryR2 {
namespace internal {
bool TriangleContainsPoint(const Point& p0, const Point& p1, const Point& p2, const Point& p) {
    Real area{Abs(Cross(p1 - p0, p2 - p0))};
    Real value{};
    value += Abs(Cross(p0 - p, p1 - p));
    value += Abs(Cross(p1 - p, p2 - p));
    value += Abs(Cross(p2 - p, p0 - p));
    return Equal(area, value);
}
} // namespace internal
// note: 凸多角形であることを確認してください。
// note: normal formにしておいてください
ContainState ConvexPolygonContainsPoint(const Polygon& polygon, const Point& p) {
    usize n{polygon.size()};
    assert(n >= static_cast<usize>(3));
    if (polygon[0] == p or polygon[1] == p or polygon[n - 1] == p) {
        return ONLINE;
    }
    if (Relation(polygon[0], polygon[1], p) == ON_SEGMENT) {
        return ONLINE;
    }
    if (Relation(polygon[0], polygon[n - 1], p) == ON_SEGMENT) {
        return ONLINE;
    }
    if (Zero(Cross(polygon[1] - polygon[0], p - polygon[0]))) {
        return OUTSIDE;
    }
    if (Zero(Cross(polygon[n - 1] - polygon[0], p - polygon[0]))) {
        return OUTSIDE;
    }
    if (!(Relation(polygon[0], polygon[1], p) == COUNTER_CLOCKWISE and Relation(polygon[0], p, polygon[n - 1]) == COUNTER_CLOCKWISE)) {
        return OUTSIDE;
    }
    auto f{[&](usize i) -> bool {
        return Relation(polygon[0], polygon[i], p) == COUNTER_CLOCKWISE;
    }};
    usize pos{BinarySearch(usize{0}, usize{n - 1}, f)};
    if (p == polygon[pos]) return ONLINE;
    if (p == polygon[pos + 1]) return ONLINE;
    if (Relation(polygon[pos], polygon[pos + 1], p) == ON_SEGMENT) return ONLINE;
    if (internal::TriangleContainsPoint(polygon[0], polygon[pos], polygon[pos + 1], p)) {
        return INSIDE;
    }
    else {
        return OUTSIDE;
    }
}
} // namespace geometryZ2
} // namespace zawa
using namespace zawa::geometryR2;
#include <iomanip>
#include <queue>
int N, NV[100];
Real H[100], THETA, GAMMA;
Polygon P[100];
Point START, GOAL;
bool inner(const Polygon& p, const Polygon& q) {
    for (int i = 0 ; i < std::ssize(p) ; i++) if (ConvexPolygonContainsPoint(q, p[i]) == OUTSIDE) return false;
    return true;
}
Real Distance(const Polygon& p, const Polygon& q) {
    if (inner(p, q) or inner(q, p)) return 0;
    Real res = 1e18l;
    for (int i = 0 ; i < std::ssize(p) ; i++) for (int j = 0 ; j < std::ssize(q) ; j++) {
        res = std::min(res, Distance(Segment{p[i], p[i+1==std::ssize(p)?0:i+1]},
                    Segment{q[j], q[j+1==std::ssize(q)?0:j+1]}));
    }
    return res;
}
Real NaiveDistance(const Polygon& poly, const Point& p) {
    if (ConvexPolygonContainsPoint(poly, p) != OUTSIDE) {
        return 0;
    }
    else {
        Real res = 1e18l;
        for (int i = 0 ; i < std::ssize(poly) ; i++) {
            res = std::min(res, Distance(p, Segment{poly[i], poly[i+1==std::ssize(poly)?0:i+1]}));
        }
        return res;
    }
}
Real solve() {
    std::vector<Polygon> hulls = [&]() {
        std::vector<PointCloud> pcs(N);
        std::vector<Polygon> res(N);
        for (int i = 0 ; i < N ; i++) {
            const Real shadow_len = H[i] * cosl(GAMMA) / sinl(GAMMA);
            for (int j = 0 ; j < NV[i] ; j++) {
                pcs[i].push_back(P[i][j]);
                pcs[i].push_back(P[i][j] - shadow_len * Point{cosl(THETA), sinl(THETA)});
            }
            res[i] = ConvexHull<true>(pcs[i]);
            assert(std::ssize(res[i]) >= 3);
            res[i].normalForm();
        }
        return res;
    }();
    const int n = N + 2, st = N, go = N + 1;
    std::vector g(n, std::vector<Real>(n));
    for (int i = 0 ; i < N ; i++) {
        const Real w = NaiveDistance(hulls[i], START);
        g[st][i] = g[i][st] = w;
    }
    for (int i = 0 ; i < N ; i++) {
        const Real w = NaiveDistance(hulls[i], GOAL);
        g[go][i] = g[i][go] = w;
    }
    g[st][go] = g[go][st] = Distance(START, GOAL);
    for (int i = 0 ; i < N ; i++) {
        for (int j = i + 1 ; j < N ; j++) {
            g[i][j] = g[j][i] = Distance(hulls[i], hulls[j]);
        }
    }
    std::vector<Real> dist(n, 1e18l);
    using qt = std::pair<Real, int>;
    std::priority_queue<qt, std::vector<qt>, std::greater<qt>> que;
    dist[st] = 0;
    que.push({dist[st], st});
    // for (int i = 0 ; i < n ; i++) {
    //     for (int j = 0 ; j < n ; j++) std::cout << g[i][j] << ' ';
    //     std::cout << std::endl;
    // }
    while (que.size()) {
        auto [d, v] = que.top();
        que.pop();
        if (Bigger(d, dist[v])) continue;
        for (int i = 0 ; i < n ; i++) if (Smaller(d + g[v][i], dist[i])) {
            dist[i] = d + g[v][i];
            que.push({dist[i], i});
        }
    }
    return dist[go];
}
int main() {
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
    std::ios::sync_with_stdio(false);
    std::cout << std::fixed << std::setprecision(5);
    while (true) {
        std::cin >> N;
        if (N == 0) break;
        for (int i = 0 ; i < N ; i++) {
            std::cin >> NV[i] >> H[i];
            Polygon p(NV[i]);
            for (int j = 0 ; j < NV[i] ; j++) std::cin >> p[j].x() >> p[j].y();
            P[i] = std::move(p);
        }
        std::cin >> THETA >> GAMMA;
        THETA = ArcToRadian(THETA);
        GAMMA = ArcToRadian(GAMMA);
        std::cin >> START.x() >> START.y() >> GOAL.x() >> GOAL.y();
        std::cout << solve() << '\n';
    }
}
accuracy: 1
time_ms: 750
memory_kb: 4476
final_rank: 15

code_snippet: #define PROBLEM "https://onlinejudge.u-aizu.ac.jp/problems/2827"
#define ERROR 0.001
#include <cstdint>
#include <cstddef>
namespace zawa {
using i16 = std::int16_t;
using i32 = std::int32_t;
using i64 = std::int64_t;
using i128 = __int128_t;
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
using usize = std::size_t;
} // namespace zawa
#include <cmath>
#include <cassert>
namespace zawa {
namespace geometryR2 {
using Real = long double;
namespace internal {
Real EPS{1e-12};
constexpr i32 negative{-1};
constexpr i32 zero{};
constexpr i32 positive{1};
} // namespace internal
Real& Eps() {
    return internal::EPS;
}
i32 Sign(Real value) {
    if (value < -Eps()) return internal::negative;
    if (value > Eps()) return internal::positive;
    return internal::zero;
}
bool Zero(Real value) {
    return Sign(value) == internal::zero;
}
bool Positive(Real value) {
    return Sign(value) == internal::positive;
}
bool Negative(Real value) {
    return Sign(value) == internal::negative;
}
bool Equal(Real a, Real b) {
    return Zero(a - b);
}
bool Smaller(Real a, Real b) {
    return Negative(a - b);
}
bool Bigger(Real a, Real b) {
    return Positive(a - b);
}
Real Square(Real value) {
    return (Zero(value) ? value : value * value);
}
Real Sqrt(Real value) {
    assert(!Negative(value));
    return (Zero(value) ? value : sqrtl(value));
}
Real Abs(Real value) {
    return (Negative(value) ? -value : value);
}
} // namespace geometryR2
 
} // namespace zawa
namespace zawa {
namespace geometryR2 {
constexpr Real PI{acosl(-1)};
constexpr Real TAU{static_cast<Real>(2) * PI};
constexpr Real ArcToRadian(Real arc) {
    return (arc * PI) / static_cast<Real>(180);
}
constexpr Real RadianToArc(Real radian) {
    return (radian * static_cast<Real>(180)) / PI;
}
} // namespace geometryR2
} // namespace zawa
#include <iostream>
namespace zawa {
namespace geometryR2 {
class Point {
private:
    Real x_{}, y_{};
public:
    /* constructor */
    Point() = default;
    Point(Real x, Real y) : x_{x}, y_{y} {}
    /* getter, setter */
    Real x() const {
        return x_;
    }
    Real& x() {
        return x_;
    }
    Real y() const {
        return y_;
    }
    Real& y() {
        return y_;
    }
    /* operator */
    Point& operator+=(const Point& rhs) {
        x_ += rhs.x();
        y_ += rhs.y();
        return *this;
    }
    friend Point operator+(const Point& lhs, const Point& rhs) {
        return Point{lhs} += rhs;
    }
    Point operator+() const {
        return *this;
    }
    Point& operator-=(const Point& rhs) {
        x_ -= rhs.x();
        y_ -= rhs.y();
        return *this;
    }
    friend Point operator-(const Point& lhs, const Point& rhs) {
        return Point{lhs} -= rhs;
    }
    Point operator-() const {
        return Point{} - *this;
    }
    Point& operator*=(Real k) {
        x_ *= k;
        y_ *= k;
        return *this;
    }
    friend Point operator*(Real k, const Point& p) {
        return Point{p} *= k;
    }
    friend Point operator*(const Point& p, Real k) {
        return Point{p} *= k;
    }
    Point& operator/=(Real k) {
        assert(!Zero(k));
        x_ /= k;
        y_ /= k;
        return *this;
    }
    friend Point operator/(Real k, const Point& p) {
        return Point{p} /= k;
    }
    friend Point operator/(const Point& p, Real k) {
        return Point{p} /= k;
    }
    friend bool operator==(const Point& lhs, const Point& rhs) {
        return Equal(lhs.x(), rhs.x()) and Equal(lhs.y(), rhs.y());
    }
    friend bool operator!=(const Point& lhs, const Point& rhs) {
        return !Equal(lhs.x(), rhs.x()) or !Equal(lhs.y(), rhs.y());
    }
    friend bool operator<(const Point& lhs, const Point& rhs) {
        return Smaller(lhs.x(), rhs.x()) or 
            (Equal(lhs.x(), rhs.x()) and Smaller(lhs.y(), rhs.y()));
    }
    friend bool operator<=(const Point& lhs, const Point& rhs) {
        return Smaller(lhs.x(), rhs.x()) or 
            (Equal(lhs.x(), rhs.x()) and (Smaller(lhs.y(), rhs.y()) or Equal(lhs.y(), rhs.y())));
    }
    friend bool operator>(const Point& lhs, const Point& rhs) {
        return Bigger(lhs.x(), rhs.x()) or
            (Equal(lhs.x(), rhs.x()) and Bigger(lhs.y(), rhs.y()));
    }
    friend bool operator>=(const Point& lhs, const Point& rhs) {
        return Bigger(lhs.x(), rhs.x()) or
            (Equal(lhs.x(), rhs.x()) and (Bigger(lhs.y(), rhs.y()) or Equal(lhs.y(), rhs.y())));
    }
    friend std::istream& operator>>(std::istream& is, Point& p) {
        is >> p.x_ >> p.y_;
        return is;
    }
    friend std::ostream& operator<<(std::ostream& os, const Point& p) {
        os << '(' << p.x_ << ',' << p.y_ << ')';
        return os;
    }
    
    /* member function */
    Real normSquare() const {
        return Square(x_) + Square(y_);
    }
    Real norm() const {
        return Sqrt(normSquare());
    }
    void normalize() {
        assert((*this) != Point{});
        (*this) /= norm(); 
    }
    Point normalized() const {
        Point res{*this};
        res.normalize();
        return res;
    }
    Point rotated(Real radian) const {
        return Point{
            x_ * cosl(radian) - y_ * sinl(radian),
            x_ * sinl(radian) + y_ * cosl(radian)
        };
    }
    void rotate(Real radian) {
        *this = rotated(radian); 
    }
    Point rotatedByArc(Real arc) const {
        return rotated(ArcToRadian(arc));
    }
    void rotateByArc(Real arc) {
        *this = rotatedByArc(arc);
    }
    Real argument() const {
        return (Negative(y_) ? TAU : static_cast<Real>(0)) + atan2l(y_, x_);
    }
    Real argumentByArc() const {
        return RadianToArc(argument());
    }
    /* friend function */
    friend Real Dot(const Point& lhs, const Point& rhs) {
        return lhs.x() * rhs.x() + lhs.y() * rhs.y();
    }
    friend Real Cross(const Point& lhs, const Point& rhs) {
        return lhs.x() * rhs.y() - lhs.y() * rhs.x();
    }
    friend Real Argument(const Point& lhs, const Point& rhs) {
        return rhs.argument() - lhs.argument();
    }
    friend bool ArgComp(const Point& lhs, const Point& rhs) {
        return Smaller(lhs.argument(), rhs.argument());
    }
};
using Vector = Point;
} // namespace geometryR2
} // namespace zawa
#include <vector>
namespace zawa {
namespace geometryR2 {
using PointCloud = std::vector<Point>;
} // namespace geometryR2
} // namespace zawa
namespace zawa {
namespace geometryR2 {
enum RELATION {
    // p0 -> p1 -> p2の順で直線上に並んでいる
    ONLINE_FRONT = -2,
    // (p1 - p0) -> (p2 - p0)が時計回りになっている
    CLOCKWISE,
    // p0 -> p2 -> p1の順で直線上に並んでいる
    ON_SEGMENT,
    // (p1 - p0) -> (p2 - p0)が反時計回りになっている
    COUNTER_CLOCKWISE,
    // p2 -> p0 -> p1、またはp1 -> p0 -> p2の順で直線上に並んでいる
    ONLINE_BACK
};
RELATION Relation(const Point& p0, const Point& p1, const Point& p2) {
    Point a{p1 - p0}, b{p2 - p0};
    if (Positive(Cross(a, b))) return COUNTER_CLOCKWISE;
    if (Negative(Cross(a, b))) return CLOCKWISE;
    if (Negative(Dot(a, b))) return ONLINE_BACK;
    if (Smaller(a.normSquare(), b.normSquare())) return ONLINE_FRONT;
    return ON_SEGMENT;
};
} // namespace geometryR2
} // namespace zawa
#include <algorithm>
#include <concepts>
namespace zawa {
namespace geometryR2 {
class Polygon {
private:
    std::vector<Point> data_;
public:
    /* member */
    usize size() const {
        return data_.size();
    }
    /* constructor */
    Polygon() = default;
    explicit Polygon(const std::vector<Point>& data) : data_{data} {}
    explicit Polygon(usize n) : data_(n) {}
    /* operator[] */
    Point& operator[](usize i) {
        assert(i < size());
        return data_[i];
    }
    const Point& operator[](usize i) const {
        assert(i < size());
        return data_[i];
    }
    friend std::istream& operator>>(std::istream& is, Polygon& polygon) {
        for (size_t i{} ; i < polygon.size() ; i++) {
            is >> polygon[i];
        }
        return is;
    }
    friend std::ostream& operator<<(std::ostream& os, const Polygon& polygon) {
        for (usize i{} ; i < polygon.size() ; i++) {
            std::cout << polygon[i] << (i + 1 == polygon.size() ? "" : " ");
        }
        return os;
    }
    /* member function */
    void orderRotate(usize i) {
        assert(i < size());
        std::rotate(data_.begin(), data_.begin() + i, data_.end());
    }
    void normalForm() {
        auto index{std::distance(data_.begin(), std::min_element(data_.begin(), data_.end()))};
        orderRotate(index);
    }
    Polygon normalFormed() const {
        Polygon res{*this};
        res.normalForm();
        return res;
    }
    bool isConvex() const {
        assert(size() >= static_cast<usize>(3));
        for (usize i{} ; i < size() ; i++) {
            if (Relation(data_[i], data_[i+1==size()?0:i+1], data_[i+2>=size()?i+2-size():i+2])
                    == CLOCKWISE) {
                return false;
            }
        }
        return true;
    }
    Real area() const {
        assert(size() >= static_cast<usize>(3));
        Real res{};
        for (usize i{1} ; i < size() ; i++) {
            res += Cross(data_[i] - data_[0], data_[i+1==size()?0:i+1] - data_[0]);
        }
        return res / static_cast<Real>(2);
    }
    void pushBack(const Point& p) {
        data_.push_back(p);
    }
    void emplaceBack(Real x, Real y) {
        data_.emplace_back(x, y);
    }
    void reserve(usize n) {
        data_.reserve(n);
    }
    template <std::input_iterator RandomAccessIterator>
    void insert(usize n, RandomAccessIterator first, RandomAccessIterator last) {
        assert(n <= size());
        data_.insert(std::next(data_.begin(), n), first, last);
    }
};
} // namespace geometryR2
} // namespace zawa
#include <iterator>
namespace zawa {
namespace geometryR2 {
template <bool strictly>
Polygon ConvexHull(const PointCloud& p) {
    PointCloud cp{p};
    std::sort(cp.begin(), cp.end());
    cp.erase(std::unique(cp.begin(), cp.end()), cp.end());
    if (cp.size() <= 2u) {
        return Polygon{cp};
    }
    PointCloud lower;
    lower.reserve(p.size());
    for (auto it{cp.begin()} ; it != cp.end() ; it++) {
        lower.push_back(*it);
        while (lower.size() >= 3u) {
            if (Relation(lower[lower.size() - 3], lower[lower.size() - 2], lower[lower.size() - 1]) == COUNTER_CLOCKWISE) {
                break;
            }
            if constexpr (!strictly) {
                if (Relation(lower[lower.size() - 3], lower[lower.size() - 2], lower[lower.size() - 1]) == ONLINE_FRONT) {
                    break;
                }
            }
            std::swap(lower[lower.size() - 2], lower[lower.size() - 1]);
            lower.pop_back();
        }
    }
    PointCloud upper;
    upper.reserve(p.size());
    for (auto it{cp.rbegin()} ; it != cp.rend() ; it++) {
        upper.push_back(*it);
        while (upper.size() >= 3u) {
            if (Relation(upper[upper.size() - 3], upper[upper.size() - 2], upper[upper.size() - 1]) == COUNTER_CLOCKWISE) {
                break;
            }
            if constexpr (!strictly) {
                if (Relation(upper[upper.size() - 3], upper[upper.size() - 2], upper[upper.size() - 1]) == ONLINE_FRONT) {
                    break;
                }
            }
            std::swap(upper[upper.size() - 2], upper[upper.size() - 1]);
            upper.pop_back();
        }
    }
    Polygon res;
    res.reserve(lower.size() + upper.size() - 2);
    res.insert(res.size(), lower.begin(), lower.end());
    res.insert(res.size(), std::next(upper.begin()), std::prev(upper.end()));
    return res;
}
} // namespace geometryR2
} // namespace zawa
namespace zawa {
namespace geometryR2 {
Real Distance(const Point& p0, const Point& p1) {
    return Point{p1 - p0}.norm();
}
Real DistanceSquare(const Point& p0, const Point& p1) {
    return Point{p1 - p0}.normSquare();
}
} // namespace geometryR2
} // namespace zawa
namespace zawa {
namespace geometryR2 {
class Segment {
private:
    Point p0_{}, p1_{};
public:
    /* constructor */
    Segment() = default;
    Segment(const Point& p0, const Point& p1) : p0_{p0}, p1_{p1} {}
    Segment(Real x0, Real y0, Real x1, Real y1) : p0_{x0, y0}, p1_{x1, y1} {}
    /* getter setter */
    const Point& p0() const {
        return p0_;
    }
    Point& p0() {
        return p0_;
    }
    const Point& p1() const {
        return p1_;
    }
    Point& p1() {
        return p1_;
    }
    /* member function */
    bool valid() const {
        return p0_ != p1_;
    }
    bool straddle(const Segment& s) const {
        return Relation(p0_, p1_, s.p0()) * Relation(p0_, p1_, s.p1()) <= 0;
    }
    Real length() const {
        assert(valid());
        return Distance(p0_, p1_);
    }
    Point midpoint() const {
        assert(valid());
        return p0_ + Vector{p1_ - p0_} / static_cast<Real>(2);
    }
};
} // namespace geometryR2
} // namespace zawa
namespace zawa {
namespace geometryR2 {
bool Intersect(const Segment& s0, const Segment& s1) {
    assert(s0.valid());
    assert(s1.valid());
    return s0.straddle(s1) and s1.straddle(s0);
}
} // namespace geometryR2
} // namespace zawa
namespace zawa {
namespace geometryR2 {
Real Distance(const Point& p, const Segment& s) {
    assert(s.valid());
    if (Negative(Dot(s.p1() - s.p0(), p - s.p0()))) {
        return Distance(p, s.p0());
    }
    if (Negative(Dot(s.p0() - s.p1(), p - s.p1()))) {
        return Distance(p, s.p1());
    }
    return Abs(Cross(s.p1() - s.p0(), p - s.p0())) / s.length();
}
bool PointOnSegment(const Point& p, const Segment& s) {
    assert(s.valid());
    return Zero(Distance(p, s));
}
} // namespace geometryR2
} // namespace zawa
namespace zawa {
namespace geometryR2 {
Real Distance(const Segment& s0, const Segment& s1) {
    assert(s0.valid());
    assert(s1.valid());
    if (Intersect(s0, s1)) {
        return static_cast<Real>(0);
    }
    else {
        return std::min({ 
                Distance(s1.p0(), s0), 
                Distance(s1.p1(), s0),
                Distance(s0.p0(), s1),
                Distance(s0.p1(), s1) });
    }
}
} // namespace geometryR2
} // namespace zawa
// #include "../Src/GeometryR2/Distance/ConvexPolygonAndPoint.hpp"
#include <functional>
#include <type_traits>
#include <utility>
namespace zawa {
namespace internal {
template <class T>
T MidPoint(T a, T b) {
    if (a > b) std::swap(a, b);
    return a + ((b - a) >> 1);
}
template <class T>
T Abs(T a, T b) {
    return (a >= b ? a - b : b - a);
}
} // namespace zawa::internal
template <class T, class Function>
T BinarySearch(T ok, T ng, const Function& f) {
    static_assert(std::is_integral_v<T>, "T must be integral type");
    static_assert(std::is_convertible_v<Function, std::function<bool(T)>>, "f must be function bool(T)");
    while (internal::Abs(ok, ng) > 1) {
        T mid{ internal::MidPoint(ok, ng) };
        (f(mid) ? ok : ng) = mid;
    }
    return ok;
}
template <class T, class Function>
T BinarySearch(T ok, T ng, const Function& f, u32 upperLimit) {
    static_assert(std::is_signed_v<T>, "T must be signed arithmetic type");
    static_assert(std::is_convertible_v<Function, std::function<bool(T)>>, "f must be function bool(T)");
    for (u32 _{} ; _ < upperLimit ; _++) {
        T mid{ (ok + ng) / (T)2 };
        (f(mid) ? ok : ng) = mid;
    }
    return ok;
}
} // namespace zawa
namespace zawa {
namespace geometryR2 {
enum ContainState {
    INSIDE,
    ONLINE,
    OUTSIDE
};
} // namespace geometryR2
} // namespace zawa
namespace zawa {
namespace geometryR2 {
namespace internal {
bool TriangleContainsPoint(const Point& p0, const Point& p1, const Point& p2, const Point& p) {
    Real area{Abs(Cross(p1 - p0, p2 - p0))};
    Real value{};
    value += Abs(Cross(p0 - p, p1 - p));
    value += Abs(Cross(p1 - p, p2 - p));
    value += Abs(Cross(p2 - p, p0 - p));
    return Equal(area, value);
}
} // namespace internal
// note: 凸多角形であることを確認してください。
// note: normal formにしておいてください
ContainState ConvexPolygonContainsPoint(const Polygon& polygon, const Point& p) {
    usize n{polygon.size()};
    assert(n >= static_cast<usize>(3));
    if (polygon[0] == p or polygon[1] == p or polygon[n - 1] == p) {
        return ONLINE;
    }
    if (Relation(polygon[0], polygon[1], p) == ON_SEGMENT) {
        return ONLINE;
    }
    if (Relation(polygon[0], polygon[n - 1], p) == ON_SEGMENT) {
        return ONLINE;
    }
    if (Zero(Cross(polygon[1] - polygon[0], p - polygon[0]))) {
        return OUTSIDE;
    }
    if (Zero(Cross(polygon[n - 1] - polygon[0], p - polygon[0]))) {
        return OUTSIDE;
    }
    if (!(Relation(polygon[0], polygon[1], p) == COUNTER_CLOCKWISE and Relation(polygon[0], p, polygon[n - 1]) == COUNTER_CLOCKWISE)) {
        return OUTSIDE;
    }
    auto f{[&](usize i) -> bool {
        return Relation(polygon[0], polygon[i], p) == COUNTER_CLOCKWISE;
    }};
    usize pos{BinarySearch(usize{0}, usize{n - 1}, f)};
    if (p == polygon[pos]) return ONLINE;
    if (p == polygon[pos + 1]) return ONLINE;
    if (Relation(polygon[pos], polygon[pos + 1], p) == ON_SEGMENT) return ONLINE;
    if (internal::TriangleContainsPoint(polygon[0], polygon[pos], polygon[pos + 1], p)) {
        return INSIDE;
    }
    else {
        return OUTSIDE;
    }
}
} // namespace geometryZ2
} // namespace zawa
using namespace zawa::geometryR2;
#include <iomanip>
#include <queue>
int N, NV[100];
Real H[100], THETA, GAMMA;
Polygon P[100];
Point START, GOAL;
bool inner(const Polygon& p, const Polygon& q) {
    for (int i = 0 ; i < std::ssize(p) ; i++) if (ConvexPolygonContainsPoint(q, p[i]) == OUTSIDE) return false;
    return true;
}
Real Distance(const Polygon& p, const Polygon& q) {
    if (inner(p, q) or inner(q, p)) return 0;
    Real res = 1e18l;
    for (int i = 0 ; i < std::ssize(p) ; i++) for (int j = 0 ; j < std::ssize(q) ; j++) {
        res = std::min(res, Distance(Segment{p[i], p[i+1==std::ssize(p)?0:i+1]},
                    Segment{q[j], q[j+1==std::ssize(q)?0:j+1]}));
    }
    return res;
}
Real Distance(const Polygon& poly, const Point& p) {
    if (ConvexPolygonContainsPoint(poly, p) != OUTSIDE) {
        return 0;
    }
    else {
        Real res = 1e18l;
        for (int i = 0 ; i < std::ssize(poly) ; i++) {
            res = std::min(res, Distance(p, Segment{poly[i], poly[i+1==std::ssize(poly)?0:i+1]}));
        }
        return res;
    }
}
Real solve() {
    std::vector<Polygon> hulls = [&]() {
        std::vector<PointCloud> pcs(N);
        std::vector<Polygon> res(N);
        for (int i = 0 ; i < N ; i++) {
            const Real shadow_len = H[i] * cosl(GAMMA) / sinl(GAMMA);
            for (int j = 0 ; j < NV[i] ; j++) {
                pcs[i].push_back(P[i][j]);
                pcs[i].push_back(P[i][j] - shadow_len * Point{cosl(THETA), sinl(THETA)});
            }
            res[i] = ConvexHull<true>(pcs[i]);
            assert(std::ssize(res[i]) >= 3);
            res[i].normalForm();
            // std::cout << res[i] << std::endl;
        }
        return res;
    }();
    const int n = N + 2, st = N, go = N + 1;
    std::vector g(n, std::vector<Real>(n));
    for (int i = 0 ; i < N ; i++) {
        const Real w = Distance(hulls[i], START);
        g[st][i] = g[i][st] = w;
    }
    for (int i = 0 ; i < N ; i++) {
        const Real w = Distance(hulls[i], GOAL);
        g[go][i] = g[i][go] = w;
    }
    g[st][go] = g[go][st] = Distance(START, GOAL);
    for (int i = 0 ; i < N ; i++) {
        for (int j = i + 1 ; j < N ; j++) {
            g[i][j] = g[j][i] = Distance(hulls[i], hulls[j]);
        }
    }
    std::vector<Real> dist(n, 1e18l);
    using qt = std::pair<Real, int>;
    std::priority_queue<qt, std::vector<qt>, std::greater<qt>> que;
    dist[st] = 0;
    que.push({dist[st], st});
    // for (int i = 0 ; i < n ; i++) {
    //     for (int j = 0 ; j < n ; j++) std::cout << g[i][j] << ' ';
    //     std::cout << std::endl;
    // }
    while (que.size()) {
        auto [d, v] = que.top();
        que.pop();
        if (Bigger(d, dist[v])) continue;
        for (int i = 0 ; i < n ; i++) if (Smaller(d + g[v][i], dist[i])) {
            dist[i] = d + g[v][i];
            que.push({dist[i], i});
        }
    }
    return dist[go];
}
int main() {
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
    std::ios::sync_with_stdio(false);
    std::cout << std::fixed << std::setprecision(5);
    while (true) {
        std::cin >> N;
        if (N == 0) break;
        for (int i = 0 ; i < N ; i++) {
            std::cin >> NV[i] >> H[i];
            Polygon p(NV[i]);
            for (int j = 0 ; j < NV[i] ; j++) std::cin >> p[j].x() >> p[j].y();
            P[i] = std::move(p);
        }
        std::cin >> THETA >> GAMMA;
        THETA = ArcToRadian(THETA);
        GAMMA = ArcToRadian(GAMMA);
        std::cin >> START.x() >> START.y() >> GOAL.x() >> GOAL.y();
        std::cout << solve() << '\n';
    }
}
accuracy: 1
time_ms: 750
memory_kb: 4608
final_rank: 16

code_snippet: #include "bits/stdc++.h"
using namespace std;

#define REP(i, n) for (int i = 0; i < n; i++)
#define REPR(i, n) for (int i = n; i >= 0; i--)
#define FOR(i, m, n) for (ll i = m; i < n; i++)
#define FORR(i, m, n) for (ll i = m; i >= n; i--)
#define REPO(i, n) for (ll i = 1; i <= n; i++)
#define ll long long
#define INFll (ll)1ll << 60 
#define MINFll (-1 * INFll)
#define ALL(n) n.begin(), n.end()
#define MOD (ll)998244353
#define P pair<ll, ll>


//ok -> verified
using Real = double;
using Pt = complex<Real>;
const Real EPS = 1e-8;

int sgn(Real a){//ok
    return (a < -EPS) ? -1 : (a > EPS) ? 1 : 0;
}
inline Pt operator*= (const Pt &a, const Real &b){
    return Pt(real(a) * b, imag(a) * b);
}

inline Pt operator* (const Pt &a, const Real &b){
    return a *= b;
}

namespace std {
    bool operator<(const Pt &a, const Pt &b){
        return a.real() != b.real() ? a.real() < b.real() : a.imag() < b.imag();
    }
}

istream &operator>> (istream &is, Pt &p){//ok
    Real a, b;
    is >> a >> b;
    p = Pt(a, b);
    return is;
}

Real dot(const Pt &a, const Pt &b) {//ok
    return real(a) * real(b) + imag(a) * imag(b);
}

Real cross(const Pt &a, const Pt &b){//ok
    return real(a) * imag(b) - real(b) * imag(a);
}

Real arg(const Pt &a){
    return atan2(imag(a), real(a));
}

int ccw(const Pt &a, const Pt &b, const Pt &c){//ok
    if (cross(b - a, c - a) > EPS) return 1; //CCW
    if (cross(b - a, c - a) < -EPS) return -1; //CW
    if (dot(a - b, c - b) < -EPS) return 2; //a-b-c
    if (dot(b - a, c - a) < -EPS) return -2; //c-a-b
    return 0; //a-c-b(on seg)
}

struct Line{//ok
    Pt a, b;
    Line(Pt a, Pt b) : a(a), b(b){};
};

struct Segment : Line{
    Segment(Pt a, Pt b) : Line(a, b){};
};

struct Circle{//ok
    Pt a;
    Real r;
    Circle(Pt a, Real r) : a(a), r(r){};
};

using Polygon = vector<Pt>;

Pt projection(const Line &a, const Pt &p){//ok
    Real t = dot(p - a.a, a.a - a.b) / norm(a.a - a.b);
    return a.a + (a.a - a.b) * t;
}

Pt projection(const Segment &a, const Pt &p){//ok
    Real t = dot(p - a.a, a.a - a.b) / norm(a.a - a.b);
    return a.a + (a.a - a.b) * t;
}

Pt reflection(const Line &a, const Pt &b){//ok
    return b + (projection(a, b) - b) * 2.0;
}

Pt reflection(const Segment &a, const Pt &b){//ok
    return b + (projection(a, b) - b) * 2.0;
}

//平行
bool parallel(const Line &a, const Line &b){
    return !sgn(cross(a.b - a.a, b.b - b.a));
}

//直角
bool orthogonal(const Line &a, const Line &b){
    return !sgn(dot(a.a - a.b, b.a - b.b));
}

bool intersect(const Line &a, const Pt &b){
    return abs(ccw(a.a, a.b, b)) != 1;
}

bool intersect(const Line &a, const Line &b){
    return !parallel(a, b);
}

bool intersect(const Line &a, const Segment &b){
    return cross(a.b - a.a, b.a - a.a) * cross(a.b - a.a, b.b - a.a) < EPS;
}

bool intersect(const Segment &a, const Pt &b){//ok
    return ccw(a.a, a.b, b) == 0;
}

bool intersect(const Segment &a, const Segment &b){//ok
    return (sgn(ccw(a.a, a.b, b.a) * ccw(a.a, a.b, b.b)) <= 0 and
    sgn(ccw(b.a, b.b, a.a) * ccw(b.a, b.b, a.b)) <= 0);
}

Real distance(const Line &a, const Pt &b);

bool intersect(const Circle &a, const Pt &b){
    return sgn(abs(a.a - b) - a.r) == 0;
}

bool intersect(const Circle &a, const Line &b){
    return sgn(distance(b, a.a) - a.r) == -1;
}

int intersect(const Circle &a, const Segment &b){
    if (norm(projection(b, a.a) - a.a) - a.r * a.r > EPS) return 0;
    auto d1 = abs(a.a - b.a), d2 = abs(a.a - b.b);
    if (d1 < a.r + EPS and d2 < a.r + EPS) return 0;
    if (d1 < a.r - EPS and d2 > a.r + EPS or d1 > a.r + EPS and d2 < a.r - EPS) return 1;
    const Pt j = projection(b, a.a);
    if (dot(b.a - j, b.b - j) < 0) return 2;
    return 0;
}

int intersect(Circle a, Circle b){ //共通接線の数 ok
    if (a.r < b.r) swap(a, b);
    Real d = abs(a.a - b.a);
    if (sgn(a.r + b.r - d) == -1) return 4;
    if (sgn(a.r + b.r - d) == 0) return 3;
    if (sgn(a.r - b.r - d) == -1) return 2;
    if (sgn(a.r - b.r - d) == 0) return 1;
    return 0;
}

Real distance(const Pt &a, const Pt &b){
    return abs(a - b);
}

Real distance(const Line &a, const Pt &b){
    return abs(b - projection(a, b));
}

Real distance(const Line &a, const Line &b){
    return intersect(a, b) ? 0 : distance(a, b.a);
}

Real distance(const Line &a, const Segment &b){
    return intersect(a, b) ? 0 : min(distance(a, b.a), distance(a, b.b));
}

Real distance(const Segment &a, const Pt &b){//ok
    Pt p = projection(a, b);
    if (intersect(a, p)) return abs(p - b);
    return min(abs(a.a - b), abs(a.b - b));
}

Real distance(const Segment &a, const Segment &b){//ok
    if (intersect(a, b)) return 0;
    return min({distance(a, b.a), distance(a, b.b),
                distance(b, a.a), distance(b, a.b)});
}

Pt crosspoint(const Line &a, const Line &b){//ok
    return a.a + (a.b - a.a) * cross(b.a - a.a, b.b - b.a) / cross(a.b - a.a, b.b - b.a);
}

Pt crosspoint(const Segment &a, const Segment &b){//ok
    return crosspoint(Line(a), Line(b));
}

pair<Pt, Pt> crosspoint(const Circle &a, const Line &b){//ok
    Pt pt = projection(b, a.a);
    Pt e = (b.b - b.a) / abs(b.b - b.a);
    if (!sgn(distance(b, a.a) - a.r)) return {pt, pt};
    double base = sqrt(a.r * a.r - norm(pt - a.a));
    return {pt - e * base, pt + e * base};
}

pair<Pt, Pt> crosspoint(const Circle &a, const Segment &b){
    Line c = Line(b);
    if (intersect(a, b) == 2) return crosspoint(a, c);
    auto res = crosspoint(a, c);
    if (dot(b.a - res.first, b.b - res.first) < 0) res.second = res.first;
    return res;
}

bool is_convex(const Polygon &p){//ok
    ll sz = p.size();
    REP(i, sz){
        if (ccw(p[(i - 1 + sz) % sz], p[i], p[(i + 1) % sz]) == -1) return false;
    }
    return true;
}

Polygon convex_hull(Polygon &p){//ok
    int n = p.size(), k = 0;
    sort(ALL(p));
    vector<Pt> ch(2 * n);
    for (int i = 0; i < n; ch[k++] = p[i++])
        while (k >= 2 and ccw(ch[k - 2], ch[k - 1], p[i]) <= 0) k--;
    for (int i = n - 2, t = k + 1; i >= 0; ch[k++] = p[i--])
        while (k >= t and ccw(ch[k - 2], ch[k - 1], p[i]) <= 0) k--;
    ch.resize(k - 1);
    return ch;
}

const double pi = acos(-1.0);

void solve(ll n){
    vector<Polygon> s(n);
    vector<ll> sn(n), h(n);
    double theta, phi;
    
    REP(i, n){
        cin >> sn[i] >> h[i];
        REP(j, sn[i]){
            ll a, b;
            cin >> a >> b;
            s[i].push_back(Pt(a, b));
        }
    }
    cin >> theta >> phi;
    theta *= (double)pi / 180.0;
    phi *= (double)pi / 180.0;

    ll sx, sy, tx, ty;
    Pt sp, tp;
    cin >> sx >> sy >> tx >> ty;
    sp = Pt(sx, sy), tp = Pt(tx, ty);

    // 1. 射影をして凸包
    REP(i, n){
        double r = ((double)h[i]) / tan(phi);
        REP(j, sn[i]){
            Pt p = s[i][j];
            double x = p.real() - r * cos(theta);
            double y = p.imag() - r * sin(theta);
            s[i].push_back(Pt(x, y));
        }
        s[i] = convex_hull(s[i]);
        sn[i] = s[i].size();
    }

    vector<vector<double>> dist(n + 2, vector<double>(n + 2));

    // 2. 凸包同士の距離
    REP(i, n){
        FOR(j, i + 1, n){
            double dmin = (double)(INFll);
            REP(k1, sn[i]){
                REP(k2, sn[j]){
                    Segment s1 = Segment(s[i][k1], s[i][(k1 + 1) % sn[i]]);
                    Segment s2 = Segment(s[j][k2], s[j][(k2 + 1) % sn[j]]);
                    dmin = min(dmin, distance(s1, s2));
                }
            }
            // 含まれる場合
            if(dmin > 0){
                Polygon pl1 = s[i], pl2 = s[j];
                pl1.push_back(s[j][0]);
                pl2.push_back(s[i][0]);
                Polygon c1 = convex_hull(pl1), c2 = convex_hull(pl2);
                bool ok = false;
                REP(k, c1.size()) if(distance(c1[k], s[j][0]) < EPS) ok = true;
                REP(k, c2.size()) if(distance(c2[k], s[i][0]) < EPS) ok = true;
                if(!ok) dmin = 0;
            }
            dist[i][j] = dist[j][i] = dmin;
        }
    }

    // 3. 凸包と点の距離
    REP(i, n){
        double dmin = (double)(INFll);
        REP(k1, sn[i]){
            Segment s1 = Segment(s[i][k1], s[i][(k1 + 1) % sn[i]]);
            dmin = min(dmin, distance(s1, sp));
        }
        // 含まれる場合
        if(dmin > 0){
            Polygon pl1 = s[i];
            pl1.push_back(sp);
            Polygon c1 = convex_hull(pl1);
            bool ok = false;
            REP(k, c1.size()) if(distance(c1[k], sp) < EPS) ok = true;
            if(!ok) dmin = 0;
        }
        dist[i][n] = dist[n][i] = dmin;
    }
    REP(i, n){
        double dmin = (double)(INFll);
        REP(k1, sn[i]){
            Segment s1 = Segment(s[i][k1], s[i][(k1 + 1) % sn[i]]);
            dmin = min(dmin, distance(s1, tp));
        }
        // 含まれる場合
        if(dmin > 0){
            Polygon pl1 = s[i];
            pl1.push_back(tp);
            Polygon c1 = convex_hull(pl1);
            bool ok = false;
            REP(k, c1.size()) if(distance(c1[k], tp) < EPS) ok = true;
            if(!ok) dmin = 0;
        }
        dist[i][n + 1] = dist[n + 1][i] = dmin;
    }
    dist[n][n + 1] = dist[n + 1][n] = distance(sp, tp);

    // 4. 適当に最短距離求める
    REP(k, n + 2) REP(i, n + 2) REP(j, n + 2) dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);

    cout << fixed << setprecision(20) << dist[n][n + 1] << endl;
}

int main(){
    ll n;
    while(1){
        cin >> n;
        if(n == 0)return 0;
        solve(n);
    }
}
accuracy: 1
time_ms: 570
memory_kb: 4352
final_rank: 11

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, s, t) for (ll i = s; i < (ll)(t); i++)
#define rrep(i, s, t) for(ll i = (ll)(t) - 1; i >= (ll)(s); i--)
#define all(x) begin(x), end(x)
#define rall(x) rbegin(x), rend(x)

#define TT template<typename T>
TT using vec = vector<T>;
template<class T1, class T2> bool chmin(T1 &x, T2 y) { return x > y ? (x = y, true) : false; }
template<class T1, class T2> bool chmax(T1 &x, T2 y) { return x < y ? (x = y, true) : false; }

struct io_setup {
    io_setup() {
        ios::sync_with_stdio(false);
        std::cin.tie(nullptr);
        cout << fixed << setprecision(15);
    }
} io_setup;

using Point = complex<long double>;
using Polygon = vector<Point>;

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

const long double pi = acos(-1.0);
const long double EPS = 1e-12;
const long double INF=1e9;

// 内積
long double dot(const Point &a, const Point &b) {
    return (a.real() * b.real() + a.imag() * b.imag());
}

// 外積
long double cross(const Point &a, const Point &b) {
    return (a.real() * b.imag() - a.imag() * b.real());
}

// 凸包
Polygon convex_hull(vector<Point> &ps) {
    int N = ps.size();
    auto compare = [](const Point &p1, const Point &p2) {
        if (p1.real() != p2.real()) return p1.real() < p2.real();
        return p1.imag() < p2.imag();
    };
    sort(ps.begin(), ps.end(), compare);
    int k = 0;
    Polygon qs(2 * N);
    // 下側凸包
    for (int i = 0; i < N; i++) {
        while (k > 1 && cross(qs[k - 1] - qs[k - 2], ps[i] - qs[k - 1]) < EPS) k--;
        qs[k++] = ps[i];
    }
    // 上側凸包
    for (int i = N - 2, t = k; i >= 0; i--) {
        while (k > t && cross(qs[k - 1] - qs[k - 2], ps[i] - qs[k - 1]) < EPS) k--;
        qs[k++] = ps[i];
    }
    qs.resize(k - 1);
    return qs;
}

// p0 から p1 へ結んだベクトルから見た p2 の位置
int counter_clockwise(const Point &p2, Point p0, Point p1) {
    // 反時計回り
    if (cross(p1 - p0, p2 - p0) > EPS) {
        return 1;
    }
    // 時計回り
    if (cross(p1 - p0, p2 - p0) < -EPS) {
        return -1;
    }
    // p2, p0, p1 の順で同一直線上
    if (dot(p1 - p0, p2 - p0) < -EPS) {
        return 2;
    }
    // p0, p1, p2 の順で同一直線上
    if (dot(p1 - p0, p2 - p0) > norm(p1 - p0) + EPS) {
        return -2;
    }
    // p2 は p0 と p1 を結ぶ線分上
    return 0;
}

// 多角形と点の包含関係
// 0 : 含まれない
// 1 : 辺の上にある
// 2 : 内部に含まれる
int is_contained(const Point p, const Polygon poly) {
    int N = poly.size();
    int cnt = 0;
    for (int i = 0; i < N; i++) {
        if (counter_clockwise(p, poly[i], poly[(i + 1) % N]) == 0) {
            return 1;
        }
        Point a = poly[i], b = poly[(i + 1) % N];
        if (a.imag() > b.imag()) swap(a, b);
        if (p.imag() < b.imag() + EPS && p.imag() > a.imag() + EPS && counter_clockwise(p, a, b) < 0) {
            cnt++;
        }
    }
    if (cnt % 2 == 0) return 0;
    else return 2;
}

// 直線 l に対する点 p の射影
Point projection(const Line l, const Point &p) {
    long double t = dot(p - l.a, l.b - l.a) / norm(l.b - l.a);
    return Point(l.a.real() + t * (l.b - l.a).real(), l.a.imag() + t * (l.b - l.a).imag());
}

int main() {
    while(1){
        int N;
        cin>>N;
        if(N==0)return 0;
        vector<int>NV(N),H(N);
        vector<Polygon>P(N);
        rep(i,0,N){
            cin>>NV[i]>>H[i];
            rep(j,0,NV[i]){
                int x,y;
                cin>>x>>y;
                Point p(x,y);
                P[i].push_back(p);
            }
        }
        long double theta,phi;
        {
            int t,p;
            cin>>t>>p;
            theta=t*2*pi/360.0;
            phi=p*2*pi/360.0;
        }
        int sx,sy,tx,ty;
        cin>>sx>>sy>>tx>>ty;
        vector<Polygon>plys;
        plys.push_back({Point(sx,sy)});
        rep(i,0,N){
            vector<Point>ps;
            for(auto p:P[i]){
                ps.push_back(p);
                ps.push_back(p-Point(H[i]/tan(phi)*cos(theta),H[i]/tan(phi)*sin(theta)));
            }
            Polygon ply=convex_hull(ps);
            plys.push_back(ply);
        }
        plys.push_back({Point(tx,ty)});
        auto f=[&](int i,int j)-> long double {
            if(plys[j].size()>1){
                for(auto p:plys[i]){
                    if(is_contained(p,plys[j])!=0)return 0;
                }
            }
            if(plys[i].size()>1){
                for(auto p:plys[j]){
                    if(is_contained(p,plys[i])!=0)return 0;
                }
            }
            long double ret=INF;
            if(plys[j].size()>1){
                for(auto p:plys[i]){
                    int s=plys[j].size();
                    for(int k=0;k<s;k++){
                        Line l(plys[j][k],plys[j][(k+1)%s]);
                        Point pro=projection(l,p);
                        if(counter_clockwise(pro,plys[j][k],plys[j][(k+1)%s])==0){
                            chmin(ret,abs(pro-p));
                        }
                    }
                }
            }
            if(plys[i].size()>1){
                for(auto p:plys[j]){
                    int s=plys[i].size();
                    for(int k=0;k<s;k++){
                        Line l(plys[i][k],plys[i][(k+1)%s]);
                        Point pro=projection(l,p);
                        if(counter_clockwise(pro,plys[i][k],plys[i][(k+1)%s])==0){
                            chmin(ret,abs(pro-p));
                        }
                    }
                }
            }
            for(auto p:plys[i]){
                for(auto q:plys[j]){
                    chmin(ret,abs(p-q));
                }
            }
            return ret;
        };
        vector<long double>D(N+2,INF);
        D[0]=0;
        priority_queue<pair<long double,int>,vector<pair<long double,int>>,greater<pair<long double,int>>>pq;
        pq.push({D[0],0});
        while(pq.size()){
            auto[d,x]=pq.top();
            pq.pop();
            if(D[x]<d-EPS)continue;
            rep(i,0,N+2){
                if(i==x)continue;
                long double cost=f(i,x);
                if(D[i]>D[x]+cost+EPS){
                    D[i]=D[x]+cost;
                    pq.push({D[i],i});
                }
            }
        }
        cout<<D[N+1]<<endl;
    }
}
accuracy: 1
time_ms: 1120
memory_kb: 4224
final_rank: 6

code_snippet: #include <bits/stdc++.h>
using namespace std;

using ll = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
using vi = vector<int>;
using vl = vector<ll>;
#define rep3(i, a, b, c) for (ll i = (a); i < (b); i += (c))
#define rep2(i, a, b) rep3(i, a, b, 1)
#define rep1(i, n) rep2(i, 0, n)
#define rep0(n) rep1(aaaaa, n)
#define ov4(a, b, c, d, name, ...) name
#define rep(...) ov4(__VA_ARGS__, rep3, rep2, rep1, rep0)(__VA_ARGS__)
#define per(i, a, b) for (ll i = (a) - 1; i >= (b); i--)
#define fore(e, v) for (auto&& e : v)
#define all(a) begin(a), end(a)
#define sz(a) (int)(size(a))
#define lb(v, x) (lower_bound(all(v), x) - begin(v))
#define eb emplace_back

template <typename T, typename S>
bool chmin(T& a, const S& b) {
  return a > b ? a = b, 1 : 0;
}
template <typename T, typename S>
bool chmax(T& a, const S& b) {
  return a < b ? a = b, 1 : 0;
}
const int INF = 1e9 + 100;
const ll INFL = 3e18 + 100;
#define i128 __int128_t
struct _ {
  _() { cin.tie(0)->sync_with_stdio(0), cout.tie(0); }
} __;

const double eps = 1e-6;

struct point {
  double first, second;
  point operator+(const point& b) const { return point{first + b.first, second + b.second}; }
  point operator-(const point& b) const {
    return point{first - b.first, second - b.second};
  }
  bool operator<(const point& b) const {
    return (first < b.first || (first == b.first && second < b.second));
  }
  bool operator==(const point& b) const {
    return first == b.first && second == b.second;
  }
  point operator*(double x) { return point{first * x, second * x}; }
  point operator/(double x) { return point{first / x, second / x}; }
};
vi argsort(const vector<point>& A) {
  vi ids(A.size());
  iota(all(ids), 0);
  sort(all(ids),
       [&](int i, int j) { return (A[i] == A[j] ? i < j : A[i] < A[j]); });
  return ids;
}  // 7ad053b1

vi convex_hull(const vector<point>& XY) {
  ll N = XY.size();
  if (N == 0) return {};
  if (N == 1) return {0};
  if (N == 2) return {0, 1};
  auto I = argsort(XY);
  auto check = [&](ll i, ll j, ll k) -> bool {
    auto xi = XY[i].first, yi = XY[i].second;
    auto xj = XY[j].first, yj = XY[j].second;
    auto xk = XY[k].first, yk = XY[k].second;
    auto dx1 = xj - xi, dy1 = yj - yi;
    auto dx2 = xk - xj, dy2 = yk - yj;
    double det = dx1 * dy2 - dy1 * dx2;
    // return det >= 0; //辺上の点を含む場合
    return det > 0;
  };
  auto calc = [&]() {
    vi P;
    for (auto&& k : I) {
      if (P.size() && XY[P.back()] == XY[k]) continue;
      while (P.size() > 1) {
        auto i = P[P.size() - 2];
        auto j = P[P.size() - 1];
        if (check(i, j, k)) break;
        P.pop_back();
      }
      P.push_back(k);
    }
    return P;
  };
  // 上側
  vi P;
  {
    vi Q1 = calc();
    P.insert(P.end(), all(Q1));
  }
  // 下側
  {
    if (!P.empty()) P.pop_back();
    reverse(all(I));
    vi Q2 = calc();
    P.insert(P.end(), all(Q2));
  }
  if (P.size() >= 2 && XY[P[0]] == XY[P.back()]) P.pop_back();
  return P;
}  // 292fabb8

double dot(point a, point b) { return a.first * b.first + a.second * b.second; }
double cross(point a, point b) {
  return a.first * b.second - a.second * b.first;
}
double dist(point a, point b) {
  return sqrt(pow(a.first - b.first, 2) + pow(a.second - b.second, 2));
}
struct line {
  point A, B;
};
point vec(line l) { return l.B - l.A; }
double abs(point p) { return sqrt(pow(p.first, 2) + pow(p.second, 2)); }

bool point_on_segment(point P, line L) {
  return dot(P - L.A, vec(L)) > -eps && dot(P - L.B, vec(L)) < eps;
}
point projection(point P, line L) {
  return L.A + vec(L) / abs(vec(L)) * dot(P - L.A, vec(L)) / abs(vec(L));
}
point reflection(point P, line L) { return projection(P, L) * 2 - P; }
double point_line_distance(point P, line L) {
  return abs(cross(P - L.A, vec(L))) / abs(vec(L));
}
double point_segment_distance(point P, line L) {
  if (dot(P - L.A, vec(L)) < 0) {
    return dist(P, L.A);
  } else if (dot(P - L.B, vec(L)) > 0) {
    return dist(P, L.B);
  } else {
    return point_line_distance(P, L);
  }
}  // 60814b22
bool is_parallel(line L1, line L2) {
  return abs(cross(vec(L1), vec(L2))) < eps;
}
point line_intersection(line L1, line L2) {
  return L1.A + vec(L1) * cross(L2.A - L1.A, vec(L2)) / cross(vec(L1), vec(L2));
}  // b7a0ccf0
bool segment_intersect(line L1, line L2) {
  return cross(L1.A - L2.A, vec(L2)) * cross(L1.B - L2.A, vec(L2)) < eps &&
         cross(L2.A - L1.A, vec(L1)) * cross(L2.B - L1.A, vec(L1)) < eps;
}  // c3399131
double segment_distance(line L1, line L2) {
  if (segment_intersect(L1, L2)) {
    return 0;
  } else {
    double ans = INF;
    ans = min(ans, point_segment_distance(L1.A, L2));
    ans = min(ans, point_segment_distance(L1.B, L2));
    ans = min(ans, point_segment_distance(L2.A, L1));
    ans = min(ans, point_segment_distance(L2.B, L1));
    return ans;
  }
}  // ad3f75d4

using Polygon = vector<point>;

const int _ON=-1,_OUT=0,_IN=1;
int contains(const Polygon& Q, const point& p) {
  bool in = false;
  for (int i = 0; i < Q.size(); i++) {
    point a = Q[i] - p, b = Q[(i + 1) % Q.size()] - p;
    if (a.second > b.second) swap(a, b);
    if (a.second <= 0 && 0 < b.second && cross(a, b) < 0) in = !in;
    if (cross(a, b) == 0 && dot(a, b) <= 0) return _ON;
  }
  return in ? _IN : _OUT;
}  // 8a4d35dc

int is_in(const Polygon& p, const point& a) {
  if (a == p[0]) return -1;
  rep(i,sz(p)){
    if(cross(a-p[i],p[(i+1)%sz(p)]-p[i])>0)return 0;
  }
  return 1;
}  // ebe4fdfe

void solve(int);
int main() {
  int N;
  while (1) {
    cin >> N;
    if (N == 0) break;
    solve(N);
  }
}

void solve(int N) {
  using PointSet = vector<point>;
  vector<PointSet> Po(N);
  vi H(N);
  rep(i, N) {
    int V;
    cin >> V >> H[i];
    rep(V) {
      double X, Y;
      cin >> X >> Y;
      Po[i].eb(point{X, Y});
    }
  }
  double suntheta, phi;
  cin >> suntheta >> phi;
  const double pi = acos(-1);
  suntheta = (suntheta + 180) / 180 * pi;
  phi = phi / 180 * pi;
  point S, G;
  cin >> S.first >> S.second >> G.first >> G.second;
  using polyline = vector<line>;
  vector<polyline> Pol(N);
  vector<Polygon> V(N);
  rep(i, N) {
    int v = sz(Po[i]);
    rep(j, v) {
      auto [X, Y] = Po[i][j];
      Po[i].eb(point{X + cos(suntheta) * H[i] / tan(phi),
                     Y + sin(suntheta) * H[i] / tan(phi)});
    }
    auto idx = convex_hull(Po[i]);
    rep(j, sz(idx)) {
      V[i].eb(Po[i][idx[j]]);
      Pol[i].eb(line{Po[i][idx[j]], Po[i][idx[(j + 1) % sz(idx)]]});
    }
  }
  // rep(i,N){
  //   cerr<<"polygon: "<<i<<endl;
  //   // fore(p,Pol[i]){
  //   //   cerr<<p.A.first<<' '<<p.A.second<<' '<<p.B.first<<' '<<p.B.second<<endl;
  //   // }
  //   // fore(p,V[i]){
  //   //   cerr<<p.first<<' '<<p.second<<endl;
  //   // }
  // }
  // polygon: 0~N-1, S: N, G: N+1
  vector cost(N+2,vector(N+2,1e18));
  rep(i,N+2)cost[i][i]=0;
  cost[N][N+1]=cost[N+1][N]=dist(S,G);
  rep(i,N){
    if(is_in(V[i],S)){
      cost[i][N]=0;
      cost[N][i]=0;
    }
    if(is_in(V[i],G)){
      cost[i][N+1]=0;
      cost[N+1][i]=0;
    }
    fore(l,Pol[i])chmin(cost[i][N],point_segment_distance(S,l));
    fore(l,Pol[i])chmin(cost[N][i],point_segment_distance(S,l));
    fore(l,Pol[i])chmin(cost[i][N+1],point_segment_distance(G,l));
    fore(l,Pol[i])chmin(cost[N+1][i],point_segment_distance(G,l));
  }
  rep(i,N){
    rep(j,N){
      if(i==j)continue;
      fore(l,Pol[i]){
        fore(p,V[j]){
          chmin(cost[i][j],point_segment_distance(p,l));
        }
      }
      fore(l,Pol[j]){
        fore(p,V[i]){
          chmin(cost[i][j],point_segment_distance(p,l));
        }
      }
      fore(l1,Pol[i]){
        fore(l2,Pol[j]){
          if(!is_parallel(l1,l2)&&segment_intersect(l1,l2))cost[i][j]=cost[j][i]=0;
        }
      }
    }
  }
  // cerr<<endl;
  // rep(i,N+2){
  //   rep(j,N+2)cerr<<cost[i][j]<<' ';
  //   cerr<<endl;
  // }
  // cerr<<"\n\n";

  rep(k,N+2){
    rep(i,N+2){
      rep(j,N+2){
        chmin(cost[i][j],cost[i][k]+cost[k][j]);
      }
    }
  }
  // rep(i,N+2){
  //   rep(j,N+2)cerr<<cost[i][j]<<' ';
  //   cerr<<endl;
  // }
  cout<<setprecision(10)<<fixed<<cost[N][N+1]<<'\n';
}
accuracy: 1
time_ms: 160
memory_kb: 4352
final_rank: 7

code_snippet: #include <bits/stdc++.h>
using namespace std;

using ll = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
using vi = vector<int>;
using vl = vector<ll>;
#define rep3(i, a, b, c) for (ll i = (a); i < (b); i += (c))
#define rep2(i, a, b) rep3(i, a, b, 1)
#define rep1(i, n) rep2(i, 0, n)
#define rep0(n) rep1(aaaaa, n)
#define ov4(a, b, c, d, name, ...) name
#define rep(...) ov4(__VA_ARGS__, rep3, rep2, rep1, rep0)(__VA_ARGS__)
#define per(i, a, b) for (ll i = (a) - 1; i >= (b); i--)
#define fore(e, v) for (auto&& e : v)
#define all(a) begin(a), end(a)
#define sz(a) (int)(size(a))
#define lb(v, x) (lower_bound(all(v), x) - begin(v))
#define eb emplace_back

template <typename T, typename S>
bool chmin(T& a, const S& b) {
  return a > b ? a = b, 1 : 0;
}
template <typename T, typename S>
bool chmax(T& a, const S& b) {
  return a < b ? a = b, 1 : 0;
}
const int INF = 1e9 + 100;
const ll INFL = 3e18 + 100;
#define i128 __int128_t
struct _ {
  _() { cin.tie(0)->sync_with_stdio(0), cout.tie(0); }
} __;

const double eps = 1e-10;

struct point {
  double first, second;
  point operator+(const point& b) const { return point{first + b.first, second + b.second}; }
  point operator-(const point& b) const {
    return point{first - b.first, second - b.second};
  }
  bool operator<(const point& b) const {
    return (first < b.first || (first == b.first && second < b.second));
  }
  bool operator==(const point& b) const {
    return first == b.first && second == b.second;
  }
  point operator*(double x) { return point{first * x, second * x}; }
  point operator/(double x) { return point{first / x, second / x}; }
};
vi argsort(const vector<point>& A) {
  vi ids(A.size());
  iota(all(ids), 0);
  sort(all(ids),
       [&](int i, int j) { return (A[i] == A[j] ? i < j : A[i] < A[j]); });
  return ids;
}  // 7ad053b1

vi convex_hull(const vector<point>& XY) {
  ll N = XY.size();
  if (N == 0) return {};
  if (N == 1) return {0};
  if (N == 2) return {0, 1};
  auto I = argsort(XY);
  auto check = [&](ll i, ll j, ll k) -> bool {
    auto xi = XY[i].first, yi = XY[i].second;
    auto xj = XY[j].first, yj = XY[j].second;
    auto xk = XY[k].first, yk = XY[k].second;
    auto dx1 = xj - xi, dy1 = yj - yi;
    auto dx2 = xk - xj, dy2 = yk - yj;
    double det = dx1 * dy2 - dy1 * dx2;
    // return det >= 0; //辺上の点を含む場合
    return det > 0;
  };
  auto calc = [&]() {
    vi P;
    for (auto&& k : I) {
      if (P.size() && XY[P.back()] == XY[k]) continue;
      while (P.size() > 1) {
        auto i = P[P.size() - 2];
        auto j = P[P.size() - 1];
        if (check(i, j, k)) break;
        P.pop_back();
      }
      P.push_back(k);
    }
    return P;
  };
  // 上側
  vi P;
  {
    vi Q1 = calc();
    P.insert(P.end(), all(Q1));
  }
  // 下側
  {
    if (!P.empty()) P.pop_back();
    reverse(all(I));
    vi Q2 = calc();
    P.insert(P.end(), all(Q2));
  }
  if (P.size() >= 2 && XY[P[0]] == XY[P.back()]) P.pop_back();
  return P;
}  // 292fabb8

double dot(point a, point b) { return a.first * b.first + a.second * b.second; }
double cross(point a, point b) {
  return a.first * b.second - a.second * b.first;
}
double dist(point a, point b) {
  return sqrt(pow(a.first - b.first, 2) + pow(a.second - b.second, 2));
}
struct line {
  point A, B;
};
point vec(line l) { return l.B - l.A; }
double abs(point p) { return sqrt(pow(p.first, 2) + pow(p.second, 2)); }

bool point_on_segment(point P, line L) {
  return dot(P - L.A, vec(L)) > -eps && dot(P - L.B, vec(L)) < eps;
}
point projection(point P, line L) {
  return L.A + vec(L) / abs(vec(L)) * dot(P - L.A, vec(L)) / abs(vec(L));
}
point reflection(point P, line L) { return projection(P, L) * 2 - P; }
double point_line_distance(point P, line L) {
  return abs(cross(P - L.A, vec(L))) / abs(vec(L));
}
double point_segment_distance(point P, line L) {
  if (dot(P - L.A, vec(L)) < 0) {
    return dist(P, L.A);
  } else if (dot(P - L.B, vec(L)) > 0) {
    return dist(P, L.B);
  } else {
    return point_line_distance(P, L);
  }
}  // 60814b22
bool is_parallel(line L1, line L2) {
  return abs(cross(vec(L1), vec(L2))) < eps*1e3;
}
point line_intersection(line L1, line L2) {
  return L1.A + vec(L1) * cross(L2.A - L1.A, vec(L2)) / cross(vec(L1), vec(L2));
}  // b7a0ccf0
bool segment_intersect(line L1, line L2) {
  return cross(L1.A - L2.A, vec(L2)) * cross(L1.B - L2.A, vec(L2)) < eps &&
         cross(L2.A - L1.A, vec(L1)) * cross(L2.B - L1.A, vec(L1)) < eps;
}  // c3399131
double segment_distance(line L1, line L2) {
  if (segment_intersect(L1, L2)) {
    return 0;
  } else {
    double ans = INF;
    ans = min(ans, point_segment_distance(L1.A, L2));
    ans = min(ans, point_segment_distance(L1.B, L2));
    ans = min(ans, point_segment_distance(L2.A, L1));
    ans = min(ans, point_segment_distance(L2.B, L1));
    return ans;
  }
}  // ad3f75d4

using Polygon = vector<point>;

const int _ON=-1,_OUT=0,_IN=1;
int contains(const Polygon& Q, const point& p) {
  bool in = false;
  for (int i = 0; i < Q.size(); i++) {
    point a = Q[i] - p, b = Q[(i + 1) % Q.size()] - p;
    if (a.second > b.second) swap(a, b);
    if (a.second <= 0 && 0 < b.second && cross(a, b) < 0) in = !in;
    if (cross(a, b) == 0 && dot(a, b) <= 0) return _ON;
  }
  return in ? _IN : _OUT;
}  // 8a4d35dc

int is_in(const Polygon& p, const point& a) {
  if (a == p[0]) return -1;
  rep(i,sz(p)){
    if(cross(a-p[i],p[(i+1)%sz(p)]-p[i])>0)return 0;
  }
  return 1;
}  // ebe4fdfe

void solve(int);
int main() {
  int N;
  while (1) {
    cin >> N;
    if (N == 0) break;
    solve(N);
  }
}

void solve(int N) {
  using PointSet = vector<point>;
  vector<PointSet> Po(N);
  vi H(N);
  rep(i, N) {
    int V;
    cin >> V >> H[i];
    rep(V) {
      double X, Y;
      cin >> X >> Y;
      Po[i].eb(point{X, Y});
    }
  }
  double suntheta, phi;
  cin >> suntheta >> phi;
  const double pi = acos(-1);
  suntheta = (suntheta + 180) / 180 * pi;
  phi = phi * pi / 180;
  point S, G;
  cin >> S.first >> S.second >> G.first >> G.second;
  using polyline = vector<line>;
  vector<polyline> Pol(N);
  vector<Polygon> V(N);
  rep(i, N) {
    int v = sz(Po[i]);
    rep(j, v) {
      auto [X, Y] = Po[i][j];
      Po[i].eb(point{X + cos(suntheta) * H[i] / tan(phi),
                     Y + sin(suntheta) * H[i] / tan(phi)});
    }
    auto idx = convex_hull(Po[i]);
    rep(j, sz(idx)) {
      V[i].eb(Po[i][idx[j]]);
      Pol[i].eb(line{Po[i][idx[j]], Po[i][idx[(j + 1) % sz(idx)]]});
    }
  }
  // rep(i,N){
  //   cerr<<"polygon: "<<i<<endl;
  //   // fore(p,Pol[i]){
  //   //   cerr<<p.A.first<<' '<<p.A.second<<' '<<p.B.first<<' '<<p.B.second<<endl;
  //   // }
  //   fore(p,V[i]){
  //     cerr<<p.first<<' '<<p.second<<endl;
  //   }
  // }
  // polygon: 0~N-1, S: N, G: N+1
  vector cost(N+2,vector(N+2,1e18));
  rep(i,N+2)cost[i][i]=0;
  cost[N][N+1]=cost[N+1][N]=dist(S,G);
  rep(i,N){
    if(is_in(V[i],S)){
      cost[i][N]=0;
      cost[N][i]=0;
    }
    if(is_in(V[i],G)){
      cost[i][N+1]=0;
      cost[N+1][i]=0;
    }
    fore(l,Pol[i])chmin(cost[i][N],point_segment_distance(S,l));
    fore(l,Pol[i])chmin(cost[N][i],point_segment_distance(S,l));
    fore(l,Pol[i])chmin(cost[i][N+1],point_segment_distance(G,l));
    fore(l,Pol[i])chmin(cost[N+1][i],point_segment_distance(G,l));
  }
  rep(i,N){
    rep(j,N){
      if(i==j)continue;
      fore(l,Pol[i]){
        fore(p,V[j]){
          chmin(cost[i][j],point_segment_distance(p,l));
        }
      }
      fore(l,Pol[j]){
        fore(p,V[i]){
          chmin(cost[i][j],point_segment_distance(p,l));
        }
      }
      fore(l1,Pol[i]){
        fore(l2,Pol[j]){
          if(!is_parallel(l1,l2)&&segment_intersect(l1,l2))cost[i][j]=cost[j][i]=0;
        }
      }
    }
  }
  // cerr<<endl;
  // rep(i,N+2){
  //   rep(j,N+2)cerr<<cost[i][j]<<' ';
  //   cerr<<endl;
  // }
  // cerr<<"\n\n";

  rep(k,N+2){
    rep(i,N+2){
      rep(j,N+2){
        chmin(cost[i][j],cost[i][k]+cost[k][j]);
      }
    }
  }
  // rep(i,N+2){
  //   rep(j,N+2)cerr<<cost[i][j]<<' ';
  //   cerr<<endl;
  // }
  cout<<setprecision(10)<<fixed<<cost[N][N+1]<<'\n';
}
accuracy: 1
time_ms: 160
memory_kb: 4352
final_rank: 7

code_snippet: #include<bits/stdc++.h>
using namespace std;

namespace noya2 {

template <typename T, typename U>
ostream &operator<<(ostream &os, const pair<T, U> &p){
    os << p.first << " " << p.second;
    return os;
}
template <typename T, typename U>
istream &operator>>(istream &is, pair<T, U> &p){
    is >> p.first >> p.second;
    return is;
}

template <typename T>
ostream &operator<<(ostream &os, const vector<T> &v){
    int s = (int)v.size();
    for (int i = 0; i < s; i++) os << (i ? " " : "") << v[i];
    return os;
}
template <typename T>
istream &operator>>(istream &is, vector<T> &v){
    for (auto &x : v) is >> x;
    return is;
}

void in() {}
template <typename T, class... U>
void in(T &t, U &...u){
    cin >> t;
    in(u...);
}

void out() { cout << "\n"; }
template <typename T, class... U, char sep = ' '>
void out(const T &t, const U &...u){
    cout << t;
    if (sizeof...(u)) cout << sep;
    out(u...);
}

template<typename T>
void out(const vector<vector<T>> &vv){
    int s = (int)vv.size();
    for (int i = 0; i < s; i++) out(vv[i]);
}

struct IoSetup {
    IoSetup(){
        cin.tie(nullptr);
        ios::sync_with_stdio(false);
        cout << fixed << setprecision(15);
        cerr << fixed << setprecision(7);
    }
} iosetup_noya2;

const int iinf = 1'000'000'007;
const long long linf = 2'000'000'000'000'000'000LL;
const long long mod998 =  998244353;
const long long mod107 = 1000000007;
const long double pi = 3.14159265358979323;
const vector<int> dx = {0,1,0,-1,1,1,-1,-1};
const vector<int> dy = {1,0,-1,0,1,-1,-1,1};
const string ALP = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const string alp = "abcdefghijklmnopqrstuvwxyz";
const string NUM = "0123456789";

void yes(){ cout << "Yes\n"; }
void no(){ cout << "No\n"; }
void YES(){ cout << "YES\n"; }
void NO(){ cout << "NO\n"; }
void yn(bool t){ t ? yes() : no(); }
void YN(bool t){ t ? YES() : NO(); }


unsigned long long inner_binary_gcd(unsigned long long a, unsigned long long b){
    if (a == 0 || b == 0) return a + b;
    int n = __builtin_ctzll(a); a >>= n;
    int m = __builtin_ctzll(b); b >>= m;
    while (a != b) {
        int mm = __builtin_ctzll(a - b);
        bool f = a > b;
        unsigned long long c = f ? a : b;
        b = f ? b : a;
        a = (c - b) >> mm;
    }
    return a << min(n, m);
}

template<typename T> T gcd_fast(T a, T b){ return static_cast<T>(inner_binary_gcd(abs(a),abs(b))); }

long long sqrt_fast(long long n) {
    if (n <= 0) return 0;
    long long x = sqrt(n);
    while ((x + 1) * (x + 1) <= n) x++;
    while (x * x > n) x--;
    return x;
}

template<typename T> T floor_div(const T n, const T d) {
    assert(d != 0);
    return n / d - static_cast<T>((n ^ d) < 0 && n % d != 0);
}

template<typename T> T ceil_div(const T n, const T d) {
    assert(d != 0);
    return n / d + static_cast<T>((n ^ d) >= 0 && n % d != 0);
}

template<typename T> void uniq(vector<T> &v){
    sort(v.begin(),v.end());
    v.erase(unique(v.begin(),v.end()),v.end());
}

template <typename T, typename U> inline bool chmin(T &x, U y) { return (y < x) ? (x = y, true) : false; }

template <typename T, typename U> inline bool chmax(T &x, U y) { return (x < y) ? (x = y, true) : false; }

template<typename T> inline bool range(T l, T x, T r){ return l <= x && x < r; }

} // namespace noya2

#define rep(i,n) for (int i = 0; i < (int)(n); i++)
#define repp(i,m,n) for (int i = (m); i < (int)(n); i++)
#define reb(i,n) for (int i = (int)(n-1); i >= 0; i--)
#define all(v) (v).begin(),(v).end()

using ll = long long;
using ld = long double;
using uint = unsigned int;
using ull = unsigned long long;
using pii = pair<int,int>;
using pll = pair<ll,ll>;
using pil = pair<int,ll>;
using pli = pair<ll,int>;

namespace noya2{

/*　~ (. _________ . /)　*/

}

using namespace noya2;

template<class T>
struct is_floating_point_number : std::bool_constant<false> {};

template<std::floating_point F>
struct is_floating_point_number<F> : std::bool_constant<true> {};


template<class F>
concept FloatingPoint = is_floating_point_number<F>::value;

template<FloatingPoint Float>
constexpr Float EPS = Float{1e-8};

template<FloatingPoint Float>
constexpr int sign(const Float &x, Float eps = EPS<Float>){
    return (x < -eps ? -1 : x > eps ? 1 : 0);
}

template<FloatingPoint Float>
struct point {
    Float x, y;
    point () {}
    point (Float _x, Float _y) : x(_x), y(_y) {}
    point &operator+=(const point &r){
        x += r.x;
        y += r.y;
        return *this;
    }
    point &operator-=(const point &r){
        x -= r.x;
        y -= r.y;
        return *this;
    }
    point operator+() const {
        return *this;
    }
    point operator-() const {
        x = -x;
        y = -y;
        return *this;
    }
    point &operator*=(const Float &a){
        x *= a;
        y *= a;
        return *this;
    }
	point &operator/=(const Float &a){
        x /= a;
        y /= a;
        return *this;
    }
    friend point operator+(const point& lhs, const point& rhs){
        return point(lhs) += rhs;
    }
    friend point operator-(const point& lhs, const point& rhs){
        return point(lhs) -= rhs;
    }
    friend point operator*(const point& lhs, const Float& a){
        return point(lhs) *= a;
    }
    friend point operator*(const Float& a, const point& rhs){
        return point(rhs) *= a;
    }
	friend point operator/(const point& lhs, const Float& a){
        return point(lhs) /= a;
    }
    auto operator<=>(const point&) const = default;
    // friend bool operator<(const point &lhs, const point &rhs){
    //     if (lhs.x == rhs.x) return lhs.y < rhs.y;
    //     return lhs.x < rhs.x;
    // }
    // friend bool operator==(const point &lhs, const point &rhs){
    //     return (lhs.x == rhs.x && lhs.y == rhs.y);
    // }
    friend std::ostream &operator<<(std::ostream &os, const point& p){
        return os << p.x << ' ' << p.y;
    }
    friend std::istream &operator>>(std::istream &is, point &a){
        Float _x, _y; is >> _x >> _y;
        a = point(_x, _y);
        return (is);
    }
    friend Float norm(const point &a) {
        return a.x*a.x + a.y*a.y;
    }
    friend Float dot(const point &a, const point &b){
        return a.x*b.x + a.y*b.y;
    }
    friend Float cross(const point &a, const point &b){
        return a.x*b.y - a.y*b.x;
    }
	friend point rot90(const point &a){
		return point(-a.y, a.x);
	}
    friend point rot(const point &a, const Float rad){
        using std::cos, std::sin;
        Float cosrad = cos(rad);
        Float sinrad = sin(rad);
        return point(a.x * cosrad - a.y * sinrad, a.x * sinrad + a.y * cosrad);
    }
    friend Float abs(const point &a){
        using std::sqrt;
        return sqrt(norm(a));
    }

    friend int quadrant_atan2(const point &a){
        // not origin point
        // ceil ( atan2(y,x) / (pi/2) )
		int signx = sign(a.x);
		int signy = sign(a.y);
		if (signx <= 0 && signy <  0) return -1;
		if (signx >  0 && signy <= 0) return  0;
		if (signx >= 0 && signy >  0) return  1;
		if (signx <  0 && signy >= 0) return  2;
        // origin point x == 0 && y == 0
        return 0;
    }

    friend int sign_cross(const point &a, const point &b){
        using std::abs;
        return sign(cross(a, b), EPS<Float> * (abs(a.x) + abs(a.y) + abs(b.x) + abs(b.y)));
        // return sign(cross(a, b), EPS<Float> * (abs<Float>(a.x) + abs<Float>(a.y) + abs<Float>(b.x) + abs<Float>(b.y)));
    }

    friend int sign_dot(const point &a, const point &b){
        using std::abs;
        return sign(dot(a, b), EPS<Float> * (abs(a.x) + abs(a.y) + abs(b.x) + abs(b.y)));
        // return sign(dot(a, b), EPS<Float> * (abs<Float>(a.x) + abs<Float>(a.y) + abs<Float>(b.x) + abs<Float>(b.y)));
    }

	friend int ccw(const point &a, point b, point c){
		b -= a;
		c -= a;
		int signcr = sign_cross(b, c);
		if (signcr > 0){
			// ccw       
			//           c
			// a --> b 
			return 1;
		}
		if (signcr < 0){
			// cw
			// a --> b
			//           c
			return -1;
		}
		if (sign_dot(b, c) < 0){
			// c  a --> b
			return 2;
		}
		if (norm(b) < norm(c)){
			// a --> b   c
			return -2;
		}
		// a - c -> b
		return 0;
	}
    friend bool has_common_point(const point &a, const point &b){
        return sign(a.x - b.x) == 0 && sign(a.y - b.y) == 0;
    }

    ld arg() const {
        std::complex<ld> a(x, y);
        return std::arg(a);
    }
};

template<FloatingPoint Float>
struct arg_less {
    constexpr bool operator()(const point<Float> &l, const point<Float> &r){
        using std::atan2;
        return atan2(l.y, l.x) < atan2(r.y, r.x);
    }
};

template<FloatingPoint Float>
void arg_sort(std::vector<point<Float>> &a){
	std::sort(a.begin(), a.end(), arg_less<Float>{});
}


template<FloatingPoint Float>
std::vector<int> upper_convex_hull_index(const std::vector<point<Float>> &a){
	if (a.empty()) return {};
	std::vector<int> ids(a.size());
    std::iota(ids.begin(), ids.end(), 0);
	std::sort(ids.begin(), ids.end(), [&](int l, int r){
		return a[l] < a[r];
	});
	std::vector<int> st(a.size());
	int ptr = 0;
	for (int i : ids){
		if (ptr >= 1 && a[st[ptr-1]].x == a[i].x) ptr--;
		while (ptr >= 2){
			int c = st[ptr-1];
			int p = st[ptr-2];
			if (sign_cross(a[i] - a[c], a[c] - a[p]) > 0){
				break;
			}
			ptr--;
		}
		st[ptr++] = i;
	}
	st.resize(ptr);
	return st;
}

template<FloatingPoint Float>
std::vector<int> lower_convex_hull_index(const std::vector<point<Float>> &a){
	if (a.empty()) return {};
	std::vector<int> ids(a.size());
    std::iota(ids.begin(), ids.end(), 0);
	std::sort(ids.begin(), ids.end(), [&](int l, int r){
		return a[l] < a[r];
	});
	std::vector<int> st(a.size());
	int ptr = 0;
	for (int i : ids){
		if (ptr >= 1 && a[st[ptr-1]].x == a[i].x) continue;
		while (ptr >= 2){
			int c = st[ptr-1];
			int p = st[ptr-2];
			if (sign_cross(a[c] - a[p], a[i] - a[c]) > 0){
				break;
			}
			ptr--;
		}
		st[ptr++] = i;
	}
	st.resize(ptr);
	return st;
}

template<FloatingPoint Float>
std::vector<int> convex_hull_index(const std::vector<point<Float>> &a){
	if (a.empty()) return {};
	auto upper = upper_convex_hull_index(a);
	auto lower = lower_convex_hull_index(a);
	if (upper.size() == 1u){
		// lower.size() == 1u
		if (a[upper.front()] == a[lower.front()]){
			return {upper.front()};
		}
		return {lower.front(), upper.front()};
	}
	if (a[upper.back()] == a[lower.back()]){
		lower.pop_back();
	}
	lower.insert(lower.end(), upper.rbegin(), upper.rend());
	if (a[upper.front()] == a[lower.front()]) lower.pop_back();
	return lower;
}


template<FloatingPoint Float>
struct line {
	point<Float> end0, end1;
    line () {}
	line (const point<Float> &_end0, const point<Float> &_end1) : end0(_end0), end1(_end1) {
		assert(end0 != end1);
	}
	// auto operator<=>(const line &) const = default;
	point<Float> direction() const {
		return end1 - end0;
	}
    friend bool is_parallel(const line &a, const line &b){
        return sign_cross(a.direction(), b.direction()) == 0;
    }
    friend bool is_orthgonal(const line &a, const line &b){
        return sign_dot(a.direction(), b.direction()) == 0;
    }
	friend bool has_common_point(const line &a, const point<Float> &b){
		return sign(cross(a.direction(), b - a.end0)) == 0;
	}
	friend bool has_common_point(const line &a, const line &b){
		return !is_parallel(a, b) || has_common_point(a, b.end0);
	}
	friend point<Float> common_point(const line &a, const line &b){
		// assert(has_common_point(a, b));
		if (is_parallel(a, b)){
			return a.end0;
		}
		return a.end0 + a.direction() * cross(b.end0 - a.end0, b.end1 - b.end0) / cross(a.direction(), b.direction());
	}
	friend point<Float> projection(const line &a, const point<Float> &b){
		auto dir = a.direction();
		return a.end0 + dir * (dot(dir, b - a.end0) / norm(dir));
	}
	friend point<Float> reflection(const line &a, const point<Float> &b){
		auto prj = projection(a, b);
		return prj + prj - b;
	}
    friend line<Float> perpendicular_bisector(const point<Float> &p1, const point<Float> &p2){
        auto ctr = (p1 + p2) / Float{2};
        auto dir = rot90(p2 - p1);
        return line<Float>(ctr, ctr + dir);
    }
};

template<FloatingPoint Float>
line<Float> perpendicular_bisector(const point<Float> &p1, const point<Float> &p2){
    auto ctr = (p1 + p2) / Float{2};
    auto dir = rot90(p2 - p1);
    return line<Float>(ctr, ctr + dir);
}


template<FloatingPoint Float>
struct segment {
	point<Float> end0, end1;
    segment () {}
	segment (const point<Float> &_end0, const point<Float> &_end1) : end0(_end0), end1(_end1) {
		assert(end0 != end1);
	}
	// auto operator<=>(const segment &) const = default;
	point<Float> direction() const {
		return end1 - end0;
	}
    friend bool is_parallel(const segment &a, const segment &b){
        return sign_cross(a.direction(), b.direction()) == 0;
    }
    friend bool is_orthgonal(const segment &a, const segment &b){
        return sign_dot(a.direction(), b.direction()) == 0;
    }
	friend bool has_common_point(const segment &a, const segment &b){
		return ccw(a.end0, a.end1, b.end0) * ccw(a.end0, a.end1, b.end1) <= 0
			&& ccw(b.end0, b.end1, a.end0) * ccw(b.end0, b.end1, a.end1) <= 0;
	}
	friend bool has_common_point(const segment &a, const point<Float> &b){
		return ccw(a.end0, a.end1, b) == 0;
	}
	friend point<Float> common_point(const segment &a, const segment &b){
		// assert(has_common_point(a, b));
		if (is_parallel(a, b)){
			if (has_common_point(a, b.end0)){
				return b.end0;
			}
			if (has_common_point(a, b.end1)){
				return b.end1;
			}
			return a.end0;
		}
		return a.end0 + a.direction() * cross(b.end0 - a.end0, b.end1 - b.end0) / cross(a.direction(), b.direction());
	}
	line<Float> as_line() const {
		return line<Float>(end0, end1);
	}
};

template<FloatingPoint Float>
struct circle {
    point<Float> center;
    Float radius;
    circle () {}
    circle (const point<Float> &_center, const Float &_radius) : center(_center), radius(_radius) {
        assert(sign(radius) >= 0);
        if (radius < Float{}){
            radius = Float{};
        }
    }
    friend int num_of_common_tangents(const circle &a, const circle &b){
        if (has_common_point(a.center, b.center) && sign(a.radius - b.radius) == 0){
            return 5; // infty
        }
        Float d = abs(a.center - b.center);
        Float r1 = a.radius, r2 = b.radius;
        int sumsign = sign(d - (r1 + r2));
        if (sumsign >= 0) return sumsign + 3;
        using std::abs;
        int diffsign = sign(d - abs(r1 - r2));
        return diffsign + 1;
    }
    friend bool has_common_point(const circle &a, const circle &b){
        int cnt = num_of_common_tangents(a, b);
        return cnt == 1 || cnt == 2 || cnt == 3 || cnt == 5;
    }
    friend bool has_common_point(const circle &c, const line<Float> &l){
        point<Float> p = reflection(l, c.center);
        Float d = abs(c.center, p);
        return sign(c.radius - d) >= 0;
    }
    friend bool has_common_point(const circle &c, const segment<Float> &s){
        return std::ranges::any_of(common_points(c, s.as_line()), [&](auto p){
            return has_common_point(s, p);
        });
    }
    friend bool has_common_point(const circle &c, const point<Float> &p){
        return sign(abs(p - c.center) - c.radius) == 0;
    }
    friend std::vector<point<Float>> common_points(const circle &a, const circle &b){
        int cnt = num_of_common_tangents(a, b);
        assert(cnt != 5); // cannot be same circle
        if (cnt == 0 || cnt == 4){
            return {};
        }
        Float d = abs(a.center - b.center);
        Float x = (d*d + a.radius*a.radius - b.radius*b.radius) / (d + d);
        point<Float> p = a.center + (b.center - a.center) * x / d;
        if (cnt == 1 || cnt == 3 || sign(a.radius - x) <= 0){
            return {p};
        }
        point<Float> v = rot90(b.center - a.center);
        using std::sqrt;
        v *= sqrt(a.radius*a.radius - x*x) / abs(v);
        return {p + v, p - v};
    }
    friend std::vector<point<Float>> common_points(const circle &c, const line<Float> &l) {
        point<Float> p = projection(l, c.center);
        Float d = abs(c.center - p);
        int cnt = sign(c.radius - d) + 1;
        if (cnt == 0){
            return {};
        }
        if (cnt == 1){
            return {p};
        }
        point<Float> v = l.direction();
        using std::sqrt;
        v *= sqrt(c.radius*c.radius - d*d) / abs(v);
        return {p + v, p - v};
    }
};

using vec = point<ld>;

ld dist_p_s(vec a, segment<ld> b){
    ld ret = 1e18;
    chmin(ret,abs(a-b.end0));
    chmin(ret,abs(a-b.end1));
    vec p = projection(b.as_line(),a);
    if (has_common_point(b,p)){
        chmin(ret,abs(a-p));
    }
    return ret;
}

ld dist_s_s(segment<ld> a, segment<ld> b){
    if (has_common_point(a,b)) return 0;
    ld ret = 1e18;
    rep(tt,2){
        swap(a,b);
        rep(ttt,2){
            swap(a.end0,a.end1);
            chmin(ret,dist_p_s(a.end0,b));
        }
    }
    return ret;
}

void solve(int n){
    vector<vector<vec>> a(n+2);
    vector<ld> h(n);
    rep(i,n){
        int k; in(k);
        a[i].resize(k);
        in(h[i]);
        in(a[i]);
    }
    ld the, phi; in(the,phi);
    the *= numbers::pi_v<ld> / 180;
    phi *= numbers::pi_v<ld> / 180;
    ld tanphi = tanl(phi);
    vec from, to; in(from,to);
    rep(i,n){
        int sz = a[i].size();
        ld w = h[i] / tanphi;
        vec dir = rot(vec(w,0),the + numbers::pi_v<ld>);
        rep(j,sz){
            a[i].emplace_back(a[i][j]+dir);
        }
        auto ids = convex_hull_index(a[i]);
        vector<vec> b;
        for (int j : ids){
            b.emplace_back(a[i][j]);
        }
        a[i] = b;
    }
    vector<vector<ld>> g(n+2,vector<ld>(n+2,1e18));
    rep(i,n) rep(j,n){
        if (i == j) continue;
        int si = a[i].size();
        int sj = a[j].size();
        rep(p,si) rep(q,sj){
            int np = (p + 1) % si;
            int nq = (q + 1) % sj;
            chmin(g[i][j],dist_s_s({a[i][p],a[i][np]},{a[j][q],a[j][nq]}));
        }
    }
    repp(i,n,n+2){
        vec f = (i == n ? from : to);
        rep(j,n){
            bool inside = true;
            int sj = a[j].size();
            rep(q,sj){
                int nq = (q + 1) % sj;
                chmin(g[i][j],dist_p_s(f,{a[j][q],a[j][nq]}));
                if (ccw(a[j][q],a[j][nq],f) != 1){
                    inside = false;
                }
            }
            if (inside){
                g[i][j] = 0;
            }
            g[j][i] = g[i][j];
        }
    }
    g[n][n+1] = g[n+1][n] = abs(from-to);
    // out(g);
    priority_queue<pair<ld,int>,vector<pair<ld,int>>,greater<pair<ld,int>>> pque;
    vector<ld> dist(n+2,1e18);
    dist[n] = 0;
    pque.emplace(dist[n],n);
    while (!pque.empty()){
        auto [d, v] = pque.top(); pque.pop();
        if (dist[v] < d) continue;
        rep(u,n+2){
            if (chmin(dist[u],d+g[v][u])){
                pque.emplace(dist[u],u);
            }
        }
    }
    out(dist[n+1]);
}

int main(){
    while (true){
        int n; in(n);
        if (n == 0) break;
        solve(n);
    }
}
accuracy: 1
time_ms: 2850
memory_kb: 4428
final_rank: 17

code_snippet: #include <algorithm>
#include <array>
#include <atomic>
#include <bitset>
#include <cassert>
#include <cctype>
#include <cerrno>
#include <cfenv>
#include <cfloat>
#include <chrono>
#include <climits>
#include <clocale>
#include <cmath>
#include <complex>
#include <condition_variable>
#include <csetjmp>
#include <csignal>
#include <cstdarg>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <exception>
#include <filesystem>
#include <forward_list>
#include <fstream>
#include <functional>
#include <future>
#include <initializer_list>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <iterator>
#include <limits>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <mutex>
#include <new>
#include <numeric>
#include <optional>
#include <ostream>
#include <queue>
#include <random>
#include <ratio>
#include <regex>
#include <scoped_allocator>
#include <set>
#include <shared_mutex>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <string_view>
#include <system_error>
#include <thread>
#include <tuple>
#include <type_traits>
#include <typeindex>
#include <typeinfo>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <valarray>
#include <variant>
#include <vector>
using namespace std;


#define rep2(i, m, n) for (int i = (m); i < (n); ++i)
#define rep(i, n) rep2(i, 0, n)
#define drep2(i, m, n) for (int i = (m)-1; i >= (n); --i)
#define drep(i, n) drep2(i, n, 0)
#define all(...) std::begin(__VA_ARGS__), std::end(__VA_ARGS__)
#define INF (long long)1001001001001001001
#define inf 1001001000
#define fi first
#define se second
#define PB push_back

using ll = long long;
using vi = vector<int>;
using vl = vector<long long>;
using pii = pair<int, int>;
using pll = pair<long long, long long>;
using LL = __int128_t;

ll gcd(ll x, ll y) {if (x == 0) return y;	return gcd(y%x, x);} 
ll lcm(ll x, ll y) { __int128_t xx,yy; xx=x; yy=y; __int128_t ans=xx * yy / gcd(x, y); ll ans2=ans; return ans2; }
template<typename T>
T POW(T x, ll n){T ret=1;	while(n>0){		if(n&1) ret=ret*x;		x=x*x;		n>>=1;	}	return ret;}
template<typename T>
T modpow(T a, ll n, T p) {	if(n==0) return (T)1;  if (n == 1) return a % p;  if (n % 2 == 1) return (a * modpow(a, n - 1, p)) % p;  T t = modpow(a, n / 2, p);  return (t * t) % p;}
template<typename T>
T modinv(T a, T m) {	if(m==0)return (T)1;	T b = m, u = 1, v = 0;	while (b) {		T t = a / b;		a -= t * b; swap(a, b);		u -= t * v; swap(u, v);	}	u %= m;	if (u < 0) u += m;	return u;}
ll REM(ll a, ll b){ return (a % b + b) % b;}
ll QUO(ll a, ll b){ return (a - REM(a, b)) / b;}
/*
random_device rd;
mt19937 gen(rd());
uniform_int_distribution<> dist1(1, 100); [1,100]
int random_value = dist1(gen);
*/
/*
auto start = chrono::steady_clock::now(); //時間計測の開始
auto now = std::chrono::steady_clock::now(); //現在時刻と開始時刻の差を測定
double elapsed = std::chrono::duration<double>(now - start).count(); //時間をdouble型で取得
*/
/*
const int MAXCOMB=510000;
std::vector<mint> FAC(MAXCOMB), FINV(MAXCOMB), INV(MAXCOMB);
void COMinit() {FAC[0] = FAC[1] = 1;FINV[0] = FINV[1] = 1;INV[1] = 1;for (int i = 2; i < MAXCOMB; i++) {FAC[i] = FAC[i - 1] * i;INV[i] = mint(0) - INV[mint::mod() % i] * (mint::mod() / i);FINV[i] = FINV[i - 1] * INV[i];}}
mint COM(int n, int k) {if (n < k) return 0;if (n < 0 || k < 0) return 0;return FAC[n] * FINV[k] * FINV[n - k];}
*/

template <typename T> inline bool chmax(T &a, T b) { return ((a < b) ? (a = b, true) : (false));}
template <typename T> inline bool chmin(T &a, T b) { return ((a > b) ? (a = b, true) : (false));}

/////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////////


template<typename T>
struct Edge{
    int from, to, index;
    T cost;
    Edge() : from(-1), to(-1), index(-1), cost(0) {}
    Edge(int _to) : from(-1), to(_to), index(-1), cost(0) {}
    Edge(int _to, T _cost) : from(-1), to(_to), index(-1), cost(_cost) {}
    Edge(int _from, int _to, int _index) : from(_from), to(_to), index(_index), cost(0) {}
    Edge(int _from, int _to, int _index, T _cost) 
        : from(_from), to(_to), index(_index), cost(_cost) {}
    bool operator<(const Edge<T>& other) const {
        return cost < other.cost; 
    }
    Edge &operator=(const int &x) {
        to = x;
        return *this;
    }
    operator int() const { return to; }
};
using Graph = vector<vector<int>>; //重みなしグラフ(行き先のみを情報にもつ)
template <typename T>
using WGraph = vector<vector<Edge<T>>>; //重みを持つグラフ

/*
  <幾何ライブラリ>
    - 

    [実装/関数]
        - Point = complex<long double> で点を扱う (x,y軸は .real, .imagで取得できる)
        - long double型の値の一致判定は equal(long double a, long double b) で行う
        - Line(Point a, Point b) : 二点a,bを結ぶ線分(直線)
            - Line(ld a, ld b, ld c) : ax+by=cを表す直線(端点はルールに従って決める)
        - Circle(Point o, ld r) : 中心が点oで半径rの円
        - 

        - Point unitVector(Point a) : aの単位ベクトルを返す
        - Point normalVector(Point a) : aを左に90度回転した点を返す
        - ld dot(Point a, Point b) : aとbの内積を返す
        - ld cross(Point a, Point b) : aとbの外積を返す
        - Point rotate(Point a, ld theta) : aをthetaだけ左回転した点を返す
        - ld radianToDegree(ld a) : ラジアンaを度数に変換
        - ld degreeToRadian(ld a) : 度数aをラジアンに変換
        - Point projection(Line l, Point p) : 点pを直線lに射影した点を求める
        - Point reflection(Line l, Point p) : 線分lに対してpと対称な点を求める
        - int ccw(Point a, Point b, Point c) 
            - aからみてb,cがこの順で反時計回りなら1
            - aからみてb,cがこの順で時計回りなら-1
            - c,a,bがこの順で同一直線上にあるなら2
            - a,b,cがこの順で同一直線上にあるなら-2
            - cが線分a,b上にあるなら0
        - bool isOrthogonal(Line l1, Line l2)
        - bool isParallel(Line l1, Line l2)
        - Point crossPoint(Line l1, line l2) : 直線としての交点
        - ld distanceBetweenSegmentAndPoint(Line l1, Point p) : 
            点pから「線分」l1への距離
        - ld distanceBetweenLineAndPoint(Line l1, Point p) : 
            点pから「直線」l1への距離
        - ld distanceBetweenSegments(Line l1, Line l2) : 
            「線分」l1と「線分」l2の距離
        - ld PolygonArea(vector<Point> p) : 
            vectorで表される多角形が
        - bool isConvex(vector<Point> p) : 
            vectorで表される多角形
        - bool isContained (vector<Point> p, Point g) : 
            多角形pに点gが含まれているなら2
            辺上にあるなら1 (格子点だと判定が不正確なはずなので整数用を使う方が良さそう)
            外側にあるなら0
        - vector<Point> ConvexHull(vector<Point>) : 
            凸包に含まれる点集合を返す (O(nlogn))
        - int isIntersect(Circle c1, Circle c2) : 
            円c1と円c2の共通接線の個数を返す
        - Circle inCircle(Point a, Point b, Point c) : 
            3点a,b,cがなす三角形の内接円を返す
        - vector<Point> crossPoint(Circle c1, Line l) : 
            円c1と直線lの交点を列挙
        - vector<Point> crossPoint(Circle c1, Circle c2) : 
            円c1と円c2の交点を列挙
        - vector<Line> tangent(Circle c1, Circle c2) : 
            円c1,円c2の共通接線を列挙
    
    [計算時間]
        - 
    
    [備考]
        - 
    
    [参照]
        - https://bakamono1357.hatenablog.com/entry/2020/04/29/025320

    [verified at]
        - https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/all
    
    [使用例]
        
        
*/

using Point = complex<long double>;
typedef Point pt;
const long double EPS = 1e-7;
const long double PI =3.1415926535;

inline bool equal(const long double &a, const long double &b) {
    return fabs(a - b) < EPS;
}

// 各種関数
// Point a=Point(1,2);
// a.real() real(a) //実部
// a.imag() imag(a) //虚部
// abs(a) //絶対値
// arg(a) //偏角
// norm(a) //複素数体のノルム
// conj(a) //共役複素数
// proj(a) //リーマン球面への射影を得る
// polar(a) //複素数を極形式で指定して作る

// 単位ベクトル(unit vector)を求める
Point unitVector(const Point &a) { return a / abs(a); }

// 法線ベクトル(normal vector)を求める
// 90度回転した単位ベクトルをかける
// -90度がよければPoint(0, -1)をかける
Point normalVector(const Point &a) { return a * Point(0, 1); }

// 内積(dot product) : a・b = |a||b|cosΘ
long double dot(const Point &a, const Point &b) {
    return (a.real() * b.real() + a.imag() * b.imag());
}

// 外積(cross product) : a×b = |a||b|sinΘ
long double cross(const Point &a, const Point &b) {
    return (a.real() * b.imag() - a.imag() * b.real());
}

// 点pを反時計回りにtheta度回転
Point rotate(const Point &p, const long double &theta) {
    return Point(cos(theta) * p.real() - sin(theta) * p.imag(),
                 sin(theta) * p.real() + cos(theta) * p.imag());
}    

// ラジアン->度
long double radianToDegree(const long double &radian) { return radian * 180.0 / PI; }

// 度->ラジアン
long double degreeToRadian(const long double &degree) { return degree * PI / 180.0; }

// Line : 直線を表す構造体
// b - a で直線・線分を表せる
struct Line {
    Point a, b;
    Line() = default;
    Line(Point a, Point b) : a(a), b(b) {}
    // Ax+By=C
    Line(long double A, long double B, long double C) {
        if(equal(A, 0)) {
            a = Point(0, C / B), b = Point(1, C / B);
        } else if(equal(B, 0)) {
            b = Point(C / A, 0), b = Point(C / A, 1);
        } else {
            a = Point(0, C / B), b = Point(C / A, 0);
        }
    }
};

// Circle : 円を表す構造体
// pが中心の位置ベクトル、rは半径
struct Circle {
    Point p;
    long double r;

    Circle() = default;

    Circle(Point p, long double r) : p(p), r(r) {}
};

// 射影(projection)
// 直線(線分)lに点pから引いた垂線の足を求める
Point projection(const Line &l, const Point &p) {
    long double t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
    return l.a + (l.a - l.b) * t;
}

// 反射(reflection)
// 直線lを対称軸として点pと線対称の位置にある点を求める
Point reflection(const Line &l, const Point &p) {
    return p + (projection(l, p) - p) * (long double)2.0;
}

// 点の回転方向
// 点a, b, cの位置関係について(aが基準点)
int ccw(const Point &a, Point b, Point c) {
    b -= a, c -= a;
    // 点a, b, c が
    // 反時計回りの時、
    if(cross(b, c) > EPS) {
        return 1;
    }
    // 時計回りの時、
    if(cross(b, c) < -EPS) {
        return -1;
    }
    // c, a, bがこの順番で同一直線上にある時、
    if(dot(b, c) < 0) {
        return 2;
    }
    // a, b, cがこの順番で同一直線上にある場合、
    if(norm(b) < norm(c)) {
        return -2;
    }
    // cが線分ab上にある場合、
    return 0;
}

// 2直線の直交判定 : a⊥b <=> dot(a, b) = 0
bool isOrthogonal(const Line &a, const Line &b) {
    return equal(dot(a.b - a.a, b.b - b.a), 0);
}
// 2直線の平行判定 : a//b <=> cross(a, b) = 0
bool isParallel(const Line &a, const Line &b) {
    return equal(cross(a.b - a.a, b.b - b.a), 0);
}

// 線分sと線分tが交差しているかどうか
bool isIntersect(const Line &s, const Line &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;
}

// 直線s, tの交点の計算
Point crossPoint(const Line &s, const Line &t) {
    long double d1 = cross(s.b - s.a, t.b - t.a);
    long double d2 = cross(s.b - s.a, s.b - t.a);
    if(equal(abs(d1), 0) && equal(abs(d2), 0)) {
        return t.a;
    }
    return t.a + (t.b - t.a) * (d2 / d1);
}


// 線分lと点pの距離を求める
// 定義：点pから「線分lのどこか」への最短距離
long double distanceBetweenSegmentAndPoint(const Line &l, const Point &p) {
    if(dot(l.b - l.a, p - l.a) < EPS) {
        return abs(p - l.a);
    }
    if(dot(l.a - l.b, p - l.b) < EPS) {
        return abs(p - l.b);
    }
    return abs(cross(l.b - l.a, p - l.a)) / abs(l.b - l.a);
}

long double distanceBetweenLineAndPoint(const Line &l, const Point &p) {
    return abs(cross(l.b - l.a, p - l.a)) / abs(l.b - l.a);
}


// 線分sとtの距離
long double distanceBetweenSegments(const Line &s, const Line &t) {
    if(isIntersect(s, t)) {
        return (long double)(0);
    }
    long double ans = distanceBetweenSegmentAndPoint(s, t.a);
    chmin(ans, distanceBetweenSegmentAndPoint(s, t.b));
    chmin(ans, distanceBetweenSegmentAndPoint(t, s.a));
    chmin(ans, distanceBetweenSegmentAndPoint(t, s.b));
    return ans;
}

// 多角形の面積を求める
long double PolygonArea(const vector<Point> &p) {
    long double res = 0;
    int n = p.size();
    for(int i = 0; i < n - 1; i++) {
        res += cross(p[i], p[i + 1]);
    }
    res += cross(p[n - 1], p[0]);
    return res * 0.5;
}

// 凸多角形かどうか
bool isConvex(const vector<Point> &p) {
    int n = p.size();
    int now, pre, nxt;
    for(int i = 0; i < n; i++) {
        pre = (i - 1 + n) % n;
        nxt = (i + 1) % n;
        now = i;
        if(ccw(p[pre], p[now], p[nxt]) == -1) {
            return false;
        }
    }
    return true;
}

// 多角形gに点pが含まれているか?
// 含まれる:2, 辺上にある:1, 含まれない:0
int isContained(const vector<Point> &g, const Point &p) {
    bool in = false;
    int n = (int)g.size();
    for(int i = 0; i < n; i++) {
        Point a = g[i] - p, b = g[(i + 1) % n] - p;
        if(imag(a) > imag(b)) {
            swap(a, b);
        }
        if(imag(a) <= EPS && EPS < imag(b) && cross(a, b) < -EPS) {
            in = !in;
        }
        if(cross(a, b) == 0 && dot(a, b) <= 0) {
            return 1;
        }
    }
    return (in ? 2 : 0);
}

// 凸包 O(NlogN)
vector<Point> ConvexHull(vector<Point> &p) {
    int n = (int)p.size(), k = 0;
    sort(all(p), [](const Point &a, const Point &b) {
        return (real(a) != real(b) ? real(a) < real(b) : imag(a) < imag(b));
    });
    vector<Point> ch(2 * n);
    // 一直線上の3点を含める -> (< -EPS)
    // 含めない -> (< EPS)
    for(int i = 0; i < n; ch[k++] = p[i++]) { // lower
        while(k >= 2 && cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) < EPS)
            --k;
    }
    for(int i = n - 2, t = k + 1; i >= 0; ch[k++] = p[i--]) { // upper
        while(k >= t && cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) < EPS)
            --k;
    }
    ch.resize(k - 1);
    return ch;
}

// 2つの円の交差判定
// 返り値は共通接線の数
int isIntersect(const Circle &c1, const Circle &c2) {
    long double d = abs(c1.p - c2.p);
    // 2つの円が離れている場合
    if(d > c1.r + c2.r + EPS) {
        return 4;
    }
    // 外接している場合
    if(equal(d, c1.r + c2.r)) {
        return 3;
    }
    // 内接している場合
    if(equal(d, abs(c1.r - c2.r))) {
        return 1;
    }
    // 内包している場合
    if(d < abs(c1.r - c2.r) - EPS) {
        return 0;
    }
    return 2;
}
 
// 三角形の内接円
Circle inCircle(const Point &a, const Point &b, const Point &c) {
     long double A = abs(b - c), B = abs(a - c), C = abs(a - b);
     Point p(A * real(a) + B * real(b) + C * real(c),
                 A * imag(a) + B * imag(b) + C * imag(c));
     p /= (A + B + C);
     long double r = distanceBetweenLineAndPoint(Line(a, b), p);
     return Circle(p, r);
}

// 円cと直線lの交点
vector<Point> crossPoint(const Circle &c, const Line &l) {
    vector<Point> res;
    long double d = distanceBetweenLineAndPoint(l, c.p);
    // 交点を持たない
    if(d > c.r + EPS) {
        return res;
    }
    // 接する
    Point h = projection(l, c.p);
    if(equal(d, c.r)) {
        res.emplace_back(h);
        return res;
    }
    Point e = unitVector(l.b - l.a);
    long double ph = sqrt(c.r * c.r - d * d);
    res.emplace_back(h - e * ph);
    res.emplace_back(h + e * ph);
    return res;
}

// 2つの円の交点
vector<Point> crossPoint(const Circle &c1, const Circle &c2) {
    vector<Point> res;
    int mode = isIntersect(c1, c2);
    // 2つの中心の距離
    long double d = abs(c1.p - c2.p);
    // 2円が離れている場合
    if(mode == 4) {
        return res;
    }
    // 1つの円がもう1つの円に内包されている場合
    if(mode == 0) {
        return res;
    }
    // 2円が外接する場合
    if(mode == 3) {
        long double t = c1.r / (c1.r + c2.r);
        res.emplace_back(c1.p + (c2.p - c1.p) * t);
        return res;
    }
    // 内接している場合
    if(mode == 1) {
        if(c2.r < c1.r - EPS) {
            res.emplace_back(c1.p + (c2.p - c1.p) * (c1.r / d));
        } else {
            res.emplace_back(c2.p + (c1.p - c2.p) * (c2.r / d));
        }
        return res;
    }
    // 2円が重なる場合
    long double rc1 = (c1.r * c1.r + d * d - c2.r * c2.r) / (2 * d);
    long double rs1 = sqrt(c1.r * c1.r - rc1 * rc1);
    if(c1.r - abs(rc1) < EPS) {
        rs1 = 0;
    }
    Point e12 = (c2.p - c1.p) / abs(c2.p - c1.p);
    res.emplace_back(c1.p + rc1 * e12 + rs1 * e12 * Point(0, 1));
    res.emplace_back(c1.p + rc1 * e12 + rs1 * e12 * Point(0, -1));
    return res;
}

// 点pを通る円cの接線
// 2本あるので、接点のみを返す
vector<Point> tangentToCircle(const Point &p, const Circle &c) {
    return crossPoint(c, Circle(p, sqrt(norm(c.p - p) - c.r * c.r)));
}

vector<Line> tangent(const Circle &a, const Circle &b) {
    vector<Line> ret;
    // 2円の中心間の距離
    long double g = abs(a.p - b.p);
    // 円が内包されている場合
    if(equal(g, 0)) {
        return ret;
    }
    Point u = unitVector(b.p - a.p);
    Point v = rotate(u, PI / 2);
    for(int s : {-1, 1}) {
        long double h = (a.r + b.r * s) / g;
        if(equal(h * h, 1)) {
            ret.emplace_back(a.p + (h > 0 ? u : -u) * a.r,
                             a.p + (h > 0 ? u : -u) * a.r + v);

        } else if(1 - h * h > 0) {
            Point U = u * h, V = v * sqrt(1 - h * h);
            ret.emplace_back(a.p + (U + V) * a.r, b.p - (U + V) * (b.r * s));
            ret.emplace_back(a.p + (U - V) * a.r, b.p - (U - V) * (b.r * s));
        }
    }
    return ret;
}

using Polygonal = vector<vector<Point>>;

//Polygonal同士の距離
long double polyDist(vector<Point> a, vector<Point> b){
    long double ans = inf;
    for (int i = 0; i < a.size();i++){
            for (int k = 0; k < b.size();k++){
                Line l1(a[i], a[(i + 1) % a.size()]);
                Line l2(b[k], b[(k + 1) % b.size()]);
                chmin(ans, distanceBetweenSegments(l1, l2));
                }
            }
            return ans;
}

//Polygonalと点
long double polyPointDist(vector<Point> a, Point p){
    long double ans = inf;
    for (int i = 0; i < a.size();i++){
            Line l1(a[i], a[(i + 1) % a.size()]);
            chmin(ans, distanceBetweenSegmentAndPoint(l1, p));
        }
    return ans;
}

// Polygonalに点が含まれるか
bool polyContained(vector<Point> a, Point p){
    return isContained(a, p);
}

int SOLVEFIN = 0;

template <typename T>
vector<T> dijkstra(vector<vector<Edge<T>>> &g, int start = 0){
    using P = pair<T, int>;
    int N = (int)g.size();
    vector<T> d(N, T(-1));
    priority_queue<P, vector<P>, greater<P>> pq;
    d[start] = 0;
    pq.emplace(0, start);
    while(!pq.empty()){
        P p = pq.top();
        pq.pop();
        int cur = p.se;
        if(d[cur] < p.fi) continue;
        for(auto dst : g[cur]){
            if(d[dst.to] == T(-1) || d[cur] + dst.cost < d[dst.to]){
                d[dst.to] = d[cur] + dst.cost;
                pq.emplace(d[dst.to],dst.to);
            }
        }
    }
    return d;
}

void solve(){
    int n;
    cin >> n;
    if(n==0){
        SOLVEFIN = 1;
        return;
    }
    vector<vector<Point>> area(n);
    vector<double> height(n);
    rep(i, n)
    {
        int m;
        cin >> m;
        double h;
        cin >> h;
        height[i] = h;
        rep(j,m){
            int x, y;
            cin >> x >> y;
            Point a(x, y);
            area[i].PB(a);
        }
    }
    double ang1, ang2;
    cin >> ang1 >> ang2;
    double r1, r2, r3, r4;
    cin >> r1 >> r2 >> r3 >> r4;
    Point start(r1, r2);
    Point goal(r3, r4);
    // ang1-180
    double dirx = cos(ang1*2*PI/360 - PI);
    double diry = sin(ang1*2*PI/360 - PI);
    
    rep(i,n){
        double len = height[i] / tan(ang2 * 2 * PI / 360);
        double dx = dirx * len;
        double dy = diry * len;
        int sz = area[i].size();
        rep(j, sz)
        {
            Point p1 = area[i][j];
            Point p4(p1.real() + dx, p1.imag() + dy);
            area[i].PB(p4);
        }
        area[i]= ConvexHull(area[i]);
        // rep(t,area[i].size()){
        //     cout << area[i][t].real() << " " << area[i][t].imag() << endl;
        // }
    }

    WGraph<double> G(n+2);

    rep(i, n + 2)
    {
        rep(j, n + 2)
        {
            if (i == j)
                continue;
            if (i > j)
                continue;
            double dist;
            if (i == n)
            {
                G[n].PB({n + 1, (double)sqrt((start.real() - goal.real()) * (start.real() - goal.real()) + (start.imag() - goal.imag()) * (start.imag() - goal.imag()))});
            }
            else if (j == n)
            {
                if (polyContained(area[i], start))
                {
                    G[i].PB({n, 0});
                    G[n].PB({i, 0});
                }
                else
                {
                    dist = polyPointDist(area[i], start);
                    G[i].PB({n, dist});
                    G[n].PB({i, dist});
                }
            }
            else if(j==n+1){
                //cout << (polyContained(poly[i], goal) ? "A" : "B") << endl;
                if (polyContained(area[i], goal))
                {
                    G[i].PB({n+1, 0});
                    G[n+1].PB({i, 0});
                }
                 else{dist = polyPointDist(area[i], goal);
                G[i].PB({n+1, dist});
                G[n+1].PB({i, dist});}
            }
            else{
                 dist = polyDist(area[i], area[j]);
                G[i].PB({j, dist});
                G[j].PB({i, dist});
            }
            //cout << i << " " << j << " "<< dist << endl;
            //cout << poly[i].size() << " " << poly[j].size() << endl;
        }
    }
    cout << dijkstra<double>(G, n)[n + 1] << endl;

    vector<Point> v3 = {Point(10, 19), Point(11, 24), Point(19.977, 16.696), Point(20.977, 21.696)};
    Point p3(15, 22);
}

signed main(){
	cin.tie(0);
	ios::sync_with_stdio(0);
	cout<<fixed<<setprecision(20);
	int TT; TT = 1; //cin >> TT;
	while(SOLVEFIN==0) solve();
}
accuracy: 1
time_ms: 1160
memory_kb: 4388
final_rank: 14

code_snippet: #include<bits/stdc++.h>
using namespace std;

using ll =long long;

#define rep(i,n) for(ll i=0;i<n;i++)
#define all(A) A.begin(),A.end()
/*
- `Point p(x, y)` で座標/ベクトルを宣言できる
- `cout << fixed << setprecision(10);` で小数出力の桁数を指定できる
*/
using Real = double;
using Point = complex<Real>;
using Poly = vector<Point>;
Real eps = 1e-6;

int Round(Real x) { return static_cast<int>(round(x)); }

// 同じか判定
bool equal(Point a, Point b) { return abs(a - b) < eps; }

struct Line {
  // 点 a, b を通る直線
  Point a, b;
  Line() = default;
  Line(Point a, Point b) : a(a), b(b) {}
  Line(Real A, Real B, Real C) {
    // 直線の方程式 Ax + By = C;
    if(equal(A, 0)) a = Point(0, C / B), b = Point(1, C / B);
    else if(equal(B, 0)) a = Point(C / A, 0), b = Point(C / A, 1);
    else a = Point(0, C / B), b = Point(C / A, 0);
  }
};

using Segment = Line;


Real norm(Point p) { return pow(p.real(), 2) + pow(p.imag(), 2); }
Real dot(Point a, Point b) { return a.real() * b.real() + a.imag() * b.imag(); }
Real cross(Point a, Point b) {
  return a.real() * b.imag() - a.imag() * b.real();
}
int ccw(Point a, Point b, Point c) {
  Point p = b - a;
  Point q = c - a;

  if(cross(p, q) > eps) return 1;
  if(cross(p, q) < -eps) return -1;
  if(dot(p, q) < 0) return 2;
  if(norm(p) < norm(q)) return -2;
  return 0;
}

bool isIntersect(Segment s, Segment t) {
  rep(i, 2) {
    if(ccw(s.a, s.b, t.a) * ccw(s.a, s.b, t.b) > 0) return false;
    swap(s, t);
  }
  return true;
}


Real distSegPoint(Segment L, Point p) {
  if(dot(L.b - L.a, p - L.a) < eps) return abs(p - L.a);
  if(dot(L.a - L.b, p - L.b) < eps) return abs(p - L.b);
  return abs(cross(L.b - L.a, p - L.a)) / abs(L.b - L.a);
}
Real distSegSeg(Segment s, Segment t) {
  if(isIntersect(s, t)) return 0;
  Real d = distSegPoint(s, t.a);
  d = min(d, distSegPoint(s, t.b));
  d = min(d, distSegPoint(t, s.a));
  d = min(d, distSegPoint(t, s.b));
  return d;
}



Poly ConvexHull(Poly p) {
  int n = p.size();
  if(n <= 2) return p;
  sort(all(p), [&](Point a, Point b) {
    return !equal(real(a), real(b)) ? real(a) < real(b) : imag(a) < imag(b);
  });
  int sz = 0;
  Poly CH(2 * n);
  rep(i, n) {
    while(sz > 1 && cross(CH[sz - 1] - CH[sz - 2], p[i] - CH[sz - 1]) < -eps)
      sz--;
    CH[sz] = p[i];
    sz++;
  }
  int t = sz;
  for(int i = n - 2; i >= 0; i--) {
    while(sz > t && cross(CH[sz - 1] - CH[sz - 2], p[i] - CH[sz - 1]) < -eps)
      sz--;
    CH[sz] = p[i];
    sz++;
  }
  CH.resize(sz - 1);
  return CH;
}

int isContain(Poly g, Point p) {
  int IN = 0;
  int n = g.size();
  rep(i, n) {
    Point a = g[i] - p;
    Point b = g[(i + 1) % n] - p;
    if(a.imag() > b.imag()) swap(a, b);
    if(imag(a) < eps && eps < imag(b) && cross(a, b) < -eps) IN ^= 1;
    if(equal(cross(a, b), 0) && dot(a, b) < eps) return 1;
  }
  return 2 * IN;
}

Real distPoly(Poly A,Poly B){
	int AN=A.size(),BN=B.size();
	for(int i=0;i<AN;i++){
		if(isContain(B,A[i]))return 0;
	}
	for(int i=0;i<BN;i++){
		if(isContain(A,B[i]))return 0;
	}
	Real res=1e18;
	for(int i=0;i<AN;i++){
		for(int j=0;j<BN;j++){
			res=min(res,distSegSeg(Segment(A[i],A[(i+1)%AN]),Segment(B[j],B[(j+1)%BN])));
		}
	}
	return res;
}

Real distPolyP(Poly A,Point B){
	int AN=A.size();
	if(isContain(A,B))return 0;
	Real res=1e18;
	for(int i=0;i<AN;i++){
		res=min(res,distSegPoint(Segment(A[i],A[(i+1)%AN]),B));
	}
	return res;
}

void solve(ll N){
	vector<Poly> P(N);
	vector<Real> H(N);
	for(int i=0;i<N;i++){
		ll V;
		cin>>V;
		P[i].resize(V);
		cin>>H[i];
		for(int j=0;j<V;j++){
			Real x,y;
			cin>>x>>y;
			P[i][j]=Point(x,y);
		}
	}
	Real pi=atan2(1.0,1.0)*4.0;
	Real tht,phi;
	cin>>tht>>phi;
	phi=phi/180.0*pi;
	tht=tht/180.0*pi;
	Point S,G;
	Real x,y;
	cin>>x>>y;
	S=Point(x,y);
	cin>>x>>y;
	G=Point(x,y);
	for(int i=0;i<N;i++){
		ll V=P[i].size();
		Real d=H[i]/tan(phi);

		for(int j=0;j<V;j++){
			Real x=real(P[i][j])+d*cos(tht+pi);
			Real y=imag(P[i][j])+d*sin(tht+pi);
			P[i].push_back(Point(x,y));
		}
		P[i]=ConvexHull(P[i]);
	}
	
	vector<Real> D(N);
	priority_queue<pair<Real,int>,vector<pair<Real,int>>,greater<pair<Real,int>>> Q;
	for(int i=0;i<N;i++){
		D[i]=distPolyP(P[i],S);
		Q.push({D[i],i});
	}
	vector<bool> seen(N,0);
	Real an=sqrt(norm(S-G));
	while(!Q.empty()){
		auto [d,id]=Q.top();
		Q.pop();
		if(seen[id])continue;
		seen[id]=1;
		an=min(an,d+distPolyP(P[id],G));
		for(int i=0;i<N;i++){
			if(seen[i])continue;
			Real nd=d+distPoly(P[i],P[id]);
			if(D[i]>nd){
				D[i]=nd;
				Q.push({nd,i});
			}
		}
	}
	cout<<an<<endl;

}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

	cout<<fixed<<setprecision(15);
	// cout<<tan(1)<<" "<<tan(3.14/4.0)<<endl;
	ll N;
	while(cin>>N,N!=0)solve(N);
}
accuracy: 1
time_ms: 320
memory_kb: 4352
final_rank: 9

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (int)(a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())

using uint = unsigned int;
using ll = long long int;
using ull = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
const int inf = 0x3fffffff;
const ll INF = 0x1fffffffffffffff;

template <typename T> inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T> inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T, typename U> T ceil(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U> T floor(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T> int popcnt(T x) {
    return __builtin_popcountll(x);
}
template <typename T> int topbit(T x) {
    return (x == 0 ? -1 : 63 - __builtin_clzll(x));
}
template <typename T> int lowbit(T x) {
    return (x == 0 ? -1 : __builtin_ctzll(x));
}

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << "P(" << p.first << ", " << p.second << ")";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "{";
    for (int i = 0; i < vec.size(); i++) {
        os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
    }
    os << "}";
    return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    os << "{";
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << "(" << itr->first << ", " << itr->second << ")";
        itr++;
        if (itr != map_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const set<T> &set_var) {
    os << "{";
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
#ifdef LOCAL
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define show(...) true
#endif
template <typename T> void _show(int i, T name) {
    cerr << '\n';
}
template <typename T1, typename T2, typename... T3>
void _show(int i, const T1 &a, const T2 &b, const T3 &...c) {
    for (; a[i] != ',' && a[i] != '\0'; i++)
        cerr << a[i];
    cerr << ":" << b << " ";
    _show(i + 1, a, c...);
}

#include <unistd.h>

namespace fastio {
static constexpr uint32_t SZ = 1 << 17;
char ibuf[SZ];
char obuf[SZ];
char out[100];
// pointer of ibuf, obuf


uint32_t pil = 0, pir = 0, por = 0;

struct Pre {
    char num[10000][4];
    constexpr Pre() : num() {
        for (int i = 0; i < 10000; i++) {
            int n = i;
            for (int j = 3; j >= 0; j--) {
                num[i][j] = n % 10 | '0';
                n /= 10;
            }
        }
    }
} constexpr pre;

inline void load() {
    memmove(ibuf, ibuf + pil, pir - pil);
    pir = pir - pil + fread(ibuf + pir - pil, 1, SZ - pir + pil, stdin);
    pil = 0;
    if (pir < SZ)
        ibuf[pir++] = '\n';
}

inline void flush() {
    fwrite(obuf, 1, por, stdout);
    por = 0;
}

void rd(char &c) {
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
}

void rd(string &x) {
    x.clear();
    char c;
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
    do {
        x += c;
        if (pil == pir)
            load();
        c = ibuf[pil++];
    } while (!isspace(c));
}

template <typename T> void rd_real(T &x) {
    string s;
    rd(s);
    x = stod(s);
}

template <typename T> void rd_integer(T &x) {
    if (pil + 100 > pir)
        load();
    char c;
    do
        c = ibuf[pil++];
    while (c < '-');
    bool minus = 0;
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (c == '-') {
            minus = 1, c = ibuf[pil++];
        }
    }
    x = 0;
    while ('0' <= c) {
        x = x * 10 + (c & 15), c = ibuf[pil++];
    }
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (minus)
            x = -x;
    }
}

void rd(int &x) {
    rd_integer(x);
}
void rd(ll &x) {
    rd_integer(x);
}
void rd(i128 &x) {
    rd_integer(x);
}
void rd(uint &x) {
    rd_integer(x);
}
void rd(ull &x) {
    rd_integer(x);
}
void rd(u128 &x) {
    rd_integer(x);
}
void rd(double &x) {
    rd_real(x);
}
void rd(long double &x) {
    rd_real(x);
}

template <class T, class U> void rd(pair<T, U> &p) {
    return rd(p.first), rd(p.second);
}
template <size_t N = 0, typename T> void rd_tuple(T &t) {
    if constexpr (N < std::tuple_size<T>::value) {
        auto &x = std::get<N>(t);
        rd(x);
        rd_tuple<N + 1>(t);
    }
}
template <class... T> void rd(tuple<T...> &tpl) {
    rd_tuple(tpl);
}

template <size_t N = 0, typename T> void rd(array<T, N> &x) {
    for (auto &d : x)
        rd(d);
}
template <class T> void rd(vector<T> &x) {
    for (auto &d : x)
        rd(d);
}

void read() {}
template <class H, class... T> void read(H &h, T &...t) {
    rd(h), read(t...);
}

void wt(const char c) {
    if (por == SZ)
        flush();
    obuf[por++] = c;
}
void wt(const string s) {
    for (char c : s)
        wt(c);
}
void wt(const char *s) {
    size_t len = strlen(s);
    for (size_t i = 0; i < len; i++)
        wt(s[i]);
}

template <typename T> void wt_integer(T x) {
    if (por > SZ - 100)
        flush();
    if (x < 0) {
        obuf[por++] = '-', x = -x;
    }
    int outi;
    for (outi = 96; x >= 10000; outi -= 4) {
        memcpy(out + outi, pre.num[x % 10000], 4);
        x /= 10000;
    }
    if (x >= 1000) {
        memcpy(obuf + por, pre.num[x], 4);
        por += 4;
    } else if (x >= 100) {
        memcpy(obuf + por, pre.num[x] + 1, 3);
        por += 3;
    } else if (x >= 10) {
        int q = (x * 103) >> 10;
        obuf[por] = q | '0';
        obuf[por + 1] = (x - q * 10) | '0';
        por += 2;
    } else
        obuf[por++] = x | '0';
    memcpy(obuf + por, out + outi + 4, 96 - outi);
    por += 96 - outi;
}

template <typename T> void wt_real(T x) {
    ostringstream oss;
    oss << fixed << setprecision(15) << double(x);
    string s = oss.str();
    wt(s);
}

void wt(int x) {
    wt_integer(x);
}
void wt(ll x) {
    wt_integer(x);
}
void wt(i128 x) {
    wt_integer(x);
}
void wt(uint x) {
    wt_integer(x);
}
void wt(ull x) {
    wt_integer(x);
}
void wt(u128 x) {
    wt_integer(x);
}
void wt(double x) {
    wt_real(x);
}
void wt(long double x) {
    wt_real(x);
}

template <class T, class U> void wt(const pair<T, U> val) {
    wt(val.first);
    wt(' ');
    wt(val.second);
}
template <size_t N = 0, typename T> void wt_tuple(const T t) {
    if constexpr (N < std::tuple_size<T>::value) {
        if constexpr (N > 0) {
            wt(' ');
        }
        const auto x = std::get<N>(t);
        wt(x);
        wt_tuple<N + 1>(t);
    }
}
template <class... T> void wt(tuple<T...> tpl) {
    wt_tuple(tpl);
}
template <class T, size_t S> void wt(const array<T, S> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}
template <class T> void wt(const vector<T> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}

void print() {
    wt('\n');
}
template <class Head, class... Tail> void print(Head &&head, Tail &&...tail) {
    wt(head);
    if (sizeof...(Tail))
        wt(' ');
    print(forward<Tail>(tail)...);
}
void __attribute__((destructor)) _d() {
    flush();
}
} // namespace fastio


using fastio::flush;
using fastio::print;
using fastio::read;

inline void first(bool i = true) {
    print(i ? "first" : "second");
}
inline void Alice(bool i = true) {
    print(i ? "Alice" : "Bob");
}
inline void Takahashi(bool i = true) {
    print(i ? "Takahashi" : "Aoki");
}
inline void yes(bool i = true) {
    print(i ? "yes" : "no");
}
inline void Yes(bool i = true) {
    print(i ? "Yes" : "No");
}
inline void No() {
    print("No");
}
inline void YES(bool i = true) {
    print(i ? "YES" : "NO");
}
inline void NO() {
    print("NO");
}
inline void Yay(bool i = true) {
    print(i ? "Yay!" : ":(");
}
inline void Possible(bool i = true) {
    print(i ? "Possible" : "Impossible");
}
inline void POSSIBLE(bool i = true) {
    print(i ? "POSSIBLE" : "IMPOSSIBLE");
}

/**
 * @brief Fast IO
 */

using T = double;
const T eps = 1e-12;
using Point = complex<T>;
using Poly = vector<Point>;
#define X real()
#define Y imag()
template <typename T> inline bool eq(const T &a, const T &b) {
    return fabs(a - b) < eps;
}
bool cmp(const Point &a, const Point &b) {
    auto sub = [&](Point a) {
        return (a.Y < 0 ? -1 : (a.Y == 0 && a.X >= 0 ? 0 : 1));
    };
    if (sub(a) != sub(b))
        return sub(a) < sub(b);
    return a.Y * b.X < a.X * b.Y;
}
struct Line {
    Point a, b, dir;
    Line() {}
    Line(Point _a, Point _b) : a(_a), b(_b), dir(b - a) {}
    Line(T A, T B, T C) {
        if (eq(A, .0)) {
            a = Point(0, C / B), b = Point(1 / C / B);
        } else if (eq(B, .0)) {
            a = Point(C / A, 0), b = Point(C / A, 1);
        } else {
            a = Point(0, C / B), b = Point(C / A, 0);
        }
    }
};
struct Segment : Line {
    Segment() {}
    Segment(Point _a, Point _b) : Line(_a, _b) {}
};
struct Circle {
    Point p;
    T r;
    Circle() {}
    Circle(Point _p, T _r) : p(_p), r(_r) {}
};

istream &operator>>(istream &is, Point &p) {
    T x, y;
    is >> x >> y;
    p = Point(x, y);
    return is;
}
ostream &operator<<(ostream &os, Point &p) {
    os << fixed << setprecision(12) << p.X << ' ' << p.Y;
    return os;
}
Point unit(const Point &a) {
    return a / abs(a);
}
T dot(const Point &a, const Point &b) {
    return a.X * b.X + a.Y * b.Y;
}
T cross(const Point &a, const Point &b) {
    return a.X * b.Y - a.Y * b.X;
}
Point rot(const Point &a, const T &theta) {
    return Point(cos(theta) * a.X - sin(theta) * a.Y,
                 sin(theta) * a.X + cos(theta) * a.Y);
}
Point rot90(const Point &a) {
    return Point(-a.Y, a.X);
}
T arg(const Point &a, const Point &b, const Point &c) {
    double ret = acos(dot(a - b, c - b) / abs(a - b) / abs(c - b));
    if (cross(a - b, c - b) < 0)
        ret = -ret;
    return ret;
}

Point Projection(const Line &l, const Point &p) {
    T t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
    return l.a + (l.a - l.b) * t;
}
Point Projection(const Segment &l, const Point &p) {
    T t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
    return l.a + (l.a - l.b) * t;
}
Point Reflection(const Line &l, const Point &p) {
    return p + (Projection(l, p) - p) * 2.;
}
int ccw(const Point &a, Point b, Point c) {
    b -= a;
    c -= a;
    if (cross(b, c) > eps)
        return 1; // ccw

    if (cross(b, c) < -eps)
        return -1; // cw

    if (dot(b, c) < 0)
        return 2; // c,a,b

    if (norm(b) < norm(c))
        return -2; // a,b,c

    return 0;      // a,c,b

}
bool isOrthogonal(const Line &a, const Line &b) {
    return eq(dot(a.b - a.a, b.b - b.a), .0);
}
bool isParallel(const Line &a, const Line &b) {
    return eq(cross(a.b - a.a, b.b - b.a), .0);
}
bool isIntersect(const Segment &a, const Segment &b) {
    return ccw(a.a, a.b, b.a) * ccw(a.a, a.b, b.b) <= 0 and
           ccw(b.a, b.b, a.a) * ccw(b.a, b.b, a.b) <= 0;
}
int isIntersect(const Circle &a, const Circle &b) {
    T d = abs(a.p - b.p);
    if (d > a.r + b.r + eps)
        return 4;
    if (eq(d, a.r + b.r))
        return 3;
    if (eq(d, abs(a.r - b.r)))
        return 1;
    if (d < abs(a.r - b.r) - eps)
        return 0;
    return 2;
}
T Dist(const Line &a, const Point &b) {
    Point c = Projection(a, b);
    return abs(c - b);
}
T Dist(const Segment &a, const Point &b) {
    if (dot(a.b - a.a, b - a.a) < eps)
        return abs(b - a.a);
    if (dot(a.a - a.b, b - a.b) < eps)
        return abs(b - a.b);
    return abs(cross(a.b - a.a, b - a.a)) / abs(a.b - a.a);
}
T Dist(const Segment &a, const Segment &b) {
    if (isIntersect(a, b))
        return .0;
    T res = min({Dist(a, b.a), Dist(a, b.b), Dist(b, a.a), Dist(b, a.b)});
    return res;
}
Point Intersection(const Line &a, const Line &b) {
    T d1 = cross(a.b - a.a, b.b - b.a);
    T d2 = cross(a.b - a.a, a.b - b.a);
    if (eq(d1, 0.) and eq(d2, 0.))
        return b.a;
    return b.a + (b.b - b.a) * (d2 / d1);
}
Poly Intersection(const Circle &a, const Line &b) {
    Poly res;
    T d = Dist(b, a.p);
    if (d > a.r + eps)
        return res;
    Point h = Projection(b, a.p);
    if (eq(d, a.r)) {
        res.push_back(h);
        return res;
    }
    Point e = unit(b.b - b.a);
    T ph = sqrt(a.r * a.r - d * d);
    res.push_back(h - e * ph);
    res.push_back(h + e * ph);
    return res;
}
Poly Intersection(const Circle &a, const Segment &b) {
    Line c(b.a, b.b);
    Poly sub = Intersection(a, c);
    double xmi = min(b.a.X, b.b.X), xma = max(b.a.X, b.b.X);
    double ymi = min(b.a.Y, b.b.Y), yma = max(b.a.Y, b.b.Y);
    Poly res;
    rep(i, 0, sub.size()) {
        if (xmi <= sub[i].X + eps and sub[i].X - eps <= xma and
            ymi <= sub[i].Y + eps and sub[i].Y - eps <= yma) {
            res.push_back(sub[i]);
        }
    }
    return res;
}
Poly Intersection(const Circle &a, const Circle &b) {
    Poly res;
    int mode = isIntersect(a, b);
    T d = abs(a.p - b.p);
    if (mode == 4 or mode == 0)
        return res;
    if (mode == 3) {
        T t = a.r / (a.r + b.r);
        res.push_back(a.p + (b.p - a.p) * t);
        return res;
    }
    if (mode == 1) {
        if (b.r < a.r - eps) {
            res.push_back(a.p + (b.p - a.p) * (a.r / d));
        } else {
            res.push_back(b.p + (a.p - b.p) * (b.r / d));
        }
        return res;
    }
    T rc = (a.r * a.r + d * d - b.r * b.r) / d / 2.;
    T rs = sqrt(a.r * a.r - rc * rc);
    if (a.r - abs(rc) < eps)
        rs = 0;
    Point e = unit(b.p - a.p);
    res.push_back(a.p + rc * e + rs * e * Point(0, 1));
    res.push_back(a.p + rc * e + rs * e * Point(0, -1));
    return res;
}
Poly HalfplaneIntersection(vector<Line> &H) {
    sort(ALL(H), [&](Line &l1, Line &l2) { return cmp(l1.dir, l2.dir); });
    auto outside = [&](Line &L, Point p) -> bool {
        return cross(L.dir, p - L.a) < -eps;
    };
    deque<Line> deq;
    int sz = 0;
    rep(i, 0, SZ(H)) {
        while (sz > 1 and
               outside(H[i], Intersection(deq[sz - 1], deq[sz - 2]))) {
            deq.pop_back();
            sz--;
        }
        while (sz > 1 and outside(H[i], Intersection(deq[0], deq[1]))) {
            deq.pop_front();
            sz--;
        }
        if (sz > 0 and fabs(cross(H[i].dir, deq[sz - 1].dir)) < eps) {
            if (dot(H[i].dir, deq[sz - 1].dir) < 0) {
                return {};
            }
            if (outside(H[i], deq[sz - 1].a)) {
                deq.pop_back();
                sz--;
            } else
                continue;
        }
        deq.push_back(H[i]);
        sz++;
    }

    while (sz > 2 and outside(deq[0], Intersection(deq[sz - 1], deq[sz - 2]))) {
        deq.pop_back();
        sz--;
    }
    while (sz > 2 and outside(deq[sz - 1], Intersection(deq[0], deq[1]))) {
        deq.pop_front();
        sz--;
    }
    if (sz < 3)
        return {};
    deq.push_back(deq.front());
    Poly ret;
    rep(i, 0, sz) ret.push_back(Intersection(deq[i], deq[i + 1]));
    return ret;
}

T Area(const Poly &a) {
    T res = 0;
    int n = a.size();
    rep(i, 0, n) res += cross(a[i], a[(i + 1) % n]);
    return fabs(res / 2.);
}
T Area(const Poly &a, const Circle &b) {
    int n = a.size();
    if (n < 3)
        return .0;
    auto rec = [&](auto self, const Circle &c, const Point &p1,
                   const Point &p2) {
        Point va = c.p - p1, vb = c.p - p2;
        T f = cross(va, vb), res = .0;
        if (eq(f, .0))
            return res;
        if (max(abs(va), abs(vb)) < c.r + eps)
            return f;
        if (Dist(Segment(p1, p2), c.p) > c.r - eps)
            return c.r * c.r * arg(vb * conj(va));
        auto u = Intersection(c, Segment(p1, p2));
        Poly sub;
        sub.push_back(p1);
        for (auto &x : u)
            sub.push_back(x);
        sub.push_back(p2);
        rep(i, 0, sub.size() - 1) res += self(self, c, sub[i], sub[i + 1]);
        return res;
    };
    T res = .0;
    rep(i, 0, n) res += rec(rec, b, a[i], a[(i + 1) % n]);
    return fabs(res / 2.);
}
T Area(const Circle &a, const Circle &b) {
    T d = abs(a.p - b.p);
    if (d >= a.r + b.r - eps)
        return .0;
    if (d <= abs(a.r - b.r) + eps) {
        T r = min(a.r, b.r);
        return M_PI * r * r;
    }
    T ath = acos((a.r * a.r + d * d - b.r * b.r) / d / a.r / 2.);
    T res = a.r * a.r * (ath - sin(ath * 2) / 2.);
    T bth = acos((b.r * b.r + d * d - a.r * a.r) / d / b.r / 2.);
    res += b.r * b.r * (bth - sin(bth * 2) / 2.);
    return fabs(res);
}
bool isConvex(const Poly &a) {
    int n = a.size();
    int cur, pre, nxt;
    rep(i, 0, n) {
        pre = (i - 1 + n) % n;
        nxt = (i + 1) % n;
        cur = i;
        if (ccw(a[pre], a[cur], a[nxt]) == -1)
            return 0;
    }
    return 1;
}
int isContained(const Poly &a,
                const Point &b) { // 0:not contain,1:on edge,2:contain

    bool res = 0;
    int n = a.size();
    rep(i, 0, n) {
        Point p = a[i] - b, q = a[(i + 1) % n] - b;
        if (p.Y > q.Y)
            swap(p, q);
        if (p.Y < eps and eps < q.Y and cross(p, q) > eps)
            res ^= 1;
        if (eq(cross(p, q), .0) and dot(p, q) < eps)
            return 1;
    }
    return (res ? 2 : 0);
}
Poly ConvexHull(Poly &a) {
    sort(ALL(a), [](const Point &p, const Point &q) {
        return (eq(p.Y, q.Y) ? p.X < q.X : p.Y < q.Y);
    });
    a.erase(unique(ALL(a)), a.end());
    int n = a.size(), k = 0;
    Poly res(n * 2);
    for (int i = 0; i < n; res[k++] = a[i++]) {
        while (k >= 2 and
               cross(res[k - 1] - res[k - 2], a[i] - res[k - 1]) < eps)
            k--;
    }
    for (int i = n - 2, t = k + 1; i >= 0; res[k++] = a[i--]) {
        while (k >= t and
               cross(res[k - 1] - res[k - 2], a[i] - res[k - 1]) < eps)
            k--;
    }
    res.resize(k - 1);
    return res;
}
T Diam(const Poly &a) {
    int n = a.size();
    int x = 0, y = 0;
    rep(i, 1, n) {
        if (a[i].Y > a[x].Y)
            x = i;
        if (a[i].Y < a[y].Y)
            y = i;
    }
    T res = abs(a[x] - a[y]);
    int i = x, j = y;
    do {
        if (cross(a[(i + 1) % n] - a[i], a[(j + 1) % n] - a[j]) < 0)
            i = (i + 1) % n;
        else
            j = (j + 1) % n;
        chmax(res, abs(a[i] - a[j]));
    } while (i != x or j != y);
    return res;
}
Poly Cut(const Poly &a, const Line &l) {
    int n = a.size();
    Poly res;
    rep(i, 0, n) {
        Point p = a[i], q = a[(i + 1) % n];
        if (ccw(l.a, l.b, p) != -1)
            res.push_back(p);
        if (ccw(l.a, l.b, p) * ccw(l.a, l.b, q) < 0)
            res.push_back(Intersection(Line(p, q), l));
    }
    return res;
}

T Closest(Poly &a) {
    int n = a.size();
    if (n <= 1)
        return 0;
    sort(ALL(a), [&](Point a, Point b) {
        return (eq(a.X, b.X) ? a.Y < b.Y : a.X < b.X);
    });
    Poly buf(n);
    auto rec = [&](auto self, int lb, int rb) -> T {
        if (rb - lb <= 1)
            return (T)INF;
        int mid = (lb + rb) >> 1;
        auto x = a[mid].X;
        T res = min(self(self, lb, mid), self(self, mid, rb));
        inplace_merge(a.begin() + lb, a.begin() + mid, a.begin() + rb,
                      [&](auto p, auto q) { return p.Y < q.Y; });
        int ptr = 0;
        rep(i, lb, rb) {
            if (abs(a[i].X - x) >= res)
                continue;
            rep(j, 0, ptr) {
                auto sub = a[i] - buf[ptr - 1 - j];
                if (sub.Y >= res)
                    break;
                chmin(res, abs(sub));
            }
            buf[ptr++] = a[i];
        }
        return res;
    };
    return rec(rec, 0, n);
}

Circle Incircle(const Point &a, const Point &b, const Point &c) {
    T A = abs(b - c), B = abs(c - a), C = abs(a - b);
    Point p(A * a.X + B * b.X + C * c.X, A * a.Y + B * b.Y + C * c.Y);
    p /= (A + B + C);
    T r = Dist(Line(a, b), p);
    return Circle(p, r);
}
Circle Circumcircle(const Point &a, const Point &b, const Point &c) {
    Line l1((a + b) / 2., (a + b) / 2. + (b - a) * Point(0, 1));
    Line l2((b + c) / 2., (b + c) / 2. + (c - b) * Point(0, 1));
    Point p = Intersection(l1, l2);
    return Circle(p, abs(p - a));
}
Poly tangent(const Point &a, const Circle &b) {
    return Intersection(b, Circle(a, sqrt(norm(b.p - a) - b.r * b.r)));
}
vector<Line> tangent(const Circle &a, const Circle &b) {
    vector<Line> res;
    T d = abs(a.p - b.p);
    if (eq(d, 0.))
        return res;
    Point u = unit(b.p - a.p);
    Point v = u * Point(0, 1);
    for (int t : {-1, 1}) {
        T h = (a.r + b.r * t) / d;
        if (eq(h * h, 1.)) {
            res.push_back(Line(a.p + (h > 0 ? u : -u) * a.r,
                               a.p + (h > 0 ? u : -u) * a.r + v));
        } else if (1 > h * h) {
            Point U = u * h, V = v * sqrt(1 - h * h);
            res.push_back(Line(a.p + (U + V) * a.r, b.p - (U + V) * (b.r * t)));
            res.push_back(Line(a.p + (U - V) * a.r, b.p - (U - V) * (b.r * t)));
        }
    }
    return res;
}

/**
 * @brief Geometry
 */

double PI = 3.14159265358979;

double Dist2(Poly& A, Poly& B) {
    int N = A.size(), M = B.size();
    bool check = true;
    rep(i,0,N) {
        if (isContained(B,A[i]) == 0) check = false;
    }
    if (check) return 0.0;
    check = true;
    rep(i,0,M) {
        if (isContained(A,B[i]) == 0) check = false;
    }
    if (check) return 0.0;
    double Ret = INF;
    rep(i,0,N) {
        rep(j,0,M) {
            Segment S1(A[i],A[(i+1)%N]);
            Segment S2(B[j],B[(j+1)%M]);
            chmin(Ret,Dist(S1,S2));
        }
    }
    return Ret;
}

int main() {
while(1) {
    int N;
    cin >> N;
    if (N == 0) return 0;
    vector<Poly> Temp(N), P(N);
    vector<double> H(N);
    rep(i,0,N) {
        int M;
        cin >> M >> H[i];
        rep(j,0,M) {
            double PX, PY;
            cin >> PX >> PY;
            Temp[i].push_back(Point(PX,PY));
        }
    }
    double A, B;
    cin >> A >> B;
    A += 180;
    B = 90 - B;
    A = PI * A / 180.0, B = PI * B / 180.0;
    Point D = polar(tan(B),A);
    rep(i,0,N) {
        int M = Temp[i].size();
        rep(j,0,M) {
            Temp[i].push_back(Temp[i][j] + D * H[i]);
        }
        P[i] = ConvexHull(Temp[i]);
    }
    {
        double SX, SY, TX, TY;
        cin >> SX >> SY >> TX >> TY;
        P.push_back({Point(SX,SY)});
        P.push_back({Point(TX,TY)});
    }
    N += 2;
    vector<vector<double>> G(N,vector<double>(N));
    rep(i,0,N) rep(j,0,N) G[i][j] = Dist2(P[i],P[j]);
    rep(k,0,N) {
        rep(i,0,N) {
            rep(j,0,N) chmin(G[i][j], G[i][k] + G[k][j]);
        }
    }
    print(G[N-2][N-1]);
}
}
accuracy: 1
time_ms: 790
memory_kb: 4308
final_rank: 10

code_snippet: #line 2 "cp-library/src/cp-template.hpp"
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ld = long double;
using uint = unsigned int;
using ull  = unsigned long long;
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128_t;
template < class T > bool chmin(T& a, T b) { if(a > b) { a = b; return true; } return false; }
template < class T > bool chmax(T& a, T b) { if(a < b) { a = b; return true; } return false; }
template < class T, class U > T ceil (T x, U y) { return (x > 0 ? (x + y - 1) / y :           x / y); }
template < class T, class U > T floor(T x, U y) { return (x > 0 ?           x / y : (x - y + 1) / y); }
int popcnt(i32 x) { return __builtin_popcount(x); }
int popcnt(u32 x) { return __builtin_popcount(x); }
int popcnt(i64 x) { return __builtin_popcountll(x); }
int popcnt(u64 x) { return __builtin_popcountll(x); }

#line 2 "cp-library/src/utility/rep_itr.hpp"
template < class T > struct itr_rep {
    T i, d;
    constexpr itr_rep(const T i) noexcept : i(i), d(1) {}
    constexpr itr_rep(const T i, const T d) noexcept : i(i), d(d) {}
    void operator++() noexcept { i += d; }
    constexpr int operator*() const noexcept { return i; }
    constexpr bool operator!=(const itr_rep x) const noexcept { return d > 0 ? i < x.i : i > x.i; }
};

template < class T > struct rep {
    const itr_rep< T > s, t;
    constexpr rep(const T t) noexcept : s(0), t(t) {}
    constexpr rep(const T s, const T t) noexcept : s(s), t(t) {}
    constexpr rep(const T s, const T t, const T d) noexcept : s(s, d), t(t, d) {}
    constexpr auto begin() const noexcept { return s; }
    constexpr auto end  () const noexcept { return t; }
};

template < class T > struct revrep {
    const itr_rep < T > s, t;
    constexpr revrep(const T t) noexcept : s(t - 1, -1), t(-1, -1) {}
    constexpr revrep(const T s, const T t) noexcept : s(t - 1, -1), t(s - 1, -1) {}
    constexpr revrep(const T s, const T t, const T d) noexcept : s(t - 1, -d), t(s - 1, -d) {}
    constexpr auto begin() const noexcept { return s; }
    constexpr auto end  () const noexcept { return t; }
};
#line 3 "cp-library/src/utility/io.hpp"

/* 128bit integer */
istream& operator>>(istream& is, i128& x) {
    std::string s; is >> s;
    int pm = (s[0] == '-');
    x = 0;
    for(int i : rep(pm, int(s.size()))) x = x * 10 + (s[i] - '0');
    if(pm) x *= -1;
    return is;
}
ostream& operator<<(ostream& os, const i128& x) {
    if(x == 0) return os << '0';
    i128 y = x;
    if(y < 0) { os << '-'; y *= -1; }
    std::vector<int> ny;
    while(y > 0) { ny.push_back(y % 10); y /= 10; }
    for(int i : revrep(ny.size())) os << ny[i];
    return os;
}

template < class S, class T > istream& operator>>(istream& is,       std::pair< S, T >& x) { is >> x.first        >> x.second; return is; }
template < class S, class T > ostream& operator<<(ostream& os, const std::pair< S, T >& x) { os << x.first << " " << x.second; return os; }

namespace scanner {
    struct sca {
        template < class T > operator T() {
            T s; std::cin >> s; return s;
        }
    };
    struct vec {
        int n;
        vec(int n) : n(n) {}
        template < class T > operator std::vector< T >() {
            std::vector< T > v(n);
            for(T& x : v) std::cin >> x;
            return v;
        }
    };
    struct mat {
        int h, w;
        mat(int h, int w) : h(h), w(w) {}
        template < class T > operator std::vector< std::vector< T > >() {
            std::vector m(h, std::vector< T >(w));
            for(std::vector< T >& v : m) for(T& x : v) std::cin >> x;
            return m;
        }
    };
    struct speedup {
        speedup() {
            std::cin.tie(0);
            std::ios::sync_with_stdio(0);
        }
    } speedup_instance;
}
scanner::sca in() { return scanner::sca(); }
scanner::vec in(int n) { return scanner::vec(n); }
scanner::mat in(int h, int w) { return scanner::mat(h, w); }

namespace printer {
    void precision(int d) { std::cout << std::fixed << std::setprecision(d); }
    void flush() { std::cout.flush(); }
}

template < class T >
ostream& operator<<(ostream& os, const std::vector< T > a) {
    int n = a.size();
    for(int i : rep(n)) { os << a[i]; if(i != n - 1) os << ' '; }
    return os;
}

int print() { std::cout << '\n'; return 0; }
template < class head, class... tail > int print(head&& h, tail&&... t) {
    std::cout << h; if(sizeof...(tail)) std::cout << ' ';
    return print(std::forward<tail>(t)...);
}
template < class T > int print_n(const std::vector< T > a) {
    int n = a.size();
    for(int i : rep(n)) std::cout << a[i] << "\n";
    return 0;
}


#line 2 "cp-library/src/utility/key_val.hpp"

template < class K, class V >
struct key_val {
    K key; V val;
    key_val() {}
    key_val(K key, V val) : key(key), val(val) {}
    template < std::size_t Index >
    std::tuple_element_t< Index, key_val >& get() {
        if constexpr (Index == 0) return key;
        if constexpr (Index == 1) return val;
    }
};

namespace std {

template < class K, class V > struct tuple_size < key_val< K, V > > : integral_constant< size_t, 2 > {};
template < class K, class V > struct tuple_element < 0, key_val< K, V > > { using type = K; };
template < class K, class V > struct tuple_element < 1, key_val< K, V > > { using type = V; };

}
#line 2 "cp-library/src/utility/vec_op.hpp"
template < class T > key_val< int, T > max_of(const vector< T >& a) {
    int i = std::max_element(a.begin(), a.end()) - a.begin();
    return {i, a[i]};
}
template < class T > key_val< int, T > min_of(const vector< T >& a) {
    int i = std::min_element(a.begin(), a.end()) - a.begin();
    return {i, a[i]};
}
template < class S, class T > S sum_of(const vector< T >& a) {
    S sum = 0;
    for(const T x : a) sum += x;
    return sum;
}
template < class S, class T > vector< S > freq_of(const vector< T >& a, T L, T R) {
    vector< S > res(R - L, S(0));
    for(const T x : a) res[x - L] += 1;
    return res;
}
template < class S, class T > struct prefix_sum {
    vector< S > s;
    prefix_sum(const vector< T >& a) : s(a) {
        s.insert(s.begin(), S(0));
        for(int i : rep(a.size())) s[i + 1] += s[i];
    }
    // [L, R)
    S sum(int L, int R) { return s[R] - s[L]; }
};
#line 3 "cp-library/src/utility/heap.hpp"

template < class T > using heap_min = std::priority_queue< T, std::vector< T >, std::greater< T > >;
template < class T > using heap_max = std::priority_queue< T, std::vector< T >, std::less< T > >;

#line 27 "cp-library/src/cp-template.hpp"

#line 1 "cp-library/src/algorithm/bin_search.hpp"
template < class T, class F >
T bin_search(T ok, T ng, F f) {
    while(abs(ng - ok) > 1) {
        T mid = (ok + ng) / 2;
        (f(mid) ? ok : ng) = mid;
    }
    return ok;
}

template < class T, class F >
T bin_search_real(T ok, T ng, F f, int step = 80) {
    while(step--) {
        T mid = (ok + ng) / 2;
        (f(mid) ? ok : ng) = mid;
    }
    return ok;
}
#line 2 "cp-library/src/algorithm/argsort.hpp"

template < class T > std::vector< int > argsort(const std::vector< T > &a) {
    std::vector< int > ids((int)a.size());
    std::iota(ids.begin(), ids.end(), 0);
    std::sort(ids.begin(), ids.end(), [&](int i, int j) {
        return a[i] < a[j] || (a[i] == a[j] && i < j);
    });
    return ids;
}
#line 1 "macro.hpp"
namespace macro {

using size_type = int;
template < class container > void  sort(container& a) { std::sort(std:: begin(a), std:: end(a)); }
template < class container > void rsort(container& a) { std::sort(std::rbegin(a), std::rend(a)); }
template < class container > void reverse(container& a) { std::reverse(std::begin(a), std::end(a)); }
template < class container > void unique(container& a) {
    std::sort(std::begin(a), std::end(a));
    a.erase(std::unique(std::begin(a), std::end(a)), std::end(a));
}
template < class container > container  sorted(const container& a) { container b = a;  sort(b); return std::move(b); }
template < class container > container rsorted(const container& a) { container b = a; rsort(b); return std::move(b); }
template < class container, class compare > void sort(container& a, const compare& cmp) { std::sort(std::begin(a), std::end(a), cmp); }
template < class container, class compare > container sorted(const container& a, const compare& cmp) { container b = a; sort(b, cmp); return std::move(b); }
template < class container, class value > size_type lower_bound(const container& a, const value& x) { return std::lower_bound(std::begin(a), std::end(a), x) - std::begin(a); }
template < class container, class value > size_type upper_bound(const container& a, const value& x) { return std::upper_bound(std::begin(a), std::end(a), x) - std::begin(a); }

const std::vector<std::pair<size_type, size_type>> dir4 = { {+1,  0}, {-1,  0}, { 0, +1}, { 0, -1} };
const std::vector<std::pair<size_type, size_type>> dir8 = { {-1, -1}, {-1,  0}, {-1, +1}, { 0, -1}, { 0, +1}, {+1, -1}, {+1,  0}, {+1, +1} };

#ifdef _DEBUG
#define debug(x) std::cout << "[" << __LINE__ << "] " << #x << ": " << x << std::endl
#else
#define debug(x)
#endif

template < class container > void concat(container& a, const container& b) {
    a.insert(std::end(a), std::begin(b), std::end(b));
}
std::vector<size_type> iota(const size_type n) {
    std::vector<size_type> I(n);
    std::iota(std::begin(I), std::end(I), 0);
    return I;
}
template < class container > std::vector<size_type> sort_idx(const container& a) {
    const size_type n = a.size();
    std::vector<size_type> I = iota(n);
    std::sort(std::begin(I), std::end(I), [&](size_type i, size_type j) { return a[i] < a[j] or (a[i] == a[j] and i < j); });
    return I;
}
template < class container, class compare > std::vector<size_type> sort_idx(const container& a, const compare& cmp) {
    const size_type n = a.size();
    std::vector<size_type> I = iota(n);
    std::sort(std::begin(I), std::end(I), [&](size_type i, size_type j) { return cmp(a[i], a[j]) or (a[i] == a[j] and i < j); });
    return std::move(I);
}

struct grid {
    using size_type = int;
    size_type H, W;
    grid(const size_type H, const size_type W) : H(H), W(W) {}
    bool contains(const size_type i, const size_type j) {
        return 0 <= i and i < H and 0 <= j and j < W;
    }
};

using f64 = long double;

template < class T > vector< T >& operator++(vector< T >& a) { for(T& x : a) x++; return a; }
template < class T > vector< T >& operator--(vector< T >& a) { for(T& x : a) x--; return a; }
template < class T > vector< T >  operator++(vector< T >& a, signed) { vector< T > res = a; for(T& x : a) x++; return res; }
template < class T > vector< T >  operator--(vector< T >& a, signed) { vector< T > res = a; for(T& x : a) x--; return res; }

} // namespace macro

using namespace macro;
#line 2 "cp-library/src/geometry/base.hpp"

template < class T > struct point {
    T x, y;
    point() : x(0), y(0) {}
    point(T x, T y) : x(x), y(y) {}
    point(std::pair< T, T > p) : x(p.first), y(p.second) {}
    point& operator+=(const point& p) { x += p.x, y += p.y; return *this; }
    point& operator-=(const point& p) { x -= p.x, y -= p.y; return *this; }
    point& operator*=(const T r) { x *= r, y *= r; return *this; }
    point& operator/=(const T r) { x /= r, y /= r; return *this; }
    point operator+(const point& p) const { return point(*this) += p; }
    point operator-(const point& p) const { return point(*this) -= p; }
    point operator*(const T r) const { return point(*this) *= r; }
    point operator/(const T r) const { return point(*this) /= r; }
    point operator-() const { return {-x, -y}; }
    bool operator==(const point& p) const { return x == p.x and y == p.y; }
    bool operator!=(const point& p) const { return x != p.x or  y != p.y; }
    bool operator<(const point& p) const { return x == p.x ? y < p.y : x < p.x; }
    point< T > rot(double theta) {
        static_assert(is_floating_point_v< T >);
        double cos_ = std::cos(theta), sin_ = std::sin(theta);
        return {cos_ * x - sin_ * y, sin_ * x + cos_ * y};
    }
};
template < class T > istream& operator>>(istream& is, point< T >& p) { return is >> p.x >> p.y; }
template < class T > ostream& operator<<(ostream& os, point< T >& p) { return os << p.x << " " << p.y; }
template < class T > T dot(const point< T >& a, const point< T >& b) { return a.x * b.x + a.y * b.y; }
template < class T > T det(const point< T >& a, const point< T >& b) { return a.x * b.y - a.y * b.x; }
template < class T > T norm(const point< T >& p) { return p.x * p.x + p.y * p.y; }
template < class T > double abs(const point< T >& p) { return std::sqrt(norm(p)); }
template < class T > double angle(const point< T >& p) { return std::atan2(p.y, p.x); }
template < class T > int sign(const T x) {
    T e = (is_integral_v< T > ? 1 : 1e-8);
    if(x <= -e) return -1;
    if(x >= +e) return +1;
    return 0;
}
template < class T > bool equals(const T& a, const T& b) { return sign(a - b) == 0; }
template < class T > int ccw(const point< T >& a, point< T > b, point< T > c) {
    b -= a, c -= a;
    if(sign(det(b, c)) == +1) return +1; // counter clockwise
    if(sign(det(b, c)) == -1) return -1; //         clockwise
    if(sign(dot(b, c)) == -1) return +2; // c-a-b
    if(norm(b) < norm(c)) return -2;     // a-b-c
    return 0;                            // a-c-b
}

template < class T > struct line {
    point< T > a, b;
    line() {}
    line(point< T > a, point< T > b) : a(a), b(b) {}
};
template < class T > point< T > projection(const line< T >& l, const point< T >& p) {
    static_assert(is_floating_point_v< T >);
    return l.a + (l.a - l.b) * dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
}
template < class T > point< T > reflection(const line< T >& l, const point< T >& p) {
    static_assert(is_floating_point_v< T >);
    return p + (projection(l, p) - p) * T(2);
}
template < class T > bool orthogonal(const line< T >& a, const line< T >& b) { return equals(dot(a.b - a.a, b.b - b.a), T(0)); }
template < class T > bool parallel  (const line< T >& a, const line< T >& b) { return equals(det(a.b - a.a, b.b - b.a), T(0)); }
template < class T > point< T > cross_point_ll(const line< T >& l, const line< T >& m) {
    static_assert(is_floating_point_v< T >);
    T A = det(l.b - l.a, m.b - m.a);
    T B = det(l.b - l.a, l.b - m.a);
    if(equals(abs(A), T(0)) and equals(abs(B), T(0))) return m.a;
    return m.a + (m.b - m.a) * B / A;
}

template < class T > using segment = line< T >;
template < class T > bool intersect_ss(const segment< T >& s, const segment< T >& t) {
    return ccw(s.a, s.b, t.a) * ccw(s.a, s.b, t.b) <= 0 and ccw(t.a, t.b, s.a) * ccw(t.a, t.b, s.b) <= 0;
}
template < class T > double distance_sp(const segment< T >& s, const point< T >& p) {
    static_assert(is_floating_point_v< T >);
    point r = projection(s, p);
    if(ccw(s.a, s.b, r) == 0) return abs(r - p);
    return std::min(abs(s.a - p), abs(s.b - p));
}
template < class T > double distance_ss(const segment< T >& a, const segment< T >& b) {
    if(intersect_ss(a, b)) return 0;
    return std::min({ distance_sp(a, b.a), distance_sp(a, b.b), distance_sp(b, a.a), distance_sp(b, a.b) });
}

template < class T > using polygon = std::vector< point< T > >;
template < class T > T area2(const polygon< T >& p) {
    T s = 0;
    int n = p.size();
    for(int i = 0; i < n; i++) s += det(p[i], p[(i + 1) % n]);
    return s;
}
template < class T > T area(const polygon< T >& p) { return area2(p) / T(2); }

template < class T > bool is_convex(const polygon< T >& p) {
    int n = p.size();
    for(int i = 0; i < n; i++) if(ccw(p[(i - 1 + n) % n], p[i], p[(i + 1) % n]) == -1) return false;
    return true;
}
template < class T > int contains(const polygon< T >& g, const point< T >& p) {
    int n = g.size();
    bool in = false;
    for(int i = 0; i < n; i++) {
        point a = g[i] - p, b = g[(i + 1) % n] - p;
        if(sign(a.y - b.y) == +1) std::swap(a, b);
        if(sign(a.y) <= 0 and sign(b.y) ==+1 and sign(det(a, b)) == -1) in = !in;
        if(sign(det(a, b)) == 0 and sign(dot(a, b)) <= 0) return 1; // ON
    }
    return in ? 2 : 0;
}
template < class T > polygon< T > convex_cut(const polygon< T >& p, const line< T >& l) {
    int n = p.size();
    polygon< T > res;
    for(int i = 0; i < n; i++) {
        point now = p[i], nxt = p[(i + 1) % n];
        if(ccw(l.a, l.b, now) != -1) res.push_back(now);
        if(ccw(l.a, l.b, now) * ccw(l.a, l.b, nxt) < 0) res.push_back(cross_point_ll(line(now, nxt), l));
    }
    return res;
}
template < class T > polygon< T > convex_hull(polygon< T >& p) {
    int n = p.size(), k = 0;
    if(n <= 2) return p;
    std::sort(p.begin(), p.end());
    polygon< T > ch(n + n);
    for(int i = 0; i < n; ch[k++] = p[i++])
        while(k >= 2 and sign(det(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1])) == -1) k--;
    for(int i = n - 2, t = k + 1; i >= 0; ch[k++] = p[i--])
        while(k >= t and sign(det(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1])) == -1) k--;
    ch.resize(k - 1);
    return ch;
}
template < class T > T diameter2(const polygon< T >& p) {
    static_assert(is_floating_point_v< T >);
    int n = p.size(), is = 0, js = 0;
    for(int i = 1; i < n; i++) {
        if(sign(p[i].y - p[is].y) == +1) is = i;
        if(sign(p[i].y - p[js].y) == -1) js = i;
    }
    T dist_max = norm(p[is] - p[js]);
    int maxi = is, i = is, maxj = js, j = js;
    do {
        if(sign(det(p[(i + 1) % n] - p[i], p[(j + 1) % n] - p[j])) >= 0) j = (j + 1) % n; else i = (i + 1) % n;
        if(norm(p[i] - p[j]) > dist_max) {
            dist_max = norm(p[i] - p[j]);
            maxi = i, maxj = j;
        }
    } while(i != is or j != js);
    return dist_max;
}
template < class T > double diameter(const polygon< T >& p) {
    static_assert(is_floating_point_v< T >);
    return std::sqrt(diameter2(p));
}

template < class T > struct circle {
    point< T > p;
    T r;
    circle() = default;
    circle(point< T > p, T r) : p(p), r(r) {}
};
template < class T > istream& operator>>(istream& is, circle< T >& c) { return is >> c.p >> c.r; }
template < class T > int intersect_cc(circle< T > c1, circle< T > c2) {
    if(c1.r < c2.r) std::swap(c1, c2);
    T d = abs(c1.p - c2.p);
    if(sign(c1.r + c2.r - d) == -1) return 4;
    if(equals(c1.r + c2.r, d)) return 3;
    if(sign(c1.r - c2.r - d) == -1) return 2;
    if(equals(c1.r - c2.r, d)) return 1;
    return 0;
}
template < class T > std::pair<point< T >, point< T >> cross_point_cc(const circle< T >& c1, const circle< T >& c2) {
    T d = abs(c1.p - c2.p);
    T a = std::acos((c1.r * c1.r + d * d - c2.r * c2.r) / (2 * c1.r * d));
    T t = angle(c2.p - c1.p);
    point< T > p1 = c1.p + point< T >(std::cos(t + a), std::sin(t + a)) * c1.r;
    point< T > p2 = c1.p + point< T >(std::cos(t - a), std::sin(t - a)) * c1.r;
    return {p1, p2};
}
#line 4 "A.cpp"

f64 solve(int N) {
    vector<int> NV(N), H(N);
    vector<polygon<f64>> P(N);
    for(int i : rep(N)) {
        cin >> NV[i] >> H[i];
        for(int k : rep(NV[i])) {
            point<f64> p; cin >> p;
            P[i].push_back(p);
        }
    }
    f64 theta = in(), phi = in();
    theta = theta / 180.0 * M_PI;
    phi = phi / 180.0 * M_PI;

    vector<polygon<f64>> C(N);
    for(int i : rep(N)) {
        polygon<f64> Q;
        for(int k : rep(NV[i])) {
            Q.push_back(P[i][k]);
            Q.push_back(P[i][k] - point<f64>(f64(H[i]) / tan(phi), 0).rot(theta));
        }
        C[i] = convex_hull(Q);
    }
    vector D(N + 2, vector(N + 2, f64(1e18)));
    const int S = N, T = S + 1;
    point<f64> PS, PT; cin >> PS >> PT;
    D[S][T] = D[T][S] = abs(PS - PT);
    for(int i : rep(N)) {
        const int ci = C[i].size();
        f64 ds = 1e18, dt = 1e18;
        for(int k : rep(ci)) {
            chmin(ds, (f64)distance_sp(segment<f64>(C[i][k], C[i][(k + 1) % ci]), PS));
            chmin(dt, (f64)distance_sp(segment<f64>(C[i][k], C[i][(k + 1) % ci]), PT));
        }
        if(contains(C[i], PS)) ds = 0;
        if(contains(C[i], PT)) dt = 0;
        D[S][i] = D[i][S] = ds;
        D[T][i] = D[i][T] = dt;
    }
    for(int i : rep(N)) for(int j : rep(N)) if(i != j) {
        const int ci = C[i].size();
        const int cj = C[j].size();
        for(int ki : rep(ci)) for(int kj : rep(cj)) {
            chmin(D[i][j], (f64)distance_ss(segment<f64>(C[i][ki], C[i][(ki + 1) % ci]), segment<f64>(C[j][kj], C[j][(kj + 1) % cj])));
        }
    }
    for(int k : rep(N + 2)) for(int i : rep(N + 2)) for(int j : rep(N + 2)) chmin(D[i][j], D[i][k] + D[k][j]);
    return D[S][T];
}

int main() {
    while(true) {
        int N; cin >> N;
        if(N == 0) return 0;
        printer::precision(20);
        print(solve(N));
    }
}
accuracy: 1
time_ms: 1680
memory_kb: 4240
final_rank: 12

code_snippet: #include <bits/stdc++.h>
#include <time.h>
using namespace std;
using ll = long long;
using vll = vector<ll>;
using vvll = vector<vll>;
using vvvll = vector<vvll>;
using vd = vector<double>;
using vvd = vector<vd>;
using vvvd = vector<vvd>;
#define all(A) A.begin(),A.end()
#define ALL(A) A.begin(),A.end()
#define rep(i, n) for (int i = 0; i < (int) (n); i++)
template<class T>
bool chmax(T& p, T q) {
    if (p < q) {
        p = q; return 1;
    }
    return 0;
};
template<class T>
bool chmin(T& p, T q) {
    if (p > q) {
        p = q; return 1;
    }
    return 0;
};


#include<complex>
// using ll = long long;
// const int INF = 1000000000;
// const ll LINF = 1001002003004005006ll;
// int dx[] = { 1,0,-1,0 }, dy[] = { 0,1,0,-1 };
// // ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
// template<class T>bool chmax(T& a, const T& b) { if (a < b) { a = b; return true; }return false; }
// template<class T>bool chmin(T& a, const T& b) { if (b < a) { a = b; return true; }return false; }

struct IOSetup {
    IOSetup() {
        cin.tie(0);
        ios::sync_with_stdio(0);
        cout << fixed << setprecision(12);
    }
} iosetup;

template<typename T>
ostream& operator<<(ostream& os, const vector<T>& v) {
    for (int i = 0; i < (int)v.size(); i++) os << v[i] << (i + 1 == (int)v.size() ? "" : " ");
    return os;
}
template<typename T>
istream& operator>>(istream& is, vector<T>& v) {
    for (T& x : v)is >> x;
    return is;
}

#line 1 "Geometry/template.cpp"
// Real
using Real = double;
const Real EPS = 1e-6;
const Real pi = acosl(-1);

// Point
using Point = complex<Real>;
istream& operator>>(istream& is, Point& p) {
    Real a, b;
    is >> a >> b;
    p = Point(a, b);
    return is;
}
ostream& operator<<(ostream& os, Point& p) {
    return os << fixed << setprecision(12) << p.real() << ' ' << p.imag();
}
inline bool eq(Real a, Real b) {
    return fabs(a - b) < EPS;
}
Point operator*(const Point& p, const Real& d) {
    return Point(real(p) * d, imag(p) * d);
}

// Line
struct Line {
    Point p1, p2;
    Line() = default;
    Line(Point p1, Point p2) :p1(p1), p2(p2) {}
    //Ax + By = C
    Line(Real A, Real B, Real C) {
        if (eq(A, 0))     p1 = Point(0, C / B), p2 = Point(1, C / B);
        else if (eq(B, 0))p1 = Point(C / A, 0), p2 = Point(C / A, 1);
        else            p1 = Point(0, C / B), p2 = Point(C / A, 0);
    }
};

// Segment
struct Segment :Line {
    Segment() = default;
    Segment(Point p1, Point p2) :Line(p1, p2) {}
};

// Polygon
using Polygon = vector<Point>;
#line 1 "Geometry/Rotate.cpp"
Point rotate(Real theta, Point p) {
    return Point(cos(theta) * real(p) - sin(theta) * imag(p), sin(theta) * real(p) + cos(theta) * imag(p));
}
#line 1 "Geometry/Dot.cpp"
// Dot
Real dot(Point a, Point b) {
    return real(a) * real(b) + imag(a) * imag(b);
}
#line 1 "Geometry/Cross.cpp"
// Cross
Real cross(Point a, Point b) {
    return real(a) * imag(b) - imag(a) * real(b);
}
#line 1 "Geometry/CounterClockWise.cpp"
// ccw (counter clockwise) (Requires: cross, dot)
//https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/all/CGL_1_C
int ccw(Point a, Point b, Point c) {
    b -= a; c -= a;
    if (cross(b, c) > EPS)       return  1;//COUNTER CLOCKWISE
    else if (cross(b, c) < -EPS) return -1;//CLOCKWISE
    else if (dot(b, c) < 0)      return  2;//c--a--b ONLINE BACK
    else if (norm(b) < norm(c)) return -2;//a--b--c ONLINE FRONT
    else                     return  0;//a--c--b ON SEGMENT
}
#line 1 "Geometry/Projection.cpp"
// Projection (Requires: dot)
Point projection(Line l, Point p) {
    // ベクトルl乗に点pからおろした垂線の足
    Real k = dot(l.p1 - l.p2, p - l.p1) / norm(l.p1 - l.p2);
    return l.p1 + (l.p1 - l.p2) * k;
}
Point projection(Segment l, Point p) {
    Real k = dot(l.p1 - l.p2, p - l.p1) / norm(l.p1 - l.p2);
    return l.p1 + (l.p1 - l.p2) * k;
}
#line 1 "Geometry/Intersect.cpp"
// Intersect (Requires : ccw, Dots, Cross, Projection)
bool intersect(Line l, Point p) {
    return abs(ccw(l.p1, l.p2, p)) != 1;
}
//直線の交差判定，外積
bool intersect(Line l1, Line l2) {
    return abs(cross(l1.p2 - l1.p1, l2.p2 - l2.p1)) > EPS || abs(cross(l1.p2 - l1.p1, l2.p2 - l1.p1)) < EPS;
}
//線分に点が乗るかの判定，ccw
bool intersect(Segment s, Point p) {
    return ccw(s.p1, s.p2, p) == 0;
}
//直線と線分の交差判定
bool intersect(Line l, Segment s) {
    return cross(l.p2 - l.p1, s.p1 - l.p1) * cross(l.p2 - l.p1, s.p2 - l.p1) < EPS;
}
//線分と線分の交差判定
bool intersect(Segment s, Segment t) {
    return ccw(s.p1, s.p2, t.p1) * ccw(s.p1, s.p2, t.p2) <= 0 && ccw(t.p1, t.p2, s.p1) * ccw(t.p1, t.p2, s.p2) <= 0;
}
#line 1 "Geometry/Distance.cpp"
// Distance (Requires: Projection, Intersect)
Real dis(Point a, Point b) {
    return abs(a - b);
}
Real dis(Line l, Point p) {
    return abs(p - projection(l, p));
}
Real dis(Segment s, Point p) {
    Point r = projection(s, p);
    if (intersect(s, r)) return abs(r - p);
    return min(abs(s.p1 - p), abs(s.p2 - p));
}
Real dis(Segment a, Segment b) {
    if (intersect(a, b)) return 0;
    return min({ dis(a,b.p1),dis(a,b.p2),dis(b,a.p1),dis(b,a.p2) });
}
Real dis(Polygon a, Polygon b) {
    Real ret = -10;
    int n = (int)a.size(), m = (int)b.size();
    for (int i = 0; i < n; i++)for (int j = 0; j < m; j++) {
        Real d = dis(Segment(a[i], a[(i + 1) % n]), Segment(b[j], b[(j + 1) % m]));
        if (ret < 0) ret = d;
        else      ret = min(ret, d);
    }
    return ret;
}
Real dis(Polygon poly, Point p) {
    Real ret = -10;
    int n = (int)poly.size();
    for (int i = 0; i < n; i++) {
        Real d = dis(Segment(poly[i], poly[(i + 1) % n]), p);
        if (ret < 0) ret = d;
        else      ret = min(ret, d);
    }
    return ret;
}
#line 1 "Geometry/CrossPoint.cpp"
//intersectをチェックすること
//v
Point crosspoint(Line l, Line m) {
    Real A = cross(m.p2 - m.p1, m.p1 - l.p1);
    Real B = cross(m.p2 - m.p1, l.p2 - l.p1);
    if (eq(A, 0) && eq(B, 0)) return l.p1;
    if (eq(B, 0))             throw "NAI";
    return l.p1 + A / B * (l.p2 - l.p1);
}
Point crosspoint(Segment l, Segment m) {
    return crosspoint(Line(l), Line(m));
}
// out 0, on 1, in 2
int contains(Polygon poly, Point p) {
    int res = 0;
    int n = (int)poly.size();
    for (int i = 0; i < n; i++) {
        Point a = poly[i] - p, b = poly[(i + 1) % n] - p;
        if (imag(a) > imag(b)) swap(a, b);
        if (imag(a) <= 0 && 0 < imag(b) && cross(a, b) < 0) res ^= 1;
        if (eq(cross(a, b), 0) && (dot(a, b) < 0 || eq(dot(a, b), 0))) return 1;
    }
    if (res) res = 2;
    return res;
}

using shadow=vector<Point>;
double Distance(shadow &A,shadow &B){
    ll AN=A.size()/2;
    ll BN=B.size()/2;
    double res=1e18;
    rep(i,AN)rep(p,BN){
        chmin(res,dis(Segment(A[i],A[(i+1)%AN]),Segment(B[p],B[(p+1)%BN])));
        chmin(res,dis(Segment(A[i],A[(i+1)%AN]),Segment(B[p+BN],B[(p+1)%BN+BN])));
        chmin(res,dis(Segment(A[i],A[(i+1)%AN]),Segment(B[p],B[p+BN])));

        chmin(res,dis(Segment(A[i+AN],A[(i+1)%AN+AN]),Segment(B[p],B[(p+1)%BN])));
        chmin(res,dis(Segment(A[i+AN],A[(i+1)%AN+AN]),Segment(B[p+BN],B[(p+1)%BN+BN])));
        chmin(res,dis(Segment(A[i+AN],A[(i+1)%AN+AN]),Segment(B[p],B[p+BN])));

        chmin(res,dis(Segment(A[i],A[i+AN]),Segment(B[p],B[(p+1)%BN])));
        chmin(res,dis(Segment(A[i],A[i+AN]),Segment(B[p+BN],B[(p+1)%BN+BN])));
        chmin(res,dis(Segment(A[i],A[i+AN]),Segment(B[p],B[p+BN])));
        
    }
    return res;
}
double Distance(shadow &A,Point &P){
    ll AN=A.size();
    ll N=AN/2;
    double res=1e18;
    rep(i,N){
        chmin(res,dis(Segment(A[i],A[(i+1)%N]),P));
        chmin(res,dis(Segment(A[i],A[i+N]),P));
        chmin(res,dis(Segment(A[i+N],A[(i+1)%N+N]),P));
    }
    return res;
}
vector<Point> convex_hull(vector<Point> p) {
    int n = (int)p.size(), k = 0;
    if (n <= 2)return p;
    sort(begin(p), end(p), [](Point a, Point b) {
        return real(a) != real(b) ? real(a) < real(b) : imag(a) < imag(b);
        });
    vector<Point>ch(2 * n);
    for (int i = 0; i < n; ch[k++] = p[i++]) {
        // while(k>=2 and cross(ch[k-1]-ch[k-2],p[i]-ch[k-1])<EPS)k--;
        while (k >= 2 && cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) < 0)k--;
    }
    for (int i = n - 2, t = k + 1; i >= 0; ch[k++] = p[i--]) {
        // while(k>=t and cross(ch[k-1]-ch[k-2],p[i]-ch[k-1])<EPS)k--;
        while (k >= t && cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) < 0)k--;
    }
    ch.resize(k - 1);
    return ch;
}


const ll mod = 1e9 + 7;
void solve(ll N) {
    vector<shadow> U(N);
    vector<double> H(N);
    rep(i,N){
        ll NV;
        cin>>NV;
        U[i].resize(NV);
        cin>>H[i];
        rep(j,NV){
            double x,y;
            cin>>x>>y;
            U[i][j]={x,y};
        }
    } 
    double th,phi;
    cin>>th>>phi;
    phi=phi/180.0*pi;
    th=th/180.0*pi+pi;
    
    rep(i,N){
        ll NV=U[i].size();
        double LEN=H[i]/tan(phi);
        rep(j,NV){
            double x=real(U[i][j]);
            double y=imag(U[i][j]);
            x+=LEN*cos(th);
            y+=LEN*sin(th);
            Point PP={x,y};
            U[i].push_back(PP);
        }
    }
    Point S,G;
    double x,y;
    cin>>x>>y;
    S={x,y};
    cin>>x>>y;
    G={x,y};

    priority_queue<pair<double,ll>,vector<pair<double,ll>>,greater<pair<double,ll>>> Q;
    vector<double> D(N,1e18);
    rep(i,N){
        D[i]=Distance(U[i],S);
        if(contains(convex_hull(U[i]),S)){
            D[i]=0;
        }
        Q.push({D[i],i});
    }
    vector<bool> seen(N,false);
    double an=dis(S,G);

    while(!Q.empty()){
        ll p=Q.top().second;
        double d=Q.top().first;
        Q.pop();
        if(seen[p])continue;
        seen[p]=1;
        double GD=Distance(U[p],G);
        if(contains(convex_hull(U[p]),G)){
            GD=0;
        }
        chmin(an,GD+d);
        rep(i,N){
            if(seen[i])continue;
            double nd=d+Distance(U[i],U[p]);
            if(chmin(D[i],nd+EPS)){
                D[i]=nd;
                Q.push({nd,i});
            }
        }

    }
    cout<<fixed<<setprecision(15)<<an<<endl;


}


int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);

    ll N;
    while (cin >> N) {
        if (N == 0)return 0;


        clock_t start = clock();
        solve(N);


        // clock_t end = clock();
        // const double time = static_cast<double>(end - start) / CLOCKS_PER_SEC * 1000.0;
        // // cout << time << endl;
    }

}
accuracy: 1
time_ms: 7590
memory_kb: 4092
final_rank: 19

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, s, t) for (int i = (int)(s); i < (int)(t); ++i)
#define revrep(i, t, s) for (int i = (int)(t)-1; i >= (int)(s); --i)
#define all(x) begin(x), end(x)
template <typename T> bool chmax(T& a, const T& b) { return a < b ? (a = b, 1) : 0; }
template <typename T> bool chmin(T& a, const T& b) { return a > b ? (a = b, 1) : 0; }

using T = double;
using Vec = std::complex<T>;

std::istream& operator>>(std::istream& is, Vec& p) {
    T x, y;
    is >> x >> y;
    p = {x, y};
    return is;
}

T dot(const Vec& a, const Vec& b) { return (std::conj(a) * b).real(); }

T cross(const Vec& a, const Vec& b) { return (std::conj(a) * b).imag(); }

const T PI = acos(-1.);
constexpr T eps = 1e-10;
inline bool eq(T a, T b) { return std::abs(a - b) <= eps; }
inline bool eq(Vec a, Vec b) { return std::abs(a - b) <= eps; }
inline bool lt(T a, T b) { return a < b - eps; }
inline bool leq(T a, T b) { return a <= b + eps; }

struct Line {
    Vec p1, p2;
    Line() = default;
    Line(const Vec& p1, const Vec& p2) : p1(p1), p2(p2) {}
    Vec dir() const { return p2 - p1; }
};

struct Segment : Line {
    using Line::Line;
};

struct Circle {
    Vec c;
    T r;
    Circle() = default;
    Circle(const Vec& c, T r) : c(c), r(r) {}
};

using Polygon = std::vector<Vec>;

Vec rot(const Vec& a, T ang) { return a * Vec(std::cos(ang), std::sin(ang)); }

Vec perp(const Vec& a) { return Vec(-a.imag(), a.real()); }

Vec projection(const Line& l, const Vec& p) {
    return l.p1 + dot(p - l.p1, l.dir()) * l.dir() / std::norm(l.dir());
}

Vec reflection(const Line& l, const Vec& p) {
    return T(2) * projection(l, p) - p;
}

// 0: collinear
// 1: counter-clockwise
// -1: clockwise
int ccw(const Vec& a, const Vec& b, const Vec& c) {
    if (eq(cross(b - a, c - a), 0)) return 0;
    if (lt(cross(b - a, c - a), 0)) return -1;
    return 1;
}

bool intersect(const Segment& s, const Vec& p) {
    Vec u = s.p1 - p, v = s.p2 - p;
    return eq(cross(u, v), 0) && leq(dot(u, v), 0);
}

// 0: outside
// 1: on the border
// 2: inside
int intersect(const Polygon& poly, const Vec& p) {
    const int n = poly.size();
    bool in = 0;
    for (int i = 0; i < n; ++i) {
        auto a = poly[i] - p, b = poly[(i + 1) % n] - p;
        if (eq(cross(a, b), 0) && (lt(dot(a, b), 0) || eq(dot(a, b), 0)))
            return 1;
        if (a.imag() > b.imag()) std::swap(a, b);
        if (leq(a.imag(), 0) && lt(0, b.imag()) && lt(cross(a, b), 0)) in ^= 1;
    }
    return in ? 2 : 0;
}

int intersect(const Segment& s, const Segment& t) {
    auto a = s.p1, b = s.p2;
    auto c = t.p1, d = t.p2;
    if (ccw(a, b, c) != ccw(a, b, d) && ccw(c, d, a) != ccw(c, d, b)) return 2;
    if (intersect(s, c) || intersect(s, d) || intersect(t, a) ||
        intersect(t, b))
        return 1;
    return 0;
}

// true if they have positive area in common or touch on the border
bool intersect(const Polygon& poly1, const Polygon& poly2) {
    const int n = poly1.size();
    const int m = poly2.size();
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            if (intersect(Segment(poly1[i], poly1[(i + 1) % n]),
                          Segment(poly2[j], poly2[(j + 1) % m]))) {
                return true;
            }
        }
    }
    return intersect(poly1, poly2[0]) || intersect(poly2, poly1[0]);
}

// 0: inside
// 1: inscribe
// 2: intersect
// 3: circumscribe
// 4: outside
int intersect(const Circle& c1, const Circle& c2) {
    T d = std::abs(c1.c - c2.c);
    if (lt(d, std::abs(c2.r - c1.r))) return 0;
    if (eq(d, std::abs(c2.r - c1.r))) return 1;
    if (eq(c1.r + c2.r, d)) return 3;
    if (lt(c1.r + c2.r, d)) return 4;
    return 2;
}

T dist(const Line& l, const Vec& p) {
    return std::abs(cross(p - l.p1, l.dir())) / std::abs(l.dir());
}

T dist(const Segment& s, const Vec& p) {
    if (lt(dot(p - s.p1, s.dir()), 0)) return std::abs(p - s.p1);
    if (lt(dot(p - s.p2, -s.dir()), 0)) return std::abs(p - s.p2);
    return std::abs(cross(p - s.p1, s.dir())) / std::abs(s.dir());
}

T dist(const Segment& s, const Segment& t) {
    if (intersect(s, t)) return T(0);
    return std::min(
        {dist(s, t.p1), dist(s, t.p2), dist(t, s.p1), dist(t, s.p2)});
}

std::vector<Vec> convex_hull(std::vector<Vec>& pts) {
    const int n = pts.size();
    if (n == 1) return pts;
    auto key = [&](auto& v) { return std::make_pair(v.imag(), v.real()); };
    sort(all(pts), [&](auto& p, auto& q) { return key(p) < key(q); });
    int k = 0;  // the number of vertices in the convex hull
    std::vector<Vec> ch(2 * n);
    // right
    for (int i = 0; i < n; ++i) {
        while (k > 1 && lt(cross(ch[k - 1] - ch[k - 2], pts[i] - ch[k - 1]), 0))
            --k;
        ch[k++] = pts[i];
    }
    int t = k;
    // left
    for (int i = n - 2; i >= 0; --i) {
        while (k > t && lt(cross(ch[k - 1] - ch[k - 2], pts[i] - ch[k - 1]), 0))
            --k;
        ch[k++] = pts[i];
    }
    ch.resize(k - 1);
    return ch;
}


void solve(int N) {
    vector<vector<Vec>> buildings(N);
    vector<T> height(N);
    rep(i,0,N) {
        int NV;
        cin>>NV;
        buildings[i].resize(NV);
        cin>>height[i];
        rep(j,0,NV){
            T x,y;
            cin>>x>>y;
            buildings[i][j]={x,y};
        }
    }
    T theta, phi;
    cin>>theta>>phi;
    Vec sp, tp;
    cin>>sp>>tp;
    theta *= PI / 180.;
    phi *= PI / 180.;
    vector<Polygon> shades(N+2);
    rep(i,0,N){
        auto vs = buildings[i];
        Vec v = -height[i] / tan(phi) * Vec(cos(theta), sin(theta));
        for (auto& p : buildings[i]){
            vs.push_back(p + v);
        }
        shades[i] = convex_hull(vs);
    }
    vector<vector<T>> G(N+2, vector<T>(N+2, 1e9));
    int s = N, t = N+1;
    shades[s] = {sp};
    shades[t] = {tp};
    rep(i,0,N)rep(j,i+1,N+2){
        if (intersect(shades[i], shades[j])) G[i][j] = G[j][i] = 0;
        else {
            T d = 1e9;
            if (j < N){
                for (auto p : shades[i]) {
                    rep(k,0,shades[j].size()){
                        chmin(d, dist(Segment(shades[j][k], shades[j][(k+1)%shades[j].size()]), p));
                    }
                }
            }
            for (auto p : shades[j]) {
                rep(k,0,shades[i].size()){
                    chmin(d, dist(Segment(shades[i][k], shades[i][(k+1)%shades[i].size()]), p));
                }
            }
            G[i][j]=G[j][i] = d;
        }
    }
    G[s][t] = G[t][s] = abs(sp - tp);
    rep(k,0,N+2)rep(i,0,N+2)rep(j,0,N+2){
        chmin(G[i][j], G[i][k] + G[k][j]);
    }
    cout << G[s][t] << "\n";
}


int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout << fixed << setprecision(15);

    while (true) {
        int N;
        cin>>N;
        if (N==0) break;
        solve(N);
    }
}
accuracy: 1
time_ms: 430
memory_kb: 4220
final_rank: 3

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using P = pair<ll, ll>;
#define rep(i, a, b) for(ll i = a; i < b; ++i)
#define rrpe(i, a, b) for(ll i = a; i >= b; --i)
constexpr ll inf = 4e18;
using Real = long double;
const Real EPS = 1e-8, PI = acos(Real(-1.0));
int sign(const Real& r) {
    if(r <= -EPS) return -1;
    if(r >= +EPS) return +1;
    return 0;
}
bool eq(const Real& a, const Real& b) {
    return sign(a - b) == 0;
}
using Point = complex<Real>;
istream& operator>>(istream& is, Point& p) {
    Real a, b;
    is >> a >> b;
    p = Point(a, b);
    return is;
}
ostream& operator<<(ostream& os, const Point& p) {
    return os << p.real() << ' ' << p.imag();
}
Real dot(const Point& p1, const Point& p2) {
    return (conj(p1) * p2).real();
}
Real cross(const Point& p1, const Point& p2) {
    return (conj(p1) * p2).imag();
}
bool comp_x(const Point& p1, const Point& p2) {
    return eq(p1.real(), p2.real()) ? p1.imag() < p2.imag() : p1.real() < p2.real();
}
int ccw(const Point& a, Point b, Point c) {
    b -= a;
    c -= a;
    if(sign(cross(b, c)) == 1) return 1;
    if(sign(cross(b, c)) == -1) return -1;
    if(sign(dot(b, c)) == -1) return 2;
    if(norm(b) < norm(c)) return -2;
    return 0;
}
struct Line {
    Point a, b;
    Line() = default;
    Line(const Point& a, const Point& b)
        : a(a), b(b) {}
};
using Segment = Line;
Point projection(const Line& l, const Point& p) {
    Real t = dot(p - l.a, l.b - l.a) / norm(l.b - l.a);
    return l.a + (l.b - l.a) * t;
}
bool is_intersect_sp(const Segment& s, const Point& p) {
    return ccw(s.a, s.b, p) == 0;
}
Real dist_sp(const Segment& s, const Point& p) {
    Point h = projection(s, p);
    if(is_intersect_sp(s, h)) return abs(h - p);
    return min(abs(s.a - p), abs(s.b - p));
}
vector<Point> convex_hull(vector<Point> ps) {
    sort(ps.begin(), ps.end(), comp_x);
    ps.erase(unique(ps.begin(), ps.end()), ps.end());
    int n = ps.size(), k = 0;
    if(n == 1) return ps;
    vector<Point> ch(2 * n);
    for(int i = 0; i < n; ch[k++] = ps[i++]) {
        while(k >= 2 and sign(cross(ch[k - 1] - ch[k - 2], ps[i] - ch[k - 1])) == -1) {
            --k;
        }
    }
    for(int i = n - 2, t = k + 1; i >= 0; ch[k++] = ps[i--]) {
        while(k >= t and sign(cross(ch[k - 1] - ch[k - 2], ps[i] - ch[k - 1])) == -1) {
            --k;
        }
    }
    ch.resize(k - 1);
    return ch;
}
int in_polygon(const vector<Point>& polygon, const Point& p) {
    int n = polygon.size();
    int ret = 0;
    rep(i, 0, n) {
        Point a = polygon[i] - p, b = polygon[(i + 1) % n] - p;
        if(eq(cross(a, b), 0.0) and sign(dot(a, b)) <= 0) return 1;
        if(a.imag() > b.imag()) swap(a, b);
        if(sign(a.imag()) <= 0 and sign(b.imag()) == 1 and sign(cross(a, b)) == 1) ret ^= 2;
    }
    return ret;
}
int main(void) {
    cin.tie(0);
    ios::sync_with_stdio(0);
    cout << fixed << setprecision(30);
    while(1) {
        int n;
        cin >> n;
        if(n == 0) break;
        vector<int> nv(n), h(n);
        vector<vector<Point>> ps(n);
        Real theta, phi;
        Point s, t;
        rep(i, 0, n) {
            cin >> nv[i] >> h[i];
            ps[i].resize(nv[i] * 2);
            rep(j, 0, nv[i]) {
                cin >> ps[i][j];
            }
        }
        cin >> theta >> phi;
        theta = theta / 180 * PI;
        phi = phi / 180 * PI;
        cin >> s >> t;
        vector<vector<Point>> conv(n);
        rep(i, 0, n) {
            rep(j, 0, nv[i]) {
                ps[i][nv[i] + j] = {ps[i][j].real() - cosl(theta) / tanl(phi) * h[i], ps[i][j].imag() - sinl(theta) / tanl(phi) * h[i]};
            }
            conv[i] = convex_hull(ps[i]);
        }
        vector<vector<pair<int, Real>>> g(n + 2);
        rep(i, 0, n) {
            rep(j, 0, i) {
                Real dist = inf;
                rep(k, 0, (int)conv[i].size()) {
                    if(in_polygon(conv[j], conv[i][k])) {
                        dist = 0.0;
                    }
                    rep(l, 0, (int)conv[j].size()) {
                        Point p1 = conv[j][l], p2 = conv[j][(l + 1) % conv[j].size()];
                        Segment seg = Segment(p1, p2);
                        dist = min(dist, dist_sp(seg, conv[i][k]));
                    }
                }
                rep(k, 0, (int)conv[j].size()) {
                    if(in_polygon(conv[i], conv[j][k])) {
                        dist = 0.0;
                    }
                    rep(l, 0, (int)conv[i].size()) {
                        Point p1 = conv[i][l], p2 = conv[i][(l + 1) % conv[i].size()];
                        Segment seg = Segment(p1, p2);
                        dist = min(dist, dist_sp(seg, conv[j][k]));
                    }
                }
                g[i].push_back({j, dist});
                g[j].push_back({i, dist});
            }
        }
        rep(i, 0, n) {
            if(in_polygon(conv[i], s)) {
                g[n].push_back({i, 0.0});
                g[i].push_back({n, 0.0});
            } else {
                Real dist = inf;
                rep(j, 0, (int)conv[i].size()) {
                    Point p1 = conv[i][j], p2 = conv[i][(j + 1) % conv[i].size()];
                    Segment seg = Segment(p1, p2);
                    dist = min(dist, dist_sp(seg, s));
                }
                g[n].push_back({i, dist});
                g[i].push_back({n, dist});
            }
            if(in_polygon(conv[i], t)) {
                g[n + 1].push_back({i, 0.0});
                g[i].push_back({n + 1, 0.0});
            } else {
                Real dist = inf;
                rep(j, 0, (int)conv[i].size()) {
                    Point p1 = conv[i][j], p2 = conv[i][(j + 1) % conv[i].size()];
                    Segment seg = Segment(p1, p2);
                    dist = min(dist, dist_sp(seg, t));
                }
                g[n + 1].push_back({i, dist});
                g[i].push_back({n + 1, dist});
            }
        }
        vector<Real> d(n + 2, inf);
        priority_queue<pair<Real, int>, vector<pair<Real, int>>, greater<pair<Real, int>>> pq;
        d[n] = 0;
        pq.push({0, n});
        while(!pq.empty()) {
            auto [dist, cur] = pq.top();
            pq.pop();
            if(d[cur] < dist) continue;
            for(const auto& [to, cost] : g[cur]) {
                if(d[to] > d[cur] + cost) {
                    d[to] = d[cur] + cost;
                    pq.push({d[to], to});
                }
            }
        }
        cout << min(d[n + 1], abs(s - t)) << '\n';
    }
}
accuracy: 1
time_ms: 2480
memory_kb: 4548
final_rank: 18

code_snippet: #include <bits/stdc++.h>
using namespace std;

#ifdef _RUTHEN
#include <debug.hpp>
#else
#define show(...) true
#endif

using ll = long long;
#define REP(i, n) for (int i = 0; i < (n); i++)
template <class T> using V = vector<T>;

template <typename T> struct Point {
    static T EPS;
    static constexpr T PI = 3.1415926535'8979323846'2643383279L;
    static void set_eps(const T &e) { EPS = e; }
    T x, y;
    Point(const T x = T(0), const T y = T(0)) : x(x), y(y) {}
    Point &operator+=(const Point &p) {
        x += p.x;
        y += p.y;
        return *this;
    }
    Point &operator-=(const Point &p) {
        x -= p.x;
        y -= p.y;
        return *this;
    }
    Point &operator*=(const Point &p) { return *this = Point(x * p.x - y * p.y, x * p.y + y * p.x); }
    Point &operator*=(const T &k) {
        x *= k;
        y *= k;
        return *this;
    }
    Point &operator/=(const Point &p) { return *this = Point(x * p.x + y * p.y, -x * p.y + y * p.x) / (p.x * p.x + p.y * p.y); }
    Point &operator/=(const T &k) {
        x /= k;
        y /= k;
        return *this;
    }

    Point operator+() const { return *this; }
    Point operator-() const { return Point(-x, -y); }

    friend Point operator+(const Point &a, const Point &b) { return Point(a) += b; }
    friend Point operator-(const Point &a, const Point &b) { return Point(a) -= b; }
    friend Point operator*(const Point &a, const Point &b) { return Point(a) *= b; }
    friend Point operator*(const Point &p, const T &k) { return Point(p) *= k; }
    friend Point operator/(const Point &a, const Point &b) { return Point(a) /= b; }
    friend Point operator/(const Point &p, const T &k) { return Point(p) /= k; }
    friend bool operator<(const Point &a, const Point &b) { return a.x == b.x ? a.y < b.y : a.x < b.x; }
    friend bool operator>(const Point &a, const Point &b) { return a.x == b.x ? a.y > b.y : a.x > b.x; }
    friend std::istream &operator>>(std::istream &is, Point &p) { return is >> p.x >> p.y; }
    friend std::ostream &operator<<(std::ostream &os, const Point &p) { return os << '(' << p.x << ' ' << p.y << ')'; }
};

template <typename T> inline int sign(const T &x) { return x < -Point<T>::EPS ? -1 : (x > Point<T>::EPS ? 1 : 0); }
template <typename T> inline bool equal(const T &a, const T &b) { return sign(a - b) == 0; }
template <typename T> inline T radian_to_degree(const T &r) { return r * 180.0 / Point<T>::PI; }
template <typename T> inline T degree_to_radian(const T &d) { return d * Point<T>::PI / 180.0; }

constexpr int IN = 2;
constexpr int ON = 1;
constexpr int OUT = 0;

template <typename T> inline bool equal(const Point<T> &a, const Point<T> &b) { return equal(a.x, b.x) and equal(a.y, b.y); }
template <typename T> inline T dot(const Point<T> &a, const Point<T> &b) { return a.x * b.x + a.y * b.y; }
template <typename T> inline T cross(const Point<T> &a, const Point<T> &b) { return a.x * b.y - a.y * b.x; }
template <typename T> inline Point<T> rotate(const Point<T> &p, const T &theta) { return p * Point<T>(std::cos(theta), std::sin(theta)); }
template <typename T> inline bool compare_x(const Point<T> &a, const Point<T> &b) { return equal(a.x, b.x) ? sign(a.y - b.y) < 0 : sign(a.x - b.x) < 0; }
template <typename T> inline bool compare_y(const Point<T> &a, const Point<T> &b) { return equal(a.y, b.y) ? sign(a.x - b.x) < 0 : sign(a.y - b.y) < 0; }
template <typename T> inline bool compare_arg(const Point<T> &a, const Point<T> &b) {
    assert(!equal(a, Point<T>(0, 0)));
    assert(!equal(b, Point<T>(0, 0)));
    if ((Point<T>(0, 0) < Point<T>(a.y, a.x)) == (Point<T>(0, 0) < Point<T>(b.y, b.x))) {
        return (a.x * b.y == a.y * b.x) ? norm(a) < norm(b) : a.x * b.y > a.y * b.x;
    } else {
        return Point<T>(a.y, a.x) > Point<T>(b.y, b.x);
    }
}
template <typename T> inline T norm(const Point<T> &p) { return p.x * p.x + p.y * p.y; }
template <typename T> inline T abs(const Point<T> &p) { return std::sqrt(norm(p)); }
template <typename T> inline T arg(const Point<T> &p) { return std::atan2(p.y, p.x); }
template <typename T> inline Point<T> polar(const T &rho, const T &theta = T(0)) { return rotate(Point<T>(rho, 0), theta); }
template <> double Point<double>::EPS = 1e-9;
template <> long double Point<long double>::EPS = 1e-12;
template <> long long Point<long long>::EPS = 0;
template <> __int128_t Point<__int128_t>::EPS = 0;

template <typename T> struct Line {
    Point<T> a, b;

    Line() = default;

    Line(const Point<T> &a, const Point<T> &b) : a(a), b(b) {}

    Line(const T &A, const T &B, const T &C) {
        assert(equal(A, 0) and equal(B, 0));
        if (equal(A, 0)) {
            a = Point<T>(0, C / B), b = Point<T>(1, C / B);
        } else if (equal(B, 0)) {
            a = Point<T>(C / A, 0), b = Point<T>(C / A, 1);
        } else if (equal(C, 0)) {
            a = Point<T>(0, 0), b = Point<T>(1, B / A);
        } else {
            a = Point<T>(0, C / B), b = Point<T>(C / A, 0);
        }
    }

    friend std::istream &operator>>(std::istream &is, Line &p) { return is >> p.a >> p.b; }
    friend std::ostream &operator<<(std::ostream &os, const Line &p) { return os << p.a << "->" << p.b; }
};

template <typename T> struct Segment : Line<T> {
    Segment() = default;

    Segment(const Point<T> &a, const Point<T> &b) : Line<T>(a, b) {}
};

constexpr int COUNTER_CLOCKWISE = 1;  // a-b-c counter clockwise
constexpr int CLOCKWISE = -1;         // a-b-c clockwise
constexpr int ONLINE_BACK = 2;        // c-a-b line
constexpr int ONLINE_FRONT = -2;      // a-b-c line
constexpr int ON_SEGMENT = 0;         // a-c-b line
template <typename T> int ccw(const Point<T> &a, Point<T> b, Point<T> c) {
    b = b - a, c = c - a;
    if (sign(cross(b, c)) == 1) return COUNTER_CLOCKWISE;
    if (sign(cross(b, c)) == -1) return CLOCKWISE;
    if (sign(dot(b, c)) == -1) return ONLINE_BACK;
    if (norm(b) < norm(c)) return ONLINE_FRONT;
    return ON_SEGMENT;
}

template <typename T> inline bool is_intersect_ss(const Segment<T> &s1, const Segment<T> &s2) { return (ccw(s1.a, s1.b, s2.a) * ccw(s1.a, s1.b, s2.b) <= 0 and ccw(s2.a, s2.b, s1.a) * ccw(s2.a, s2.b, s1.b) <= 0); }

template <typename T> Point<T> projection(const Line<T> &l, const Point<T> &p) {
    T t = dot(p - l.a, l.b - l.a) / norm(l.b - l.a);
    return l.a + t * (l.b - l.a);
}

template <typename T> inline bool is_intersect_sp(const Segment<T> &s, const Point<T> &p) { return ccw(s.a, s.b, p) == ON_SEGMENT or sign(norm(s.a - p)) == 0 or sign(norm(s.b - p)) == 0; }

template <typename T> T distance_sp(const Segment<T> &s, const Point<T> &p) {
    Point<T> r = projection(s, p);
    if (is_intersect_sp(s, r)) {
        return abs(r - p);
    }
    return std::min(abs(s.a - p), abs(s.b - p));
}

template <typename T> T distance_ss(const Segment<T> &s1, const Segment<T> &s2) {
    if (is_intersect_ss(s1, s2)) return T(0);
    return std::min({distance_sp(s1, s2.a), distance_sp(s1, s2.b), distance_sp(s2, s1.a), distance_sp(s2, s1.b)});
}

template <typename T> using Polygon = std::vector<Point<T>>;
template <typename T> std::istream &operator>>(std::istream &is, Polygon<T> &p) {
    for (auto &&pi : p) is >> pi;
    return is;
}
template <typename T> std::ostream &operator<<(std::ostream &os, const Polygon<T> &p) {
    for (auto &&pi : p) os << pi << " -> ";
    return os;
}

template <typename T> int polygon_contain(const Polygon<T> &q, const Point<T> &p) {
    bool x = false;
    int n = (int)q.size();
    for (int i = 0; i < n - 1; i++) {
        if (is_intersect_sp(Segment(q[i], q[i + 1]), p)) return ON;
        Point a = q[i] - p, b = q[i + 1] - p;
        if (a.y > b.y) std::swap(a, b);
        // a.y < b.y
        // check each point's y is 0 at most 1 times
        if (sign(a.y) <= 0 and sign(b.y) > 0 and sign(cross(a, b)) > 0) x = !x;
    }
    {
        if (is_intersect_sp(Segment(q[n - 1], q[0]), p)) return ON;
        Point a = q[n - 1] - p, b = q[0] - p;
        if (a.y > b.y) std::swap(a, b);
        if (sign(a.y) <= 0 and sign(b.y) > 0 and sign(cross(a, b)) > 0) x = !x;
    }
    return (x ? IN : OUT);
}

template <typename T> Polygon<T> monotone_chain(std::vector<Point<T>> &p, bool strict = true) {
    int n = (int)p.size();
    if (n <= 2) return p;
    std::sort(p.begin(), p.end(), compare_x<T>);
    Polygon<T> r;
    r.reserve(n * 2);
    if (strict) {
        for (int i = 0; i < n; i++) {
            while (r.size() >= 2 and ccw(r[r.size() - 2], r[r.size() - 1], p[i]) != CLOCKWISE) {
                r.pop_back();
            }
            r.push_back(p[i]);
        }
        int t = r.size() + 1;
        for (int i = n - 2; i >= 0; i--) {
            while (r.size() >= t and ccw(r[r.size() - 2], r[r.size() - 1], p[i]) != CLOCKWISE) {
                r.pop_back();
            }
            r.push_back(p[i]);
        }
    } else {
        for (int i = 0; i < n; i++) {
            while (r.size() >= 2 and ccw(r[r.size() - 2], r[r.size() - 1], p[i]) == COUNTER_CLOCKWISE) {
                r.pop_back();
            }
            r.push_back(p[i]);
        }
        int t = r.size() + 1;
        for (int i = n - 2; i >= 0; i--) {
            while (r.size() >= t and ccw(r[r.size() - 2], r[r.size() - 1], p[i]) == COUNTER_CLOCKWISE) {
                r.pop_back();
            }
            r.push_back(p[i]);
        }
    }
    r.pop_back();
    std::reverse(r.begin(), r.end());
    return r;
}

void solve(int N) {
    using D = double;
    using Pt = Point<D>;
    using Seg = Segment<D>;
    using Poly = Polygon<D>;
    // input
    vector<Poly> polys(N);
    vector<D> H(N);
    REP(i, N) {
        int NV;
        cin >> NV >> H[i];
        polys[i].resize(NV);
        cin >> polys[i];
    }
    D theta, phi;
    Pt S, T;
    cin >> theta >> phi >> S >> T;
    vector<Poly> convex(N);
    REP(i, N) {
        Poly cur = polys[i];
        int NV = int(cur.size());
        Pt vec = rotate(Pt(H[i] / tan(degree_to_radian(phi)), 0), degree_to_radian(theta + 180.0));
        REP(j, NV) cur.push_back(cur[j] + vec);
        convex[i] = monotone_chain(cur);
    }
    int N2 = N + 2;
    D INF = 1 << 30;
    vector dp(N2, vector(N2, INF));
    REP(i, N2) dp[i][i] = 0;
    REP(i, N) REP(j, i) {
        bool isCross = false;
        int si = int(convex[i].size()), sj = int(convex[j].size());
        REP(k, si) {
            if (polygon_contain(convex[j], convex[i][k]) != 0) {
                isCross = true;
            }
            if (isCross) break;
        }
        REP(k, sj) {
            if (polygon_contain(convex[i], convex[j][k]) != 0) {
                isCross = true;
            }
            if (isCross) break;
        }
        if (isCross) {
            dp[i][j] = dp[j][i] = 0;
            continue;
        }
        REP(k, si) {
            REP(l, sj) {
                D dist = distance_ss(Seg(convex[i][k], convex[i][(k + 1) % si]), Seg(convex[j][l], convex[j][(l + 1) % sj]));
                dp[i][j] = min(dp[i][j], dist);
                dp[j][i] = min(dp[j][i], dist);
            }
        }
    }
    int s = N, t = N + 1;
    REP(i, N) {
        int si = int(convex[i].size());
        if (polygon_contain(convex[i], S) != 0) {
            dp[s][i] = dp[i][s] = 0;
        }
        if (polygon_contain(convex[i], T) != 0) {
            dp[t][i] = dp[i][t] = 0;
        }
        REP(j, si) {
            {
                D dist = distance_sp(Seg(convex[i][j], convex[i][(j + 1) % si]), S);
                dp[s][i] = min(dp[s][i], dist);
                dp[i][s] = min(dp[i][s], dist);
            }
            {
                D dist = distance_sp(Seg(convex[i][j], convex[i][(j + 1) % si]), T);
                dp[t][i] = min(dp[t][i], dist);
                dp[i][t] = min(dp[i][t], dist);
            }
        }
    }
    dp[s][t] = dp[t][s] = abs(S - T);
    REP(k, N2) REP(i, N2) REP(j, N2) dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);
    cout << fixed << setprecision(15) << dp[s][t] << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int N;
    while (cin >> N, N != 0) solve(N);
    return 0;
}
accuracy: 1
time_ms: 480
memory_kb: 4248
final_rank: 5

code_snippet: #include <bits/stdc++.h>
using namespace std;

#ifdef _RUTHEN
#include <debug.hpp>
#else
#define show(...) true
#endif

using ll = long long;
#define REP(i, n) for (int i = 0; i < (n); i++)
template <class T> using V = vector<T>;

// point
template <typename T> struct Point {
    static T EPS;
    static constexpr T PI = 3.1415926535'8979323846'2643383279L;
    static void set_eps(const T &e) { EPS = e; }
    T x, y;
    Point(const T x = T(0), const T y = T(0)) : x(x), y(y) {}
    Point &operator+=(const Point &p) {
        x += p.x;
        y += p.y;
        return *this;
    }
    Point &operator-=(const Point &p) {
        x -= p.x;
        y -= p.y;
        return *this;
    }
    Point &operator*=(const Point &p) { return *this = Point(x * p.x - y * p.y, x * p.y + y * p.x); }
    Point &operator*=(const T &k) {
        x *= k;
        y *= k;
        return *this;
    }
    Point &operator/=(const Point &p) { return *this = Point(x * p.x + y * p.y, -x * p.y + y * p.x) / (p.x * p.x + p.y * p.y); }
    Point &operator/=(const T &k) {
        x /= k;
        y /= k;
        return *this;
    }

    Point operator+() const { return *this; }
    Point operator-() const { return Point(-x, -y); }

    friend Point operator+(const Point &a, const Point &b) { return Point(a) += b; }
    friend Point operator-(const Point &a, const Point &b) { return Point(a) -= b; }
    friend Point operator*(const Point &a, const Point &b) { return Point(a) *= b; }
    friend Point operator*(const Point &p, const T &k) { return Point(p) *= k; }
    friend Point operator/(const Point &a, const Point &b) { return Point(a) /= b; }
    friend Point operator/(const Point &p, const T &k) { return Point(p) /= k; }
    // for std::set, std::map, compare_arg, ...
    friend bool operator<(const Point &a, const Point &b) { return a.x == b.x ? a.y < b.y : a.x < b.x; }
    friend bool operator>(const Point &a, const Point &b) { return a.x == b.x ? a.y > b.y : a.x > b.x; }
    // I/O
    friend std::istream &operator>>(std::istream &is, Point &p) { return is >> p.x >> p.y; }
    friend std::ostream &operator<<(std::ostream &os, const Point &p) { return os << '(' << p.x << ' ' << p.y << ')'; }
};

// template
template <typename T> inline int sign(const T &x) { return x < -Point<T>::EPS ? -1 : (x > Point<T>::EPS ? 1 : 0); }
template <typename T> inline bool equal(const T &a, const T &b) { return sign(a - b) == 0; }
template <typename T> inline T radian_to_degree(const T &r) { return r * 180.0 / Point<T>::PI; }
template <typename T> inline T degree_to_radian(const T &d) { return d * Point<T>::PI / 180.0; }

// contain enum
constexpr int IN = 2;
constexpr int ON = 1;
constexpr int OUT = 0;

// equal (point and point)
template <typename T> inline bool equal(const Point<T> &a, const Point<T> &b) { return equal(a.x, b.x) and equal(a.y, b.y); }
// inner product
template <typename T> inline T dot(const Point<T> &a, const Point<T> &b) { return a.x * b.x + a.y * b.y; }
// outer product
template <typename T> inline T cross(const Point<T> &a, const Point<T> &b) { return a.x * b.y - a.y * b.x; }
// rotate Point p counterclockwise by theta radian
template <typename T> inline Point<T> rotate(const Point<T> &p, const T &theta) { return p * Point<T>(std::cos(theta), std::sin(theta)); }
// compare (x, y)
template <typename T> inline bool compare_x(const Point<T> &a, const Point<T> &b) { return equal(a.x, b.x) ? sign(a.y - b.y) < 0 : sign(a.x - b.x) < 0; }
// compare (y, x)
template <typename T> inline bool compare_y(const Point<T> &a, const Point<T> &b) { return equal(a.y, b.y) ? sign(a.x - b.x) < 0 : sign(a.y - b.y) < 0; }
// compare by (arg(p), norm(p)) [0, 360)
template <typename T> inline bool compare_arg(const Point<T> &a, const Point<T> &b) {
    // https://ngtkana.hatenablog.com/entry/2021/11/13/202103
    assert(!equal(a, Point<T>(0, 0)));
    assert(!equal(b, Point<T>(0, 0)));
    if ((Point<T>(0, 0) < Point<T>(a.y, a.x)) == (Point<T>(0, 0) < Point<T>(b.y, b.x))) {
        return (a.x * b.y == a.y * b.x) ? norm(a) < norm(b) : a.x * b.y > a.y * b.x;
    } else {
        return Point<T>(a.y, a.x) > Point<T>(b.y, b.x);
    }
}
// |p| ^ 2
template <typename T> inline T norm(const Point<T> &p) { return p.x * p.x + p.y * p.y; }
// |p|
template <typename T> inline T abs(const Point<T> &p) { return std::sqrt(norm(p)); }
// arg
template <typename T> inline T arg(const Point<T> &p) { return std::atan2(p.y, p.x); }
// polar
template <typename T> inline Point<T> polar(const T &rho, const T &theta = T(0)) { return rotate(Point<T>(rho, 0), theta); }
// EPS
template <> double Point<double>::EPS = 1e-9;
template <> long double Point<long double>::EPS = 1e-12;
template <> long long Point<long long>::EPS = 0;
template <> __int128_t Point<__int128_t>::EPS = 0;
// change EPS
// using Double = double;
// using Pt = Point<Double>;
// Point<Double>::set_eps(new_eps);

// line
template <typename T> struct Line {
    Point<T> a, b;

    Line() = default;

    Line(const Point<T> &a, const Point<T> &b) : a(a), b(b) {}

    // Ax + By = C
    Line(const T &A, const T &B, const T &C) {
        assert(equal(A, 0) and equal(B, 0));
        if (equal(A, 0)) {
            a = Point<T>(0, C / B), b = Point<T>(1, C / B);
        } else if (equal(B, 0)) {
            a = Point<T>(C / A, 0), b = Point<T>(C / A, 1);
        } else if (equal(C, 0)) {
            a = Point<T>(0, 0), b = Point<T>(1, B / A);
        } else {
            a = Point<T>(0, C / B), b = Point<T>(C / A, 0);
        }
    }

    friend std::istream &operator>>(std::istream &is, Line &p) { return is >> p.a >> p.b; }
    friend std::ostream &operator<<(std::ostream &os, const Line &p) { return os << p.a << "->" << p.b; }
};

// segment
template <typename T> struct Segment : Line<T> {
    Segment() = default;

    Segment(const Point<T> &a, const Point<T> &b) : Line<T>(a, b) {}
};

// counter clockwise
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_C
constexpr int COUNTER_CLOCKWISE = 1;  // a-b-c counter clockwise
constexpr int CLOCKWISE = -1;         // a-b-c clockwise
constexpr int ONLINE_BACK = 2;        // c-a-b line
constexpr int ONLINE_FRONT = -2;      // a-b-c line
constexpr int ON_SEGMENT = 0;         // a-c-b line
template <typename T> int ccw(const Point<T> &a, Point<T> b, Point<T> c) {
    b = b - a, c = c - a;
    if (sign(cross(b, c)) == 1) return COUNTER_CLOCKWISE;
    if (sign(cross(b, c)) == -1) return CLOCKWISE;
    if (sign(dot(b, c)) == -1) return ONLINE_BACK;
    if (norm(b) < norm(c)) return ONLINE_FRONT;
    return ON_SEGMENT;
}

// intersection (segment and segment)
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_B
template <typename T> inline bool is_intersect_ss(const Segment<T> &s1, const Segment<T> &s2) { return (ccw(s1.a, s1.b, s2.a) * ccw(s1.a, s1.b, s2.b) <= 0 and ccw(s2.a, s2.b, s1.a) * ccw(s2.a, s2.b, s1.b) <= 0); }

// projection
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_A
template <typename T> Point<T> projection(const Line<T> &l, const Point<T> &p) {
    T t = dot(p - l.a, l.b - l.a) / norm(l.b - l.a);
    return l.a + t * (l.b - l.a);
}

// intersection (segment and point)
// ccw(a, b, c) == ON_SEGMENT -> a - c - b
template <typename T> inline bool is_intersect_sp(const Segment<T> &s, const Point<T> &p) { return ccw(s.a, s.b, p) == ON_SEGMENT or sign(norm(s.a - p)) == 0 or sign(norm(s.b - p)) == 0; }

// distance (segment and point)
template <typename T> T distance_sp(const Segment<T> &s, const Point<T> &p) {
    Point<T> r = projection(s, p);
    if (is_intersect_sp(s, r)) {
        return abs(r - p);
    }
    return std::min(abs(s.a - p), abs(s.b - p));
}

// distance (segment and segment)
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_D
template <typename T> T distance_ss(const Segment<T> &s1, const Segment<T> &s2) {
    if (is_intersect_ss(s1, s2)) return T(0);
    return std::min({distance_sp(s1, s2.a), distance_sp(s1, s2.b), distance_sp(s2, s1.a), distance_sp(s2, s1.b)});
}

// polygon
template <typename T> using Polygon = std::vector<Point<T>>;
template <typename T> std::istream &operator>>(std::istream &is, Polygon<T> &p) {
    for (auto &&pi : p) is >> pi;
    return is;
}
template <typename T> std::ostream &operator<<(std::ostream &os, const Polygon<T> &p) {
    for (auto &&pi : p) os << pi << " -> ";
    return os;
}

// polygon contain point -> 2 (IN)
// polygon cross point -> 1 (ON)
// otherwise -> 0 (OUT)
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_3_C
template <typename T> int polygon_contain(const Polygon<T> &q, const Point<T> &p) {
    bool x = false;
    int n = (int)q.size();
    for (int i = 0; i < n - 1; i++) {
        if (is_intersect_sp(Segment(q[i], q[i + 1]), p)) return ON;
        Point a = q[i] - p, b = q[i + 1] - p;
        if (a.y > b.y) std::swap(a, b);
        // a.y < b.y
        // check each point's y is 0 at most 1 times
        if (sign(a.y) <= 0 and sign(b.y) > 0 and sign(cross(a, b)) > 0) x = !x;
    }
    {
        if (is_intersect_sp(Segment(q[n - 1], q[0]), p)) return ON;
        Point a = q[n - 1] - p, b = q[0] - p;
        if (a.y > b.y) std::swap(a, b);
        if (sign(a.y) <= 0 and sign(b.y) > 0 and sign(cross(a, b)) > 0) x = !x;
    }
    return (x ? IN : OUT);
}

// convex hull (Andrew's monotone chain convex hull algorithm)
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_4_A
// sort (x, y) by lexicographical order, use stack, calculate upper convex hull and lower convex hull
// counter clockwise order
// assume the return value of ccw is not ONLINE_BACK or ONLINE_FRONT (lexicographical order)
// strict is true : points on the edges of the convex hull are not included (the number of points is minimized)
// complexity: O(n \log n) (n: the number of points)
template <typename T> Polygon<T> monotone_chain(std::vector<Point<T>> &p, bool strict = true) {
    int n = (int)p.size();
    if (n <= 2) return p;
    std::sort(p.begin(), p.end(), compare_x<T>);
    Polygon<T> r;
    r.reserve(n * 2);
    if (strict) {
        for (int i = 0; i < n; i++) {
            while (r.size() >= 2 and ccw(r[r.size() - 2], r[r.size() - 1], p[i]) != CLOCKWISE) {
                r.pop_back();
            }
            r.push_back(p[i]);
        }
        int t = r.size() + 1;
        for (int i = n - 2; i >= 0; i--) {
            while (r.size() >= t and ccw(r[r.size() - 2], r[r.size() - 1], p[i]) != CLOCKWISE) {
                r.pop_back();
            }
            r.push_back(p[i]);
        }
    } else {
        for (int i = 0; i < n; i++) {
            while (r.size() >= 2 and ccw(r[r.size() - 2], r[r.size() - 1], p[i]) == COUNTER_CLOCKWISE) {
                r.pop_back();
            }
            r.push_back(p[i]);
        }
        int t = r.size() + 1;
        for (int i = n - 2; i >= 0; i--) {
            while (r.size() >= t and ccw(r[r.size() - 2], r[r.size() - 1], p[i]) == COUNTER_CLOCKWISE) {
                r.pop_back();
            }
            r.push_back(p[i]);
        }
    }
    r.pop_back();
    std::reverse(r.begin(), r.end());
    return r;
}

void solve(int N) {
    using D = double;
    using Pt = Point<D>;
    using Seg = Segment<D>;
    using Poly = Polygon<D>;
    // input
    vector<Poly> polys(N);
    vector<D> H(N);
    REP(i, N) {
        int NV;
        cin >> NV >> H[i];
        polys[i].resize(NV);
        cin >> polys[i];
    }
    D theta, phi;
    Pt S, T;
    cin >> theta >> phi >> S >> T;
    vector<Poly> convex(N);
    REP(i, N) {
        Poly cur = polys[i];
        int NV = int(cur.size());
        Pt vec = rotate(Pt(H[i] / tan(degree_to_radian(phi)), 0), degree_to_radian(theta + 180.0));
        REP(j, NV) cur.push_back(cur[j] + vec);
        convex[i] = monotone_chain(cur);
    }
    int N2 = N + 2;
    D INF = 1 << 30;
    vector dp(N2, vector(N2, INF));
    REP(i, N2) dp[i][i] = 0;
    REP(i, N) REP(j, i) {
        bool isCross = false;
        int si = int(convex[i].size()), sj = int(convex[j].size());
        REP(k, si) {
            if (polygon_contain(convex[j], convex[i][k]) != 0) {
                isCross = true;
            }
            if (isCross) break;
        }
        REP(k, sj) {
            if (polygon_contain(convex[i], convex[j][k]) != 0) {
                isCross = true;
            }
            if (isCross) break;
        }
        if (isCross) {
            dp[i][j] = dp[j][i] = 0;
            continue;
        }
        REP(k, si) {
            REP(l, sj) {
                D dist = distance_ss(Seg(convex[i][k], convex[i][(k + 1) % si]), Seg(convex[j][l], convex[j][(l + 1) % sj]));
                dp[i][j] = min(dp[i][j], dist);
                dp[j][i] = min(dp[j][i], dist);
            }
        }
    }
    int s = N, t = N + 1;
    REP(i, N) {
        int si = int(convex[i].size());
        if (polygon_contain(convex[i], S) != 0) {
            dp[s][i] = dp[i][s] = 0;
        }
        if (polygon_contain(convex[i], T) != 0) {
            dp[t][i] = dp[i][t] = 0;
        }
        REP(j, si) {
            {
                D dist = distance_sp(Seg(convex[i][j], convex[i][(j + 1) % si]), S);
                dp[s][i] = min(dp[s][i], dist);
                dp[i][s] = min(dp[i][s], dist);
            }
            {
                D dist = distance_sp(Seg(convex[i][j], convex[i][(j + 1) % si]), T);
                dp[t][i] = min(dp[t][i], dist);
                dp[i][t] = min(dp[i][t], dist);
            }
        }
    }
    dp[s][t] = dp[t][s] = abs(S - T);
    REP(k, N2) REP(i, N2) REP(j, N2) dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);
    cout << fixed << setprecision(15) << dp[s][t] << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int N;
    while (cin >> N, N != 0) solve(N);
    return 0;
}
accuracy: 1
time_ms: 480
memory_kb: 4176
final_rank: 2

code_snippet: #include <bits/stdc++.h>
using namespace std;

#ifdef _RUTHEN
#include <debug.hpp>
#else
#define show(...) true
#endif

using ll = long long;
#define REP(i, n) for (int i = 0; i < (n); i++)
template <class T> using V = vector<T>;

// point
template <typename T> struct Point {
    static T EPS;
    static constexpr T PI = 3.1415926535'8979323846'2643383279L;
    static void set_eps(const T &e) { EPS = e; }
    T x, y;
    Point(const T x = T(0), const T y = T(0)) : x(x), y(y) {}
    Point &operator+=(const Point &p) {
        x += p.x;
        y += p.y;
        return *this;
    }
    Point &operator-=(const Point &p) {
        x -= p.x;
        y -= p.y;
        return *this;
    }
    Point &operator*=(const Point &p) { return *this = Point(x * p.x - y * p.y, x * p.y + y * p.x); }
    Point &operator*=(const T &k) {
        x *= k;
        y *= k;
        return *this;
    }
    Point &operator/=(const Point &p) { return *this = Point(x * p.x + y * p.y, -x * p.y + y * p.x) / (p.x * p.x + p.y * p.y); }
    Point &operator/=(const T &k) {
        x /= k;
        y /= k;
        return *this;
    }

    Point operator+() const { return *this; }
    Point operator-() const { return Point(-x, -y); }

    friend Point operator+(const Point &a, const Point &b) { return Point(a) += b; }
    friend Point operator-(const Point &a, const Point &b) { return Point(a) -= b; }
    friend Point operator*(const Point &a, const Point &b) { return Point(a) *= b; }
    friend Point operator*(const Point &p, const T &k) { return Point(p) *= k; }
    friend Point operator/(const Point &a, const Point &b) { return Point(a) /= b; }
    friend Point operator/(const Point &p, const T &k) { return Point(p) /= k; }
    // for std::set, std::map, compare_arg, ...
    friend bool operator<(const Point &a, const Point &b) { return a.x == b.x ? a.y < b.y : a.x < b.x; }
    friend bool operator>(const Point &a, const Point &b) { return a.x == b.x ? a.y > b.y : a.x > b.x; }
    // I/O
    friend std::istream &operator>>(std::istream &is, Point &p) { return is >> p.x >> p.y; }
    friend std::ostream &operator<<(std::ostream &os, const Point &p) { return os << '(' << p.x << ' ' << p.y << ')'; }
};

// template
template <typename T> inline int sign(const T &x) { return x < -Point<T>::EPS ? -1 : (x > Point<T>::EPS ? 1 : 0); }
template <typename T> inline bool equal(const T &a, const T &b) { return sign(a - b) == 0; }
template <typename T> inline T radian_to_degree(const T &r) { return r * 180.0 / Point<T>::PI; }
template <typename T> inline T degree_to_radian(const T &d) { return d * Point<T>::PI / 180.0; }

// contain enum
constexpr int IN = 2;
constexpr int ON = 1;
constexpr int OUT = 0;

// equal (point and point)
template <typename T> inline bool equal(const Point<T> &a, const Point<T> &b) { return equal(a.x, b.x) and equal(a.y, b.y); }
// inner product
template <typename T> inline T dot(const Point<T> &a, const Point<T> &b) { return a.x * b.x + a.y * b.y; }
// outer product
template <typename T> inline T cross(const Point<T> &a, const Point<T> &b) { return a.x * b.y - a.y * b.x; }
// rotate Point p counterclockwise by theta radian
template <typename T> inline Point<T> rotate(const Point<T> &p, const T &theta) { return p * Point<T>(std::cos(theta), std::sin(theta)); }
// compare (x, y)
template <typename T> inline bool compare_x(const Point<T> &a, const Point<T> &b) { return equal(a.x, b.x) ? sign(a.y - b.y) < 0 : sign(a.x - b.x) < 0; }
// compare (y, x)
template <typename T> inline bool compare_y(const Point<T> &a, const Point<T> &b) { return equal(a.y, b.y) ? sign(a.x - b.x) < 0 : sign(a.y - b.y) < 0; }
// compare by (arg(p), norm(p)) [0, 360)
template <typename T> inline bool compare_arg(const Point<T> &a, const Point<T> &b) {
    // https://ngtkana.hatenablog.com/entry/2021/11/13/202103
    assert(!equal(a, Point<T>(0, 0)));
    assert(!equal(b, Point<T>(0, 0)));
    if ((Point<T>(0, 0) < Point<T>(a.y, a.x)) == (Point<T>(0, 0) < Point<T>(b.y, b.x))) {
        return (a.x * b.y == a.y * b.x) ? norm(a) < norm(b) : a.x * b.y > a.y * b.x;
    } else {
        return Point<T>(a.y, a.x) > Point<T>(b.y, b.x);
    }
}
// |p| ^ 2
template <typename T> inline T norm(const Point<T> &p) { return p.x * p.x + p.y * p.y; }
// |p|
template <typename T> inline T abs(const Point<T> &p) { return std::sqrt(norm(p)); }
// arg
template <typename T> inline T arg(const Point<T> &p) { return std::atan2(p.y, p.x); }
// polar
template <typename T> inline Point<T> polar(const T &rho, const T &theta = T(0)) { return rotate(Point<T>(rho, 0), theta); }
// EPS
template <> double Point<double>::EPS = 1e-9;
template <> long double Point<long double>::EPS = 1e-12;
template <> long long Point<long long>::EPS = 0;
template <> __int128_t Point<__int128_t>::EPS = 0;
// change EPS
// using Double = double;
// using Pt = Point<Double>;
// Point<Double>::set_eps(new_eps);

// line
template <typename T> struct Line {
    Point<T> a, b;

    Line() = default;

    Line(const Point<T> &a, const Point<T> &b) : a(a), b(b) {}

    // Ax + By = C
    Line(const T &A, const T &B, const T &C) {
        assert(equal(A, 0) and equal(B, 0));
        if (equal(A, 0)) {
            a = Point<T>(0, C / B), b = Point<T>(1, C / B);
        } else if (equal(B, 0)) {
            a = Point<T>(C / A, 0), b = Point<T>(C / A, 1);
        } else if (equal(C, 0)) {
            a = Point<T>(0, 0), b = Point<T>(1, B / A);
        } else {
            a = Point<T>(0, C / B), b = Point<T>(C / A, 0);
        }
    }

    friend std::istream &operator>>(std::istream &is, Line &p) { return is >> p.a >> p.b; }
    friend std::ostream &operator<<(std::ostream &os, const Line &p) { return os << p.a << "->" << p.b; }
};

// segment
template <typename T> struct Segment : Line<T> {
    Segment() = default;

    Segment(const Point<T> &a, const Point<T> &b) : Line<T>(a, b) {}
};

// counter clockwise
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_C
constexpr int COUNTER_CLOCKWISE = 1;  // a-b-c counter clockwise
constexpr int CLOCKWISE = -1;         // a-b-c clockwise
constexpr int ONLINE_BACK = 2;        // c-a-b line
constexpr int ONLINE_FRONT = -2;      // a-b-c line
constexpr int ON_SEGMENT = 0;         // a-c-b line
template <typename T> int ccw(const Point<T> &a, Point<T> b, Point<T> c) {
    b = b - a, c = c - a;
    if (sign(cross(b, c)) == 1) return COUNTER_CLOCKWISE;
    if (sign(cross(b, c)) == -1) return CLOCKWISE;
    if (sign(dot(b, c)) == -1) return ONLINE_BACK;
    if (norm(b) < norm(c)) return ONLINE_FRONT;
    return ON_SEGMENT;
}

// intersection (segment and segment)
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_B
template <typename T> inline bool is_intersect_ss(const Segment<T> &s1, const Segment<T> &s2) { return (ccw(s1.a, s1.b, s2.a) * ccw(s1.a, s1.b, s2.b) <= 0 and ccw(s2.a, s2.b, s1.a) * ccw(s2.a, s2.b, s1.b) <= 0); }

// projection
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_A
template <typename T> Point<T> projection(const Line<T> &l, const Point<T> &p) {
    T t = dot(p - l.a, l.b - l.a) / norm(l.b - l.a);
    return l.a + t * (l.b - l.a);
}

// intersection (segment and point)
// ccw(a, b, c) == ON_SEGMENT -> a - c - b
template <typename T> inline bool is_intersect_sp(const Segment<T> &s, const Point<T> &p) { return ccw(s.a, s.b, p) == ON_SEGMENT or sign(norm(s.a - p)) == 0 or sign(norm(s.b - p)) == 0; }

// distance (segment and point)
template <typename T> T distance_sp(const Segment<T> &s, const Point<T> &p) {
    Point<T> r = projection(s, p);
    if (is_intersect_sp(s, r)) {
        return abs(r - p);
    }
    return std::min(abs(s.a - p), abs(s.b - p));
}

// distance (segment and segment)
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_D
template <typename T> T distance_ss(const Segment<T> &s1, const Segment<T> &s2) {
    if (is_intersect_ss(s1, s2)) return T(0);
    return std::min({distance_sp(s1, s2.a), distance_sp(s1, s2.b), distance_sp(s2, s1.a), distance_sp(s2, s1.b)});
}

// polygon
template <typename T> using Polygon = std::vector<Point<T>>;
template <typename T> std::istream &operator>>(std::istream &is, Polygon<T> &p) {
    for (auto &&pi : p) is >> pi;
    return is;
}
template <typename T> std::ostream &operator<<(std::ostream &os, const Polygon<T> &p) {
    for (auto &&pi : p) os << pi << " -> ";
    return os;
}

// polygon contain point -> 2 (IN)
// polygon cross point -> 1 (ON)
// otherwise -> 0 (OUT)
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_3_C
template <typename T> int polygon_contain(const Polygon<T> &q, const Point<T> &p) {
    bool x = false;
    int n = (int)q.size();
    for (int i = 0; i < n - 1; i++) {
        if (is_intersect_sp(Segment(q[i], q[i + 1]), p)) return ON;
        Point a = q[i] - p, b = q[i + 1] - p;
        if (a.y > b.y) std::swap(a, b);
        // a.y < b.y
        // check each point's y is 0 at most 1 times
        if (sign(a.y) <= 0 and sign(b.y) > 0 and sign(cross(a, b)) > 0) x = !x;
    }
    {
        if (is_intersect_sp(Segment(q[n - 1], q[0]), p)) return ON;
        Point a = q[n - 1] - p, b = q[0] - p;
        if (a.y > b.y) std::swap(a, b);
        if (sign(a.y) <= 0 and sign(b.y) > 0 and sign(cross(a, b)) > 0) x = !x;
    }
    return (x ? IN : OUT);
}

// convex hull (Andrew's monotone chain convex hull algorithm)
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_4_A
// sort (x, y) by lexicographical order, use stack, calculate upper convex hull and lower convex hull
// counter clockwise order
// assume the return value of ccw is not ONLINE_BACK or ONLINE_FRONT (lexicographical order)
// strict is true : points on the edges of the convex hull are not included (the number of points is minimized)
// complexity: O(n \log n) (n: the number of points)
template <typename T> Polygon<T> monotone_chain(std::vector<Point<T>> &p, bool strict = true) {
    int n = (int)p.size();
    if (n <= 2) return p;
    std::sort(p.begin(), p.end(), compare_x<T>);
    Polygon<T> r;
    r.reserve(n * 2);
    if (strict) {
        for (int i = 0; i < n; i++) {
            while (r.size() >= 2 and ccw(r[r.size() - 2], r[r.size() - 1], p[i]) != CLOCKWISE) {
                r.pop_back();
            }
            r.push_back(p[i]);
        }
        int t = r.size() + 1;
        for (int i = n - 2; i >= 0; i--) {
            while (r.size() >= t and ccw(r[r.size() - 2], r[r.size() - 1], p[i]) != CLOCKWISE) {
                r.pop_back();
            }
            r.push_back(p[i]);
        }
    } else {
        for (int i = 0; i < n; i++) {
            while (r.size() >= 2 and ccw(r[r.size() - 2], r[r.size() - 1], p[i]) == COUNTER_CLOCKWISE) {
                r.pop_back();
            }
            r.push_back(p[i]);
        }
        int t = r.size() + 1;
        for (int i = n - 2; i >= 0; i--) {
            while (r.size() >= t and ccw(r[r.size() - 2], r[r.size() - 1], p[i]) == COUNTER_CLOCKWISE) {
                r.pop_back();
            }
            r.push_back(p[i]);
        }
    }
    r.pop_back();
    std::reverse(r.begin(), r.end());
    return r;
}

void solve(int N) {
    using D = double;
    using Pt = Point<D>;
    using Seg = Segment<D>;
    using Poly = Polygon<D>;
    // input
    vector<Poly> polys;
    vector<D> H(N);
    REP(i, N) {
        int NV;
        cin >> NV >> H[i];
        Poly xy(NV);
        REP(j, NV) cin >> xy[j];
        polys.push_back(xy);
    }
    D theta, phi;
    Pt S, T;
    cin >> theta >> phi >> S >> T;
    vector<Poly> convex(N);
    REP(i, N) {
        Poly cur = polys[i];
        int NV = int(cur.size());
        Pt vec = rotate(Pt(H[i] / tan(degree_to_radian(phi)), 0), degree_to_radian(theta + 180.0));
        REP(j, NV) cur.push_back(cur[j] + vec);
        convex[i] = monotone_chain(cur);
    }
    //    show(convex);
    int N2 = N + 2;
    D INF = 1 << 30;
    vector dp(N2, vector(N2, INF));
    REP(i, N2) dp[i][i] = 0;
    REP(i, N) REP(j, i) {
        bool isCross = false;
        int si = int(convex[i].size()), sj = int(convex[j].size());
        REP(k, si) {
            if (polygon_contain(convex[j], convex[i][k]) != 0) {
                isCross = true;
            }
            if (isCross) break;
        }
        REP(k, sj) {
            if (polygon_contain(convex[i], convex[j][k]) != 0) {
                isCross = true;
            }
            if (isCross) break;
        }
        if (isCross) {
            dp[i][j] = dp[j][i] = 0;
            continue;
        }
        REP(k, si) {
            REP(l, sj) {
                D dist = distance_ss(Seg(convex[i][k], convex[i][(k + 1) % si]), Seg(convex[j][l], convex[j][(l + 1) % sj]));
                dp[i][j] = min(dp[i][j], dist);
                dp[j][i] = min(dp[j][i], dist);
            }
        }
    }
    int s = N, t = N + 1;
    REP(i, N) {
        int si = int(convex[i].size());
        if (polygon_contain(convex[i], S) != 0) {
            dp[s][i] = dp[i][s] = 0;
        }
        if (polygon_contain(convex[i], T) != 0) {
            dp[t][i] = dp[i][t] = 0;
        }
        REP(j, si) {
            {
                D dist = distance_sp(Seg(convex[i][j], convex[i][(j + 1) % si]), S);
                dp[s][i] = min(dp[s][i], dist);
                dp[i][s] = min(dp[i][s], dist);
            }
            {
                D dist = distance_sp(Seg(convex[i][j], convex[i][(j + 1) % si]), T);
                dp[t][i] = min(dp[t][i], dist);
                dp[i][t] = min(dp[i][t], dist);
            }
        }
    }
    dp[s][t] = dp[t][s] = abs(S - T);
    REP(k, N2) REP(i, N2) REP(j, N2) dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);
    cout << fixed << setprecision(15) << dp[s][t] << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int N;
    while (cin >> N, N != 0) solve(N);
    return 0;
}
accuracy: 1
time_ms: 480
memory_kb: 4232
final_rank: 4

code_snippet: #include <bits/stdc++.h>

using namespace std;

const double INFD = 1e9;
const double PI = acos(-1);
const double EPS = 1e-6;

bool betweenD(double x, double l, double r) {
    return min(l, r) <= x + EPS && x <= max(l, r) + EPS;
}

bool sameD(double x, double y) {
    return betweenD(x, y, y);
}

struct Point {
    double x, y;

    Point() : x(0), y(0) {}
    Point(double _x, double _y) : x(_x), y(_y) {}

    bool operator<(const Point& other) const {
        return make_pair(x, y) < make_pair(other.x, other.y);
    }

    bool operator==(const Point& other) const {
        return sameD(x, other.x) && sameD(y, other.y);
    }
};

double eDist(const Point& p1, const Point& p2) {
    return hypot(p1.x - p2.x, p1.y - p2.y);
}

double cross(const Point& a, const Point& b, const Point& c) {
    double v1 = a.x - c.x;
    double v2 = a.y - c.y;
    double u1 = b.x - c.x;
    double u2 = b.y - c.y;
    return v1 * u2 - v2 * u1;
}

struct Line {
    double a, b, c;

    Line() : a(0), b(0), c(0) {}
    Line(double _a, double _b, double _c) : a(_a), b(_b), c(_c) {}

    Line(Point p1, Point p2) {
        if (p1 < p2) {
            swap(p1, p2);
        }
        if (sameD(p1.x, p2.x)) {
            a = 1, b = 0, c = -p1.x;
        } else {
            a = -(p1.y - p2.y) / (p1.x - p2.x), b = 1, c = -(a * p1.x) - p1.y;
        }
    }

    Line(const Point& p, double slope) {
        if (sameD(slope, INFD)) {
            a = 1, b = 0, c = -p.x;
        } else {
            a = -slope, b = 1, c = -(a * p.x) - p.y;
        }
    }

    bool operator==(const Line& other) const {
        return sameD(a, other.a) && sameD(b, other.b) && sameD(c, other.c);
    }

    double slope() const {
        if (sameD(b, 0)) {
            return INFD;
        }
        return -(a / b);
    }
};

struct Vec {
    double x, y;

    Vec() : x(0), y(0) {}
    Vec(double _x, double _y) : x(_x), y(_y) {}
    Vec(const Point& a) : x(a.x), y(a.y) {}
    Vec(const Point& a, const Point& b) : x(b.x - a.x), y(b.y - a.y) {}
};

Vec scale(const Vec& v, double s) {
    return Vec(v.x * s, v.y * s);
}

Vec flip(const Vec& v) {
    return Vec(-v.x, -v.y);
}

double dot(const Vec& u, const Vec& v) {
    return (u.x * v.x + u.y * v.y);
}

double cross(const Vec& u, const Vec& v) {
    return (u.x * v.y - u.y * v.x);
}

double normSq(const Vec& v) {
    return (v.x * v.x + v.y * v.y);
}

Point translate(const Point& p, const Vec& v) {
    return Point(p.x + v.x, p.y + v.y);
}

struct Segment {
    Point p, q;
    Line l;

    Segment() : p(), q(), l() {}

    Segment(const Point& _p, const Point& _q) : p(_p), q(_q) {
        if (q < p) {
            swap(p, q);
        }
        l = Line(p, q);
    }

    bool operator==(const Segment& other) const {
        return p == other.p && q == other.q;
    }
};

double pointToLine(const Point& p, const Point& a, const Point& b, Point& c) {
    Vec ap(a, p), ab(a, b);
    double u = dot(ap, ab) / normSq(ab);
    c = translate(a, scale(ab, u));
    return eDist(p, c);
}

int orientation(const Point& p, const Point& q, const Point& r) {
    Vec pq(p, q), pr(p, r);
    double c = cross(pq, pr);
    if (c < -EPS) {
        return -1;
    }
    if (c > EPS) {
        return 1;
    }
    return 0;
}

bool onSegment(const Point& p, const Segment& s) {
    if (orientation(s.p, s.q, p) != 0) {
        return false;
    }
    return betweenD(p.x, s.p.x, s.q.x) && betweenD(p.y, s.p.y, s.q.y);
}

bool sIntersection(const Segment& s1, const Segment& s2) {
    double o1 = orientation(s1.p, s1.q, s2.p);
    double o2 = orientation(s1.p, s1.q, s2.q);
    double o3 = orientation(s2.p, s2.q, s1.p);
    double o4 = orientation(s2.p, s2.q, s1.q);
    if (o1 != o2 && o3 != o4) {
        return true;
    }
    if (o1 == 0 && onSegment(s2.p, s1)) {
        return true;
    }
    if (o2 == 0 && onSegment(s2.q, s1)) {
        return true;
    }
    if (o3 == 0 && onSegment(s1.p, s2)) {
        return true;
    }
    if (o4 == 0 && onSegment(s1.q, s2)) {
        return true;
    }
    return false;
}

double pointToSegment(
    const Point& p, const Point& a, const Point& b, Point& c
) {
    if (a == b) {
        return hypot(p.x - a.x, p.y - a.y);
    }
    Vec ap(a, p), ab(a, b);
    double u = dot(ap, ab) / normSq(ab);
    if (u < EPS) {
        c = a;
        return eDist(p, a);
    }
    if (u + EPS > 1) {
        c = b;
        return eDist(p, b);
    }
    return pointToLine(p, a, b, c);
}

double segmentToSegment(const Segment& s1, const Segment& s2) {
    if (sIntersection(s1, s2)) {
        return 0;
    }
    double ret = INFD;
    Point dummy;
    ret = min(ret, pointToSegment(s1.p, s2.p, s2.q, dummy));
    ret = min(ret, pointToSegment(s1.q, s2.p, s2.q, dummy));
    ret = min(ret, pointToSegment(s2.p, s1.p, s1.q, dummy));
    ret = min(ret, pointToSegment(s2.q, s1.p, s1.q, dummy));
    return ret;
}

using Polygon = vector<Point>;

Polygon getConvexHull(vector<Point> points) {
    sort(begin(points), end(points));
    vector<Point> ret;
    for (int i = 0; i < (int)points.size(); ++i) {
        while (ret.size() > 1
               && cross(points[i], ret[ret.size() - 1], ret[ret.size() - 2])
                      < -EPS) {
            ret.pop_back();
        }
        ret.push_back(points[i]);
    }
    for (int i = points.size() - 2, sz = ret.size(); i >= 0; --i) {
        while ((int)ret.size() > sz
               && cross(points[i], ret[ret.size() - 1], ret[ret.size() - 2])
                      < -EPS) {
            ret.pop_back();
        }
        if (i == 0) {
            break;
        }
        ret.push_back(points[i]);
    }
    return ret;
}

bool isInside(const Point& pv, Polygon& x) {
    int n = x.size(), wn = 0;
    x.push_back(x[0]);
    for (int i = 0; i < n; ++i) {
        if (((x[i + 1].x <= pv.x && x[i].x >= pv.x)
             || (x[i + 1].x >= pv.x && x[i].x <= pv.x))
            && ((x[i + 1].y <= pv.y && x[i].y >= pv.y)
                || (x[i + 1].y >= pv.y && x[i].y <= pv.y))) {
            if (cross(x[i], x[i + 1], pv) == 0) {
                x.pop_back();
                return true;
            }
        }
    }
    for (int i = 0; i < n; ++i) {
        if (x[i].y <= pv.y) {
            if (x[i + 1].y > pv.y && cross(x[i], x[i + 1], pv) > 0) {
                ++wn;
            }
        } else if (x[i + 1].y <= pv.y && cross(x[i], x[i + 1], pv) < 0) {
            --wn;
        }
    }
    x.pop_back();
    return wn != 0;
}

double pointToPolygon(const Point& p, Polygon& poly) {
    if (isInside(p, poly)) {
        return 0;
    }
    int n = poly.size();
    double ans = INFD;
    for (int i = 0; i < n; ++i) {
        Point dummy;
        ans = min(ans, pointToSegment(p, poly[i], poly[(i + 1) % n], dummy));
    }
    return ans;
}

double polygonToPolygon(Polygon& pa, Polygon& pb) {
    int n = pa.size(), m = pb.size();
    for (int i = 0; i < n; ++i) {
        if (isInside(pa[i], pb)) {
            return 0;
        }
    }
    for (int i = 0; i < m; ++i) {
        if (isInside(pb[i], pa)) {
            return 0;
        }
    }
    double ans = INFD;
    for (int i = 0; i < n; ++i) {
        Segment a(pa[i], pa[(i + 1) % n]);
        for (int j = 0; j < m; ++j) {
            Segment b(pb[j], pb[(j + 1) % m]);
            ans = min(ans, segmentToSegment(a, b));
        }
    }
    return ans;
}

double toRads(double degrees) {
    return degrees * PI / 180;
}

template <typename T> using RevPQ = priority_queue<T, vector<T>, greater<T>>;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    // Point dasd;
    // cout << pointToSegment(Point(100, 100), Point(200, 200), Point(200, 200),
    // dasd) << '\n'; return 0;

    int n;
    while (cin >> n) {
        if (n == 0) {
            break;
        }
        vector<Polygon> buildings(n);
        vector<int> heights(n);
        for (int i = 0; i < n; ++i) {
            auto& building = buildings[i];
            int m;
            cin >> m;
            cin >> heights[i];
            building.resize(m);
            for (auto& j : building) {
                cin >> j.x >> j.y;
            }
        }
        double theta, gamma;
        cin >> theta >> gamma;
        theta = (int)(theta + 180) % 360;
        // printf("theta: %.2f, gamma: %.2f\n", theta, gamma);
        theta = toRads(theta), gamma = toRads(gamma);
        // printf("theta: %.2f, gamma: %.2f\n", theta, gamma);
        int sx, sy, tx, ty;
        cin >> sx >> sy >> tx >> ty;
        for (int i = 0; i < n; ++i) {
            auto& building = buildings[i];
            auto& height = heights[i];
            double spDist = height / sin(gamma) * cos(gamma);
            // printf("dist: %f %f\n", spDist, sin(gamma));
            double dx = spDist * cos(theta);
            double dy = spDist * sin(theta);
            // printf("dx: %.2f, dy: %.2f\n", dx, dy);
            Vec offset(dx, dy);
            int m = building.size();
            for (int j = 0; j < m; ++j) {
                auto& p = building[j];
                building.emplace_back(translate(p, offset));
            }
            // for (auto& j : building) {
            //     printf("(%f, %f) ", j.x, j.y);
            // }
            // printf("\n");
            building = getConvexHull(building);
            // for (auto& j : building) {
            //     printf("(%f, %f) ", j.x, j.y);
            // }
            // printf("\n\n");
        }
        Point s(sx, sy), t(tx, ty);
        int sIdx = n;
        int tIdx = n + 1;
        int m = n + 2;
        vector adj(m, vector(m, INFD));
        for (int i = 0; i < m; ++i) {
            adj[i][i] = 0;
        }
        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j < n; ++j) {
                adj[i][j] = polygonToPolygon(buildings[i], buildings[j]);
            }
        }
        for (int i = 0; i < n; ++i) {
            adj[i][sIdx] = pointToPolygon(s, buildings[i]);
            adj[i][tIdx] = pointToPolygon(t, buildings[i]);
        }
        Point dummy;
        adj[sIdx][tIdx] = pointToSegment(s, t, t, dummy);
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < i; ++j) {
                adj[i][j] = adj[j][i];
            }
        }
        vector<double> cost(m, INFD);
        RevPQ<pair<double, int>> q;
        cost[sIdx] = 0;
        q.emplace(0, sIdx);
        while (!q.empty()) {
            auto [curCost, curPos] = q.top();
            q.pop();
            for (int nextPos = 0; nextPos < m; ++nextPos) {
                double nextCost = curCost + adj[curPos][nextPos];
                if (nextCost < cost[nextPos]) {
                    cost[nextPos] = nextCost;
                    q.emplace(nextCost, nextPos);
                }
            }
        }
        cout << fixed << setprecision(6) << cost[tIdx] << '\n';
    }
}
accuracy: 1
time_ms: 430
memory_kb: 4000
final_rank: 1