#include<iostream>
#include<vector>
#include<algorithm>

const double EPS = 1e-24;
const int INF = 1 << 24;

struct P{
	double x, y;
	P operator-(const P& rp);
};

P P::operator-(const P& rp){
	return {x-rp.x, y-rp.y};
}

typedef P Vector;
typedef long double ld;

struct Segment{
	P a, b;
};

ld abs(P p){
	return sqrt(p.x * p.x + p.y * p.y);
}

double crossProduct(Vector v1, Vector v2){
	return v1.x * v2.y - v2.x * v1.y;
}

double dotProduct(Vector v1, Vector v2){
	return v1.x * v1.y + v2.x * v2.y;
}

struct Rectangle{
	P ps[4];
};

struct Triangle{
	P A, B, C;
};

class UnionFind{
public:
	int size;
	std::vector<int> par, rank;
	UnionFind(int);
	int find(int x);
	bool same(int x, int y);
	void unite(int x, int y);
};

UnionFind::UnionFind(int _size)
	:par(_size), rank(_size), size(_size){
	for(int i=0;i<size;i++){
		par[i] = i;
		rank[i] = 0;
	}
}

int UnionFind::find(int x){
	if(par[x] == x){
		return x;
	}
	return par[x] = find(par[x]);
}

bool UnionFind::same(int x, int y){
	return find(x) == find(y);
}

void UnionFind::unite(int x, int y){
	x = find(x);
	y = find(y);
	if(x == y)return;//もう併合している

	if(rank[x] < rank[y]){
		par[x] = par[y];
	}else{
		par[y] = par[x];
		if(rank[x] == rank[y]){
			rank[y]++;
		}
	}
}

ld mysqrt(ld x){
	ld a = sqrt(static_cast<double>(x));
	while(abs(x-a*a) > EPS){
		a = (a + x/a) / 2.0L;
	}
	return a;
}

ld heron(Triangle t){
	ld a = abs(t.A - t.B), b = abs(t.B - t.C), c = abs(t.C - t.A),
		s = (a + b + c) / 2;
	return mysqrt(s * (s-a) * (s-b) * (s-c));
}

bool is_intersected_ls(Segment s1, Segment s2){
	return (crossProduct(s1.b-s1.a, s2.a-s1.a) * crossProduct(s1.b-s1.a, s2.b-s1.a) < EPS) &&
		(crossProduct(s2.b-s2.a, s1.a-s2.a) * crossProduct(s2.b-s2.a, s1.b-s2.a) < EPS);
}

ld menseki(Rectangle r){
	Triangle t1 = {r.ps[0], r.ps[1], r.ps[2]},
		t2 = {r.ps[2], r.ps[3], r.ps[0]};
		return heron(t1) + heron(t2);
}

double distance_ls_p(Segment s, P p){
	if(dotProduct(s.b-s.a, p-s.a) < EPS)return abs(p-s.a);
	if(dotProduct(s.a-s.b, p-s.b) < EPS)return abs(p-s.b);
	return abs(crossProduct(s.b-s.a, p-s.a)) / abs(s.b-s.a);
}

bool kousa(Rectangle r1, Rectangle r2){
	for(int i=0;i<4;i++){//r1[i](線分)と長方形が交差するか
		for(int j=0;j<4;j++){
			Segment s1 = {r1.ps[i], r1.ps[(i+1)%4]},
				s2 = {r2.ps[j], r2.ps[(j+1)%4]};
			if(is_intersected_ls(s1, s2)){
				return true;
			}
		}
	}

	//面積判定
	if(menseki(r1)< menseki(r2) + EPS){
		std::swap(r1, r2);
	}

	int near = 0;//r1.ps[0]とr1.ps[1]を結んだ線分との距離が最もちかいr2の点の添字
	Segment first_s = {r1.ps[0], r1.ps[1]};
	for(int i=1;i<4;i++){
		if(distance_ls_p(first_s, r2.ps[i]) < distance_ls_p(first_s, r2.ps[near]) + EPS){
			near = i;
		}
	}

	ld Sa = menseki(r2), Sb = menseki(r1);
	for(int i=0;i<2;i++){
		for(int j=0;j<4;j++){
			Triangle t = {r1.ps[j], r1.ps[(j+1)%4], r2.ps[(near+j)%4]};
			Sa += heron(t);
		}
		std::swap(r1, r2);
	}

	if(abs(Sa - Sb) < EPS)return true;
	return false;
}

int main(){
	int n;
	while(std::cin >> n, n){
		int res[100];
		for(int k=0;k<n;k++){
			int m;
			std::cin >> m;

			Rectangle rs[100];
			for(int i=0;i<m;i++){
				P (&ps)[4] = rs[i].ps;
				for(int j=0;j<4;j++){
					std::cin >> ps[j].x >> ps[j].y;
				}
			}

			UnionFind uf(100);
			for(int i=0;i<m;i++){
				for(int j=i+1;j<m;j++){
					if(kousa(rs[i], rs[j])){//片方灯せばいいかなー
						uf.unite(i, j);//併合
					}
				}
			}

			std::vector<int> v;//大元さん
			for(int i=0;i<m;i++){
				int par = uf.find(i);
				if(std::find(v.begin(), v.end(), par) == v.end()){
					v.push_back(i);
				}
			}

			res[k] = v.size();
		}

		for(int i=0;i<n;i++){
			std::cout << res[i] << std::endl;
		}
	}
}