#include<iostream>
#include<sstream>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<algorithm>
#include<numeric>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<complex>
#include<cassert>

#define rep(i,n) for(int i=0;i<n;i++)
#define all(c) (c).begin(),(c).end()
#define mp make_pair
#define pb push_back
#define rp(i,c) rep(i,(c).size())
#define fr(i,c) for(__typeof((c).begin()) i=(c).begin();i!=(c).end();i++)
#define dbg(x) cerr<<#x<<" = "<<(x)<<endl

using namespace std;

typedef long long ll;
typedef vector<int> vi;
typedef pair<int,int> pi;
typedef complex<double> P;
const int inf=1<<28;
const double INF=1e12,EPS=1e-9;

double cross(const P& a, const P& b) {
  return imag(conj(a)*b);
}
double dot(const P& a, const P& b) {
  return real(conj(a)*b);
}
struct L : public vector<P> {
  L(const P &a, const P &b) {
    push_back(a); push_back(b);
  }
};
typedef vector<P> polygon;

int ccw(P a, P b, P c) {
  b -= a; c -= a;
  if (cross(b, c) > 0)   return +1;       // counter clockwise
  if (cross(b, c) < 0)   return -1;       // clockwise
  if (dot(b, c) < 0)     return +2;       // c--a--b on line
  if (norm(b) < norm(c)) return -2;       // a--b--c on line
  return 0;
}

#define curr(P, i) P[i]
#define next(P, i) P[(i+1)%P.size()]
enum { OUT, ON, IN };
int contains(const polygon& poly, const P& p) {
  bool in = false;
  for (int i = 0; i < poly.size(); ++i) {
    P a = curr(poly,i) - p, b = next(poly,i) - p;
    if (imag(a) > imag(b)) swap(a, b);
    if (imag(a) <= 0 && 0 < imag(b))
      if (cross(a, b) < 0) in = !in;
    if (cross(a, b) == 0 && dot(a, b) <= 0) return ON;
  }
  return in ? IN : OUT;
}
bool intersectSS(const L &s, const L &t) {
  return ccw(s[0],s[1],t[0])*ccw(s[0],s[1],t[1]) <= 0 &&
         ccw(t[0],t[1],s[0])*ccw(t[0],t[1],s[1]) <= 0;
}
bool intersectPP(const polygon &a,const polygon &b)
{
	if(contains(a,b[0])||contains(b,a[0]))return 1;
	
	rep(i,a.size())rep(j,b.size())
	{
		if(intersectSS(L(a[i],a[(i+1)%a.size()]),L(b[i],b[(i+1)%b.size()])))return 1;
	}
	return 0;
}

int p[100];
int root(int x)
{
	if(p[x]==x)return x;
	return p[x]=root(p[x]);
}
void merge(int a,int b)
{
	a=root(a); b=root(b);
	if(a!=b)p[a]=b;
}

int n,m;
vector<polygon> polys;

int cnt(){
	rep(i,m)rep(j,i)if(intersectPP(polys[i],polys[j]))merge(i,j);
	
	bool V[100]={0};
	int ans=0;
	rep(i,m)if(!V[root(i)])V[root(i)]=1,ans++;
	return ans;
}

int main()
{
	while(cin>>n,n)
	{
		rep(N,n)
		{
			cin>>m;
			rep(i,m)p[i]=i;
			polys.clear();
			
			rep(i,m)
			{
				polygon poly;
				int x[4],y[4];
				
				rep(j,4)cin>>x[j]>>y[j],poly.pb(P(x[j],y[j]));
				polys.pb(poly);
			}
			cout<<cnt()<<endl;
		}
	}
	return 0;
}