// The strip alignment from S.DenovoBestAngle (followed by SetXY)
// produces a transform A->B = best.T for the scaled down scapes.
// Here's how to convert that to a transform between the original
// montages:
//
// Recapitulate total process...
//	TAffine	Rbi, Ra, t;
//
// Scale back up
//	best.T.MulXY( scr.crossscale );
//	A.x0 *= scr.crossscale;
//	A.y0 *= scr.crossscale;
//	B.x0 *= scr.crossscale;
//	B.y0 *= scr.crossscale;
//
// A-montage -> A-oriented
//	Ra.NUSetRot( A.deg*PI/180 );
//
// A-oriented -> A-scape
//	Ra.AddXY( -A.x0, -A.y0 );
//
// A-scape -> B-scape
//	t = best.T * Ra;
//
// B-scape -> B-oriented
//	t.AddXY( B.x0, B.y0 );
//
// B-oriented -> B-montage
//	Rbi.NUSetRot( -B.deg*PI/180 );
//	best.T = Rbi * t;
//
static void ScapeStuff()
{
	clock_t		t0 = StartTiming();
	CSuperscape	A, B;
	ThmRec		thm;
	CThmScan	S;
	CorRec		best;

	B.FindLayerIndices( gArgs.zb );
	B.OrientLayer();

	if( gArgs.ismb ) {

		char	buf[256];

		B.MakeWholeRaster();
		sprintf( buf, "montages/M_%d_0.png", gArgs.zb );
		B.DrawRas( buf );
		B.KillRas();
		B.WriteMeta( 'M', gArgs.zb );
		t0 = StopTiming( flog, "MakeMontage", t0 );
	}

	if( !gArgs.isab )
		return;

	A.FindLayerIndices( gArgs.za );
	A.OrientLayer();

	MakeStripRasters( A, B );
	t0 = StopTiming( flog, "MakeStrips", t0 );

	A.MakePoints( thm.av, thm.ap );
	A.WriteMeta( 'A', gArgs.za );

	B.MakePoints( thm.bv, thm.bp );
	B.WriteMeta( 'B', gArgs.zb );

	thm.ftc.clear();
	thm.reqArea	= int(gW * gH * inv_scl * inv_scl);
	thm.olap1D	= int(gW * inv_scl * 0.5);
	thm.scl		= 1;

	S.Initialize( flog, best );
	S.SetRThresh( scr.stripmincorr );
	S.SetNbMaxHt( 0.99 );
	S.SetSweepConstXY( false );
	S.SetSweepPretweak( true );
	S.SetSweepNThreads( scr.stripslots );
	S.SetUseCorrR( true );
	S.SetNewAngProc( NewAngProc );

	gA = &A;
	gB = &B;
	gS = &S;

	if( gArgs.abdbg ) {

		//S.RFromAngle( best, gArgs.abctr, thm );
		//S.Pretweaks( best.R, gArgs.abctr, thm );
		dbgCor = true;
		S.RFromAngle( best, gArgs.abctr, thm );
	}
	else {

		if( scr.stripsweepspan && scr.stripsweepstep ) {

			S.DenovoBestAngle( best,
				0, scr.stripsweepspan / 2, scr.stripsweepstep,
				thm, true );
		}
		else {
			best.A = 0;
			S.PeakHunt( best, 0, thm );
		}

		best.T.Apply_R_Part( A.Opts );

		best.X += B.Opts.x - A.Opts.x;
		best.Y += B.Opts.y - A.Opts.y;

		best.T.SetXY( best.X, best.Y );

		fprintf( flog, "*T: [0,1,2,3,4,5] (strip-strip)\n" );
		fprintf( flog, "[%f,%f,%f,%f,%f,%f]\n",
		best.T.t[0], best.T.t[1], best.T.t[2],
		best.T.t[3], best.T.t[4], best.T.t[5] );
	}

	t0 = StopTiming( flog, "Corr", t0 );
}
// Return true if changes made.
//
static bool ThisBZ(
	vector<BlkZ>			&vZ,
	vector<vector<Pair> >	&P,
	const CSuperscape		&A,
	ThmRec					&thm,
	int						&next_isN )
{
	clock_t		t0 = StartTiming();
	CSuperscape	B;
	CThmScan	S;
	CorRec		best;

	fprintf( flog, "\n--- Start B layer ----\n" );

	B.SetLabel( 'B' );
	next_isN = B.FindLayerIndices( next_isN );

	if( gArgs.abdbg ) {
		while( next_isN != -1 && TS.vtil[B.is0].z != gArgs.dbgz )
			next_isN = B.FindLayerIndices( next_isN );
	}

	if( next_isN == -1 ) {
		fprintf( flog, "$$$ Exhausted B layers $$$\n" );
		return false;
	}

	if( !B.MakeRasB( A.bb ) )
		return false;

	B.DrawRas();

	if( !B.MakePoints( thm.bv, thm.bp ) ) {
		fprintf( flog, "No B points for z=%d.\n", TS.vtil[B.is0].z );
		return false;
	}

	B.WriteMeta();
	t0 = StopTiming( flog, "MakeRasB", t0 );

	thm.ftc.clear();
	thm.reqArea	= int(kPairMinOlap * A.ws * A.hs);
	thm.olap1D	= 4;
	thm.scl		= 1;

	int	Ox	= int(A.x0 - B.x0),
		Oy	= int(A.y0 - B.y0),
		Rx	= int((1.0 - scr.blockxyconf) * A.ws),
		Ry	= int((1.0 - scr.blockxyconf) * A.hs);

	S.Initialize( flog, best );
	S.SetRThresh( scr.blockmincorr );
	S.SetNbMaxHt( 0.99 );
	S.SetSweepConstXY( false );
	S.SetSweepPretweak( true );
	S.SetSweepNThreads( scr.blockslots );
	S.SetUseCorrR( true );
	S.SetDisc( Ox, Oy, Rx, Ry );

	if( gArgs.abdbg ) {

		S.Pretweaks( 0, gArgs.abctr, thm );
		dbgCor = true;
		S.RFromAngle( best, gArgs.abctr, thm );
	}
	else {

		S.Pretweaks( 0, 0, thm );

		if( !S.DenovoBestAngle( best,
				0, scr.blocksweepspan / 2, scr.blocksweepstep,
				thm, false ) ) {

			fprintf( flog, "Low corr [%g] for z=%d.\n",
			best.R, TS.vtil[B.is0].z );

			return false;
		}

		Point	Aorigin = A.Opts;

		best.T.Apply_R_Part( Aorigin );

		best.X += B.Opts.x - Aorigin.x;
		best.Y += B.Opts.y - Aorigin.y;

		best.T.SetXY( best.X, best.Y );

		fprintf( flog, "*T: [0,1,2,3,4,5] (block-block)\n" );
		fprintf( flog, "[%f,%f,%f,%f,%f,%f]\n",
		best.T.t[0], best.T.t[1], best.T.t[2],
		best.T.t[3], best.T.t[4], best.T.t[5] );
	}

	t0 = StopTiming( flog, "Corr", t0 );

	if( gArgs.abdbg )
		return false;

// Build: montage -> montage transform

	TAffine	s, t;

	// A montage -> A block image
	s.NUSetScl( inv_scl );
	s.AddXY( -A.x0, -A.y0 );

	// A block image -> B block image
	t = best.T * s;

	// B block image -> B montage
	t.AddXY( B.x0, B.y0 );
	s.NUSetScl( scr.crossscale );
	best.T = s * t;

// Append to list

	vZ.push_back( BlkZ( best.T, best.R, TS.vtil[B.is0].z ) );

// Accumulate pairs

	FindPairs( vZ, P, A, B );

	return true;
}