#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <climits>
#include <complex>
using namespace std;

#define REP(i,a,n) for(int i=(a); i<(int)(n); i++)
#define rep(i,n) REP(i,0,n)
#define all(x) (x).begin(),(x).end()
#define mp make_pair
#define pb push_back
#define EPS 1e-8
#define DEB 0

typedef complex<double> P;

int n,m;
int edge[128][128],redge[128][128];
struct Sikaku{
	P v[4];
} sikaku[110];



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

int ccw(P p0, P p1, P p2){
	P a(p1-p0), b(p2-p0);
	if( cross(a,b)>EPS ) return 1;
	if( cross(a,b)<-EPS ) return -1;
	if( dot(a,b)<-EPS ) return 2;
	if( abs(a) < abs(b) ) return -2;
	return 0;
}

bool isintersected(P a1, P a2, P b1, P b2){
	return ( ccw(a1,a2,b1)*ccw(a1,a2,b2)<=0 &&
			 ccw(b1,b2,a1)*ccw(b1,b2,a2)<=0 );
}


bool ok(int a, int b){
	rep(i,4)rep(j,4){
		if( isintersected(sikaku[a].v[i],sikaku[a].v[(i+1)%4],
						  sikaku[b].v[j],sikaku[b].v[(j+1)%4]) ){
			return true;
		}
	}
	int amax,amay,amix,amiy,bmax,bmay,bmix,bmiy;
	amax=amay=bmax=bmay=-99999;
	amix=amiy=bmix=bmiy=99999;
	rep(i,4){
		amax = max(amax, (int)(sikaku[a].v[i].real()+EPS));
		amay = max(amay, (int)(sikaku[a].v[i].imag()+EPS));
		amix = min(amix, (int)(sikaku[a].v[i].real()+EPS));
		amiy = min(amiy, (int)(sikaku[a].v[i].imag()+EPS));

		bmax = max(bmax, (int)(sikaku[b].v[i].real()+EPS));
		bmay = max(bmay, (int)(sikaku[b].v[i].imag()+EPS));
		bmix = min(bmix, (int)(sikaku[b].v[i].real()+EPS));
		bmiy = min(bmiy, (int)(sikaku[b].v[i].imag()+EPS));
	}
#if DEB	
	printf("%d %d %d %d  %d %d %d %d\n",amix,amax,amiy,amay, bmix,bmax,bmiy,bmay);
#endif	
	if( amix<=bmix && bmax<=amax && amiy<=bmiy && bmay<=amay ){
		return true;
	}
	if( bmix<=amix && amax<=bmax && bmiy<=amiy && amay<=bmay ){
		return true;
	}
	return false;
}


int num[128],visi[128],cnt;
void dfs(int node){
	rep(i,m)if( !visi[i] && edge[node][i]>0 ){
		visi[i] = 1;
		dfs(i);
	}
	num[cnt++] = node;
}

vector<set<int> > vst;

int redfs(int node, set<int>& st){
	st.insert(node);
	rep(i,m)if( !visi[i] && redge[node][i]>0 ){
		visi[i] = 1;
		redfs(i,st);
	}
}

int main(){
	//cout << ccw(P(100,100), P(100,200), P(0,0)) << endl;
	
	while(scanf("%d",&n),n){
		rep(i,n){
			vst.clear();
			rep(i,128)rep(j,128)edge[i][j]=redge[i][j]=0;
			
			scanf("%d",&m);
			rep(j,m){
				int x[4],y[4];
				scanf("%d%d%d%d%d%d%d%d",x,y,x+1,y+1,x+2,y+2,x+3,y+3);
				rep(k,4){
					sikaku[j].v[k].real() = (double)x[k];
					sikaku[j].v[k].imag() = (double)y[k];
				}
			}
			rep(j,m)rep(k,m)if( j!=k ){
				if( ok(j,k) ){
					edge[j][k] = 1;
					redge[k][j] = 1;
				}
			}

			memset(visi,0,sizeof(visi));
			cnt = 0;
			rep(i,m)if( !visi[i] ){
				visi[i] = 1;
				dfs(i);
			}

#if DEB
			rep(i,m){
				rep(j,m){
					printf("%d ",edge[i][j]);
				}
				puts("");
			}
			puts("num");
			rep(i,m){
				printf("num[%d]:%d\n",i,num[i]);
			}
#endif			

			set<int> ss;
			memset(visi,0,sizeof(visi));
			for(int j=cnt-1; j>=0; j--)if( !visi[num[j]] ){
				ss.clear();
				visi[num[j]] = 1;
				redfs(num[j],ss);
				vst.push_back(ss);
			}

			cout << vst.size() << endl;
		}
		
	}
	
	return 0;
}