#include<complex>
#include<vector>
#include<iostream>
#include<stack>
#include<queue>
#include<cmath>

#define FRONT 0x01
#define RIGHT 0x02
#define BACK 0x04
#define LEFT 0x08
#define OVER 0x10

#define sc second
#define fr first

#define REP(i,n) for(int i = 0; i < (int)(n); ++i)

using namespace std;

typedef double elem;
typedef complex<elem> point, vec;
typedef pair<point, point> line, hline, seg, pp;

const double eps = 1.0e-6;
const double pi = acos(-1.0);
point base(0,0);

// oÍ
ostream &operator<<(ostream &os, const pair<point,point> &p){
  os << p.fr << "-" << p.sc;
  return os;
}

// ®¬_Ìô¢AÈÇ
bool eq(elem a, elem b){ return abs(a-b) < eps; }
bool lt(elem a, elem b){ return !eq(a,b) && a < b; }
bool leq(elem a, elem b){ return eq(a,b) || a < b; }
bool gt(elem a, elem b){ return !eq(a,b) && a > b; }
bool geq(elem a, elem b){ return eq(a,b) || a > b; }
bool ltz(elem a){ return lt( a, 0 ); }
bool gtz(elem a){ return gt( a, 0 ); }
bool eqv(vec a, vec b){ return eq( abs(b-a),0); }
bool is_zv(vec v){ return eq(abs(v),0); }
elem emax(elem a, elem b){ return gt(a,b)?a:b; }
elem emin(elem a, elem b){ return lt(a,b)?a:b; }

// _Iy[^
bool far(point a, point b){ return gtz( abs(b-a) ); }
bool near(point a, point b){ return leq( abs(b-a), 0 ); }
elem dot(vec a, vec b){ return (a.real() * b.real() + a.imag() * b.imag() ); }
elem cross(vec a, vec b){ return ( a.real() * b.imag() - a.imag() * b.real() ); }


// CCW oXg¾ªA¸xÉæé
int ccw(point a, point b, point x){
  b -= a;
  x -= a;
  // if( is_zv(b) || is_zv( x ) ) return ERROR;
  if( gtz( cross(b,x) ) ) return LEFT;
  if( ltz( cross(b,x) ) ) return RIGHT;
  if( ltz( dot(b,x) ) ) return BACK;
  if( lt( abs(b), abs(x) ) ) return FRONT;
  return OVER;
}

// üªÌð·»è
bool intersectedSS(seg a, seg b)
{
  if( ccw(a.fr,a.sc,b.fr)&OVER || ccw(a.fr,a.sc,b.sc)&OVER ) return true;
  return
    ( ccw(a.fr,a.sc,b.fr) | ccw(a.fr,a.sc,b.sc) ) == (LEFT|RIGHT) &&
    ( ccw(b.fr,b.sc,a.fr) | ccw(b.fr,b.sc,a.sc) ) == (LEFT|RIGHT) ;
}

struct rectangle{
  int id;
  vector<point> p;
  vector<int> con;
  rectangle(int id):id(id){p.reserve(4);}
  bool inside( const point &t )const{
    int n = p.size();
    for(int i = 0; i < n; ++i){
      if( RIGHT != ccw(p[i],p[(i+1)%n],t) ){
	return false;
      }
    }
    return true;
  }
  bool online( const point &t )const{
    int n = p.size();
    for(int i = 0; i < p.size(); ++i){
      if( ccw(p[i],p[(i+1)%n],t) == OVER ) return true;
    }
    return false;
  }
  bool include( const rectangle &r ) const {
    for(int i = 0; i < r.p.size(); ++i){
      if( !inside( r.p[i] ) ) return false;
    }
    return true;
  }
  bool touch( const rectangle &r )const{
    int n = p.size();
    int rn = r.p.size();

    for(int i = 0; i < rn; ++i){
      if( inside(r.p[i]) || online(r.p[i]) ) return true;
    }
    for(int i = 0; i < n; ++i){
      seg l(p[i],p[(i+1)%n]);
      for(int j = 0; j < rn; ++j){
	seg m(r.p[j],r.p[(j+1)%rn]);
	if( intersectedSS( l, m ) ) return true;
      }
    }
    return false;
  }
};

int BFS(bool vis[], int st, const vector<rectangle> &vrect)
{
  queue<int> qr;
  qr.push(st);
  vis[st]=true;
  while(!qr.empty()){
    int now = qr.front();
    qr.pop();
    for(int i = 0; i < vrect[now].con.size(); ++i){
      int next = vrect[now].con[i];
      if( !vis[next] ){
	vis[next]=true;
	qr.push( vrect[ vrect[now].con[i] ].id );
      }
    }
  }
  return 1;
}

int main(){
  /*
  rectangle r1(-2),r2(-1);
  r1.p.push_back( point(5,3) );
  r1.p.push_back( point(499,329) );
  r1.p.push_back( point(921,479) );
  r1.p.push_back( point(993,21) );

  r2.p.push_back( point(5,3) );
  r2.p.push_back( point(499,329) );
  r2.p.push_back( point(921,479) );
  r2.p.push_back( point(993,21) );
  
  cout << r1.touch( r2 ) << endl;
  cout << r1.online( point(5,3) ) << endl;
  cout << r1.inside( point(921,478) ) << endl;
  cout << r1.inside( point(993,22) ) << endl;
  */
  while(true){
    int N;
    cin >> N;
    if( N == 0 ) break;
    for(int i = 0; i < N; ++i){
      int M;
      int ans = 0;
      vector<rectangle> vrect;
      cin >> M;
      for(int j = 0; j < M; ++j){
	rectangle rect(j);
	for(int k = 0; k < 4; ++k){
	  elem x,y;
	  cin >> x >> y;
	  rect.p.push_back( point(x,y) );
	}
	vrect.push_back( rect );
      }
      
      for(int j = 0; j < M; ++j){
	for(int k = 0; k < M; ++k){
	  if( j!=k ){
	    if( vrect[j].touch( vrect[k] ) ){
	      vrect[j].con.push_back( k );
	      vrect[k].con.push_back( j );
	    }
	   }
	}
      }
      
      bool vis[M];
      fill(vis,vis+M,false);
      for(int j = 0; j < M; ++j){
	if( !vis[j] )
	  ans += BFS( vis, j, vrect );
      }
       
      cout << ans << endl;
    }
  }
  return 0;
}

Measured memory_bytes: 1000